From 4a64092408c3ee3acd511e0e7d550ea144e9adda Mon Sep 17 00:00:00 2001 From: Volte6 <143822+Volte6@users.noreply.github.com> Date: Sat, 16 May 2026 18:05:54 -0700 Subject: [PATCH] pet scripting, pet attack messages, pet attack frequency, --- .../building/scripting/FUNCTIONS_ACTORS.md | 14 +- .../building/scripting/FUNCTIONS_PETS.md | 202 +++++++++++++ .../guides/building/scripting/README.md | 5 + .../building/scripting/SCRIPTING_PETS.md | 135 +++++++++ _datafiles/html/admin/pets-api.html | 158 ++++++++-- _datafiles/html/admin/pets.html | 107 ++++++- _datafiles/world/default/pets/cat.js | 14 + _datafiles/world/default/pets/cat.yaml | 1 + _datafiles/world/default/pets/dog.js | 16 + _datafiles/world/default/pets/dog.yaml | 1 + _datafiles/world/default/pets/mule.js | 14 + _datafiles/world/default/pets/mule.yaml | 1 + _datafiles/world/default/pets/owl.js | 14 + _datafiles/world/default/pets/owl.yaml | 1 + internal/hooks/NewRound_UserRoundTick.go | 6 + internal/pets/admin.go | 28 ++ internal/pets/pets.go | 24 ++ internal/scripting/actor_func.go | 10 +- internal/scripting/memory.go | 1 + internal/scripting/pet.go | 276 ++++++++++++++++++ internal/scripting/pet_func.go | 151 ++++++++++ internal/scripting/schema.go | 47 +++ internal/scripting/scripting.go | 3 + internal/usercommands/usercommands.go | 7 + internal/web/api_routes.go | 4 +- internal/web/api_v1_pets.go | 49 ++++ 26 files changed, 1245 insertions(+), 44 deletions(-) create mode 100644 _datafiles/guides/building/scripting/FUNCTIONS_PETS.md create mode 100644 _datafiles/guides/building/scripting/SCRIPTING_PETS.md create mode 100644 _datafiles/world/default/pets/cat.js create mode 100644 _datafiles/world/default/pets/dog.js create mode 100644 _datafiles/world/default/pets/mule.js create mode 100644 _datafiles/world/default/pets/owl.js create mode 100644 internal/scripting/pet.go create mode 100644 internal/scripting/pet_func.go diff --git a/_datafiles/guides/building/scripting/FUNCTIONS_ACTORS.md b/_datafiles/guides/building/scripting/FUNCTIONS_ACTORS.md index 45568d5cc..3124407ef 100644 --- a/_datafiles/guides/building/scripting/FUNCTIONS_ACTORS.md +++ b/_datafiles/guides/building/scripting/FUNCTIONS_ACTORS.md @@ -571,7 +571,19 @@ Returns the shorthand ID string to refer to the mob or player ( `@123` or `#122` Uncurses any objects the target has equipped ## [ActorObject.GetPet()](/internal/scripting/actor_func.go) -Returns the pet object for the actor, or null +Returns the [PetObject](FUNCTIONS_PETS.md) for this actor, or `null` if the actor has no pet. + +_Note: Only meaningful for user actors. Mob actors always return `null`._ + +_Note: The returned PetObject is a live reference. Mutations such as `SetName()`, `Feed()`, and `Starve()` take effect immediately on the owner's character data._ + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null) { + room.SendText(pet.Name() + ' is here!'); +} +``` ## [ActorObject.GrantXP(xpAmt int, reason string)](/internal/scripting/actor_func.go) Gives experience points to the actor diff --git a/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md b/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md new file mode 100644 index 000000000..3b87d3fcc --- /dev/null +++ b/_datafiles/guides/building/scripting/FUNCTIONS_PETS.md @@ -0,0 +1,202 @@ +# PetObject + +PetObject represents a pet owned by a player. It is obtained by calling +[ActorObject.GetPet()](FUNCTIONS_ACTORS.md#actorobjectgetpet) and returns +`null` when the actor has no pet. + +The object is a **live reference** into the owner's character data. Mutations +take effect immediately and are persisted the next time the character is saved. + +- [PetObject](#petobject) + - [PetObject.Type() string](#petobjecttype-string) + - [PetObject.Name() string](#petobjectname-string) + - [PetObject.NameSimple() string](#petobjectnamesimple-string) + - [PetObject.SetName(name string)](#petobjectsetnamename-string) + - [PetObject.Level() int](#petobjectlevel-int) + - [PetObject.Food() string](#petobjectfood-string) + - [PetObject.FoodLevel() int](#petobjectfoodlevel-int) + - [PetObject.Feed()](#petobjectfeed) + - [PetObject.Starve()](#petobjectstarve) + - [PetObject.GetStatMod(statName string) int](#petobjectgetstatmodstatname-string-int) + - [PetObject.GetCapacity() int](#petobjectgetcapacity-int) + - [PetObject.ItemCount() int](#petobjectitemcount-int) + - [PetObject.HasScript() bool](#petobjecthasscript-bool) + +--- + +## [PetObject.Type() string](/internal/scripting/pet_func.go) +Returns the pet's type identifier, such as `"dog"`, `"cat"`, `"owl"`, or `"mule"`. + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null && pet.Type() === 'dog') { + room.SendText(pet.NameSimple() + ' barks!'); +} +``` + +--- + +## [PetObject.Name() string](/internal/scripting/pet_func.go) +Returns the pet's full display name, including ANSI colour tags and any hunger +indicator such as `(Hungry)` or `(Starving)`. + +Use this when sending the name to players as part of room or character output. +Use [NameSimple()](#petobjectnamesimple-string) when you need a plain string +for comparisons or concatenation. + +--- + +## [PetObject.NameSimple() string](/internal/scripting/pet_func.go) +Returns the plain text name of the pet with no colour tags or hunger indicator. +Falls back to the type identifier if the player has not given the pet a custom +name. + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null) { + room.SendText(pet.NameSimple() + ' trots along behind you.'); +} +``` + +--- + +## [PetObject.SetName(name string)](/internal/scripting/pet_func.go) +Renames the pet. The change is reflected immediately in `Name()` and +`NameSimple()`. + +Pass an empty string to clear the custom name and revert the display name to +the pet's type identifier. + +| Argument | Explanation | +| --- | --- | +| name | The new plain text name, or `""` to clear it. | + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null && pet.NameSimple() === pet.Type()) { + pet.SetName('Biscuit'); + actor.SendText('You name your pet Biscuit.'); +} +``` + +--- + +## [PetObject.Level() int](/internal/scripting/pet_func.go) +Returns the pet's current level, from 1 (minimum) to 10 (maximum). + +Pet level increases when the pet is well-fed at the daily tick and decreases +when the pet is starving. + +--- + +## [PetObject.Food() string](/internal/scripting/pet_func.go) +Returns the pet's current hunger state as a human-readable string. + +| Value | Meaning | +| --- | --- | +| `"Starving"` | Food level 0 — pet will lose a level at the next daily tick | +| `"Hungry"` | Food level 1 | +| `"Satisfied"` | Food level 2 | +| `"Full"` | Food level 3 — pet will gain a level at the next daily tick | + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null && pet.Food() === 'Starving') { + actor.SendText(pet.NameSimple() + ' looks at you with desperate eyes.'); +} +``` + +--- + +## [PetObject.FoodLevel() int](/internal/scripting/pet_func.go) +Returns the pet's raw hunger value: `0` (Starving) through `3` (Full). + +Useful when you need a numeric comparison rather than a string. + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null && pet.FoodLevel() < 2) { + actor.SendText(pet.NameSimple() + ' nudges you hopefully.'); +} +``` + +--- + +## [PetObject.Feed()](/internal/scripting/pet_func.go) +Increases the pet's hunger level by one step, up to a maximum of 3 (Full). +Has no effect if the pet is already full. + +_Note: This does not consume any item from the player's inventory. Use it when +a script wants to feed the pet as a side-effect of some other action._ + +--- + +## [PetObject.Starve()](/internal/scripting/pet_func.go) +Decreases the pet's hunger level by one step, down to a minimum of 0 +(Starving). Has no effect if the pet is already starving. + +--- + +## [PetObject.GetStatMod(statName string) int](/internal/scripting/pet_func.go) +Returns the stat modifier the pet currently grants its owner for the named +stat. Returns `0` if the pet provides no modifier for that stat at its current +level. + +Stat modifiers are defined per ability level in the pet's YAML definition and +scale up as the pet levels. + +| Argument | Explanation | +| --- | --- | +| statName | A stat name such as `"strength"`, `"speed"`, `"smarts"`, `"vitality"`, `"mysticism"`, or `"perception"`. | + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null) { + var speedBonus = pet.GetStatMod('speed'); + if (speedBonus > 0) { + actor.SendText(pet.NameSimple() + ' makes you feel quicker. (+' + speedBonus + ' speed)'); + } +} +``` + +--- + +## [PetObject.GetCapacity() int](/internal/scripting/pet_func.go) +Returns the number of items the pet can carry at its current level. Returns `0` +if the pet type has no carry ability (e.g. a cat or owl). + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null && pet.GetCapacity() > 0) { + actor.SendText(pet.NameSimple() + ' can carry ' + pet.GetCapacity() + ' item(s).'); +} +``` + +--- + +## [PetObject.ItemCount() int](/internal/scripting/pet_func.go) +Returns the number of items the pet is currently carrying. + +**Example:** +```javascript +var pet = actor.GetPet(); +if (pet !== null) { + var free = pet.GetCapacity() - pet.ItemCount(); + actor.SendText(pet.NameSimple() + ' has ' + free + ' free slot(s).'); +} +``` + +--- + +## [PetObject.HasScript() bool](/internal/scripting/pet_func.go) +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. diff --git a/_datafiles/guides/building/scripting/README.md b/_datafiles/guides/building/scripting/README.md index 7acda9267..18e5a0a02 100644 --- a/_datafiles/guides/building/scripting/README.md +++ b/_datafiles/guides/building/scripting/README.md @@ -14,6 +14,9 @@ See [Item Scripting](SCRIPTING_ITEMS.md) # Buff Scripting See [Buff Scripting](SCRIPTING_BUFFS.md) +# Pet Scripting +See [Pet Scripting](SCRIPTING_PETS.md) + # Spell Scripting See [Spell Scripting](SCRIPTING_SPELLS.md) @@ -27,6 +30,8 @@ See [Spell Scripting](SCRIPTING_SPELLS.md) [ItemObject Functions](FUNCTIONS_ITEMS.md) - Functions that query or alter item data. +[PetObject Functions](FUNCTIONS_PETS.md) - Functions that query or alter pet data. + [Utility Functions](FUNCTIONS_UTIL.md) - Helper and info functions. [Messaging Functions](FUNCTIONS_MESSAGING.md) - Helper and info functions. diff --git a/_datafiles/guides/building/scripting/SCRIPTING_PETS.md b/_datafiles/guides/building/scripting/SCRIPTING_PETS.md new file mode 100644 index 000000000..a880250d2 --- /dev/null +++ b/_datafiles/guides/building/scripting/SCRIPTING_PETS.md @@ -0,0 +1,135 @@ +# Pet Scripting + +Example scripts: +* [Dog pet script](/_datafiles/world/default/pets/scripts/dog.js) +* [Cat pet script](/_datafiles/world/default/pets/scripts/cat.js) +* [Owl pet script](/_datafiles/world/default/pets/scripts/owl.js) +* [Mule pet script](/_datafiles/world/default/pets/scripts/mule.js) + +## Script paths + +Pet scripts reside in the same directory as the pet's YAML definition file, +with `.js` replacing `.yaml` in the filename. + +For example, the `dog` pet type defined at: + +``` +_datafiles/world/default/pets/dog.yaml +``` + +loads its script from: + +``` +_datafiles/world/default/pets/dog.js +``` + +## Script scope + +Variables defined at the global scope of a pet script are shared across all +owners of that pet type. If you need to store data specific to a single owner, +use the owner's [SetTempData / GetTempData](FUNCTIONS_ACTORS.md#actorobjectsettempdatakey-string-value-any) +or [SetMiscCharacterData / GetMiscCharacterData](FUNCTIONS_ACTORS.md#actorobjectsetmisccharacterdatakey-string-value-any). + +## Script functions + +The following functions are invoked automatically when defined in a pet script: + +--- + +``` +function PetAct(pet PetObject, actor ActorObject, room RoomObject) { +} +``` + +`PetAct()` is called each round with a probability determined by the pet +type's `RoundActChance` property (0–100). If `RoundActChance` is 0 the +function is never called. If it is 100 it is called every round. + +`PetAct` is **not** called while the pet's owner is in combat. + +The chance is evaluated by the engine before the function is invoked, so +`PetAct` itself does not need a top-level probability check. Add your own +`RandInt` guard inside the function only if you want behaviour that fires +less often than the configured chance. + +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 PetAct(pet, actor, room) { + // ~5% chance per round to do something visible + if (RandInt(1, 100) <= 5) { + room.SendText(pet.NameSimple() + ' sniffs the air curiously.'); + } +} +``` + +--- + +``` +function onCommand(cmd string, rest string, pet PetObject, actor ActorObject, room RoomObject) { +} +``` + +`onCommand()` is called whenever the pet's owner types any command. + +Returning `true` halts further command processing (the command is consumed). +Returning `false` allows the command to continue through the normal pipeline. + +This fires after buff `onCommand` handlers and before item `onCommand` +handlers. + +| Argument | Explanation | +| --- | --- | +| cmd | The command word typed by the owner, such as `"look"` or `"attack"`. | +| rest | Everything entered after the command word (may be empty). | +| pet | [PetObject](FUNCTIONS_PETS.md) | +| actor | [ActorObject](FUNCTIONS_ACTORS.md) — the owner. | +| room | [RoomObject](FUNCTIONS_ROOMS.md) | + +**Example:** +```javascript +function onCommand(cmd, rest, pet, actor, room) { + if (cmd === 'attack') { + if (RandInt(1, 4) === 1) { + room.SendText(pet.NameSimple() + ' growls menacingly.'); + } + } + return false; +} +``` + +--- + +``` +function onCommand_{command}(rest string, pet PetObject, actor ActorObject, room RoomObject) { +} +``` + +`onCommand_{command}()` is called when the owner types the specific command +named after the underscore. + +If a specific handler is defined, the generic `onCommand()` will **not** fire +for that command. + +| Argument | Explanation | +| --- | --- | +| rest | Everything entered after the command word (may be empty). | +| pet | [PetObject](FUNCTIONS_PETS.md) | +| actor | [ActorObject](FUNCTIONS_ACTORS.md) — the owner. | +| room | [RoomObject](FUNCTIONS_ROOMS.md) | + +**Example:** +```javascript +// Called only when the owner types 'pet' (the pet command) +function onCommand_pet(rest, pet, actor, room) { + room.SendText(pet.NameSimple() + ' wags its tail happily.'); + return false; +} +``` diff --git a/_datafiles/html/admin/pets-api.html b/_datafiles/html/admin/pets-api.html index a2662d898..2bf2137dc 100644 --- a/_datafiles/html/admin/pets-api.html +++ b/_datafiles/html/admin/pets-api.html @@ -14,6 +14,7 @@ .method-get { background: var(--color-success-bg); color: var(--color-success-text); } .method-post { background: var(--color-info-bg); color: var(--color-info-text); } .method-patch { background: var(--color-warning-bg); color: var(--color-warning-text); } + .method-put { background: var(--color-warning-bg); color: var(--color-warning-text); } .method-delete { background: var(--color-error-bg); color: var(--color-error-text); } .api-path { font-family: monospace; font-size: 0.9rem; color: var(--color-primary); } .api-desc { font-size: 0.85rem; color: var(--color-text-muted); margin-left: auto; text-align: right; } @@ -42,13 +43,15 @@ .params-table td { padding: 0.4rem 0.6rem; border: 1px solid var(--color-border); vertical-align: top; } .params-table td:first-child { font-family: monospace; color: var(--color-primary); } .required { color: var(--color-error-text); font-size: 0.75rem; font-weight: 600; } + .section-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-subtle); margin: 1rem 0 0.4rem; }
REST endpoints for listing, creating, updating, and deleting pet type definitions. View / Edit
+REST endpoints for listing, creating, updating, deleting, and scripting pet type definitions. View / Edit
Creates a new pet type definition. The type field is the unique identifier and is lowercased automatically. Returns 409 if the type already exists.
Creates a new pet type definition. The type field is the unique identifier and is lowercased automatically. Returns 400 if the type already exists.
| Field | Type | Description |
|---|---|---|
| type required | string | Unique pet type identifier (e.g. wolf). Lowercased on save. |
| name | string | Display name shown when the player has not named their pet. |
| namestyle | string | Optional color pattern applied to the pet name (e.g. :rainbow). |
| abilities | object[] | Array of ability definitions. Each ability has levelgranted, combatchance, damage, attackmessages, statmods, buffids, and capacity. attackmessages is only meaningful when damage is set; fields: toowner, totarget, toroom, miss. When damage is set, combatchance is enforced to be at least 1. |
| roundactchance | int | 0–100 chance per round that PetAct() is invoked. Defaults to 0 (disabled). Not called while the owner is in combat. |
| abilities | object[] |
+ Array of ability objects. Each entry may contain: + • levelgranted (int) — pet level at which this ability unlocks (1–10)+ • combatchance (int, 0–100) — percent chance per combat round the pet attacks+ • damage ({diceroll: string}) — damage dice roll (e.g. "1d6+2")+ • attackmessages ({toowner, totarget, toroom, miss}) — optional custom combat messages; tokens: {petname}, {damage}, {targetname}+ • statmods (object) — stat bonuses granted to the owner (e.g. {"strength": 2})+ • buffids (int[]) — buff IDs permanently applied to the owner+ • capacity (int) — number of items the pet can carry
+ |
{petname} is the pet type identifier (e.g. wolf). Fields in the request body overwrite the existing definition. The type is always preserved from the URL.
{petname} is the pet type identifier (e.g. wolf). The request body is merged over the existing definition. The type is always preserved from the URL.
| Parameter | In | Description |
|---|
Request body accepts the same fields as POST: name, namestyle, roundactchance, abilities. The type field in the body is ignored; the URL value is always used.
{petname} is the pet type identifier. Removes the YAML file from disk and unregisters the type from memory.
{petname} is the pet type identifier. Removes the YAML definition and any associated .js script file from disk, and unregisters the type from memory.
| Parameter | In | Description |
|---|
| Parameter | In | Description |
|---|---|---|
| petname required | path | Pet type identifier. |
Writes the provided JavaScript to the pet's script file alongside its YAML definition (e.g. pets/wolf.js). Send an empty string to delete the script file. The in-memory VM cache for this pet type is invalidated automatically so the new script is used on the next invocation.
| Parameter / Field | In | Description |
|---|---|---|
| petname required | path | Pet type identifier. |
| script required | body (JSON) | JavaScript source string. Pass "" to remove the script file. |
: for a color pattern.PetAct() script function is called. Set to 0 to disable scripted ambient behaviour. Not called while the owner is in combat..js file alongside the pet’s .yaml definition. Clear the field and save to delete the script.