// 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/ // // ===========================================================// // NPC Region Manager / Killer // OpenSim 0.9.2.1 (OSSL) - Prim script // - Touch by OWNER shows dialog of NPCs in region (only NPCs) // - NPCs are listed closest-to-prim first // - Remove selected NPC, then menu reopens // - Optional mass kill: KILL ALL, and KILL PREFIX ("Grug" by default) // - Button list reversed (visual order) // ============================================================================ // ============================================================================ // [CONFIGURATION] // ============================================================================ integer CFG_ENABLE_KILL_ALL = TRUE; // Adds "KILL ALL" option integer CFG_ENABLE_KILL_PREFIX = TRUE; // Adds "KILL GRUGS" option(s) list CFG_KILL_PREFIXES = [ "Grug" ];// First-name prefixes to target integer CFG_REQUIRE_CONFIRM_MASS = TRUE; // Confirm mass kills float CFG_MENU_TIMEOUT_SECONDS = 30.0; // Listen timeout integer CFG_ALLOW_OWNER_ONLY = TRUE; // OWNER touch only (recommended) // ============================================================================ // [CONSTANTS] // ============================================================================ integer C_DIALOG_BUTTON_LIMIT = 12; string BTN_REFRESH = "REFRESH"; string BTN_PREV = "<<"; string BTN_NEXT = ">>"; string BTN_CLOSE = "CLOSE"; string BTN_KILL_ALL = "KILL ALL"; string MODE_MENU = "MENU"; string MODE_CONFIRM = "CONFIRM"; // ============================================================================ // [RUNTIME VARIABLES] // ============================================================================ integer gListenHandle = 0; integer gChan = 0; key gOwner = NULL_KEY; integer gPage = 0; list gNpcKeys = []; // keys of NPCs in region list gNpcNames = []; // full names for display, same order as gNpcKeys list gBtnMap = []; // [label, key, label, key, ...] for current page string gMode = "MENU"; // confirmation state string gConfirmKind = ""; // "ALL" or "PREFIX" string gConfirmData = ""; // prefix string if needed // ============================================================================ // [HELPERS] // ============================================================================ integer _startsWith(string s, string prefix) { integer lp = llStringLength(prefix); if (lp <= 0) return FALSE; if (llStringLength(s) < lp) return FALSE; if (llGetSubString(s, 0, lp - 1) == prefix) return TRUE; return FALSE; } string _firstName(string fullName) { integer p = llSubStringIndex(fullName, " "); if (p == -1) return fullName; return llGetSubString(fullName, 0, p - 1); } string _trimTo24(string s) { if (llStringLength(s) <= 24) return s; return llGetSubString(s, 0, 23); } string _pad2(integer n) { if (n < 10) return "0" + (string)n; return (string)n; } integer _idxInBtnMap(string label) { integer i = 0; integer len = llGetListLength(gBtnMap); for (i = 0; i < len; i += 2) { if (llList2String(gBtnMap, i) == label) return i; } return -1; } list _reverseList(list src) { list out = []; integer i = llGetListLength(src) - 1; for (; i >= 0; i = i - 1) { out += [ llList2String(src, i) ]; } return out; } vector _getObjPosSafe(key id) { list d = llGetObjectDetails(id, [OBJECT_POS]); if (llGetListLength(d) > 0) { return llList2Vector(d, 0); } return ZERO_VECTOR; } integer _npcScan() { gNpcKeys = []; gNpcNames = []; vector here = llGetPos(); // packed triples: [dist, key, name, dist, key, name, ...] list packed = []; list agents = llGetAgentList(AGENT_LIST_REGION, []); integer i = 0; integer len = llGetListLength(agents); for (i = 0; i < len; i = i + 1) { key id = llList2Key(agents, i); if (osIsNpc(id)) { string nm = llKey2Name(id); if (nm == "") nm = "(unknown)"; vector p = _getObjPosSafe(id); float dist = llVecDist(here, p); packed += [ dist, id, nm ]; } } // Selection sort by dist ascending (closest first) integer count = llGetListLength(packed) / 3; integer a = 0; for (a = 0; a < count; a = a + 1) { integer best = a; float bestDist = llList2Float(packed, (best * 3) + 0); integer b = a + 1; for (b = a + 1; b < count; b = b + 1) { float d2 = llList2Float(packed, (b * 3) + 0); if (d2 < bestDist) { best = b; bestDist = d2; } } if (best != a) { integer ai = a * 3; integer bi = best * 3; list tripleA = llList2List(packed, ai, ai + 2); list tripleB = llList2List(packed, bi, bi + 2); packed = llListReplaceList(packed, tripleB, ai, ai + 2); packed = llListReplaceList(packed, tripleA, bi, bi + 2); } } // Unpack integer t = 0; integer tlen = llGetListLength(packed); for (t = 0; t < tlen; t = t + 3) { key k = llList2Key(packed, t + 1); string n = llList2String(packed, t + 2); gNpcKeys += [ k ]; gNpcNames += [ n ]; } return llGetListLength(gNpcKeys); } integer _countNpcByPrefix(string prefix) { integer count = 0; integer i = 0; integer len = llGetListLength(gNpcNames); for (i = 0; i < len; i = i + 1) { string fn = _firstName(llList2String(gNpcNames, i)); if (_startsWith(fn, prefix)) { count = count + 1; } } return count; } integer _killNpc(key npcId) { if (npcId == NULL_KEY) return FALSE; osNpcRemove(npcId); return TRUE; } integer _killAllNpcs() { integer killed = 0; integer i = 0; integer len = llGetListLength(gNpcKeys); for (i = 0; i < len; i = i + 1) { key id = llList2Key(gNpcKeys, i); osNpcRemove(id); killed = killed + 1; } return killed; } integer _killByPrefix(string prefix) { integer killed = 0; integer i = 0; integer len = llGetListLength(gNpcKeys); for (i = 0; i < len; i = i + 1) { string fn = _firstName(llList2String(gNpcNames, i)); if (_startsWith(fn, prefix)) { key id = llList2Key(gNpcKeys, i); osNpcRemove(id); killed = killed + 1; } } return killed; } integer _openListen() { if (gListenHandle != 0) llListenRemove(gListenHandle); gChan = (integer)(llFrand(1000000.0) * -1.0) - 1000; gListenHandle = llListen(gChan, "", gOwner, ""); llSetTimerEvent(CFG_MENU_TIMEOUT_SECONDS); return TRUE; } integer _closeListen() { llSetTimerEvent(0.0); if (gListenHandle != 0) { llListenRemove(gListenHandle); gListenHandle = 0; } return TRUE; } list _buildMassButtons() { list mass = []; if (CFG_ENABLE_KILL_ALL) { mass += [ BTN_KILL_ALL ]; } if (CFG_ENABLE_KILL_PREFIX) { integer i = 0; integer len = llGetListLength(CFG_KILL_PREFIXES); for (i = 0; i < len; i = i + 1) { string p = llList2String(CFG_KILL_PREFIXES, i); string b = "KILL " + p + "S"; mass += [ _trimTo24(b) ]; } } return mass; } integer _showMenu() { gMode = MODE_MENU; _npcScan(); integer npcCount = llGetListLength(gNpcKeys); integer pageCount = 1; list massBtns = _buildMassButtons(); integer reserved = 4 + llGetListLength(massBtns); integer npcSlots = C_DIALOG_BUTTON_LIMIT - reserved; if (npcSlots < 1) npcSlots = 1; if (npcCount > 0) { pageCount = (npcCount + npcSlots - 1) / npcSlots; if (gPage < 0) gPage = 0; if (gPage >= pageCount) gPage = pageCount - 1; } else { gPage = 0; } integer start = gPage * npcSlots; integer end = start + npcSlots - 1; if (end >= npcCount) end = npcCount - 1; list buttons = []; gBtnMap = []; if (npcCount > 0) { integer idx = 0; for (idx = start; idx <= end; idx = idx + 1) { string nm = llList2String(gNpcNames, idx); integer ord = (idx - start) + 1; string label = _trimTo24(_pad2(ord) + " " + nm); buttons += [ label ]; gBtnMap += [ label, llList2Key(gNpcKeys, idx) ]; } } buttons += massBtns; buttons += [ BTN_REFRESH, BTN_PREV, BTN_NEXT, BTN_CLOSE ]; string title = ""; //default 'NPC Manager\n' title += "NPCs in region: " + (string)npcCount + "\n"; if (npcCount > 0) { title += "Page " + (string)(gPage + 1) + " / " + (string)pageCount + "\n"; title += "Select an NPC to remove."; } else { title += "No NPCs detected.\n"; title += "Mass buttons (if enabled) still exist because humans love danger."; } _openListen(); // Reverse visual button order (LSL-safe) buttons = _reverseList(buttons); llDialog(gOwner, title, buttons, gChan); return TRUE; } integer _showConfirm(string kind, string data) { gMode = MODE_CONFIRM; gConfirmKind = kind; gConfirmData = data; string msg = "Confirm action:\n"; if (kind == "ALL") { msg += "Remove ALL NPCs in region.\n"; msg += "This is the part where people regret clicking."; } else if (kind == "PREFIX") { integer count = _countNpcByPrefix(data); msg += "Remove NPCs with first-name prefix: " + data + "\n"; msg += "Matches now: " + (string)count + "\n"; } else { msg += "Unknown confirm mode."; } list btns = [ "YES", "NO" ]; _openListen(); llDialog(gOwner, msg, btns, gChan); return TRUE; } // ============================================================================ // [STATES] // ============================================================================ default { state_entry() { gOwner = llGetOwner(); _closeListen(); } on_rez(integer p) { llResetScript(); } changed(integer c) { if (c & CHANGED_OWNER) { llResetScript(); } } touch_start(integer n) { key toucher = llDetectedKey(0); if (CFG_ALLOW_OWNER_ONLY) { if (toucher != llGetOwner()) { return; } } gOwner = llGetOwner(); gPage = 0; _showMenu(); } listen(integer channel, string name, key id, string message) { if (id != gOwner) return; if (gMode == MODE_CONFIRM) { if (message == "NO") { _showMenu(); return; } if (message == "YES") { _npcScan(); if (gConfirmKind == "ALL") { integer k = _killAllNpcs(); llOwnerSay("NPC Manager: Removed " + (string)k + " NPC(s)."); } else if (gConfirmKind == "PREFIX") { integer k2 = _killByPrefix(gConfirmData); llOwnerSay("NPC Manager: Removed " + (string)k2 + " NPC(s) with prefix '" + gConfirmData + "'."); } _showMenu(); return; } return; } if (message == BTN_CLOSE) { _closeListen(); return; } if (message == BTN_REFRESH) { _showMenu(); return; } if (message == BTN_PREV) { gPage = gPage - 1; _showMenu(); return; } if (message == BTN_NEXT) { gPage = gPage + 1; _showMenu(); return; } if (message == BTN_KILL_ALL) { if (!CFG_ENABLE_KILL_ALL) return; if (CFG_REQUIRE_CONFIRM_MASS) { _showConfirm("ALL", ""); return; } _npcScan(); integer k = _killAllNpcs(); llOwnerSay("NPC Manager: Removed " + (string)k + " NPC(s)."); _showMenu(); return; } if (CFG_ENABLE_KILL_PREFIX) { integer i = 0; integer len = llGetListLength(CFG_KILL_PREFIXES); for (i = 0; i < len; i = i + 1) { string p = llList2String(CFG_KILL_PREFIXES, i); string b = _trimTo24("KILL " + p + "S"); if (message == b) { if (CFG_REQUIRE_CONFIRM_MASS) { _showConfirm("PREFIX", p); return; } _npcScan(); integer k2 = _killByPrefix(p); llOwnerSay("NPC Manager: Removed " + (string)k2 + " NPC(s) with prefix '" + p + "'."); _showMenu(); return; } } } integer at = _idxInBtnMap(message); if (at != -1) { key npcId = llList2Key(gBtnMap, at + 1); _killNpc(npcId); _showMenu(); return; } } timer() { _closeListen(); } }