From 2be9936005f96a3c4a05d9462af88e62685ceb1a Mon Sep 17 00:00:00 2001 From: Morquin Date: Thu, 17 Jul 2025 08:57:36 +0200 Subject: [PATCH 1/6] Added template-based rendering for items and mobs Added: - ItemLookData struct with GetLookData() method for template data building - MobLookData struct with GetLookData() method for template data building - look-item.template for rendering item descriptions - look-mob.template for rendering mob descriptions - Screenreader variants for all new templates - Character template screenreader variants for description and inventory-look - Conditional equipment display in mob templates (only shows equipped items) Changed: - Look command to use templates for items and mobs instead of hardcoded strings - MobLookData Equipment field to pointer type for template method compatibility - Mob templates to only display equipment section when items are equipped Fixed: - Circular import issue by passing charmer name as parameter - Container reference removed from items package to avoid dependency issues --- .../description.screenreader.template | 8 +++ .../inventory-look.screenreader.template | 22 +++++++ .../look-item.screenreader.template | 37 +++++++++++ .../templates/descriptions/look-item.template | 37 +++++++++++ .../look-mob.screenreader.template | 44 +++++++++++++ .../templates/descriptions/look-mob.template | 41 ++++++++++++ internal/items/items.go | 63 +++++++++++++++++++ internal/mobs/mobs.go | 53 ++++++++++++++++ internal/usercommands/look.go | 39 +++++------- 9 files changed, 319 insertions(+), 25 deletions(-) create mode 100644 _datafiles/world/default/templates/character/description.screenreader.template create mode 100644 _datafiles/world/default/templates/character/inventory-look.screenreader.template create mode 100644 _datafiles/world/default/templates/descriptions/look-item.screenreader.template create mode 100644 _datafiles/world/default/templates/descriptions/look-item.template create mode 100644 _datafiles/world/default/templates/descriptions/look-mob.screenreader.template create mode 100644 _datafiles/world/default/templates/descriptions/look-mob.template 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..dcf4e897 --- /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 .IsReadable }} +You should probably read this. +{{- else if .IsDrinkable }} +You could probably drink this. +{{- else if .IsEdible }} +You could probably eat this. +{{- else if .IsLockpicks }} +These are used with the picklock command. +{{- else if .IsKey }} +When you find the right door, keys are added to your keyring automatically. +{{- else if .IsWearable }} +It looks like wearable {{ .ItemSpec.Type }} equipment. +{{- end }} +{{- if .IsWeapon }} +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 .IsCursed }} +It's CURSED! Once equipped, it cannot be removed without magical help. +{{- end }} +{{- if .IsEnchanted }} +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..616a51e9 --- /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 .IsReadable }} +- You should probably read this. +{{- else if .IsDrinkable }} +- You could probably drink this. +{{- else if .IsEdible }} +- You could probably eat this. +{{- else if .IsLockpicks }} +- These are used with the picklock command. +{{- else if .IsKey }} +- When you find the right door, keys are added to your keyring automatically. +{{- else if .IsWearable }} +- It looks like wearable {{ .ItemSpec.Type }} equipment. +{{- end }} +{{- if .IsWeapon }} +- 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 .IsCursed }} +- It's CURSED! Once equipped, it cannot be removed without magical help. +{{- end }} +{{- if .IsEnchanted }} +- 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..45778c9a --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template @@ -0,0 +1,44 @@ +{{/* Screenreader-friendly template for looking at mobs */}} +{{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} + +Description: +{{ .Character.GetDescription }} + +Health Status: {{ .HealthStatus }} +{{- $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 $hasEquipment }} + +Equipment: +{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}Weapon: {{ .Equipment.Weapon.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}Offhand: {{ .Equipment.Offhand.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}Head: {{ .Equipment.Head.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}Neck: {{ .Equipment.Neck.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}Body: {{ .Equipment.Body.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}Belt: {{ .Equipment.Belt.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}Gloves: {{ .Equipment.Gloves.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}Ring: {{ .Equipment.Ring.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}Legs: {{ .Equipment.Legs.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}Feet: {{ .Equipment.Feet.NameSimple }} +{{ 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..07c2e12d --- /dev/null +++ b/_datafiles/world/default/templates/descriptions/look-mob.template @@ -0,0 +1,41 @@ +{{/* Template for looking at mobs */}} +.: {{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} + ┌─ .:Description ────────────────────────────────────────────────────────────┐ + {{ splitstring .Character.GetDescription 72 " "}} + {{ .HealthStatus }} + └────────────────────────────────────────────────────────────────────────────┘ +{{- $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 $hasEquipment }} + ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ +{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }} Weapon: {{ .Equipment.Weapon.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }} Offhand: {{ .Equipment.Offhand.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }} Head: {{ .Equipment.Head.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }} Neck: {{ .Equipment.Neck.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }} Body: {{ .Equipment.Body.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }} Belt: {{ .Equipment.Belt.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }} Gloves: {{ .Equipment.Gloves.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }} Ring: {{ .Equipment.Ring.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }} Legs: {{ .Equipment.Legs.NameSimple }} +{{ end -}} +{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }} Feet: {{ .Equipment.Feet.NameSimple }} +{{ end }} └────────────────────────────────────────────────────────────────────────────┘ +{{- end }} + Carrying: {{ .CarryingStatus }} objects \ No newline at end of file diff --git a/internal/items/items.go b/internal/items/items.go index 79a35e48..e71f2025 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" + IsWeapon bool + IsReadable bool + IsDrinkable bool + IsEdible bool + IsLockpicks bool + IsKey bool + IsWearable bool + IsUsable bool + WeaponHands int + WeaponType string // "claws", "shooting", etc. + WaitRounds int + HasUses bool + UsesRemaining int + MaxUses int + IsCursed bool + IsEnchanted bool + EnchantLevel int +} + +// GetLookData returns data structure for template-based item descriptions +func (i *Item) GetLookData(location string) ItemLookData { + iSpec := i.GetSpec() + + data := ItemLookData{ + Item: i, + ItemSpec: iSpec, + Location: location, + IsWeapon: iSpec.Type == Weapon, + IsReadable: iSpec.Type == Readable, + IsDrinkable: iSpec.Subtype == Drinkable, + IsEdible: iSpec.Subtype == Edible, + IsLockpicks: iSpec.Type == Lockpicks, + IsKey: iSpec.Type == Key, + IsWearable: iSpec.Subtype == Wearable, + IsUsable: iSpec.Subtype == Usable, + HasUses: iSpec.Uses > 0, + UsesRemaining: i.Uses, + MaxUses: iSpec.Uses, + IsCursed: i.IsCursed(), + IsEnchanted: i.Enchantments > 0, + EnchantLevel: int(i.Enchantments), + } + + if data.IsWeapon { + 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/mobs/mobs.go b/internal/mobs/mobs.go index 24625fb6..46492121 100644 --- a/internal/mobs/mobs.go +++ b/internal/mobs/mobs.go @@ -708,6 +708,59 @@ 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 { + data := MobLookData{ + Mob: m, + Character: &m.Character, + Equipment: &m.Character.Equipment, + IsCharmed: m.Character.IsCharmed(), + IsShop: m.HasShop(), + CharmedBy: charmerName, + } + + // Calculate health status + 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/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 From 7d7abe1b5fb11de3e24a72ee85d1721bacb423af Mon Sep 17 00:00:00 2001 From: Morquin Date: Sat, 19 Jul 2025 00:04:44 +0200 Subject: [PATCH 2/6] Added configurable equipment display for mob template Added: - UserInterface config section with Formats and Display subsections - ShowEmptyEquipmentSlots config option to control equipment display - Config method ShouldShowEmptyEquipmentSlots() for checking the setting - Template function showEmptyEquipmentSlots to access config from templates - GetUserInterfaceConfig() helper function - "xxx is not wearing anything" message when mob has no equipment and showEmpty is false Changed: - Moved TextFormats config under UserInterface.Formats for better organization - Updated mob templates to respect ShowEmptyEquipmentSlots configuration - Equipment display now shows all slots when true (default), only equipped items when false Fixed: - Template conditionals to properly handle all equipment display scenarios - Both regular and screenreader templates now correctly show equipment based on config --- _datafiles/config.yaml | 57 +++++++++------ .../look-mob.screenreader.template | 56 ++++++++++++++- .../templates/descriptions/look-mob.template | 53 +++++++++++++- internal/configs/config.userinterface.go | 72 +++++++++++++++++++ internal/configs/configs.go | 42 +++++++---- internal/mapper/mapper.go | 8 +-- internal/rooms/rooms.go | 1 - internal/templates/templatesfunctions.go | 3 + 8 files changed, 248 insertions(+), 44 deletions(-) create mode 100644 internal/configs/config.userinterface.go diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index d42ea917..d8ed5f11 100755 --- a/_datafiles/config.yaml +++ b/_datafiles/config.yaml @@ -326,29 +326,40 @@ Integrations: ################################################################################ # -# 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 ################################################################################ # diff --git a/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template index 45778c9a..f645eb31 100644 --- a/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template +++ b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template @@ -5,6 +5,7 @@ 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 }} @@ -16,7 +17,59 @@ Health Status: {{ .HealthStatus }} {{- 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 $hasEquipment }} +{{- if not $hasEquipment }} +{{- if not $showEmpty }} + +{{ .Character.Name }} is not wearing anything. +{{- else }} + +Equipment: +{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }}Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }}Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }}Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }}Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }}Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }}Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }}Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }}Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }}Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }}Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- end }} +{{- else }} +{{- if $showEmpty }} + +Equipment: +{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }}Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }}Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }}Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }}Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }}Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }}Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }}Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }}Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }}Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }}Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}nothing{{ end }} +{{ end -}} +{{- else }} Equipment: {{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}Weapon: {{ .Equipment.Weapon.NameSimple }} @@ -40,5 +93,6 @@ Equipment: {{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}Feet: {{ .Equipment.Feet.NameSimple }} {{ 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 index 07c2e12d..ed26f582 100644 --- a/_datafiles/world/default/templates/descriptions/look-mob.template +++ b/_datafiles/world/default/templates/descriptions/look-mob.template @@ -4,6 +4,7 @@ {{ 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 }} @@ -15,7 +16,56 @@ {{- 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 $hasEquipment }} +{{- if not $hasEquipment }} +{{- if not $showEmpty }} + {{ .Character.Name }} is not wearing anything. +{{- else }} + ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ +{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end }} └────────────────────────────────────────────────────────────────────────────┘ +{{- end }} +{{- else }} +{{- if $showEmpty }} + ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ +{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end -}} +{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}-nothing-{{ end }} +{{ end }} └────────────────────────────────────────────────────────────────────────────┘ +{{- else }} ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ {{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }} Weapon: {{ .Equipment.Weapon.NameSimple }} {{ end -}} @@ -37,5 +87,6 @@ {{ end -}} {{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }} Feet: {{ .Equipment.Feet.NameSimple }} {{ end }} └────────────────────────────────────────────────────────────────────────────┘ +{{- end }} {{- end }} Carrying: {{ .CarryingStatus }} objects \ No newline at end of file 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..dcce4a8c 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -34,20 +34,21 @@ 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"` + TextFormats TextFormats `yaml:"TextFormats"` // Deprecated: Use UserInterface.Formats + 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,6 +192,19 @@ func (c *Config) Validate() { c.FilePaths.Validate() c.GamePlay.Validate() c.Integrations.Validate() + + // Migrate old TextFormats to UserInterface.Formats if needed + if c.TextFormats.Prompt != "" && c.UserInterface.Formats.Prompt == "" { + c.UserInterface.Formats = UserInterfaceFormats{ + Prompt: c.TextFormats.Prompt, + EnterRoomMessageWrapper: c.TextFormats.EnterRoomMessageWrapper, + ExitRoomMessageWrapper: c.TextFormats.ExitRoomMessageWrapper, + Time: c.TextFormats.Time, + TimeShort: c.TextFormats.TimeShort, + } + } + + c.UserInterface.Validate() c.TextFormats.Validate() c.Translation.Validate() c.Network.Validate() 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/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) }, From 903f0b09713e0b32606433f65fe0eaa79871f3ef Mon Sep 17 00:00:00 2001 From: Morquin Date: Sat, 19 Jul 2025 23:43:13 +0200 Subject: [PATCH 3/6] Refactored templates and improved defensive programming Added: - Nil checks in Item.GetLookData() to handle nil items - Nil checks in Mob.GetLookData() to handle nil mobs - Division-by-zero protection for mob health calculations Changed: - Consolidated equipment display logic in mob templates from 3 blocks to 1 - Used template variables to control equipment slot visibility - Reduced code duplication in both standard and screenreader templates Removed: - TextFormats to UserInterface.Formats migration logic - Redundant equipment display blocks in mob templates Breaking Changes: - Users with custom config files must manually update TextFormats to UserInterface.Formats structure --- .../look-mob.screenreader.template | 113 +++++++----------- .../templates/descriptions/look-mob.template | 110 +++++++---------- internal/configs/configs.go | 12 -- internal/items/items.go | 7 ++ internal/mobs/mobs.go | 35 ++++-- 5 files changed, 115 insertions(+), 162 deletions(-) diff --git a/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template index f645eb31..d32be275 100644 --- a/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template +++ b/_datafiles/world/default/templates/descriptions/look-mob.screenreader.template @@ -1,5 +1,5 @@ {{/* Screenreader-friendly template for looking at mobs */}} -{{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} +Name: {{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }} Description: {{ .Character.GetDescription }} @@ -17,81 +17,52 @@ Health Status: {{ .HealthStatus }} {{- 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 not $hasEquipment }} -{{- if not $showEmpty }} +{{- if and (not $hasEquipment) (not $showEmpty) }} {{ .Character.Name }} is not wearing anything. -{{- else }} +{{- else if or $hasEquipment $showEmpty }} Equipment: -{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }}Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }}Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }}Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }}Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }}Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }}Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }}Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }}Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }}Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }}Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} +{{- $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 }} -{{- else }} -{{- if $showEmpty }} - -Equipment: -{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }}Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }}Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }}Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }}Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }}Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }}Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }}Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }}Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }}Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }}Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}nothing{{ end }} -{{ end -}} -{{- else }} - -Equipment: -{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}Weapon: {{ .Equipment.Weapon.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}Offhand: {{ .Equipment.Offhand.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}Head: {{ .Equipment.Head.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}Neck: {{ .Equipment.Neck.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}Body: {{ .Equipment.Body.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}Belt: {{ .Equipment.Belt.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}Gloves: {{ .Equipment.Gloves.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}Ring: {{ .Equipment.Ring.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}Legs: {{ .Equipment.Legs.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}Feet: {{ .Equipment.Feet.NameSimple }} -{{ 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 }} diff --git a/_datafiles/world/default/templates/descriptions/look-mob.template b/_datafiles/world/default/templates/descriptions/look-mob.template index ed26f582..b10dc025 100644 --- a/_datafiles/world/default/templates/descriptions/look-mob.template +++ b/_datafiles/world/default/templates/descriptions/look-mob.template @@ -16,77 +16,51 @@ {{- 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 not $hasEquipment }} -{{- if not $showEmpty }} +{{- if and (not $hasEquipment) (not $showEmpty) }} {{ .Character.Name }} is not wearing anything. -{{- else }} +{{- else if or $hasEquipment $showEmpty }} ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ -{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end }} └────────────────────────────────────────────────────────────────────────────┘ +{{- $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 }} -{{- else }} -{{- if $showEmpty }} - ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ -{{ if or (and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0)) (and $showEmpty (not .Equipment.Weapon.IsDisabled)) }} Weapon: {{ if gt .Equipment.Weapon.ItemId 0 }}{{ .Equipment.Weapon.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0)) (and $showEmpty (not .Equipment.Offhand.IsDisabled)) }} Offhand: {{ if gt .Equipment.Offhand.ItemId 0 }}{{ .Equipment.Offhand.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0)) (and $showEmpty (not .Equipment.Head.IsDisabled)) }} Head: {{ if gt .Equipment.Head.ItemId 0 }}{{ .Equipment.Head.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0)) (and $showEmpty (not .Equipment.Neck.IsDisabled)) }} Neck: {{ if gt .Equipment.Neck.ItemId 0 }}{{ .Equipment.Neck.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0)) (and $showEmpty (not .Equipment.Body.IsDisabled)) }} Body: {{ if gt .Equipment.Body.ItemId 0 }}{{ .Equipment.Body.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0)) (and $showEmpty (not .Equipment.Belt.IsDisabled)) }} Belt: {{ if gt .Equipment.Belt.ItemId 0 }}{{ .Equipment.Belt.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0)) (and $showEmpty (not .Equipment.Gloves.IsDisabled)) }} Gloves: {{ if gt .Equipment.Gloves.ItemId 0 }}{{ .Equipment.Gloves.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0)) (and $showEmpty (not .Equipment.Ring.IsDisabled)) }} Ring: {{ if gt .Equipment.Ring.ItemId 0 }}{{ .Equipment.Ring.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0)) (and $showEmpty (not .Equipment.Legs.IsDisabled)) }} Legs: {{ if gt .Equipment.Legs.ItemId 0 }}{{ .Equipment.Legs.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end -}} -{{- if or (and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0)) (and $showEmpty (not .Equipment.Feet.IsDisabled)) }} Feet: {{ if gt .Equipment.Feet.ItemId 0 }}{{ .Equipment.Feet.NameSimple }}{{ else }}-nothing-{{ end }} -{{ end }} └────────────────────────────────────────────────────────────────────────────┘ -{{- else }} - ┌─ .:Equipment ──────────────────────────────────────────────────────────────┐ -{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }} Weapon: {{ .Equipment.Weapon.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }} Offhand: {{ .Equipment.Offhand.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }} Head: {{ .Equipment.Head.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }} Neck: {{ .Equipment.Neck.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }} Body: {{ .Equipment.Body.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }} Belt: {{ .Equipment.Belt.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }} Gloves: {{ .Equipment.Gloves.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }} Ring: {{ .Equipment.Ring.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }} Legs: {{ .Equipment.Legs.NameSimple }} -{{ end -}} -{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }} Feet: {{ .Equipment.Feet.NameSimple }} -{{ 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/configs.go b/internal/configs/configs.go index dcce4a8c..d2c6e28b 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -192,18 +192,6 @@ func (c *Config) Validate() { c.FilePaths.Validate() c.GamePlay.Validate() c.Integrations.Validate() - - // Migrate old TextFormats to UserInterface.Formats if needed - if c.TextFormats.Prompt != "" && c.UserInterface.Formats.Prompt == "" { - c.UserInterface.Formats = UserInterfaceFormats{ - Prompt: c.TextFormats.Prompt, - EnterRoomMessageWrapper: c.TextFormats.EnterRoomMessageWrapper, - ExitRoomMessageWrapper: c.TextFormats.ExitRoomMessageWrapper, - Time: c.TextFormats.Time, - TimeShort: c.TextFormats.TimeShort, - } - } - c.UserInterface.Validate() c.TextFormats.Validate() c.Translation.Validate() diff --git a/internal/items/items.go b/internal/items/items.go index e71f2025..223103ea 100644 --- a/internal/items/items.go +++ b/internal/items/items.go @@ -194,6 +194,13 @@ type ItemLookData struct { // 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, + } + } + iSpec := i.GetSpec() data := ItemLookData{ diff --git a/internal/mobs/mobs.go b/internal/mobs/mobs.go index 46492121..483a4ae9 100644 --- a/internal/mobs/mobs.go +++ b/internal/mobs/mobs.go @@ -723,6 +723,15 @@ type MobLookData struct { // 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, @@ -732,18 +741,22 @@ func (m *Mob) GetLookData(charmerName string) MobLookData { CharmedBy: charmerName, } - // Calculate health status - 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." + // 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 { - data.HealthStatus = m.Character.Name + " looks like they're about to die!" + 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 From e84b47f4c5494bfb43d587fefe471d09a46c0de1 Mon Sep 17 00:00:00 2001 From: Morquin Date: Mon, 21 Jul 2025 20:36:37 +0200 Subject: [PATCH 4/6] Setting correct default settings in config.yaml --- _datafiles/config.yaml | 122 ++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index d8ed5f11..03d12d49 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,8 +320,7 @@ 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: "" ################################################################################ # @@ -337,15 +335,15 @@ UserInterface: # - 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}:' + 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' + Time: "Monday, 02-Jan-2006 3:04:05PM" # - TimeShort - # Same as Time, but shorter form - TimeShort: 'Jan 2 ''06 3:04PM' + TimeShort: "Jan 2 '06 3:04PM" # - EnterRoomMessageWrapper - # Decorate entrance text with this. Put a %s where the message should be. EnterRoomMessageWrapper: " >>> %s\n" @@ -370,15 +368,15 @@ UserInterface: 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" ################################################################################ # @@ -422,7 +420,8 @@ Network: # - TimeoutMods - # Whether Admin/Mod users get timed out when reaching MaxIdleSeconds # If set to false, Admins & Mods never get force disconnected. - TimeoutMods: false + TimeoutMods: + false # - 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). @@ -499,7 +498,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 @@ -513,39 +512,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" ################################################################################ # @@ -566,7 +565,6 @@ Roles: builder: ["room.info", "build"] helper: ["paz", "teleport.playername", "locate"] - ################################################################################ # # Modules From 7ad447266088db1e175832ed37ab4781241d9d88 Mon Sep 17 00:00:00 2001 From: Morquin Date: Fri, 8 Aug 2025 11:06:28 +0200 Subject: [PATCH 5/6] Refactored ItemLookData to use string-based type comparisons Changed: - Replaced boolean fields (IsWeapon, IsReadable, etc.) with Type and Subtype strings - Replaced IsCursed/IsEnchanted booleans with extensible Adjectives map - Updated templates to use string equality checks (e.g., {{if eq .Type "readable"}}) Fixed: - Corrected TimeoutMods YAML formatting from multi-line to single-line --- _datafiles/config.yaml | 5 +-- .../look-item.screenreader.template | 18 ++++----- .../templates/descriptions/look-item.template | 18 ++++----- internal/items/items.go | 39 ++++++++----------- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index 03d12d49..b18c7d8a 100755 --- a/_datafiles/config.yaml +++ b/_datafiles/config.yaml @@ -420,9 +420,8 @@ Network: # - TimeoutMods - # Whether Admin/Mod users get timed out when reaching MaxIdleSeconds # If set to false, Admins & Mods never get force disconnected. - TimeoutMods: - false - # - ZombieSeconds - + TimeoutMods: false + # - 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 diff --git a/_datafiles/world/default/templates/descriptions/look-item.screenreader.template b/_datafiles/world/default/templates/descriptions/look-item.screenreader.template index dcf4e897..5e81a101 100644 --- a/_datafiles/world/default/templates/descriptions/look-item.screenreader.template +++ b/_datafiles/world/default/templates/descriptions/look-item.screenreader.template @@ -2,20 +2,20 @@ You look at the {{ .Item.Name }} {{ .Location }}: {{ .ItemSpec.Description }} -{{- if .IsReadable }} +{{- if eq .Type "readable" }} You should probably read this. -{{- else if .IsDrinkable }} +{{- else if eq .Subtype "drinkable" }} You could probably drink this. -{{- else if .IsEdible }} +{{- else if eq .Subtype "edible" }} You could probably eat this. -{{- else if .IsLockpicks }} +{{- else if eq .Type "lockpicks" }} These are used with the picklock command. -{{- else if .IsKey }} +{{- else if eq .Type "key" }} When you find the right door, keys are added to your keyring automatically. -{{- else if .IsWearable }} +{{- else if eq .Subtype "wearable" }} It looks like wearable {{ .ItemSpec.Type }} equipment. {{- end }} -{{- if .IsWeapon }} +{{- 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. @@ -29,9 +29,9 @@ It requires an extra {{ .WaitRounds }} round(s) between attacks. {{- if .HasUses }} It has {{ .UsesRemaining }}/{{ .MaxUses }} uses remaining. {{- end }} -{{- if .IsCursed }} +{{- if index .Adjectives "cursed" }} It's CURSED! Once equipped, it cannot be removed without magical help. {{- end }} -{{- if .IsEnchanted }} +{{- 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 index 616a51e9..b8690d2d 100644 --- a/_datafiles/world/default/templates/descriptions/look-item.template +++ b/_datafiles/world/default/templates/descriptions/look-item.template @@ -2,20 +2,20 @@ You look at the {{ .Item.DisplayName }} {{ .Location }}: {{ .ItemSpec.Description }} -{{- if .IsReadable }} +{{- if eq .Type "readable" }} - You should probably read this. -{{- else if .IsDrinkable }} +{{- else if eq .Subtype "drinkable" }} - You could probably drink this. -{{- else if .IsEdible }} +{{- else if eq .Subtype "edible" }} - You could probably eat this. -{{- else if .IsLockpicks }} +{{- else if eq .Type "lockpicks" }} - These are used with the picklock command. -{{- else if .IsKey }} +{{- else if eq .Type "key" }} - When you find the right door, keys are added to your keyring automatically. -{{- else if .IsWearable }} +{{- else if eq .Subtype "wearable" }} - It looks like wearable {{ .ItemSpec.Type }} equipment. {{- end }} -{{- if .IsWeapon }} +{{- 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. @@ -29,9 +29,9 @@ You look at the {{ .Item.DisplayName }} {{ .Location }}: {{- if .HasUses }} - It has {{ .UsesRemaining }}/{{ .MaxUses }} uses remaining. {{- end }} -{{- if .IsCursed }} +{{- if index .Adjectives "cursed" }} - It's CURSED! Once equipped, it cannot be removed without magical help. {{- end }} -{{- if .IsEnchanted }} +{{- if index .Adjectives "enchanted" }} - It glows with a magical aura (enchantment level {{ .EnchantLevel }}). {{- end }} \ No newline at end of file diff --git a/internal/items/items.go b/internal/items/items.go index 223103ea..c58f4c54 100644 --- a/internal/items/items.go +++ b/internal/items/items.go @@ -172,23 +172,16 @@ func (i *Item) Validate() { type ItemLookData struct { Item *Item ItemSpec ItemSpec - Location string // "in your backpack", "you are wearing" - IsWeapon bool - IsReadable bool - IsDrinkable bool - IsEdible bool - IsLockpicks bool - IsKey bool - IsWearable bool - IsUsable bool + 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 - IsCursed bool - IsEnchanted bool EnchantLevel int } @@ -197,7 +190,10 @@ func (i *Item) GetLookData(location string) ItemLookData { // Defensive check for nil item if i == nil { return ItemLookData{ - Location: location, + Location: location, + Type: "", + Subtype: "", + Adjectives: make(map[string]bool), } } @@ -207,23 +203,20 @@ func (i *Item) GetLookData(location string) ItemLookData { Item: i, ItemSpec: iSpec, Location: location, - IsWeapon: iSpec.Type == Weapon, - IsReadable: iSpec.Type == Readable, - IsDrinkable: iSpec.Subtype == Drinkable, - IsEdible: iSpec.Subtype == Edible, - IsLockpicks: iSpec.Type == Lockpicks, - IsKey: iSpec.Type == Key, - IsWearable: iSpec.Subtype == Wearable, - IsUsable: iSpec.Subtype == Usable, + Type: string(iSpec.Type), + Subtype: string(iSpec.Subtype), + Adjectives: make(map[string]bool), HasUses: iSpec.Uses > 0, UsesRemaining: i.Uses, MaxUses: iSpec.Uses, - IsCursed: i.IsCursed(), - IsEnchanted: i.Enchantments > 0, EnchantLevel: int(i.Enchantments), } - if data.IsWeapon { + // 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 From 975264b8718387873733d54b7dbafca093c8e720 Mon Sep 17 00:00:00 2001 From: Morquin Date: Sun, 10 Aug 2025 01:03:20 +0200 Subject: [PATCH 6/6] Removed deprecated TextFormats config structure Removed: - TextFormats struct definition and field from main Config struct - TextFormats.Validate() call from config validation process - config.textformats.go file containing backwards compatibility function - Duplicate configuration entries in server output Changed: - All references from GetTextFormatsConfig() to GetUserInterfaceConfig().Formats - Direct TextFormats field access to UserInterface.Formats field access - Config structure to use single source of truth for format settings Fixed: - Duplicate prompt settings appearing in server set command output - Confusion from having both TextFormats.Prompt and UserInterface.Formats.Prompt - Inconsistent config access patterns across the codebase Updated files: - internal/configs/configs.go - removed TextFormats field and validation - internal/mobcommands/go.go - updated to use GetUserInterfaceConfig().Formats - internal/usercommands/go.go - updated to use GetUserInterfaceConfig().Formats - internal/usercommands/history.go - updated to use GetUserInterfaceConfig().Formats - internal/usercommands/set.go - updated to use GetUserInterfaceConfig().Formats - internal/usercommands/start.go - updated to use GetUserInterfaceConfig().Formats - internal/users/inbox.go - updated direct access to UserInterface.Formats - internal/users/userrecord.prompt.go - updated to use GetUserInterfaceConfig().Formats - modules/gmcp/gmcp.Game.go - updated direct access to UserInterface.Formats --- internal/configs/config.textformats.go | 55 -------------------------- internal/configs/configs.go | 2 - internal/mobcommands/go.go | 4 +- internal/usercommands/go.go | 2 +- internal/usercommands/history.go | 2 +- internal/usercommands/set.go | 2 +- internal/usercommands/start.go | 2 +- internal/users/inbox.go | 2 +- internal/users/userrecord.prompt.go | 2 +- modules/gmcp/gmcp.Game.go | 2 +- 10 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 internal/configs/config.textformats.go 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/configs.go b/internal/configs/configs.go index d2c6e28b..a45e3813 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -42,7 +42,6 @@ type Config struct { GamePlay GamePlay `yaml:"GamePlay"` UserInterface UserInterface `yaml:"UserInterface"` Integrations Integrations `yaml:"Integrations"` - TextFormats TextFormats `yaml:"TextFormats"` // Deprecated: Use UserInterface.Formats Translation Translation `yaml:"Translation"` Network Network `yaml:"Network"` Scripting Scripting `yaml:"Scripting"` @@ -193,7 +192,6 @@ func (c *Config) Validate() { c.GamePlay.Validate() c.Integrations.Validate() c.UserInterface.Validate() - c.TextFormats.Validate() c.Translation.Validate() c.Network.Validate() c.Scripting.Validate() 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/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/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": [`