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