Skip to content

Commit c5eb01f

Browse files
committed
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
1 parent 5ec6e4f commit c5eb01f

File tree

9 files changed

+319
-25
lines changed

9 files changed

+319
-25
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{{ .Name }} ({{ .AlignmentName }})
2+
{{- $tnl := .XPTNL -}}
3+
{{- $pct := (pct .Experience $tnl ) -}}
4+
5+
Description:
6+
{{ .GetDescription }}
7+
8+
Health Status: {{ .GetHealthAppearance }}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Equipment:
2+
{{ if not .Equipment.Weapon.IsDisabled }}Weapon: {{ .Equipment.Weapon.NameSimple }}
3+
{{ end -}}
4+
{{- if not .Equipment.Offhand.IsDisabled }}Offhand: {{ .Equipment.Offhand.NameSimple }}
5+
{{ end -}}
6+
{{- if not .Equipment.Head.IsDisabled }}Head: {{ .Equipment.Head.NameSimple }}
7+
{{ end -}}
8+
{{- if not .Equipment.Neck.IsDisabled }}Neck: {{ .Equipment.Neck.NameSimple }}
9+
{{ end -}}
10+
{{- if not .Equipment.Body.IsDisabled }}Body: {{ .Equipment.Body.NameSimple }}
11+
{{ end -}}
12+
{{- if not .Equipment.Belt.IsDisabled }}Belt: {{ .Equipment.Belt.NameSimple }}
13+
{{ end -}}
14+
{{- if not .Equipment.Gloves.IsDisabled }}Gloves: {{ .Equipment.Gloves.NameSimple }}
15+
{{ end -}}
16+
{{- if not .Equipment.Ring.IsDisabled }}Ring: {{ .Equipment.Ring.NameSimple }}
17+
{{ end -}}
18+
{{- if not .Equipment.Legs.IsDisabled }}Legs: {{ .Equipment.Legs.NameSimple }}
19+
{{ end -}}
20+
{{- if not .Equipment.Feet.IsDisabled }}Feet: {{ .Equipment.Feet.NameSimple }}
21+
{{ end }}
22+
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
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{{/* Screenreader-friendly template for looking at items */}}
2+
You look at the {{ .Item.Name }} {{ .Location }}:
3+
4+
{{ .ItemSpec.Description }}
5+
{{- if .IsReadable }}
6+
You should probably read this.
7+
{{- else if .IsDrinkable }}
8+
You could probably drink this.
9+
{{- else if .IsEdible }}
10+
You could probably eat this.
11+
{{- else if .IsLockpicks }}
12+
These are used with the picklock command.
13+
{{- else if .IsKey }}
14+
When you find the right door, keys are added to your keyring automatically.
15+
{{- else if .IsWearable }}
16+
It looks like wearable {{ .ItemSpec.Type }} equipment.
17+
{{- end }}
18+
{{- if .IsWeapon }}
19+
It looks like a {{ .WeaponHands }}-Handed weapon.
20+
{{- if eq .WeaponType "claws" }}
21+
It looks like a claws weapon. These can be dual wielded without training.
22+
{{- else if eq .WeaponType "shooting" }}
23+
This can fired into adjacent areas. (help shoot)
24+
{{- end }}
25+
{{- if gt .WaitRounds 0 }}
26+
It requires an extra {{ .WaitRounds }} round(s) between attacks.
27+
{{- end }}
28+
{{- end }}
29+
{{- if .HasUses }}
30+
It has {{ .UsesRemaining }}/{{ .MaxUses }} uses remaining.
31+
{{- end }}
32+
{{- if .IsCursed }}
33+
It's CURSED! Once equipped, it cannot be removed without magical help.
34+
{{- end }}
35+
{{- if .IsEnchanted }}
36+
It glows with a magical aura (enchantment level {{ .EnchantLevel }}).
37+
{{- end }}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{{/* Template for looking at items */}}
2+
You look at the <ansi fg="item">{{ .Item.DisplayName }}</ansi> {{ .Location }}:
3+
4+
{{ .ItemSpec.Description }}
5+
{{- if .IsReadable }}
6+
- You should probably <ansi fg="command">read</ansi> this.
7+
{{- else if .IsDrinkable }}
8+
- You could probably <ansi fg="command">drink</ansi> this.
9+
{{- else if .IsEdible }}
10+
- You could probably <ansi fg="command">eat</ansi> this.
11+
{{- else if .IsLockpicks }}
12+
- These are used with the <ansi fg="command">picklock</ansi> command.
13+
{{- else if .IsKey }}
14+
- When you find the right door, keys are added to your keyring automatically.
15+
{{- else if .IsWearable }}
16+
- It looks like wearable <ansi fg="magenta">{{ .ItemSpec.Type }}</ansi> equipment.
17+
{{- end }}
18+
{{- if .IsWeapon }}
19+
- It looks like a <ansi fg="yellow">{{ .WeaponHands }}-Handed</ansi> weapon.
20+
{{- if eq .WeaponType "claws" }}
21+
- It looks like a <ansi fg="yellow">claws</ansi> weapon. These can be dual wielded without training.
22+
{{- else if eq .WeaponType "shooting" }}
23+
- This can fired into adjacent areas. (<ansi fg="command">help shoot</ansi>)
24+
{{- end }}
25+
{{- if gt .WaitRounds 0 }}
26+
- It requires an extra <ansi fg="red">{{ .WaitRounds }}</ansi> round(s) between attacks.
27+
{{- end }}
28+
{{- end }}
29+
{{- if .HasUses }}
30+
- It has <ansi fg="yellow">{{ .UsesRemaining }}/{{ .MaxUses }}</ansi> uses remaining.
31+
{{- end }}
32+
{{- if .IsCursed }}
33+
- It's <ansi fg="red-bold">CURSED!</ansi> Once equipped, it cannot be removed without magical help.
34+
{{- end }}
35+
{{- if .IsEnchanted }}
36+
- It glows with a <ansi fg="enchanted">magical aura</ansi> (enchantment level {{ .EnchantLevel }}).
37+
{{- end }}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{{/* Screenreader-friendly template for looking at mobs */}}
2+
{{ .Character.Name }}{{ if .IsCharmed }} (charmed by {{ .CharmedBy }}){{ end }}{{ if .IsShop }} (merchant){{ end }}
3+
4+
Description:
5+
{{ .Character.GetDescription }}
6+
7+
Health Status: {{ .HealthStatus }}
8+
{{- $hasEquipment := false }}
9+
{{- if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
10+
{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
11+
{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
12+
{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
13+
{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
14+
{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
15+
{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
16+
{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
17+
{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
18+
{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
19+
{{- if $hasEquipment }}
20+
21+
Equipment:
22+
{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}Weapon: {{ .Equipment.Weapon.NameSimple }}
23+
{{ end -}}
24+
{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}Offhand: {{ .Equipment.Offhand.NameSimple }}
25+
{{ end -}}
26+
{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}Head: {{ .Equipment.Head.NameSimple }}
27+
{{ end -}}
28+
{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}Neck: {{ .Equipment.Neck.NameSimple }}
29+
{{ end -}}
30+
{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}Body: {{ .Equipment.Body.NameSimple }}
31+
{{ end -}}
32+
{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}Belt: {{ .Equipment.Belt.NameSimple }}
33+
{{ end -}}
34+
{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}Gloves: {{ .Equipment.Gloves.NameSimple }}
35+
{{ end -}}
36+
{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}Ring: {{ .Equipment.Ring.NameSimple }}
37+
{{ end -}}
38+
{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}Legs: {{ .Equipment.Legs.NameSimple }}
39+
{{ end -}}
40+
{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}Feet: {{ .Equipment.Feet.NameSimple }}
41+
{{ end -}}
42+
{{- end }}
43+
44+
Carrying: {{ .CarryingStatus }} objects
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{{/* Template for looking at mobs */}}
2+
<ansi fg="black-bold">.:</ansi> <ansi fg="mobname">{{ .Character.Name }}</ansi>{{ if .IsCharmed }} (<ansi fg="yellow">charmed by {{ .CharmedBy }}</ansi>){{ end }}{{ if .IsShop }} (<ansi fg="green">merchant</ansi>){{ end }}
3+
┌─ <ansi fg="black-bold">.:</ansi><ansi fg="20">Description</ansi> ────────────────────────────────────────────────────────────┐
4+
{{ splitstring .Character.GetDescription 72 " "}}
5+
{{ .HealthStatus }}
6+
└────────────────────────────────────────────────────────────────────────────┘
7+
{{- $hasEquipment := false }}
8+
{{- if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
9+
{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
10+
{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
11+
{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
12+
{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
13+
{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
14+
{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
15+
{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
16+
{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
17+
{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }}{{ $hasEquipment = true }}{{ end }}
18+
{{- if $hasEquipment }}
19+
┌─ <ansi fg="black-bold">.:</ansi><ansi fg="20">Equipment</ansi> ──────────────────────────────────────────────────────────────┐
20+
{{ if and (not .Equipment.Weapon.IsDisabled) (gt .Equipment.Weapon.ItemId 0) }} <ansi fg="yellow">Weapon: </ansi><ansi fg="itemname">{{ .Equipment.Weapon.NameSimple }}</ansi>
21+
{{ end -}}
22+
{{- if and (not .Equipment.Offhand.IsDisabled) (gt .Equipment.Offhand.ItemId 0) }} <ansi fg="yellow">Offhand: </ansi><ansi fg="itemname">{{ .Equipment.Offhand.NameSimple }}</ansi>
23+
{{ end -}}
24+
{{- if and (not .Equipment.Head.IsDisabled) (gt .Equipment.Head.ItemId 0) }} <ansi fg="yellow">Head: </ansi><ansi fg="itemname">{{ .Equipment.Head.NameSimple }}</ansi>
25+
{{ end -}}
26+
{{- if and (not .Equipment.Neck.IsDisabled) (gt .Equipment.Neck.ItemId 0) }} <ansi fg="yellow">Neck: </ansi><ansi fg="itemname">{{ .Equipment.Neck.NameSimple }}</ansi>
27+
{{ end -}}
28+
{{- if and (not .Equipment.Body.IsDisabled) (gt .Equipment.Body.ItemId 0) }} <ansi fg="yellow">Body: </ansi><ansi fg="itemname">{{ .Equipment.Body.NameSimple }}</ansi>
29+
{{ end -}}
30+
{{- if and (not .Equipment.Belt.IsDisabled) (gt .Equipment.Belt.ItemId 0) }} <ansi fg="yellow">Belt: </ansi><ansi fg="itemname">{{ .Equipment.Belt.NameSimple }}</ansi>
31+
{{ end -}}
32+
{{- if and (not .Equipment.Gloves.IsDisabled) (gt .Equipment.Gloves.ItemId 0) }} <ansi fg="yellow">Gloves: </ansi><ansi fg="itemname">{{ .Equipment.Gloves.NameSimple }}</ansi>
33+
{{ end -}}
34+
{{- if and (not .Equipment.Ring.IsDisabled) (gt .Equipment.Ring.ItemId 0) }} <ansi fg="yellow">Ring: </ansi><ansi fg="itemname">{{ .Equipment.Ring.NameSimple }}</ansi>
35+
{{ end -}}
36+
{{- if and (not .Equipment.Legs.IsDisabled) (gt .Equipment.Legs.ItemId 0) }} <ansi fg="yellow">Legs: </ansi><ansi fg="itemname">{{ .Equipment.Legs.NameSimple }}</ansi>
37+
{{ end -}}
38+
{{- if and (not .Equipment.Feet.IsDisabled) (gt .Equipment.Feet.ItemId 0) }} <ansi fg="yellow">Feet: </ansi><ansi fg="itemname">{{ .Equipment.Feet.NameSimple }}</ansi>
39+
{{ end }} └────────────────────────────────────────────────────────────────────────────┘
40+
{{- end }}
41+
Carrying: {{ .CarryingStatus }} objects

internal/items/items.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,69 @@ func (i *Item) Validate() {
168168

169169
}
170170

171+
// ItemLookData contains all the data needed for the look-item template
172+
type ItemLookData struct {
173+
Item *Item
174+
ItemSpec ItemSpec
175+
Location string // "in your backpack", "you are wearing"
176+
IsWeapon bool
177+
IsReadable bool
178+
IsDrinkable bool
179+
IsEdible bool
180+
IsLockpicks bool
181+
IsKey bool
182+
IsWearable bool
183+
IsUsable bool
184+
WeaponHands int
185+
WeaponType string // "claws", "shooting", etc.
186+
WaitRounds int
187+
HasUses bool
188+
UsesRemaining int
189+
MaxUses int
190+
IsCursed bool
191+
IsEnchanted bool
192+
EnchantLevel int
193+
}
194+
195+
// GetLookData returns data structure for template-based item descriptions
196+
func (i *Item) GetLookData(location string) ItemLookData {
197+
iSpec := i.GetSpec()
198+
199+
data := ItemLookData{
200+
Item: i,
201+
ItemSpec: iSpec,
202+
Location: location,
203+
IsWeapon: iSpec.Type == Weapon,
204+
IsReadable: iSpec.Type == Readable,
205+
IsDrinkable: iSpec.Subtype == Drinkable,
206+
IsEdible: iSpec.Subtype == Edible,
207+
IsLockpicks: iSpec.Type == Lockpicks,
208+
IsKey: iSpec.Type == Key,
209+
IsWearable: iSpec.Subtype == Wearable,
210+
IsUsable: iSpec.Subtype == Usable,
211+
HasUses: iSpec.Uses > 0,
212+
UsesRemaining: i.Uses,
213+
MaxUses: iSpec.Uses,
214+
IsCursed: i.IsCursed(),
215+
IsEnchanted: i.Enchantments > 0,
216+
EnchantLevel: int(i.Enchantments),
217+
}
218+
219+
if data.IsWeapon {
220+
data.WeaponHands = iSpec.Hands
221+
data.WaitRounds = iSpec.WaitRounds
222+
223+
switch iSpec.Subtype {
224+
case Claws:
225+
data.WeaponType = "claws"
226+
case Shooting:
227+
data.WeaponType = "shooting"
228+
}
229+
}
230+
231+
return data
232+
}
233+
171234
func (i *Item) GetLongDescription() string {
172235

173236
iSpec := i.GetSpec()

internal/mobs/mobs.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,59 @@ func (m *Mob) GetScriptPath() string {
708708
return util.FilePath(fullScriptPath)
709709
}
710710

711+
// MobLookData contains all the data needed for the look-mob template
712+
type MobLookData struct {
713+
Mob *Mob
714+
Character *characters.Character
715+
HealthStatus string
716+
Equipment *characters.Worn
717+
ItemCount int
718+
CarryingStatus string // "no", "a few", "several", "lots of"
719+
IsCharmed bool
720+
IsShop bool
721+
CharmedBy string // charmer's name if applicable
722+
}
723+
724+
// GetLookData returns data structure for template-based mob descriptions
725+
func (m *Mob) GetLookData(charmerName string) MobLookData {
726+
data := MobLookData{
727+
Mob: m,
728+
Character: &m.Character,
729+
Equipment: &m.Character.Equipment,
730+
IsCharmed: m.Character.IsCharmed(),
731+
IsShop: m.HasShop(),
732+
CharmedBy: charmerName,
733+
}
734+
735+
// Calculate health status
736+
healthPct := int(math.Ceil((float64(m.Character.Health) / float64(m.Character.HealthMax.Value)) * 100))
737+
if healthPct >= 100 {
738+
data.HealthStatus = m.Character.Name + " is in perfect health."
739+
} else if healthPct >= 80 {
740+
data.HealthStatus = m.Character.Name + " has a few scratches."
741+
} else if healthPct >= 50 {
742+
data.HealthStatus = m.Character.Name + " has some cuts and bruises."
743+
} else if healthPct >= 15 {
744+
data.HealthStatus = m.Character.Name + " looks to be in pretty bad shape."
745+
} else {
746+
data.HealthStatus = m.Character.Name + " looks like they're about to die!"
747+
}
748+
749+
// Calculate carrying status
750+
data.ItemCount = len(m.Character.Items)
751+
if data.ItemCount == 0 {
752+
data.CarryingStatus = "no"
753+
} else if data.ItemCount < 4 {
754+
data.CarryingStatus = "a few"
755+
} else if data.ItemCount < 7 {
756+
data.CarryingStatus = "several"
757+
} else {
758+
data.CarryingStatus = "lots of"
759+
}
760+
761+
return data
762+
}
763+
711764
func ReduceHostility() {
712765

713766
for groupName, group := range mobsHatePlayers {

internal/usercommands/look.go

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,16 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
123123
)
124124
}
125125

126-
descTxt, _ := templates.Process("character/description", &m.Character, user.UserId)
127-
user.SendText(descTxt)
128-
129-
itemNames := []string{}
130-
for _, item := range m.Character.Items {
131-
itemNames = append(itemNames, item.DisplayName())
132-
}
133-
134-
invData := map[string]any{
135-
`Equipment`: &m.Character.Equipment,
136-
`ItemNames`: itemNames,
126+
// Use the new template-based look system for mobs
127+
charmerName := ""
128+
if m.Character.IsCharmed() && m.Character.Charmed.UserId > 0 {
129+
if charmer := users.GetByUserId(m.Character.Charmed.UserId); charmer != nil {
130+
charmerName = charmer.Character.Name
131+
}
137132
}
138-
139-
inventoryTxt, _ := templates.Process("character/inventory-look", invData, user.UserId)
140-
user.SendText(inventoryTxt)
133+
mobLookData := m.GetLookData(charmerName)
134+
mobDescTxt, _ := templates.Process("descriptions/look-mob", mobLookData, user.UserId)
135+
user.SendText(mobDescTxt)
141136
}
142137

143138
user.SendText(statusTxt)
@@ -284,25 +279,19 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
284279

285280
if foundItem {
286281

287-
user.SendText(``)
288-
289-
user.SendText(
290-
fmt.Sprintf(`You look at the <ansi fg="item">%s</ansi> %s:`, lookItem.DisplayName(), lookDestination),
291-
)
292-
293-
user.SendText(``)
294-
295282
if !isSneaking {
296283
room.SendText(
297284
fmt.Sprintf(`<ansi fg="username">%s</ansi> is admiring their <ansi fg="item">%s</ansi>.`, user.Character.Name, lookItem.DisplayName()),
298285
user.UserId,
299286
)
300287
}
301288

302-
user.SendText(
303-
lookItem.GetLongDescription(),
304-
)
289+
// Use the new template-based look system for items
290+
itemLookData := lookItem.GetLookData(lookDestination)
291+
itemDescTxt, _ := templates.Process("descriptions/look-item", itemLookData, user.UserId)
305292

293+
user.SendText(``)
294+
user.SendText(itemDescTxt)
306295
user.SendText(``)
307296

308297
return true, nil

0 commit comments

Comments
 (0)