updated to useing preprocesser in firestorm, saveing space in memory, changed to new experience database

This commit is contained in:
RebeccaNod1 2026-03-24 16:46:07 -06:00
parent 991b478e01
commit 5c3eb15c16
7 changed files with 387 additions and 175 deletions

138
Experience_Admin_HUD.lsl Normal file
View file

@ -0,0 +1,138 @@
// ============================================================================
// Experience Admin HUD
// ============================================================================
// Instructions:
// 1. Create a new box, put this script inside.
// 2. DO NOT FORGET to check "Use Experience" and select Final Girlz I.N.C!
// 3. Attach it to your HUD or click it if it's rezzed.
//
// Commands (type in local chat):
// /99 keys - Lists all keys currently in the entire database
// /99 get [Key] - Reads and displays the data inside a specific key
// /99 del [Key] - Deletes a specific key from the database
// ============================================================================
key requestKeys;
key requestRead;
key requestDelete;
key requestWrite;
key lastUser;
string pendingSetKey;
string pendingSetValue;
default {
state_entry() {
lastUser = llGetOwner();
llRegionSayTo(lastUser, 0, "🔧 Experience Admin Terminal Ready.");
llRegionSayTo(lastUser, 0, "💬 Type commands in local chat:");
llRegionSayTo(lastUser, 0, " /99 keys");
llRegionSayTo(lastUser, 0, " /99 get key_name");
llRegionSayTo(lastUser, 0, " /99 set key_name=Hello World");
llRegionSayTo(lastUser, 0, " /99 del key_name");
llListen(99, "", NULL_KEY, "");
}
listen(integer channel, string name, key id, string msg) {
// SECURITY CHECK: Strictly whitelist authorized Admin usernames!
string uname = llStringTrim(llToLower(llGetUsername(id)), STRING_TRIM);
if (id != llGetOwner() && uname != "djmusica28" && uname != "finalgirlzbot") {
llRegionSayTo(id, 0, "❌ Access Denied: You are not on the authorized Administrator whitelist for this database.");
return;
}
lastUser = id;
msg = llStringTrim(msg, STRING_TRIM);
if (msg == "keys") {
llRegionSayTo(lastUser, 0, "🔍 Requesting list of database keys (up to 100)...");
requestKeys = llKeysKeyValue(0, 100);
}
else if (llGetSubString(msg, 0, 3) == "get ") {
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "🔍 Reading key: " + targetKey + "...");
requestRead = llReadKeyValue(targetKey);
}
else if (llGetSubString(msg, 0, 3) == "set ") {
string payload = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
integer equalSign = llSubStringIndex(payload, "=");
if (equalSign != -1) {
pendingSetKey = llStringTrim(llGetSubString(payload, 0, equalSign - 1), STRING_TRIM);
pendingSetValue = llStringTrim(llGetSubString(payload, equalSign + 1, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "💾 Writing key: " + pendingSetKey + "...");
requestWrite = llUpdateKeyValue(pendingSetKey, pendingSetValue, FALSE, "");
} else {
llRegionSayTo(lastUser, 0, "❌ Format: /99 set key=value");
}
}
else if (llGetSubString(msg, 0, 3) == "del ") {
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
llRegionSayTo(lastUser, 0, "🗑️ Deleting key: " + targetKey + "...");
requestDelete = llDeleteKeyValue(targetKey);
}
}
dataserver(key queryid, string data) {
// llKeysKeyValue returns data as CSV:
// Success: 1,keyname1,keyname2...
// Failure: 0,error_code
if (queryid == requestKeys) {
list result = llCSV2List(data);
if (llList2Integer(result, 0) == 1) {
// Success
integer numKeys = llGetListLength(result) - 1;
llRegionSayTo(lastUser, 0, "✅ Found " + (string)numKeys + " keys in database:");
integer i;
for(i = 1; i < llGetListLength(result); i++) {
llRegionSayTo(lastUser, 0, " 🔑 " + llList2String(result, i));
}
} else if (llList2Integer(result, 1) == 3) {
llRegionSayTo(lastUser, 0, "📭 The database is completely empty! No keys exist.");
} else {
llRegionSayTo(lastUser, 0, "❌ Failed to read keys (Error " + llList2String(result, 1) + ")");
}
}
// llReadKeyValue returns data as: status,value
else if (queryid == requestRead) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
string val = llGetSubString(data, comma + 1, -1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "📄 Content:\n" + val);
} else if (status == "3") {
llRegionSayTo(lastUser, 0, "❌ That key doesn't exist!");
} else {
llRegionSayTo(lastUser, 0, "❌ Read failed (Status: " + status + ")");
}
}
// llDeleteKeyValue dataserver logic
else if (queryid == requestDelete) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "✅ Key successfully deleted!");
} else if (status == "3") {
llRegionSayTo(lastUser, 0, "❌ Key didn't exist to delete.");
} else {
llRegionSayTo(lastUser, 0, "❌ Delete failed (Status: " + status + ")");
}
}
// Write logic
else if (queryid == requestWrite) {
integer comma = llSubStringIndex(data, ",");
string status = llGetSubString(data, 0, comma - 1);
if (status == "1") {
llRegionSayTo(lastUser, 0, "✅ Key saved successfully!");
} else if (status == "3") {
// Key didn't exist to update, manually create it!
requestWrite = llCreateKeyValue(pendingSetKey, pendingSetValue);
} else {
llRegionSayTo(lastUser, 0, "❌ Write failed (Status: " + status + ")");
}
}
}
}

View file

@ -92,7 +92,15 @@ reportMemoryUsage(string scriptName) {
} }
// Verbose logging control - toggled by owner // Verbose logging control - toggled by owner
#define DEBUG_LOGS 0 // Set to 1 to enable logs, 0 to STRIP from memory completely
#if DEBUG_LOGS
#define dbg(msg) dbg(msg)
integer VERBOSE_LOGGING = FALSE; integer VERBOSE_LOGGING = FALSE;
#else
#define dbg(msg)
#define VERBOSE_LOGGING 0
#endif
// Original VERBOSE_LOGGING definition removed
// Message constants // Message constants
#define MSG_SHOW_DIALOG 101 #define MSG_SHOW_DIALOG 101
@ -118,30 +126,30 @@ continueCurrentRound() {
if (continuePlayerIdx != -1 && continuePlayerIdx < llGetListLength(lives)) { if (continuePlayerIdx != -1 && continuePlayerIdx < llGetListLength(lives)) {
integer continuePlayerLives = llList2Integer(lives, continuePlayerIdx); integer continuePlayerLives = llList2Integer(lives, continuePlayerIdx);
if (continuePlayerLives <= 0) { if (continuePlayerLives <= 0) {
llOwnerSay("🛑 [Game Manager] continueCurrentRound: Found eliminated player " + continueName + " with " + (string)continuePlayerLives + " lives - aborting round start"); dbg("🛑 [Game Manager] continueCurrentRound: Found eliminated player " + continueName + " with " + (string)continuePlayerLives + " lives - aborting round start");
return; return;
} }
} }
} }
if (perilPlayer == "" || perilPlayer == "NONE") { if (perilPlayer == "" || perilPlayer == "NONE") {
llOwnerSay("🛑 [Game Manager] continueCurrentRound: Invalid peril player (" + perilPlayer + ") - aborting round start"); dbg("🛑 [Game Manager] continueCurrentRound: Invalid peril player (" + perilPlayer + ") - aborting round start");
return; return;
} }
// Verify peril player exists and has lives // Verify peril player exists and has lives
integer perilIdx = llListFindList(names, [perilPlayer]); integer perilIdx = llListFindList(names, [perilPlayer]);
if (perilIdx == -1) { if (perilIdx == -1) {
llOwnerSay("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " not found in names list - aborting round start"); dbg("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " not found in names list - aborting round start");
return; return;
} }
if (perilIdx >= llGetListLength(lives)) { if (perilIdx >= llGetListLength(lives)) {
llOwnerSay("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " index out of bounds - aborting round start"); dbg("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " index out of bounds - aborting round start");
return; return;
} }
integer perilLives = llList2Integer(lives, perilIdx); integer perilLives = llList2Integer(lives, perilIdx);
if (perilLives <= 0) { if (perilLives <= 0) {
llOwnerSay("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " has " + (string)perilLives + " lives - aborting round start"); dbg("🛑 [Game Manager] continueCurrentRound: Peril player " + perilPlayer + " has " + (string)perilLives + " lives - aborting round start");
return; return;
} }
@ -149,7 +157,7 @@ continueCurrentRound() {
// globalPickedNumbers will be cleared when first player picks // globalPickedNumbers will be cleared when first player picks
picksData = []; picksData = [];
llOwnerSay("🔄 [Game Manager] continueCurrentRound: All validation passed, starting fresh round with " + (string)llGetListLength(names) + " players"); dbg("🔄 [Game Manager] continueCurrentRound: All validation passed, starting fresh round with " + (string)llGetListLength(names) + " players");
// Set round as started for the new round // Set round as started for the new round
roundStarted = TRUE; roundStarted = TRUE;
@ -165,7 +173,7 @@ continueCurrentRound() {
} }
currentPickerIdx = 0; currentPickerIdx = 0;
llOwnerSay("🎯 [Game Manager] Pick queue created: " + llList2CSV(pickQueue) + ", requesting dice type"); dbg("🎯 [Game Manager] Pick queue created: " + llList2CSV(pickQueue) + ", requesting dice type");
// Request dice type directly from Calculator for the new round // Request dice type directly from Calculator for the new round
llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY); llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY);
@ -176,12 +184,12 @@ continueCurrentRound() {
startNextRound() { startNextRound() {
// Prevent multiple calls to start round // Prevent multiple calls to start round
if (roundStarted) { if (roundStarted) {
llOwnerSay("⚠️ Round already started, ignoring duplicate start request"); dbg("⚠️ Round already started, ignoring duplicate start request");
return; return;
} }
if (llGetListLength(names) < 2) { if (llGetListLength(names) < 2) {
llOwnerSay("⚠️ Need at least 2 players to start the game."); dbg("⚠️ Need at least 2 players to start the game.");
return; return;
} }
@ -200,7 +208,7 @@ startNextRound() {
return; return;
} }
llOwnerSay("🎯 Game Manager starting new round..."); dbg("🎯 Game Manager starting new round...");
// Set roundStarted immediately to prevent duplicate calls // Set roundStarted immediately to prevent duplicate calls
roundStarted = TRUE; roundStarted = TRUE;
@ -216,7 +224,7 @@ startNextRound() {
llMessageLinked(12, 3005, perilPlayer, NULL_KEY); // MSG_UPDATE_PERIL_PLAYER llMessageLinked(12, 3005, perilPlayer, NULL_KEY); // MSG_UPDATE_PERIL_PLAYER
// Sync state to floater manager first, then update floaters to show new peril player immediately // Sync state to floater manager first, then update floaters to show new peril player immediately
llOwnerSay("🔄 [Game Manager] Syncing state and updating floaters for new peril player: " + perilPlayer); dbg("🔄 [Game Manager] Syncing state and updating floaters for new peril player: " + perilPlayer);
syncStateToMain(); // Sync the peril player to all modules first syncStateToMain(); // Sync the peril player to all modules first
llSleep(0.1); // Brief delay to ensure sync propagates llSleep(0.1); // Brief delay to ensure sync propagates
integer startJ; integer startJ;
@ -230,7 +238,7 @@ startNextRound() {
// globalPickedNumbers will be cleared when first player picks // globalPickedNumbers will be cleared when first player picks
// Create pick queue with peril player first // Create pick queue with peril player first
llOwnerSay("🎯 Debug - Creating pickQueue. names: " + llList2CSV(names) + ", perilPlayer: " + perilPlayer); dbg("🎯 Debug - Creating pickQueue. names: " + llList2CSV(names) + ", perilPlayer: " + perilPlayer);
pickQueue = [perilPlayer]; pickQueue = [perilPlayer];
integer k; integer k;
for (k = 0; k < llGetListLength(names); k++) { for (k = 0; k < llGetListLength(names); k++) {
@ -240,11 +248,11 @@ startNextRound() {
} }
} }
currentPickerIdx = 0; currentPickerIdx = 0;
llOwnerSay("🎯 Debug - Created pickQueue: " + llList2CSV(pickQueue) + ", currentPickerIdx: " + (string)currentPickerIdx); dbg("🎯 Debug - Created pickQueue: " + llList2CSV(pickQueue) + ", currentPickerIdx: " + (string)currentPickerIdx);
// Request dice type for this round // Request dice type for this round
llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY); llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY);
llOwnerSay("🎯 Game Manager round setup complete, requesting dice type..."); dbg("🎯 Game Manager round setup complete, requesting dice type...");
} }
showNextPickerDialog() { showNextPickerDialog() {
@ -254,40 +262,40 @@ showNextPickerDialog() {
// Clear globalPickedNumbers if this is the first picker of a new round // Clear globalPickedNumbers if this is the first picker of a new round
if (currentPickerIdx == 0 && llGetListLength(picksData) == 0) { if (currentPickerIdx == 0 && llGetListLength(picksData) == 0) {
globalPickedNumbers = []; globalPickedNumbers = [];
llOwnerSay("🔄 [Game Manager] Cleared globalPickedNumbers at start of new round"); dbg("🔄 [Game Manager] Cleared globalPickedNumbers at start of new round");
} }
if (diceType <= 0) { if (diceType <= 0) {
llOwnerSay("❌ Cannot show picker dialog: diceType not set (" + (string)diceType + ")"); dbg("❌ Cannot show picker dialog: diceType not set (" + (string)diceType + ")");
return; return;
} }
if (currentPickerIdx >= llGetListLength(pickQueue)) { if (currentPickerIdx >= llGetListLength(pickQueue)) {
llOwnerSay("❌ Cannot show picker dialog: currentPickerIdx (" + (string)currentPickerIdx + ") >= pickQueue length (" + (string)llGetListLength(pickQueue) + ")"); dbg("❌ Cannot show picker dialog: currentPickerIdx (" + (string)currentPickerIdx + ") >= pickQueue length (" + (string)llGetListLength(pickQueue) + ")");
return; return;
} }
string firstName = llList2String(pickQueue, currentPickerIdx); string firstName = llList2String(pickQueue, currentPickerIdx);
if (firstName == "") { if (firstName == "") {
llOwnerSay("❌ Cannot show picker dialog: empty player name at index " + (string)currentPickerIdx); dbg("❌ Cannot show picker dialog: empty player name at index " + (string)currentPickerIdx);
return; return;
} }
// CRITICAL: Verify this player is still alive and in the game // CRITICAL: Verify this player is still alive and in the game
integer showPlayerIdx = llListFindList(names, [firstName]); integer showPlayerIdx = llListFindList(names, [firstName]);
if (showPlayerIdx == -1) { if (showPlayerIdx == -1) {
llOwnerSay("❌ Cannot show picker dialog: player " + firstName + " not found in current game"); dbg("❌ Cannot show picker dialog: player " + firstName + " not found in current game");
return; return;
} }
if (showPlayerIdx >= llGetListLength(lives)) { if (showPlayerIdx >= llGetListLength(lives)) {
llOwnerSay("❌ Cannot show picker dialog: player " + firstName + " index out of bounds for lives list"); dbg("❌ Cannot show picker dialog: player " + firstName + " index out of bounds for lives list");
return; return;
} }
integer showPlayerLives = llList2Integer(lives, showPlayerIdx); integer showPlayerLives = llList2Integer(lives, showPlayerIdx);
if (showPlayerLives <= 0) { if (showPlayerLives <= 0) {
llOwnerSay("❌ Cannot show picker dialog: player " + firstName + " has been eliminated (" + (string)showPlayerLives + " lives)"); dbg("❌ Cannot show picker dialog: player " + firstName + " has been eliminated (" + (string)showPlayerLives + " lives)");
return; return;
} }
@ -299,7 +307,7 @@ showNextPickerDialog() {
string existingPicks = llGetSubString(llList2String(picksData, showIdx), llStringLength(firstName) + 1, -1); string existingPicks = llGetSubString(llList2String(picksData, showIdx), llStringLength(firstName) + 1, -1);
if (existingPicks != "") { if (existingPicks != "") {
alreadyHasPicks = TRUE; alreadyHasPicks = TRUE;
llOwnerSay("🎯 Game Manager: " + firstName + " already has picks (" + existingPicks + "), advancing to next player"); dbg("🎯 Game Manager: " + firstName + " already has picks (" + existingPicks + "), advancing to next player");
} }
} }
} }
@ -319,7 +327,7 @@ showNextPickerDialog() {
integer nameIdx = llListFindList(names, [firstName]); integer nameIdx = llListFindList(names, [firstName]);
if (nameIdx == -1) { if (nameIdx == -1) {
llOwnerSay("❌ Cannot show picker dialog: player " + firstName + " not found in names list"); dbg("❌ Cannot show picker dialog: player " + firstName + " not found in names list");
return; return;
} }
@ -337,7 +345,7 @@ showNextPickerDialog() {
string existingBotPicks = llGetSubString(llList2String(picksData, showJ), llStringLength(firstName) + 1, -1); string existingBotPicks = llGetSubString(llList2String(picksData, showJ), llStringLength(firstName) + 1, -1);
if (existingBotPicks != "") { if (existingBotPicks != "") {
botAlreadyHasPicks = TRUE; botAlreadyHasPicks = TRUE;
llOwnerSay("⚠️ [Game Manager] Bot " + firstName + " already has picks, skipping bot command: " + existingBotPicks); dbg("⚠️ [Game Manager] Bot " + firstName + " already has picks, skipping bot command: " + existingBotPicks);
} }
} }
} }
@ -354,9 +362,9 @@ showNextPickerDialog() {
list showCompleteAvoidList = buildCompleteAvoidanceList(); list showCompleteAvoidList = buildCompleteAvoidanceList();
string showAvoidListStr = llList2CSV(showCompleteAvoidList); string showAvoidListStr = llList2CSV(showCompleteAvoidList);
string botCommand = "BOT_PICK:" + firstName + ":" + (string)showPicksNeeded + ":" + (string)diceType + ":" + showAvoidListStr; string botCommand = "BOT_PICK:" + firstName + ":" + (string)showPicksNeeded + ":" + (string)diceType + ":" + showAvoidListStr;
llOwnerSay("🎯 [Game Manager] Sending bot command with complete avoid list (" + (string)llGetListLength(showCompleteAvoidList) + " numbers): " + showAvoidListStr); dbg("🎯 [Game Manager] Sending bot command with complete avoid list (" + (string)llGetListLength(showCompleteAvoidList) + " numbers): " + showAvoidListStr);
llMessageLinked(LINK_SET, -9999, botCommand, NULL_KEY); llMessageLinked(LINK_SET, -9999, botCommand, NULL_KEY);
llOwnerSay("🤖 " + firstName + " is automatically picking " + (string)showPicksNeeded + " numbers..."); dbg("🤖 " + firstName + " is automatically picking " + (string)showPicksNeeded + " numbers...");
} else { } else {
// Bot already has picks, advance to next player immediately // Bot already has picks, advance to next player immediately
currentPickerIdx++; currentPickerIdx++;
@ -382,7 +390,7 @@ showNextPickerDialog() {
string humanAvoidListStr = llList2CSV(humanCompleteAvoidList); string humanAvoidListStr = llList2CSV(humanCompleteAvoidList);
string dialogPayload = firstName + "|" + (string)diceType + "|" + (string)humanPicksNeeded + "|" + humanAvoidListStr; string dialogPayload = firstName + "|" + (string)diceType + "|" + (string)humanPicksNeeded + "|" + humanAvoidListStr;
llOwnerSay("🎯 Showing pick dialog for " + firstName); dbg("🎯 Showing pick dialog for " + firstName);
llSleep(0.5); // Brief delay to prevent spam llSleep(0.5); // Brief delay to prevent spam
// Send dialog request through Player_RegistrationManager (it has the correct player keys) // Send dialog request through Player_RegistrationManager (it has the correct player keys)
@ -403,7 +411,7 @@ syncStateToMain() {
default { default {
state_entry() { state_entry() {
reportMemoryUsage("Game Manager"); reportMemoryUsage("Game Manager");
llOwnerSay("🎯 Game Manager initializing..."); dbg("🎯 Game Manager initializing...");
// Initialize/reset all game state variables // Initialize/reset all game state variables
players = []; players = [];
@ -424,12 +432,12 @@ default {
lastBotPickMessage = ""; lastBotPickMessage = "";
lastProcessTime = 0; lastProcessTime = 0;
llOwnerSay("🎯 Game Manager ready!"); dbg("🎯 Game Manager ready!");
} }
on_rez(integer start_param) { on_rez(integer start_param) {
reportMemoryUsage("Game Manager"); reportMemoryUsage("Game Manager");
llOwnerSay("🔄 Game Manager rezzed - reinitializing..."); dbg("🔄 Game Manager rezzed - reinitializing...");
// Reset all game state variables on rez // Reset all game state variables on rez
players = []; players = [];
@ -450,7 +458,7 @@ default {
lastBotPickMessage = ""; lastBotPickMessage = "";
lastProcessTime = 0; lastProcessTime = 0;
llOwnerSay("✅ Game Manager reset complete after rez!"); dbg("✅ Game Manager reset complete after rez!");
} }
link_message(integer sender, integer num, string str, key id) { link_message(integer sender, integer num, string str, key id) {
@ -528,7 +536,7 @@ default {
} }
if (livesChanged && newPerilPlayer != "" && newPerilPlayer != "NONE" && roundStarted && allPicksEmpty) { if (livesChanged && newPerilPlayer != "" && newPerilPlayer != "NONE" && roundStarted && allPicksEmpty) {
llOwnerSay("🎯 Post-roll state update detected - continuing round"); dbg("🎯 Post-roll state update detected - continuing round");
continueCurrentRound(); continueCurrentRound();
} }
} }
@ -538,21 +546,21 @@ default {
// Receive legacy game state updates from main controller (simplified version) // Receive legacy game state updates from main controller (simplified version)
if (num == 9071) { if (num == 9071) {
llOwnerSay("🔧 [Game Manager] Received sync: " + str); dbg("🔧 [Game Manager] Received sync: " + str);
list parts = llParseString2List(str, ["~"], []); list parts = llParseString2List(str, ["~"], []);
llOwnerSay("🔧 [Game Manager] Parsed into " + (string)llGetListLength(parts) + " parts"); dbg("🔧 [Game Manager] Parsed into " + (string)llGetListLength(parts) + " parts");
// MEMORY OPTIMIZED: Skip heavy validation that causes sync rejections // MEMORY OPTIMIZED: Skip heavy validation that causes sync rejections
// Trust that Main Controller sends valid data // Trust that Main Controller sends valid data
// Handle special RESET sync message // Handle special RESET sync message
if (llGetListLength(parts) >= 5 && llList2String(parts, 0) == "RESET") { if (llGetListLength(parts) >= 5 && llList2String(parts, 0) == "RESET") {
llOwnerSay("🔄 [Game Manager] Received reset sync - ignoring during reset"); dbg("🔄 [Game Manager] Received reset sync - ignoring during reset");
return; return;
} }
if (llGetListLength(parts) < 4) { if (llGetListLength(parts) < 4) {
llOwnerSay("⚠️ [Game Manager] Incomplete sync message received, parts: " + (string)llGetListLength(parts) + " - IGNORING"); dbg("⚠️ [Game Manager] Incomplete sync message received, parts: " + (string)llGetListLength(parts) + " - IGNORING");
return; return;
} }
{ {
@ -588,14 +596,14 @@ default {
} }
} }
llOwnerSay("🎯 [Game Manager] Updated: " + (string)llGetListLength(names) + " players (minimal parsing)"); dbg("🎯 [Game Manager] Updated: " + (string)llGetListLength(names) + " players (minimal parsing)");
} }
// Update peril player // Update peril player
string receivedPerilPlayer = llList2String(parts, 2); string receivedPerilPlayer = llList2String(parts, 2);
if (receivedPerilPlayer != "NONE" && receivedPerilPlayer != "") { if (receivedPerilPlayer != "NONE" && receivedPerilPlayer != "") {
perilPlayer = receivedPerilPlayer; perilPlayer = receivedPerilPlayer;
llOwnerSay("🎯 [Game Manager] Peril player updated to: " + perilPlayer); dbg("🎯 [Game Manager] Peril player updated to: " + perilPlayer);
} }
// MEMORY OPTIMIZED: Skip all other complex processing // MEMORY OPTIMIZED: Skip all other complex processing
@ -608,26 +616,26 @@ default {
list newPickQueue = llCSV2List(str); list newPickQueue = llCSV2List(str);
string senderStr = (string)sender; string senderStr = (string)sender;
string queueLenStr = (string)llGetListLength(newPickQueue); string queueLenStr = (string)llGetListLength(newPickQueue);
llOwnerSay("🔍 [Game Manager] Received pickQueue sync from sender " + senderStr + ": '" + str + "' (" + queueLenStr + " items)"); dbg("🔍 [Game Manager] Received pickQueue sync from sender " + senderStr + ": '" + str + "' (" + queueLenStr + " items)");
// STRONGEST PROTECTION: Never accept empty pickQueues during any active round // STRONGEST PROTECTION: Never accept empty pickQueues during any active round
if (roundStarted && llGetListLength(pickQueue) > 0 && (str == "" || llGetListLength(newPickQueue) == 0)) { if (roundStarted && llGetListLength(pickQueue) > 0 && (str == "" || llGetListLength(newPickQueue) == 0)) {
llOwnerSay("🔍 [Game Manager] REJECTING empty/invalid pickQueue sync during active round - keeping: " + llList2CSV(pickQueue)); dbg("🔍 [Game Manager] REJECTING empty/invalid pickQueue sync during active round - keeping: " + llList2CSV(pickQueue));
return; return;
} }
// Additional protection: Don't accept pickQueue syncs if we just created a valid one // Additional protection: Don't accept pickQueue syncs if we just created a valid one
if (llGetListLength(pickQueue) > 0 && (str == "" || llGetListLength(newPickQueue) == 0)) { if (llGetListLength(pickQueue) > 0 && (str == "" || llGetListLength(newPickQueue) == 0)) {
llOwnerSay("🔍 [Game Manager] REJECTING empty pickQueue sync - keeping valid queue: " + llList2CSV(pickQueue)); dbg("🔍 [Game Manager] REJECTING empty pickQueue sync - keeping valid queue: " + llList2CSV(pickQueue));
return; return;
} }
// Only accept valid non-empty pickQueues // Only accept valid non-empty pickQueues
if (llGetListLength(newPickQueue) > 0) { if (llGetListLength(newPickQueue) > 0) {
pickQueue = newPickQueue; pickQueue = newPickQueue;
llOwnerSay("🔍 [Game Manager] pickQueue updated to: " + llList2CSV(pickQueue)); dbg("🔍 [Game Manager] pickQueue updated to: " + llList2CSV(pickQueue));
} else { } else {
llOwnerSay("🔍 [Game Manager] Ignoring invalid pickQueue sync - keeping current: " + llList2CSV(pickQueue)); dbg("🔍 [Game Manager] Ignoring invalid pickQueue sync - keeping current: " + llList2CSV(pickQueue));
} }
return; return;
} }
@ -636,13 +644,13 @@ default {
if (num == MSG_DICE_TYPE_RESULT) { if (num == MSG_DICE_TYPE_RESULT) {
// Prevent duplicate processing of dice type results // Prevent duplicate processing of dice type results
if (diceTypeProcessed) { if (diceTypeProcessed) {
llOwnerSay("🎲 Ignoring duplicate dice type result: " + str); dbg("🎲 Ignoring duplicate dice type result: " + str);
return; return;
} }
diceType = (integer)str; diceType = (integer)str;
diceTypeProcessed = TRUE; diceTypeProcessed = TRUE;
llOwnerSay("🎲 Game Manager received dice type: d" + str + " from Calculator"); dbg("🎲 Game Manager received dice type: d" + str + " from Calculator");
llSleep(0.3); // Brief delay llSleep(0.3); // Brief delay
// Start dialog if round has been started and we have a queue // Start dialog if round has been started and we have a queue
@ -662,7 +670,7 @@ default {
} }
if (!alreadyHasPicks) { if (!alreadyHasPicks) {
llOwnerSay("🎯 Starting picks for " + currentPlayerName); dbg("🎯 Starting picks for " + currentPlayerName);
llSleep(0.5); llSleep(0.5);
showNextPickerDialog(); showNextPickerDialog();
} }
@ -679,7 +687,7 @@ default {
string playerName = llList2String(parts, 1); string playerName = llList2String(parts, 1);
string picksStr = llList2String(parts, 2); string picksStr = llList2String(parts, 2);
llOwnerSay("👤 HUMAN: " + playerName + " picking " + picksStr + " (global has: " + llList2CSV(globalPickedNumbers) + ")"); dbg("👤 HUMAN: " + playerName + " picking " + picksStr + " (global has: " + llList2CSV(globalPickedNumbers) + ")");
// Check if this player already has picks to prevent duplicate processing // Check if this player already has picks to prevent duplicate processing
integer alreadyHasPicks = FALSE; integer alreadyHasPicks = FALSE;
@ -689,7 +697,7 @@ default {
if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) { if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) {
string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1); string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1);
if (existingPicks != "") { if (existingPicks != "") {
llOwnerSay("⚠️ [Game Manager] " + playerName + " already has picks: " + existingPicks + ", ignoring duplicate HUMAN_PICKED"); dbg("⚠️ [Game Manager] " + playerName + " already has picks: " + existingPicks + ", ignoring duplicate HUMAN_PICKED");
alreadyHasPicks = TRUE; alreadyHasPicks = TRUE;
} }
} }
@ -706,7 +714,7 @@ default {
list duplicatePicks = []; list duplicatePicks = [];
integer i; integer i;
llOwnerSay("🔍 [Game Manager] Validating " + playerName + "'s picks against complete avoid list (" + (string)llGetListLength(completeAvoidList) + " numbers): " + llList2CSV(completeAvoidList)); dbg("🔍 [Game Manager] Validating " + playerName + "'s picks against complete avoid list (" + (string)llGetListLength(completeAvoidList) + " numbers): " + llList2CSV(completeAvoidList));
for (i = 0; i < llGetListLength(newPicks); i++) { for (i = 0; i < llGetListLength(newPicks); i++) {
string pick = llStringTrim(llList2String(newPicks, i), STRING_TRIM); string pick = llStringTrim(llList2String(newPicks, i), STRING_TRIM);
@ -721,7 +729,7 @@ default {
} }
if (llGetListLength(duplicatePicks) > 0) { if (llGetListLength(duplicatePicks) > 0) {
llOwnerSay("❌ DUPLICATE PICKS REJECTED: " + playerName + " tried to pick: " + llList2CSV(duplicatePicks)); dbg("❌ DUPLICATE PICKS REJECTED: " + playerName + " tried to pick: " + llList2CSV(duplicatePicks));
integer idx = llListFindList(names, [playerName]); integer idx = llListFindList(names, [playerName]);
if (idx != -1) { if (idx != -1) {
llRegionSayTo(llList2Key(players, idx), 0, "❌ Some picks were already taken. Please pick again."); llRegionSayTo(llList2Key(players, idx), 0, "❌ Some picks were already taken. Please pick again.");
@ -736,7 +744,7 @@ default {
} }
} }
// Re-show the dialog with updated globally picked numbers // Re-show the dialog with updated globally picked numbers
llOwnerSay("⚠️ [Game Manager] Re-showing pick dialog for " + playerName + " with updated picks"); dbg("⚠️ [Game Manager] Re-showing pick dialog for " + playerName + " with updated picks");
// Calculate picks needed based on peril player's lives // Calculate picks needed based on peril player's lives
integer perilIdx = llListFindList(names, [perilPlayer]); integer perilIdx = llListFindList(names, [perilPlayer]);
@ -771,7 +779,7 @@ default {
} else { } else {
picksData = llListReplaceList(picksData, [newEntry], picksIdx, picksIdx); picksData = llListReplaceList(picksData, [newEntry], picksIdx, picksIdx);
} }
llOwnerSay("🟢 " + playerName + " picks saved: " + picksStr); dbg("🟢 " + playerName + " picks saved: " + picksStr);
llSay(0, "🎯 " + playerName + " stakes their life on numbers: " + picksStr + " 🎲"); llSay(0, "🎯 " + playerName + " stakes their life on numbers: " + picksStr + " 🎲");
@ -797,19 +805,19 @@ default {
currentPickerIdx++; currentPickerIdx++;
if (currentPickerIdx < llGetListLength(pickQueue)) { if (currentPickerIdx < llGetListLength(pickQueue)) {
llOwnerSay("🔍 [Game Manager] More players in queue, showing next dialog"); dbg("🔍 [Game Manager] More players in queue, showing next dialog");
showNextPickerDialog(); showNextPickerDialog();
} else { } else {
// All picked, show roll dialog // All picked, show roll dialog
llOwnerSay("🎯 [Game Manager] All players have picked! Showing roll dialog to " + perilPlayer); dbg("🎯 [Game Manager] All players have picked! Showing roll dialog to " + perilPlayer);
// CRITICAL: Sync the updated picks data to all modules before roll phase // CRITICAL: Sync the updated picks data to all modules before roll phase
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase..."); dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain(); syncStateToMain();
llSleep(1.0); // Longer delay to ensure sync propagates to all modules llSleep(1.0); // Longer delay to ensure sync propagates to all modules
// Send roll dialog through Player_RegistrationManager // Send roll dialog through Player_RegistrationManager
llOwnerSay("🎯 [Game Manager] Sending roll dialog request for: " + perilPlayer); dbg("🎯 [Game Manager] Sending roll dialog request for: " + perilPlayer);
string rollRequest = "SHOW_ROLL_DIALOG|" + perilPlayer + "|" + perilPlayer; string rollRequest = "SHOW_ROLL_DIALOG|" + perilPlayer + "|" + perilPlayer;
llMessageLinked(LINK_SET, MSG_DIALOG_FORWARD_REQUEST, rollRequest, NULL_KEY); llMessageLinked(LINK_SET, MSG_DIALOG_FORWARD_REQUEST, rollRequest, NULL_KEY);
} }
@ -820,13 +828,13 @@ default {
// Handle bot picks // Handle bot picks
if (num == -9997 && llSubStringIndex(str, "BOT_PICKED:") == 0) { if (num == -9997 && llSubStringIndex(str, "BOT_PICKED:") == 0) {
llOwnerSay("🔍 [Game Manager] BOT_PICKED message received: " + str); dbg("🔍 [Game Manager] BOT_PICKED message received: " + str);
list parts = llParseString2List(str, [":"], []); list parts = llParseString2List(str, [":"], []);
if (llGetListLength(parts) >= 3) { if (llGetListLength(parts) >= 3) {
string playerName = llList2String(parts, 1); string playerName = llList2String(parts, 1);
string picksStr = llList2String(parts, 2); string picksStr = llList2String(parts, 2);
llOwnerSay("🤖 BOT: " + playerName + " picking " + picksStr + " (global had: " + llList2CSV(globalPickedNumbers) + ")"); dbg("🤖 BOT: " + playerName + " picking " + picksStr + " (global had: " + llList2CSV(globalPickedNumbers) + ")");
// Check if this bot has already made picks this round to prevent duplicates // Check if this bot has already made picks this round to prevent duplicates
integer existingPicksIdx = -1; integer existingPicksIdx = -1;
@ -835,7 +843,7 @@ default {
if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) { if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) {
string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1); string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1);
if (existingPicks != "") { if (existingPicks != "") {
llOwnerSay("⚠️ [Game Manager] Ignoring duplicate BOT_PICKED for " + playerName + " (already has picks: " + existingPicks + ")"); dbg("⚠️ [Game Manager] Ignoring duplicate BOT_PICKED for " + playerName + " (already has picks: " + existingPicks + ")");
// IMPORTANT: Even though this is a duplicate, check if all players have now picked // IMPORTANT: Even though this is a duplicate, check if all players have now picked
// Count how many players have picks // Count how many players have picks
@ -857,15 +865,15 @@ default {
// If all players have picks, advance to roll phase // If all players have picks, advance to roll phase
if (playersWithPicks >= llGetListLength(names)) { if (playersWithPicks >= llGetListLength(names)) {
llOwnerSay("🎯 [Game Manager] All players have picks! Moving to roll phase..."); dbg("🎯 [Game Manager] All players have picks! Moving to roll phase...");
// CRITICAL: Sync the updated picks data to all modules before roll phase // CRITICAL: Sync the updated picks data to all modules before roll phase
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase..."); dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain(); syncStateToMain();
llSleep(1.0); // Longer delay to ensure sync propagates to all modules llSleep(1.0); // Longer delay to ensure sync propagates to all modules
// Force close any active number picker dialogs // Force close any active number picker dialogs
llOwnerSay("🚫 [Game Manager] Sending CLOSE_ALL_DIALOGS command"); dbg("🚫 [Game Manager] Sending CLOSE_ALL_DIALOGS command");
llMessageLinked(LINK_SET, -9999, "CLOSE_ALL_DIALOGS", NULL_KEY); llMessageLinked(LINK_SET, -9999, "CLOSE_ALL_DIALOGS", NULL_KEY);
// Send roll dialog through Player_RegistrationManager // Send roll dialog through Player_RegistrationManager
@ -881,8 +889,8 @@ default {
// VALIDATE: Check for duplicate picks before saving // VALIDATE: Check for duplicate picks before saving
if (picksStr != "" && llListFindList(globalPickedNumbers, [picksStr]) != -1) { if (picksStr != "" && llListFindList(globalPickedNumbers, [picksStr]) != -1) {
llOwnerSay("❌ [Game Manager] Bot " + playerName + " tried to pick duplicate number: " + picksStr + " - REJECTING"); dbg("❌ [Game Manager] Bot " + playerName + " tried to pick duplicate number: " + picksStr + " - REJECTING");
llOwnerSay("🙄 [Game Manager] Sending new pick command to Bot Manager for " + playerName); dbg("🙄 [Game Manager] Sending new pick command to Bot Manager for " + playerName);
// Calculate picks needed for bot retry // Calculate picks needed for bot retry
integer perilIdx = llListFindList(names, [perilPlayer]); integer perilIdx = llListFindList(names, [perilPlayer]);
@ -896,14 +904,14 @@ default {
list completeAvoidList = buildCompleteAvoidanceList(); list completeAvoidList = buildCompleteAvoidanceList();
string avoidListStr = llList2CSV(completeAvoidList); string avoidListStr = llList2CSV(completeAvoidList);
string botCommand = "BOT_PICK:" + playerName + ":" + (string)picksNeeded + ":" + (string)diceType + ":" + avoidListStr; string botCommand = "BOT_PICK:" + playerName + ":" + (string)picksNeeded + ":" + (string)diceType + ":" + avoidListStr;
llOwnerSay("🎯 [Game Manager] Sending retry bot command with complete avoid list (" + (string)llGetListLength(completeAvoidList) + " numbers): " + avoidListStr); dbg("🎯 [Game Manager] Sending retry bot command with complete avoid list (" + (string)llGetListLength(completeAvoidList) + " numbers): " + avoidListStr);
llMessageLinked(LINK_SET, -9999, botCommand, NULL_KEY); llMessageLinked(LINK_SET, -9999, botCommand, NULL_KEY);
return; // Don't save this pick return; // Don't save this pick
} }
// Update picks data // Update picks data
integer idx = llListFindList(names, [playerName]); integer idx = llListFindList(names, [playerName]);
llOwnerSay("🔧 [Game Manager] Looking for bot " + playerName + " in names list: " + llList2CSV(names) + " (idx: " + (string)idx + ")"); dbg("🔧 [Game Manager] Looking for bot " + playerName + " in names list: " + llList2CSV(names) + " (idx: " + (string)idx + ")");
if (idx != -1) { if (idx != -1) {
integer picksIdx = -1; integer picksIdx = -1;
integer k; integer k;
@ -963,19 +971,19 @@ default {
} }
} }
llOwnerSay("🔍 [Game Manager] Bot pick complete - players with picks: " + (string)playersWithPicks + "/" + (string)llGetListLength(names)); dbg("🔍 [Game Manager] Bot pick complete - players with picks: " + (string)playersWithPicks + "/" + (string)llGetListLength(names));
// If all players have picks, advance to roll phase // If all players have picks, advance to roll phase
if (playersWithPicks >= llGetListLength(names)) { if (playersWithPicks >= llGetListLength(names)) {
llOwnerSay("🎯 [Game Manager] All players have picks! Moving to roll phase..."); dbg("🎯 [Game Manager] All players have picks! Moving to roll phase...");
// CRITICAL: Sync the updated picks data to all modules before roll phase // CRITICAL: Sync the updated picks data to all modules before roll phase
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase..."); dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain(); syncStateToMain();
llSleep(0.2); // Brief delay to ensure sync propagates llSleep(0.2); // Brief delay to ensure sync propagates
// Force close any active number picker dialogs // Force close any active number picker dialogs
llOwnerSay("🚫 [Game Manager] Sending CLOSE_ALL_DIALOGS command"); dbg("🚫 [Game Manager] Sending CLOSE_ALL_DIALOGS command");
llMessageLinked(LINK_SET, -9999, "CLOSE_ALL_DIALOGS", NULL_KEY); llMessageLinked(LINK_SET, -9999, "CLOSE_ALL_DIALOGS", NULL_KEY);
// Send roll dialog through Player_RegistrationManager // Send roll dialog through Player_RegistrationManager
@ -995,12 +1003,12 @@ default {
// Handle continue round requests from Main Controller // Handle continue round requests from Main Controller
if (num == MSG_CONTINUE_ROUND) { if (num == MSG_CONTINUE_ROUND) {
llOwnerSay("🎯 [Game Manager] Received continue round request with peril player: '" + str + "'"); dbg("🎯 [Game Manager] Received continue round request with peril player: '" + str + "'");
// Update peril player from the continue message if provided // Update peril player from the continue message if provided
if (str != "" && str != "NONE") { if (str != "" && str != "NONE") {
perilPlayer = str; perilPlayer = str;
llOwnerSay("🎯 [Game Manager] Updated peril player to: " + perilPlayer); dbg("🎯 [Game Manager] Updated peril player to: " + perilPlayer);
// Reset round state and continue with existing peril player // Reset round state and continue with existing peril player
roundStarted = FALSE; roundStarted = FALSE;
@ -1012,7 +1020,7 @@ default {
continueCurrentRound(); continueCurrentRound();
} else { } else {
// Empty peril player means this is initial game start - use startNextRound() // Empty peril player means this is initial game start - use startNextRound()
llOwnerSay("🎯 [Game Manager] Empty peril player - starting initial game round"); dbg("🎯 [Game Manager] Empty peril player - starting initial game round");
// Reset round state // Reset round state
roundStarted = FALSE; roundStarted = FALSE;
@ -1030,11 +1038,13 @@ default {
if (num == 9011 && llSubStringIndex(str, "VERBOSE_LOGGING|") == 0) { if (num == 9011 && llSubStringIndex(str, "VERBOSE_LOGGING|") == 0) {
list parts = llParseString2List(str, ["|"], []); list parts = llParseString2List(str, ["|"], []);
if (llGetListLength(parts) >= 2) { if (llGetListLength(parts) >= 2) {
#if DEBUG_LOGS
VERBOSE_LOGGING = (integer)llList2String(parts, 1); VERBOSE_LOGGING = (integer)llList2String(parts, 1);
#endif
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
llOwnerSay("🔍 [Game Manager] Verbose logging ON"); dbg("🔍 [Game Manager] Verbose logging ON");
} else { } else {
llOwnerSay("🔍 [Game Manager] Verbose logging OFF"); dbg("🔍 [Game Manager] Verbose logging OFF");
} }
} }
return; return;
@ -1051,13 +1061,13 @@ default {
diceTypeProcessed = FALSE; // Reset for new game diceTypeProcessed = FALSE; // Reset for new game
roundContinueInProgress = FALSE; // Reset protection flag roundContinueInProgress = FALSE; // Reset protection flag
lastSyncProcessTime = 0; // Reset sync timing lastSyncProcessTime = 0; // Reset sync timing
llOwnerSay("🎯 Game Manager reset - ready for new game!"); dbg("🎯 Game Manager reset - ready for new game!");
return; return;
} }
// Handle emergency state reset (for when game gets stuck) // Handle emergency state reset (for when game gets stuck)
if (num == -99998 && str == "EMERGENCY_RESET") { if (num == -99998 && str == "EMERGENCY_RESET") {
llOwnerSay("🚨 [Game Manager] Emergency reset triggered!"); dbg("🚨 [Game Manager] Emergency reset triggered!");
roundStarted = FALSE; roundStarted = FALSE;
perilPlayer = ""; perilPlayer = "";
currentPickerIdx = 0; currentPickerIdx = 0;
@ -1065,7 +1075,7 @@ default {
diceTypeProcessed = FALSE; diceTypeProcessed = FALSE;
roundContinueInProgress = FALSE; roundContinueInProgress = FALSE;
lastSyncProcessTime = 0; // Reset sync timing lastSyncProcessTime = 0; // Reset sync timing
llOwnerSay("🔒 [Game Manager] Emergency state reset complete"); dbg("🔒 [Game Manager] Emergency state reset complete");
return; return;
} }
} }

View file

@ -7,7 +7,15 @@
// ============================================================================= // =============================================================================
// Verbose logging control // Verbose logging control
integer VERBOSE_LOGGING = FALSE; // Global flag for verbose debug logs - DISABLED to save memory #define DEBUG_LOGS 0 // Set to 1 to enable, 0 to STRIP all logs from memory
#if DEBUG_LOGS
#define dbg(msg) llOwnerSay(msg)
integer VERBOSE_LOGGING = FALSE;
#else
#define dbg(msg)
#define VERBOSE_LOGGING 0
#endif
#define MSG_TOGGLE_VERBOSE_LOGS 9998 // Message to toggle verbose logging #define MSG_TOGGLE_VERBOSE_LOGS 9998 // Message to toggle verbose logging
// Message constants for link communication // Message constants for link communication
@ -919,7 +927,11 @@ updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
} }
default { default {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() { state_entry() {
llListen(1, "", llGetOwner(), ""); // Listen on channel 1 for local owner testing commands
reportMemoryUsage("Scoreboard Manager"); reportMemoryUsage("Scoreboard Manager");
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
llOwnerSay("📈 Scoreboard Manager ready! (Linkset Version)"); llOwnerSay("📈 Scoreboard Manager ready! (Linkset Version)");
@ -938,6 +950,8 @@ default {
// Load persistent leaderboard data from KVP Experience database // Load persistent leaderboard data from KVP Experience database
loadLeaderboardData(); loadLeaderboardData();
// Set a timer to passively auto-refresh the global scoreboard every 5 minutes
llSetTimerEvent(300.0);
// NOTE: generateLeaderboardText() will be triggered by dataserver automatically when data arrives // NOTE: generateLeaderboardText() will be triggered by dataserver automatically when data arrives
// No need to generate it randomly before data arrives // No need to generate it randomly before data arrives
@ -954,6 +968,7 @@ default {
link_message(integer sender, integer num, string str, key id) { link_message(integer sender, integer num, string str, key id) {
// Handle verbose logging toggle // Handle verbose logging toggle
#if DEBUG_LOGS
if (num == MSG_TOGGLE_VERBOSE_LOGS) { if (num == MSG_TOGGLE_VERBOSE_LOGS) {
VERBOSE_LOGGING = !VERBOSE_LOGGING; VERBOSE_LOGGING = !VERBOSE_LOGGING;
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
@ -963,6 +978,7 @@ default {
} }
return; return;
} }
#endif
// Only listen to messages from the main controller (link 1) // Only listen to messages from the main controller (link 1)
if (sender != 1) return; if (sender != 1) return;
@ -1124,7 +1140,7 @@ default {
string status = llGetSubString(data, 0, comma - 1); string status = llGetSubString(data, 0, comma - 1);
string val = llGetSubString(data, comma + 1, -1); string val = llGetSubString(data, comma + 1, -1);
if (status == "0") { if (status == "1") {
leaderboardNames = []; leaderboardNames = [];
leaderboardWins = []; leaderboardWins = [];
leaderboardLosses = []; leaderboardLosses = [];
@ -1165,7 +1181,7 @@ default {
if (status == "3") { if (status == "3") {
// Update failed because key didn't exist yet! Create it. // Update failed because key didn't exist yet! Create it.
llCreateKeyValue("Peril_LB_Top50", pendingSerialize); llCreateKeyValue("Peril_LB_Top50", pendingSerialize);
} else if (status == "0") { } else if (status == "1") {
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
llOwnerSay("✅ Global scoreboard synced successfully."); llOwnerSay("✅ Global scoreboard synced successfully.");
} }
@ -1183,4 +1199,32 @@ default {
llOwnerSay(" HTTP requests pending: " + (string)(llGetListLength(httpRequests) / 2)); llOwnerSay(" HTTP requests pending: " + (string)(llGetListLength(httpRequests) / 2));
} }
} }
timer() {
if (VERBOSE_LOGGING) {
llOwnerSay("⏱️ Auto-refreshing Global Scoreboard Data...");
}
loadLeaderboardData();
}
listen(integer channel, string name, key id, string message) {
string msg = llToLower(llStringTrim(message, STRING_TRIM));
if (msg == "testx") {
llOwnerSay("Testing X overlays on all 10 player slots...");
integer i;
for (i = 0; i < 10; i++) {
integer overlayIdx = getOverlayPrimLink(i);
if (overlayIdx >= FIRST_OVERLAY_PRIM && overlayIdx <= LAST_OVERLAY_PRIM) {
llSetLinkPrimitiveParamsFast(overlayIdx, [
PRIM_TEXTURE, ALL_SIDES, TEXTURE_ELIMINATED_X, <1,1,0>, <0,0,0>, 0.0,
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0
]);
}
}
}
else if (msg == "clearx") {
llOwnerSay("Refreshing display to clear test X overlays...");
refreshPlayerDisplay();
}
}
} }

View file

@ -115,6 +115,9 @@ distributeToBank(string text, list linkNumbers) {
} }
default { default {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() { state_entry() {
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
llOwnerSay("📋 Leaderboard Communication Script ready! (Linkset Version)"); llOwnerSay("📋 Leaderboard Communication Script ready! (Linkset Version)");

View file

@ -19,6 +19,13 @@ string getPlayerName(key id) {
// LINKSET COMMUNICATION CONSTANTS // LINKSET COMMUNICATION CONSTANTS
// ============================================================================= // =============================================================================
#define DEBUG_LOGS 0 // Set to 1 to enable logs, 0 to STRIP them from bytecode and recover memory
#if DEBUG_LOGS
#define dbg(msg) dbg(msg)
#else
#define dbg(msg)
#endif
// Target link numbers (UPDATED after overlay prim insertion) // Target link numbers (UPDATED after overlay prim insertion)
#define SCOREBOARD_LINK 12 // Scoreboard manager cube #define SCOREBOARD_LINK 12 // Scoreboard manager cube
#define LEADERBOARD_LINK 35 // Leaderboard manager (first XyzzyText prim) #define LEADERBOARD_LINK 35 // Leaderboard manager (first XyzzyText prim)
@ -343,7 +350,7 @@ resetGame() {
llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_WINNER, "", NULL_KEY); // Clear winner glow llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_WINNER, "", NULL_KEY); // Clear winner glow
llMessageLinked(DICE_LINK, MSG_CLEAR_DICE, "", NULL_KEY); llMessageLinked(DICE_LINK, MSG_CLEAR_DICE, "", NULL_KEY);
llOwnerSay("🎮 Game reset! All state cleared (including scoreboard)."); dbg("🎮 Game reset! All state cleared (including scoreboard).");
statusTimer = 0; statusTimer = 0;
lastStatus = ""; lastStatus = "";
@ -365,7 +372,7 @@ resetGame() {
default { default {
state_entry() { state_entry() {
reportMemoryUsage("Main Controller"); reportMemoryUsage("Main Controller");
llOwnerSay("🎮 Main Controller ready! (Linkset Version)"); dbg("🎮 Main Controller ready! (Linkset Version)");
// Initialize lockout system // Initialize lockout system
gameOwner = llGetOwner(); gameOwner = llGetOwner();
@ -377,12 +384,12 @@ default {
// Reset the game on startup to ensure clean state // Reset the game on startup to ensure clean state
resetGame(); resetGame();
llOwnerSay("✅ Main Controller initialization complete!"); dbg("✅ Main Controller initialization complete!");
} }
on_rez(integer start_param) { on_rez(integer start_param) {
reportMemoryUsage("Main Controller"); reportMemoryUsage("Main Controller");
llOwnerSay("🔄 Main Controller rezzed - resetting game state..."); dbg("🔄 Main Controller rezzed - resetting game state...");
// Re-initialize lockout system // Re-initialize lockout system
gameOwner = llGetOwner(); gameOwner = llGetOwner();
@ -394,7 +401,7 @@ default {
// Reset the game to ensure clean state when rezzed // Reset the game to ensure clean state when rezzed
resetGame(); resetGame();
llOwnerSay("✅ Main Controller reset complete after rez!"); dbg("✅ Main Controller reset complete after rez!");
} }
touch_start(integer total_number) { touch_start(integer total_number) {
@ -475,11 +482,11 @@ default {
} }
if (toucher == llGetOwner()) { if (toucher == llGetOwner()) {
llOwnerSay("🔍 [DEBUG] Owner touched - idx: " + (string)idx + ", players list: " + llList2CSV(players)); dbg("🔍 [DEBUG] Owner touched - idx: " + (string)idx + ", players list: " + llList2CSV(players));
if (idx == -1) { if (idx == -1) {
// Owner is not in game - show join/admin choice // Owner is not in game - show join/admin choice
if (llListFindList(pendingRegistrations, [toucher]) != -1) { if (llListFindList(pendingRegistrations, [toucher]) != -1) {
llOwnerSay("⏳ Registration already in progress for " + getPlayerName(toucher)); dbg("⏳ Registration already in progress for " + getPlayerName(toucher));
return; return;
} }
@ -521,7 +528,7 @@ default {
string playerName = getPlayerName(toucher); string playerName = getPlayerName(toucher);
if (playerName != "") { if (playerName != "") {
if (llListFindList(pendingRegistrations, [toucher]) != -1) { if (llListFindList(pendingRegistrations, [toucher]) != -1) {
llOwnerSay("⏳ Registration already in progress for " + playerName); dbg("⏳ Registration already in progress for " + playerName);
return; return;
} }
@ -538,7 +545,7 @@ default {
} }
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "player|" + (string)isStarter, toucher); llMessageLinked(LINK_SET, MSG_SHOW_MENU, "player|" + (string)isStarter, toucher);
} else { } else {
llOwnerSay("⚠️ Could not get name for toucher: " + (string)toucher); dbg("⚠️ Could not get name for toucher: " + (string)toucher);
} }
} }
} }
@ -567,8 +574,8 @@ default {
integer existingPlayerIdx = llListFindList(players, [id]); integer existingPlayerIdx = llListFindList(players, [id]);
if (existingPlayerIdx != -1) { if (existingPlayerIdx != -1) {
llOwnerSay("⚠️ [Main Controller] DUPLICATE PLAYER KEY DETECTED - Player: " + newName + " (" + (string)id + ") already exists!"); dbg("⚠️ [Main Controller] DUPLICATE PLAYER KEY DETECTED - Player: " + newName + " (" + (string)id + ") already exists!");
llOwnerSay("⚠️ [Main Controller] Players list length: " + (string)llGetListLength(players) + ", Names list length: " + (string)llGetListLength(names)); dbg("⚠️ [Main Controller] Players list length: " + (string)llGetListLength(players) + ", Names list length: " + (string)llGetListLength(names));
return; // Skip adding duplicate return; // Skip adding duplicate
} }
@ -585,8 +592,8 @@ default {
} }
// That's it! Player_RegistrationManager already sent sync and scoreboard updates // That's it! Player_RegistrationManager already sent sync and scoreboard updates
llOwnerSay("✅ Player added to master lists: " + newName + " (" + (string)llGetFreeMemory() + " bytes free)"); dbg("✅ Player added to master lists: " + newName + " (" + (string)llGetFreeMemory() + " bytes free)");
llOwnerSay("🔍 [DEBUG] Updated lists - Players: " + (string)llGetListLength(players) + ", Names: " + (string)llGetListLength(names)); dbg("🔍 [DEBUG] Updated lists - Players: " + (string)llGetListLength(players) + ", Names: " + (string)llGetListLength(names));
} }
return; return;
} }
@ -625,7 +632,7 @@ default {
// SEND WINNER GLOW to scoreboard and trigger floater update // SEND WINNER GLOW to scoreboard and trigger floater update
llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_WINNER, potentialWinner, NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_WINNER, potentialWinner, NULL_KEY);
llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, potentialWinner, NULL_KEY); // Update winner's floater llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, potentialWinner, NULL_KEY); // Update winner's floater
llOwnerSay("🏆 [Main Controller] Pre-victory winner glow sent for: " + potentialWinner); dbg("🏆 [Main Controller] Pre-victory winner glow sent for: " + potentialWinner);
} }
} }
@ -718,7 +725,7 @@ default {
// ENHANCED: Give eliminated player's floater time to show eliminated status // ENHANCED: Give eliminated player's floater time to show eliminated status
// before declaring victory and cleaning up // before declaring victory and cleaning up
llOwnerSay("⏳ [Main Controller] Final elimination detected - allowing display time before victory"); dbg("⏳ [Main Controller] Final elimination detected - allowing display time before victory");
llSleep(2.0); // Give final eliminated player time to show red eliminated status llSleep(2.0); // Give final eliminated player time to show red eliminated status
// CRITICAL: Set victory flag immediately to prevent sync processing // CRITICAL: Set victory flag immediately to prevent sync processing
@ -750,7 +757,7 @@ default {
llMessageLinked(SCOREBOARD_LINK, MSG_GAME_WON, winner, NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_GAME_WON, winner, NULL_KEY);
// Use timer instead of llSleep to avoid immediate reset // Use timer instead of llSleep to avoid immediate reset
llOwnerSay("🏆 [Main Controller] Winner glow applied - starting victory delay timer"); dbg("🏆 [Main Controller] Winner glow applied - starting victory delay timer");
victoryDelayTimer = llGetUnixTime(); victoryDelayTimer = llGetUnixTime();
currentTimerMode = TIMER_VICTORY_DELAY; currentTimerMode = TIMER_VICTORY_DELAY;
llSetTimerEvent(1.0); // Check every second llSetTimerEvent(1.0); // Check every second
@ -800,7 +807,7 @@ default {
// Handle emergency reset for stuck games // Handle emergency reset for stuck games
if (num == -99998 && str == "EMERGENCY_RESET") { if (num == -99998 && str == "EMERGENCY_RESET") {
llOwnerSay("🚨 [Main Controller] Emergency reset triggered - sending emergency reset to all scripts"); dbg("🚨 [Main Controller] Emergency reset triggered - sending emergency reset to all scripts");
// Signal all scripts to emergency reset // Signal all scripts to emergency reset
llMessageLinked(LINK_SET, -99998, "EMERGENCY_RESET", NULL_KEY); llMessageLinked(LINK_SET, -99998, "EMERGENCY_RESET", NULL_KEY);
llSleep(0.5); llSleep(0.5);
@ -857,7 +864,7 @@ default {
integer idx = llListFindList(names, [playerName]); integer idx = llListFindList(names, [playerName]);
if (idx != -1) { if (idx != -1) {
if (llSubStringIndex(playerName, "Bot") == 0) { if (llSubStringIndex(playerName, "Bot") == 0) {
llOwnerSay("🤖 Bots are always ready and cannot change state"); dbg("🤖 Bots are always ready and cannot change state");
return; return;
} }
@ -971,12 +978,12 @@ default {
} }
if (newPerilPlayer != "") { if (newPerilPlayer != "") {
llOwnerSay("🎯 [Main Controller] Peril player left - assigning new peril player: " + newPerilPlayer); dbg("🎯 [Main Controller] Peril player left - assigning new peril player: " + newPerilPlayer);
perilPlayer = newPerilPlayer; perilPlayer = newPerilPlayer;
// Send peril player update to scoreboard for glow effect // Send peril player update to scoreboard for glow effect
llMessageLinked(SCOREBOARD_LINK, 3005, newPerilPlayer, NULL_KEY); // MSG_UPDATE_PERIL_PLAYER llMessageLinked(SCOREBOARD_LINK, 3005, newPerilPlayer, NULL_KEY); // MSG_UPDATE_PERIL_PLAYER
} else { } else {
llOwnerSay("⚠️ [Main Controller] No valid peril player candidates found after player left!"); dbg("⚠️ [Main Controller] No valid peril player candidates found after player left!");
perilPlayer = "NONE"; perilPlayer = "NONE";
// Clear peril player glow on scoreboard // Clear peril player glow on scoreboard
llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_PERIL_PLAYER, "", NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_PERIL_PLAYER, "", NULL_KEY);
@ -1006,7 +1013,7 @@ default {
// Check if we should reset the game (no players left) // Check if we should reset the game (no players left)
if (llGetListLength(names) == 0) { if (llGetListLength(names) == 0) {
llOwnerSay("🔄 [Main Controller] All players left - resetting game"); dbg("🔄 [Main Controller] All players left - resetting game");
// Force clear any remaining game state before reset // Force clear any remaining game state before reset
roundStarted = FALSE; roundStarted = FALSE;
gameStarting = FALSE; gameStarting = FALSE;
@ -1018,7 +1025,7 @@ default {
// Skip updateHelpers to prevent memory crashes during player removal // Skip updateHelpers to prevent memory crashes during player removal
// Scoreboard was already updated with MSG_REMOVE_PLAYER above // Scoreboard was already updated with MSG_REMOVE_PLAYER above
llOwnerSay("✅ [Main Controller] Synchronized removal of " + leavingName); dbg("✅ [Main Controller] Synchronized removal of " + leavingName);
} }
} }
return; return;
@ -1029,7 +1036,7 @@ default {
if (id == gameOwner && str == "LOCK_GAME") { if (id == gameOwner && str == "LOCK_GAME") {
isLocked = TRUE; isLocked = TRUE;
updateFloatingText(); updateFloatingText();
llOwnerSay("🔒 [Main Controller] Game has been LOCKED - Floating text updated"); dbg("🔒 [Main Controller] Game has been LOCKED - Floating text updated");
} }
return; return;
} }
@ -1038,7 +1045,7 @@ default {
if (id == gameOwner && str == "UNLOCK_GAME") { if (id == gameOwner && str == "UNLOCK_GAME") {
isLocked = FALSE; isLocked = FALSE;
updateFloatingText(); updateFloatingText();
llOwnerSay("🔓 [Main Controller] Game has been UNLOCKED - Floating text updated"); dbg("🔓 [Main Controller] Game has been UNLOCKED - Floating text updated");
} }
return; return;
} }
@ -1059,13 +1066,13 @@ default {
// Handle memory monitor messages // Handle memory monitor messages
if (num == MSG_EMERGENCY_CLEANUP) { if (num == MSG_EMERGENCY_CLEANUP) {
// Removed emergencyMemoryCleanup call to save memory // Removed emergencyMemoryCleanup call to save memory
llOwnerSay("Emergency cleanup disabled to prevent crashes"); dbg("Emergency cleanup disabled to prevent crashes");
return; return;
} }
// Handle reset requests from other scripts (like Game_Manager) // Handle reset requests from other scripts (like Game_Manager)
if (num == -99998 && str == "REQUEST_GAME_RESET") { if (num == -99998 && str == "REQUEST_GAME_RESET") {
llOwnerSay("🔄 [Main Controller] Reset requested by " + (string)sender + ", executing..."); dbg("🔄 [Main Controller] Reset requested by " + (string)sender + ", executing...");
resetGame(); resetGame();
return; return;
} }
@ -1076,7 +1083,7 @@ default {
if (channel == rollDialogChannel) { if (channel == rollDialogChannel) {
if (msg == "Start Next Round" || msg == "BEGIN KILLING GAME") { if (msg == "Start Next Round" || msg == "BEGIN KILLING GAME") {
if (roundStarted) { if (roundStarted) {
llOwnerSay("⚠️ Round already in progress, ignoring duplicate round start from roll dialog"); dbg("⚠️ Round already in progress, ignoring duplicate round start from roll dialog");
return; return;
} }
@ -1124,23 +1131,23 @@ default {
if (msg == "Reset Leaderboard") { if (msg == "Reset Leaderboard") {
// CHANGED: Send to scoreboard, which will handle leaderboard reset // CHANGED: Send to scoreboard, which will handle leaderboard reset
llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY);
llOwnerSay("🏆 Leaderboard scores reset - game wins cleared!"); dbg("🏆 Leaderboard scores reset - game wins cleared!");
return; return;
} }
if (msg == "Reset All") { if (msg == "Reset All") {
resetGame(); resetGame();
llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY);
llOwnerSay("🔄 Complete reset - game and leaderboard cleared!"); dbg("🔄 Complete reset - game and leaderboard cleared!");
return; return;
} }
if (msg == "Memory Stats") { if (msg == "Memory Stats") {
// Removed reportMemoryStats call to save memory // Removed reportMemoryStats call to save memory
llOwnerSay("Memory stats reporting disabled to prevent crashes"); dbg("Memory stats reporting disabled to prevent crashes");
return; return;
} }
if (msg == "Add Test Player") { if (msg == "Add Test Player") {
if (llGetListLength(players) >= MAX_PLAYERS) { if (llGetListLength(players) >= MAX_PLAYERS) {
llOwnerSay("⚠️ Cannot add test player; the game is full (max " + (string)MAX_PLAYERS + ")."); dbg("⚠️ Cannot add test player; the game is full (max " + (string)MAX_PLAYERS + ").");
return; return;
} }
@ -1161,11 +1168,11 @@ default {
// Don't show menu immediately - let the player touch the board again // Don't show menu immediately - let the player touch the board again
// This avoids timing issues where the menu is shown before registration completes // This avoids timing issues where the menu is shown before registration completes
llOwnerSay("✅ " + testName + " registration sent - touch the board again to refresh menu"); dbg("✅ " + testName + " registration sent - touch the board again to refresh menu");
return; return;
} }
if (msg == "Force Floaters") { if (msg == "Force Floaters") {
llOwnerSay("🔧 Forcing floater creation for all " + (string)llGetListLength(names) + " players..."); dbg("🔧 Forcing floater creation for all " + (string)llGetListLength(names) + " players...");
integer i; integer i;
for (i = 0; i < llGetListLength(names); i++) { for (i = 0; i < llGetListLength(names); i++) {
string playerName = llList2String(names, i); string playerName = llList2String(names, i);
@ -1173,7 +1180,7 @@ default {
llMessageLinked(LINK_SET, MSG_REZ_FLOAT, playerName, playerKey); llMessageLinked(LINK_SET, MSG_REZ_FLOAT, playerName, playerKey);
llSleep(0.3); llSleep(0.3);
} }
llOwnerSay("✅ Floater creation requests sent for all players!"); dbg("✅ Floater creation requests sent for all players!");
// After forcing floaters, show appropriate menu based on owner registration status // After forcing floaters, show appropriate menu based on owner registration status
integer ownerIdx = llListFindList(players, [llGetOwner()]); integer ownerIdx = llListFindList(players, [llGetOwner()]);
@ -1205,7 +1212,7 @@ default {
integer playerCount = llGetListLength(players); integer playerCount = llGetListLength(players);
integer nameCount = llGetListLength(names); integer nameCount = llGetListLength(names);
if (playerCount < 2) { if (playerCount < 2) {
llOwnerSay("⚠️ Need at least 2 players to start the game."); dbg("⚠️ Need at least 2 players to start the game.");
return; return;
} }
@ -1220,7 +1227,7 @@ default {
} }
if (starterName == "") { if (starterName == "") {
llOwnerSay("⚠️ Only the game starter can start the game."); dbg("⚠️ Only the game starter can start the game.");
return; return;
} }
@ -1236,7 +1243,7 @@ default {
} }
if (llGetListLength(notReadyPlayers) > 0) { if (llGetListLength(notReadyPlayers) > 0) {
llOwnerSay("⚠️ Cannot start game. These players are not ready: " + llList2CSV(notReadyPlayers)); dbg("⚠️ Cannot start game. These players are not ready: " + llList2CSV(notReadyPlayers));
return; return;
} }
@ -1269,7 +1276,7 @@ default {
lastStatus = ""; lastStatus = "";
// CHANGED: Use link message instead of llRegionSay // CHANGED: Use link message instead of llRegionSay
llMessageLinked(SCOREBOARD_LINK, MSG_GAME_STATUS, "Title", NULL_KEY); llMessageLinked(SCOREBOARD_LINK, MSG_GAME_STATUS, "Title", NULL_KEY);
llOwnerSay("📢 Status cleared - reverted to Title"); dbg("📢 Status cleared - reverted to Title");
currentTimerMode = TIMER_IDLE; currentTimerMode = TIMER_IDLE;
llSetTimerEvent(0); llSetTimerEvent(0);
} }
@ -1278,7 +1285,7 @@ default {
if (victoryDelayTimer > 0) { if (victoryDelayTimer > 0) {
integer elapsed = llGetUnixTime() - victoryDelayTimer; integer elapsed = llGetUnixTime() - victoryDelayTimer;
if (elapsed >= (integer)(STATUS_DISPLAY_TIME * 3.0)) { // 24 seconds if (elapsed >= (integer)(STATUS_DISPLAY_TIME * 3.0)) { // 24 seconds
llOwnerSay("🏆 [Main Controller] Victory display time complete - executing game reset"); dbg("🏆 [Main Controller] Victory display time complete - executing game reset");
victoryDelayTimer = 0; victoryDelayTimer = 0;
currentTimerMode = TIMER_IDLE; currentTimerMode = TIMER_IDLE;
llSetTimerEvent(0); llSetTimerEvent(0);

View file

@ -1,3 +1,10 @@
#define DEBUG_LOGS 0 // Set to 1 to enable logs, 0 to STRIP from memory completely
#if DEBUG_LOGS
#define dbg(msg) dbg(msg)
#else
#define dbg(msg)
#endif
// === Dialog Handler (Owner & Player) with unified Ready/Leave menu and join support === // === Dialog Handler (Owner & Player) with unified Ready/Leave menu and join support ===
// Helper function to get display name with fallback to username // Helper function to get display name with fallback to username
@ -51,11 +58,11 @@ initializeChannels() {
DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range
// Report channel to owner for debugging // Report channel to owner for debugging
llOwnerSay("🔧 [Owner/Player Dialog] Dynamic channels initialized:"); dbg("🔧 [Owner/Player Dialog] Dynamic channels initialized:");
llOwnerSay(" Main Dialog: " + (string)MAIN_DIALOG_CHANNEL); dbg(" Main Dialog: " + (string)MAIN_DIALOG_CHANNEL);
llOwnerSay(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL); dbg(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
llOwnerSay(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL); dbg(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
llOwnerSay(" Dice: " + (string)DICE_DATA_CHANNEL); dbg(" Dice: " + (string)DICE_DATA_CHANNEL);
} }
integer DIALOG_CHANNEL; // Legacy variable, will be set dynamically integer DIALOG_CHANNEL; // Legacy variable, will be set dynamically
@ -165,7 +172,7 @@ showTroubleshootingMenu(key id) {
showReadyLeaveMenu(key id, integer isStarter, integer isOwner) { showReadyLeaveMenu(key id, integer isStarter, integer isOwner) {
// Check for timeout of previous request // Check for timeout of previous request
if (pendingMenuPlayer != NULL_KEY && (llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) { if (pendingMenuPlayer != NULL_KEY && (llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) {
llOwnerSay("⏰ Previous menu request timed out, clearing pending state"); dbg("⏰ Previous menu request timed out, clearing pending state");
pendingMenuPlayer = NULL_KEY; pendingMenuPlayer = NULL_KEY;
} }
@ -176,7 +183,7 @@ showReadyLeaveMenu(key id, integer isStarter, integer isOwner) {
// If there's a pending request for a different player, warn about it but proceed // If there's a pending request for a different player, warn about it but proceed
if (pendingMenuPlayer != NULL_KEY && pendingMenuPlayer != id) { if (pendingMenuPlayer != NULL_KEY && pendingMenuPlayer != id) {
llOwnerSay("⚠️ [Debug] Overriding pending request for " + (string)pendingMenuPlayer + " with new request for " + (string)id); dbg("⚠️ [Debug] Overriding pending request for " + (string)pendingMenuPlayer + " with new request for " + (string)id);
} }
pendingMenuPlayer = id; pendingMenuPlayer = id;
@ -291,7 +298,7 @@ showKickPlayerMenu(key id, list playerNames) {
string buttonLabel = "👢 " + kickDisplayName; string buttonLabel = "👢 " + kickDisplayName;
// Debug: Check button length // Debug: Check button length
llOwnerSay("🔍 Debug kick button: '" + buttonLabel + "' = " + (string)llStringLength(buttonLabel) + " chars"); dbg("🔍 Debug kick button: '" + buttonLabel + "' = " + (string)llStringLength(buttonLabel) + " chars");
kickOptions += [buttonLabel]; kickOptions += [buttonLabel];
kickDisplayNames += [buttonLabel]; // Store the full button label kickDisplayNames += [buttonLabel]; // Store the full button label
@ -363,14 +370,14 @@ default {
leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", ""); leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
diceHandle = llListen(DICE_DATA_CHANNEL, "", "", ""); diceHandle = llListen(DICE_DATA_CHANNEL, "", "", "");
llOwnerSay("🎭 Owner and Player Dialog Handler ready with lockout system!"); dbg("🎭 Owner and Player Dialog Handler ready with lockout system!");
llOwnerSay("🔓 Game is UNLOCKED - All players can access menus"); dbg("🔓 Game is UNLOCKED - All players can access menus");
} }
on_rez(integer start_param) { on_rez(integer start_param) {
reportMemoryUsage("🎭 Player Dialog"); reportMemoryUsage("🎭 Player Dialog");
llOwnerSay("🔄 Player Dialog Handler rezzed - reinitializing..."); dbg("🔄 Player Dialog Handler rezzed - reinitializing...");
// Re-initialize dynamic channels // Re-initialize dynamic channels
initializeChannels(); initializeChannels();
@ -408,7 +415,7 @@ default {
leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", ""); leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
diceHandle = llListen(DICE_DATA_CHANNEL, "", "", ""); diceHandle = llListen(DICE_DATA_CHANNEL, "", "", "");
llOwnerSay("✅ Player Dialog Handler reset complete after rez!"); dbg("✅ Player Dialog Handler reset complete after rez!");
} }
link_message(integer sender, integer num, string str, key id) { link_message(integer sender, integer num, string str, key id) {
@ -424,14 +431,14 @@ default {
currentPickList = []; currentPickList = [];
currentPickTarget = ""; currentPickTarget = "";
currentPickLimit = 3; currentPickLimit = 3;
llOwnerSay("🎭 Owner and Player Dialog Handler reset!"); dbg("🎭 Owner and Player Dialog Handler reset!");
return; return;
} }
if (num == MSG_SHOW_MENU) { if (num == MSG_SHOW_MENU) {
list args = llParseString2List(str, ["|"], []); list args = llParseString2List(str, ["|"], []);
if (llGetListLength(args) < 2) { if (llGetListLength(args) < 2) {
llOwnerSay("⚠️ Invalid message format: " + str); dbg("⚠️ Invalid message format: " + str);
return; return;
} }
string targetType = llList2String(args, 0); string targetType = llList2String(args, 0);
@ -468,7 +475,7 @@ default {
if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) { if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) {
// Check for timeout // Check for timeout
if ((llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) { if ((llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) {
llOwnerSay("⏰ Menu request timed out, ignoring response"); dbg("⏰ Menu request timed out, ignoring response");
pendingMenuPlayer = NULL_KEY; pendingMenuPlayer = NULL_KEY;
return; return;
} }
@ -479,20 +486,20 @@ default {
pendingMenuRequestID = 0; pendingMenuRequestID = 0;
} }
} else { } else {
llOwnerSay("⚠️ Invalid ready state result format - expected 4 parts, got " + (string)llGetListLength(parts)); dbg("⚠️ Invalid ready state result format - expected 4 parts, got " + (string)llGetListLength(parts));
} }
return; return;
} }
else if (num == MSG_PLAYER_LIST_RESULT) { else if (num == MSG_PLAYER_LIST_RESULT) {
list playerNames = llParseString2List(str, [","], []); list playerNames = llParseString2List(str, [","], []);
llOwnerSay("📋 Fetching list of players for pick management..."); dbg("📋 Fetching list of players for pick management...");
showPickManageMenu(id, playerNames); showPickManageMenu(id, playerNames);
} }
// Handle player list result for kick functionality // Handle player list result for kick functionality
else if (num == 8009) { else if (num == 8009) {
llOwnerSay("🔍 [Dialog Handler] Received kick player list string: '" + str + "'"); dbg("🔍 [Dialog Handler] Received kick player list string: '" + str + "'");
list rawNames = llParseString2List(str, [","], []); list rawNames = llParseString2List(str, [","], []);
llOwnerSay("🔍 [Dialog Handler] Raw parsed into " + (string)llGetListLength(rawNames) + " names: " + llList2CSV(rawNames)); dbg("🔍 [Dialog Handler] Raw parsed into " + (string)llGetListLength(rawNames) + " names: " + llList2CSV(rawNames));
// Filter out any command strings that might have gotten mixed in // Filter out any command strings that might have gotten mixed in
list playerNames = []; list playerNames = [];
@ -506,11 +513,11 @@ default {
llSubStringIndex(name, "MSG_") != 0) { llSubStringIndex(name, "MSG_") != 0) {
playerNames += [name]; playerNames += [name];
} else { } else {
llOwnerSay("⚠️ [Dialog Handler] Filtered out invalid name: '" + name + "'"); dbg("⚠️ [Dialog Handler] Filtered out invalid name: '" + name + "'");
} }
} }
llOwnerSay("🔍 [Dialog Handler] Filtered to " + (string)llGetListLength(playerNames) + " valid names: " + llList2CSV(playerNames)); dbg("🔍 [Dialog Handler] Filtered to " + (string)llGetListLength(playerNames) + " valid names: " + llList2CSV(playerNames));
showKickPlayerMenu(id, playerNames); showKickPlayerMenu(id, playerNames);
} }
else if (num == MSG_PICK_LIST_RESULT) { else if (num == MSG_PICK_LIST_RESULT) {
@ -518,11 +525,11 @@ default {
string returnedName = llList2String(parts, 0); string returnedName = llList2String(parts, 0);
string picks = llList2String(parts, 1); string picks = llList2String(parts, 1);
if (llStringTrim(returnedName, STRING_TRIM) == llStringTrim(currentPickTarget, STRING_TRIM)) { if (llStringTrim(returnedName, STRING_TRIM) == llStringTrim(currentPickTarget, STRING_TRIM)) {
llOwnerSay("✅ Matched pick list for: " + returnedName); dbg("✅ Matched pick list for: " + returnedName);
currentPickList = llCSV2List(picks); currentPickList = llCSV2List(picks);
llMessageLinked(LINK_THIS, MSG_LIFE_LOOKUP, currentPickTarget, id); llMessageLinked(LINK_THIS, MSG_LIFE_LOOKUP, currentPickTarget, id);
} else { } else {
llOwnerSay("❌ Name mismatch: got " + returnedName + " but expected " + currentPickTarget); dbg("❌ Name mismatch: got " + returnedName + " but expected " + currentPickTarget);
} }
} }
else if (num == MSG_LIFE_LOOKUP) { else if (num == MSG_LIFE_LOOKUP) {
@ -546,7 +553,7 @@ default {
if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) { if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) {
// Check for timeout // Check for timeout
if ((llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) { if ((llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) {
llOwnerSay("⏰ Owner status query timed out, ignoring response"); dbg("⏰ Owner status query timed out, ignoring response");
pendingMenuPlayer = NULL_KEY; pendingMenuPlayer = NULL_KEY;
return; return;
} }
@ -554,13 +561,13 @@ default {
// Decide which menu to show based on registration status // Decide which menu to show based on registration status
if (isRegistered) { if (isRegistered) {
// Owner is registered - show ready/leave menu with Owner button // Owner is registered - show ready/leave menu with Owner button
llOwnerSay("🔍 [Debug] Owner is registered, isStarter=" + (string)isStarter + ", showing ready/leave menu"); dbg("🔍 [Debug] Owner is registered, isStarter=" + (string)isStarter + ", showing ready/leave menu");
llOwnerSay("🔍 [Debug] About to call showReadyLeaveMenu with id=" + (string)id + ", isStarter=" + (string)isStarter + ", isOwner=TRUE"); dbg("🔍 [Debug] About to call showReadyLeaveMenu with id=" + (string)id + ", isStarter=" + (string)isStarter + ", isOwner=TRUE");
// DON'T reset pending state here - showReadyLeaveMenu needs it! // DON'T reset pending state here - showReadyLeaveMenu needs it!
// The ready state handler will reset it when the dialog is shown // The ready state handler will reset it when the dialog is shown
showReadyLeaveMenu(id, isStarter, TRUE); showReadyLeaveMenu(id, isStarter, TRUE);
llOwnerSay("🔍 [Debug] showReadyLeaveMenu call completed"); dbg("🔍 [Debug] showReadyLeaveMenu call completed");
} else if (isPending) { } else if (isPending) {
llRegionSayTo(id, 0, "⏳ Registration already in progress for " + ownerName); llRegionSayTo(id, 0, "⏳ Registration already in progress for " + ownerName);
// Reset pending state for non-registered responses // Reset pending state for non-registered responses
@ -576,10 +583,10 @@ default {
pendingMenuRequestID = 0; pendingMenuRequestID = 0;
} }
} else { } else {
llOwnerSay("⚠️ Owner status response mismatch - ignoring"); dbg("⚠️ Owner status response mismatch - ignoring");
} }
} else { } else {
llOwnerSay("⚠️ Invalid owner status result format - expected 5 parts, got " + (string)llGetListLength(parts)); dbg("⚠️ Invalid owner status result format - expected 5 parts, got " + (string)llGetListLength(parts));
} }
return; return;
} }
@ -603,7 +610,7 @@ default {
isLocked = TRUE; isLocked = TRUE;
// Notify Main Controller to update floating text // Notify Main Controller to update floating text
llMessageLinked(LINK_SET, 9001, "LOCK_GAME", id); llMessageLinked(LINK_SET, 9001, "LOCK_GAME", id);
llOwnerSay("🔒 Game has been LOCKED - Only owner can access any features"); dbg("🔒 Game has been LOCKED - Only owner can access any features");
llSay(0, "🔒 Game has been locked by the owner. Only the owner can access any features."); llSay(0, "🔒 Game has been locked by the owner. Only the owner can access any features.");
showOwnerMenu(id); // Refresh the owner menu showOwnerMenu(id); // Refresh the owner menu
} else { } else {
@ -615,7 +622,7 @@ default {
isLocked = FALSE; isLocked = FALSE;
// Notify Main Controller to update floating text // Notify Main Controller to update floating text
llMessageLinked(LINK_SET, 9002, "UNLOCK_GAME", id); llMessageLinked(LINK_SET, 9002, "UNLOCK_GAME", id);
llOwnerSay("🔓 Game has been UNLOCKED - All players can access menus"); dbg("🔓 Game has been UNLOCKED - All players can access menus");
llSay(0, "🔓 Game has been unlocked by the owner. All features are now available."); llSay(0, "🔓 Game has been unlocked by the owner. All features are now available.");
showOwnerMenu(id); // Refresh the owner menu showOwnerMenu(id); // Refresh the owner menu
} else { } else {
@ -676,25 +683,25 @@ default {
} }
else if (msg == "Cleanup Floaters") { else if (msg == "Cleanup Floaters") {
// Universal floater cleanup that works even after script resets // Universal floater cleanup that works even after script resets
llOwnerSay("🧩 Force cleaning ALL possible floater channels..."); dbg("🧩 Force cleaning ALL possible floater channels...");
llMessageLinked(LINK_SET, MSG_CLEANUP_ALL_FLOATERS, "", NULL_KEY); llMessageLinked(LINK_SET, MSG_CLEANUP_ALL_FLOATERS, "", NULL_KEY);
} }
else if (msg == "Force Floaters") { else if (msg == "Force Floaters") {
// Force creation of floaters for all registered players - send as dialog response to Main Controller // Force creation of floaters for all registered players - send as dialog response to Main Controller
llOwnerSay("🔧 Requesting floater creation for all registered players..."); dbg("🔧 Requesting floater creation for all registered players...");
// This will be handled by Main Controller's dialog listener // This will be handled by Main Controller's dialog listener
// No need to forward via link_message - the Main Controller is already listening on DIALOG_CHANNEL // No need to forward via link_message - the Main Controller is already listening on DIALOG_CHANNEL
} }
else if (msg == "🔍 Toggle Verbose Logs") { else if (msg == "🔍 Toggle Verbose Logs") {
// Toggle verbose logging system-wide // Toggle verbose logging system-wide
llOwnerSay("🔍 Toggling verbose logging system-wide..."); dbg("🔍 Toggling verbose logging system-wide...");
llMessageLinked(LINK_SET, 9010, "TOGGLE_VERBOSE_LOGS", id); llMessageLinked(LINK_SET, 9010, "TOGGLE_VERBOSE_LOGS", id);
// Return to troubleshooting menu after toggling // Return to troubleshooting menu after toggling
showTroubleshootingMenu(id); showTroubleshootingMenu(id);
} }
else if (msg == "🔄 Check for Updates") { else if (msg == "🔄 Check for Updates") {
// Trigger update check via Update_Receiver script // Trigger update check via Update_Receiver script
llOwnerSay("🔄 Checking GitHub for Peril Dice updates..."); dbg("🔄 Checking GitHub for Peril Dice updates...");
llMessageLinked(LINK_SET, 2100, "CHECK_UPDATES_REQUEST", id); llMessageLinked(LINK_SET, 2100, "CHECK_UPDATES_REQUEST", id);
// Return to troubleshooting menu // Return to troubleshooting menu
showTroubleshootingMenu(id); showTroubleshootingMenu(id);
@ -704,7 +711,7 @@ default {
} }
else if (llSubStringIndex(msg, "🛠 ") == 0) { else if (llSubStringIndex(msg, "🛠 ") == 0) {
currentPickTarget = llStringTrim(llDeleteSubString(msg, 0, 1), STRING_TRIM); currentPickTarget = llStringTrim(llDeleteSubString(msg, 0, 1), STRING_TRIM);
llOwnerSay("🎯 Pick target set to: " + currentPickTarget); dbg("🎯 Pick target set to: " + currentPickTarget);
currentPickList = []; currentPickList = [];
llMessageLinked(LINK_THIS, 206, currentPickTarget, id); llMessageLinked(LINK_THIS, 206, currentPickTarget, id);
} }
@ -731,7 +738,7 @@ default {
// Send kick message to Controller_MessageHandler (not sync channel 107!) // Send kick message to Controller_MessageHandler (not sync channel 107!)
llMessageLinked(LINK_SET, 8007, "KICK_PLAYER|" + playerToKick + "|" + (string)id, NULL_KEY); llMessageLinked(LINK_SET, 8007, "KICK_PLAYER|" + playerToKick + "|" + (string)id, NULL_KEY);
} else { } else {
llOwnerSay("⚠️ Could not find original name for kick selection: " + msg); dbg("⚠️ Could not find original name for kick selection: " + msg);
} }
// Return to player management menu // Return to player management menu
showPlayerManagementMenu(id); showPlayerManagementMenu(id);
@ -739,19 +746,19 @@ default {
// Forward numeric picks to the game logic // Forward numeric picks to the game logic
else if ((integer)msg > 0) { else if ((integer)msg > 0) {
if (llGetListLength(currentPickList) >= currentPickLimit) { if (llGetListLength(currentPickList) >= currentPickLimit) {
llOwnerSay("⚠️ Reached pick limit of " + (string)currentPickLimit + " for " + currentPickTarget); dbg("⚠️ Reached pick limit of " + (string)currentPickLimit + " for " + currentPickTarget);
showPickListMenu(id); showPickListMenu(id);
return; return;
} }
if (llListFindList(currentPickList, [msg]) != -1) { if (llListFindList(currentPickList, [msg]) != -1) {
llOwnerSay("⚠️ Pick already exists: " + msg); dbg("⚠️ Pick already exists: " + msg);
showPickListMenu(id); showPickListMenu(id);
return; return;
} }
integer i; integer i;
for (i = 0; i < llGetListLength(currentPickList); i++) { for (i = 0; i < llGetListLength(currentPickList); i++) {
if (llList2String(currentPickList, i) == msg) { if (llList2String(currentPickList, i) == msg) {
llOwnerSay("⚠️ Duplicate pick detected."); dbg("⚠️ Duplicate pick detected.");
return; return;
} }
} }
@ -779,9 +786,9 @@ default {
// Store display info: [type, offset, rotation, ...] // Store display info: [type, offset, rotation, ...]
foundDisplays += [object_type, offset, rel_rot]; foundDisplays += [object_type, offset, rel_rot];
llOwnerSay("✅ Found " + object_type + " display at position " + (string)object_pos); dbg("✅ Found " + object_type + " display at position " + (string)object_pos);
llOwnerSay(" Config: " + object_type + "_offset=" + (string)offset); dbg(" Config: " + object_type + "_offset=" + (string)offset);
llOwnerSay(" Rotation: " + object_type + "_rotation=" + (string)rel_rot); dbg(" Rotation: " + object_type + "_rotation=" + (string)rel_rot);
} }
} }
// Note: Automatic auto-config requests are now handled by Controller_Discovery.lsl // Note: Automatic auto-config requests are now handled by Controller_Discovery.lsl
@ -795,18 +802,18 @@ default {
integer numDisplays = llGetListLength(foundDisplays) / 3; // 3 items per display (type, offset, rotation) integer numDisplays = llGetListLength(foundDisplays) / 3; // 3 items per display (type, offset, rotation)
if (numDisplays == 0) { if (numDisplays == 0) {
llOwnerSay("\n⚠️ No follower displays found within 20 meters."); dbg("\n⚠️ No follower displays found within 20 meters.");
llOwnerSay("📝 Make sure your displays have the follower script and are close enough."); dbg("📝 Make sure your displays have the follower script and are close enough.");
llOwnerSay("🔄 Check that they use the same dynamic channel system as this controller."); dbg("🔄 Check that they use the same dynamic channel system as this controller.");
} else { } else {
llOwnerSay("\n🎉 Position scan complete! Found " + (string)numDisplays + " display(s)."); dbg("\n🎉 Position scan complete! Found " + (string)numDisplays + " display(s).");
llOwnerSay("\n📄 CONFIG UPDATE METHODS:"); dbg("\n📄 CONFIG UPDATE METHODS:");
llOwnerSay("1️⃣ MANUAL: Copy the config lines above to each display's 'config' notecard"); dbg("1️⃣ MANUAL: Copy the config lines above to each display's 'config' notecard");
llOwnerSay("2️⃣ AUTOMATIC: The displays should move to their new positions automatically!"); dbg("2️⃣ AUTOMATIC: The displays should move to their new positions automatically!");
llOwnerSay("\n📆 The new positions are now saved and will be remembered when objects are rezzed."); dbg("\n📆 The new positions are now saved and will be remembered when objects are rezzed.");
// Send position update commands to displays (future enhancement) // Send position update commands to displays (future enhancement)
llOwnerSay("🔄 Broadcasting position updates to all found displays..."); dbg("🔄 Broadcasting position updates to all found displays...");
// Broadcast updates to each found display // Broadcast updates to each found display
integer i; integer i;
@ -822,18 +829,18 @@ default {
else if (displayType == "dice") updateChannel = DICE_DATA_CHANNEL; else if (displayType == "dice") updateChannel = DICE_DATA_CHANNEL;
else { else {
// Skip unknown display types // Skip unknown display types
llOwnerSay("⚠️ Unknown display type: " + displayType); dbg("⚠️ Unknown display type: " + displayType);
} }
// Only send update if we found a valid channel // Only send update if we found a valid channel
if (displayType == "scoreboard" || displayType == "leaderboard" || displayType == "dice") { if (displayType == "scoreboard" || displayType == "leaderboard" || displayType == "dice") {
string updateMsg = "POSITION_UPDATE|" + displayType + "|" + (string)displayOffset + "|" + (string)displayRotation; string updateMsg = "POSITION_UPDATE|" + displayType + "|" + (string)displayOffset + "|" + (string)displayRotation;
llRegionSay(updateChannel, updateMsg); llRegionSay(updateChannel, updateMsg);
llOwnerSay("✅ Sent position update to " + displayType + " display"); dbg("✅ Sent position update to " + displayType + " display");
} }
} }
llOwnerSay("\n✨ Position reset complete! Your displays should now be in the correct positions."); dbg("\n✨ Position reset complete! Your displays should now be in the correct positions.");
} }
} }
} }

View file

@ -46,6 +46,9 @@ displayDiceText(string text) {
} }
default { default {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() { state_entry() {
if (VERBOSE_LOGGING) { if (VERBOSE_LOGGING) {
llOwnerSay("🎲 Dice Display Bridge ready! (Linkset Version)"); llOwnerSay("🎲 Dice Display Bridge ready! (Linkset Version)");