Peril_Dice/Helper.lsl
RebeccaNod1 494f01c7bf # Changelog
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
2025-08-03 04:38:55 -05:00

162 lines
6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// === Game Helper Functions Module (Max 10 players) ===
// This version chooses a dice size based on the number of players,
// ensuring at least three numbers are available per player.
integer MSG_GET_DICE_TYPE = 1001;
integer MSG_DICE_TYPE_RESULT = 1005;
integer MSG_GET_PICKS_REQUIRED = 1002;
integer MSG_GET_PICKER_INDEX = 1003;
integer MSG_SERIALIZE_GAME_STATE = 1004;
integer MSG_SYNC_GAME_STATE = 107;
integer MSG_SYNC_PICKQUEUE = 2001;
list lives;
list picksData;
string perilPlayer;
list names;
list pickQueue;
// List of supported dice sizes; ensure the last entry can accommodate 10 players × 3 picks = 30
list STANDARD_DICE = [6, 12, 20, 30];
// Determine which die to use based on player count.
// Each player should have at least 3 numbers available to choose from.
integer getDiceType(integer playerCount) {
integer minimumSides = playerCount * 3;
integer i;
for (i = 0; i < llGetListLength(STANDARD_DICE); i++) {
integer sides = llList2Integer(STANDARD_DICE, i);
if (sides >= minimumSides) {
return sides;
}
}
// If no predefined die has enough sides, return the largest supported die (d30)
return llList2Integer(STANDARD_DICE, llGetListLength(STANDARD_DICE) - 1);
}
// Determine how many picks a player should make based on the peril player's lives
integer getPicksRequiredFromName(string name) {
integer idx = llListFindList(names, [name]);
if (idx == -1) {
integer i;
for (i = 0; i < llGetListLength(names); i++) {
string testName = llList2String(names, i);
if (llSubStringIndex(testName, name) != -1) {
idx = i;
i = llGetListLength(names);
}
}
}
if (idx == -1) return 0;
integer lifeCount = (integer)llList2String(lives, idx);
// Pick count = 4 - peril player's lives (3 lives=1 pick, 2 lives=2 picks, 1 life=3 picks)
return 4 - lifeCount;
}
// Find the index of a player in the pickQueue by name
integer getPickerIndex(string name) {
return llListFindList(pickQueue, [name]);
}
// Serialize the current game state for syncing with other scripts
string serializeGameState() {
return llList2CSV(lives) + "~" + llList2CSV(picksData) + "~" + perilPlayer + "~" + llList2CSV(names);
}
// Show a pick management dialog for the owner.
// LSL does not support a void return type, so this returns an integer which is ignored.
integer showPickManager(string player, key id) {
string searchName = player;
if (llSubStringIndex(searchName, "~") != -1) {
list parts = llParseString2List(searchName, ["~"], []);
searchName = llList2String(parts, 1);
}
integer idx = llListFindList(names, [searchName]);
if (idx == -1) {
integer i;
for (i = 0; i < llGetListLength(names); i++) {
string testName = llList2String(names, i);
if (llSubStringIndex(testName, searchName) != -1) {
idx = i;
llOwnerSay("✅ Matched pick list for: " + testName);
i = llGetListLength(names);
}
}
if (idx == -1) {
llOwnerSay("⚠️ Could not find player: " + player);
return 0;
}
}
integer maxPicks = getPicksRequiredFromName(llList2String(names, idx));
string currentData = llList2String(picksData, idx);
list parts = llParseString2List(currentData, ["|"], []);
list currentPicks = [];
if (llGetListLength(parts) > 1) {
currentPicks = llParseString2List(llList2String(parts, 1), [","], []);
}
list buttons = [];
integer i;
for (i = 1; i <= 6; i++) {
string numStr = (string)i;
if (llListFindList(currentPicks, [numStr]) != -1) {
buttons += ["❌ " + numStr];
} else if (llGetListLength(currentPicks) < maxPicks) {
buttons += ["➕ " + numStr];
}
}
llOwnerSay("🛠 Showing pick dialog to: " + (string)id);
llDialog(id, "🛠 Managing picks for: " + searchName + "\nCurrent: " + llList2CSV(currentPicks), buttons, -88888);
// Return dummy value since LSL does not have a void return type
return 0;
}
default {
state_entry() {
llOwnerSay("🧮 Helper Module ready!");
}
link_message(integer sender, integer num, string str, key id) {
if (num == MSG_SYNC_GAME_STATE) {
list parts = llParseString2List(str, ["~"], []);
lives = llCSV2List(llList2String(parts, 0));
picksData = llCSV2List(llList2String(parts, 1));
perilPlayer = llList2String(parts, 2);
names = llCSV2List(llList2String(parts, 3));
}
else if (num == MSG_SYNC_PICKQUEUE) {
pickQueue = llCSV2List(str);
}
else if (num == MSG_GET_DICE_TYPE) {
integer playerCount = (integer)str;
llOwnerSay("📊 playerCount received: " + (string)playerCount);
integer result = getDiceType(playerCount);
// Broadcast the dice type result across the entire linkset so that
// the main game controller can receive it. Using LINK_THIS
// restricted the message to the helper's prim, preventing the
// controller from seeing MSG_DICE_TYPE_RESULT and causing the
// game not to start.
llMessageLinked(LINK_SET, MSG_DICE_TYPE_RESULT, (string)result, NULL_KEY);
}
else if (num == MSG_GET_PICKS_REQUIRED) {
integer result = getPicksRequiredFromName(str);
llMessageLinked(LINK_THIS, MSG_GET_PICKS_REQUIRED, (string)result, NULL_KEY);
}
else if (num == MSG_GET_PICKER_INDEX) {
integer result = getPickerIndex(str);
llMessageLinked(LINK_THIS, MSG_GET_PICKER_INDEX, (string)result, NULL_KEY);
}
else if (num == MSG_SERIALIZE_GAME_STATE) {
llMessageLinked(LINK_THIS, MSG_SERIALIZE_GAME_STATE, serializeGameState(), NULL_KEY);
}
else if (num == 206) {
llOwnerSay("📨 MSG 206 received for: " + str + " from: " + (string)id);
showPickManager(str, id);
}
}
}