All notable changes to Peril Dice will be documented in this file. ## [2.1.0] - 2025-01-08 ### ✨ Major Features Added - **Ready State System**: Players must mark themselves ready before games can start - **Game Protection**: Prevents players from joining games in progress - **Performance Optimization**: Reduced lag by limiting confetti to wins only - **Enhanced Bot Intelligence**: Improved bot behavior and duplicate number avoidance ### 🔧 Core Fixes - **Complete Game Flow**: Fixed end-to-end gameplay from start to finish - **Elimination Logic**: Proper handling of player elimination and game continuation - **Win Condition**: Fixed victory detection and game ending - **Data Synchronization**: Resolved corruption in pick data between scripts - **Peril Player Assignment**: Fixed race conditions in peril player transitions ### 🎮 Gameplay Improvements - **Dynamic Picking**: Number of picks now properly based on peril player's lives - **Duplicate Prevention**: Enhanced system to prevent duplicate number selection - **Dialog Pagination**: Improved number selection interface for d30 dice - **Floater Updates**: Real-time HUD updates throughout gameplay - **Bot Continuation**: Bots now properly continue games when humans are eliminated ### 🛠️ Technical Enhancements - **Modular Communication**: Improved inter-script messaging system - **Error Handling**: Comprehensive validation and error recovery - **Memory Management**: Better floater cleanup and channel tracking - **Data Encoding**: Fixed CSV conflicts with semicolon encoding system - **State Management**: Robust game state synchronization across modules ### 🐛 Bug Fixes - Fixed missing pick dialogs after game events - Resolved duplicate message issues between scripts - Fixed lives reset problems between rounds - Corrected floater display inconsistencies - Fixed bot confusion during player elimination - Resolved pick data corruption issues - Fixed peril player variable sync problems ### ⚡ Performance - Removed confetti from life loss (keeping only victory confetti) - Optimized debug output to reduce lag - Improved floater management efficiency - Enhanced bot processing speed ## [2.0.0] - 2024-12-XX ### 🎉 Initial Modular Release - **Modular Architecture**: Separated functionality into specialized scripts - **Bot System**: Added AI players for testing and gameplay variety - **Floating HUD**: Real-time player statistics display - **Dynamic Dice**: Automatic dice sizing based on player count - **Player Management**: Runtime player joining and leaving - **Owner Controls**: Administrative functions for game management ### 🎲 Core Game Features - **Pick System**: Number selection with validation - **Rolling Mechanism**: Automated dice rolling with visual effects - **Life Management**: 3-life system with elimination - **Peril Tracking**: Dynamic peril player assignment - **Victory Conditions**: Last player standing wins ### 🏗️ Architecture - **Main Controller**: Central game logic and state management - **Dialog Handlers**: User interface and input processing - **Roll Module**: Dice mechanics and particle effects - **Bot Manager**: AI player behavior and automation - **Floater Manager**: HUD display coordination - **Game Helpers**: Utility functions and calculations ## [1.0.0] - 2024-XX-XX ### 🌟 Original Release - Basic Peril Dice game implementation - Manual gameplay mechanics - Simple player tracking - Basic dice rolling functionality --- ## Development Notes ### Known Issues - **Bot Race Condition**: Bots may occasionally pick duplicate numbers when triggered simultaneously - **Debug Output**: Extended games may experience minor lag from comprehensive logging ### Future Enhancements - Serialized bot picking to eliminate race conditions - Configurable debug levels - Additional game modes and variants - Enhanced visual effects and animations ### Credits - **Original Game Design**: Noose the Bunny (djmusica28) - Second Life - **Automation & Development**: Enhanced for automated gameplay - **Architecture**: Modular LSL system for scalability and maintainability
154 lines
6.6 KiB
Text
154 lines
6.6 KiB
Text
// Bot Manager for Peril Dice
|
|
// Handles logic for test players (bots): picking numbers and rolling dice
|
|
|
|
// Configuration
|
|
integer LISTEN_CHANNEL = -9999; // Change if needed to match main controller
|
|
integer MSG_SYNC_GAME_STATE = 107;
|
|
|
|
// Track game state to validate bot commands
|
|
list names = [];
|
|
list lives = [];
|
|
string perilPlayer = "";
|
|
|
|
// Helper to parse and respond to pick commands
|
|
doBotPick(string botName, integer count, integer diceMax, list avoidNumbers) {
|
|
// Validate bot exists and is still alive
|
|
integer botIdx = llListFindList(names, [botName]);
|
|
if (botIdx == -1) {
|
|
llOwnerSay("[Bot Manager] ❌ Bot '" + botName + "' not found in game - ignoring command");
|
|
return;
|
|
}
|
|
|
|
integer botLives = llList2Integer(lives, botIdx);
|
|
if (botLives <= 0) {
|
|
llOwnerSay("[Bot Manager] ❌ Bot '" + botName + "' is eliminated (lives=" + (string)botLives + ") - ignoring command");
|
|
return;
|
|
}
|
|
|
|
llOwnerSay("[Bot Manager] ✅ Picking " + (string)count + " numbers for " + botName + " (lives=" + (string)botLives + ", max=" + (string)diceMax + ")");
|
|
if (llGetListLength(avoidNumbers) > 0) {
|
|
llOwnerSay("[Bot Manager] Avoiding already picked numbers: " + llList2CSV(avoidNumbers));
|
|
}
|
|
list picks;
|
|
integer attempts = 0;
|
|
while (llGetListLength(picks) < count && attempts < 100) {
|
|
integer roll = 1 + (integer)(llFrand((float)diceMax));
|
|
string rollStr = (string)roll;
|
|
// Check if not already picked by this bot and not picked by others
|
|
if (llListFindList(picks, [rollStr]) == -1 && llListFindList(avoidNumbers, [rollStr]) == -1) {
|
|
picks += rollStr;
|
|
}
|
|
attempts++;
|
|
}
|
|
string pickString = llDumpList2String(picks, ",");
|
|
string response = "BOT_PICKED:" + botName + ":" + pickString;
|
|
llOwnerSay("[Bot Manager] Sending: " + response);
|
|
llMessageLinked(LINK_SET, -9997, response, NULL_KEY);
|
|
}
|
|
|
|
// Helper to respond to peril roll commands
|
|
doBotRoll(string botName, integer diceMax) {
|
|
// Validate bot exists and is still alive
|
|
integer botIdx = llListFindList(names, [botName]);
|
|
if (botIdx == -1) {
|
|
llOwnerSay("[Bot Manager] ❌ Bot '" + botName + "' not found in game - ignoring roll command");
|
|
return;
|
|
}
|
|
|
|
integer botLives = llList2Integer(lives, botIdx);
|
|
if (botLives <= 0) {
|
|
llOwnerSay("[Bot Manager] ❌ Bot '" + botName + "' is eliminated (lives=" + (string)botLives + ") - ignoring roll command");
|
|
return;
|
|
}
|
|
|
|
if (botName != perilPlayer) {
|
|
llOwnerSay("[Bot Manager] ❌ Bot '" + botName + "' is not the peril player (current peril: '" + perilPlayer + "') - ignoring roll command");
|
|
return;
|
|
}
|
|
|
|
llOwnerSay("[Bot Manager] ✅ Bot '" + botName + "' rolling dice (max=" + (string)diceMax + ")");
|
|
integer roll = 1 + (integer)(llFrand((float)diceMax));
|
|
llRegionSay(LISTEN_CHANNEL, "BOT_ROLL:" + botName + ":" + (string)roll);
|
|
}
|
|
|
|
default {
|
|
state_entry() {
|
|
llListen(LISTEN_CHANNEL, "", NULL_KEY, "");
|
|
llOwnerSay("[Bot Manager] Ready and listening on channel " + (string)LISTEN_CHANNEL);
|
|
llOwnerSay("[Bot Manager] My key: " + (string)llGetKey());
|
|
llOwnerSay("[Bot Manager] My position: " + (string)llGetPos());
|
|
}
|
|
|
|
link_message(integer sender, integer num, string str, key id) {
|
|
// Handle game state sync to track player eliminations
|
|
if (num == MSG_SYNC_GAME_STATE) {
|
|
list parts = llParseString2List(str, ["~"], []);
|
|
if (llGetListLength(parts) >= 4) {
|
|
lives = llCSV2List(llList2String(parts, 0));
|
|
// Don't need picks data for validation, skip parts[1]
|
|
string receivedPeril = llList2String(parts, 2);
|
|
if (receivedPeril == "NONE") {
|
|
perilPlayer = "";
|
|
} else {
|
|
perilPlayer = receivedPeril;
|
|
}
|
|
names = llCSV2List(llList2String(parts, 3));
|
|
llOwnerSay("[Bot Manager] 🔄 Sync: " + (string)llGetListLength(names) + " players, peril='" + perilPlayer + "'");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (num == -9999) {
|
|
llOwnerSay("[Bot Manager] Received link message: " + str);
|
|
if (llSubStringIndex(str, "BOT_PICK:") == 0) {
|
|
// Format: BOT_PICK:bot_1:3:20:1,2,3 (last part is optional already picked numbers)
|
|
list parts = llParseStringKeepNulls(str, [":"], []);
|
|
if (llGetListLength(parts) >= 4) {
|
|
string botName = llList2String(parts, 1);
|
|
integer count = (integer)llList2String(parts, 2);
|
|
integer diceMax = (integer)llList2String(parts, 3);
|
|
list avoidNumbers = [];
|
|
if (llGetListLength(parts) >= 5) {
|
|
string avoidStr = llList2String(parts, 4);
|
|
if (avoidStr != "") {
|
|
avoidNumbers = llCSV2List(avoidStr);
|
|
}
|
|
}
|
|
doBotPick(botName, count, diceMax, avoidNumbers);
|
|
}
|
|
}
|
|
else if (llSubStringIndex(str, "BOT_PERIL_ROLL:") == 0) {
|
|
// Format: BOT_PERIL_ROLL:bot_1:20
|
|
list parts = llParseStringKeepNulls(str, [":"], []);
|
|
if (llGetListLength(parts) == 3) {
|
|
string botName = llList2String(parts, 1);
|
|
integer diceMax = (integer)llList2String(parts, 2);
|
|
doBotRoll(botName, diceMax);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
listen(integer channel, string name, key id, string message) {
|
|
llOwnerSay("[Bot Manager] Received: " + message);
|
|
if (llSubStringIndex(message, "BOT_PICK:") == 0) {
|
|
// Format: BOT_PICK:bot_1:3:20
|
|
list parts = llParseStringKeepNulls(message, [":"], []);
|
|
if (llGetListLength(parts) == 4) {
|
|
string botName = llList2String(parts, 1);
|
|
integer count = (integer)llList2String(parts, 2);
|
|
integer diceMax = (integer)llList2String(parts, 3);
|
|
doBotPick(botName, count, diceMax, []);
|
|
}
|
|
}
|
|
else if (llSubStringIndex(message, "BOT_PERIL_ROLL:") == 0) {
|
|
// Format: BOT_PERIL_ROLL:bot_1:20
|
|
list parts = llParseStringKeepNulls(message, [":"], []);
|
|
if (llGetListLength(parts) == 3) {
|
|
string botName = llList2String(parts, 1);
|
|
integer diceMax = (integer)llList2String(parts, 2);
|
|
doBotRoll(botName, diceMax);
|
|
}
|
|
}
|
|
}
|
|
}
|