Forgot? Join

Wolf systems

Wolf Roleplay and Game Mode

Switch Game Mode on and the region’s game appears in your viewer: health, stamina and hunger, a bag, pets, weapons, an action bar and a target frame, drawn by the viewer itself with no HUD to wear. Anyone can build their own game with the wolfGame functions.

Wolf Territories only. This needs WolfViewer or WolfStorm on a Wolf Territories region. On other grids the menu items, buttons and panels are not shown at all.

Other viewers do not show the game screens. The roleplay kit's objects fall back to local chat and ordinary menus only when the game refuses a notice or menu, for example when you are not playing.

Overview

Lone Wolf on the Sandbox beach with Game Mode on. In the middle of the screen is the Roleplay window in the wolves theme: a wolf portrait ringed by stat colours, a GAME MODE ON switch, a wallet showing 100 Bones, and the tabs Stats, Combat, Bag, Pets, Game, Look and Help, with Combat open showing empty Main hand, Off hand and Ranged weapon slots and a paper doll of a wolf with armour slots. Across the bottom is the game bar: the wolf portrait, Health 100, Stamina 100 and Hunger 78 orbs, twelve action slots numbered 1 to 9, 0, minus and equals, Main and Ranged weapon slots, the page buttons Character, Combat, Bag, Pets, Game, Look, Help, Panel and GAME: ON, eight bag slots and 100 Bones.
ScreenshotGame Mode on the Sandbox region: the Roleplay window open on its Combat tab, and the game bar across the bottom.

Game Mode: on or off

Game Mode is off until you switch it on. Switched on, the region’s game shows and you are joined to it automatically wherever you go; switched off, the game screens go away. Switch it with any of these:

  • World > Game Mode, or the toolbar’s Game Mode button (it says On or Off).
  • The paw icon in the bottom-right corner, beside the Toolbar, Car, Plane and Boat icons. With Game Mode off it switches it on; with it on it shows or hides the game bar, and the game keeps running while the bar is hidden.
  • GAME: ON / OFF on the game bar, and the switch at the top of the Roleplay window.

Some regions force Game Mode on (so nobody can switch it off to avoid a fight). There the switch shows a padlock and reads LOCKED ON, and Leave this game is hidden. Your own choice comes back on the next region.

Two kinds of game

Managed games

Run by the grid. Stats regenerate and decay by themselves, health is the avatar’s own health, death respawns you at the game’s spawn point, and the game’s money, items and pets are kept by the grid, the same on every region of the game. The Wolf Territories game is one.

External games

An existing system with its own server and rules (Knightwood’s KRS, for example) that uses the viewer as its screen: its scripts set what the player sees, and nothing on the grid ticks.

Each game has its own money, separate from WT$.

Playing

The game bar on its own: the name plate Lone Wolf above a wolf portrait, red Health 100, amber Stamina 100 and green Hunger 78 orbs, twelve empty action slots numbered 1 to 9, 0, minus and equals, WEAPONS Main and Ranged slots, the page buttons Character, Combat, Bag, Pets, Game, Look, Help, Panel and GAME: ON, eight empty bag slots, 100 Bones and Bag empty.
ScreenshotThe game bar across the bottom of the screen.

The game bar

  • Left: your portrait with the experience ring round it and your level, then the game’s first three stats as orbs, each with its number and name. Click either for your character.
  • Centre: twelve action slots. The keys 1 to 9, 0, - and = press them, while Game Mode is on and you are not typing. Then your weapon slots: the rounds left, a sweep while reloading; click a weapon to reload it, or to open the Combat page.
  • Right: the first eight things in your bag and your wallet (for the Wolf Territories game, Bones).
  • Along the top: the page buttons Character, Combat, Bag, Pets, Game, Look and Help open the Roleplay window on that page (press again to close it). PANEL turns the plate behind the bar on or off; GAME: ON / OFF is the Game Mode switch.
  • One panel uses the bottom of the screen at a time: the game bar steps aside for the car dashboard and the flight or sailing deck and comes back when they go. The paw icon, or switching Game Mode on, takes the bottom back.
The Roleplay window in the wolves theme, open on Combat: weapon slots Main hand, Off hand and Ranged, all empty; armour slots Head, Neck, Body, Hands, Legs, Feet and Ring around a photograph of a wolf; an Abilities note and an Effects on you note; and the buttons Leave this game and Clear screen effects.
ScreenshotThe Roleplay window, Combat tab.

The Roleplay window

Open it from World > Roleplay, the toolbar’s Roleplay button, or the game bar. The top shows your portrait, name and game, the Game Mode switch and your wallet.

The Roleplay window tabs
TabWhat is on it
StatsEvery stat as a ring with its number, your level, the game's information panel, and markers on other regions (Show on map, Teleport: both ask first).
CombatYour weapon cards and the armour slots round a paper doll, each with Unequip; your abilities; the buffs and debuffs on you.
BagA grid of slots: what you hold, with its count, and everything else the game has at 0. Items with a slot have an Equip button.
PetsA card for each pet: its picture, name, type, age and stats. "Died" shows as a word.
GameThe game's logo, name, who runs it, whether you are playing, its description, rules and website.
LookThe window's background picture: the theme's own, a texture dragged from your inventory, or a picture file from your computer, with its strength and a tint (Natural, Amber, Moonlight, Blood, Forest or Dusk).
HelpHow to play, and the photograph and font credits.

At the foot: Leave this game leaves only this game (it asks first; your stats are kept, and Game Mode stays on), and Clear screen effects clears any flash, blur or blackout and puts the downed overlay away. The window closes with its X or Escape, and every control can be reached with Tab.

What else you will see

Notices

Top right, at most four. Information and success notices stay 6 seconds, warnings 10; a danger notice stays until you close it.

Dialogs and buttons

Questions from the game, top right like ordinary script menus, and the game's own buttons in the Roleplay window.

Targets

With Game Mode on, click an avatar or object to target it: its frame shows top centre with its health, level, and a coloured rim with the word Hostile, Neutral or Friendly. Escape or the frame's X clears it.

Down and dead

A downed player is held still under a dark overlay that may count down; you can still chat for help. At zero health you die and the game respawns you.

Screen effects

A flash when hit, a pulse at low health, blur, blackout, greyscale or a shake. Escape or Clear screen effects always clears them, and they are toned down if your system asks for reduced motion.

Markers and tags

Markers on your screen, minimap and world map; game tags, a line under an avatar's name drawn as a game tag so it cannot pass for a group title.

Unverified

Anything that does not come from the game's own registered scripts (a worn HUD, or an object named like the game) is marked Unverified, with an icon and the word, and drawn plain. A web page from an unverified script asks before it opens.

The Wolf Territories game

The grid’s own game, in the wolves theme. It runs on every Wolf Territories region that has no game of its own: switch Game Mode on and you are playing.

Stats

The Wolf Territories game stats
StatHow it works
HealthYou die at 0 and respawn. It comes back by 1 a minute.
StaminaComes back by 10 a minute. At 0 you slow down. Hunting and fishing cost stamina.
HungerHow full you are. It falls by 1 a minute; at 0 you slow down. Eat to fill it.

Bones, items and pets

  • Bones are the money. You start with 100, and a new player gets 3 Meat, 2 Fresh Water and 2 Berries.
  • The most of each item you can carry: Meat 50, Fish 50, Berries 50, Healing Herbs 20, Fresh Water 20, Antler 99, Pelt 99, Raven Feather 99.
  • Pets: the Wolf Pup and the Raven, at most 3 of each. Each has Fed and Happiness (0 to 100); both fall by 1 a minute, even while you are away, so feed your pets and play with them.

The kit

The Wolf Territories Roleplay Kit, from the Wolf Games account. Every object and script in it is full permission: rez them, take them apart and read the scripts. Gathering objects keep their waiting time on you, not on the object, so ten bushes do not give ten times the berries.

The Wolf Territories Roleplay Kit objects
ObjectWhat it does
Berry Bush2-4 Berries, once every 5 minutes.
Fishing JettyCast a line (5 stamina): 7 casts in 10 catch 1-2 Fish. Stay within 20 m. Once every 2 minutes.
Fresh Water Spring3 Fresh Water, once every 3 minutes.
Herb Patch1-2 Healing Herbs, once every 10 minutes.
Deer (Hunting Ground)Hunt it (15 stamina): 6 in 10 succeed, 2-3 Meat and sometimes a Pelt and an Antler. A miss can end with a kick (8 damage). Once every 5 minutes.
Raven PerchUsually a Raven Feather, sometimes two, sometimes nothing. Once every 4 minutes.
CampfireCook and eat: Meat gives back 30 hunger, Fish 25, Berries 8.
Drinking Pool1 Fresh Water for 40 stamina.
Healer's Stone1 Healing Herbs for 25 health (not while you are down).
Pack TraderBuys Pelts (12 Bones), Antlers (8) and Raven Feathers (3); sells Meat (6), Fish (5), 3 Berries (4), 2 Fresh Water (3) and Healing Herbs (10). Every trade happens completely or not at all.
Pet DenAdopt a Wolf Pup (60 Bones) or a Raven (40 Bones): name it, pay, and get the pet's object.
Wolf Pup and RavenRez one and touch it: Feed meat or fish, Play (+20 Happiness, once a minute), Rename, Change pet. The pet itself is kept by the game, so every copy of the object can show it.

Why the kit’s objects may change your game

Only a game’s own scripts may move its items, money and pets, or change a player’s stats. Every script in the kit is registered for the Wolf Territories game. The registration belongs to the script’s exact contents: copying an object keeps it working, but if you edit a kit script and save it, it becomes a new script the game refuses (“only the game’s own scripts may ...”). Otherwise anyone could change a berry bush to hand out a thousand berries.

Make your own game

  1. On a region you hold, create the game with wolfGameCreate: a name, a currency and how death works. The region then runs it.
  2. Add its stats with wolfGameDefineStat, and its items and pets with wolfGameDefineItem, wolfGameDefinePet and wolfGameDefinePetStat.
  3. Build its objects. Register each script that may move money, items and pets with wolfGameTrustScript (register it again after every edit).
  4. To run the same game on your other regions, read its key with wolfGameKey() on the first region, then call wolfGameRegister(key) on each of the others.

Every call returns TRUE when it worked; when it is refused, wolfGetLastError() says why (an unknown key, a value out of range, or a rights rule).

Creating a game

// From the Sandbox test box: a managed game with Test Coins, health tied to the
// avatar's own, stamina that slows you at zero, and hunger that drains health.
wolfGameCreate(["name", "Sandbox Test", "kind", "managed",
    "description", "Wolf Roleplay test game", "currency", "Test Coins", "symbol", "TC",
    "start", 50, "playerpay", 1, "death", "spawn", "respawn", 5]);
wolfGameDefineStat("health", ["label", "Health", "max", 100, "start", 100,
    "rate", 1, "health", 1, "colour", "#ef4444", "order", 0]);
wolfGameDefineStat("stamina", ["label", "Stamina", "max", 100, "start", 100,
    "rate", 10, "zero", "slow", "colour", "#eab308", "order", 1]);
wolfGameDefineStat("hunger", ["label", "Hunger", "max", 100, "start", 100,
    "rate", -30, "zero", "drain", "target", "health", "amount", 60, "colour", "#84cc16", "order", 2]);

wolfGameCreate and wolfGameSet rules

Game rules
KeyValue
nameThe game's name, unique, up to 64 characters. Required to create.
kind"managed" (the default) or "external".
theme"wolves" or "plain" (the default).
description / rulesText up to 500 / 2000 characters, shown on the Game tab.
websiteAn https:// address.
logoA texture key: the portrait and the Game tab's logo.
currency / symbolThe money's name (up to 24 characters) and symbol (up to 8).
currencyiconThe money's picture, a texture key.
startThe money a new player starts with (0 to start with).
playerpay1 lets players pay each other with their own objects.
death"spawn" (the default), "home" or "stay".
respawnSeconds before a dead player comes back, 0 to 600 (10 to start with).
spawnA vector: where players respawn, on this region (defaults to the object).
starterWhat a player's first join gives, e.g. "meat=3,water=2".
webhook / webhooksecretAn https:// address the grid tells about game events, and a 16 to 64 character secret that signs them.

Stats: wolfGameDefineStat rules

At most 16 stats. The first three (by order) are the orbs on the game bar.

Stat rules
KeyValue
labelThe name shown, up to 32 characters.
min / max / startWhole numbers; max must be more than min, and start between them (0, 100 and 100 to start with).
ratePer minute: positive regenerates, negative decays.
zeroWhat happens at the minimum: "none", "die", "slow" or "drain".
target / amountFor "drain": the stat to drain, and how much a minute.
colour#rrggbb.
iconA texture key, or a short text glyph (up to 16 characters).
visible / order0 or 1; the sort order.
health1 ties the stat to the avatar's own health.
keep1 keeps it through a respawn (experience, gold).

Items and pets

  • wolfGameDefineItem(name, rules): label, icon, max (per player, 0 = no limit), order; and for combat a kind and slot (see Combat). At most 200 items.
  • Give and take with wolfGameItemAdd / wolfGameItemTake. For any trade use wolfGameExchange(avatar, take, give, reason): one step, all or nothing, with "$" standing for the game’s money.
  • wolfGameDefinePet(type, rules): label, icon, max per player, order. wolfGameDefinePetStat(type, stat, rules): label, min, max, start, rate (per minute, and it keeps running while the pet is in inventory), zero ("none" or "die"), colour, icon, order.
  • wolfGamePetCreate(owner, type, name, data) returns the new pet’s key; keep it in the pet object. The pet lives on the grid, aged by real time.
Defining items and a pet type (farm_setup.lsl, 32 lines: show or hide)

Download farm_setup.lsl

// Wolf Roleplay example - FARM + PETS SETUP (run once by the game's owner on a region running
// their game). Defines the items the farm and the pet shop use, and the wolf pup pet type.
// Everything here can also be done from the control panel's Roleplay section.

say(string what, integer ok)
{
    if (ok) llOwnerSay(what + ": OK");
    else llOwnerSay(what + ": REFUSED - " + wolfGetLastError());
}

default
{
    touch_start(integer n)
    {
        if (llDetectedKey(0) != llGetOwner()) return;
        if (wolfGameKey() == "") { llOwnerSay("This region runs no game - wolfGameCreate first."); return; }

        // Items: what players hold, on the grid, the same on every region of the game.
        say("item seed",     wolfGameDefineItem("seed",     ["label", "Wheat seed", "max", 500, "order", 1]));
        say("item wheat",    wolfGameDefineItem("wheat",    ["label", "Wheat",      "max", 9999, "order", 2]));
        say("item pet_food", wolfGameDefineItem("pet_food", ["label", "Pet food",   "max", 200, "order", 3]));

        // A pet type and its stats. Rates are per MINUTE and keep running while the pet is in
        // inventory or its owner is offline - a pup left a week without food will be gone.
        say("pet wolf_pup", wolfGameDefinePet("wolf_pup", ["label", "Wolf pup", "max", 3]));
        say("pup hunger",    wolfGameDefinePetStat("wolf_pup", "hunger",
            ["label", "Food", "max", 100, "start", 80, "rate", -1, "zero", "die", "colour", "#84cc16", "order", 0]));
        say("pup happiness", wolfGameDefinePetStat("wolf_pup", "happiness",
            ["label", "Happy", "max", 100, "start", 80, "rate", -2, "zero", "none", "colour", "#eab308", "order", 1]));
    }
}
A market stall: every trade one wolfGameExchange (market_stall.lsl, 52 lines: show or hide)

Download market_stall.lsl

// Wolf Roleplay example - MARKET STALL. Owned by the game's owner (or a no-modify copy made by
// them). Every trade is ONE wolfGameExchange: the goods and the money move together or not at
// all, so a player can never pay and not get the seeds, or sell wheat and not get paid.

list TRADES = [
    // button,          take,              give
    "Sell 10 wheat",    "wheat|10",        "$|25",
    "Buy 5 seeds",      "$|10",            "seed|5",
    "Buy pet food",     "$|8",             "pet_food|5"
];

integer MENU_CH;
integer gMenu;

list parse(string s)
{
    list p = llParseString2List(s, ["|"], []);
    return [llList2String(p, 0), (integer)llList2String(p, 1)];
}

default
{
    state_entry()
    {
        MENU_CH = -3000000 - (integer)llFrand(1000000);
        llSetText("Market stall\nTouch to trade", <1, 1, 1>, 1.0);
    }

    touch_start(integer n)
    {
        key av = llDetectedKey(0);
        if (!wolfGameIsPlaying(av)) { llRegionSayTo(av, 0, "Join the game first."); return; }
        list buttons = llList2ListStrided(TRADES, 0, -1, 3);
        llListenRemove(gMenu);
        gMenu = llListen(MENU_CH, "", av, "");
        // The player's own Wolf viewer shows the dialog; the answer comes back here as chat.
        if (!wolfGameDialog(av, "Market stall", "You have " + (string)wolfGameItemCount(av, "wheat")
                            + " wheat and " + (string)wolfGameBalance(av) + " coins.", buttons, MENU_CH))
            llDialog(av, "Market stall", buttons, MENU_CH);   // not a Wolf viewer, or refused
    }

    listen(integer ch, string name, key av, string msg)
    {
        integer i = llListFindList(TRADES, [msg]);
        if (i == -1 || i % 3 != 0) return;
        if (wolfGameExchange(av, parse(llList2String(TRADES, i + 1)), parse(llList2String(TRADES, i + 2)), msg))
            wolfGameNotify(av, "Market", msg + " - done.", "success");
        else
            wolfGameNotify(av, "Market", "Not done: " + wolfGetLastError(), "warning");
    }
}
A farm plot that grows wheat (farm_plot.lsl, 123 lines: show or hide)

Download farm_plot.lsl

// Wolf Roleplay example - FARM PLOT. One prim of soil. Owned by the game's owner (or a no-modify
// copy made by them), so it may move the game's items (wolfGameExchange).
//
//   touch an empty plot   -> plant: takes 1 seed
//   touch a growing plot  -> water it (rain on the region waters it for you)
//   touch a ripe plot     -> harvest: 3-5 wheat and 1-2 seeds back
//
// The crop survives a region restart: its state is kept in linkset data, and growth is worked
// out from the time it was planted, not counted by a timer. Left dry for longer than DRY_SECONDS
// it withers and the seed is lost.

integer GROW_SECONDS = 1800;      // each of the two growing stages
integer DRY_SECONDS  = 2700;      // how long it can go without water
vector  SOIL   = <0.36, 0.25, 0.16>;
vector  SPROUT = <0.45, 0.70, 0.25>;
vector  RIPE   = <0.85, 0.72, 0.30>;
vector  DEAD   = <0.40, 0.38, 0.30>;

integer gMenu;
integer MENU_CH;

string  lsd(string k)                 { return llLinksetDataRead(k); }
put(string k, string v)               { llLinksetDataWrite(k, v); }

// 0 empty, 1 seedling, 2 growing, 3 ripe, 4 withered
integer stage()
{
    integer planted = (integer)lsd("planted");
    if (planted == 0) return 0;
    if ((integer)lsd("withered")) return 4;
    integer now = llGetUnixTime();
    if (now - (integer)lsd("watered") > DRY_SECONDS && now - planted < 2 * GROW_SECONDS)
    {
        put("withered", "1");
        return 4;
    }
    if (now - planted >= 2 * GROW_SECONDS) return 3;
    if (now - planted >= GROW_SECONDS) return 2;
    return 1;
}

show()
{
    integer s = stage();
    vector c = SOIL; string t = "Empty plot\nTouch to plant (1 seed)";
    if (s == 1) { c = SPROUT; t = "Seedling\nTouch to water"; }
    else if (s == 2) { c = SPROUT; t = "Growing\nTouch to water"; }
    else if (s == 3) { c = RIPE; t = "Ripe wheat\nTouch to harvest"; }
    else if (s == 4) { c = DEAD; t = "Withered - nobody watered it\nTouch to clear"; }
    llSetColor(c, ALL_SIDES);
    llSetText(t, <1, 1, 1>, 1.0);
}

// Rain on the region waters every plot on it.
checkRain()
{
    list w = wolfGetRegionWeather();
    integer i = llListFindList(w, ["kind"]);
    if (i != -1 && llList2String(w, i + 1) == "rain" && (integer)lsd("planted") && !(integer)lsd("withered"))
        put("watered", (string)llGetUnixTime());
}

default
{
    state_entry()
    {
        MENU_CH = -2000000 - (integer)llFrand(1000000);
        show();
        llSetTimerEvent(60.0);
    }

    on_rez(integer p) { llResetScript(); }

    timer() { checkRain(); show(); }

    touch_start(integer n)
    {
        key av = llDetectedKey(0);
        if (!wolfGameIsPlaying(av))
        {
            llRegionSayTo(av, 0, "Join the game first (Roleplay floater > Join).");
            return;
        }
        integer s = stage();
        if (s == 0)
        {
            // Planting costs one seed - taken in the same step the plot is claimed.
            if (!wolfGameExchange(av, ["seed", 1], [], "planted wheat"))
            {
                llRegionSayTo(av, 0, "You need a wheat seed to plant here (" + wolfGetLastError() + ").");
                return;
            }
            put("planted", (string)llGetUnixTime());
            put("watered", (string)llGetUnixTime());
            put("farmer", (string)av);
            put("withered", "0");
        }
        else if (s == 1 || s == 2)
        {
            put("watered", (string)llGetUnixTime());
            llRegionSayTo(av, 0, "Watered.");
        }
        else if (s == 3)
        {
            integer wheat = 3 + (integer)llFrand(3.0);
            integer seeds = 1 + (integer)llFrand(2.0);
            if (!wolfGameExchange(av, [], ["wheat", wheat, "seed", seeds], "harvest"))
            {
                llRegionSayTo(av, 0, "Could not harvest: " + wolfGetLastError());
                return;
            }
            wolfGameNotify(av, "Harvest", "You harvested " + (string)wheat + " wheat and " + (string)seeds + " seeds.", "success");
            llLinksetDataReset();
        }
        else if (s == 4)
        {
            llLinksetDataReset();
            llRegionSayTo(av, 0, "Cleared. Plant again with a seed.");
        }
        show();
    }
}
A pet shop (pet_shop.lsl, 57 lines: show or hide)

Download pet_shop.lsl

// Wolf Roleplay example - PET SHOP. Owned by the game's owner. Put a no-modify copy of the pet
// object ("Wolf pup", with pet.lsl inside, made by the game's owner) in this prim's inventory.
//
// Buying: 100 coins, then a pet record on the grid (wolfGamePetCreate) and the pet object. The
// object finds its own record the first time it is rezzed (pet.lsl), so the pet's name, age and
// hunger stay with the pet - not with one copy of the object.

integer PRICE = 100;
string  PET_OBJECT = "Wolf pup";

integer MENU_CH;
integer gMenu;
key     gBuyer;

default
{
    state_entry()
    {
        MENU_CH = -4000000 - (integer)llFrand(1000000);
        llSetText("Pet shop\nWolf pup - " + (string)PRICE + " coins\nTouch to buy", <1, 1, 1>, 1.0);
    }

    touch_start(integer n)
    {
        key av = llDetectedKey(0);
        if (!wolfGameIsPlaying(av)) { llRegionSayTo(av, 0, "Join the game first."); return; }
        gBuyer = av;
        llListenRemove(gMenu);
        gMenu = llListen(MENU_CH, "", av, "");
        llTextBox(av, "Name your wolf pup (" + (string)PRICE + " coins):", MENU_CH);
    }

    listen(integer ch, string name, key av, string msg)
    {
        if (av != gBuyer) return;
        llListenRemove(gMenu);
        string petName = llStringTrim(msg, STRING_TRIM);
        if (petName == "") petName = "Pup";
        // Pay first; if the pet cannot be made, the money goes back.
        if (!wolfGameExchange(av, ["$", PRICE], [], "bought a wolf pup"))
        {
            wolfGameNotify(av, "Pet shop", "Not bought: " + wolfGetLastError(), "warning");
            return;
        }
        string pet = wolfGamePetCreate(av, "wolf_pup", petName, ["colour", "grey"]);
        if (pet == "")
        {
            string why = wolfGetLastError();
            wolfGameExchange(av, [], ["$", PRICE], "refund - pet not made");
            wolfGameNotify(av, "Pet shop", "Refunded: " + why, "warning");
            return;
        }
        llGiveInventory(av, PET_OBJECT);
        wolfGameNotify(av, "Pet shop", petName + " is yours. Rez the Wolf pup from your inventory.", "success");
    }
}
The pet object (pet.lsl, 107 lines: show or hide)

Download pet.lsl

// Wolf Roleplay example - THE PET OBJECT ("Wolf pup"). Made by the game's owner, given no-modify,
// so this script may change its own owner's pet (it counts as the game's own item).
//
// The pet itself lives on the grid; this object only shows it. The first time it is rezzed it
// takes the owner's newest pet of this type that no object has claimed yet, and keeps that pet
// key in linkset data, so the same pet comes back every time it is rezzed.
//
//   touch: Feed (1 pet food), Play, Rename, Status

string  TYPE = "wolf_pup";
integer MENU_CH;
integer gMenu;
integer gRename;

string pet() { return llLinksetDataRead("pet"); }

// The owner's pets of this type that are alive: the newest one this object can take.
claim()
{
    list pets = wolfGamePets(llGetOwner());        // [key, type, name, alive, ...]
    integer i;
    string chosen = "";
    for (i = 0; i < llGetListLength(pets); i += 4)
        if (llList2String(pets, i + 1) == TYPE && llList2Integer(pets, i + 3))
            chosen = llList2String(pets, i);         // the list is oldest first; keep the newest
    if (chosen != "") llLinksetDataWrite("pet", chosen);
}

show()
{
    if (pet() == "") { llSetText("No pet - buy one at the pet shop", <1, 0.6, 0.6>, 1.0); return; }
    list p = wolfGamePetGet(pet());                // [type, name, owner, alive, age, stat, v, m, ...]
    if (p == [])
    {
        llSetText("(" + wolfGetLastError() + ")", <1, 0.6, 0.6>, 1.0);
        return;
    }
    if (llList2String(p, 2) != (string)llGetOwner())
    {
        // Given or sold to someone else since: this copy no longer shows that pet.
        llLinksetDataDelete("pet");
        llSetText("This pet belongs to someone else now", <1, 0.6, 0.6>, 1.0);
        return;
    }
    string t = llList2String(p, 1);
    if (!llList2Integer(p, 3)) t += " (died)";
    else
    {
        integer days = llList2Integer(p, 4) / 86400;
        t += " - " + (string)days + " day" + llList2String(["s", ""], days == 1);
        integer i;
        for (i = 5; i < llGetListLength(p); i += 3)
            t += "\n" + llList2String(p, i) + " " + (string)llList2Integer(p, i + 1) + "/" + (string)llList2Integer(p, i + 2);
    }
    llSetText(t, <1, 1, 1>, 1.0);
}

default
{
    state_entry()
    {
        MENU_CH = -5000000 - (integer)llFrand(1000000);
        if (pet() == "") claim();
        show();
        llSetTimerEvent(120.0);
    }

    on_rez(integer p) { llResetScript(); }
    changed(integer c) { if (c & CHANGED_OWNER) { llLinksetDataReset(); llResetScript(); } }
    timer() { show(); }

    touch_start(integer n)
    {
        key av = llDetectedKey(0);
        if (av != llGetOwner()) return;
        llListenRemove(gMenu);
        gMenu = llListen(MENU_CH, "", av, "");
        llDialog(av, "Your wolf pup", ["Feed", "Play", "Rename", "Status"], MENU_CH);
    }

    listen(integer ch, string name, key av, string msg)
    {
        if (ch == MENU_CH + 1)
        {
            llListenRemove(gRename);
            if (!wolfGamePetRename(pet(), llStringTrim(msg, STRING_TRIM))) llOwnerSay(wolfGetLastError());
        }
        else if (msg == "Feed")
        {
            // One pet food, taken from the owner's game items, then +30 food for the pup.
            if (!wolfGameExchange(av, ["pet_food", 1], [], "fed the pup")) llOwnerSay("No pet food: " + wolfGetLastError());
            else if (!wolfGamePetAddStat(pet(), "hunger", 30)) llOwnerSay(wolfGetLastError());
        }
        else if (msg == "Play")
        {
            if (!wolfGamePetAddStat(pet(), "happiness", 20)) llOwnerSay(wolfGetLastError());
            else llSetTimerEvent(120.0);
        }
        else if (msg == "Rename")
        {
            gRename = llListen(MENU_CH + 1, "", av, "");
            llTextBox(av, "New name:", MENU_CH + 1);
        }
        show();
    }
}

Some example comments mention a Roleplay section in the control panel. It is not on the website yet; everything it would do can be done from these scripts.

Events: wolfGameListen

wolfGameListen(channel) sends the game’s events to your script as chat on that channel, from the game’s name and key (0 stops them). A game script hears every player, so check the avatar in each message; a worn object hears only its wearer’s.

Game events
MessageWhen
join|avA player joins.
arrive|avA player is now playing on this region: set up their action bar.
leave|avA player leaves.
death|av|killer|causeA player dies.
respawn|avA player comes back.
zero|av|statA stat reached its minimum.
pay|from|to|amountOne player paid another.
item|av|item|+nA player's item count changed.
equip|av|slot|item and unequip|av|slot|itemEquipment changed.
target|av|keyA player picked a target.
buffend|av|idA buff or debuff ran out.

Webhooks. With a webhook set, the grid also posts each event (except target changes) to your server as JSON: event, game, region, regionName, agent, extra and time. With a webhooksecret the post carries X-Wolf-Game-Time (unix seconds) and X-Wolf-Game-Sig, the hex HMAC-SHA256 of the time, a newline and the body, so your server can tell it came from the grid.

Who may do what

  • A worn script may drive its own wearer’s screen, and may join its wearer to the game.
  • A script whose owner holds the region may run the region’s game and drive the screen of anyone playing it there.
  • The game’s own scripts (its owner’s, and those registered with wolfGameTrustScript) may act on anyone playing it; money, items and pets move only for them.
  • wolfGameForceMode(TRUE) keeps Game Mode on for everyone on the region (the region’s holder, or a grid god). It never changes anyone’s own saved choice.

Combat

Fighting, spells and guns. The region checks every press of an action button (its cooldown, cast time, ammunition and cost) before your script hears it, so a modified viewer cannot fire faster or for free.

Weapons and armour

  • Slots: weapons in main, off and ranged; armour in head, body, hands, legs, feet, neck, ring (and off for a shield).
  • Define them as items with a kind ("weapon", "armour", "spell", "consumable", "material") and a slot. Weapons can show damage, range, firemode, magazine and their ammo item, and be twohand in main; armour shows defence. These are for display: your scripts decide the real numbers.
  • Players equip from the Bag tab; scripts with wolfGameEquip / wolfGameUnequip, and read it with wolfGameLoadout.

The action bar

wolfGameSetAbility(avatar, id, rules) puts a button on the player’s bar (at most 12): label, icon, cooldown, cast, cost and amount, key, enabled, group (a shared cooldown), weapon and ammo, target (1 = needs one), order and channel. A press comes back on the channel from the avatar’s key under the name #wolfgame, which no viewer can chat under:

Hearing the presses

llListen(channel, "#wolfgame", avatar, "");
// then: "cast|id|target", "use|id|target", "interrupted|id"
Combat functions
FunctionWhat it shows or does
wolfGameCooldown / wolfGameInterruptStart (or end) a cooldown; stop a cast in progress.
wolfGameSetGear / wolfGameSetAmmo / wolfGameReload / wolfGameGetAmmoWhat a weapon slot shows, its magazine and reserve, a reload bar.
wolfGameSetTarget / wolfGameGetTarget / wolfGameTargetHealthThe target frame: name, health, level, hostility (hostile, neutral, friendly), icon, subtitle.
wolfGameAddBuff / wolfGameRemoveBuffBuffs and debuffs with an icon, time, stacks and text (at most 20).
wolfGameCombatTextA floating number over the target or the player: hit, crit, heal, miss, dodge, block, immune or absorb.
wolfGameDamage / wolfGameHeal / wolfGameKill / wolfGameRespawnHealth, death and coming back.
wolfGameEffectflash, pulse, blur, blackout, desaturate, shake or clear.
Combat setup: mana, a sword, a rifle and its rounds, armour, trusted scripts (combat_setup.lsl, 54 lines: show or hide)

Download combat_setup.lsl

// Wolf Roleplay example - COMBAT SETUP (run once by the game's owner on a region running their
// game). Defines the mana stat, the weapons and armour the two combat examples use, and registers
// those two scripts as the game's OWN (wolfGameTrustScript) - only the game's own scripts may hurt
// players, take mana, and move ammunition.
//
// Put copies of "combat_sword_and_spell" and "combat_gun" in THIS object's inventory first: a
// script is trusted by its asset, and a copy has the same asset as long as nobody edits it.
// Everything here can also be done from the control panel's Roleplay section.

// TRUE makes this region an arena: Game Mode stays on for everyone here, so nobody can switch it
// off to avoid a fight (their own saved choice is untouched; it applies again when they leave).
// FALSE leaves the region as it is - it never turns off forcing set from the control panel;
// wolfGameForceMode(FALSE) does that.
integer FORCE_GAME_MODE = FALSE;

say(string what, integer ok)
{
    if (ok) llOwnerSay(what + ": OK");
    else llOwnerSay(what + ": REFUSED - " + wolfGetLastError());
}

default
{
    touch_start(integer n)
    {
        if (llDetectedKey(0) != llGetOwner()) return;
        if (wolfGameKey() == "") { llOwnerSay("This region runs no game - wolfGameCreate first."); return; }

        // Mana: spells cost it, it comes back 6 a minute. The region TAKES the cost of an
        // ability set by the game's own script, so the spell script never counts mana itself.
        say("stat mana", wolfGameDefineStat("mana",
            ["label", "Mana", "min", 0, "max", 100, "start", 100, "rate", 6, "colour", "#3b82f6", "order", 3]));

        // Weapons and armour. The damage/range/firemode/defence are what the weapons panel SHOWS;
        // the scripts decide the real numbers.
        say("item sword", wolfGameDefineItem("sword",
            ["label", "Iron sword", "kind", "weapon", "slot", "main", "damage", "12-18", "range", "3 m",
             "firemode", "melee", "max", 1, "order", 10]));
        say("item rifle_ammo", wolfGameDefineItem("rifle_ammo",
            ["label", "Rifle rounds", "kind", "consumable", "max", 600, "order", 12]));
        say("item rifle", wolfGameDefineItem("rifle",
            ["label", "Hunting rifle", "kind", "weapon", "slot", "ranged", "damage", "8-12", "range", "100 m",
             "firemode", "semi-auto", "magazine", 30, "ammo", "rifle_ammo", "max", 1, "order", 11]));
        say("item leather", wolfGameDefineItem("leather",
            ["label", "Leather jerkin", "kind", "armour", "slot", "body", "defence", "5", "max", 1, "order", 13]));

        // The two worn weapon scripts are the game's own.
        say("trust combat_sword_and_spell", wolfGameTrustScript("combat_sword_and_spell"));
        say("trust combat_gun", wolfGameTrustScript("combat_gun"));

        if (FORCE_GAME_MODE) say("force Game Mode", wolfGameForceMode(TRUE));
    }
}
A sword and two spells, with cooldowns, a cast bar, damage numbers and buffs (combat_sword_and_spell.lsl, 180 lines: show or hide)

Download combat_sword_and_spell.lsl

// Wolf Roleplay example - SWORD AND SPELLS. Worn (a sword attachment, or a HUD). Registered as
// one of the game's own scripts by combat_setup, so the region lets it hurt players and takes the
// mana its spells cost.
//
// What the player sees, in their Wolf viewer:
//   - an action bar: 1 Slash (melee, needs the sword equipped), 2 Fireball (1.5 s cast, 20 mana,
//     leaves a Burning debuff), 3 Mend (2 s cast, 30 mana, heals and gives a Mended buff);
//   - real cooldown sweeps, a cast bar, floating damage numbers and a hit marker;
//   - the target frame (they pick a target by selecting an avatar).
// The REGION checks every press - cooldown, cast, mana - and takes the mana; this script is told
// on its channel from the wearer's key under the name "#wolfgame": "cast|id|target", "use|id|target", "interrupted|id".
// Slash and Fireball need a target: without one the region says "No target" and takes nothing.

string ICON_SLASH = "";      // texture keys for the buttons ("" shows the label's letters)
string ICON_FIREBALL = "";
string ICON_MEND = "";
string ICON_BURNING = "";
string ICON_MENDED = "";

float SWORD_RANGE = 3.5;
float SPELL_RANGE = 40.0;

integer gChannel;            // ability uses come back here
integer gEvents;             // the game's events about the wearer (wolfGameListen)
integer gUseListen;
integer gEventListen;
key gWearer;
list gBurning;               // [target, seconds left, ...] - Fireball's damage over time

setup()
{
    gWearer = llGetOwner();
    llListenRemove(gUseListen);
    llListenRemove(gEventListen);
    gUseListen = llListen(gChannel, "#wolfgame", gWearer, "");   // "#wolfgame": only the region, never a viewer, sends under that name
    gEventListen = llListen(gEvents, "", NULL_KEY, "");
    // A worn object hears its own wearer's game events: equip/unequip, arrive, death.
    wolfGameListen(gEvents);
    showBar();
}

integer armed()
{
    return llListFindList(wolfGameLoadout(gWearer), ["main", "sword"]) != -1;
}

showBar()
{
    // A screen is per region: this runs on attach, on every region change and on "arrive".
    wolfGameSetAbility(gWearer, "slash", ["label", "Slash", "icon", ICON_SLASH, "cooldown", 1.5,
        "key", "1", "enabled", armed(), "target", 1, "channel", gChannel, "order", 1]);
    wolfGameSetAbility(gWearer, "fireball", ["label", "Fireball", "icon", ICON_FIREBALL, "cooldown", 6.0,
        "cast", 1.5, "cost", "mana", "amount", 20, "key", "2", "target", 1, "channel", gChannel, "order", 2]);
    wolfGameSetAbility(gWearer, "mend", ["label", "Mend", "icon", ICON_MEND, "cooldown", 15.0,
        "cast", 2.0, "cost", "mana", "amount", 30, "key", "3", "group", "heals", "channel", gChannel, "order", 3]);
}

clearBar()
{
    wolfGameRemoveAbility(gWearer, "slash");
    wolfGameRemoveAbility(gWearer, "fireball");
    wolfGameRemoveAbility(gWearer, "mend");
}

// Hurt `target` (a player of the game here, within `range`) by lo..hi, 1 in 10 a critical hit.
// Both screens get it: a number over the target and a hit marker for the attacker, a number
// over themselves and a red flash for the target.
integer strike(key target, integer lo, integer hi, float range, string cause, string colour)
{
    if (target == NULL_KEY) return FALSE;   // the region refuses "No target" before this
    list d = llGetObjectDetails(target, [OBJECT_POS]);
    if (d == [] || llVecDist(llList2Vector(d, 0), llGetPos()) > range)
    {
        wolfGameCombatText(gWearer, target, 0, "miss", "");
        return FALSE;
    }
    if (!wolfGameIsPlaying(target))
    {
        wolfGameCombatText(gWearer, target, 0, "immune", "");
        return FALSE;
    }
    integer dmg = lo + (integer)llFrand(hi - lo + 1);
    string kind = "hit";
    if (llFrand(1.0) < 0.1) { dmg *= 2; kind = "crit"; }
    if (!wolfGameDamage(target, dmg, cause)) { llOwnerSay(cause + ": " + wolfGetLastError()); return FALSE; }
    wolfGameCombatText(gWearer, target, dmg, kind, colour);
    wolfGameCombatText(target, target, dmg, kind, colour);
    wolfGameEffect(target, "flash", 35, 0.4);
    return TRUE;
}

default
{
    state_entry()
    {
        gChannel = -7000000 - (integer)llFrand(1000000);
        gEvents = gChannel - 1;
        if (llGetAttached()) setup();
    }

    attach(key id)
    {
        if (id != NULL_KEY) llResetScript();
        else clearBar();
    }

    changed(integer c)
    {
        if (c & CHANGED_REGION) setup();
        if (c & CHANGED_OWNER) llResetScript();
    }

    listen(integer ch, string name, key id, string msg)
    {
        list p = llParseStringKeepNulls(msg, ["|"], []);
        string what = llList2String(p, 0);
        if (ch == gEvents)
        {
            // From the game: "equip|av|slot|item", "unequip|av|slot|item", "arrive|av", ...
            // A game script hears EVERY player's events: only the wearer's are this sword's.
            if ((key)llList2String(p, 1) != gWearer) return;
            if (what == "arrive") showBar();
            else if ((what == "equip" || what == "unequip") && llList2String(p, 3) == "sword")
                wolfGameSetAbility(gWearer, "slash", ["enabled", what == "equip"]);
            return;
        }
        // From the region, as the wearer: the ability presses it has already checked.
        string ability = llList2String(p, 1);
        key target = (key)llList2String(p, 2);
        if (what == "use")
        {
            if (ability == "slash")
                strike(target, 12, 18, SWORD_RANGE, "sword", "");
            else if (ability == "fireball")
            {
                if (strike(target, 25, 35, SPELL_RANGE, "fireball", "#ff7a1a"))
                {
                    // Burning: 3 damage a second for 6 s, shown on the target's buff bar.
                    wolfGameAddBuff(target, "burning", ["label", "Burning", "icon", ICON_BURNING, "seconds", 6,
                        "type", "debuff", "text", "Takes 3 fire damage a second."]);
                    integer i = llListFindList(gBurning, [target]);
                    if (i == -1) gBurning += [target, 6];
                    else gBurning = llListReplaceList(gBurning, [6], i + 1, i + 1);
                    llSetTimerEvent(1.0);
                }
            }
            else if (ability == "mend")
            {
                if (wolfGameHeal(gWearer, 30))
                {
                    wolfGameCombatText(gWearer, gWearer, 30, "heal", "");
                    wolfGameAddBuff(gWearer, "mended", ["label", "Mended", "icon", ICON_MENDED, "seconds", 10,
                        "type", "buff", "text", "Recently healed."]);
                }
            }
        }
        // "cast|id|target" (a cast began) and "interrupted|id" (it was stopped) also arrive here:
        // the place to start and stop a casting animation, which this example does not have.
    }

    timer()
    {
        list keep;
        integer i;
        for (i = 0; i < llGetListLength(gBurning); i += 2)
        {
            key t = llList2Key(gBurning, i);
            integer left = llList2Integer(gBurning, i + 1) - 1;
            if (wolfGameIsPlaying(t) && wolfGameDamage(t, 3, "burning"))
            {
                wolfGameCombatText(gWearer, t, 3, "hit", "#ff7a1a");
                wolfGameCombatText(t, t, 3, "hit", "#ff7a1a");
            }
            if (left > 0) keep += [t, left];
        }
        gBurning = keep;
        if (gBurning == []) llSetTimerEvent(0.0);
    }
}
A rifle with a magazine, reserve and reload (combat_gun.lsl, 189 lines: show or hide)

Download combat_gun.lsl

// Wolf Roleplay example - A GUN WITH AMMO AND RELOAD. Worn (the rifle attachment). Registered as
// one of the game's own scripts by combat_setup, so the region lets it hurt players and move the
// rifle_ammo item.
//
// What the player sees, in their Wolf viewer:
//   - the rifle on their weapons panel (from the equipped "rifle" item: name, damage, range,
//     fire mode) with its magazine "24 / 30" and reserve, and a reload bar;
//   - action bar: 4 Fire (one round), 5 Aimed shot (1.2 s cast, three rounds, always critical);
//   - R (or the Reload button) reloads; firing on an empty magazine asks for a reload by itself.
//
// WHO COUNTS WHAT. The magazine is on the player's screen and the REGION takes the rounds each shot
// uses, refusing "Out of ammo - reload" when there are too few. The reserve is the player's
// "rifle_ammo" item on the grid: a reload takes rounds from it with wolfGameItemTake, so ammunition
// bought, looted or traded is the same everywhere.

integer MAGAZINE = 30;
float RELOAD_SECONDS = 2.5;
float RANGE = 100.0;
string ICON_FIRE = "";        // texture keys for the buttons ("" shows the label's letters)
string ICON_AIMED = "";

integer gChannel;
integer gEvents;
integer gUseListen;
integer gEventListen;
key gWearer;
integer gMag;                 // rounds in the magazine, as this script last set or read them
integer gReloading;

integer equipped()
{
    return llListFindList(wolfGameLoadout(gWearer), ["ranged", "rifle"]) != -1;
}

integer reserve()
{
    return wolfGameItemCount(gWearer, "rifle_ammo");
}

setup()
{
    gWearer = llGetOwner();
    llListenRemove(gUseListen);
    llListenRemove(gEventListen);
    gUseListen = llListen(gChannel, "#wolfgame", gWearer, "");   // "#wolfgame": only the region, never a viewer, sends under that name
    gEventListen = llListen(gEvents, "", NULL_KEY, "");
    wolfGameListen(gEvents);
    if (equipped()) arm();
    else disarm();
}

arm()
{
    // A screen is per region, so the magazine is set again on every arrival from this script's count.
    wolfGameSetGear(gWearer, "ranged", ["channel", gChannel, "magazine", MAGAZINE]);
    wolfGameSetAmmo(gWearer, "ranged", gMag, reserve());
    wolfGameSetAbility(gWearer, "fire", ["label", "Fire", "icon", ICON_FIRE, "cooldown", 0.2,
        "weapon", "ranged", "ammo", 1, "key", "4", "channel", gChannel, "order", 4]);
    wolfGameSetAbility(gWearer, "aimed", ["label", "Aimed shot", "icon", ICON_AIMED, "cooldown", 5.0,
        "cast", 1.2, "weapon", "ranged", "ammo", 3, "key", "5", "target", 1, "channel", gChannel, "order", 5]);
}

disarm()
{
    wolfGameRemoveAbility(gWearer, "fire");
    wolfGameRemoveAbility(gWearer, "aimed");
    wolfGameSetGear(gWearer, "ranged", []);
}

// The magazine as the region has it now (it takes the rounds of every shot).
readMagazine()
{
    list a = wolfGameGetAmmo(gWearer, "ranged");
    if (a != [] && llList2Integer(a, 0) >= 0) gMag = llList2Integer(a, 0);
}

startReload()
{
    if (gReloading) return;
    readMagazine();
    if (gMag >= MAGAZINE) return;
    if (reserve() <= 0)
    {
        wolfGameNotify(gWearer, "Rifle", "No rifle rounds left.", "warning");
        return;
    }
    gReloading = TRUE;
    wolfGameReload(gWearer, "ranged", RELOAD_SECONDS);   // the bar, and no firing meanwhile
    llSetTimerEvent(RELOAD_SECONDS);
}

// One shot: at the target when there is one (and it is in the line of fire), else straight ahead.
shoot(key target, integer lo, integer hi, integer crit)
{
    vector from = llGetPos() + <0.0, 0.0, 0.6>;
    vector dir = llRot2Fwd(llGetRootRotation());
    if (target != NULL_KEY)
    {
        list d = llGetObjectDetails(target, [OBJECT_POS]);
        if (d != []) dir = llVecNorm(llList2Vector(d, 0) - from);
    }
    list hit = llCastRay(from + dir * 0.6, from + dir * RANGE, [RC_REJECT_TYPES, RC_REJECT_LAND, RC_MAX_HITS, 1]);
    key victim = llList2Key(hit, 0);
    if (llList2Integer(hit, -1) < 1 || victim == gWearer || !wolfGameIsPlaying(victim))
    {
        if (target != NULL_KEY) wolfGameCombatText(gWearer, target, 0, "miss", "");
        return;
    }
    integer dmg = lo + (integer)llFrand(hi - lo + 1);
    string kind = "hit";
    if (crit) { dmg *= 2; kind = "crit"; }
    if (!wolfGameDamage(victim, dmg, "rifle")) { llOwnerSay("rifle: " + wolfGetLastError()); return; }
    wolfGameCombatText(gWearer, victim, dmg, kind, "");
    wolfGameCombatText(victim, victim, dmg, kind, "");
    wolfGameEffect(victim, "flash", 30, 0.3);
}

default
{
    state_entry()
    {
        gChannel = -8000000 - (integer)llFrand(1000000);
        gEvents = gChannel - 1;
        gMag = MAGAZINE;   // a new rifle comes loaded
        if (llGetAttached()) setup();
    }

    attach(key id)
    {
        if (id != NULL_KEY) setup();
        else disarm();
    }

    changed(integer c)
    {
        if (c & CHANGED_REGION) setup();
        if (c & CHANGED_OWNER) llResetScript();
    }

    listen(integer ch, string name, key id, string msg)
    {
        list p = llParseStringKeepNulls(msg, ["|"], []);
        string what = llList2String(p, 0);
        if (ch == gEvents)
        {
            // A game script hears EVERY player's events: only the wearer's are this rifle's.
            if ((key)llList2String(p, 1) != gWearer) return;
            if (what == "arrive") setup();
            else if (llList2String(p, 3) == "rifle" && llList2String(p, 2) == "ranged")
            {
                if (what == "equip") arm();
                else if (what == "unequip") disarm();
            }
            // Rounds bought, looted or traded show at once as the reserve.
            else if (what == "item" && llList2String(p, 2) == "rifle_ammo" && equipped() && !gReloading)
            {
                readMagazine();
                wolfGameSetAmmo(gWearer, "ranged", gMag, reserve());
            }
            return;
        }
        if (what == "reload" && llList2String(p, 1) == "ranged") { startReload(); return; }
        if (what != "use") return;
        string ability = llList2String(p, 1);
        key target = (key)llList2String(p, 2);
        if (ability == "fire") shoot(target, 8, 12, FALSE);
        else if (ability == "aimed") shoot(target, 10, 14, TRUE);
        readMagazine();
    }

    timer()
    {
        llSetTimerEvent(0.0);
        if (!gReloading) return;
        gReloading = FALSE;
        readMagazine();
        integer have = reserve();
        integer take = MAGAZINE - gMag;
        if (take > have) take = have;
        if (take > 0 && wolfGameItemTake(gWearer, "rifle_ammo", take, "reload"))
        {
            gMag += take;
            have -= take;
        }
        else if (take > 0) wolfGameNotify(gWearer, "Rifle", "Could not reload: " + wolfGetLastError(), "warning");
        wolfGameSetAmmo(gWearer, "ranged", gMag, have);   // also ends the reload bar
    }
}

Players reload by clicking the weapon on the game bar, or with Reload on the Combat tab. The R key is not used for it: viewers already use R for other things, and the viewer’s own key wins.

External games

A system that already keeps its own stats, money and rules on its own server, such as Knightwood’s KRS, can still use the viewer as its screen. Its worn HUD drives its wearer’s screen; scripts whose owner holds the region drive the screen of anyone playing there. Nothing is kept or ticked by the grid for these:

Display functions for external games
To showUse
Bars for your own statswolfGameShowStat / wolfGameHideStat
Your own moneywolfGameSetCurrencyDisplay(avatar, "KR$", 1500)
A level and experience barwolfGameSetLevel
Notices, dialogs and buttonswolfGameNotify, wolfGameDialog, wolfGameSetButtons, wolfGameSetPanel
Downed, speed, screen effectswolfGameSetDowned, wolfGameSetSpeed, wolfGameEffect
Your own web page in the viewerwolfGameShowWeb (https only: a panel, an overlay or a window) / wolfGameCloseWeb
A sound only that player hearswolfGameSound
Markers, across your regionswolfGameMarker / wolfGameClearMarker
A line under a name tagwolfGameSetTag
Weapons, abilities, targets, buffs, combat textwolfGameSetGear, wolfGameSetAmmo, wolfGameReload, wolfGameSetAbility (a cost that is not a game stat is shown, never taken), wolfGameSetTarget, wolfGameTargetHealth, wolfGameAddBuff, wolfGameCombatText

Each player’s screen accepts about 10 messages a second, and anything not from the region’s own game is marked Unverified to the player.

All functions

Every wolfGame function the grid runs, read from the region software. Each links to its page in the LSL Guide.

Roleplay: the game

integer wolfGameCreate(list rules)

Make a Wolf Roleplay game for this region, owned by the script's owner, who must hold the region. Rules: name (unique), kind (managed|external), theme (wolves|plain), description, website, rules, logo, currency, symbol, currencyicon (the currency's picture, a texture key), start, playerpay, death (spawn|home|stay), respawn (seconds), spawn (vector - defaults to this object), webhook, webhooksecret, starter (items a player's first join gives, e.g. "meat=3,water=2"). Register the game's own scripts with wolfGameTrustScript. TRUE when created.

integer wolfGameSet(list rules)

Change this region's game (same rules as wolfGameCreate). Only the game's owner.

list wolfGameGet()

This region's game as key/value pairs, or an empty list. Includes "forced" (1 when the region forces Game Mode on).

integer wolfGameDefineStat(string name, list rules)

Add or change a stat: label, min, max, start, rate (per minute, + regenerates, - decays), zero (none|die|slow|drain), target and amount (for drain), colour, icon (a texture key; or a short text glyph), visible, order, health (1 = tied to the avatar's health), keep (1 = kept through a respawn: xp, gold). At most 16 stats.

integer wolfGameRemoveStat(string name)

Remove a stat from this region's game.

list wolfGameStats()

The names of this region's game stats.

integer wolfGameRegister(string gameKey)

Run an existing game (one you own, or the Wolf Territories default game) on this region.

integer wolfGameUnregister()

Stop running a game on this region.

string wolfGameKey()

This region's game key, or "" when it runs none.

integer wolfGameSetSpawn(vector pos)

Where this game's players respawn, on this region.

integer wolfGameListen(integer channel)

Receive game events on this channel as chat (from the game's name and key; check the av field - a game script hears every player): join|av, arrive|av (playing on this region now - set up their action bar), leave|av, death|av|killer|cause, respawn|av, zero|av|stat, pay|from|to|amount, item|av|item|+n, equip|av|slot|item, unequip|av|slot|item, target|av|key, buffend|av|id. 0 stops. The region's holder and the game's own scripts hear everyone; a WORN object hears only its own wearer's events.

Roleplay: players, stats and money

integer wolfGameJoin(string avatar)

Join an avatar to this region's game. The region's holder, the avatar's own worn object, or a game-made item the avatar owns. On a forced region an avatar whose own switch is off is read in as on arrival (playing a moment later) - never re-joined to a game they left.

integer wolfGameLeave(string avatar)

Take an avatar out of this region's game (their wallet stays).

integer wolfGameIsPlaying(string avatar)

TRUE when the avatar plays this region's game and is here.

list wolfGamePlayers()

The keys of everyone playing this region's game here.

integer wolfGameResetPlayer(string avatar)

Every stat back to its start value.

integer wolfGameGetStat(string avatar, string stat)

One stat's value. 0 and wolfGetLastError() set when it cannot be read.

integer wolfGameSetStat(string avatar, string stat, integer value)

Set a stat (held to its min and max). Reaching the minimum runs the stat's zero action.

integer wolfGameAddStat(string avatar, string stat, integer delta)

Add to a stat (negative takes away) - eating, drinking, a stamina drink.

integer wolfGameSetStatMax(string avatar, string stat, integer max)

This player's own maximum for a stat - a skill that raises max stamina.

list wolfGameGetStats(string avatar)

[name, value, max, ...] for every stat.

integer wolfGameDamage(string avatar, integer amount, string cause)

Take health away. At 0 the player dies and the game respawns them.

integer wolfGameHeal(string avatar, integer amount)

Give health back (not to the dead - wolfGameRespawn does that).

integer wolfGameRespawn(string avatar)

Bring a dead player back now instead of after the game's respawn time.

integer wolfGameBalance(string avatar)

The avatar's balance of this game's currency.

integer wolfGameTake(string avatar, integer amount, string reason)

Take game currency from a player; refused when they have too little.

integer wolfGamePay(string from, string to, integer amount, string reason)

Move game currency from one player to another. The payer's own object, when the game allows players to pay; or the region's holder.

integer wolfGameSetData(string avatar, string key, string value)

Store a value for this player in this game (class, race, quest step...). "" removes it.

string wolfGameGetData(string avatar, string key)

A value stored with wolfGameSetData, or "".

Roleplay: the player's screen

integer wolfGameShowStat(string avatar, string name, integer value, integer max, string colour, string icon)

Show a bar on the player's Wolf viewer: name, value, max, colour (#rrggbb), icon (a texture key; or a short text glyph). For games that keep their own stats.

integer wolfGameHideStat(string avatar, string name)

Remove a bar shown with wolfGameShowStat.

integer wolfGameSetCurrencyDisplay(string avatar, string symbol, integer amount)

Show a balance from your own system on the player's screen ("KR$", 1500). "" hides it.

integer wolfGameNotify(string avatar, string title, string text, string type)

A notice on the player's screen. Type: info, success, warning or danger.

integer wolfGameDialog(string avatar, string title, string text, list buttons, integer channel)

A dialog on the player's Wolf viewer. The button pressed comes back on the channel as chat from the avatar, like llDialog.

integer wolfGameSetButtons(string avatar, list idLabelPairs, integer channel)

Buttons in the player's Roleplay panel: [id, label, id, label...]. A press comes back on the channel as chat from the avatar: the id.

integer wolfGameEffect(string avatar, string effect, integer strength, float seconds)

A screen effect: flash, pulse, blur, blackout, desaturate, shake or clear. Strength 0-100, seconds 0-600.

integer wolfGameSetDowned(string avatar, integer on, string text, integer seconds)

Down (TRUE) or up (FALSE): an overlay on the player's screen, and the region holds them still.

integer wolfGameSetSpeed(string avatar, float factor)

Walking/running speed, 0.1 to 2.0 (1.0 is normal). The region enforces it.

integer wolfGameShowWeb(string avatar, string url, string mode, integer width, integer height)

Your own https:// page in the player's viewer: mode panel, overlay or window; width and height 0 for automatic.

integer wolfGameCloseWeb(string avatar)

Close the page opened by wolfGameShowWeb.

integer wolfGameSound(string avatar, string sound, float volume)

Play a sound that only this player hears (name in this object, or a sound key). Volume 0.0-1.0.

integer wolfGameMarker(string avatar, string id, string region, vector pos, string label, string colour, float seconds)

A marker on the player's screen, minimap and world map: id, region ("" = this one), position, label, colour, seconds (0 = until cleared).

integer wolfGameClearMarker(string avatar, string id)

Remove a marker ("" removes them all).

integer wolfGameSetTag(string avatar, string text, string colour)

A line under the avatar's name tag, seen by everyone using a Wolf viewer. "" removes it.

integer wolfGameSetPanel(string avatar, string title, list labelValuePairs)

An information panel in the player's Roleplay floater: title, [label, value, ...].

Roleplay: items and pets

integer wolfGameDefineItem(string name, list rules)

Define a game item (seeds, wheat, bread, lockpicks, a sword): label, icon (a texture key; or a short text glyph), max (per player, 0 = no limit), order; kind ("" | weapon | armour | spell | consumable | material); for weapons and armour a slot (weapons: main, off, ranged; armour: off, head, body, hands, legs, feet, neck, ring) and display stats - damage, range, firemode, magazine, ammo (its ammunition item), twohand (main only), defence. Only the game's owner.

integer wolfGameRemoveItem(string name)

Remove an item's definition (players keep what they hold).

integer wolfGameItemAdd(string avatar, string item, integer qty, string reason)

Give a player some of an item. The game's owner or the game's own no-modify items only.

integer wolfGameItemTake(string avatar, string item, integer qty, string reason)

Take some of an item from a player; refused when they have too few.

integer wolfGameItemCount(string avatar, string item)

How many of an item the player holds.

list wolfGameItems(string avatar)

[item, quantity, ...] for everything the player holds.

integer wolfGameExchange(string avatar, list take, list give, string reason)

One atomic swap: take [name, qty, ...] and give [name, qty, ...] - "$" is the game's money. Selling 10 wheat for 50 coins, planting a seed. All or nothing.

integer wolfGameDefinePet(string type, list rules)

Define a pet type (wolf_pup, pony): label, icon (a texture key; or a short text glyph), max (per player, 0 = no limit), order. Only the game's owner.

integer wolfGameRemovePet(string type)

Remove a pet type no pet still has.

integer wolfGameDefinePetStat(string type, string stat, list rules)

A pet type's stat: label, min, max, start, rate (per minute, keeps running while the pet is in inventory), zero (none|die), colour, icon (a texture key; or a short text glyph), order.

string wolfGamePetCreate(string owner, string type, string name, list data)

A new pet for a player (a pet shop, a hatchery, breeding): owner, type, name, [data key, value, ...]. Returns the pet's key - keep it in the pet object (linkset data). "" when refused.

list wolfGamePetGet(string pet)

[type, name, owner, alive, age seconds, stat, value, max, ...] - stats as they are NOW, aged by real time.

string wolfGamePetGetData(string pet, string key)

A value stored with the pet (genes, colour, tricks), or "".

integer wolfGamePetSetStat(string pet, string stat, integer value)

Set a pet stat. The pet owner's own game items, or the game's own items on the game's land.

integer wolfGamePetAddStat(string pet, string stat, integer delta)

Add to a pet stat - feeding, playing, grooming.

integer wolfGamePetTransfer(string pet, string to)

Give or sell the pet to another player of the game.

integer wolfGamePetRelease(string pet)

Let the pet go: its record is deleted.

list wolfGamePets(string owner)

[pet key, type, name, alive, ...] for an avatar's pets in this game.

Roleplay: trusted scripts and forced Game Mode

integer wolfGameTrustScript(string scriptName)

Register a script in this object as one of the game's OWN scripts (by its asset - an edited copy is not). Only the game's owner. The game's own scripts are the only ones that may change players, money, items and pets.

integer wolfGameUntrustScript(string scriptName)

Take a script in this object off the game's own list.

integer wolfGameForceMode(integer on)

TRUE keeps Game Mode ON for everyone on this region (they cannot switch it off or leave the game here - "so people dont turn it off to avoid getting killed"); FALSE gives them their own choice back. Never changes anyone's own saved switch. The region's holder or a grid god.

Roleplay: combat

integer wolfGameEquip(string avatar, string item)

Put an item the player holds into its slot (a weapon or armour item - see wolfGameDefineItem). Kept per player per game. The game's own scripts; players equip from their viewer.

integer wolfGameUnequip(string avatar, string slot)

Empty an equipment slot: main, off, ranged, head, body, hands, legs, feet, neck or ring.

list wolfGameLoadout(string avatar)

[slot, item, ...] - what the player has equipped.

integer wolfGameSetAbility(string avatar, string id, list rules)

Add or change a button on the player's action bar: label, icon, cooldown (s), cast (s), cost (a stat) and amount, key (hint), enabled, group (shared cooldown), weapon (a slot whose ammo it uses), ammo (rounds per use), target (1 = refused with "No target" when the player has none), order, channel. A press is checked by the region (cooldown, cast, ammo; the cost is TAKEN when the game's own script set it and it is a stat of the game - never down to the minimum of a stat whose emptiness does something, such as health) and comes back on the channel from the avatar's key under the name "#wolfgame" (listen with llListen(channel, "#wolfgame", avatar, "") - no viewer can chat under that name): cast|id|target, use|id|target, interrupted|id. At most 12.

integer wolfGameRemoveAbility(string avatar, string id)

Remove an action-bar button ("" removes them all).

integer wolfGameCooldown(string avatar, string id, float seconds)

Start an ability's cooldown for this many seconds (0 ends it).

integer wolfGameInterrupt(string avatar)

Stop the player's cast in progress (a stun, a hit). Its script hears interrupted|id.

integer wolfGameSetGear(string avatar, string slot, list rules)

What an equipment slot shows on the player's weapons panel (for games that keep their own weapons): label, icon, damage, range, firemode, defence, ammoname, magazine, ammo, reserve, channel (reload requests come back as reload|slot, from the avatar under the name "#wolfgame"). [] clears it.

integer wolfGameSetAmmo(string avatar, string slot, integer ammo, integer reserve)

A slot's ammo: rounds in the magazine and in reserve (-1 = not shown). Ends a reload. The region takes the rounds an ability uses by itself.

integer wolfGameReload(string avatar, string slot, float seconds)

Show a reload on a weapon slot for this many seconds (0 stops it); its abilities cannot fire meanwhile. Call wolfGameSetAmmo when it is done.

list wolfGameGetAmmo(string avatar, string slot)

[ammo, magazine, reserve, reloading] for a slot.

integer wolfGameSetTarget(string avatar, string target, list rules)

The player's target frame: target key ("" clears), then name, health, max, level, hostility (hostile|neutral|friendly), icon, text (subtitle), dead. With no health, a player of this game shows their live health.

string wolfGameGetTarget(string avatar)

The player's current target, or NULL_KEY. Players pick targets in their viewer; you hear target|av|key.

integer wolfGameTargetHealth(string target, integer health, integer max)

Update the health bar on the target frame of everyone targeting this key (a monster taking hits).

integer wolfGameAddBuff(string avatar, string id, list rules)

A buff or debuff on the player's screen: label, icon, seconds (0 = until removed; given again it restarts), stacks, type (buff|debuff), text. At most 20. You hear buffend|av|id when it runs out.

integer wolfGameRemoveBuff(string avatar, string id)

Remove a buff or debuff ("" removes them all).

integer wolfGameCombatText(string avatar, string at, integer amount, string kind, string colour)

A floating combat number: over the target `at` (damage dealt, with a hit marker) or over the player (at = the player or "": damage taken). Kind: hit, crit, heal, miss, dodge, block, immune, absorb. Colour #rrggbb or "".