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
#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;
#else
#define dbg(msg)
#define VERBOSE_LOGGING 0
#endif
// Original VERBOSE_LOGGING definition removed
// Message constants
#define MSG_SHOW_DIALOG 101
@ -118,30 +126,30 @@ continueCurrentRound() {
if (continuePlayerIdx != -1 && continuePlayerIdx < llGetListLength(lives)) {
integer continuePlayerLives = llList2Integer(lives, continuePlayerIdx);
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;
}
}
}
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;
}
// Verify peril player exists and has lives
integer perilIdx = llListFindList(names, [perilPlayer]);
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;
}
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;
}
integer perilLives = llList2Integer(lives, perilIdx);
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;
}
@ -149,7 +157,7 @@ continueCurrentRound() {
// globalPickedNumbers will be cleared when first player picks
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
roundStarted = TRUE;
@ -165,7 +173,7 @@ continueCurrentRound() {
}
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
llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY);
@ -176,12 +184,12 @@ continueCurrentRound() {
startNextRound() {
// Prevent multiple calls to start round
if (roundStarted) {
llOwnerSay("⚠️ Round already started, ignoring duplicate start request");
dbg("⚠️ Round already started, ignoring duplicate start request");
return;
}
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;
}
@ -200,7 +208,7 @@ startNextRound() {
return;
}
llOwnerSay("🎯 Game Manager starting new round...");
dbg("🎯 Game Manager starting new round...");
// Set roundStarted immediately to prevent duplicate calls
roundStarted = TRUE;
@ -216,7 +224,7 @@ startNextRound() {
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
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
llSleep(0.1); // Brief delay to ensure sync propagates
integer startJ;
@ -230,7 +238,7 @@ startNextRound() {
// globalPickedNumbers will be cleared when first player picks
// 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];
integer k;
for (k = 0; k < llGetListLength(names); k++) {
@ -240,11 +248,11 @@ startNextRound() {
}
}
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
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() {
@ -254,40 +262,40 @@ showNextPickerDialog() {
// Clear globalPickedNumbers if this is the first picker of a new round
if (currentPickerIdx == 0 && llGetListLength(picksData) == 0) {
globalPickedNumbers = [];
llOwnerSay("🔄 [Game Manager] Cleared globalPickedNumbers at start of new round");
dbg("🔄 [Game Manager] Cleared globalPickedNumbers at start of new round");
}
if (diceType <= 0) {
llOwnerSay("❌ Cannot show picker dialog: diceType not set (" + (string)diceType + ")");
dbg("❌ Cannot show picker dialog: diceType not set (" + (string)diceType + ")");
return;
}
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;
}
string firstName = llList2String(pickQueue, currentPickerIdx);
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;
}
// CRITICAL: Verify this player is still alive and in the game
integer showPlayerIdx = llListFindList(names, [firstName]);
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;
}
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;
}
integer showPlayerLives = llList2Integer(lives, showPlayerIdx);
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;
}
@ -299,7 +307,7 @@ showNextPickerDialog() {
string existingPicks = llGetSubString(llList2String(picksData, showIdx), llStringLength(firstName) + 1, -1);
if (existingPicks != "") {
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]);
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;
}
@ -337,7 +345,7 @@ showNextPickerDialog() {
string existingBotPicks = llGetSubString(llList2String(picksData, showJ), llStringLength(firstName) + 1, -1);
if (existingBotPicks != "") {
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();
string showAvoidListStr = llList2CSV(showCompleteAvoidList);
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);
llOwnerSay("🤖 " + firstName + " is automatically picking " + (string)showPicksNeeded + " numbers...");
dbg("🤖 " + firstName + " is automatically picking " + (string)showPicksNeeded + " numbers...");
} else {
// Bot already has picks, advance to next player immediately
currentPickerIdx++;
@ -382,7 +390,7 @@ showNextPickerDialog() {
string humanAvoidListStr = llList2CSV(humanCompleteAvoidList);
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
// Send dialog request through Player_RegistrationManager (it has the correct player keys)
@ -403,7 +411,7 @@ syncStateToMain() {
default {
state_entry() {
reportMemoryUsage("Game Manager");
llOwnerSay("🎯 Game Manager initializing...");
dbg("🎯 Game Manager initializing...");
// Initialize/reset all game state variables
players = [];
@ -424,12 +432,12 @@ default {
lastBotPickMessage = "";
lastProcessTime = 0;
llOwnerSay("🎯 Game Manager ready!");
dbg("🎯 Game Manager ready!");
}
on_rez(integer start_param) {
reportMemoryUsage("Game Manager");
llOwnerSay("🔄 Game Manager rezzed - reinitializing...");
dbg("🔄 Game Manager rezzed - reinitializing...");
// Reset all game state variables on rez
players = [];
@ -450,7 +458,7 @@ default {
lastBotPickMessage = "";
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) {
@ -528,7 +536,7 @@ default {
}
if (livesChanged && newPerilPlayer != "" && newPerilPlayer != "NONE" && roundStarted && allPicksEmpty) {
llOwnerSay("🎯 Post-roll state update detected - continuing round");
dbg("🎯 Post-roll state update detected - continuing round");
continueCurrentRound();
}
}
@ -538,21 +546,21 @@ default {
// Receive legacy game state updates from main controller (simplified version)
if (num == 9071) {
llOwnerSay("🔧 [Game Manager] Received sync: " + str);
dbg("🔧 [Game Manager] Received sync: " + 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
// Trust that Main Controller sends valid data
// Handle special RESET sync message
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;
}
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;
}
{
@ -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
string receivedPerilPlayer = llList2String(parts, 2);
if (receivedPerilPlayer != "NONE" && 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
@ -608,26 +616,26 @@ default {
list newPickQueue = llCSV2List(str);
string senderStr = (string)sender;
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
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;
}
// Additional protection: Don't accept pickQueue syncs if we just created a valid one
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;
}
// Only accept valid non-empty pickQueues
if (llGetListLength(newPickQueue) > 0) {
pickQueue = newPickQueue;
llOwnerSay("🔍 [Game Manager] pickQueue updated to: " + llList2CSV(pickQueue));
dbg("🔍 [Game Manager] pickQueue updated to: " + llList2CSV(pickQueue));
} else {
llOwnerSay("🔍 [Game Manager] Ignoring invalid pickQueue sync - keeping current: " + llList2CSV(pickQueue));
dbg("🔍 [Game Manager] Ignoring invalid pickQueue sync - keeping current: " + llList2CSV(pickQueue));
}
return;
}
@ -636,13 +644,13 @@ default {
if (num == MSG_DICE_TYPE_RESULT) {
// Prevent duplicate processing of dice type results
if (diceTypeProcessed) {
llOwnerSay("🎲 Ignoring duplicate dice type result: " + str);
dbg("🎲 Ignoring duplicate dice type result: " + str);
return;
}
diceType = (integer)str;
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
// Start dialog if round has been started and we have a queue
@ -662,7 +670,7 @@ default {
}
if (!alreadyHasPicks) {
llOwnerSay("🎯 Starting picks for " + currentPlayerName);
dbg("🎯 Starting picks for " + currentPlayerName);
llSleep(0.5);
showNextPickerDialog();
}
@ -679,7 +687,7 @@ default {
string playerName = llList2String(parts, 1);
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
integer alreadyHasPicks = FALSE;
@ -689,7 +697,7 @@ default {
if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) {
string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1);
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;
}
}
@ -706,7 +714,7 @@ default {
list duplicatePicks = [];
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++) {
string pick = llStringTrim(llList2String(newPicks, i), STRING_TRIM);
@ -721,7 +729,7 @@ default {
}
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]);
if (idx != -1) {
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
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
integer perilIdx = llListFindList(names, [perilPlayer]);
@ -771,7 +779,7 @@ default {
} else {
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 + " 🎲");
@ -797,19 +805,19 @@ default {
currentPickerIdx++;
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();
} else {
// 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
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase...");
dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain();
llSleep(1.0); // Longer delay to ensure sync propagates to all modules
// 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;
llMessageLinked(LINK_SET, MSG_DIALOG_FORWARD_REQUEST, rollRequest, NULL_KEY);
}
@ -820,13 +828,13 @@ default {
// Handle bot picks
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, [":"], []);
if (llGetListLength(parts) >= 3) {
string playerName = llList2String(parts, 1);
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
integer existingPicksIdx = -1;
@ -835,7 +843,7 @@ default {
if (llSubStringIndex(llList2String(picksData, m), playerName + "|") == 0) {
string existingPicks = llGetSubString(llList2String(picksData, m), llStringLength(playerName) + 1, -1);
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
// Count how many players have picks
@ -857,15 +865,15 @@ default {
// If all players have picks, advance to roll phase
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
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase...");
dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain();
llSleep(1.0); // Longer delay to ensure sync propagates to all modules
// 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);
// Send roll dialog through Player_RegistrationManager
@ -881,8 +889,8 @@ default {
// VALIDATE: Check for duplicate picks before saving
if (picksStr != "" && llListFindList(globalPickedNumbers, [picksStr]) != -1) {
llOwnerSay("❌ [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] Bot " + playerName + " tried to pick duplicate number: " + picksStr + " - REJECTING");
dbg("🙄 [Game Manager] Sending new pick command to Bot Manager for " + playerName);
// Calculate picks needed for bot retry
integer perilIdx = llListFindList(names, [perilPlayer]);
@ -896,14 +904,14 @@ default {
list completeAvoidList = buildCompleteAvoidanceList();
string avoidListStr = llList2CSV(completeAvoidList);
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);
return; // Don't save this pick
}
// Update picks data
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) {
integer picksIdx = -1;
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 (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
llOwnerSay("🔄 [Game Manager] Syncing final picks data before roll phase...");
dbg("🔄 [Game Manager] Syncing final picks data before roll phase...");
syncStateToMain();
llSleep(0.2); // Brief delay to ensure sync propagates
// 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);
// Send roll dialog through Player_RegistrationManager
@ -995,12 +1003,12 @@ default {
// Handle continue round requests from Main Controller
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
if (str != "" && str != "NONE") {
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
roundStarted = FALSE;
@ -1012,7 +1020,7 @@ default {
continueCurrentRound();
} else {
// 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
roundStarted = FALSE;
@ -1030,11 +1038,13 @@ default {
if (num == 9011 && llSubStringIndex(str, "VERBOSE_LOGGING|") == 0) {
list parts = llParseString2List(str, ["|"], []);
if (llGetListLength(parts) >= 2) {
#if DEBUG_LOGS
VERBOSE_LOGGING = (integer)llList2String(parts, 1);
#endif
if (VERBOSE_LOGGING) {
llOwnerSay("🔍 [Game Manager] Verbose logging ON");
dbg("🔍 [Game Manager] Verbose logging ON");
} else {
llOwnerSay("🔍 [Game Manager] Verbose logging OFF");
dbg("🔍 [Game Manager] Verbose logging OFF");
}
}
return;
@ -1051,13 +1061,13 @@ default {
diceTypeProcessed = FALSE; // Reset for new game
roundContinueInProgress = FALSE; // Reset protection flag
lastSyncProcessTime = 0; // Reset sync timing
llOwnerSay("🎯 Game Manager reset - ready for new game!");
dbg("🎯 Game Manager reset - ready for new game!");
return;
}
// Handle emergency state reset (for when game gets stuck)
if (num == -99998 && str == "EMERGENCY_RESET") {
llOwnerSay("🚨 [Game Manager] Emergency reset triggered!");
dbg("🚨 [Game Manager] Emergency reset triggered!");
roundStarted = FALSE;
perilPlayer = "";
currentPickerIdx = 0;
@ -1065,7 +1075,7 @@ default {
diceTypeProcessed = FALSE;
roundContinueInProgress = FALSE;
lastSyncProcessTime = 0; // Reset sync timing
llOwnerSay("🔒 [Game Manager] Emergency state reset complete");
dbg("🔒 [Game Manager] Emergency state reset complete");
return;
}
}

View file

@ -7,7 +7,15 @@
// =============================================================================
// 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
// Message constants for link communication
@ -919,7 +927,11 @@ updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
}
default {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() {
llListen(1, "", llGetOwner(), ""); // Listen on channel 1 for local owner testing commands
reportMemoryUsage("Scoreboard Manager");
if (VERBOSE_LOGGING) {
llOwnerSay("📈 Scoreboard Manager ready! (Linkset Version)");
@ -938,6 +950,8 @@ default {
// Load persistent leaderboard data from KVP Experience database
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
// No need to generate it randomly before data arrives
@ -954,6 +968,7 @@ default {
link_message(integer sender, integer num, string str, key id) {
// Handle verbose logging toggle
#if DEBUG_LOGS
if (num == MSG_TOGGLE_VERBOSE_LOGS) {
VERBOSE_LOGGING = !VERBOSE_LOGGING;
if (VERBOSE_LOGGING) {
@ -963,6 +978,7 @@ default {
}
return;
}
#endif
// Only listen to messages from the main controller (link 1)
if (sender != 1) return;
@ -1124,7 +1140,7 @@ default {
string status = llGetSubString(data, 0, comma - 1);
string val = llGetSubString(data, comma + 1, -1);
if (status == "0") {
if (status == "1") {
leaderboardNames = [];
leaderboardWins = [];
leaderboardLosses = [];
@ -1165,7 +1181,7 @@ default {
if (status == "3") {
// Update failed because key didn't exist yet! Create it.
llCreateKeyValue("Peril_LB_Top50", pendingSerialize);
} else if (status == "0") {
} else if (status == "1") {
if (VERBOSE_LOGGING) {
llOwnerSay("✅ Global scoreboard synced successfully.");
}
@ -1183,4 +1199,32 @@ default {
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 {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() {
if (VERBOSE_LOGGING) {
llOwnerSay("📋 Leaderboard Communication Script ready! (Linkset Version)");

View file

@ -19,6 +19,13 @@ string getPlayerName(key id) {
// 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)
#define SCOREBOARD_LINK 12 // Scoreboard manager cube
#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(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;
lastStatus = "";
@ -365,7 +372,7 @@ resetGame() {
default {
state_entry() {
reportMemoryUsage("Main Controller");
llOwnerSay("🎮 Main Controller ready! (Linkset Version)");
dbg("🎮 Main Controller ready! (Linkset Version)");
// Initialize lockout system
gameOwner = llGetOwner();
@ -377,12 +384,12 @@ default {
// Reset the game on startup to ensure clean state
resetGame();
llOwnerSay("✅ Main Controller initialization complete!");
dbg("✅ Main Controller initialization complete!");
}
on_rez(integer start_param) {
reportMemoryUsage("Main Controller");
llOwnerSay("🔄 Main Controller rezzed - resetting game state...");
dbg("🔄 Main Controller rezzed - resetting game state...");
// Re-initialize lockout system
gameOwner = llGetOwner();
@ -394,7 +401,7 @@ default {
// Reset the game to ensure clean state when rezzed
resetGame();
llOwnerSay("✅ Main Controller reset complete after rez!");
dbg("✅ Main Controller reset complete after rez!");
}
touch_start(integer total_number) {
@ -475,11 +482,11 @@ default {
}
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) {
// Owner is not in game - show join/admin choice
if (llListFindList(pendingRegistrations, [toucher]) != -1) {
llOwnerSay("⏳ Registration already in progress for " + getPlayerName(toucher));
dbg("⏳ Registration already in progress for " + getPlayerName(toucher));
return;
}
@ -521,7 +528,7 @@ default {
string playerName = getPlayerName(toucher);
if (playerName != "") {
if (llListFindList(pendingRegistrations, [toucher]) != -1) {
llOwnerSay("⏳ Registration already in progress for " + playerName);
dbg("⏳ Registration already in progress for " + playerName);
return;
}
@ -538,7 +545,7 @@ default {
}
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "player|" + (string)isStarter, toucher);
} 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]);
if (existingPlayerIdx != -1) {
llOwnerSay("⚠️ [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] DUPLICATE PLAYER KEY DETECTED - Player: " + newName + " (" + (string)id + ") already exists!");
dbg("⚠️ [Main Controller] Players list length: " + (string)llGetListLength(players) + ", Names list length: " + (string)llGetListLength(names));
return; // Skip adding duplicate
}
@ -585,8 +592,8 @@ default {
}
// That's it! Player_RegistrationManager already sent sync and scoreboard updates
llOwnerSay("✅ Player added to master lists: " + newName + " (" + (string)llGetFreeMemory() + " bytes free)");
llOwnerSay("🔍 [DEBUG] Updated lists - Players: " + (string)llGetListLength(players) + ", Names: " + (string)llGetListLength(names));
dbg("✅ Player added to master lists: " + newName + " (" + (string)llGetFreeMemory() + " bytes free)");
dbg("🔍 [DEBUG] Updated lists - Players: " + (string)llGetListLength(players) + ", Names: " + (string)llGetListLength(names));
}
return;
}
@ -625,7 +632,7 @@ default {
// SEND WINNER GLOW to scoreboard and trigger floater update
llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_WINNER, potentialWinner, NULL_KEY);
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
// 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
// CRITICAL: Set victory flag immediately to prevent sync processing
@ -750,7 +757,7 @@ default {
llMessageLinked(SCOREBOARD_LINK, MSG_GAME_WON, winner, NULL_KEY);
// 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();
currentTimerMode = TIMER_VICTORY_DELAY;
llSetTimerEvent(1.0); // Check every second
@ -800,7 +807,7 @@ default {
// Handle emergency reset for stuck games
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
llMessageLinked(LINK_SET, -99998, "EMERGENCY_RESET", NULL_KEY);
llSleep(0.5);
@ -857,7 +864,7 @@ default {
integer idx = llListFindList(names, [playerName]);
if (idx != -1) {
if (llSubStringIndex(playerName, "Bot") == 0) {
llOwnerSay("🤖 Bots are always ready and cannot change state");
dbg("🤖 Bots are always ready and cannot change state");
return;
}
@ -971,12 +978,12 @@ default {
}
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;
// Send peril player update to scoreboard for glow effect
llMessageLinked(SCOREBOARD_LINK, 3005, newPerilPlayer, NULL_KEY); // MSG_UPDATE_PERIL_PLAYER
} 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";
// Clear peril player glow on scoreboard
llMessageLinked(SCOREBOARD_LINK, MSG_UPDATE_PERIL_PLAYER, "", NULL_KEY);
@ -1006,7 +1013,7 @@ default {
// Check if we should reset the game (no players left)
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
roundStarted = FALSE;
gameStarting = FALSE;
@ -1018,7 +1025,7 @@ default {
// Skip updateHelpers to prevent memory crashes during player removal
// Scoreboard was already updated with MSG_REMOVE_PLAYER above
llOwnerSay("✅ [Main Controller] Synchronized removal of " + leavingName);
dbg("✅ [Main Controller] Synchronized removal of " + leavingName);
}
}
return;
@ -1029,7 +1036,7 @@ default {
if (id == gameOwner && str == "LOCK_GAME") {
isLocked = TRUE;
updateFloatingText();
llOwnerSay("🔒 [Main Controller] Game has been LOCKED - Floating text updated");
dbg("🔒 [Main Controller] Game has been LOCKED - Floating text updated");
}
return;
}
@ -1038,7 +1045,7 @@ default {
if (id == gameOwner && str == "UNLOCK_GAME") {
isLocked = FALSE;
updateFloatingText();
llOwnerSay("🔓 [Main Controller] Game has been UNLOCKED - Floating text updated");
dbg("🔓 [Main Controller] Game has been UNLOCKED - Floating text updated");
}
return;
}
@ -1059,13 +1066,13 @@ default {
// Handle memory monitor messages
if (num == MSG_EMERGENCY_CLEANUP) {
// Removed emergencyMemoryCleanup call to save memory
llOwnerSay("Emergency cleanup disabled to prevent crashes");
dbg("Emergency cleanup disabled to prevent crashes");
return;
}
// Handle reset requests from other scripts (like Game_Manager)
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();
return;
}
@ -1076,7 +1083,7 @@ default {
if (channel == rollDialogChannel) {
if (msg == "Start Next Round" || msg == "BEGIN KILLING GAME") {
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;
}
@ -1124,23 +1131,23 @@ default {
if (msg == "Reset Leaderboard") {
// CHANGED: Send to scoreboard, which will handle leaderboard reset
llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY);
llOwnerSay("🏆 Leaderboard scores reset - game wins cleared!");
dbg("🏆 Leaderboard scores reset - game wins cleared!");
return;
}
if (msg == "Reset All") {
resetGame();
llMessageLinked(SCOREBOARD_LINK, MSG_RESET_LEADERBOARD, "", NULL_KEY);
llOwnerSay("🔄 Complete reset - game and leaderboard cleared!");
dbg("🔄 Complete reset - game and leaderboard cleared!");
return;
}
if (msg == "Memory Stats") {
// Removed reportMemoryStats call to save memory
llOwnerSay("Memory stats reporting disabled to prevent crashes");
dbg("Memory stats reporting disabled to prevent crashes");
return;
}
if (msg == "Add Test Player") {
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;
}
@ -1161,11 +1168,11 @@ default {
// Don't show menu immediately - let the player touch the board again
// 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;
}
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;
for (i = 0; i < llGetListLength(names); i++) {
string playerName = llList2String(names, i);
@ -1173,7 +1180,7 @@ default {
llMessageLinked(LINK_SET, MSG_REZ_FLOAT, playerName, playerKey);
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
integer ownerIdx = llListFindList(players, [llGetOwner()]);
@ -1205,7 +1212,7 @@ default {
integer playerCount = llGetListLength(players);
integer nameCount = llGetListLength(names);
if (playerCount < 2) {
llOwnerSay("⚠️ Need at least 2 players to start the game.");
dbg("⚠️ Need at least 2 players to start the game.");
return;
}
@ -1220,7 +1227,7 @@ default {
}
if (starterName == "") {
llOwnerSay("⚠️ Only the game starter can start the game.");
dbg("⚠️ Only the game starter can start the game.");
return;
}
@ -1236,7 +1243,7 @@ default {
}
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;
}
@ -1269,7 +1276,7 @@ default {
lastStatus = "";
// CHANGED: Use link message instead of llRegionSay
llMessageLinked(SCOREBOARD_LINK, MSG_GAME_STATUS, "Title", NULL_KEY);
llOwnerSay("📢 Status cleared - reverted to Title");
dbg("📢 Status cleared - reverted to Title");
currentTimerMode = TIMER_IDLE;
llSetTimerEvent(0);
}
@ -1278,7 +1285,7 @@ default {
if (victoryDelayTimer > 0) {
integer elapsed = llGetUnixTime() - victoryDelayTimer;
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;
currentTimerMode = TIMER_IDLE;
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 ===
// Helper function to get display name with fallback to username
@ -51,11 +58,11 @@ initializeChannels() {
DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range
// Report channel to owner for debugging
llOwnerSay("🔧 [Owner/Player Dialog] Dynamic channels initialized:");
llOwnerSay(" Main Dialog: " + (string)MAIN_DIALOG_CHANNEL);
llOwnerSay(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
llOwnerSay(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
llOwnerSay(" Dice: " + (string)DICE_DATA_CHANNEL);
dbg("🔧 [Owner/Player Dialog] Dynamic channels initialized:");
dbg(" Main Dialog: " + (string)MAIN_DIALOG_CHANNEL);
dbg(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
dbg(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
dbg(" Dice: " + (string)DICE_DATA_CHANNEL);
}
integer DIALOG_CHANNEL; // Legacy variable, will be set dynamically
@ -165,7 +172,7 @@ showTroubleshootingMenu(key id) {
showReadyLeaveMenu(key id, integer isStarter, integer isOwner) {
// Check for timeout of previous request
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;
}
@ -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 (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;
@ -291,7 +298,7 @@ showKickPlayerMenu(key id, list playerNames) {
string buttonLabel = "👢 " + kickDisplayName;
// Debug: Check button length
llOwnerSay("🔍 Debug kick button: '" + buttonLabel + "' = " + (string)llStringLength(buttonLabel) + " chars");
dbg("🔍 Debug kick button: '" + buttonLabel + "' = " + (string)llStringLength(buttonLabel) + " chars");
kickOptions += [buttonLabel];
kickDisplayNames += [buttonLabel]; // Store the full button label
@ -363,14 +370,14 @@ default {
leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
diceHandle = llListen(DICE_DATA_CHANNEL, "", "", "");
llOwnerSay("🎭 Owner and Player Dialog Handler ready with lockout system!");
llOwnerSay("🔓 Game is UNLOCKED - All players can access menus");
dbg("🎭 Owner and Player Dialog Handler ready with lockout system!");
dbg("🔓 Game is UNLOCKED - All players can access menus");
}
on_rez(integer start_param) {
reportMemoryUsage("🎭 Player Dialog");
llOwnerSay("🔄 Player Dialog Handler rezzed - reinitializing...");
dbg("🔄 Player Dialog Handler rezzed - reinitializing...");
// Re-initialize dynamic channels
initializeChannels();
@ -408,7 +415,7 @@ default {
leaderboardHandle = llListen(LEADERBOARD_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) {
@ -424,14 +431,14 @@ default {
currentPickList = [];
currentPickTarget = "";
currentPickLimit = 3;
llOwnerSay("🎭 Owner and Player Dialog Handler reset!");
dbg("🎭 Owner and Player Dialog Handler reset!");
return;
}
if (num == MSG_SHOW_MENU) {
list args = llParseString2List(str, ["|"], []);
if (llGetListLength(args) < 2) {
llOwnerSay("⚠️ Invalid message format: " + str);
dbg("⚠️ Invalid message format: " + str);
return;
}
string targetType = llList2String(args, 0);
@ -468,7 +475,7 @@ default {
if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) {
// Check for timeout
if ((llGetTime() - pendingMenuTimestamp) > MENU_REQUEST_TIMEOUT) {
llOwnerSay("⏰ Menu request timed out, ignoring response");
dbg("⏰ Menu request timed out, ignoring response");
pendingMenuPlayer = NULL_KEY;
return;
}
@ -479,20 +486,20 @@ default {
pendingMenuRequestID = 0;
}
} 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;
}
else if (num == MSG_PLAYER_LIST_RESULT) {
list playerNames = llParseString2List(str, [","], []);
llOwnerSay("📋 Fetching list of players for pick management...");
dbg("📋 Fetching list of players for pick management...");
showPickManageMenu(id, playerNames);
}
// Handle player list result for kick functionality
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, [","], []);
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
list playerNames = [];
@ -506,11 +513,11 @@ default {
llSubStringIndex(name, "MSG_") != 0) {
playerNames += [name];
} 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);
}
else if (num == MSG_PICK_LIST_RESULT) {
@ -518,11 +525,11 @@ default {
string returnedName = llList2String(parts, 0);
string picks = llList2String(parts, 1);
if (llStringTrim(returnedName, STRING_TRIM) == llStringTrim(currentPickTarget, STRING_TRIM)) {
llOwnerSay("✅ Matched pick list for: " + returnedName);
dbg("✅ Matched pick list for: " + returnedName);
currentPickList = llCSV2List(picks);
llMessageLinked(LINK_THIS, MSG_LIFE_LOOKUP, currentPickTarget, id);
} else {
llOwnerSay("❌ Name mismatch: got " + returnedName + " but expected " + currentPickTarget);
dbg("❌ Name mismatch: got " + returnedName + " but expected " + currentPickTarget);
}
}
else if (num == MSG_LIFE_LOOKUP) {
@ -546,7 +553,7 @@ default {
if (pendingMenuPlayer == id && responseRequestID == pendingMenuRequestID) {
// Check for 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;
return;
}
@ -554,13 +561,13 @@ default {
// Decide which menu to show based on registration status
if (isRegistered) {
// Owner is registered - show ready/leave menu with Owner button
llOwnerSay("🔍 [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] Owner is registered, isStarter=" + (string)isStarter + ", showing ready/leave menu");
dbg("🔍 [Debug] About to call showReadyLeaveMenu with id=" + (string)id + ", isStarter=" + (string)isStarter + ", isOwner=TRUE");
// DON'T reset pending state here - showReadyLeaveMenu needs it!
// The ready state handler will reset it when the dialog is shown
showReadyLeaveMenu(id, isStarter, TRUE);
llOwnerSay("🔍 [Debug] showReadyLeaveMenu call completed");
dbg("🔍 [Debug] showReadyLeaveMenu call completed");
} else if (isPending) {
llRegionSayTo(id, 0, "⏳ Registration already in progress for " + ownerName);
// Reset pending state for non-registered responses
@ -576,10 +583,10 @@ default {
pendingMenuRequestID = 0;
}
} else {
llOwnerSay("⚠️ Owner status response mismatch - ignoring");
dbg("⚠️ Owner status response mismatch - ignoring");
}
} 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;
}
@ -603,7 +610,7 @@ default {
isLocked = TRUE;
// Notify Main Controller to update floating text
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.");
showOwnerMenu(id); // Refresh the owner menu
} else {
@ -615,7 +622,7 @@ default {
isLocked = FALSE;
// Notify Main Controller to update floating text
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.");
showOwnerMenu(id); // Refresh the owner menu
} else {
@ -676,25 +683,25 @@ default {
}
else if (msg == "Cleanup Floaters") {
// 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);
}
else if (msg == "Force Floaters") {
// 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
// No need to forward via link_message - the Main Controller is already listening on DIALOG_CHANNEL
}
else if (msg == "🔍 Toggle Verbose Logs") {
// 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);
// Return to troubleshooting menu after toggling
showTroubleshootingMenu(id);
}
else if (msg == "🔄 Check for Updates") {
// 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);
// Return to troubleshooting menu
showTroubleshootingMenu(id);
@ -704,7 +711,7 @@ default {
}
else if (llSubStringIndex(msg, "🛠 ") == 0) {
currentPickTarget = llStringTrim(llDeleteSubString(msg, 0, 1), STRING_TRIM);
llOwnerSay("🎯 Pick target set to: " + currentPickTarget);
dbg("🎯 Pick target set to: " + currentPickTarget);
currentPickList = [];
llMessageLinked(LINK_THIS, 206, currentPickTarget, id);
}
@ -731,7 +738,7 @@ default {
// Send kick message to Controller_MessageHandler (not sync channel 107!)
llMessageLinked(LINK_SET, 8007, "KICK_PLAYER|" + playerToKick + "|" + (string)id, NULL_KEY);
} 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
showPlayerManagementMenu(id);
@ -739,19 +746,19 @@ default {
// Forward numeric picks to the game logic
else if ((integer)msg > 0) {
if (llGetListLength(currentPickList) >= currentPickLimit) {
llOwnerSay("⚠️ Reached pick limit of " + (string)currentPickLimit + " for " + currentPickTarget);
dbg("⚠️ Reached pick limit of " + (string)currentPickLimit + " for " + currentPickTarget);
showPickListMenu(id);
return;
}
if (llListFindList(currentPickList, [msg]) != -1) {
llOwnerSay("⚠️ Pick already exists: " + msg);
dbg("⚠️ Pick already exists: " + msg);
showPickListMenu(id);
return;
}
integer i;
for (i = 0; i < llGetListLength(currentPickList); i++) {
if (llList2String(currentPickList, i) == msg) {
llOwnerSay("⚠️ Duplicate pick detected.");
dbg("⚠️ Duplicate pick detected.");
return;
}
}
@ -779,9 +786,9 @@ default {
// Store display info: [type, offset, rotation, ...]
foundDisplays += [object_type, offset, rel_rot];
llOwnerSay("✅ Found " + object_type + " display at position " + (string)object_pos);
llOwnerSay(" Config: " + object_type + "_offset=" + (string)offset);
llOwnerSay(" Rotation: " + object_type + "_rotation=" + (string)rel_rot);
dbg("✅ Found " + object_type + " display at position " + (string)object_pos);
dbg(" Config: " + object_type + "_offset=" + (string)offset);
dbg(" Rotation: " + object_type + "_rotation=" + (string)rel_rot);
}
}
// 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)
if (numDisplays == 0) {
llOwnerSay("\n⚠️ No follower displays found within 20 meters.");
llOwnerSay("📝 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("\n⚠️ No follower displays found within 20 meters.");
dbg("📝 Make sure your displays have the follower script and are close enough.");
dbg("🔄 Check that they use the same dynamic channel system as this controller.");
} else {
llOwnerSay("\n🎉 Position scan complete! Found " + (string)numDisplays + " display(s).");
llOwnerSay("\n📄 CONFIG UPDATE METHODS:");
llOwnerSay("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!");
llOwnerSay("\n📆 The new positions are now saved and will be remembered when objects are rezzed.");
dbg("\n🎉 Position scan complete! Found " + (string)numDisplays + " display(s).");
dbg("\n📄 CONFIG UPDATE METHODS:");
dbg("1️⃣ MANUAL: Copy the config lines above to each display's 'config' notecard");
dbg("2️⃣ AUTOMATIC: The displays should move to their new positions automatically!");
dbg("\n📆 The new positions are now saved and will be remembered when objects are rezzed.");
// 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
integer i;
@ -822,18 +829,18 @@ default {
else if (displayType == "dice") updateChannel = DICE_DATA_CHANNEL;
else {
// Skip unknown display types
llOwnerSay("⚠️ Unknown display type: " + displayType);
dbg("⚠️ Unknown display type: " + displayType);
}
// Only send update if we found a valid channel
if (displayType == "scoreboard" || displayType == "leaderboard" || displayType == "dice") {
string updateMsg = "POSITION_UPDATE|" + displayType + "|" + (string)displayOffset + "|" + (string)displayRotation;
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 {
on_rez(integer start_param) {
llResetScript(); // Fixes Z-fighting and alignment glitches on rez
}
state_entry() {
if (VERBOSE_LOGGING) {
llOwnerSay("🎲 Dice Display Bridge ready! (Linkset Version)");