diff --git a/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md b/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md
index 3b87d3fcc..89a9f4225 100644
--- a/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md
+++ b/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md
@@ -21,6 +21,8 @@ take effect immediately and are persisted the next time the character is saved.
- [PetObject.GetCapacity() int](#petobjectgetcapacity-int)
- [PetObject.ItemCount() int](#petobjectitemcount-int)
- [PetObject.HasScript() bool](#petobjecthasscript-bool)
+ - [PetObject.IsMissing() bool](#petobjectismissing-bool)
+ - [PetObject.GoMissing(rounds int)](#petobjectgomissingrounds-int)
---
@@ -200,3 +202,54 @@ Returns `true` if this pet type has a script file on disk.
Useful in generic scripts that want to check whether a pet will respond to
events before attempting to trigger them.
+
+---
+
+## [PetObject.IsMissing() bool](/internal/scripting/pet_func.go)
+Returns `true` when the pet is temporarily absent (`MissingCountdown > 0`).
+
+While missing, the pet does not appear in room descriptions, does not
+participate in combat, does not contribute stat or buff bonuses, and does not
+respond to commands or `PetAct` ticks.
+
+**Example:**
+```javascript
+var pet = actor.GetPet();
+if (pet !== null && pet.IsMissing()) {
+ actor.SendText('Your pet has wandered off somewhere...');
+}
+```
+
+---
+
+## [PetObject.GoMissing(rounds int)](/internal/scripting/pet_func.go)
+Causes the pet to go absent for the given number of rounds, or returns it
+immediately when called with `0`.
+
+- **Positive value**: sets the countdown, fires `PetLeave()`, and hides the
+ pet from all game systems until the countdown reaches zero.
+- **Zero**: clears the countdown immediately and fires `PetReturn()`. Has no
+ effect if the pet is not currently missing.
+
+| Argument | Explanation |
+| --- | --- |
+| rounds | Rounds to be absent, or `0` to return immediately. Negative values are treated as `0`. |
+
+**Example:**
+```javascript
+function PetAct(pet, actor, room) {
+ // 1% chance per round for the pet to wander off for 10 rounds
+ if (RandInt(1, 100) === 1) {
+ pet.GoMissing(10);
+ }
+}
+
+// Return the pet early from a script command
+function onCommand_recall(rest, pet, actor, room) {
+ if (pet.IsMissing()) {
+ pet.GoMissing(0);
+ actor.SendText('Your pet bounds back to your side!');
+ }
+ return true;
+}
+```
diff --git a/_datafiles/guides/building/scripting/SCRIPTING_PETS.md b/_datafiles/guides/building/scripting/SCRIPTING_PETS.md
index a880250d2..d9b01ae8e 100644
--- a/_datafiles/guides/building/scripting/SCRIPTING_PETS.md
+++ b/_datafiles/guides/building/scripting/SCRIPTING_PETS.md
@@ -133,3 +133,60 @@ function onCommand_pet(rest, pet, actor, room) {
return false;
}
```
+
+---
+
+```javascript
+function PetLeave(pet, actor, room) {
+}
+```
+
+`PetLeave()` is called immediately when
+[PetObject.GoMissing()](FUNCTIONS_PETS.md#petobjectgomissingrounds-int) is
+invoked. Use it to emit a message or trigger effects when the pet disappears.
+
+The pet is already marked as missing when this fires, so `pet.IsMissing()`
+returns `true` inside the handler.
+
+There is no return value.
+
+| Argument | Explanation |
+| --- | --- |
+| pet | [PetObject](FUNCTIONS_PETS.md) — the pet. |
+| actor | [ActorObject](FUNCTIONS_ACTORS.md) — the player who owns the pet. |
+| room | [RoomObject](FUNCTIONS_ROOMS.md) — the room both are in. |
+
+**Example:**
+```javascript
+function PetLeave(pet, actor, room) {
+ room.SendText(pet.NameSimple() + ' darts into the shadows and disappears!');
+}
+```
+
+---
+
+```javascript
+function PetReturn(pet, actor, room) {
+}
+```
+
+`PetReturn()` is called the round the pet's `MissingCountdown` reaches zero.
+Use it to announce the pet's return or apply any effects.
+
+The countdown has already reached zero when this fires, so `pet.IsMissing()`
+returns `false` inside the handler.
+
+There is no return value.
+
+| Argument | Explanation |
+| --- | --- |
+| pet | [PetObject](FUNCTIONS_PETS.md) — the pet. |
+| actor | [ActorObject](FUNCTIONS_ACTORS.md) — the player who owns the pet. |
+| room | [RoomObject](FUNCTIONS_ROOMS.md) — the room both are in. |
+
+**Example:**
+```javascript
+function PetReturn(pet, actor, room) {
+ room.SendText(pet.NameSimple() + ' trots back to your side.');
+}
+```
diff --git a/internal/characters/character.go b/internal/characters/character.go
index 82df32e71..074c21cbf 100644
--- a/internal/characters/character.go
+++ b/internal/characters/character.go
@@ -1442,7 +1442,11 @@ func (c *Character) MovementCost() int {
}
func (c *Character) StatMod(statName string) int {
- return c.Equipment.StatMod(statName) + c.Buffs.StatMod(statName) + c.Pet.StatMod(statName)
+ petMod := 0
+ if !c.Pet.IsMissing() {
+ petMod = c.Pet.StatMod(statName)
+ }
+ return c.Equipment.StatMod(statName) + c.Buffs.StatMod(statName) + petMod
}
// returns true if something has changed.
@@ -2262,7 +2266,7 @@ func (c *Character) reapplyPermabuffs(removedItems ...items.Item) {
}
// Apply any buffs from pet
- if c.Pet.Exists() {
+ if c.Pet.Exists() && !c.Pet.IsMissing() {
for _, buffId := range c.Pet.GetBuffs() {
buffIdCount[buffId] = 100 // Don't allow pet buffs to be removed, keep this number high
}
diff --git a/internal/combat/combat.go b/internal/combat/combat.go
index 7243063bf..551250bcb 100644
--- a/internal/combat/combat.go
+++ b/internal/combat/combat.go
@@ -405,52 +405,54 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char
}
// Pet has a 20% chance per attack round to join the fight (once, regardless of weapon count)
- chance, petDmg := sourceChar.Pet.GetEffectiveDamage()
- if chance > 0 && util.RollDice(1, chance) <= chance {
- if sourceChar.RoomId == targetChar.RoomId {
- if sourceChar.Pet.Exists() && petDmg.DiceRoll != `` {
+ if sourceChar.Pet.Exists() && !sourceChar.Pet.IsMissing() {
+ chance, petDmg := sourceChar.Pet.GetEffectiveDamage()
+ if chance > 0 && util.RollDice(1, chance) <= chance {
+ if sourceChar.RoomId == targetChar.RoomId {
+ if petDmg.DiceRoll != `` {
- pAttacks, pDCount, pDSides, pDBonus, critBuffs := sourceChar.Pet.GetDiceRoll()
- combatMsgs := sourceChar.Pet.GetCombatMessages(string(targetType))
+ pAttacks, pDCount, pDSides, pDBonus, critBuffs := sourceChar.Pet.GetDiceRoll()
+ combatMsgs := sourceChar.Pet.GetCombatMessages(string(targetType))
- for p := 0; p < pAttacks; p++ {
+ for p := 0; p < pAttacks; p++ {
- if !Hits(0, targetChar.Stats.Speed.ValueAdj, 0) {
- targetDisplayName := fmt.Sprintf(`%s`, string(targetType), targetChar.Name)
- toAttackerMsg := combatMsgs.ApplyTokens(combatMsgs.Miss, sourceChar.Pet.DisplayName(), 0, targetDisplayName)
- attackResult.SendToSource(toAttackerMsg)
- continue
- }
+ if !Hits(0, targetChar.Stats.Speed.ValueAdj, 0) {
+ targetDisplayName := fmt.Sprintf(`%s`, string(targetType), targetChar.Name)
+ toAttackerMsg := combatMsgs.ApplyTokens(combatMsgs.Miss, sourceChar.Pet.DisplayName(), 0, targetDisplayName)
+ attackResult.SendToSource(toAttackerMsg)
+ continue
+ }
- attackTargetDamage := util.RollDice(pDCount, pDSides) + pDBonus
+ attackTargetDamage := util.RollDice(pDCount, pDSides) + pDBonus
- attackTargetDamage, _ = applyDefenseReduction(attackTargetDamage, targetChar.GetDefense())
+ attackTargetDamage, _ = applyDefenseReduction(attackTargetDamage, targetChar.GetDefense())
- attackResult.DamageToTarget += attackTargetDamage
+ attackResult.DamageToTarget += attackTargetDamage
- targetDisplayName := fmt.Sprintf(`%s`, string(targetType), targetChar.Name)
- petDisplayName := sourceChar.Pet.DisplayName()
+ targetDisplayName := fmt.Sprintf(`%s`, string(targetType), targetChar.Name)
+ petDisplayName := sourceChar.Pet.DisplayName()
- toAttackerMsg := combatMsgs.ApplyTokens(combatMsgs.ToOwner, petDisplayName, attackTargetDamage, targetDisplayName)
- attackResult.SendToSource(toAttackerMsg)
+ toAttackerMsg := combatMsgs.ApplyTokens(combatMsgs.ToOwner, petDisplayName, attackTargetDamage, targetDisplayName)
+ attackResult.SendToSource(toAttackerMsg)
- toDefenderMsg := combatMsgs.ApplyTokens(combatMsgs.ToTarget, petDisplayName, attackTargetDamage, targetDisplayName)
- attackResult.SendToTarget(toDefenderMsg)
+ toDefenderMsg := combatMsgs.ApplyTokens(combatMsgs.ToTarget, petDisplayName, attackTargetDamage, targetDisplayName)
+ attackResult.SendToTarget(toDefenderMsg)
- toAttackerRoomMsg := combatMsgs.ApplyTokens(combatMsgs.ToRoom, petDisplayName, attackTargetDamage, targetDisplayName)
- attackResult.SendToTargetRoom(toAttackerRoomMsg)
+ toAttackerRoomMsg := combatMsgs.ApplyTokens(combatMsgs.ToRoom, petDisplayName, attackTargetDamage, targetDisplayName)
+ attackResult.SendToTargetRoom(toAttackerRoomMsg)
- // pets doing max damage are considered "crits" and will always apply any special critBuffs
- if len(critBuffs) > 0 && (attackTargetDamage == (pDCount*pDSides)+pDBonus) {
- attackResult.BuffTarget = critBuffs
+ // pets doing max damage are considered "crits" and will always apply any special critBuffs
+ if len(critBuffs) > 0 && (attackTargetDamage == (pDCount*pDSides)+pDBonus) {
+ attackResult.BuffTarget = critBuffs
+ }
}
- }
+ }
}
}
}
-
}
+
return attackResult
}
diff --git a/internal/hooks/NewRound_UserRoundTick.go b/internal/hooks/NewRound_UserRoundTick.go
index 5bfbdde00..0b63b4728 100644
--- a/internal/hooks/NewRound_UserRoundTick.go
+++ b/internal/hooks/NewRound_UserRoundTick.go
@@ -97,9 +97,18 @@ func UserRoundTick(e events.Event) events.ListenerReturn {
user.Command(`zombieact`)
}
+ // Decrement pet missing countdown each round.
+ petJustReturned := false
+ if user.Character.Pet.Exists() && user.Character.Pet.IsMissing() {
+ if user.Character.Pet.DecrementMissing() {
+ petJustReturned = true
+ scripting.TryPetScriptEvent(`PetReturn`, uId)
+ }
+ }
+
// Fire PetAct script using the pet type's configured RoundActChance.
- // Not called while the owner is in combat.
- if user.Character.Pet.Exists() && user.Character.Aggro == nil && user.Character.Pet.RoundActChance > 0 && util.Rand(100) < user.Character.Pet.RoundActChance {
+ // Not called while the owner is in combat, or on the same round the pet returns.
+ if !petJustReturned && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() && user.Character.Aggro == nil && user.Character.Pet.RoundActChance > 0 && util.Rand(100) < user.Character.Pet.RoundActChance {
scripting.TryPetScriptEvent(`PetAct`, uId)
}
diff --git a/internal/pets/pets.go b/internal/pets/pets.go
index 7134c085a..c523217c7 100644
--- a/internal/pets/pets.go
+++ b/internal/pets/pets.go
@@ -19,16 +19,17 @@ import (
)
type Pet struct {
- Name string `yaml:"name,omitempty"` // Name of the pet (player provided hopefully)
- NameStyle string `yaml:"namestyle,omitempty"` // Optional color pattern to apply
- Type string `yaml:"type"` // type of pet
- RoundActChance int `yaml:"roundactchance,omitempty"` // 0-100 chance per round to fire PetAct script
- Food Food `yaml:"food,omitempty"` // how much food the pet has
- Level int `yaml:"level,omitempty"` // Pet level (1-10)
- LastMealRound uint8 `yaml:"lastmealround,omitempty"` // When the pet was last fed
- LastLevelCheck string `yaml:"lastlevelcheck,omitempty"` // "{year}.{day}" of last daily tick
- Abilities []PetAbility `yaml:"abilities,omitempty"` // Refreshed from definition file on Validate()
- Items []items.Item `yaml:"items,omitempty"` // Items held by this pet
+ Name string `yaml:"name,omitempty"` // Name of the pet (player provided hopefully)
+ NameStyle string `yaml:"namestyle,omitempty"` // Optional color pattern to apply
+ Type string `yaml:"type"` // type of pet
+ RoundActChance int `yaml:"roundactchance,omitempty"` // 0-100 chance per round to fire PetAct script
+ Food Food `yaml:"food,omitempty"` // how much food the pet has
+ Level int `yaml:"level,omitempty"` // Pet level (1-10)
+ LastMealRound uint8 `yaml:"lastmealround,omitempty"` // When the pet was last fed
+ LastLevelCheck string `yaml:"lastlevelcheck,omitempty"` // "{year}.{day}" of last daily tick
+ Abilities []PetAbility `yaml:"abilities,omitempty"` // Refreshed from definition file on Validate()
+ Items []items.Item `yaml:"items,omitempty"` // Items held by this pet
+ MissingCountdown int `yaml:"missingcountdown,omitempty"` // When non-zero, pet is absent
cachedAbility *PetAbility `yaml:"-"` // cached current ability
cachedLevel int `yaml:"-"` // level when cache was set
@@ -86,6 +87,30 @@ func (p *Pet) Exists() bool {
return p.Type != ``
}
+// IsMissing returns true when the pet is temporarily absent.
+func (p *Pet) IsMissing() bool {
+ return p.MissingCountdown > 0
+}
+
+// GoMissing sets the missing countdown to the given number of rounds.
+// A value of zero clears the missing state immediately.
+func (p *Pet) GoMissing(rounds int) {
+ if rounds < 0 {
+ rounds = 0
+ }
+ p.MissingCountdown = rounds
+}
+
+// DecrementMissing decrements the missing countdown by one.
+// Returns true if the countdown just reached zero (pet is returning this round).
+func (p *Pet) DecrementMissing() bool {
+ if p.MissingCountdown <= 0 {
+ return false
+ }
+ p.MissingCountdown--
+ return p.MissingCountdown == 0
+}
+
func (p *Pet) DisplayName() string {
name := p.Name
diff --git a/internal/rooms/roomdetails.go b/internal/rooms/roomdetails.go
index de37f0d4a..07fa6122e 100644
--- a/internal/rooms/roomdetails.go
+++ b/internal/rooms/roomdetails.go
@@ -264,7 +264,7 @@ func GetDetails(r *Room, user *users.UserRecord, tinymap ...[]string) RoomTempla
}
}
- if user.Character.Pet.Exists() && r.RoomId == user.Character.RoomId {
+ if user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() && r.RoomId == user.Character.RoomId {
details.VisiblePlayers = append(details.VisiblePlayers, fmt.Sprintf(`%s (your pet)`, user.Character.Pet.DisplayName()))
}
diff --git a/internal/rooms/rooms.go b/internal/rooms/rooms.go
index 458f51de5..36c2c3999 100644
--- a/internal/rooms/rooms.go
+++ b/internal/rooms/rooms.go
@@ -1207,7 +1207,7 @@ func (r *Room) GetMobs(findTypes ...FindFlag) []int {
continue
}
- if typeFlag&FindHasPet == FindHasPet && mob.Character.Pet.Exists() {
+ if typeFlag&FindHasPet == FindHasPet && mob.Character.Pet.Exists() && !mob.Character.Pet.IsMissing() {
mobMatches = append(mobMatches, mobId)
continue
}
@@ -1297,7 +1297,7 @@ func (r *Room) GetPlayers(findTypes ...FindFlag) []int {
continue
}
- if typeFlag&FindHasPet == FindHasPet && user.Character.Pet.Exists() {
+ if typeFlag&FindHasPet == FindHasPet && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
playerMatches = append(playerMatches, userId)
continue
}
diff --git a/internal/scripting/actor_func.go b/internal/scripting/actor_func.go
index 056878689..481ba49c2 100644
--- a/internal/scripting/actor_func.go
+++ b/internal/scripting/actor_func.go
@@ -714,7 +714,7 @@ func (a ScriptActor) GetPet() *ScriptPet {
if !a.characterRecord.Pet.Exists() {
return nil
}
- return GetPet(&a.characterRecord.Pet)
+ return GetPet(&a.characterRecord.Pet, a.userId)
}
func (a ScriptActor) GrantXP(xpAmt int, reason string) {
diff --git a/internal/scripting/pet.go b/internal/scripting/pet.go
index 919d3558f..19aaa77d5 100644
--- a/internal/scripting/pet.go
+++ b/internal/scripting/pet.go
@@ -42,7 +42,7 @@ func TryPetScriptEvent(eventName string, userId int) (bool, error) {
return false, errors.New("user has no pet")
}
- sPet := GetPet(&user.Character.Pet)
+ sPet := GetPet(&user.Character.Pet, userId)
if sPet == nil {
return false, errors.New("pet not found")
}
@@ -112,7 +112,7 @@ func TryPetCommand(cmd string, rest string, userId int) (bool, error) {
return false, ErrEventNotFound
}
- sPet := GetPet(&user.Character.Pet)
+ sPet := GetPet(&user.Character.Pet, userId)
if sPet == nil {
return false, ErrEventNotFound
}
diff --git a/internal/scripting/pet_func.go b/internal/scripting/pet_func.go
index ea0816dba..d194610d0 100644
--- a/internal/scripting/pet_func.go
+++ b/internal/scripting/pet_func.go
@@ -13,6 +13,7 @@ func setPetFunctions(vm *goja.Runtime) {
// mutations are reflected immediately without any extra save call.
type ScriptPet struct {
petRecord *pets.Pet
+ userId int
}
// Type returns the pet's type identifier (e.g. "dog", "cat", "owl").
@@ -120,6 +121,41 @@ func (p ScriptPet) ItemCount() int {
return 0
}
+// IsMissing returns true if the pet is currently absent (MissingCountdown > 0).
+func (p ScriptPet) IsMissing() bool {
+ if p.petRecord != nil {
+ return p.petRecord.IsMissing()
+ }
+ return false
+}
+
+// GoMissing causes the pet to go absent for the given number of rounds.
+// Pass 0 to return the pet immediately, firing PetReturn instead of PetLeave.
+// Any positive value fires PetLeave and begins the countdown.
+func (p ScriptPet) GoMissing(rounds int) {
+ if p.petRecord == nil {
+ return
+ }
+ if rounds <= 0 {
+ if !p.petRecord.IsMissing() {
+ return
+ }
+ p.petRecord.GoMissing(0)
+ if p.userId > 0 {
+ TryPetScriptEvent(`PetReturn`, p.userId)
+ }
+ return
+ }
+ if !p.petRecord.IsMissing() {
+ p.petRecord.GoMissing(rounds)
+ if p.userId > 0 {
+ TryPetScriptEvent(`PetLeave`, p.userId)
+ }
+ } else {
+ p.petRecord.GoMissing(rounds)
+ }
+}
+
// HasScript returns true if this pet type has a script file on disk.
func (p ScriptPet) HasScript() bool {
if p.petRecord != nil {
@@ -143,9 +179,15 @@ func (p ScriptPet) getScript() string {
// GetPet returns a ScriptPet wrapping the given pet pointer.
// Returns nil if pet is nil or does not exist.
-func GetPet(pet *pets.Pet) *ScriptPet {
+// Pass the owner's userId as the optional second argument to enable
+// script-triggered events such as GoMissing.
+func GetPet(pet *pets.Pet, userId ...int) *ScriptPet {
if pet == nil || !pet.Exists() {
return nil
}
- return &ScriptPet{petRecord: pet}
+ sp := &ScriptPet{petRecord: pet}
+ if len(userId) > 0 {
+ sp.userId = userId[0]
+ }
+ return sp
}
diff --git a/internal/scripting/schema.go b/internal/scripting/schema.go
index 29680873a..b6456dc67 100644
--- a/internal/scripting/schema.go
+++ b/internal/scripting/schema.go
@@ -405,6 +405,28 @@ func petScriptType() *ScriptTypeDef {
ReturnSemantics: "Return value is ignored.",
Stub: "function PetAct(pet, actor, room) {\n\n}\n",
},
+ {
+ Name: "PetLeave",
+ Description: "Called when the pet goes missing (GoMissing is invoked with a positive value). Shares the same signature as PetAct.",
+ Params: []ScriptFuncParam{
+ {Name: "pet", Type: "PetObject", Description: "The pet."},
+ {Name: "actor", Type: "ActorObject", Description: "The owner of the pet."},
+ {Name: "room", Type: "RoomObject", Description: "The room the pet and owner are in."},
+ },
+ ReturnSemantics: "Return value is ignored.",
+ Stub: "function PetLeave(pet, actor, room) {\n\n}\n",
+ },
+ {
+ Name: "PetReturn",
+ Description: "Called when the pet returns after its MissingCountdown reaches zero. Shares the same signature as PetAct.",
+ Params: []ScriptFuncParam{
+ {Name: "pet", Type: "PetObject", Description: "The pet."},
+ {Name: "actor", Type: "ActorObject", Description: "The owner of the pet."},
+ {Name: "room", Type: "RoomObject", Description: "The room the pet and owner are in."},
+ },
+ ReturnSemantics: "Return value is ignored.",
+ Stub: "function PetReturn(pet, actor, room) {\n\n}\n",
+ },
{
Name: "onCommand",
Description: "Called when any command is typed by the pet's owner.",
diff --git a/internal/usercommands/feed.go b/internal/usercommands/feed.go
index 061e766a5..cf0ac8abd 100644
--- a/internal/usercommands/feed.go
+++ b/internal/usercommands/feed.go
@@ -12,7 +12,7 @@ import (
func Feed(rest string, user *users.UserRecord, room *rooms.Room, flags events.EventFlag) (bool, error) {
- if !user.Character.Pet.Exists() {
+ if !user.Character.Pet.Exists() || user.Character.Pet.IsMissing() {
user.SendText(`You don't have a pet to feed.`)
return true, nil
}
diff --git a/internal/usercommands/get.go b/internal/usercommands/get.go
index 6a0958b67..a772e037b 100644
--- a/internal/usercommands/get.go
+++ b/internal/usercommands/get.go
@@ -75,7 +75,7 @@ func Get(rest string, user *users.UserRecord, room *rooms.Room, flags events.Eve
// Look for any pets in the room
//
petUserId = room.FindByPetName(args[len(args)-1])
- if petUserId == 0 && args[len(args)-1] == `pet` && user.Character.Pet.Exists() {
+ if petUserId == 0 && args[len(args)-1] == `pet` && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
petUserId = user.UserId
}
if petUserId > 0 {
diff --git a/internal/usercommands/give.go b/internal/usercommands/give.go
index 73799ee50..6f2ba54ff 100644
--- a/internal/usercommands/give.go
+++ b/internal/usercommands/give.go
@@ -224,7 +224,7 @@ func Give(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
// Look for any pets in the room
//
petUserId := room.FindByPetName(giveWho)
- if petUserId == 0 && giveWho == `pet` && user.Character.Pet.Exists() {
+ if petUserId == 0 && giveWho == `pet` && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
petUserId = user.UserId
}
if petUserId > 0 {
diff --git a/internal/usercommands/go.go b/internal/usercommands/go.go
index 5a8c18345..10d7d5bd0 100644
--- a/internal/usercommands/go.go
+++ b/internal/usercommands/go.go
@@ -199,7 +199,7 @@ func Go(rest string, user *users.UserRecord, room *rooms.Room, flags events.Even
))
// Tell the old room they are leaving
- if user.Character.Pet.Exists() {
+ if user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
room.SendText(
fmt.Sprintf(string(c.ExitRoomMessageWrapper),
@@ -216,7 +216,7 @@ func Go(rest string, user *users.UserRecord, room *rooms.Room, flags events.Even
}
// Tell everyone if the pet is following
- if user.Character.Pet.Exists() {
+ if user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
user.SendText(fmt.Sprintf(`%s follows you.`, user.Character.Pet.DisplayName()))
diff --git a/internal/usercommands/look.go b/internal/usercommands/look.go
index 2e7b5a9a3..2c305999b 100644
--- a/internal/usercommands/look.go
+++ b/internal/usercommands/look.go
@@ -15,6 +15,7 @@ import (
"github.com/GoMudEngine/GoMud/internal/rooms"
"github.com/GoMudEngine/GoMud/internal/templates"
"github.com/GoMudEngine/GoMud/internal/users"
+ "github.com/GoMudEngine/GoMud/internal/util"
)
func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.EventFlag) (bool, error) {
@@ -347,9 +348,23 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
// Look for any pets in the room
//
petUserId := room.FindByPetName(rest)
- if petUserId == 0 && rest == `pet` && user.Character.Pet.Exists() {
+ if petUserId == 0 && rest == `pet` && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
petUserId = user.UserId
}
+ // If the user typed "pet" or the pet's name but their own pet is missing, tell them.
+ if petUserId == 0 && user.Character.Pet.Exists() && user.Character.Pet.IsMissing() {
+ if rest == `pet` {
+ user.SendText(fmt.Sprintf(`%s is not here right now.`, user.Character.Pet.DisplayName()))
+ return true, nil
+ }
+ if petName := user.Character.Pet.Name; petName != `` {
+ if match, _ := util.FindMatchIn(rest, petName); match != `` {
+ user.SendText(fmt.Sprintf(`%s is not here right now.`, user.Character.Pet.DisplayName()))
+ return true, nil
+ }
+ }
+ }
+
if petUserId > 0 {
if petUser := users.GetByUserId(petUserId); petUser != nil {
diff --git a/internal/usercommands/pet.go b/internal/usercommands/pet.go
index 06f6faf5d..afe71d384 100644
--- a/internal/usercommands/pet.go
+++ b/internal/usercommands/pet.go
@@ -23,7 +23,7 @@ func Pet(rest string, user *users.UserRecord, room *rooms.Room, flags events.Eve
if args[0] == `name` {
- if !user.Character.Pet.Exists() {
+ if !user.Character.Pet.Exists() || user.Character.Pet.IsMissing() {
user.SendText(`You have no pet to name.`)
return true, nil
}
@@ -69,13 +69,9 @@ func Pet(rest string, user *users.UserRecord, room *rooms.Room, flags events.Eve
}
petUserId := room.FindByPetName(rest)
- if petUserId == 0 && rest == `pet` && user.Character.Pet.Exists() {
+ if petUserId == 0 && rest == `pet` && user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
petUserId = user.UserId
}
- if petUserId == 0 {
- user.SendText(`Can't find that to pet.`)
- return true, nil
- }
petUser := users.GetByUserId(petUserId)
if petUser == nil {
diff --git a/internal/usercommands/usercommands.go b/internal/usercommands/usercommands.go
index f1973b331..155b7609d 100644
--- a/internal/usercommands/usercommands.go
+++ b/internal/usercommands/usercommands.go
@@ -337,7 +337,7 @@ func TryCommand(cmd string, rest string, userId int, flags events.EventFlag) (bo
}
// Check if the pet script intercepts this command
- if user.Character.Pet.Exists() {
+ if user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
if handled, err := scripting.TryPetCommand(cmd, rest, user.UserId); err == nil && handled {
return true, nil
}
diff --git a/modules/gmcp/gmcp.Char.go b/modules/gmcp/gmcp.Char.go
index 4778727f3..d1e17a0b7 100644
--- a/modules/gmcp/gmcp.Char.go
+++ b/modules/gmcp/gmcp.Char.go
@@ -515,7 +515,7 @@ func (g *GMCPCharModule) GetCharNode(user *users.UserRecord, gmcpModule string)
payload.Pets = []GMCPCharModule_Payload_Pet{}
- if user.Character.Pet.Exists() {
+ if user.Character.Pet.Exists() && !user.Character.Pet.IsMissing() {
p := GMCPCharModule_Payload_Pet{
Name: user.Character.Pet.Name,