Peril_Dice/PerilController.lsl
RebeccaNod1 f895f9851c
✅ Today's Completed Tasks Summary
🎯 Refactored the Game Architecture
We modularized the original Peril Dice controller into separate cohesive components:

Main Peril Dice Controller

Handles core logic: player join/leave, peril selection, game sync, and communication routing.

Broadcasts updates via MSG_SYNC_GAME_STATE after major state changes.

Applies llSleep(0.2) to improve message delivery reliability.

Dialog Handler Module

Manages the number-picking dialog.

Tracks player picks and updates picksData accordingly.

Sends state sync after every valid pick.

Fixed pick serialization bugs and ensured updates trigger correctly.

Float Rezzer Module

Registers player data and rezzes a StatFloat object beside each participant.

Applies llSetObjectDesc(name); before rezzing for easier identification.

On update, broadcasts llRegionSay messages including current lives, peril, and picks.

StatFloat (HUD Script)

Listens on assigned channel to receive floating text updates.

Parses synced game state from link_message to extract lives, picks, and peril data.

Dynamically follows the avatar.

Enhanced parsing reliability and fixed index mapping for correct player display.

Roll and Confetti Module

Performs dice rolls and checks for matches.

Transfers peril role on match or removes a life otherwise.

Triggers confetti on a miss and updates floats for all players.

🛠 Debugging and Enhancements
Added llOwnerSay debug statements across all modules.

Resolved multiple name not defined within scope (nndws) issues.

Verified and cleaned up serialized game data formats across modules.

Ensured consistent use of player indexing and life count mapping.
2025-07-21 03:22:32 -05:00

139 lines
4.9 KiB
Text

// === Main Peril Dice Controller (Modular) ===
integer syncChannel = -77777;
integer numberPickChannel = -77888;
list players = [];
list names = [];
list lives = [];
string perilPlayer = "";
list globalPickedNumbers = [];
list picksData = [];
list pickQueue = [];
integer currentPickerIdx = 0;
integer diceType = 6;
integer MSG_SHOW_DIALOG = 101;
integer MSG_ROLL_RESULT = 102;
integer MSG_UPDATE_FLOAT = 103;
integer MSG_CLEANUP_FLOAT = 104;
integer MSG_REZ_FLOAT = 105;
integer MSG_REGISTER_PLAYER = 106;
integer MSG_SYNC_GAME_STATE = 107;
integer getDiceType(integer playerCount) {
return ((playerCount + 1) / 2) * 6;
}
integer getPicksRequired(integer lifeCount) {
if (lifeCount == 3) return 1;
if (lifeCount == 2) return 2;
return 3;
}
string serializeGameState() {
return llList2CSV(lives) + "~" + llList2CSV(picksData) + "~" + perilPlayer + "~" + llList2CSV(names);
}
resetGame() {
string gameSync;
integer i;
for (i = 0; i < llGetListLength(names); i++) {
integer ch = -777000 + i;
llMessageLinked(LINK_SET, MSG_CLEANUP_FLOAT, (string)ch, NULL_KEY);
}
players = names = lives = picksData = globalPickedNumbers = [];
perilPlayer = "";
pickQueue = [];
currentPickerIdx = 0;
llSay(syncChannel, "RESET");
llOwnerSay("🔄 Game reset!");
// defer game state sync until picks are populated
llSleep(0.2);
}
default {
state_entry() {
llListen(0, "", NULL_KEY, "");
llListen(syncChannel, "", NULL_KEY, "");
llListen(numberPickChannel, "", NULL_KEY, "");
llListen(-88888, "", NULL_KEY, "");
llOwnerSay("🎲 Modular Peril Controller Ready");
}
touch_start(integer total_number) {
string gameSync;
key toucher = llDetectedKey(0);
string name = llDetectedName(0);
integer idx = llListFindList(players, [toucher]);
if (~idx) {
llDialog(toucher, "❓ You are already in the game. Leave the game?", ["Yes", "No"], -88888);
return;
}
players += [toucher];
names += [name];
lives += [3];
llOwnerSay("✅ " + name + " has joined the game with 3 lives.");
llMessageLinked(LINK_SET, MSG_REGISTER_PLAYER, name + "|" + (string)toucher, NULL_KEY);
llMessageLinked(LINK_SET, MSG_REZ_FLOAT, name, toucher);
llSleep(0.2);
gameSync = serializeGameState();
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, gameSync, NULL_KEY);
llSleep(0.2);
llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, name, toucher);
}
listen(integer chan, string speakerName, key id, string msg) {
string gameSync;
if (chan == -88888 && msg == "Yes") {
integer i = llListFindList(players, [id]);
if (i != -1) {
integer ch = -777000 + i;
llMessageLinked(LINK_SET, MSG_CLEANUP_FLOAT, (string)ch, NULL_KEY);
players = llDeleteSubList(players, i, i);
names = llDeleteSubList(names, i, i);
lives = llDeleteSubList(lives, i, i);
picksData = llDeleteSubList(picksData, i, i);
globalPickedNumbers = llDeleteSubList(globalPickedNumbers, i, i);
llOwnerSay("👋 Player left the game: " + llKey2Name(id));
gameSync = serializeGameState();
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, gameSync, NULL_KEY);
llSleep(0.2);
}
}
else if (chan == 0 && llToLower(msg) == "/reset" && id == llGetOwner()) {
resetGame();
}
else if (chan == 0 && llToLower(msg) == "/start" && id == llGetOwner()) {
if (llGetListLength(names) == 0) {
llOwnerSay("⚠️ No players to choose from.");
return;
}
integer r = (integer)llFrand(llGetListLength(names));
perilPlayer = llList2String(names, r);
picksData = globalPickedNumbers = [];
diceType = getDiceType(llGetListLength(names));
pickQueue = names;
currentPickerIdx = 0;
llSay(syncChannel, "PERIL:" + perilPlayer);
llOwnerSay("🔥 Peril player is: " + perilPlayer);
gameSync = serializeGameState();
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, gameSync, NULL_KEY);
llSleep(0.2);
integer j;
for (j = 0; j < llGetListLength(names); j++) {
llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, llList2String(names, j), llList2Key(players, j));
}
string firstName = llList2String(pickQueue, currentPickerIdx);
key firstKey = llList2Key(players, llListFindList(names, [firstName]));
llMessageLinked(LINK_SET, MSG_SHOW_DIALOG, firstName, firstKey);
gameSync = serializeGameState();
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, gameSync, NULL_KEY);
}
}
}