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. 🔧 Features ✅ Player System Max players: 10 Each player starts with 3 lives Players can Join, Leave, or mark themselves Ready The first player to join gets a Start Game button The owner can join the game and also has a special Owner menu 🎲 Dice Scaling Dice type is automatically chosen to ensure at least 3 picks per player Dice types supported: d6, d12, d20, d30 Player Count Dice Type 1–2 d6 3–4 d12 5–6 d20 7–10 d30 🧍 Test Players Owner can add/remove test players (bots) Test players rez stat floaters at offset positions to prevent overlap Floater objects follow players and update in real time 🪟 HUD & Stat Floaters Players receive a floating HUD showing: Player name Lives Picks Peril status or game state 📦 Modular Architecture Separated scripts: Main Controller: Handles core game logic Dialog Handler: Manages pick dialogs Dice Selector: Returns dice type based on player count Stat Float Manager: Rezzes and updates floaters Stat Float Object: Displays floating text per player 🕹️ Game Flow Players touch the controller object to join the game Once 2+ players have joined, the first player gets the option to Start Game Dice type is selected automatically Each player is prompted (in order) to select 3 numbers After all picks are made: The dice is rolled The result is compared to each player’s picks If the peril player’s number is rolled or no one picked it, the peril player loses a life The system checks for eliminations and starts the next round 🧰 Admin Controls Owner can: Join or leave the game Open a special Owner menu Add/remove test bots Reset the game Debug current state (optional)
156 lines
5.3 KiB
Text
156 lines
5.3 KiB
Text
integer MSG_SYNC_GAME_STATE = 107;
|
|
|
|
list validatePicksData(list picksData, list names) {
|
|
list allPicksFlat = [];
|
|
integer i;
|
|
|
|
for (i = 0; i < llGetListLength(picksData); i++) {
|
|
string entry = llList2String(picksData, i);
|
|
list parts = llParseString2List(entry, ["|"], []);
|
|
|
|
if (llGetListLength(parts) == 2) {
|
|
string name = llList2String(parts, 0);
|
|
string picks = llList2String(parts, 1);
|
|
list pickNums = llParseString2List(picks, [","], []);
|
|
|
|
// Check if name is in names list
|
|
if (names != [] && llListFindList(names, [name]) == -1) {
|
|
llOwnerSay("⚠️ picksData name not in names list: " + name);
|
|
}
|
|
|
|
// Check for duplicate picks in this player's list
|
|
integer j;
|
|
list uniqueCheck = [];
|
|
for (j = 0; j < llGetListLength(pickNums); j++) {
|
|
string pick = llList2String(pickNums, j);
|
|
if ((string)((float)pick) != pick) {
|
|
llOwnerSay("⚠️ Invalid number in picks for " + name + ": '" + pick + "'");
|
|
}
|
|
if (~llListFindList(uniqueCheck, [pick])) {
|
|
llOwnerSay("🚨 Duplicate pick in " + name + "'s picks: " + pick);
|
|
} else {
|
|
uniqueCheck += [pick];
|
|
}
|
|
allPicksFlat += [pick];
|
|
}
|
|
|
|
llOwnerSay("✅ " + name + "'s picks: " + llList2CSV(pickNums));
|
|
} else {
|
|
llOwnerSay("⚠️ Malformed entry in picksData: " + entry);
|
|
}
|
|
}
|
|
|
|
// Check for duplicate picks across all players
|
|
list seen = [];
|
|
integer k;
|
|
for (k = 0; k < llGetListLength(allPicksFlat); k++) {
|
|
string val = llList2String(allPicksFlat, k);
|
|
if (~llListFindList(seen, [val])) {
|
|
llOwnerSay("❌ Duplicate pick across players: " + val);
|
|
} else {
|
|
seen += [val];
|
|
}
|
|
}
|
|
|
|
return picksData;
|
|
}
|
|
|
|
|
|
|
|
default {
|
|
link_message(integer sender, integer num, string str, key id) {
|
|
if (num == MSG_SYNC_GAME_STATE) {
|
|
list parts = llParseString2List(str, ["~"], []);
|
|
if (llGetListLength(parts) < 4) {
|
|
llOwnerSay("⚠️ Incomplete game state received.");
|
|
return;
|
|
}
|
|
|
|
list lives = llCSV2List(llList2String(parts, 0));
|
|
list picksData = llCSV2List(llList2String(parts, 1));
|
|
string perilPlayer = llList2String(parts, 2);
|
|
list names = llCSV2List(llList2String(parts, 3));
|
|
|
|
llOwnerSay("🔍 Checking picksData...");
|
|
validatePicksData(picksData, names);
|
|
}
|
|
}
|
|
}
|
|
integer MSG_SYNC_GAME_STATE = 107;
|
|
|
|
list validatePicksData(list picksData, list names) {
|
|
list allPicksFlat = [];
|
|
integer i;
|
|
|
|
for (i = 0; i < llGetListLength(picksData); i++) {
|
|
string entry = llList2String(picksData, i);
|
|
list parts = llParseString2List(entry, ["|"], []);
|
|
|
|
if (llGetListLength(parts) == 2) {
|
|
string name = llList2String(parts, 0);
|
|
string picks = llList2String(parts, 1);
|
|
list pickNums = llParseString2List(picks, [","], []);
|
|
|
|
// Check if name is in names list
|
|
if (names != [] && llListFindList(names, [name]) == -1) {
|
|
llOwnerSay("⚠️ picksData name not in names list: " + name);
|
|
}
|
|
|
|
// Check for duplicate picks in this player's list
|
|
integer j;
|
|
list uniqueCheck = [];
|
|
for (j = 0; j < llGetListLength(pickNums); j++) {
|
|
string pick = llList2String(pickNums, j);
|
|
if ((string)((float)pick) != pick) {
|
|
llOwnerSay("⚠️ Invalid number in picks for " + name + ": '" + pick + "'");
|
|
}
|
|
if (~llListFindList(uniqueCheck, [pick])) {
|
|
llOwnerSay("🚨 Duplicate pick in " + name + "'s picks: " + pick);
|
|
} else {
|
|
uniqueCheck += [pick];
|
|
}
|
|
allPicksFlat += [pick];
|
|
}
|
|
|
|
llOwnerSay("✅ " + name + "'s picks: " + llList2CSV(pickNums));
|
|
} else {
|
|
llOwnerSay("⚠️ Malformed entry in picksData: " + entry);
|
|
}
|
|
}
|
|
|
|
// Check for duplicate picks across all players
|
|
list seen = [];
|
|
integer k;
|
|
for (k = 0; k < llGetListLength(allPicksFlat); k++) {
|
|
string val = llList2String(allPicksFlat, k);
|
|
if (~llListFindList(seen, [val])) {
|
|
llOwnerSay("❌ Duplicate pick across players: " + val);
|
|
} else {
|
|
seen += [val];
|
|
}
|
|
}
|
|
|
|
return picksData;
|
|
}
|
|
|
|
|
|
|
|
default {
|
|
link_message(integer sender, integer num, string str, key id) {
|
|
if (num == MSG_SYNC_GAME_STATE) {
|
|
list parts = llParseString2List(str, ["~"], []);
|
|
if (llGetListLength(parts) < 4) {
|
|
llOwnerSay("⚠️ Incomplete game state received.");
|
|
return;
|
|
}
|
|
|
|
list lives = llCSV2List(llList2String(parts, 0));
|
|
list picksData = llCSV2List(llList2String(parts, 1));
|
|
string perilPlayer = llList2String(parts, 2);
|
|
list names = llCSV2List(llList2String(parts, 3));
|
|
|
|
llOwnerSay("🔍 Checking picksData...");
|
|
validatePicksData(picksData, names);
|
|
}
|
|
}
|
|
}
|