diff --git a/FURWARE_Leaderboard_Communication_Linkset.lsl b/FURWARE_Leaderboard_Communication_Linkset.lsl index 2528d3d..95afdf6 100644 --- a/FURWARE_Leaderboard_Communication_Linkset.lsl +++ b/FURWARE_Leaderboard_Communication_Linkset.lsl @@ -1,6 +1,10 @@ #include "Peril_Constants.lsl" string leftText = ""; +string firstName = ""; +integer blinkState = 0; +integer canBlink = 0; + // Distribute text to the FURWARE "Leaderboard" boxes distributeFullText(string text) { @@ -10,7 +14,33 @@ distributeFullText(string text) { if (llGetListLength(parts) >= 3) { // Distribute each column to its virtual box llMessageLinked(LINK_SET, 0, llList2String(parts, 0), (key)"fw_data:LB_Rank"); - llMessageLinked(LINK_SET, 0, llList2String(parts, 1), (key)"fw_data:LB_Name"); + + // Distribute Name column (Split first line for blinking) + string nameText = llList2String(parts, 1); + integer firstLineEnd = llSubStringIndex(nameText, "\n"); + if (firstLineEnd != -1) { + firstName = llGetSubString(nameText, 0, firstLineEnd - 1); + string remainingNames = llGetSubString(nameText, firstLineEnd + 1, -1); + llMessageLinked(LINK_SET, 0, firstName, (key)"fw_data:LB_Name_Top"); + llMessageLinked(LINK_SET, 0, remainingNames, (key)"fw_data:LB_Name"); + } else { + firstName = nameText; + llMessageLinked(LINK_SET, 0, firstName, (key)"fw_data:LB_Name_Top"); + llMessageLinked(LINK_SET, 0, "", (key)"fw_data:LB_Name"); + } + + // Check if we should blink (#1 spot on first page only) + integer offset = (integer)llList2String(parts, 5); + if (offset == 0) { + canBlink = TRUE; + llSetTimerEvent(0.5); + } else { + canBlink = FALSE; + llSetTimerEvent(0.0); + // Ensure name is visible on non-blinking pages + llMessageLinked(LINK_SET, 0, firstName, (key)"fw_data:LB_Name_Top"); + } + llMessageLinked(LINK_SET, 0, llList2String(parts, 2), (key)"fw_data:LB_WL"); // Route Navigation Arrows to dedicated button boxes @@ -41,8 +71,13 @@ default { // Registering virtual columns + Navigation buttons // Coordinates: x, y, width, height (0-indexed) + // border=lr added to create vertical separators between columns llMessageLinked(LINK_SET, 0, "", (key)"fw_addbox:LB_Rank:Leaderboard:0,1,4,10:"); - llMessageLinked(LINK_SET, 0, "", (key)"fw_addbox:LB_Name:Leaderboard:4,1,20,10:"); + + // Split Name into Top (for blinking) and Body + llMessageLinked(LINK_SET, 0, "", (key)"fw_addbox:LB_Name_Top:Leaderboard:4,1,20,1:border=lr"); + llMessageLinked(LINK_SET, 0, "", (key)"fw_addbox:LB_Name:Leaderboard:4,2,20,9:border=lr"); + llMessageLinked(LINK_SET, 0, "", (key)"fw_addbox:LB_WL:Leaderboard:24,1,8,10:"); // Navigation Row (Shifted down to Y=11) @@ -58,6 +93,22 @@ default { // Trigger Leaderboard Manager to download from KVP Database llMessageLinked(LINK_SET, MSG_RESET_LEADERBOARD, "START_SYNC", NULL_KEY); + + // Start Blink Timer (0.5s intervals) + llSetTimerEvent(0.5); + } + + timer() { + if (!canBlink) { + llSetTimerEvent(0.0); + return; + } + blinkState = !blinkState; + if (blinkState) { + llMessageLinked(LINK_SET, 0, firstName, (key)"fw_data:LB_Name_Top"); + } else { + llMessageLinked(LINK_SET, 0, "", (key)"fw_data:LB_Name_Top"); + } } touch_start(integer _n) { diff --git a/Game_Leaderboard_Manager.lsl b/Game_Leaderboard_Manager.lsl index 9ba7382..d5ea870 100644 --- a/Game_Leaderboard_Manager.lsl +++ b/Game_Leaderboard_Manager.lsl @@ -149,6 +149,8 @@ startUpdateSequence(string pName, string pResult) { } default { + on_rez(integer _p) { llResetScript(); } + state_entry() { REPORT_MEMORY(); dbg("⏳ [Leaderboard] Startup: Handshake link active."); @@ -343,7 +345,7 @@ default { } string _p = " "; if (_offset > 0) _p = "[ < PREV ]"; string _n = " "; if (_offset + 10 < _globalTotal) _n = "[ NEXT > ]"; - llMessageLinked(LINK_SET, MSG_DISPLAY_LEADERBOARD, "COLUMNS|" + _rankCol + "|" + _nameCol + "|" + _statsCol + "|" + _p + "|" + _n, NULL_KEY); + llMessageLinked(LINK_SET, MSG_DISPLAY_LEADERBOARD, "COLUMNS|" + _rankCol + "|" + _nameCol + "|" + _statsCol + "|" + _p + "|" + _n + "|" + (string)_offset, NULL_KEY); } else if (num == MSG_LB_PAGE_NEXT) { integer _maxEntries = llGetListLength(leaderboardData) / 3; @@ -367,6 +369,18 @@ default { if (str == "START_SYNC") { loadLBPage(1); } + else if (str == "WIPE" || str == "HARD_RESET") { + if (id != (key)GLOBAL_ADMIN) { + llOwnerSay("🚫 [Security] Unauthorized WIPE attempt blocked from: " + (string)id); + return; + } + // Soft Wipe: Clear all shards to empty in world datastore + integer _i; + for (_i = 1; _i <= MAX_LB_PAGES; _i++) { + llUpdateKeyValue(LB_KEY_PREFIX + (string)_i, "", FALSE, ""); + } + dbg("🚫 [Leaderboard Manager] GLOBAL WIPE: All 10 datastore shards have been set to empty."); + } else if (str == "DEBUG_GENERATE" && llGetOwner() == llGetCreator()) { generateShardedData(); } diff --git a/Main_Controller_Linkset.lsl b/Main_Controller_Linkset.lsl index d45188e..8a820da 100644 --- a/Main_Controller_Linkset.lsl +++ b/Main_Controller_Linkset.lsl @@ -1127,15 +1127,30 @@ default { return; } if (msg == "Reset Leaderboard") { - // CHANGED: Send to scoreboard, which will handle leaderboard reset - llMessageLinked(LINK_SCOREBOARD, MSG_RESET_LEADERBOARD, "", NULL_KEY); - dbg("🏆 Leaderboard scores reset - game wins cleared!"); + llDialog(id, "⚠️ WARNING: This will PERMANENTLY WIPE the World Ranking database (all 10 shards). Are you sure?", ["YES, WIPE LB", "NO, CANCEL"], DIALOG_CHANNEL); + return; + } + if (msg == "YES, WIPE LB") { + if (id != (key)GLOBAL_ADMIN) { + llRegionSayTo(id, 0, "🚫 Access Denied: Only the Global Admin can perform this action."); + return; + } + llMessageLinked(LINK_SCOREBOARD, MSG_RESET_LEADERBOARD, "WIPE", id); // Use 'id' to pass user key + llOwnerSay("🏆 GLOBAL LEADERBOARD WIPE confirmed and executed."); return; } if (msg == "Reset All") { + llDialog(id, "⚠️ WARNING: This will factory reset the game AND permanently wipe the leaderboard. Are you sure?", ["YES, RESET ALL", "NO, CANCEL"], DIALOG_CHANNEL); + return; + } + if (msg == "YES, RESET ALL") { + if (id != (key)GLOBAL_ADMIN) { + llRegionSayTo(id, 0, "🚫 Access Denied: Only the Global Admin can perform this action."); + return; + } resetGame(); - llMessageLinked(LINK_SCOREBOARD, MSG_RESET_LEADERBOARD, "", NULL_KEY); - dbg("🔄 Complete reset - game and leaderboard cleared!"); + llMessageLinked(LINK_SCOREBOARD, MSG_RESET_LEADERBOARD, "WIPE", id); // Use 'id' for security check + llOwnerSay("🔄 FULL RESET confirmed and executed."); return; } if (msg == "Memory Stats") { diff --git a/Peril_Constants.lsl b/Peril_Constants.lsl index f672127..806a68d 100644 --- a/Peril_Constants.lsl +++ b/Peril_Constants.lsl @@ -190,10 +190,12 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C #define DELAY_CONFETTI_TICK 0.2 // Confetti particle interval (0.2s) #define DELAY_CONFETTI_PREP 0.5 // Confetti preparation delay (0.5s) #define DELAY_FLOAT_UPDATE 0.1 // Floater status update delay (0.1s) - // --- Game Timing Settings (Originals relocated here) --- #define BOT_PICK_DELAY 2.0 // Bot picking decision time (2.0s) #define HUMAN_PICK_DELAY 1.0 // Human picking buffer (1.0s) #define DIALOG_DELAY 1.5 // Base dialog presentation delay (1.5s) #define STATUS_DISPLAY_TIME 8.0 // How long status text stays on display (8.0s) #define BOT_RESPONSE_DELAY 1.5 // Bot response timing (1.5s) + +// --- Security --- +#define GLOBAL_ADMIN "3e01527d-a9ff-4776-b2b8-918ab622f70f"