## [2.6.0] - 2025-08-09
This commit is contained in:
parent
9e480e6bcc
commit
9f918be0c9
9 changed files with 155 additions and 120 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
**Peril Dice Game System - Created by Rebecca Nod and Noose the Bunny**
|
||||||
|
|
||||||
All notable changes to Peril Dice will be documented in this file.
|
All notable changes to Peril Dice will be documented in this file.
|
||||||
|
|
||||||
## [2.6.0] - 2025-08-09
|
## [2.6.0] - 2025-08-09
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# 🎲 **PERIL DICE GAME - OFFICIAL RULES**
|
# 🎲 **PERIL DICE GAME - OFFICIAL RULES**
|
||||||
|
|
||||||
|
**Created by Rebecca Nod and Noose the Bunny**
|
||||||
|
|
||||||
*Original concept by **Noose the Bunny** (djmusica28) in Second Life*
|
*Original concept by **Noose the Bunny** (djmusica28) in Second Life*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -166,9 +166,9 @@ default {
|
||||||
integer playerCount = (integer)str;
|
integer playerCount = (integer)str;
|
||||||
llOwnerSay("📈 Calculator: Calculating dice type for " + (string)playerCount + " players");
|
llOwnerSay("📈 Calculator: Calculating dice type for " + (string)playerCount + " players");
|
||||||
integer result = getDiceType(playerCount);
|
integer result = getDiceType(playerCount);
|
||||||
// Send result ONLY back to the requesting script to prevent duplicate processing
|
// Send result to ALL scripts via LINK_SET so Game Manager receives it directly
|
||||||
// Main Controller will forward it to Game Manager as needed
|
// Each script will handle duplicate prevention on their end
|
||||||
llMessageLinked(sender, MSG_DICE_TYPE_RESULT, (string)result, NULL_KEY);
|
llMessageLinked(LINK_SET, MSG_DICE_TYPE_RESULT, (string)result, NULL_KEY);
|
||||||
}
|
}
|
||||||
else if (num == MSG_GET_PICKS_REQUIRED) {
|
else if (num == MSG_GET_PICKS_REQUIRED) {
|
||||||
integer result = getPicksRequiredFromName(str);
|
integer result = getPicksRequiredFromName(str);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ integer diceType = 6;
|
||||||
key currentPicker;
|
key currentPicker;
|
||||||
integer roundStarted = FALSE;
|
integer roundStarted = FALSE;
|
||||||
integer diceTypeProcessed = FALSE; // Track if we've already processed dice type for this round
|
integer diceTypeProcessed = FALSE; // Track if we've already processed dice type for this round
|
||||||
|
integer ignorePicksSync = FALSE; // Temporarily ignore picks data sync during round initialization
|
||||||
|
|
||||||
// Duplicate request prevention
|
// Duplicate request prevention
|
||||||
string lastHumanPickMessage = "";
|
string lastHumanPickMessage = "";
|
||||||
|
|
@ -50,6 +51,8 @@ continueCurrentRound() {
|
||||||
picksData = [];
|
picksData = [];
|
||||||
globalPickedNumbers = [];
|
globalPickedNumbers = [];
|
||||||
|
|
||||||
|
llOwnerSay("🔄 [Game Manager] continueCurrentRound: Cleared picks data, starting fresh");
|
||||||
|
|
||||||
// Set round as started for the new round
|
// Set round as started for the new round
|
||||||
roundStarted = TRUE;
|
roundStarted = TRUE;
|
||||||
diceTypeProcessed = FALSE; // Reset dice type processing for new round
|
diceTypeProcessed = FALSE; // Reset dice type processing for new round
|
||||||
|
|
@ -65,7 +68,9 @@ continueCurrentRound() {
|
||||||
}
|
}
|
||||||
currentPickerIdx = 0;
|
currentPickerIdx = 0;
|
||||||
|
|
||||||
// Request dice type for the new round
|
llOwnerSay("🎯 [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);
|
llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY);
|
||||||
|
|
||||||
// Note: showNextPickerDialog() will be called when dice type result is received
|
// Note: showNextPickerDialog() will be called when dice type result is received
|
||||||
|
|
@ -282,49 +287,59 @@ default {
|
||||||
newPicksData = llParseString2List(picksDataStr, ["^"], []);
|
newPicksData = llParseString2List(picksDataStr, ["^"], []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't sync peril player from Main Controller - Game Manager is authoritative
|
// Game Manager is authoritative for peril player, but check for eliminations
|
||||||
// string receivedPeril = llList2String(parts, 2);
|
list newNames = llCSV2List(llList2String(parts, 3));
|
||||||
// Game Manager manages its own peril player state
|
|
||||||
names = llCSV2List(llList2String(parts, 3));
|
// Check if current peril player was eliminated
|
||||||
|
if (perilPlayer != "" && perilPlayer != "NONE") {
|
||||||
|
integer perilStillExists = llListFindList(newNames, [perilPlayer]) != -1;
|
||||||
|
if (!perilStillExists && llListFindList(names, [perilPlayer]) != -1) {
|
||||||
|
// Peril player was eliminated! Game Manager needs to assign new peril player
|
||||||
|
llOwnerSay("🚨 [Game Manager] Detected elimination of peril player: " + perilPlayer);
|
||||||
|
|
||||||
|
// Find first remaining alive player to be new peril player
|
||||||
|
string newPerilPlayer = "";
|
||||||
|
integer k;
|
||||||
|
for (k = 0; k < llGetListLength(newNames) && newPerilPlayer == ""; k++) {
|
||||||
|
string candidateName = llList2String(newNames, k);
|
||||||
|
integer candidateIdx = llListFindList(newNames, [candidateName]);
|
||||||
|
if (candidateIdx != -1 && candidateIdx < llGetListLength(newLives)) {
|
||||||
|
integer candidateLives = llList2Integer(newLives, candidateIdx);
|
||||||
|
if (candidateLives > 0) {
|
||||||
|
newPerilPlayer = candidateName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPerilPlayer != "") {
|
||||||
|
llOwnerSay("🎯 [Game Manager] Assigning new peril player: " + newPerilPlayer);
|
||||||
|
perilPlayer = newPerilPlayer;
|
||||||
|
} else {
|
||||||
|
llOwnerSay("⚠️ [Game Manager] No valid peril player candidates found!");
|
||||||
|
perilPlayer = "NONE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
names = newNames;
|
||||||
|
|
||||||
// New: also receive players list if available (for dialog targeting)
|
// New: also receive players list if available (for dialog targeting)
|
||||||
if (llGetListLength(parts) >= 5) {
|
if (llGetListLength(parts) >= 5) {
|
||||||
players = llCSV2List(llList2String(parts, 4));
|
players = llCSV2List(llList2String(parts, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
// AUTOMATIC ROUND CONTINUATION: Detect post-roll state
|
// Always update state normally - don't auto-continue rounds
|
||||||
// Check if lives have changed (indicating a roll occurred) and all picks are now empty
|
// The Roll module will handle post-roll logic and Main Controller will manage round flow
|
||||||
|
lives = newLives;
|
||||||
|
picksData = newPicksData;
|
||||||
|
|
||||||
|
// Only log if this appears to be a post-roll update for debugging
|
||||||
string oldLivesStr = llList2CSV(lives);
|
string oldLivesStr = llList2CSV(lives);
|
||||||
string newLivesStr = llList2CSV(newLives);
|
string newLivesStr = llList2CSV(newLives);
|
||||||
integer livesChanged = (oldLivesStr != newLivesStr);
|
integer livesChanged = (oldLivesStr != newLivesStr);
|
||||||
|
|
||||||
// Check if all picks are empty (indicating post-roll state)
|
if (livesChanged) {
|
||||||
integer allPicksEmpty = TRUE;
|
llOwnerSay("🔍 [Game Manager] Lives changed - roll completed, waiting for proper round continuation signal");
|
||||||
integer i;
|
|
||||||
for (i = 0; i < llGetListLength(newPicksData) && allPicksEmpty; i++) {
|
|
||||||
string entry = llList2String(newPicksData, i);
|
|
||||||
list entryParts = llParseString2List(entry, ["|"], []);
|
|
||||||
if (llGetListLength(entryParts) >= 2 && llList2String(entryParts, 1) != "") {
|
|
||||||
allPicksEmpty = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If lives changed and all picks are empty, this is post-roll - start new round
|
|
||||||
if (livesChanged && allPicksEmpty && roundStarted && perilPlayer != "" && perilPlayer != "NONE") {
|
|
||||||
llOwnerSay("🎯 [Game Manager] Post-roll detected - automatically continuing to next round");
|
|
||||||
|
|
||||||
// Update our state first
|
|
||||||
lives = newLives;
|
|
||||||
picksData = newPicksData;
|
|
||||||
|
|
||||||
// Reset for new round and continue
|
|
||||||
roundStarted = FALSE;
|
|
||||||
currentPickerIdx = 0;
|
|
||||||
continueCurrentRound();
|
|
||||||
} else {
|
|
||||||
// Normal state update
|
|
||||||
lives = newLives;
|
|
||||||
picksData = newPicksData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,6 @@ list floaterChannels = []; // Track actual floater channels for cleanup
|
||||||
|
|
||||||
list pickQueue = [];
|
list pickQueue = [];
|
||||||
integer currentPickerIdx = 0;
|
integer currentPickerIdx = 0;
|
||||||
integer diceType = 6;
|
|
||||||
|
|
||||||
integer MSG_SHOW_DIALOG = 101;
|
integer MSG_SHOW_DIALOG = 101;
|
||||||
integer MSG_ROLL_RESULT = 102;
|
integer MSG_ROLL_RESULT = 102;
|
||||||
integer MSG_UPDATE_FLOAT = 103;
|
integer MSG_UPDATE_FLOAT = 103;
|
||||||
|
|
@ -157,10 +155,9 @@ integer CONTROLLER_FACE = 1; // Face to display start image on (front face)
|
||||||
string TEXTURE_START = "title_start"; // Start image texture
|
string TEXTURE_START = "title_start"; // Start image texture
|
||||||
string TEXTURE_GAME_ACTIVE = "game_active"; // Optional: different texture during game
|
string TEXTURE_GAME_ACTIVE = "game_active"; // Optional: different texture during game
|
||||||
|
|
||||||
integer MSG_GET_DICE_TYPE = 1001;
|
// Dice type handling is now done by Game Manager directly
|
||||||
integer MSG_GET_PICKS_REQUIRED = 1002;
|
integer MSG_GET_PICKS_REQUIRED = 1002;
|
||||||
integer MSG_GET_PICKER_INDEX = 1003;
|
integer MSG_GET_PICKER_INDEX = 1003;
|
||||||
integer MSG_DICE_TYPE_RESULT = 1005;
|
|
||||||
integer MSG_SERIALIZE_GAME_STATE = 1004;
|
integer MSG_SERIALIZE_GAME_STATE = 1004;
|
||||||
integer MSG_SYNC_PICKQUEUE = 2001;
|
integer MSG_SYNC_PICKQUEUE = 2001;
|
||||||
|
|
||||||
|
|
@ -168,12 +165,13 @@ integer MSG_SYNC_PICKQUEUE = 2001;
|
||||||
// mirror the value used in Floater Manager to avoid inconsistencies.
|
// mirror the value used in Floater Manager to avoid inconsistencies.
|
||||||
integer MAX_PLAYERS = 10;
|
integer MAX_PLAYERS = 10;
|
||||||
|
|
||||||
integer TIMEOUT_SECONDS = 600;
|
integer TIMEOUT_SECONDS = 120; // Reduced from 600 (10 min) to 120 (2 min)
|
||||||
integer timeoutTimer;
|
integer timeoutTimer;
|
||||||
|
|
||||||
integer warning2min = 120;
|
// Updated warning times for 2-minute timeout
|
||||||
integer warning5min = 300;
|
integer warning30sec = 30; // 30 seconds remaining
|
||||||
integer warning9min = 540;
|
integer warning1min = 60; // 1 minute remaining
|
||||||
|
integer warning90sec = 90; // 1.5 minutes remaining
|
||||||
integer lastWarning = 0;
|
integer lastWarning = 0;
|
||||||
|
|
||||||
key currentPicker;
|
key currentPicker;
|
||||||
|
|
@ -266,10 +264,6 @@ updateHelpers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request helper-calculated values
|
// Request helper-calculated values
|
||||||
requestDiceType() {
|
|
||||||
llMessageLinked(LINK_SET, MSG_GET_DICE_TYPE, (string)llGetListLength(names), NULL_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPicksRequired(integer idx) {
|
requestPicksRequired(integer idx) {
|
||||||
llMessageLinked(LINK_SET, MSG_GET_PICKS_REQUIRED, llList2String(names, idx), NULL_KEY);
|
llMessageLinked(LINK_SET, MSG_GET_PICKS_REQUIRED, llList2String(names, idx), NULL_KEY);
|
||||||
}
|
}
|
||||||
|
|
@ -329,11 +323,9 @@ resetGame() {
|
||||||
currentPickerIdx = 0;
|
currentPickerIdx = 0;
|
||||||
roundStarted = FALSE; // Reset round flag
|
roundStarted = FALSE; // Reset round flag
|
||||||
gameStarting = FALSE; // Reset game starting flag
|
gameStarting = FALSE; // Reset game starting flag
|
||||||
diceTypeProcessed = FALSE; // Reset dice type processing flag
|
|
||||||
currentPicker = NULL_KEY; // Reset current picker
|
currentPicker = NULL_KEY; // Reset current picker
|
||||||
timeoutTimer = 0; // Reset timeout timer
|
timeoutTimer = 0; // Reset timeout timer
|
||||||
lastWarning = 0; // Reset warning timer
|
lastWarning = 0; // Reset warning timer
|
||||||
diceType = 6; // Reset dice type to default
|
|
||||||
|
|
||||||
// Send reset message to all other scripts to clear their state
|
// Send reset message to all other scripts to clear their state
|
||||||
llMessageLinked(LINK_SET, -99999, "FULL_RESET", NULL_KEY);
|
llMessageLinked(LINK_SET, -99999, "FULL_RESET", NULL_KEY);
|
||||||
|
|
@ -370,7 +362,6 @@ resetGame() {
|
||||||
|
|
||||||
integer roundStarted = FALSE;
|
integer roundStarted = FALSE;
|
||||||
integer gameStarting = FALSE; // Track when game is in startup sequence
|
integer gameStarting = FALSE; // Track when game is in startup sequence
|
||||||
integer diceTypeProcessed = FALSE; // Track if we've already processed initial dice type
|
|
||||||
|
|
||||||
// CONTINUE_ROUND logic now handled entirely by Game Manager
|
// CONTINUE_ROUND logic now handled entirely by Game Manager
|
||||||
|
|
||||||
|
|
@ -547,24 +538,8 @@ if (toucher == llGetOwner() && roundStarted) {
|
||||||
|
|
||||||
link_message(integer sender, integer num, string str, key id) {
|
link_message(integer sender, integer num, string str, key id) {
|
||||||
// Player list and pick data handling now delegated to Controller_MessageHandler.lsl
|
// Player list and pick data handling now delegated to Controller_MessageHandler.lsl
|
||||||
// Handle dice type result from helper - forward to Game Manager once
|
// Main Controller no longer needs to handle dice type results
|
||||||
if (num == MSG_DICE_TYPE_RESULT) {
|
// Game Manager handles all dice type logic directly
|
||||||
// Prevent duplicate processing of the same dice type result
|
|
||||||
if (diceTypeProcessed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
diceType = (integer)str;
|
|
||||||
diceTypeProcessed = TRUE;
|
|
||||||
|
|
||||||
// Set round started flag and update helpers
|
|
||||||
roundStarted = TRUE;
|
|
||||||
updateHelpers();
|
|
||||||
|
|
||||||
// Forward to Game Manager ONCE
|
|
||||||
llMessageLinked(LINK_SET, MSG_DICE_TYPE_RESULT, (string)diceType, NULL_KEY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (num == MSG_GET_PICKS_REQUIRED) {
|
if (num == MSG_GET_PICKS_REQUIRED) {
|
||||||
integer picksRequired = (integer)str;
|
integer picksRequired = (integer)str;
|
||||||
llOwnerSay(" Picks required: " + str);
|
llOwnerSay(" Picks required: " + str);
|
||||||
|
|
@ -704,41 +679,9 @@ sendStatusMessage("Victory"); // Show victory status
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the eliminated player was the peril player, assign a new one and continue game
|
// Let Game Manager handle peril player reassignment after elimination
|
||||||
if (eliminatedPlayer == perilPlayer) {
|
// Just update helpers and let Game Manager detect the elimination via sync
|
||||||
// Find first remaining alive player to be new peril player
|
|
||||||
perilPlayer = "";
|
|
||||||
integer k;
|
|
||||||
for (k = 0; k < llGetListLength(names) && perilPlayer == ""; k++) {
|
|
||||||
string candidateName = llList2String(names, k);
|
|
||||||
if (llList2Integer(lives, k) > 0) {
|
|
||||||
perilPlayer = candidateName;
|
|
||||||
llOwnerSay("🔄 After elimination, new peril player: " + perilPlayer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue the game with the new peril player (don't reset roundStarted)
|
|
||||||
if (perilPlayer != "") {
|
|
||||||
llOwnerSay("🎯 Continuing game with " + perilPlayer + " as new peril player");
|
|
||||||
// Reset picks and queue for new round, but keep roundStarted = TRUE
|
|
||||||
picksData = [];
|
|
||||||
globalPickedNumbers = [];
|
|
||||||
pickQueue = [perilPlayer];
|
|
||||||
integer i;
|
|
||||||
for (i = 0; i < llGetListLength(names); i++) {
|
|
||||||
string playerName = llList2String(names, i);
|
|
||||||
if (playerName != perilPlayer) {
|
|
||||||
pickQueue += [playerName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentPickerIdx = 0;
|
|
||||||
updateHelpers();
|
updateHelpers();
|
||||||
requestDiceType();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Continue with existing peril player
|
|
||||||
updateHelpers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -836,11 +779,18 @@ resetGame();
|
||||||
}
|
}
|
||||||
// Handle start next round request - delegate to Game Manager
|
// Handle start next round request - delegate to Game Manager
|
||||||
if (num == 997) {
|
if (num == 997) {
|
||||||
|
// Check sender to prevent infinite loops - don't process our own messages
|
||||||
|
if (sender == llGetLinkNumber()) {
|
||||||
|
return; // Ignore messages from ourself
|
||||||
|
}
|
||||||
|
|
||||||
// Additional protection against round start spam
|
// Additional protection against round start spam
|
||||||
if (roundStarted && str == "START_NEXT_ROUND") {
|
if (roundStarted && str == "START_NEXT_ROUND") {
|
||||||
llOwnerSay("⚠️ [Main Controller] Round already in progress, ignoring START_NEXT_ROUND");
|
llOwnerSay("⚠️ [Main Controller] Round already in progress, ignoring START_NEXT_ROUND");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Game Manager will handle dice type processing for new round
|
||||||
|
llOwnerSay("🔄 [Main Controller] Starting new round - Game Manager will handle dice type");
|
||||||
// Forward the request to Game Manager
|
// Forward the request to Game Manager
|
||||||
llMessageLinked(LINK_SET, num, str, id);
|
llMessageLinked(LINK_SET, num, str, id);
|
||||||
// Don't request dice type here - it's handled in the "Start Game" listener below
|
// Don't request dice type here - it's handled in the "Start Game" listener below
|
||||||
|
|
@ -1016,6 +966,31 @@ llRegionSay(SCOREBOARD_CHANNEL_1, "RESET_LEADERBOARD"); // Reset leaderboard sco
|
||||||
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|" + (string)ownerIsStarter, llGetOwner());
|
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|" + (string)ownerIsStarter, llGetOwner());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (msg == "Force Floaters") {
|
||||||
|
// Force creation of floaters for all registered players
|
||||||
|
llOwnerSay("🔧 Forcing floater creation for all " + (string)llGetListLength(names) + " players...");
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < llGetListLength(names); i++) {
|
||||||
|
string playerName = llList2String(names, i);
|
||||||
|
key playerKey = llList2Key(players, i);
|
||||||
|
llMessageLinked(LINK_SET, MSG_REZ_FLOAT, playerName, playerKey);
|
||||||
|
llSleep(0.3); // Brief delay between rezzing to prevent spam
|
||||||
|
}
|
||||||
|
llOwnerSay("✅ Floater creation requests sent for all players!");
|
||||||
|
// Refresh owner menu
|
||||||
|
integer ownerIdx = llListFindList(players, [llGetOwner()]);
|
||||||
|
integer ownerIsStarter = TRUE; // Default to TRUE for owner
|
||||||
|
integer n;
|
||||||
|
for (n = 0; n < ownerIdx && ownerIsStarter; n++) {
|
||||||
|
string existingName = llList2String(names, n);
|
||||||
|
// If there's a human player before the owner, owner is not starter
|
||||||
|
if (llSubStringIndex(existingName, "TestBot") != 0) {
|
||||||
|
ownerIsStarter = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|" + (string)ownerIsStarter, llGetOwner());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Allow the first registered player (index 0) or the owner to start the game.
|
// Allow the first registered player (index 0) or the owner to start the game.
|
||||||
// When the starter clicks "Start Game", begin the round.
|
// When the starter clicks "Start Game", begin the round.
|
||||||
|
|
@ -1102,15 +1077,15 @@ llRegionSay(SCOREBOARD_CHANNEL_1, "RESET_LEADERBOARD"); // Reset leaderboard sco
|
||||||
llSetTimerEvent(0);
|
llSetTimerEvent(0);
|
||||||
} else {
|
} else {
|
||||||
integer remaining = TIMEOUT_SECONDS - elapsed;
|
integer remaining = TIMEOUT_SECONDS - elapsed;
|
||||||
if (remaining <= warning2min && lastWarning < warning2min) {
|
if (remaining <= warning30sec && lastWarning < warning30sec) {
|
||||||
llOwnerSay("⚠️ " + (string)(remaining / 60) + " minutes remaining for dialog response");
|
llOwnerSay("⚠️ 30 seconds remaining for dialog response");
|
||||||
lastWarning = warning2min;
|
lastWarning = warning30sec;
|
||||||
} else if (remaining <= warning5min && lastWarning < warning5min) {
|
} else if (remaining <= warning1min && lastWarning < warning1min) {
|
||||||
llOwnerSay("⚠️ " + (string)(remaining / 60) + " minutes remaining for dialog response");
|
llOwnerSay("⚠️ 1 minute remaining for dialog response");
|
||||||
lastWarning = warning5min;
|
lastWarning = warning1min;
|
||||||
} else if (remaining <= warning9min && lastWarning < warning9min) {
|
} else if (remaining <= warning90sec && lastWarning < warning90sec) {
|
||||||
llOwnerSay("⚠️ " + (string)(remaining / 60) + " minutes remaining for dialog response");
|
llOwnerSay("⚠️ 1.5 minutes remaining for dialog response");
|
||||||
lastWarning = warning9min;
|
lastWarning = warning90sec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ list mainOwnerOptions = ["🎮 Game Participation", "👥 Player Management", "
|
||||||
list gameParticipationOptions = ["Join Game", "Leave Game", "⬅️ Back to Main"];
|
list gameParticipationOptions = ["Join Game", "Leave Game", "⬅️ Back to Main"];
|
||||||
list playerManagementOptions = ["Add Test Player", "⬅️ Back to Main"];
|
list playerManagementOptions = ["Add Test Player", "⬅️ Back to Main"];
|
||||||
list resetOptions = ["Reset Game", "Reset Leaderboard", "Reset All", "⬅️ Back to Main"];
|
list resetOptions = ["Reset Game", "Reset Leaderboard", "Reset All", "⬅️ Back to Main"];
|
||||||
list troubleshootingOptions = ["Cleanup Floaters", "Reset Follower Positions", "⬅️ Back to Main"];
|
list troubleshootingOptions = ["Cleanup Floaters", "Force Floaters", "Reset Follower Positions", "⬅️ Back to Main"];
|
||||||
|
|
||||||
// State tracking for dynamic ready menu with race condition protection
|
// State tracking for dynamic ready menu with race condition protection
|
||||||
key pendingMenuPlayer = NULL_KEY;
|
key pendingMenuPlayer = NULL_KEY;
|
||||||
|
|
@ -102,7 +102,7 @@ integer pendingMenuIsOwner = FALSE;
|
||||||
integer pendingMenuRequestID = 0; // Unique ID for each menu request
|
integer pendingMenuRequestID = 0; // Unique ID for each menu request
|
||||||
integer currentRequestID = 0; // Counter for generating unique request IDs
|
integer currentRequestID = 0; // Counter for generating unique request IDs
|
||||||
float pendingMenuTimestamp = 0.0; // Timeout for pending requests
|
float pendingMenuTimestamp = 0.0; // Timeout for pending requests
|
||||||
float MENU_REQUEST_TIMEOUT = 10.0; // 10 second timeout for menu requests
|
float MENU_REQUEST_TIMEOUT = 5.0; // 5 second timeout for menu requests
|
||||||
|
|
||||||
// Display the main categorized owner menu
|
// Display the main categorized owner menu
|
||||||
showOwnerMenu(key id) {
|
showOwnerMenu(key id) {
|
||||||
|
|
@ -372,9 +372,15 @@ 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...");
|
llOwnerSay("🧩 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") {
|
||||||
|
// Force creation of floaters for all registered players - send as dialog response to Main Controller
|
||||||
|
llOwnerSay("🔧 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 == "Reset Follower Positions") {
|
else if (msg == "Reset Follower Positions") {
|
||||||
// Integrated position reset functionality with automation options
|
// Integrated position reset functionality with automation options
|
||||||
if (id == llGetOwner()) {
|
if (id == llGetOwner()) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# 🎲 Peril Dice — Modular Game System for Second Life
|
# 🎲 Peril Dice — Modular Game System for Second Life
|
||||||
|
|
||||||
|
**Created by Rebecca Nod and Noose the Bunny**
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Peril Dice is a multiplayer elimination game where each player selects numbers before a die is rolled. If the peril player's number is rolled, they lose a life. Players are eliminated when they reach zero lives.
|
Peril Dice is a multiplayer elimination game where each player selects numbers before a die is rolled. If the peril player's number is rolled, they lose a life. Players are eliminated when they reach zero lives.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
🎲 PERIL DICE — MODULAR GAME SYSTEM FOR SECOND LIFE
|
🎲 PERIL DICE — MODULAR GAME SYSTEM FOR SECOND LIFE
|
||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
|
CREATED BY REBECCA NOD AND NOOSE THE BUNNY
|
||||||
|
|
||||||
OVERVIEW
|
OVERVIEW
|
||||||
--------
|
--------
|
||||||
Peril Dice is a multiplayer elimination game where each player selects numbers before a die is rolled. If the peril player's number is rolled, they lose a life. Players are eliminated when they reach zero lives.
|
Peril Dice is a multiplayer elimination game where each player selects numbers before a die is rolled. If the peril player's number is rolled, they lose a life. Players are eliminated when they reach zero lives.
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ list names = [];
|
||||||
list lives = [];
|
list lives = [];
|
||||||
list picksData = [];
|
list picksData = [];
|
||||||
string perilPlayer = "";
|
string perilPlayer = "";
|
||||||
|
integer diceType = 6; // Store the dice type for rolling
|
||||||
|
integer shouldRoll = FALSE; // Flag to trigger roll after dice type is received
|
||||||
|
|
||||||
list getPicksFor(string nameInput) {
|
list getPicksFor(string nameInput) {
|
||||||
integer i;
|
integer i;
|
||||||
|
|
@ -197,7 +199,9 @@ default {
|
||||||
if (llSubStringIndex(str, "TestBot") == 0) {
|
if (llSubStringIndex(str, "TestBot") == 0) {
|
||||||
// Auto-roll for bots
|
// Auto-roll for bots
|
||||||
llOwnerSay("🤖 " + str + " (bot) is auto-rolling...");
|
llOwnerSay("🤖 " + str + " (bot) is auto-rolling...");
|
||||||
llMessageLinked(LINK_SET, 996, "GET_DICE_TYPE", NULL_KEY);
|
shouldRoll = TRUE; // Set flag for bot auto-roll
|
||||||
|
// Request dice type directly from Calculator
|
||||||
|
llMessageLinked(LINK_SET, 1001, (string)llGetListLength(names), NULL_KEY);
|
||||||
} else {
|
} else {
|
||||||
// Show dialog for human players
|
// Show dialog for human players
|
||||||
llDialog(id, "🎲 THE MOMENT OF TRUTH! You're in ultimate peril. Will you face your fate?", ["ROLL THE DICE OF FATE"], rollDialogChannel);
|
llDialog(id, "🎲 THE MOMENT OF TRUTH! You're in ultimate peril. Will you face your fate?", ["ROLL THE DICE OF FATE"], rollDialogChannel);
|
||||||
|
|
@ -205,6 +209,31 @@ default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle dice type requests from listen() events
|
||||||
|
else if (num == 996) {
|
||||||
|
// This is a GET_DICE_TYPE request from our own listen handler
|
||||||
|
// Forward it to the Main Controller to get the current dice type
|
||||||
|
llMessageLinked(LINK_SET, 1001, (string)llGetListLength(names), NULL_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle dice type response - store dice type and perform roll if requested
|
||||||
|
else if (num == 1005) { // MSG_DICE_TYPE_RESULT
|
||||||
|
diceType = (integer)str;
|
||||||
|
llOwnerSay("🎲 [Roll Module] Received dice type: d" + (string)diceType);
|
||||||
|
|
||||||
|
// If roll was requested from listen handler, perform it now
|
||||||
|
if (shouldRoll) {
|
||||||
|
shouldRoll = FALSE; // Reset flag
|
||||||
|
llOwnerSay("🎲 [Roll Module] Performing requested roll with d" + (string)diceType);
|
||||||
|
|
||||||
|
// Use MSG_ROLL_RESULT handler to perform the actual roll
|
||||||
|
llMessageLinked(LINK_THIS, MSG_ROLL_RESULT, (string)diceType, NULL_KEY);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy MSG_ROLL_RESULT handler (if still used somewhere)
|
||||||
else if (num == MSG_ROLL_RESULT) {
|
else if (num == MSG_ROLL_RESULT) {
|
||||||
integer diceType = (integer)str;
|
integer diceType = (integer)str;
|
||||||
integer result = rollDice(diceType);
|
integer result = rollDice(diceType);
|
||||||
|
|
@ -344,9 +373,11 @@ default {
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string msg) {
|
listen(integer channel, string name, key id, string msg) {
|
||||||
if (msg == "Roll" || msg == "ROLL THE DICE OF FATE") {
|
if (msg == "Roll" || msg == "ROLL THE DICE OF FATE") {
|
||||||
// Handle dice roll - need to get dice type from main controller
|
// Handle dice roll - need to get dice type from Calculator
|
||||||
llOwnerSay("🎲 Roll button clicked by " + name + " (key: " + (string)id + ")");
|
llOwnerSay("🎲 Roll button clicked by " + name + " (key: " + (string)id + ")");
|
||||||
llMessageLinked(LINK_SET, 996, "GET_DICE_TYPE", NULL_KEY);
|
shouldRoll = TRUE; // Set flag to perform roll when dice type is received
|
||||||
|
// Request dice type directly from Calculator
|
||||||
|
llMessageLinked(LINK_SET, 1001, (string)llGetListLength(names), NULL_KEY);
|
||||||
} else {
|
} else {
|
||||||
llMessageLinked(LINK_THIS, channel, msg, id);
|
llMessageLinked(LINK_THIS, channel, msg, id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue