// 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); } }