diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index d42ea917..b18c7d8a 100755 --- a/_datafiles/config.yaml +++ b/_datafiles/config.yaml @@ -51,16 +51,16 @@ Server: # Commands to run when a user logs in. These commands are run by the user # and can be anything they have access to. OnLoginCommands: - - 'emote @appears before you in a flash of ⚡lightning⚡!' - - print - - motd - - print - - online - - print - - inbox check - - print - - mudletmap - - checkclient + - 'emote @appears before you in a flash of ⚡lightning⚡!' + - print + - motd + - print + - online + - print + - inbox check + - print + - mudletmap + - checkclient # - Motd - # Message of the day. This is displayed when the motd command is run. Motd: '{{ t "Motd" }}' @@ -77,12 +77,12 @@ Server: # It is a good idea to lock configs related to folder/file paths to prevent # accidental changes that could break the game. Locked: - - FilePaths - - Server.CurrentVersion - - Server.NextRoomId - - Server.Seed - - Server.OnLoginCommands - - Server.BannedNames + - FilePaths + - Server.CurrentVersion + - Server.NextRoomId + - Server.Seed + - Server.OnLoginCommands + - Server.BannedNames ################################################################################ # @@ -140,7 +140,6 @@ LootGoblin: # suddenly the goblin shows up. IncludeRecentRooms: true - ################################################################################ # # ENGINE TIMING @@ -321,34 +320,44 @@ Integrations: Discord: # Optional webhook URL to send mud event messages to, such as joins/disconnects # Can also be set via environment variable: DISCORD_WEBHOOK_URL - WebhookUrl: '' - + WebhookUrl: "" ################################################################################ # -# TEXT FORMATS -# Basic text strings that occur often, and may need customization -# -################################################################################ -TextFormats: - # - Prompt - - # Default prompt formatting. - # See: "help prompt" in game to learn more about this. - Prompt: '{8}[{t} {T} {255}HP:{hp}{8}/{HP} {255}MP:{13}{mp}{8}/{13}{MP}{8}]{239}{h}{8}:' - # - TimeFormat - - # When real world time is shown, what format should be used? - # This uses a Go time format string, which is kinda weird. - # See: https://go.dev/src/time/format.go - Time: 'Monday, 02-Jan-2006 3:04:05PM' - # - TimeFormatShort - - # Same as TimeFormat, but shorter form - TimeShort: 'Jan 2 ''06 3:04PM' - # - EnterRoomMessageWrapper - - # Decorate entrance text with this. Put a %s where the message should be. - EnterRoomMessageWrapper: " >>> %s\n" - # - ExitRoomMessageWrapper - - # Decorate exit text with this. Put a %s where the message should be. - ExitRoomMessageWrapper: " >>> %s\n" +# USER INTERFACE +# Visual preferences and text formatting options +# +################################################################################ +UserInterface: + # - Formats - + # Text format strings that control how various messages appear + Formats: + # - Prompt - + # Default prompt formatting. + # See: "help prompt" in game to learn more about this. + Prompt: "{8}[{t} {T} {255}HP:{hp}{8}/{HP} {255}MP:{13}{mp}{8}/{13}{MP}{8}]{239}{h}{8}:" + # - Time - + # When real world time is shown, what format should be used? + # This uses a Go time format string, which is kinda weird. + # See: https://go.dev/src/time/format.go + Time: "Monday, 02-Jan-2006 3:04:05PM" + # - TimeShort - + # Same as Time, but shorter form + TimeShort: "Jan 2 '06 3:04PM" + # - EnterRoomMessageWrapper - + # Decorate entrance text with this. Put a %s where the message should be. + EnterRoomMessageWrapper: " >>> %s\n" + # - ExitRoomMessageWrapper - + # Decorate exit text with this. Put a %s where the message should be. + ExitRoomMessageWrapper: " >>> %s\n" + # - Display - + # Visual display preferences + Display: + # - ShowEmptyEquipmentSlots - + # Whether to show empty equipment slots when looking at characters/mobs + # true = show all slots including empty ones (traditional MUD style) + # false = only show equipped items (cleaner display) + ShowEmptyEquipmentSlots: true ################################################################################ # @@ -359,15 +368,15 @@ TextFormats: Translation: # - DefaultLanguage - # Specify the default game language (fallback) - DefaultLanguage: 'en' + DefaultLanguage: "en" # - Language - # Specify the game language - Language: 'en' + Language: "en" # - LanguagePaths - # Specify the game language file paths LanguagePaths: - - '_datafiles/localize' - - '_datafiles/world/default/localize' + - "_datafiles/localize" + - "_datafiles/world/default/localize" ################################################################################ # @@ -412,7 +421,7 @@ Network: # Whether Admin/Mod users get timed out when reaching MaxIdleSeconds # If set to false, Admins & Mods never get force disconnected. TimeoutMods: false - # - ZombieSeconds - + # - ZombieSeconds - # How many seconds a character stays active/in game after a network connection # is lost. Set to 0 to instantly log out characters (exploitable). ZombieSeconds: 60 @@ -488,7 +497,7 @@ Validation: # (This is a regular expression, you must understand how to create them) # Set to empty to disable. If the regex is invalid it will revert to the # default of '^[a-zA-Z0-9_]+$' - NameRejectRegex: '^[a-zA-Z0-9_]+$' + NameRejectRegex: "^[a-zA-Z0-9_]+$" NameRejectReason: "Must only contain Alpha-numeric and underscores." # - BannedNames - # Names that are not allowed to be used by players. This is a good place to @@ -502,39 +511,39 @@ Validation: # that contain that name. For example, "*admin*" would ban "admin", "superadmin", # "administrator", etc. BannedNames: - - "*admin*" - - "*moderator*" - - "player*" - - "user*" - - "me" - - "myself" - - "self" - - "us" - - "you" - - "them" - - "everyone" - - "someone" - - "anyone" - - "nobody" - - "somebody" - - "anybody" - - "none" - - "nothing" - - "something" - - "anything" - - "everything" - - "all" - - "north*" - - "south*" - - "east*" - - "west*" - - "up" - - "down" - - "chest" - - "door" - - "new" - - "join" - - "register" + - "*admin*" + - "*moderator*" + - "player*" + - "user*" + - "me" + - "myself" + - "self" + - "us" + - "you" + - "them" + - "everyone" + - "someone" + - "anyone" + - "nobody" + - "somebody" + - "anybody" + - "none" + - "nothing" + - "something" + - "anything" + - "everything" + - "all" + - "north*" + - "south*" + - "east*" + - "west*" + - "up" + - "down" + - "chest" + - "door" + - "new" + - "join" + - "register" ################################################################################ # @@ -555,7 +564,6 @@ Roles: builder: ["room.info", "build"] helper: ["paz", "teleport.playername", "locate"] - ################################################################################ # # Modules diff --git a/_datafiles/world/default/templates/character/description.screenreader.template b/_datafiles/world/default/templates/character/description.screenreader.template new file mode 100644 index 00000000..58aebe21 --- /dev/null +++ b/_datafiles/world/default/templates/character/description.screenreader.template @@ -0,0 +1,8 @@ +{{ .Name }} ({{ .AlignmentName }}) +{{- $tnl := .XPTNL -}} +{{- $pct := (pct .Experience $tnl ) -}} + +Description: +{{ .GetDescription }} + +Health Status: {{ .GetHealthAppearance }} \ No newline at end of file diff --git a/_datafiles/world/default/templates/character/inventory-look.screenreader.template b/_datafiles/world/default/templates/character/inventory-look.screenreader.template new file mode 100644 index 00000000..2f58cc51 --- /dev/null +++ b/_datafiles/world/default/templates/character/inventory-look.screenreader.template @@ -0,0 +1,22 @@ +Equipment: +{{ if not .Equipment.Weapon.IsDisabled }}Weapon: {{ .Equipment.Weapon.NameSimple }} +{{ end -}} +{{- if not .Equipment.Offhand.IsDisabled }}Offhand: {{ .Equipment.Offhand.NameSimple }} +{{ end -}} +{{- if not .Equipment.Head.IsDisabled }}Head: {{ .Equipment.Head.NameSimple }} +{{ end -}} +{{- if not .Equipment.Neck.IsDisabled }}Neck: {{ .Equipment.Neck.NameSimple }} +{{ end -}} +{{- if not .Equipment.Body.IsDisabled }}Body: {{ .Equipment.Body.NameSimple }} +{{ end -}} +{{- if not .Equipment.Belt.IsDisabled }}Belt: {{ .Equipment.Belt.NameSimple }} +{{ end -}} +{{- if not .Equipment.Gloves.IsDisabled }}Gloves: {{ .Equipment.Gloves.NameSimple }} +{{ end -}} +{{- if not .Equipment.Ring.IsDisabled }}Ring: {{ .Equipment.Ring.NameSimple }} +{{ end -}} +{{- if not .Equipment.Legs.IsDisabled }}Legs: {{ .Equipment.Legs.NameSimple }} +{{ end -}} +{{- if not .Equipment.Feet.IsDisabled }}Feet: {{ .Equipment.Feet.NameSimple }} +{{ end }} +Carrying: {{ $itmCt := len .ItemNames }}{{ if eq $itmCt 0 }}no{{ else if lt $itmCt 4 }}a few{{ else if lt $itmCt 7 }}several{{ else }}lots of{{ end }} objects \ No newline at end of file diff --git a/_datafiles/world/default/templates/descriptions/look-item.screenreader.template b/_datafiles/world/default/templates/descriptions/look-item.screenreader.template new file mode 100644 index 00000000..5e81a101 --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-item.screenreader.template @@ -0,0 +1,37 @@ +{{/* Screenreader-friendly template for looking at items */}} +You look at the {{ .Item.Name }} {{ .Location }}: + +{{ .ItemSpec.Description }} +{{- if eq .Type "readable" }} +You should probably read this. +{{- else if eq .Subtype "drinkable" }} +You could probably drink this. +{{- else if eq .Subtype "edible" }} +You could probably eat this. +{{- else if eq .Type "lockpicks" }} +These are used with the picklock command. +{{- else if eq .Type "key" }} +When you find the right door, keys are added to your keyring automatically. +{{- else if eq .Subtype "wearable" }} +It looks like wearable {{ .ItemSpec.Type }} equipment. +{{- end }} +{{- if eq .Type "weapon" }} +It looks like a {{ .WeaponHands }}-Handed weapon. +{{- if eq .WeaponType "claws" }} +It looks like a claws weapon. These can be dual wielded without training. +{{- else if eq .WeaponType "shooting" }} +This can fired into adjacent areas. (help shoot) +{{- end }} +{{- if gt .WaitRounds 0 }} +It requires an extra {{ .WaitRounds }} round(s) between attacks. +{{- end }} +{{- end }} +{{- if .HasUses }} +It has {{ .UsesRemaining }}/{{ .MaxUses }} uses remaining. +{{- end }} +{{- if index .Adjectives "cursed" }} +It's CURSED! Once equipped, it cannot be removed without magical help. +{{- end }} +{{- if index .Adjectives "enchanted" }} +It glows with a magical aura (enchantment level {{ .EnchantLevel }}). +{{- end }} \ No newline at end of file diff --git a/_datafiles/world/default/templates/descriptions/look-item.template b/_datafiles/world/default/templates/descriptions/look-item.template new file mode 100644 index 00000000..b8690d2d --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-item.template @@ -0,0 +1,37 @@ +{{/* Template for looking at items */}} +You look at the {{ .Item.DisplayName }} {{ .Location }}: + +{{ .ItemSpec.Description }} +{{- if eq .Type "readable" }} +- You should probably read this. +{{- else if eq .Subtype "drinkable" }} +- You could probably drink this. +{{- else if eq .Subtype "edible" }} +- You could probably eat this. +{{- else if eq .Type "lockpicks" }} +- These are used with the picklock command. +{{- else if eq .Type "key" }} +- When you find the right door, keys are added to your keyring automatically. +{{- else if eq .Subtype "wearable" }} +- It looks like wearable {{ .ItemSpec.Type }} equipment. +{{- end }} +{{- if eq .Type "weapon" }} +- It looks like a {{ .WeaponHands }}-Handed weapon. +{{- if eq .WeaponType "claws" }} +- It looks like a claws weapon. These can be dual wielded without training. +{{- else if eq .WeaponType "shooting" }} +- This can fired into adjacent areas. (help shoot) +{{- end }} +{{- if gt .WaitRounds 0 }} +- It requires an extra {{ .WaitRounds }} round(s) between attacks. +{{- end }} +{{- end }} +{{- if .HasUses }} +- It has {{ .UsesRemaining }}/{{ .MaxUses }} uses remaining. +{{- end }} +{{- if index .Adjectives "cursed" }} +- It's CURSED! Once equipped, it cannot be removed without magical help. +{{- end }} +{{- if index .Adjectives "enchanted" }} +- It glows with a magical aura (enchantment level {{ .EnchantLevel }}). +{{- end }} \ No newline at end of file diff --git a/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template new file mode 100644 index 00000000..d32be275 --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template @@ -0,0 +1,69 @@ +{{/* Screenreader-friendly template for looking at mobs */}} +Name: {{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} + +Description: +{{ .Character.GetDescription }} + +Health Status: {{ .HealthStatus }} +{{- $showEmpty := showEmptyEquipmentSlots }} +{{- $hasEquipment := false }} +{{- if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not $hasEquipment) (not $showEmpty) }} + +{{ .Character.Name }} is not wearing anything. +{{- else if or $hasEquipment $showEmpty }} + +Equipment: +{{- $showItem := false }} +{{- $showItem = or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} +{{- if $showItem }} +Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} +{{- if $showItem }} +Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} +{{- if $showItem }} +Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} +{{- if $showItem }} +Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} +{{- if $showItem }} +Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} +{{- if $showItem }} +Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} +{{- if $showItem }} +Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} +{{- if $showItem }} +Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} +{{- if $showItem }} +Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} +{{- if $showItem }} +Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}nothing{{ end }} +{{- end }} +{{- end }} + +Carrying: {{ .CarryingStatus }} objects \ No newline at end of file diff --git a/_datafiles/world/default/templates/descriptions/look-mob.template b/_datafiles/world/default/templates/descriptions/look-mob.template new file mode 100644 index 00000000..b10dc025 --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-mob.template @@ -0,0 +1,66 @@ +{{/* Template for looking at mobs */}} +.: {{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} + ┌─ .:Description ────────────────────────────────────────────────────────────┐ + {{ splitstring .Character.GetDescription 72 " "}} + {{ .HealthStatus }} + └────────────────────────────────────────────────────────────────────────────┘ +{{- $showEmpty := showEmptyEquipmentSlots }} +{{- $hasEquipment := false }} +{{- if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}{{ $hasEquipment = true }}{{ end }} +{{- if and (not $hasEquipment) (not $showEmpty) }} + {{ .Character.Name }} is not wearing anything. +{{- else if or $hasEquipment $showEmpty }} + ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ +{{- $showItem := false }} +{{- $showItem = or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} +{{- if $showItem }} + Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} +{{- if $showItem }} + Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} +{{- if $showItem }} + Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} +{{- if $showItem }} + Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} +{{- if $showItem }} + Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} +{{- if $showItem }} + Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} +{{- if $showItem }} + Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} +{{- if $showItem }} + Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} +{{- if $showItem }} + Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} +{{- $showItem = or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} +{{- if $showItem }} + Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}-nothing-{{ end }} +{{- end }} + └────────────────────────────────────────────────────────────────────────────┘ +{{- end }} + Carrying: {{ .CarryingStatus }} objects \ No newline at end of file diff --git a/internal/configs/config.textformats.go b/internal/configs/config.textformats.go deleted file mode 100644 index 8db34fa7..00000000 --- a/internal/configs/config.textformats.go +++ /dev/null @@ -1,55 +0,0 @@ -package configs - -import ( - "strings" -) - -type TextFormats struct { - Prompt ConfigString `yaml:"Prompt"` // The in-game status prompt style - EnterRoomMessageWrapper ConfigString `yaml:"EnterRoomMessageWrapper"` // Special enter messages - ExitRoomMessageWrapper ConfigString `yaml:"ExitRoomMessageWrapper"` // Special exit messages - Time ConfigString `yaml:"Time"` // How to format time when displaying real time - TimeShort ConfigString `yaml:"TimeShort"` // How to format time when displaying real time (shortform) -} - -func (m *TextFormats) Validate() { - - if m.Prompt == `` { - m.Prompt = `{8}[{t} {T} {255}HP:{hp}{8}/{HP} {255}MP:{13}{mp}{8}/{13}{MP}{8}]{239}{h}{8}:` - } - - // Must have a message wrapper... - if m.EnterRoomMessageWrapper == `` { - m.EnterRoomMessageWrapper = `%s` // default - } - if strings.LastIndex(string(m.EnterRoomMessageWrapper), `%s`) < 0 { - m.EnterRoomMessageWrapper += `%s` // default - } - - // Must have a message wrapper... - if m.ExitRoomMessageWrapper == `` { - m.ExitRoomMessageWrapper = `%s` // default - } - if strings.LastIndex(string(m.ExitRoomMessageWrapper), `%s`) < 0 { - m.ExitRoomMessageWrapper += `%s` // default - } - - if m.Time == `` { - m.Time = `Monday, 02-Jan-2006 03:04:05PM` - } - - if m.TimeShort == `` { - m.TimeShort = `Jan 2 '06 3:04PM` - } - -} - -func GetTextFormatsConfig() TextFormats { - configDataLock.RLock() - defer configDataLock.RUnlock() - - if !configData.validated { - configData.Validate() - } - return configData.TextFormats -} diff --git a/internal/configs/config.userinterface.go b/internal/configs/config.userinterface.go new file mode 100644 index 00000000..de351df6 --- /dev/null +++ b/internal/configs/config.userinterface.go @@ -0,0 +1,72 @@ +package configs + +import ( + "strings" +) + +type UserInterface struct { + Formats UserInterfaceFormats `yaml:"Formats"` + Display UserInterfaceDisplay `yaml:"Display"` +} + +type UserInterfaceFormats struct { + Prompt ConfigString `yaml:"Prompt"` // The in-game status prompt style + EnterRoomMessageWrapper ConfigString `yaml:"EnterRoomMessageWrapper"` // Special enter messages + ExitRoomMessageWrapper ConfigString `yaml:"ExitRoomMessageWrapper"` // Special exit messages + Time ConfigString `yaml:"Time"` // How to format time when displaying real time + TimeShort ConfigString `yaml:"TimeShort"` // How to format time when displaying real time (shortform) +} + +type UserInterfaceDisplay struct { + ShowEmptyEquipmentSlots ConfigBool `yaml:"ShowEmptyEquipmentSlots"` // Whether to show empty equipment slots when looking at characters/mobs +} + +func (u *UserInterface) Validate() { + u.Formats.Validate() + u.Display.Validate() +} + +func (f *UserInterfaceFormats) Validate() { + if f.Prompt == `` { + f.Prompt = `{8}[{t} {T} {255}HP:{hp}{8}/{HP} {255}MP:{13}{mp}{8}/{13}{MP}{8}]{239}{h}{8}:` + } + + // Must have a message wrapper... + if f.EnterRoomMessageWrapper == `` { + f.EnterRoomMessageWrapper = `%s` // default + } + if strings.LastIndex(string(f.EnterRoomMessageWrapper), `%s`) < 0 { + f.EnterRoomMessageWrapper += `%s` // append if missing + } + + // Must have a message wrapper... + if f.ExitRoomMessageWrapper == `` { + f.ExitRoomMessageWrapper = `%s` // default + } + if strings.LastIndex(string(f.ExitRoomMessageWrapper), `%s`) < 0 { + f.ExitRoomMessageWrapper += `%s` // append if missing + } + + if f.Time == `` { + f.Time = `Monday, 02-Jan-2006 03:04:05PM` + } + + if f.TimeShort == `` { + f.TimeShort = `Jan 2 '06 3:04PM` + } +} + +func (d *UserInterfaceDisplay) Validate() { + // ShowEmptyEquipmentSlots defaults to true (show all slots) + // The ConfigBool type handles the default value +} + +// Convenience method to check if empty equipment slots should be shown +func (c Config) ShouldShowEmptyEquipmentSlots() bool { + return bool(c.UserInterface.Display.ShowEmptyEquipmentSlots) +} + +// GetUserInterfaceConfig returns the UserInterface configuration +func GetUserInterfaceConfig() UserInterface { + return GetConfig().UserInterface +} diff --git a/internal/configs/configs.go b/internal/configs/configs.go index 5018a06a..a45e3813 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -34,20 +34,20 @@ var ( type Config struct { // Start config subsections - Server Server `yaml:"Server"` - Memory Memory `yaml:"Memory"` - LootGoblin LootGoblin `yaml:"LootGoblin"` - Timing Timing `yaml:"Timing"` - FilePaths FilePaths `yaml:"FilePaths"` - GamePlay GamePlay `yaml:"GamePlay"` - Integrations Integrations `yaml:"Integrations"` - TextFormats TextFormats `yaml:"TextFormats"` - Translation Translation `yaml:"Translation"` - Network Network `yaml:"Network"` - Scripting Scripting `yaml:"Scripting"` - SpecialRooms SpecialRooms `yaml:"SpecialRooms"` - Validation Validation `yaml:"Validation"` - Roles Roles `yaml:"Roles"` + Server Server `yaml:"Server"` + Memory Memory `yaml:"Memory"` + LootGoblin LootGoblin `yaml:"LootGoblin"` + Timing Timing `yaml:"Timing"` + FilePaths FilePaths `yaml:"FilePaths"` + GamePlay GamePlay `yaml:"GamePlay"` + UserInterface UserInterface `yaml:"UserInterface"` + Integrations Integrations `yaml:"Integrations"` + Translation Translation `yaml:"Translation"` + Network Network `yaml:"Network"` + Scripting Scripting `yaml:"Scripting"` + SpecialRooms SpecialRooms `yaml:"SpecialRooms"` + Validation Validation `yaml:"Validation"` + Roles Roles `yaml:"Roles"` // Plugins is a special case Modules Modules `yaml:"Modules"` @@ -191,7 +191,7 @@ func (c *Config) Validate() { c.FilePaths.Validate() c.GamePlay.Validate() c.Integrations.Validate() - c.TextFormats.Validate() + c.UserInterface.Validate() c.Translation.Validate() c.Network.Validate() c.Scripting.Validate() diff --git a/internal/items/items.go b/internal/items/items.go index 79a35e48..c58f4c54 100644 --- a/internal/items/items.go +++ b/internal/items/items.go @@ -168,6 +168,69 @@ func (i *Item) Validate() { } +// ItemLookData contains all the data needed for the look-item template +type ItemLookData struct { + Item *Item + ItemSpec ItemSpec + Location string // "in your backpack", "you are wearing" + Type string // Item type for template comparisons + Subtype string // Item subtype for template comparisons + Adjectives map[string]bool // Extensible flags (cursed, enchanted, etc) + WeaponHands int + WeaponType string // "claws", "shooting", etc. + WaitRounds int + HasUses bool + UsesRemaining int + MaxUses int + EnchantLevel int +} + +// GetLookData returns data structure for template-based item descriptions +func (i *Item) GetLookData(location string) ItemLookData { + // Defensive check for nil item + if i == nil { + return ItemLookData{ + Location: location, + Type: "", + Subtype: "", + Adjectives: make(map[string]bool), + } + } + + iSpec := i.GetSpec() + + data := ItemLookData{ + Item: i, + ItemSpec: iSpec, + Location: location, + Type: string(iSpec.Type), + Subtype: string(iSpec.Subtype), + Adjectives: make(map[string]bool), + HasUses: iSpec.Uses > 0, + UsesRemaining: i.Uses, + MaxUses: iSpec.Uses, + EnchantLevel: int(i.Enchantments), + } + + // Set adjectives for extensibility + data.Adjectives["cursed"] = i.IsCursed() + data.Adjectives["enchanted"] = i.Enchantments > 0 + + if iSpec.Type == Weapon { + data.WeaponHands = iSpec.Hands + data.WaitRounds = iSpec.WaitRounds + + switch iSpec.Subtype { + case Claws: + data.WeaponType = "claws" + case Shooting: + data.WeaponType = "shooting" + } + } + + return data +} + func (i *Item) GetLongDescription() string { iSpec := i.GetSpec() diff --git a/internal/mapper/mapper.go b/internal/mapper/mapper.go index 044d5d50..a6c15492 100644 --- a/internal/mapper/mapper.go +++ b/internal/mapper/mapper.go @@ -998,15 +998,15 @@ func PreCacheMaps() { func validateRoomBiomes() { missingBiomeCount := 0 invalidBiomeCount := 0 - + for _, roomId := range rooms.GetAllRoomIds() { room := rooms.LoadRoom(roomId) if room == nil { continue } - + originalBiome := room.Biome - + // Check if room has no biome if originalBiome == "" { zoneBiome := rooms.GetZoneBiome(room.Zone) @@ -1022,7 +1022,7 @@ func validateRoomBiomes() { } } } - + if missingBiomeCount > 0 || invalidBiomeCount > 0 { mudlog.Info("Biome validation complete", "missing", missingBiomeCount, "invalid", invalidBiomeCount) } diff --git a/internal/mobcommands/go.go b/internal/mobcommands/go.go index 231b6ec0..c3aadc92 100644 --- a/internal/mobcommands/go.go +++ b/internal/mobcommands/go.go @@ -30,7 +30,7 @@ func Go(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) { } if !foundRoomExit { - c := configs.GetTextFormatsConfig() + c := configs.GetUserInterfaceConfig().Formats if forceRoomId == room.RoomId { return true, nil @@ -112,7 +112,7 @@ func Go(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) { room.RemoveMob(mob.InstanceId) destRoom.AddMob(mob.InstanceId) - c := configs.GetTextFormatsConfig() + c := configs.GetUserInterfaceConfig().Formats // Tell the old room they are leaving room.SendText( diff --git a/internal/mobs/mobs.go b/internal/mobs/mobs.go index 24625fb6..483a4ae9 100644 --- a/internal/mobs/mobs.go +++ b/internal/mobs/mobs.go @@ -708,6 +708,72 @@ func (m *Mob) GetScriptPath() string { return util.FilePath(fullScriptPath) } +// MobLookData contains all the data needed for the look-mob template +type MobLookData struct { + Mob *Mob + Character *characters.Character + HealthStatus string + Equipment *characters.Worn + ItemCount int + CarryingStatus string // "no", "a few", "several", "lots of" + IsCharmed bool + IsShop bool + CharmedBy string // charmer's name if applicable +} + +// GetLookData returns data structure for template-based mob descriptions +func (m *Mob) GetLookData(charmerName string) MobLookData { + // Defensive check for nil mob + if m == nil { + return MobLookData{ + CharmedBy: charmerName, + HealthStatus: "Unknown health status.", + CarryingStatus: "unknown", + } + } + + data := MobLookData{ + Mob: m, + Character: &m.Character, + Equipment: &m.Character.Equipment, + IsCharmed: m.Character.IsCharmed(), + IsShop: m.HasShop(), + CharmedBy: charmerName, + } + + // Calculate health status with defensive checks + if m.Character.HealthMax.Value <= 0 { + data.HealthStatus = m.Character.Name + " appears to be in an unknown state." + } else { + healthPct := int(math.Ceil((float64(m.Character.Health) / float64(m.Character.HealthMax.Value)) * 100)) + if healthPct >= 100 { + data.HealthStatus = m.Character.Name + " is in perfect health." + } else if healthPct >= 80 { + data.HealthStatus = m.Character.Name + " has a few scratches." + } else if healthPct >= 50 { + data.HealthStatus = m.Character.Name + " has some cuts and bruises." + } else if healthPct >= 15 { + data.HealthStatus = m.Character.Name + " looks to be in pretty bad shape." + } else { + data.HealthStatus = m.Character.Name + " looks like they're about to die!" + } + } + + // Calculate carrying status + data.ItemCount = len(m.Character.Items) + if data.ItemCount == 0 { + data.CarryingStatus = "no" + } else if data.ItemCount < 4 { + data.CarryingStatus = "a few" + } else if data.ItemCount < 7 { + data.CarryingStatus = "several" + } else { + data.CarryingStatus = "lots of" + } + + return data +} + func ReduceHostility() { for groupName, group := range mobsHatePlayers { diff --git a/internal/rooms/rooms.go b/internal/rooms/rooms.go index 49906960..285d34f1 100644 --- a/internal/rooms/rooms.go +++ b/internal/rooms/rooms.go @@ -2198,7 +2198,6 @@ func (r *Room) Validate() error { } } - // Make sure all items are validated (and have uids) for i := range r.Items { r.Items[i].Validate() diff --git a/internal/templates/templatesfunctions.go b/internal/templates/templatesfunctions.go index 0a4fe1f4..a5a7ee2d 100644 --- a/internal/templates/templatesfunctions.go +++ b/internal/templates/templatesfunctions.go @@ -199,6 +199,9 @@ var ( "permadeath": func() bool { return bool(configs.GetGamePlayConfig().Death.PermaDeath) }, + "showEmptyEquipmentSlots": func() bool { + return configs.GetConfig().ShouldShowEmptyEquipmentSlots() + }, "zodiac": func(year int) string { return gametime.GetZodiac(year) }, diff --git a/internal/usercommands/go.go b/internal/usercommands/go.go index 5a8c1834..1c41c498 100644 --- a/internal/usercommands/go.go +++ b/internal/usercommands/go.go @@ -29,7 +29,7 @@ func Go(rest string, user *users.UserRecord, room *rooms.Room, flags events.Even return true, nil } - c := configs.GetTextFormatsConfig() + c := configs.GetUserInterfaceConfig().Formats isSneaking := user.Character.HasBuffFlag(buffs.Hidden) diff --git a/internal/usercommands/history.go b/internal/usercommands/history.go index 1a6e4a03..afccf2f1 100644 --- a/internal/usercommands/history.go +++ b/internal/usercommands/history.go @@ -21,7 +21,7 @@ func History(rest string, user *users.UserRecord, room *rooms.Room, flags events `%s`, } - tFormat := string(configs.GetTextFormatsConfig().TimeShort) + tFormat := string(configs.GetUserInterfaceConfig().Formats.TimeShort) for itm := range user.EventLog.Items { diff --git a/internal/usercommands/look.go b/internal/usercommands/look.go index 05d86ac6..01a2e21a 100644 --- a/internal/usercommands/look.go +++ b/internal/usercommands/look.go @@ -122,21 +122,16 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev ) } - descTxt, _ := templates.Process("character/description", &m.Character, user.UserId) - user.SendText(descTxt) - - itemNames := []string{} - for _, item := range m.Character.Items { - itemNames = append(itemNames, item.DisplayName()) - } - - invData := map[string]any{ - `Equipment`: &m.Character.Equipment, - `ItemNames`: itemNames, + // Use the new template-based look system for mobs + charmerName := "" + if m.Character.IsCharmed() && m.Character.Charmed.UserId > 0 { + if charmer := users.GetByUserId(m.Character.Charmed.UserId); charmer != nil { + charmerName = charmer.Character.Name + } } - - inventoryTxt, _ := templates.Process("character/inventory-look", invData, user.UserId) - user.SendText(inventoryTxt) + mobLookData := m.GetLookData(charmerName) + mobDescTxt, _ := templates.Process("descriptions/look-mob", mobLookData, user.UserId) + user.SendText(mobDescTxt) } user.SendText(statusTxt) @@ -283,14 +278,6 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev if foundItem { - user.SendText(``) - - user.SendText( - fmt.Sprintf(`You look at the %s %s:`, lookItem.DisplayName(), lookDestination), - ) - - user.SendText(``) - if !isSneaking { room.SendText( fmt.Sprintf(`%s is admiring their %s.`, user.Character.Name, lookItem.DisplayName()), @@ -298,10 +285,12 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev ) } - user.SendText( - lookItem.GetLongDescription(), - ) + // Use the new template-based look system for items + itemLookData := lookItem.GetLookData(lookDestination) + itemDescTxt, _ := templates.Process("descriptions/look-item", itemLookData, user.UserId) + user.SendText(``) + user.SendText(itemDescTxt) user.SendText(``) return true, nil diff --git a/internal/usercommands/set.go b/internal/usercommands/set.go index 6bfee604..fac62c1e 100644 --- a/internal/usercommands/set.go +++ b/internal/usercommands/set.go @@ -15,7 +15,7 @@ import ( func Set(rest string, user *users.UserRecord, room *rooms.Room, flags events.EventFlag) (bool, error) { args := util.SplitButRespectQuotes(strings.ToLower(rest)) - c := configs.GetTextFormatsConfig() + c := configs.GetUserInterfaceConfig().Formats if len(args) == 0 { diff --git a/internal/usercommands/start.go b/internal/usercommands/start.go index f99c43da..93135a18 100644 --- a/internal/usercommands/start.go +++ b/internal/usercommands/start.go @@ -211,7 +211,7 @@ func Start(rest string, user *users.UserRecord, room *rooms.Room, flags events.E // Tell the new room they have arrived destRoom.SendText( - fmt.Sprintf(configs.GetTextFormatsConfig().EnterRoomMessageWrapper.String(), + fmt.Sprintf(configs.GetUserInterfaceConfig().Formats.EnterRoomMessageWrapper.String(), fmt.Sprintf(`%s enters from somewhere.`, user.Character.Name), ), user.UserId, diff --git a/internal/users/inbox.go b/internal/users/inbox.go index d23e018c..753df06c 100644 --- a/internal/users/inbox.go +++ b/internal/users/inbox.go @@ -57,6 +57,6 @@ func (i *Inbox) Empty() { } func (m Message) DateString() string { - tFormat := string(configs.GetConfig().TextFormats.Time) + tFormat := string(configs.GetConfig().UserInterface.Formats.Time) return m.DateSent.Format(tFormat) } diff --git a/internal/users/userrecord.prompt.go b/internal/users/userrecord.prompt.go index 3a070879..3ca97f12 100644 --- a/internal/users/userrecord.prompt.go +++ b/internal/users/userrecord.prompt.go @@ -48,7 +48,7 @@ func (u *UserRecord) GetCommandPrompt() string { if len(promptOut) == 0 { if promptDefaultCompiled == `` { - promptDefaultCompiled = util.ConvertColorShortTags(configs.GetTextFormatsConfig().Prompt.String()) + promptDefaultCompiled = util.ConvertColorShortTags(configs.GetUserInterfaceConfig().Formats.Prompt.String()) } var customPrompt any = nil diff --git a/modules/gmcp/gmcp.Game.go b/modules/gmcp/gmcp.Game.go index 3d14336d..737dd1ba 100644 --- a/modules/gmcp/gmcp.Game.go +++ b/modules/gmcp/gmcp.Game.go @@ -40,7 +40,7 @@ func (g *GMCPGameModule) onJoinLeave(e events.Event) events.ListenerReturn { c := configs.GetConfig() - tFormat := string(c.TextFormats.Time) + tFormat := string(c.UserInterface.Formats.Time) whoPayload := `"Who": { "Players": [`