// IF YOU VALUE THIS WORK, PLEASE LEAVE ATTRIBUTION INTACT // // // // _|_| // // _| _| _|_|_|_| _|_| _|_|_| _|_| // // _| _| _| _| _| _| _| _|_|_|_| // // _| _| _| _| _| _| _| _| // // _|_| _|_|_|_| _|_| _| _| _|_|_| // // MiniVerse // //............................................................// //.....................▒▓▓▒▒▒▒▓▓▓▓▓▓▒▒▒▓▓░....................// //.................._▒▓▒.Ozone.MiniVerse.▒▓▒_.................// //................_▒▓▓▓▒____PRESENTS____▒▓▓▓▓▓▒_..............// //..............░▓▓▓▓▓▓▒___I.M.A.G.E.___▒▓▓▓▓▓▓▓░.............// //.............▒▓▓▓▓▓▓▓▒__IMAGE_MATRIX__▒▓▓▓▓▓▓▓▓▒............// //............▒▓▒▓▓▓▓▒_ACTION_GAME_ENGINE_▒▓▓▓▓▒▓▓▒...........// //..........▒▓▓▓▓░...▒▓▓▒_By_Spax_Orion_▒▓▓▒...░▓▓▓▓▒.........// //.........▒▓▓▓▓░......▒▓_& Dirty Helga_▓▓▒░.....░▒▓▓▓........// //.........▓▓▓▓▒░.._......░▒▓▓▓▓▓▓▓▓▓▓▓▒░......_..░▒▓▓▓.......// //.........▓▓▓▓░.._▓░▓░_....▓▓▓▓▓▓▓▓▓▓...._▒▓▒▓_..░▓▓▓▓.......// //.........▓▓▓▓.._▓█░▒█▒.....▒▓▓▓▓▓▓▒.....▒█▒░█▒_..▓▓▓▓.......// //........▒▓▓▓▓▓▒_..▒._░_▒▓▓▒░.▒▒.░▒▓▓▒_░_.▒.._▒▓▓▓▓▓▓▒.......// //.......▒▓▓▓▓▓▓▓▓▓_...▒▓▓▓▓▓░..▒▒..░▓▓▓▓▓▒..._▓▓▓▓▓▓▓▓▒......// //......▓▓▓▓▓▓▓▓▓▓▓_.▓▓▓▓▓▒.._▓▓▓█_..▒▓▓▓▓▓._▓▓▓▓▓▓▓▓▓▓▓......// //......▓▓▓▓▓▒▒▓▓▓▒▓▓▓▓▓▓▒░.▒▓▓▓▓▓█▒.░▒▓▓▓▓▓▒▒▓▓▓▒▓▓▓▓▓▓......// //......▓▓▓▓▓░.▓▓▓.▒▓▓▓▓▓░..▒▓▓▓▓▓▓▒..░▓▓▓▓▓░.▓▓▓.░▓▓▓▓▓......// //........▒▓▓█░.▓▓▓.▒▓▓▓▓▓__▓_..▒▒.._▓__▓▓▓▓▓░.▓▓▓.▒▓▓▓▒......// //.........░▒░░▒▓▒░▒▓▓▓▓▓▓▒▒▓▒▒▒▓▓▒▒▒▓▒▒▓▓▓▓▓▒░▒▓▒░░▒░░.......// //.........░▒.░▓▓░.▓▓▓▓█▒▒▒█▒▒▒█▒▒█▒▒▒█▒▒▒█▓▓▓▓.░▓▓░.▒░.......// //.............░▓▓░_▓▓▓▓█................█▓▓▓▓_░▓▓░...........// //..............░▒▓▓▓█▓._▒_.█░....█░._█_.▓█▓▓▓▒░..............// //.................░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░................// //.................░▒▓▓▓▒▒▓▒▒▓▓▓▓▒▒▓▓▓▓▒▒▓▓▒░.................// //..................▒▓▓░░▓░░▓▓▓▓░░▓▓▓▓░░▓▓▒...................// //.................▒▓░..█__▓▓▓▓__▓▓▓▓__█..░▓▒.................// //...................░...░..░▓▓░..░▓▓░..░...░.................// //.................... ..░...░▒░....░▒░...░...................// //............................................................// // Common sense not included and must be supplied by end user // // Read ENTIRE script first and LEARN: KNOW what you're using // // CC BY-NC https://creativecommons.org/licenses/by-nc/4.0/ // // ===========================================================// // IMAGE Arena Entrance Hook and Weapon Deployment // // Touch -> teleport to SPAWN POINT -> rez SlugShooter // // -> INIT secure handshake OpenSim 0.9.2.1 Yeti friendly // // ===========================================================// // CC BY-NC // https://creativecommons.org/licenses/by-nc/4.0/ // ---------------- OWNER CONFIG ---------------- string GUN_OBJECT_NAME = "SlugShooter"; //weapon name string ORBIT_SR_OBJECT_NAME = "orbitSR"; // follower audio relay object in rezzer inventory vector ORBIT_REZ_OFFSET_LOCAL = <0.6, 0.2, 0.4>; // small offset so it doesn't rez inside the gun // Staging / Dead Zone / Queue area (where user gets permissions prompt safely) vector STAGING_POS = <0,0,0>; // <-- set to your respawn / queue location vector STAGING_LOOKAT = <1,1,1>; // Rez position for the gun (near rezzer, then it follows user) vector REZ_OFFSET_LOCAL = <0.8, 0.0, 0.5>; // relative to rezzer prim rotation // Anti-spam float TOUCH_COOLDOWN_SEC = 1.5; // Security / mapping integer MAX_ACTIVE = 30; // max remembered guns (for busy regions) // ------------------------------------------------------------ list gAvs = []; // avatar keys list gChans = []; // integer channels list gTokens = []; // string tokens list gOrbitKeys = []; // keys of orbitSR list gLastTouch = []; // float times (llGetTime) // util: find avatar index integer idxOf(key av) { integer i; for (i = 0; i < llGetListLength(gAvs); i++) if (llList2Key(gAvs, i) == av) return i; return -1; } // util: generate a private-ish negative channel integer makeChan(key av) { // Derive from avatar UUID + rezzer UUID; keep it negative. string s = (string)av + (string)llGetKey(); integer c = (integer)("0x" + llGetSubString(llMD5String(s, 0), 0, 6)); if (c < 0) c = -c; if (c < 1000) c += 1000; return -c; } // util: token string makeToken(key av) { return llGetSubString(llMD5String((string)av + (string)llGetUnixTime() + (string)llFrand(999999.0), 0), 0, 10); } // util: keep lists bounded trimIfNeeded() { integer n = llGetListLength(gAvs); if (n <= MAX_ACTIVE) return; // Drop oldest entry (index 0) gAvs = llDeleteSubList(gAvs, 0, 0); gChans = llDeleteSubList(gChans, 0, 0); gTokens = llDeleteSubList(gTokens, 0, 0); gLastTouch = llDeleteSubList(gLastTouch, 0, 0); gOrbitKeys = llDeleteSubList(gOrbitKeys, 0, 0); } // send OFF to a user’s current gun (if any) sendOff(integer index) { integer ch = llList2Integer(gChans, index); string tok = llList2String(gTokens, index); // Gun listens on its private channel and will llDie() on valid OFF|token llRegionSay(ch, "OFF|" + tok); // orbitSR listens on same private channel; OFF kills it too llRegionSay(ch, "OFF|" + tok); // Optional: if we cached a key, try to kill by direct message too (belt + suspenders) key orb = llList2Key(gOrbitKeys, index); if (orb != NULL_KEY) llRegionSayTo(orb, ch, "OFF|" + tok); } // delete mapping cleanly (ALL parallel lists) clearMapping(integer index) { gAvs = llDeleteSubList(gAvs, index, index); gChans = llDeleteSubList(gChans, index, index); gTokens = llDeleteSubList(gTokens, index, index); gLastTouch = llDeleteSubList(gLastTouch, index, index); gOrbitKeys = llDeleteSubList(gOrbitKeys, index, index); } // record/update mapping setMapping(key av, integer ch, string tok) { integer i = idxOf(av); if (i == -1) { gAvs += [av]; gChans += [ch]; gTokens += [tok]; gLastTouch += [llGetTime()]; gOrbitKeys += [NULL_KEY]; trimIfNeeded(); return; } gChans = llListReplaceList(gChans, [ch], i, i); gTokens = llListReplaceList(gTokens, [tok], i, i); gLastTouch = llListReplaceList(gLastTouch, [llGetTime()], i, i); } // cooldown check integer onCooldown(key av) { integer i = idxOf(av); if (i == -1) return FALSE; float last = llList2Float(gLastTouch, i); float now = llGetTime(); if ((now - last) < TOUCH_COOLDOWN_SEC) return TRUE; gLastTouch = llListReplaceList(gLastTouch, [now], i, i); return FALSE; } default { state_entry() { // No listeners needed; we to gun via private channel using llRegionSay } on_rez(integer p) { llResetScript(); } touch_start(integer n) { key av = llDetectedKey(0); if (onCooldown(av)) return; // If they already have a gun registered, touching again can recall it cleanly. integer i = idxOf(av); if (i != -1) { sendOff(i); clearMapping(i); llInstantMessage(av, "SlugShooter recycled."); return; } // Teleport them to staging / dead zone FIRST (as requested) // (Region name is required by osTeleportAgent) string region = llGetRegionName(); osTeleportAgent(av, region, STAGING_POS, STAGING_LOOKAT); // Brief delay so the viewer/region settles before permissions prompts from the gun llSleep(0.55); // Prepare secure channel + token for this avatar integer ch = makeChan(av); string tok = makeToken(av); setMapping(av, ch, tok); // Rez the gun near the rezzer; it will follow once initialized if (llGetInventoryType(GUN_OBJECT_NAME) != INVENTORY_OBJECT) { llInstantMessage(av, "ERROR: SlugShooter object missing from rezzer inventory."); return; } vector rezPos = llGetPos() + (REZ_OFFSET_LOCAL * llGetRot()); rotation rezRot = llGetRot(); llRezObject(GUN_OBJECT_NAME, rezPos, ZERO_VECTOR, rezRot, ch); // Give the gun its marching orders on the private channel // INIT|token|avatarUUID llSleep(0.10); llRegionSay(ch, "INIT|" + tok + "|" + (string)av); // Rez orbitSR audio follower (no permissions, follows via messages) if (llGetInventoryType(ORBIT_SR_OBJECT_NAME) == INVENTORY_OBJECT) { vector oPos = llGetPos() + (ORBIT_REZ_OFFSET_LOCAL * llGetRot()); llRezObject(ORBIT_SR_OBJECT_NAME, oPos, ZERO_VECTOR, rezRot, ch); // Tell orbitSR to bind to this session token (and optionally start a loop later) llSleep(0.10); llRegionSay(ch, "BIND|" + tok + "|" + (string)av); } else { llInstantMessage(av, "WARNING: orbitSR object missing from rezzer inventory."); } llInstantMessage(av, "SlugShooter deploying. Accept permissions, then test fire in mouselook."); } }