// Copyright 2025 Fiona Sweet - Free to use for any purpose // StageGuard.lsl — OpenSim/LSL // Put this in an invisible prim that surrounds your stage. // Set the prim to PHANTOM. The script turns on volume-detect. // =================== CONFIG =================== list ALLOWED = [ // Add avatar UUIDs here (strings) "78e7a55f-5c3d-4e5f-9a20-2026adba8660", //Niki Stark "ededfb98-67d2-46ac-ab74-6a67f7660667" //Prim ]; integer ALLOW_OWNER = FALSE; // let the prim's owner always pass integer ALLOW_ESTATE = FALSE; // let estate managers/owners pass (requires os* estate checks) integer ALLOW_GROUP = FALSE; // if TRUE, allow avatars in the prim's group vector SAFE_POS = <86.0, 151.0, 30.0>; // where to send unauthorized avatars integer SHOW_DEBUG = FALSE; // TRUE shows renter/allowed hint hovertext string DENY_MSG = "Sorry, the stage is restricted. Teleporting you back to the audience area."; // Optional: if your grid doesn’t allow osTeleportAgent or you want a fallback, // set USE_PUSH_FALLBACK = TRUE to gently push people out instead of teleporting. integer USE_PUSH_FALLBACK = FALSE; float PUSH_STRENGTH = 10.0; // ============================================== // ---- internal state ---- integer gArmed = TRUE; // touch to toggle on/off integer isAllowed(key av) { // skip non-avatars if (llGetAgentSize(av) == ZERO_VECTOR) return TRUE; // Owner bypass if (ALLOW_OWNER && av == llGetOwner()) return TRUE; // Estate manager/owner bypass (OpenSim OSSL) // Not all grids expose an API for this in LSL; commonly you’d whitelist staff in ALLOWED. // If you have osIsEstateManager, uncomment this block: // if (ALLOW_ESTATE && osIsEstateManager(av)) return TRUE; // Group check if (ALLOW_GROUP && llSameGroup(av)) return TRUE; // Explicit allow list if (~llListFindList(ALLOWED, [ (string)av ])) return TRUE; return FALSE; } teleportOut(key av) { // Teleport (preferred) if (!USE_PUSH_FALLBACK) { // OSSL: requires sufficient threat level (usually estate powers). osTeleportAgent(av, SAFE_POS, ZERO_VECTOR); llInstantMessage(av, DENY_MSG); return; } // Fallback push (no OSSL) vector dir = llVecNorm(SAFE_POS - llGetPos()); if (dir == ZERO_VECTOR) dir = <1,0,0>; llPushObject(av, dir * PUSH_STRENGTH, ZERO_VECTOR, FALSE); llInstantMessage(av, DENY_MSG); } refreshDebug() { if (SHOW_DEBUG) { string txt = "StageGuard: " + (gArmed ? "ARMED" : "DISARMED") + "\nAllowed: " + (string)llGetListLength(ALLOWED) + (ALLOW_GROUP ? " + group" : "") + (ALLOW_OWNER ? " + owner" : "") + (ALLOW_ESTATE ? " + estate" : ""); llSetText(txt, <1,1,0>, 1.0); } else llSetText("", ZERO_VECTOR, 0.0); } default { state_entry() { // make sure we're a volume detector llVolumeDetect(TRUE); refreshDebug(); } on_rez(integer p) { llResetScript(); } // Touch to arm/disarm quickly (for builders) touch_start(integer n) { key who = llDetectedKey(0); // Only owner can toggle if (who != llGetOwner()) return; gArmed = !gArmed; llOwnerSay("StageGuard is now " + (gArmed ? "ARMED" : "DISARMED") + "."); refreshDebug(); } // When someone enters the stage volume collision_start(integer n) { if (!gArmed) return; integer i; for (i = 0; i < n; ++i) { key av = llDetectedKey(i); // ignore non-avatars (objects) if (llGetAgentSize(av) == ZERO_VECTOR) jump next; if (!isAllowed(av)) { teleportOut(av); } @next; } } // Keep sweeping while they remain in the volume (optional safety) collision(integer n) { if (!gArmed) return; integer i; for (i = 0; i < n; ++i) { key av = llDetectedKey(i); if (llGetAgentSize(av) == ZERO_VECTOR) jump next2; if (!isAllowed(av)) { teleportOut(av); } @next2; } } }