diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml
index eda22803..6a838178 100755
--- a/_datafiles/config.yaml
+++ b/_datafiles/config.yaml
@@ -186,6 +186,12 @@ ShopRestockRate: 6 hours
# varied.
# Note: see _datafiles/combat-messages/*
ConsistentAttackMessages: true
+# - CorpsesEnabled -
+# If set to true, corpses are left behind by players or mobs that are killed.
+CorpsesEnabled: true
+# - CorpseDecayTime -
+# How long until corpses crumble to dust (Go away).
+CorpseDecayTime: 1 hour
# - MaxAltCharacters -
# How many characters beyond their original character can they create? Players
# can swap between characters and work on them independently if this is set
diff --git a/_datafiles/world/default/ansi-aliases.yaml b/_datafiles/world/default/ansi-aliases.yaml
index 7597f32a..fb926147 100644
--- a/_datafiles/world/default/ansi-aliases.yaml
+++ b/_datafiles/world/default/ansi-aliases.yaml
@@ -7,7 +7,7 @@ color8:
username: 93 # Bright yellow
username-aggro: red
username-downed: 90 # Bright black
- mobname: 14
+ mobname: 14
mobname-aggro: 91 # Bright red
mobname-downed: red
petname: 3
@@ -156,6 +156,8 @@ color8:
script-text: 10
broadcast-prefix: 8
broadcast-body: 13
+ mob-corpse: 8
+ user-corpse: 8
color256:
table-title: 2
userid: black
@@ -306,4 +308,5 @@ color256:
script-text: 155
broadcast-prefix: 135
broadcast-body: 164
-
\ No newline at end of file
+ mob-corpse: 67
+ user-corpse: 143
\ No newline at end of file
diff --git a/_datafiles/world/default/keywords.yaml b/_datafiles/world/default/keywords.yaml
index add800b3..79270420 100644
--- a/_datafiles/world/default/keywords.yaml
+++ b/_datafiles/world/default/keywords.yaml
@@ -27,6 +27,7 @@ help:
- character
- pets
- train
+ - bury
communication:
- emote
- say
diff --git a/_datafiles/world/default/templates/character/description-corpse.template b/_datafiles/world/default/templates/character/description-corpse.template
new file mode 100644
index 00000000..ecf56a63
--- /dev/null
+++ b/_datafiles/world/default/templates/character/description-corpse.template
@@ -0,0 +1,8 @@
+
+.: {{ .Name }} corpse
+ ┌─ .:Description ────────────────────────────────────────────────────────────┐
+ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
+ {{ splitstring .GetDescription 72 " "}}
+ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
+ This is a corpse. They are dead.
+ └────────────────────────────────────────────────────────────────────────────┘
diff --git a/_datafiles/world/default/templates/character/description.template b/_datafiles/world/default/templates/character/description.template
index e3e72a55..7cc17d5a 100644
--- a/_datafiles/world/default/templates/character/description.template
+++ b/_datafiles/world/default/templates/character/description.template
@@ -1,9 +1,9 @@
-.: {{ .Character.Name }} ({{ .Character.AlignmentName }})
-{{- $tnl := .Character.XPTNL -}}
-{{- $pct := (pct .Character.Experience $tnl ) -}}
-{{- $exp := printf "%d/%d (%d%%)" .Character.Experience $tnl $pct }}
+.: {{ .Name }} ({{ .AlignmentName }})
+{{- $tnl := .XPTNL -}}
+{{- $pct := (pct .Experience $tnl ) -}}
+{{- $exp := printf "%d/%d (%d%%)" .Experience $tnl $pct }}
┌─ .:Description ────────────────────────────────────────────────────────────┐
- {{ splitstring .Character.GetDescription 72 " "}}
- {{ .Character.GetHealthAppearance }}
+ {{ splitstring .GetDescription 72 " "}}
+ {{ .GetHealthAppearance }}
└────────────────────────────────────────────────────────────────────────────┘
diff --git a/_datafiles/world/default/templates/character/status-lite.template b/_datafiles/world/default/templates/character/status-lite.template
index 545db7cf..ce9dbfb6 100644
--- a/_datafiles/world/default/templates/character/status-lite.template
+++ b/_datafiles/world/default/templates/character/status-lite.template
@@ -1,12 +1,12 @@
- .: {{ .Character.Name }} the {{ profession .Character }}
-{{- $tnl := .Character.XPTNL -}}
-{{- $pct := (pct .Character.Experience $tnl ) -}}
-{{- $exp := printf "%d/%d (%d%%)" .Character.Experience $tnl $pct }}
+ .: {{ .Name }} the {{ profession . }}
+{{- $tnl := .XPTNL -}}
+{{- $pct := (pct .Experience $tnl ) -}}
+{{- $exp := printf "%d/%d (%d%%)" .Experience $tnl $pct }}
┌─ .:Info ──────────────────────┐ ┌─ .:Attributes ───────────────────────────┐
- │ Health: {{ printf "%-10d" .Character.Health }} Max: {{ printf "%-6d" .Character.HealthMax.Value }}│ │ Strength: {{ printf "%-4d(%-3d)" .Character.Stats.Strength.Value (.Character.StatMod "strength") }} Vitality: {{ printf "%-4d(%-3d)" .Character.Stats.Vitality.Value (.Character.StatMod "vitality") }} │
- Mana: {{ printf "%-10d" .Character.Mana }} Max: {{ printf "%-6d" .Character.ManaMax.Value }} Speed: {{ printf "%-4d(%-3d)" .Character.Stats.Speed.Value (.Character.StatMod "speed") }} Mysticism: {{ printf "%-4d(%-3d)" .Character.Stats.Mysticism.Value (.Character.StatMod "mysticism") }}
- Armor: {{ printf "%-22s" ( printf "%d" (.Character.GetDefense)) }} Smarts: {{ printf "%-4d(%-3d)" .Character.Stats.Smarts.Value (.Character.StatMod "smarts") }} Percept: {{ printf "%-4d(%-3d)" .Character.Stats.Perception.Value (.Character.StatMod "perception") }}
- Level: {{ printf "%-22d" .Character.Level }}
- │ Gold: {{ printf "%-22s" (numberFormat .Character.Gold) }}│ │ │
+ │ Health: {{ printf "%-10d" .Health }} Max: {{ printf "%-6d" .HealthMax.Value }}│ │ Strength: {{ printf "%-4d(%-3d)" .Stats.Strength.Value (.StatMod "strength") }} Vitality: {{ printf "%-4d(%-3d)" .Stats.Vitality.Value (.StatMod "vitality") }} │
+ Mana: {{ printf "%-10d" .Mana }} Max: {{ printf "%-6d" .ManaMax.Value }} Speed: {{ printf "%-4d(%-3d)" .Stats.Speed.Value (.StatMod "speed") }} Mysticism: {{ printf "%-4d(%-3d)" .Stats.Mysticism.Value (.StatMod "mysticism") }}
+ Armor: {{ printf "%-22s" ( printf "%d" (.GetDefense)) }} Smarts: {{ printf "%-4d(%-3d)" .Stats.Smarts.Value (.StatMod "smarts") }} Percept: {{ printf "%-4d(%-3d)" .Stats.Perception.Value (.StatMod "perception") }}
+ Level: {{ printf "%-22d" .Level }}
+ │ Gold: {{ printf "%-22s" (numberFormat .Gold) }}│ │ │
└───────────────────────────────┘ └──────────────────────────────────────────┘
\ No newline at end of file
diff --git a/_datafiles/world/default/templates/help/bury.template b/_datafiles/world/default/templates/help/bury.template
new file mode 100644
index 00000000..084ba917
--- /dev/null
+++ b/_datafiles/world/default/templates/help/bury.template
@@ -0,0 +1,9 @@
+.: Help for bury
+
+The bury command cleans up corpses from the room.
+
+Usage:
+
+ bury rat corpse
+
+
diff --git a/_datafiles/world/empty/ansi-aliases.yaml b/_datafiles/world/empty/ansi-aliases.yaml
index 7597f32a..fb926147 100644
--- a/_datafiles/world/empty/ansi-aliases.yaml
+++ b/_datafiles/world/empty/ansi-aliases.yaml
@@ -7,7 +7,7 @@ color8:
username: 93 # Bright yellow
username-aggro: red
username-downed: 90 # Bright black
- mobname: 14
+ mobname: 14
mobname-aggro: 91 # Bright red
mobname-downed: red
petname: 3
@@ -156,6 +156,8 @@ color8:
script-text: 10
broadcast-prefix: 8
broadcast-body: 13
+ mob-corpse: 8
+ user-corpse: 8
color256:
table-title: 2
userid: black
@@ -306,4 +308,5 @@ color256:
script-text: 155
broadcast-prefix: 135
broadcast-body: 164
-
\ No newline at end of file
+ mob-corpse: 67
+ user-corpse: 143
\ No newline at end of file
diff --git a/_datafiles/world/empty/keywords.yaml b/_datafiles/world/empty/keywords.yaml
index add800b3..79270420 100644
--- a/_datafiles/world/empty/keywords.yaml
+++ b/_datafiles/world/empty/keywords.yaml
@@ -27,6 +27,7 @@ help:
- character
- pets
- train
+ - bury
communication:
- emote
- say
diff --git a/_datafiles/world/empty/templates/character/description-corpse.template b/_datafiles/world/empty/templates/character/description-corpse.template
new file mode 100644
index 00000000..ecf56a63
--- /dev/null
+++ b/_datafiles/world/empty/templates/character/description-corpse.template
@@ -0,0 +1,8 @@
+
+.: {{ .Name }} corpse
+ ┌─ .:Description ────────────────────────────────────────────────────────────┐
+ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
+ {{ splitstring .GetDescription 72 " "}}
+ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
+ This is a corpse. They are dead.
+ └────────────────────────────────────────────────────────────────────────────┘
diff --git a/_datafiles/world/empty/templates/character/description.template b/_datafiles/world/empty/templates/character/description.template
index e3e72a55..7cc17d5a 100644
--- a/_datafiles/world/empty/templates/character/description.template
+++ b/_datafiles/world/empty/templates/character/description.template
@@ -1,9 +1,9 @@
-.: {{ .Character.Name }} ({{ .Character.AlignmentName }})
-{{- $tnl := .Character.XPTNL -}}
-{{- $pct := (pct .Character.Experience $tnl ) -}}
-{{- $exp := printf "%d/%d (%d%%)" .Character.Experience $tnl $pct }}
+.: {{ .Name }} ({{ .AlignmentName }})
+{{- $tnl := .XPTNL -}}
+{{- $pct := (pct .Experience $tnl ) -}}
+{{- $exp := printf "%d/%d (%d%%)" .Experience $tnl $pct }}
┌─ .:Description ────────────────────────────────────────────────────────────┐
- {{ splitstring .Character.GetDescription 72 " "}}
- {{ .Character.GetHealthAppearance }}
+ {{ splitstring .GetDescription 72 " "}}
+ {{ .GetHealthAppearance }}
└────────────────────────────────────────────────────────────────────────────┘
diff --git a/_datafiles/world/empty/templates/character/status-lite.template b/_datafiles/world/empty/templates/character/status-lite.template
index 545db7cf..ce9dbfb6 100644
--- a/_datafiles/world/empty/templates/character/status-lite.template
+++ b/_datafiles/world/empty/templates/character/status-lite.template
@@ -1,12 +1,12 @@
- .: {{ .Character.Name }} the {{ profession .Character }}
-{{- $tnl := .Character.XPTNL -}}
-{{- $pct := (pct .Character.Experience $tnl ) -}}
-{{- $exp := printf "%d/%d (%d%%)" .Character.Experience $tnl $pct }}
+ .: {{ .Name }} the {{ profession . }}
+{{- $tnl := .XPTNL -}}
+{{- $pct := (pct .Experience $tnl ) -}}
+{{- $exp := printf "%d/%d (%d%%)" .Experience $tnl $pct }}
┌─ .:Info ──────────────────────┐ ┌─ .:Attributes ───────────────────────────┐
- │ Health: {{ printf "%-10d" .Character.Health }} Max: {{ printf "%-6d" .Character.HealthMax.Value }}│ │ Strength: {{ printf "%-4d(%-3d)" .Character.Stats.Strength.Value (.Character.StatMod "strength") }} Vitality: {{ printf "%-4d(%-3d)" .Character.Stats.Vitality.Value (.Character.StatMod "vitality") }} │
- Mana: {{ printf "%-10d" .Character.Mana }} Max: {{ printf "%-6d" .Character.ManaMax.Value }} Speed: {{ printf "%-4d(%-3d)" .Character.Stats.Speed.Value (.Character.StatMod "speed") }} Mysticism: {{ printf "%-4d(%-3d)" .Character.Stats.Mysticism.Value (.Character.StatMod "mysticism") }}
- Armor: {{ printf "%-22s" ( printf "%d" (.Character.GetDefense)) }} Smarts: {{ printf "%-4d(%-3d)" .Character.Stats.Smarts.Value (.Character.StatMod "smarts") }} Percept: {{ printf "%-4d(%-3d)" .Character.Stats.Perception.Value (.Character.StatMod "perception") }}
- Level: {{ printf "%-22d" .Character.Level }}
- │ Gold: {{ printf "%-22s" (numberFormat .Character.Gold) }}│ │ │
+ │ Health: {{ printf "%-10d" .Health }} Max: {{ printf "%-6d" .HealthMax.Value }}│ │ Strength: {{ printf "%-4d(%-3d)" .Stats.Strength.Value (.StatMod "strength") }} Vitality: {{ printf "%-4d(%-3d)" .Stats.Vitality.Value (.StatMod "vitality") }} │
+ Mana: {{ printf "%-10d" .Mana }} Max: {{ printf "%-6d" .ManaMax.Value }} Speed: {{ printf "%-4d(%-3d)" .Stats.Speed.Value (.StatMod "speed") }} Mysticism: {{ printf "%-4d(%-3d)" .Stats.Mysticism.Value (.StatMod "mysticism") }}
+ Armor: {{ printf "%-22s" ( printf "%d" (.GetDefense)) }} Smarts: {{ printf "%-4d(%-3d)" .Stats.Smarts.Value (.StatMod "smarts") }} Percept: {{ printf "%-4d(%-3d)" .Stats.Perception.Value (.StatMod "perception") }}
+ Level: {{ printf "%-22d" .Level }}
+ │ Gold: {{ printf "%-22s" (numberFormat .Gold) }}│ │ │
└───────────────────────────────┘ └──────────────────────────────────────────┘
\ No newline at end of file
diff --git a/_datafiles/world/empty/templates/help/bury.template b/_datafiles/world/empty/templates/help/bury.template
new file mode 100644
index 00000000..084ba917
--- /dev/null
+++ b/_datafiles/world/empty/templates/help/bury.template
@@ -0,0 +1,9 @@
+.: Help for bury
+
+The bury command cleans up corpses from the room.
+
+Usage:
+
+ bury rat corpse
+
+
diff --git a/go.mod b/go.mod
index 2de7544f..cc89a53c 100644
--- a/go.mod
+++ b/go.mod
@@ -7,15 +7,19 @@ require (
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
github.com/gorilla/websocket v1.5.3
github.com/natefinch/lumberjack v2.0.0+incompatible
+ github.com/stretchr/testify v1.8.0
)
require (
github.com/BurntSushi/toml v1.4.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/text v0.3.8 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
diff --git a/go.sum b/go.sum
index d8487d53..abe10d55 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,7 @@ github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4M
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
@@ -39,6 +40,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -77,5 +81,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/configs/configs.go b/internal/configs/configs.go
index af476371..29c1604f 100644
--- a/internal/configs/configs.go
+++ b/internal/configs/configs.go
@@ -76,13 +76,13 @@ type Config struct {
EnterRoomMessageWrapper ConfigString `yaml:"EnterRoomMessageWrapper"`
ExitRoomMessageWrapper ConfigString `yaml:"ExitRoomMessageWrapper"`
- MaxIdleSeconds ConfigInt `yaml:"MaxIdleSeconds"` // How many seconds a player can go without a command in game before being kicked.
TimeoutMods ConfigBool `yaml:"TimeoutMods"` // Whether to kick admin/mods when idle too long.
ZombieSeconds ConfigInt `yaml:"ZombieSeconds"` // How many seconds a player will be a zombie allowing them to reconnect.
LogoutRounds ConfigInt `yaml:"LogoutRounds"` // How many rounds of uninterrupted meditation must be completed to log out.
StartRoom ConfigInt `yaml:"StartRoom"` // Default starting room.
DeathRecoveryRoom ConfigInt `yaml:"DeathRecoveryRoom"` // Recovery room after dying.
TutorialStartRooms ConfigSliceString `yaml:"TutorialStartRooms"` // List of all rooms that can be used to begin the tutorial process
+ MaxIdleSeconds ConfigInt `yaml:"MaxIdleSeconds"` // How many seconds a player can go without a command in game before being kicked.
// Perma-death related configs
PermaDeath ConfigBool `yaml:"PermaDeath"` // Is permadeath enabled?
@@ -92,9 +92,11 @@ type Config struct {
PricePerLife ConfigInt `yaml:"PricePerLife"` // Price in gold to buy new lives
ShopRestockRate ConfigString `yaml:"ShopRestockRate"` // Default time it takes to restock 1 quantity in shops
- ConsistentAttackMessages ConfigBool `yaml:"ConsistentAttackMessages"` // Whether each weapon has consistent attack messages
MaxAltCharacters ConfigInt `yaml:"MaxAltCharacters"` // How many characters beyond the default character can they create?
AfkSeconds ConfigInt `yaml:"AfkSeconds"` // How long until a player is marked as afk?
+ ConsistentAttackMessages ConfigBool `yaml:"ConsistentAttackMessages"` // Whether each weapon has consistent attack messages
+ CorpsesEnabled ConfigBool `yaml:"CorpsesEnabled"` // Whether corpses are left behind after mob/player deaths
+ CorpseDecayTime ConfigString `yaml:"CorpseDecayTime"` // How long until corpses decay to dust (go away)
LeaderboardSize ConfigInt `yaml:"LeaderboardSize"` // Maximum size of leaderboard
diff --git a/internal/mobcommands/bury.go b/internal/mobcommands/bury.go
new file mode 100644
index 00000000..83914a35
--- /dev/null
+++ b/internal/mobcommands/bury.go
@@ -0,0 +1,38 @@
+package mobcommands
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/volte6/gomud/internal/mobs"
+ "github.com/volte6/gomud/internal/rooms"
+ "github.com/volte6/gomud/internal/util"
+)
+
+func Bury(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
+
+ args := util.SplitButRespectQuotes(strings.ToLower(rest))
+
+ if len(args) == 0 {
+ return true, nil
+ }
+
+ if corpse, corpseFound := room.FindCorpse(rest); corpseFound {
+
+ if room.RemoveCorpse(corpse) {
+
+ corpseColor := `mob-corpse`
+ if corpse.UserId > 0 {
+ corpseColor = `user-corpse`
+ }
+
+ room.SendText(fmt.Sprintf(`%s buries the %s corpse.`, mob.Character.Name, corpseColor, corpse.Character.Name))
+ return true, nil
+
+ }
+
+ return true, nil
+ }
+
+ return true, nil
+}
diff --git a/internal/mobcommands/mobcommands.go b/internal/mobcommands/mobcommands.go
index c9ff7a7f..8900e524 100644
--- a/internal/mobcommands/mobcommands.go
+++ b/internal/mobcommands/mobcommands.go
@@ -29,6 +29,7 @@ var (
"befriend": {Befriend, false},
"break": {Break, false},
"broadcast": {Broadcast, false},
+ `bury`: {Bury, false},
"cast": {Cast, false},
"converse": {Converse, false},
"callforhelp": {CallForHelp, false},
diff --git a/internal/mobcommands/suicide.go b/internal/mobcommands/suicide.go
index d2757ad6..ff170946 100644
--- a/internal/mobcommands/suicide.go
+++ b/internal/mobcommands/suicide.go
@@ -7,6 +7,7 @@ import (
"github.com/volte6/gomud/internal/buffs"
"github.com/volte6/gomud/internal/combat"
+ "github.com/volte6/gomud/internal/configs"
"github.com/volte6/gomud/internal/mobs"
"github.com/volte6/gomud/internal/parties"
"github.com/volte6/gomud/internal/rooms"
@@ -18,6 +19,9 @@ import (
func Suicide(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
+ config := configs.GetConfig()
+ currentRound := util.GetRoundCount()
+
if rest != `vanish` && mob.Character.HasBuffFlag(buffs.ReviveOnDeath) {
mob.Character.Health = mob.Character.HealthMax.Value
@@ -65,7 +69,7 @@ func Suicide(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
if mob.MobId == 38 {
if mob.Character.Charmed != nil {
if tmpU := users.GetByUserId(mob.Character.Charmed.UserId); tmpU != nil {
- tmpU.SetTempData(`lastGuideRound`, util.GetRoundCount())
+ tmpU.SetTempData(`lastGuideRound`, currentRound)
}
}
}
@@ -316,5 +320,13 @@ func Suicide(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
// Remove from current room
room.RemoveMob(mob.InstanceId)
+ if config.CorpsesEnabled {
+ room.AddCorpse(rooms.Corpse{
+ MobId: int(mob.MobId),
+ Character: mob.Character,
+ RoundCreated: currentRound,
+ })
+ }
+
return true, nil
}
diff --git a/internal/rooms/corpse.go b/internal/rooms/corpse.go
new file mode 100644
index 00000000..583a1786
--- /dev/null
+++ b/internal/rooms/corpse.go
@@ -0,0 +1,34 @@
+package rooms
+
+import (
+ "github.com/volte6/gomud/internal/characters"
+ "github.com/volte6/gomud/internal/gametime"
+)
+
+type Corpse struct {
+ UserId int
+ MobId int
+ Character characters.Character
+ RoundCreated uint64
+ Prunable bool // Whether it can be removed
+}
+
+func (c *Corpse) Update(roundNow uint64, decayRate string) {
+
+ if c.Prunable {
+ return
+ }
+
+ if decayRate == `` {
+ decayRate = `1 week`
+ }
+
+ gd := gametime.GetDate(c.RoundCreated)
+ decayRound := gd.AddPeriod(decayRate)
+
+ // Has enough time passed to do the respawn?
+ if roundNow >= decayRound {
+ c.Prunable = true
+ }
+
+}
diff --git a/internal/rooms/corpse_test.go b/internal/rooms/corpse_test.go
new file mode 100644
index 00000000..54481c37
--- /dev/null
+++ b/internal/rooms/corpse_test.go
@@ -0,0 +1,116 @@
+package rooms
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/volte6/gomud/internal/characters"
+ "github.com/volte6/gomud/internal/gametime"
+)
+
+// Test that if the corpse is already prunable, calling Update does nothing.
+func TestCorpseUpdate_PrunableAlreadyTrue(t *testing.T) {
+ corpse := &Corpse{
+ UserId: 1,
+ MobId: 2,
+ Character: characters.Character{},
+ RoundCreated: 100,
+ Prunable: true,
+ }
+
+ // Even if we call Update with a large roundNow, prunable should remain true.
+ corpse.Update(9999, "1 week")
+ assert.True(t, corpse.Prunable, "Corpse should remain prunable when already set to true")
+}
+
+// Test that if decayRate is empty, it defaults to "1 week". We assume "1 week"
+// is longer than the provided roundNow in this example, so it should remain false.
+func TestCorpseUpdate_EmptyDecayRateNotEnoughTime(t *testing.T) {
+ // Suppose RoundCreated=0 and roundNow=5000,
+ // we assume "1 week" (whatever that translates to in rounds) is > 5000.
+ corpse := &Corpse{
+ UserId: 1,
+ MobId: 2,
+ Character: characters.Character{},
+ RoundCreated: 5000,
+ Prunable: false,
+ }
+
+ corpse.Update(5000, "")
+ assert.False(t, corpse.Prunable, "Corpse should not be prunable yet if not enough time has elapsed for default decay rate")
+}
+
+// Test that if decayRate is empty, it defaults to "1 week". We assume "1 week"
+// is shorter than the provided roundNow, so it becomes prunable.
+func TestCorpseUpdate_EmptyDecayRateSufficientTime(t *testing.T) {
+ // Suppose RoundCreated=0 and roundNow=20000,
+ // we assume "1 week" < 20000 (whatever that is in your game logic).
+ corpse := &Corpse{
+ UserId: 1,
+ MobId: 2,
+ Character: characters.Character{},
+ RoundCreated: 0,
+ Prunable: false,
+ }
+
+ corpse.Update(20000, "")
+ assert.True(t, corpse.Prunable, "Corpse should become prunable if enough time has elapsed for default decay rate")
+}
+
+// Test a custom decay rate, ensuring that when roundNow < decay threshold,
+// the corpse is not prunable, and once roundNow >= threshold, it becomes prunable.
+func TestCorpseUpdate_CustomDecayRate(t *testing.T) {
+ // For demonstration, let's assume "2 weeks" is a certain number of rounds,
+ // but the exact number depends on your game logic.
+ // We'll test a transition point: just before and just after the threshold.
+ corpse := &Corpse{
+ UserId: 1,
+ MobId: 2,
+ Character: characters.Character{},
+ RoundCreated: 1000, // The starting round
+ Prunable: false,
+ }
+
+ // Just before decay threshold
+ // e.g., if "2 weeks" from round 1000 is 21000, pass in 20999
+ corpse.Update(1005, "2 weeks")
+ assert.False(t, corpse.Prunable, "Corpse should not be prunable just before custom decay threshold")
+
+ // At or after decay threshold
+ // e.g., pass in 21000 or greater
+ corpse.Update(21000, "2 weeks")
+ assert.True(t, corpse.Prunable, "Corpse should become prunable at or after custom decay threshold")
+}
+
+// Example test that checks repeated updates before and after crossing the threshold.
+func TestCorpseUpdate_MultipleCalls(t *testing.T) {
+ // For demonstration, let's assume "1 week" is N rounds in your logic.
+ corpse := &Corpse{
+ RoundCreated: 100,
+ Prunable: false,
+ }
+
+ // First update: not enough rounds have passed
+ corpse.Update(105, "1 week")
+ assert.False(t, corpse.Prunable, "Corpse should still not be prunable if not enough rounds elapsed")
+
+ // Second update: enough rounds have passed
+ corpse.Update(20000, "1 week")
+ assert.True(t, corpse.Prunable, "Corpse should become prunable after sufficient rounds elapsed")
+}
+
+// If you want to specifically test or mock the gametime package, you can do so
+// in a separate test or by creating a mock gametime function. This example
+// simply relies on the assumption that gametime.GetDate and its AddPeriod logic
+// are correct. If those are crucial, consider additional testing around them.
+func TestCorpseUpdate_GametimeIntegration(t *testing.T) {
+ // If you have a need to test how gametime transforms RoundCreated + decayRate,
+ // you might do something like:
+
+ gd := gametime.GetDate(0)
+ decayRound := gd.AddPeriod("1 week")
+
+ // Ensure that the decayRound is what you expect...
+ // But that might be tested better in the gametime package itself.
+ assert.NotZero(t, decayRound, "Decay round should not be zero")
+}
diff --git a/internal/rooms/roomdetails.go b/internal/rooms/roomdetails.go
index fce28873..dcf2223e 100644
--- a/internal/rooms/roomdetails.go
+++ b/internal/rooms/roomdetails.go
@@ -21,6 +21,7 @@ import (
type RoomTemplateDetails struct {
VisiblePlayers []string
VisibleMobs []string
+ VisibleCorpses []string
VisibleExits map[string]exit.RoomExit
TemporaryExits map[string]exit.TemporaryRoomExit
UserId int
@@ -64,6 +65,7 @@ func GetDetails(r *Room, user *users.UserRecord) RoomTemplateDetails {
details := RoomTemplateDetails{
VisiblePlayers: []string{},
VisibleMobs: []string{},
+ VisibleCorpses: []string{},
VisibleExits: make(map[string]exit.RoomExit),
TemporaryExits: make(map[string]exit.TemporaryRoomExit),
Zone: r.Zone,
@@ -320,6 +322,41 @@ func GetDetails(r *Room, user *users.UserRecord) RoomTemplateDetails {
}
}
+ // add any corpses present
+ mobCorpses := map[string]int{}
+ playerCorpses := map[string]int{}
+
+ for _, c := range r.Corpses {
+ if c.Prunable {
+ continue
+ }
+
+ if c.MobId > 0 {
+ mobCorpses[c.Character.Name] = mobCorpses[c.Character.Name] + 1
+ }
+
+ if c.UserId > 0 {
+ playerCorpses[c.Character.Name] = playerCorpses[c.Character.Name] + 1
+ }
+
+ }
+
+ for name, qty := range playerCorpses {
+ if qty == 1 {
+ details.VisibleCorpses = append(details.VisibleCorpses, fmt.Sprintf(`%s corpse`, name))
+ } else {
+ details.VisibleCorpses = append(details.VisibleCorpses, fmt.Sprintf(`%d %s corpses`, qty, name))
+ }
+ }
+
+ for name, qty := range mobCorpses {
+ if qty == 1 {
+ details.VisibleCorpses = append(details.VisibleCorpses, fmt.Sprintf(`%s corpse`, name))
+ } else {
+ details.VisibleCorpses = append(details.VisibleCorpses, fmt.Sprintf(`%d %s corpses`, qty, name))
+ }
+ }
+
// assign mutator exits last so that they can overwrite normal exits
for mut := range r.ActiveMutators {
spec := mut.GetSpec()
diff --git a/internal/rooms/rooms.go b/internal/rooms/rooms.go
index c534a022..72d7203a 100644
--- a/internal/rooms/rooms.go
+++ b/internal/rooms/rooms.go
@@ -79,6 +79,7 @@ type Room struct {
Nouns map[string]string `yaml:"nouns,omitempty"` // Interesting nouns to highlight in the room or reveal on succesful searches.
Items []items.Item `yaml:"items,omitempty"`
Stash []items.Item `yaml:"stash,omitempty"` // list of items in the room that are not visible to players
+ Corpses []Corpse `yaml:"-"` // Any corpses laying around from recent deaths
Gold int `yaml:"gold,omitempty"` // How much gold is on the ground?
SpawnInfo []SpawnInfo `yaml:"spawninfo,omitempty"` // key is creature ID, value is spawn chance
SkillTraining map[string]TrainingRange `yaml:"skilltraining,omitempty"` // list of skills that can be trained in this room
@@ -169,6 +170,60 @@ func (r *Room) GetVisibility() int {
return visibility
}
+func (r *Room) AddCorpse(c Corpse) {
+ r.Corpses = append(r.Corpses, c)
+}
+
+func (r *Room) RemoveCorpse(c Corpse) bool {
+ for idx, corpse := range r.Corpses {
+ if corpse.MobId != c.MobId {
+ continue
+ }
+ if corpse.UserId != c.UserId {
+ continue
+ }
+ if corpse.Character.Name != c.Character.Name {
+ continue
+ }
+ if corpse.RoundCreated != c.RoundCreated {
+ continue
+ }
+
+ r.Corpses = append(r.Corpses[:idx], r.Corpses[idx+1:]...)
+
+ return true
+ }
+ return false
+}
+
+func (r *Room) UpdateCorpses(roundNow uint64) {
+
+ c := configs.GetConfig()
+
+ if !c.CorpsesEnabled {
+ return
+ }
+
+ removeIdx := []int{}
+ for idx, corpse := range r.Corpses {
+ corpse.Update(roundNow, c.CorpseDecayTime.String())
+ if corpse.Prunable {
+ removeIdx = append(removeIdx, idx)
+ if corpse.MobId > 0 {
+ r.SendText(fmt.Sprintf(`A %s corpse crumbles to dust.`, corpse.Character.Name))
+ }
+ if corpse.UserId > 0 {
+ r.SendText(fmt.Sprintf(`A %s corpse crumbles to dust.`, corpse.Character.Name))
+ }
+ }
+ r.Corpses[idx] = corpse
+ }
+
+ for i := len(removeIdx) - 1; i >= 0; i-- {
+ r.Corpses = append(r.Corpses[:removeIdx[i]], r.Corpses[removeIdx[i]+1:]...)
+ }
+}
+
func (r *Room) SendTextCommunication(txt string, excludeUserIds ...int) {
events.AddToQueue(events.Message{
@@ -842,6 +897,58 @@ func (r *Room) GetAllFloorItems(stash bool) []items.Item {
return found
}
+func (r *Room) FindCorpse(searchName string) (Corpse, bool) {
+
+ // First search for player corpses that match
+
+ playerCorpseLookup := map[string]int{}
+ playerCorpses := []string{}
+
+ mobCorpseLookup := map[string]int{}
+ mobCorpses := []string{}
+
+ for idx, c := range r.Corpses {
+
+ if c.Prunable {
+ continue
+ }
+
+ if c.UserId > 0 {
+ name := c.Character.Name + ` corpse`
+ if _, ok := playerCorpseLookup[name]; !ok {
+ playerCorpseLookup[name] = idx
+ playerCorpses = append(playerCorpses, name)
+ }
+ }
+
+ if c.MobId > 0 {
+ name := c.Character.Name + ` corpse`
+ if _, ok := mobCorpseLookup[name]; !ok {
+ mobCorpseLookup[name] = idx
+ mobCorpses = append(mobCorpses, name)
+ }
+ }
+ }
+
+ userMatch, closeUserMatch := util.FindMatchIn(searchName, playerCorpses...)
+ if userMatch != `` {
+ return r.Corpses[playerCorpseLookup[userMatch]], true
+ }
+
+ mobMatch, closeMobMatch := util.FindMatchIn(searchName, mobCorpses...)
+ if mobMatch != `` {
+ return r.Corpses[mobCorpseLookup[mobMatch]], true
+ }
+
+ if closeUserMatch != `` {
+ return r.Corpses[playerCorpseLookup[closeUserMatch]], true
+ } else if closeMobMatch != `` {
+ return r.Corpses[mobCorpseLookup[closeMobMatch]], true
+ }
+
+ return Corpse{}, false
+}
+
func (r *Room) FindOnFloor(itemName string, stash bool) (items.Item, bool) {
if stash {
@@ -1784,7 +1891,6 @@ func (r *Room) RoundTick() {
r.Mutators.Update(roundNow)
for mut := range r.ActiveMutators {
-
spec := mut.GetSpec()
r.ApplyBuffIdToPlayers(spec.PlayerBuffIds...)
r.ApplyBuffIdToMobs(spec.MobBuffIds...)
@@ -1816,6 +1922,11 @@ func (r *Room) RoundTick() {
}
}
}
+
+ //
+ // Decay any corpses
+ //
+ r.UpdateCorpses(roundNow)
}
func (r *Room) addPlayer(userId int) int {
diff --git a/internal/rooms/rooms_test.go b/internal/rooms/rooms_test.go
new file mode 100644
index 00000000..2f6ed4c2
--- /dev/null
+++ b/internal/rooms/rooms_test.go
@@ -0,0 +1,96 @@
+package rooms
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/volte6/gomud/internal/characters"
+)
+
+func TestRoom_AddCorpse(t *testing.T) {
+ r := &Room{}
+ assert.Empty(t, r.Corpses, "Expected no corpses initially")
+
+ corpse := Corpse{
+ MobId: 0,
+ UserId: 123,
+ Character: characters.Character{Name: "TestPlayer"},
+ RoundCreated: 10,
+ }
+ r.AddCorpse(corpse)
+ assert.Len(t, r.Corpses, 1, "Expected exactly one corpse after adding")
+ assert.Equal(t, corpse, r.Corpses[0], "Expected the added corpse to match")
+}
+
+func TestRoom_RemoveCorpse(t *testing.T) {
+ r := &Room{}
+ corpse1 := Corpse{
+ MobId: 0,
+ UserId: 123,
+ Character: characters.Character{Name: "PlayerOne"},
+ RoundCreated: 10,
+ }
+ corpse2 := Corpse{
+ MobId: 456,
+ UserId: 0,
+ Character: characters.Character{Name: "MobOne"},
+ RoundCreated: 11,
+ }
+
+ r.AddCorpse(corpse1)
+ r.AddCorpse(corpse2)
+
+ // Removing existing corpse
+ removed := r.RemoveCorpse(corpse1)
+ assert.True(t, removed, "Expected to remove an existing corpse successfully")
+ assert.Len(t, r.Corpses, 1, "Expected exactly one corpse remaining")
+
+ // Try removing a corpse that does not exist
+ nonExistent := Corpse{
+ MobId: 999,
+ UserId: 999,
+ Character: characters.Character{Name: "Ghost"},
+ RoundCreated: 99,
+ }
+ removed = r.RemoveCorpse(nonExistent)
+ assert.False(t, removed, "Expected removal to fail for non-existent corpse")
+ assert.Len(t, r.Corpses, 1, "Expected no change in corpses")
+}
+
+func TestRoom_FindCorpse(t *testing.T) {
+ r := &Room{}
+
+ playerCorpse := Corpse{
+ UserId: 123,
+ Character: characters.Character{Name: "PlayerOne"},
+ RoundCreated: 5,
+ Prunable: false,
+ }
+ mobCorpse := Corpse{
+ MobId: 456,
+ Character: characters.Character{Name: "MobOne"},
+ RoundCreated: 6,
+ Prunable: false,
+ }
+ r.AddCorpse(playerCorpse)
+ r.AddCorpse(mobCorpse)
+
+ // Exact search
+ found, ok := r.FindCorpse("PlayerOne corpse")
+ assert.True(t, ok, "Expected to find player corpse by exact name")
+ assert.Equal(t, "PlayerOne", found.Character.Name, "Expected found corpse to match the correct character")
+
+ // Searching for mob
+ found, ok = r.FindCorpse("MobOne corpse")
+ assert.True(t, ok, "Expected to find mob corpse by exact name")
+ assert.Equal(t, "MobOne", found.Character.Name, "Expected found corpse to match the correct character")
+
+ // Searching partial name (depends on your util.FindMatchIn logic)
+ found, ok = r.FindCorpse("player")
+ assert.True(t, ok, "Expected to find a close match for player corpse")
+ assert.Equal(t, "PlayerOne", found.Character.Name, "Expected found corpse to be the player's")
+
+ // Non-existent
+ found, ok = r.FindCorpse("NonExistent")
+ assert.False(t, ok, "Expected not to find a missing corpse")
+}
diff --git a/internal/usercommands/bury.go b/internal/usercommands/bury.go
new file mode 100644
index 00000000..1a580863
--- /dev/null
+++ b/internal/usercommands/bury.go
@@ -0,0 +1,42 @@
+package usercommands
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/volte6/gomud/internal/rooms"
+ "github.com/volte6/gomud/internal/users"
+ "github.com/volte6/gomud/internal/util"
+)
+
+func Bury(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
+
+ args := util.SplitButRespectQuotes(strings.ToLower(rest))
+
+ if len(args) == 0 {
+ user.SendText("Bury what?")
+ return true, nil
+ }
+
+ if corpse, corpseFound := room.FindCorpse(rest); corpseFound {
+
+ if room.RemoveCorpse(corpse) {
+
+ corpseColor := `mob-corpse`
+ if corpse.UserId > 0 {
+ corpseColor = `user-corpse`
+ }
+
+ user.SendText(fmt.Sprintf(`You bury the %s corpse.`, corpseColor, corpse.Character.Name))
+ room.SendText(fmt.Sprintf(`%s buries the %s corpse.`, user.Character.Name, corpseColor, corpse.Character.Name), user.UserId)
+ return true, nil
+
+ }
+
+ return true, nil
+ }
+
+ user.SendText(fmt.Sprintf("You don't see a %s around for burying.", rest))
+
+ return true, nil
+}
diff --git a/internal/usercommands/get.go b/internal/usercommands/get.go
index c443d8c8..a509cf61 100644
--- a/internal/usercommands/get.go
+++ b/internal/usercommands/get.go
@@ -309,6 +309,11 @@ func Get(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
}
+ if _, corpseFound := room.FindCorpse(rest); corpseFound {
+ user.SendText(`You can't pick up corpses. What would people think?`)
+ return true, nil
+ }
+
user.SendText(fmt.Sprintf("You don't see a %s around.", rest))
return true, nil
diff --git a/internal/usercommands/look.go b/internal/usercommands/look.go
index 7ceb99b9..a382d0e2 100644
--- a/internal/usercommands/look.go
+++ b/internal/usercommands/look.go
@@ -87,7 +87,7 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
u.UserId)
}
- descTxt, _ := templates.Process("character/description", u)
+ descTxt, _ := templates.Process("character/description", u.Character)
user.SendText(descTxt)
itemNames := []string{}
@@ -115,7 +115,7 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
)
}
- descTxt, _ := templates.Process("character/description", m)
+ descTxt, _ := templates.Process("character/description", &m.Character)
user.SendText(descTxt)
itemNames := []string{}
@@ -317,6 +317,54 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
}
}
+ if len(room.Corpses) > 0 {
+
+ mobCorpseLookup := map[string]int{}
+ mobCorpses := []string{}
+
+ playerCorpseLookup := map[string]int{}
+ playerCorpses := []string{}
+ for idx, c := range room.Corpses {
+ if c.Prunable {
+ continue
+ }
+
+ if c.MobId > 0 {
+ name := c.Character.Name + ` corpse`
+ if _, ok := mobCorpseLookup[name]; !ok {
+ mobCorpseLookup[name] = idx
+ mobCorpses = append(mobCorpses, name)
+ }
+ }
+
+ if c.UserId > 0 {
+ name := c.Character.Name + ` corpse`
+ if _, ok := playerCorpseLookup[name]; !ok {
+ playerCorpseLookup[name] = idx
+ playerCorpses = append(playerCorpses, name)
+ }
+ }
+ }
+
+ if corpse, corpseFound := room.FindCorpse(rest); corpseFound {
+
+ corpseColor := `mob-corpse`
+ if corpse.UserId > 0 {
+ corpseColor = `user-corpse`
+ }
+
+ user.SendText(fmt.Sprintf(`You look at the %s corpse.`, corpseColor, corpse.Character.Name))
+ room.SendText(fmt.Sprintf(`%s is looking at the %s corpse.`, user.Character.Name, corpseColor, corpse.Character.Name), user.UserId)
+
+ descTxt, _ := templates.Process("character/description-corpse", &corpse.Character)
+ user.SendText(descTxt)
+
+ return true, nil
+
+ }
+
+ }
+
// Nothing found
user.SendText("Look at what???")
@@ -428,6 +476,8 @@ func lookRoom(user *users.UserRecord, roomId int, secretLook bool) {
groundStuff = append(groundStuff, name)
}
+ groundStuff = append(groundStuff, details.VisibleCorpses...)
+
groundDetails := map[string]any{
`GroundStuff`: groundStuff,
`IsDark`: room.GetBiome().IsDark(),
diff --git a/internal/usercommands/skill.peep.go b/internal/usercommands/skill.peep.go
index dd359438..a49189ea 100644
--- a/internal/usercommands/skill.peep.go
+++ b/internal/usercommands/skill.peep.go
@@ -63,7 +63,7 @@ func Peep(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
targetName := u.Character.GetPlayerName(user.UserId).String()
if skillLevel >= 2 {
- statusTxt, _ = templates.Process("character/status-lite", u)
+ statusTxt, _ = templates.Process("character/status-lite", u.Character)
}
if skillLevel >= 3 {
@@ -128,7 +128,7 @@ func Peep(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
targetName := m.Character.GetMobName(user.UserId).String()
if skillLevel >= 2 {
- statusTxt, _ = templates.Process("character/status-lite", m)
+ statusTxt, _ = templates.Process("character/status-lite", &m.Character)
}
if skillLevel >= 3 {
diff --git a/internal/usercommands/suicide.go b/internal/usercommands/suicide.go
index 6e1d6e9c..8678f5b9 100644
--- a/internal/usercommands/suicide.go
+++ b/internal/usercommands/suicide.go
@@ -20,6 +20,7 @@ import (
func Suicide(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) {
config := configs.GetConfig()
+ currentRound := util.GetRoundCount()
if user.Character.Zone == `Shadow Realm` {
user.SendText(`You're already dead!`)
@@ -193,5 +194,13 @@ func Suicide(rest string, user *users.UserRecord, room *rooms.Room) (bool, error
rooms.MoveToRoom(user.UserId, int(config.DeathRecoveryRoom))
+ if config.CorpsesEnabled {
+ room.AddCorpse(rooms.Corpse{
+ UserId: user.UserId,
+ Character: *user.Character,
+ RoundCreated: currentRound,
+ })
+ }
+
return true, nil
}
diff --git a/internal/usercommands/usercommands.go b/internal/usercommands/usercommands.go
index 306d4271..f73dded7 100644
--- a/internal/usercommands/usercommands.go
+++ b/internal/usercommands/usercommands.go
@@ -38,6 +38,7 @@ var (
`badcommands`: {BadCommands, true, true}, // Admin only
`biome`: {Biome, true, false},
`broadcast`: {Broadcast, true, false},
+ `bury`: {Bury, false, false},
`character`: {Character, true, false},
`tackle`: {Tackle, false, false},
`bank`: {Bank, false, false},