removing files that are not needed anymore

This commit is contained in:
RebeccaNod1 2025-08-12 06:28:53 -05:00
parent 571e324dc7
commit 00c849c208
11 changed files with 0 additions and 1352 deletions

View file

@ -1,175 +0,0 @@
# Automatic Rez-Time Position Detection 🎯
## Yes! Displays automatically detect their positions at rez!
The Peril Dice Game now features **fully automatic position detection** - no config files required, no manual scanning needed. Just rez your displays and they automatically configure themselves!
## How It Works
### ✨ **At Rez Time:**
1. Display rez without config notecard
2. Display immediately broadcasts: "I'm a [scoreboard/leaderboard/dice] at position X"
3. Controller receives the request and calculates the relative position
4. Controller responds: "Your offset is Y, your rotation is Z"
5. Display configures itself and announces: "Ready!"
**Total time: ~1-2 seconds after rez** ⚡
### 🔄 **Automatic Retry System:**
- If no controller response in 5 seconds → retry request
- If still no response in 10 seconds → retry again
- Continues until controller found or manually configured
## Three Positioning Methods
### 🚀 **Method 1: Automatic Rez Detection (NEW!)**
**Zero interaction required!**
- Rez displays anywhere near controller
- They auto-configure instantly
- **No config files needed**
- **No menu interactions needed**
### 🛠️ **Method 2: Manual Position Reset (Enhanced)**
- Position displays manually where you want them
- Owner Menu → 🛠️ Troubleshooting → Reset Follower Positions
- Automatically updates all display positions
- Still works if auto-rez detection fails
### 📝 **Method 3: Traditional Config (Legacy)**
- Create config notecard with position values
- Still supported for advanced users
- Overrides automatic detection
## Technical Implementation
### Follower Display Side
```lsl
// At rez/state_entry:
if (no_config_notecard) {
// Broadcast auto-config request
llRegionSay(DATA_CHANNEL, "AUTO_CONFIG_REQUEST|scoreboard|<pos>|<rot>");
// Set retry timer
llSetTimerEvent(5.0);
}
// When controller responds:
llRegionSay(DATA_CHANNEL, "AUTO_CONFIG_RESPONSE|scoreboard|<offset>|<rotation>");
// Configure self automatically
MY_OFFSET = received_offset;
MY_ROTATION = received_rotation;
```
### Controller Side
```lsl
// Listen for auto-config requests
if (msg starts with "AUTO_CONFIG_REQUEST") {
// Calculate relative position
vector offset = (display_pos - controller_pos) / controller_rot;
rotation rel_rot = display_rot / controller_rot;
// Send response back
llRegionSay(display_channel, "AUTO_CONFIG_RESPONSE|" + type + "|" + offset + "|" + rotation);
}
```
## User Experience
### 🎮 **For Game Owners:**
- **Rez and forget** - displays auto-configure
- See automatic configuration messages in chat
- Manual reset still available if needed
- Works with multiple displays simultaneously
### 🔧 **For Display Setup:**
- Set `DISPLAY_TYPE = "scoreboard"` in follower script
- Rez display near controller (within ~96 meters)
- Watch it auto-configure in 1-2 seconds
- No config notecard required
## Configuration Messages
You'll see messages like:
```
🎆 Auto-configured scoreboard display at rez!
Position: <128.1, 45.7, 22.0>
Calculated offset: <0.0, 3.0, 0.5>
Calculated rotation: <0.0, 0.0, 0.0, 1.0>
```
On display side:
```
🎆 scoreboard auto-configured successfully!
Auto-detected offset: <0.0, 3.0, 0.5>
Auto-detected rotation: <0.0, 0.0, 0.0, 1.0>
🟢 Ready and listening for controller movement...
```
## Compatibility & Fallbacks
### ✅ **Multi-Instance Safe**
- Each game table has unique channels
- No interference between multiple setups
- Works with owner-specific dynamic channels
### 🛡️ **Robust Fallback Chain**
1. **Auto-detection at rez** (preferred)
2. **Manual position reset** (if auto fails)
3. **Config notecard** (traditional method)
4. **Default positions** (as last resort)
### 🔄 **Migration Friendly**
- Existing config-based displays continue working
- New displays use auto-detection
- Mixed setups supported
- Gradual migration possible
## Troubleshooting
### "No controller response - retrying"
- **Cause**: Controller too far away or not running
- **Solution**: Move display closer or ensure controller is active
- **Range**: Typically works within ~50-100 meters
### Display doesn't auto-configure
- Check `DISPLAY_TYPE` is set correctly in follower script
- Verify controller has Owner Dialog Handler with auto-config support
- Ensure both use same dynamic channel system
- Try manual position reset as backup
### Multiple displays configure incorrectly
- Auto-detection uses current positions at rez time
- Position displays where you want them BEFORE rezzing
- Use manual reset to adjust positions later
## Benefits Summary
### 🎯 **User Benefits**
- **Zero configuration** - just rez and play
- **Instant setup** - no waiting or manual steps
- **Error-free** - no manual config editing mistakes
- **Intuitive** - works as users expect
### 🔧 **Technical Benefits**
- **Self-documenting** - positions are automatically calculated
- **Consistent** - eliminates configuration drift
- **Scalable** - works with any number of displays
- **Reliable** - multiple fallback methods
### 🚀 **Operational Benefits**
- **Faster deployment** - no setup time
- **Reduced support** - fewer configuration issues
- **Better UX** - seamless experience
- **Future-proof** - extensible architecture
---
## The Complete Automation Journey
**Phase 1** ✅: Manual config files (traditional)
**Phase 2** ✅: Integrated position reset tool (manual scan)
**Phase 3** ✅: Automatic position updates (no manual config editing)
**Phase 4** ✅: **Automatic rez-time detection (zero interaction)**
**We've achieved full automation!** 🎉
Just rez your displays and they work - no config files, no menus, no manual steps required.

View file

@ -1,203 +0,0 @@
// === Controller Discovery Helper ===
// Handles all controller discovery functionality for the Main Controller
// Also handles automatic position configuration for newly rezzed displays
// Communicates with other scripts via link messages
// =============================================================================
// DYNAMIC CHANNEL & DISCOVERY SYSTEM
// =============================================================================
// Fixed discovery channel for client objects to find controller
integer DISCOVERY_CHANNEL = -77000;
// Base channel offset - should match other controller scripts
integer CHANNEL_BASE = -77000;
// Calculate channels dynamically to avoid hardcoded conflicts
integer calculateChannel(integer offset) {
// Use BOTH owner's key AND object's key to make channels unique per game instance
string ownerStr = (string)llGetOwner();
string objectStr = (string)llGetKey();
string combinedStr = ownerStr + objectStr;
// Create a more unique hash using both keys
string hashStr = llMD5String(combinedStr, 0);
integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0));
integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1));
integer combinedHash = hash1 * 16 + hash2; // Creates 0-255 range
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
// Dynamic channel variables for auto-config communication
integer SCOREBOARD_DATA_CHANNEL;
integer LEADERBOARD_DATA_CHANNEL;
integer DICE_DATA_CHANNEL;
// Client connection tracking
list connectedClients = []; // List of client object keys that have connected
list expectedClientTypes = ["scoreboard", "leaderboard", "dice"]; // Expected client types
integer discoveryBroadcastActive = TRUE;
float DISCOVERY_BROADCAST_INTERVAL = 30.0; // Broadcast availability every 30 seconds
// Message constants for inter-script communication
integer MSG_DISCOVERY_START = 5001;
integer MSG_DISCOVERY_STOP = 5002;
integer MSG_CLIENT_CONNECTED = 5003;
integer MSG_DISCOVERY_STATUS = 5004;
// Channel initialization function
initializeChannels() {
SCOREBOARD_DATA_CHANNEL = calculateChannel(6); // ~-83000 range
LEADERBOARD_DATA_CHANNEL = calculateChannel(7); // ~-84000 range
DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range
// Report channels for debugging
llOwnerSay("🔧 [Discovery] Auto-config channels initialized:");
llOwnerSay(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
llOwnerSay(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
llOwnerSay(" Dice: " + (string)DICE_DATA_CHANNEL);
}
default {
state_entry() {
llOwnerSay("📡 [Discovery] Controller Discovery Helper ready!");
// Initialize auto-config channels
initializeChannels();
// Listen on discovery channel for external objects seeking controller
llListen(DISCOVERY_CHANNEL, "", "", "");
// Listen on data channels for auto-config requests
llListen(SCOREBOARD_DATA_CHANNEL, "", "", "");
llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
llListen(DICE_DATA_CHANNEL, "", "", "");
// Start broadcasting controller availability
discoveryBroadcastActive = TRUE;
llOwnerSay("📺 [Discovery] Broadcasting controller availability...");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey()); // Use controller object key
// Set timer to periodically announce availability
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
// Notify main controller that discovery is active
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|" + (string)llGetListLength(connectedClients), NULL_KEY);
}
listen(integer channel, string name, key id, string msg) {
if (channel == DISCOVERY_CHANNEL) {
if (llSubStringIndex(msg, "FIND_CONTROLLER|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
string objectType = llList2String(parts, 1);
string controllerKey = (string)llGetKey(); // Use actual controller object key
llOwnerSay("📡 [Discovery] Request from " + objectType + " (" + (string)id + ")");
llRegionSayTo(id, DISCOVERY_CHANNEL, "CONTROLLER_FOUND|" + controllerKey);
llOwnerSay("📡 [Discovery] Sent controller key to " + objectType + ": " + controllerKey);
}
else if (llSubStringIndex(msg, "CLIENT_CONNECTED|") == 0) {
// Handle client connection notifications
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 3) {
string clientType = llList2String(parts, 1);
key clientKey = (key)llList2String(parts, 2);
// Track connected client
if (llListFindList(connectedClients, [clientKey]) == -1) {
connectedClients += [clientKey];
llOwnerSay("✅ [Discovery] " + clientType + " connected (" + (string)clientKey + ")");
llOwnerSay("🔗 [Discovery] Connected clients: " + (string)llGetListLength(connectedClients));
// Notify main controller of connection
llMessageLinked(LINK_SET, MSG_CLIENT_CONNECTED, clientType + "|" + (string)clientKey, NULL_KEY);
// Check if we should stop broadcasting
if (llGetListLength(connectedClients) >= llGetListLength(expectedClientTypes)) {
llOwnerSay("🎯 [Discovery] All expected clients connected - stopping broadcasts");
discoveryBroadcastActive = FALSE;
llSetTimerEvent(0.0);
// Notify main controller that discovery is complete
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "COMPLETE|" + (string)llGetListLength(connectedClients), NULL_KEY);
} else {
// Update status
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|" + (string)llGetListLength(connectedClients), NULL_KEY);
}
}
}
}
}
// Handle automatic configuration requests from newly rezzed displays
else if ((channel == SCOREBOARD_DATA_CHANNEL || channel == LEADERBOARD_DATA_CHANNEL || channel == DICE_DATA_CHANNEL) &&
llSubStringIndex(msg, "AUTO_CONFIG_REQUEST|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 4) {
string requestType = llList2String(parts, 1);
vector displayPos = (vector)llList2String(parts, 2);
rotation displayRot = (rotation)llList2String(parts, 3);
// Calculate offset and rotation relative to controller
vector controller_current_pos = llGetPos();
rotation controller_current_rot = llGetRot();
vector calculatedOffset = (displayPos - controller_current_pos) / controller_current_rot;
rotation calculatedRotation = displayRot / controller_current_rot;
// Send auto-config response back to the display on the correct channel
integer responseChannel;
if (requestType == "scoreboard") responseChannel = SCOREBOARD_DATA_CHANNEL;
else if (requestType == "leaderboard") responseChannel = LEADERBOARD_DATA_CHANNEL;
else if (requestType == "dice") responseChannel = DICE_DATA_CHANNEL;
else return; // Unknown display type
string responseMsg = "AUTO_CONFIG_RESPONSE|" + requestType + "|" +
(string)calculatedOffset + "|" + (string)calculatedRotation;
llRegionSay(responseChannel, responseMsg);
llOwnerSay("🎆 [Discovery] Auto-configured " + requestType + " display at rez!");
llOwnerSay(" Position: " + (string)displayPos);
llOwnerSay(" Calculated offset: " + (string)calculatedOffset);
llOwnerSay(" Calculated rotation: " + (string)calculatedRotation);
}
}
}
timer() {
if (discoveryBroadcastActive) {
// Broadcast controller availability
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
} else {
llSetTimerEvent(0.0);
}
}
link_message(integer sender, integer num, string str, key id) {
if (num == MSG_DISCOVERY_START) {
// Restart discovery broadcasts
discoveryBroadcastActive = TRUE;
llOwnerSay("📺 [Discovery] Restarting broadcasts");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
}
else if (num == MSG_DISCOVERY_STOP) {
// Stop discovery broadcasts
discoveryBroadcastActive = FALSE;
llSetTimerEvent(0.0);
llOwnerSay("⏹️ [Discovery] Broadcasts stopped");
}
else if (num == -99999 && str == "FULL_RESET") {
// Handle full game reset
connectedClients = [];
discoveryBroadcastActive = TRUE;
llOwnerSay("🔄 [Discovery] Reset - restarting broadcasts");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|0", NULL_KEY);
}
}
}

View file

@ -1,220 +0,0 @@
# Peril Dice - Single Linkset Conversion ✅ COMPLETED
**Implementation Date: August 12, 2025**
**Status: FULLY OPERATIONAL**
## Current Setup
- **Controller**: 1 prim (all game scripts)
- **Scoreboard**: 23 prims (1 root + 1 backboard + 1 actions + 20 player prims)
- **Leaderboard**: ~48 prims (XyzzyText display)
- **Dice Display**: 2 prims
## Linking Process (Correct SL Order)
### Selection Order:
1. **Select Dice Display FIRST** → Gets highest link numbers (end of chain)
2. **Select Leaderboard SECOND** → Gets middle link numbers
3. **Select Scoreboard THIRD** → Gets lower link numbers
4. **Select Controller LAST** → Becomes ROOT PRIM (Link 1)
### Resulting Link Structure:
```
Link 1: Controller (Root) ← Last selected
├── Links 2-24: Scoreboard ← Third selected (23 prims)
│ ├── Link 2: Scoreboard root (script here)
│ ├── Link 3: Background prim
│ ├── Link 4: Actions/Status prim
│ └── Links 5-24: Player prims (20 prims - 2 per player)
├── Links 25-72: Leaderboard ← Second selected (~48 prims)
└── Links 73-74: Dice Display ← First selected (2 prims)
```
## Script Placement & Communication
### Controller Scripts (Link 1 - Root)
**Keep ALL existing game scripts here:**
- `Main_Controller.lsl`
- `Game_Manager.lsl`
- `Bot_Manager.lsl`
- `Controller_Memory.lsl`
- All other controller scripts...
**Communication changes in controller scripts:**
```lsl
// OLD (cross-object):
llRegionSay(SCOREBOARD_CHANNEL_1, "GAME_STATUS|Victory");
llRegionSay(SCOREBOARD_CHANNEL_2, "PLAYER_UPDATE|John|3|uuid");
llRegionSay(SCOREBOARD_CHANNEL_3, "DICE_ROLL|John|5");
// NEW (link messages):
llMessageLinked(2, MSG_GAME_STATUS, "Victory", NULL_KEY); // To scoreboard
llMessageLinked(2, MSG_PLAYER_UPDATE, "John|3|uuid", NULL_KEY); // To scoreboard
llMessageLinked(73, MSG_DICE_ROLL, "John|5", NULL_KEY); // To dice display
llMessageLinked(25, MSG_GAME_WON, "John", NULL_KEY); // To leaderboard
```
### Scoreboard Manager (Link 2)
**Modified `Game_Scoreboard_Manager.lsl`:**
**Critical Update Required - Prim Index Mapping:**
```lsl
// OLD prim indices (when scoreboard was standalone):
integer BACKGROUND_PRIM = 2; // Was link 2
integer ACTIONS_PRIM = 3; // Was link 3
integer FIRST_PLAYER_PRIM = 4; // Was link 4
// integer LAST_PLAYER_PRIM = 23; // Was link 23 (last of 20 player prims)
// NEW prim indices (when scoreboard is part of larger linkset):
integer BACKGROUND_PRIM = 3; // Now link 3 (2+1)
integer ACTIONS_PRIM = 4; // Now link 4 (3+1)
integer FIRST_PLAYER_PRIM = 5; // Now link 5 (4+1)
integer LAST_PLAYER_PRIM = 24; // Now link 24 (5+19, last of 20 player prims)
// Player prims now span links 5-24 instead of 4-23
```
```lsl
default {
state_entry() {
// No need for setupPlayerGrid() - prims already positioned from linking
loadLeaderboardData();
llOwnerSay("📊 Scoreboard ready - managing prims " + (string)FIRST_PLAYER_PRIM + "-" + (string)LAST_PLAYER_PRIM);
}
link_message(integer sender, integer num, string str, key id) {
if (sender != 1) return; // Only listen to controller
if (num == MSG_GAME_STATUS) {
updateActionsPrim(str);
}
else if (num == MSG_PLAYER_UPDATE) {
list parts = llParseString2List(str, ["|"], []);
string name = llList2String(parts, 0);
integer lives = (integer)llList2String(parts, 1);
key uuid = (key)llList2String(parts, 2);
updatePlayerDisplay(name, lives, uuid);
}
else if (num == MSG_CLEAR_GAME) {
clearAllPlayers();
updateActionsPrim("Title");
}
}
}
```
### Leaderboard Manager (Link 25)
**Modified `Leaderboard_Communication_Clean.lsl`:**
```lsl
default {
state_entry() {
// Initialize XyzzyText on links 25-72
initializeLeaderboard();
}
link_message(integer sender, integer num, string str, key id) {
if (sender != 1) return; // Only from controller
if (num == MSG_GAME_WON) {
handleGameWon(str);
}
else if (num == MSG_GAME_LOST) {
handleGameLost(str);
}
else if (num == MSG_RESET_LEADERBOARD) {
resetLeaderboard();
}
}
}
```
### Dice Display Manager (Link 71)
**Modified `XyzzyText_Dice_Bridge.lsl`:**
```lsl
default {
state_entry() {
// Initialize dice display on links 71-72
initializeDiceDisplay();
}
link_message(integer sender, integer num, string str, key id) {
if (sender != 1) return; // Only from controller
if (num == MSG_DICE_ROLL) {
list parts = llParseString2List(str, ["|"], []);
string player = llList2String(parts, 0);
integer value = (integer)llList2String(parts, 1);
showDiceRoll(player, value);
}
else if (num == MSG_CLEAR_DICE) {
clearDiceDisplay();
}
}
}
```
## Message Constants (Add to ALL scripts)
```lsl
// Scoreboard messages (to link 2)
integer MSG_GAME_STATUS = 3001;
integer MSG_PLAYER_UPDATE = 3002;
integer MSG_CLEAR_GAME = 3003;
// Leaderboard messages (to link 25)
integer MSG_GAME_WON = 3010;
integer MSG_GAME_LOST = 3011;
integer MSG_RESET_LEADERBOARD = 3012;
// Dice messages (to link 73)
integer MSG_DICE_ROLL = 3020;
integer MSG_CLEAR_DICE = 3021;
```
## Code Removal (From ALL display scripts)
**Remove these completely:**
- ✂️ Controller discovery system (`startControllerDiscovery()`, etc.)
- ✂️ Dynamic channel calculations (`calculateChannel()`, etc.)
- ✂️ All `llListen()` and `listen()` handlers
- ✂️ Channel variables (`SCOREBOARD_CHANNEL`, etc.)
- ✂️ Discovery timers and retry logic
- ✂️ `llRegionSay()` responses
## Benefits Summary
✅ **50%+ Performance Improvement**: Link messages vs region chat
✅ **Zero Channel Conflicts**: No channel management needed
✅ **Single Object Deployment**: One object to rez/position
✅ **Bulletproof Reliability**: No discovery failures
✅ **Easier Maintenance**: All components linked together
✅ **Reduced Memory Usage**: Remove discovery & channel code
✅ **Simplified Setup**: No positioning multiple objects
## Implementation Results ✅ COMPLETED
**Final Testing Results:**
- ✅ Game starts with multiple players
- ✅ Scoreboard shows player hearts correctly
- ✅ Leaderboard updates with wins/losses
- ✅ Dice display shows rolls correctly
- ✅ Status messages appear on scoreboard
- ✅ Game reset clears all displays
- ✅ All floater HUDs still work
- ✅ Bot players function normally
- ✅ Dice type synchronization fixed
- ✅ Win condition loops resolved
- ✅ Communication architecture stable
## Additional Improvements Implemented
- **Dice Type Synchronization**: Fixed race conditions between Game Manager and Roll Module
- **Targeted Calculator Responses**: Calculator now responds only to requesting module
- **Win Loop Prevention**: Game properly stops when only one player remains
- **Module Independence**: Clean separation of responsibilities between components
- **Enhanced Game Flow**: Improved round completion detection and continuation logic
## Final Timeline (Actual)
- **Architecture Planning**: 4 hours
- **Script modifications**: 6 hours (expanded scope)
- **Physical linking**: 30 minutes
- **Testing & debugging**: 3 hours
- **Bug fixes & refinements**: 4 hours
- **Total**: ~17.5 hours
**Result**: The game system is now significantly more robust, professional, and reliable than the original multi-object design!

View file

@ -1,108 +0,0 @@
# PERIL DICE GAME - REMAINING FIXES TODO
## ✅ COMPLETED
- [x] **Listen Handle Leaks** - Fixed in all scripts with proper cleanup and management
- [x] **Timer Event Conflicts** - Fixed in Main.lsl with unified timer system using TIMER_IDLE, TIMER_STATUS, TIMER_TIMEOUT states
- [x] **Race Condition in Dialog Recovery** - Fixed in Owner and Player Dialog Handler.lsl with request ID tracking, timeout validation, and stale response detection
- [x] **Bot Validation Logic Gaps** - Fixed in Bot Manager.lsl with improved algorithm, availability calculation, graceful degradation, and memory monitoring
- [x] **Memory Usage Monitoring** - Implemented in Main.lsl with memory checks at critical points, warnings at 80%, emergency cleanup at 90%, and owner-accessible memory stats
- [x] **Channel Number Conflicts** - Implemented dynamic channel configuration in Main.lsl with owner-unique channels, collision avoidance, and backward compatibility
## ✅ ALL CRITICAL FIXES COMPLETED
### 📡 LATEST ADDITION (v2.6.0) - Advanced Channel Management & Debug Cleanup ✅ **COMPLETED**
**Files:** ALL major scripts (`Main.lsl`, `Bot_Manager.lsl`, `Floater_Manager.lsl`, etc.)
**Was:** Potential channel conflicts between multiple instances + verbose debug output spam
**Fixed:**
- **Enhanced Dynamic Channel System**: Implemented sophisticated per-instance channel calculation
- Uses combined hash of owner key + object key for complete uniqueness
- Prevents interference between multiple game tables owned by same person
- MD5-based hash generation creates 0-255 range distribution across all channel ranges
- Each component gets isolated channel space (~-78000 to ~-86000 ranges)
- **Professional Debug Output Cleanup**: Removed verbose debug messages causing console spam
- Main Controller: Eliminated touch event and dice result debug logging
- Floater Manager: Removed picks data, cleanup confirmations, and sync debug messages
- Preserved essential error reporting while eliminating noise
- Professional-grade logging suitable for production deployment
- **Multi-Instance Production Ready**: Game system now supports multiple concurrent deployments
- **Channel Debugging Tools**: Detailed channel assignments reported to owner on startup
---
### 5. ~~Memory Usage Concerns~~ ✅ **COMPLETED**
**Files:** `Main.lsl`
**Was:** Large lists stored without memory monitoring
**Fixed:**
- Added `llGetUsedMemory()` checks in critical functions (`updateHelpers`, `player_registration`)
- Memory warnings at 80% usage with 60-second intervals to prevent spam
- Emergency cleanup at 90% usage with list optimization and garbage collection
- Owner-accessible "Memory Stats" command in dialog system
- Comprehensive memory reporting showing usage by data structure
---
### 6. ~~Channel Number Conflicts~~ ✅ **COMPLETED** ⭐ **MULTI-INSTANCE SAFE**
**Files:** `Main.lsl`, `ChannelConfig.lsl`, `Bot Manager.lsl`, `Roll Confetti Module.lsl`, `Game Manager.lsl`, `Owner and Player Dialog Handler.lsl`, `Number Picker Dialog Handler.lsl`, `Helper.lsl`, `Game_Scoreboard_Manager.lsl`
**Was:** Hard-coded channels could conflict with other LSL objects AND multiple instances by same owner would interfere
**Fixed:**
- Implemented **ENHANCED** dynamic channel calculation using BOTH owner key AND object key hashing for complete per-instance uniqueness across ALL scripts
- Created centralized channel configuration system in ChannelConfig.lsl
- Updated Main.lsl to use dynamic channels for all communications:
- Sync, Dialog, Roll Dialog, Bot Commands (calculated ranges ~-77100 to ~-77500)
- Scoreboard channels (3 separate channels for different message types)
- Floater base channels with dynamic indexing
- Updated ALL supporting scripts with dynamic channel configuration:
- Bot Manager: Dynamic bot command channel (~-77500 range)
- Roll Confetti Module: Dynamic scoreboard channels for status/dice display
- Owner/Player Dialog Handler: Dynamic main dialog channel (~-77400 range)
- Number Picker Dialog Handler: Dynamic number pick channel (~-77200 range)
- Game_Scoreboard_Manager: All 3 scoreboard channels dynamically configured
- Helper: Removed hardcoded dialog usage, delegated to proper handlers
- Added collision avoidance through owner-specific hash offsets
- Maintained backward compatibility with legacy channel variables where needed
- Added comprehensive channel reporting for debugging across all scripts
- **COMPREHENSIVE SCAN COMPLETED**: All hardcoded channels eliminated from critical scripts
---
## 🎯 IMPLEMENTATION ORDER
### Phase 1: Critical Functionality (✅ COMPLETED)
1. ✅ **Timer Event Conflicts** - COMPLETED
2. ✅ **Dialog Race Conditions** - COMPLETED
### Phase 2: Edge Case Handling (✅ COMPLETED)
3. ✅ **Bot Validation Logic** - COMPLETED
4. ✅ **Memory Usage Monitoring** - COMPLETED
### Phase 3: Compatibility (✅ COMPLETED)
5. ✅ **Channel Number Conflicts** - COMPLETED
---
## 📋 TESTING CHECKLIST
After each fix, test:
- [ ] Game starts properly with 2-10 players
- [ ] Status messages display and clear correctly
- [ ] Dialogs show to correct players
- [ ] Bots pick appropriate numbers of picks
- [ ] Memory usage stays reasonable during long games
- [ ] No channel conflicts with other objects
- [ ] Game resets work properly
- [ ] All error conditions handled gracefully
---
## 🚨 NOTES
- **Backup all scripts before making changes**
- **Test each fix individually before moving to next**
- **Monitor error logs during testing**
- **Check memory usage with `llGetUsedMemory()` during testing**
- **Test with maximum players (10) to stress test**
---
*Created: 2025-01-08*
*Status: Ready for implementation*

View file

@ -1,412 +0,0 @@
// Peril Game Display Follower Script - Config Version
// This script reads position settings from a "config" notecard
// Change DISPLAY_TYPE below to match your display object
string DISPLAY_TYPE = "scoreboard"; // Change to: "scoreboard", "leaderboard", or "dice"
// =============================================================================
// CONTROLLER DISCOVERY SYSTEM
// =============================================================================
// Fixed discovery channel for finding controller
integer DISCOVERY_CHANNEL = -77000;
// Base channel offset - should match Main.lsl
integer CHANNEL_BASE = -77000;
// Controller discovery state
key CONTROLLER_KEY = NULL_KEY;
integer DISCOVERY_ATTEMPTS = 0;
integer MAX_DISCOVERY_ATTEMPTS = 10;
integer AUTO_CONFIG_ATTEMPTS = 0;
integer MAX_AUTO_CONFIG_ATTEMPTS = 5;
integer INITIALIZED = FALSE; // Flag to prevent multiple initializations
// Calculate channels dynamically using controller key for consistency
integer calculateChannelWithController(integer offset, key controllerKey) {
// Use owner's key AND CONTROLLER's object key to ensure all objects use same channels
// This should match the calculateChannel method in Controller_Discovery.lsl
string ownerStr = (string)llGetOwner();
string controllerObjStr = (string)controllerKey; // This is the controller object key
string combinedStr = ownerStr + controllerObjStr;
// Create a more unique hash using both keys
string hashStr = llMD5String(combinedStr, 0);
integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0));
integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1));
integer combinedHash = hash1 * 16 + hash2; // Creates 0-255 range
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
// Legacy function for backward compatibility (uses own key)
integer calculateChannel(integer offset) {
string ownerStr = (string)llGetOwner();
string objectStr = (string)llGetKey();
string combinedStr = ownerStr + objectStr;
string hashStr = llMD5String(combinedStr, 0);
integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0));
integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1));
integer combinedHash = hash1 * 16 + hash2;
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
// Channel mapping for different display types (with controller discovery)
integer getDataChannelForType(string displayType) {
if (CONTROLLER_KEY != NULL_KEY) {
// Use controller-based channels for consistency
if (displayType == "scoreboard") return calculateChannelWithController(6, CONTROLLER_KEY); // SCOREBOARD_CHANNEL_1
else if (displayType == "leaderboard") return calculateChannelWithController(7, CONTROLLER_KEY); // SCOREBOARD_CHANNEL_2
else if (displayType == "dice") return calculateChannelWithController(8, CONTROLLER_KEY); // SCOREBOARD_CHANNEL_3
else return calculateChannelWithController(1, CONTROLLER_KEY); // Default to sync channel
} else {
// Fallback to legacy calculation during discovery
if (displayType == "scoreboard") return calculateChannel(6); // SCOREBOARD_CHANNEL_1
else if (displayType == "leaderboard") return calculateChannel(7); // SCOREBOARD_CHANNEL_2
else if (displayType == "dice") return calculateChannel(8); // SCOREBOARD_CHANNEL_3
else return calculateChannel(1); // Default to sync channel
}
}
// Controller discovery function
startControllerDiscovery() {
llOwnerSay("📡 [" + DISPLAY_TYPE + "] Starting controller discovery...");
// Listen on discovery channel
llListen(DISCOVERY_CHANNEL, "", "", "");
// Broadcast discovery request
llRegionSay(DISCOVERY_CHANNEL, "FIND_CONTROLLER|" + DISPLAY_TYPE);
// Reset discovery attempts
DISCOVERY_ATTEMPTS = 0;
// Set timer for retry if needed
llSetTimerEvent(5.0);
}
// These will be loaded from config notecard
vector MY_OFFSET;
rotation MY_ROTATION;
integer MY_CHANNEL;
integer MY_DATA_CHANNEL;
// Controller tracking
key master_key;
vector last_master_pos;
rotation last_master_rot;
// Config reading
integer config_line = 0;
key config_query;
// Function to initialize channels and config after controller discovery
initializeAfterDiscovery() {
if (CONTROLLER_KEY != NULL_KEY) {
llOwnerSay("🆗 [" + DISPLAY_TYPE + "] Controller found! Initializing channels...");
} else {
llOwnerSay("⚠️ [" + DISPLAY_TYPE + "] Initializing in legacy mode (no controller found)...");
}
// Update channels with controller key (or legacy channels if NULL)
if (CONTROLLER_KEY != NULL_KEY) {
MY_CHANNEL = calculateChannelWithController(1, CONTROLLER_KEY); // Position sync channel
MY_DATA_CHANNEL = getDataChannelForType(DISPLAY_TYPE);
} else {
// Use legacy channels when no controller found
MY_CHANNEL = calculateChannel(1); // Position sync channel
MY_DATA_CHANNEL = getDataChannelForType(DISPLAY_TYPE); // This will use legacy calculation
}
llOwnerSay("🔧 [" + DISPLAY_TYPE + "] Dynamic channels initialized:");
llOwnerSay(" Position sync: " + (string)MY_CHANNEL);
llOwnerSay(" Data channel: " + (string)MY_DATA_CHANNEL);
// Start reading config notecard or use auto-detection
if (llGetInventoryType("config") == INVENTORY_NOTECARD) {
llOwnerSay("📜 Reading config notecard...");
config_query = llGetNotecardLine("config", 0);
config_line = 0;
} else if (CONTROLLER_KEY != NULL_KEY) {
// Only try auto-detection if we have a controller
llOwnerSay("⚠️ No 'config' notecard found - using auto-detection mode!");
llOwnerSay("📍 Looking for controller to auto-configure position...");
// Set up listeners for auto-detection with correct channels
llListen(MY_CHANNEL, "", "", ""); // Position sync channel
llListen(MY_DATA_CHANNEL, "", "", ""); // Data/scan channel
// Request auto-configuration from controller
llRegionSay(MY_DATA_CHANNEL, "AUTO_CONFIG_REQUEST|" + DISPLAY_TYPE + "|" +
(string)llGetPos() + "|" + (string)llGetRot());
// Reset auto-config retry counter
AUTO_CONFIG_ATTEMPTS = 0;
// Set a timer to retry if no response
llSetTimerEvent(5.0);
} else {
// No controller and no config - just set up basic listeners in legacy mode
llOwnerSay("⚠️ [" + DISPLAY_TYPE + "] No controller or config found - legacy mode with default positioning");
// Set up listeners for legacy mode
llListen(MY_CHANNEL, "", "", ""); // Position sync channel
llListen(MY_DATA_CHANNEL, "", "", ""); // Data/scan channel
// Set default positioning values
MY_OFFSET = <0.0, 0.0, 0.0>;
MY_ROTATION = <0.0, 0.0, 0.0, 1.0>;
llOwnerSay("🔄 [" + DISPLAY_TYPE + "] Ready in legacy mode - listening for position updates");
}
// Mark as initialized to prevent repeated initialization
INITIALIZED = TRUE;
}
default {
state_entry() {
llOwnerSay("🎮 Starting " + DISPLAY_TYPE + " follower - discovering controller...");
// Start controller discovery first
startControllerDiscovery();
}
dataserver(key query_id, string data) {
if (query_id == config_query) {
if (data == EOF) {
// Finished reading config
llOwnerSay("🔧 [" + DISPLAY_TYPE + " Follower] Dynamic channels initialized:");
llOwnerSay(" Position sync: " + (string)MY_CHANNEL);
llOwnerSay(" Data channel: " + (string)MY_DATA_CHANNEL);
llOwnerSay(DISPLAY_TYPE + " config loaded. Listening for controller...");
llListen(MY_CHANNEL, "", "", ""); // Position sync channel
llListen(MY_DATA_CHANNEL, "", "", ""); // Data/scan channel
llRegionSay(MY_CHANNEL, "FOLLOWER_READY|" + DISPLAY_TYPE);
} else if (data != "") {
// Parse config line
data = llStringTrim(data, STRING_TRIM);
if (llGetSubString(data, 0, 0) != "#") { // Skip comments
integer eq_pos = llSubStringIndex(data, "=");
if (eq_pos != -1) {
string config_key = llStringTrim(llGetSubString(data, 0, eq_pos - 1), STRING_TRIM);
string value = llStringTrim(llGetSubString(data, eq_pos + 1, -1), STRING_TRIM);
// Load values for this display type
if (config_key == DISPLAY_TYPE + "_offset") {
MY_OFFSET = (vector)value;
} else if (config_key == DISPLAY_TYPE + "_rotation") {
MY_ROTATION = (rotation)value;
} else if (config_key == DISPLAY_TYPE + "_channel") {
if (value == "DYNAMIC") {
MY_CHANNEL = calculateChannel(1); // Position sync channel
} else {
MY_CHANNEL = (integer)value; // Legacy hardcoded channel
}
} else if (config_key == DISPLAY_TYPE + "_data_channel") {
if (llSubStringIndex(value, "DYNAMIC") == 0) {
MY_DATA_CHANNEL = getDataChannelForType(DISPLAY_TYPE);
} else {
MY_DATA_CHANNEL = (integer)value; // Legacy hardcoded channel
}
}
}
}
// Read next line
config_line++;
config_query = llGetNotecardLine("config", config_line);
} else {
// Read next line even if this one is empty
config_line++;
config_query = llGetNotecardLine("config", config_line);
}
}
}
listen(integer channel, string name, key id, string msg) {
// Handle controller discovery responses
if (channel == DISCOVERY_CHANNEL) {
if (llSubStringIndex(msg, "CONTROLLER_FOUND|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
CONTROLLER_KEY = (key)llList2String(parts, 1);
llOwnerSay("✅ [" + DISPLAY_TYPE + "] Controller discovered: " + (string)CONTROLLER_KEY);
// Cancel discovery timer
llSetTimerEvent(0.0);
// Initialize with controller key
initializeAfterDiscovery();
return;
}
else if (llSubStringIndex(msg, "CONTROLLER_AVAILABLE|") == 0) {
// Handle broadcast availability messages - but only if not already initialized
if (INITIALIZED) {
// Already initialized, ignore repeated availability messages
return;
}
list parts = llParseString2List(msg, ["|"], []);
CONTROLLER_KEY = (key)llList2String(parts, 1);
llOwnerSay("✅ [" + DISPLAY_TYPE + "] Controller available: " + (string)CONTROLLER_KEY);
// Cancel discovery timer
llSetTimerEvent(0.0);
// Initialize with controller key
initializeAfterDiscovery();
return;
}
}
if (channel == MY_CHANNEL) {
if (llSubStringIndex(msg, "CONTROLLER_MOVE|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
master_key = (key)llList2String(parts, 1);
vector master_pos = (vector)llList2String(parts, 2);
rotation master_rot = (rotation)llList2String(parts, 3);
// Calculate new position
vector new_pos = master_pos + (MY_OFFSET * master_rot);
rotation new_rot = MY_ROTATION * master_rot;
llSetPos(new_pos);
llSetRot(new_rot);
last_master_pos = master_pos;
last_master_rot = master_rot;
}
} else if (channel == MY_DATA_CHANNEL) {
if (llSubStringIndex(msg, "POSITION_SCAN|") == 0) {
// Respond to position scan for reset tool
llRegionSay(MY_DATA_CHANNEL, "POSITION_RESPONSE|" + DISPLAY_TYPE + "|" +
(string)llGetPos() + "|" + (string)llGetRot());
}
else if (llSubStringIndex(msg, "POSITION_UPDATE|") == 0) {
// Handle automatic position updates from integrated reset tool
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 4) {
string updateType = llList2String(parts, 1);
if (updateType == DISPLAY_TYPE) {
vector newOffset = (vector)llList2String(parts, 2);
rotation newRotation = (rotation)llList2String(parts, 3);
// Update our stored offset and rotation
MY_OFFSET = newOffset;
MY_ROTATION = newRotation;
llOwnerSay("✨ " + DISPLAY_TYPE + " position updated automatically!");
llOwnerSay("New offset: " + (string)MY_OFFSET);
llOwnerSay("New rotation: " + (string)MY_ROTATION);
// If we have a master position, update our position immediately
if (master_key != NULL_KEY) {
vector new_pos = last_master_pos + (MY_OFFSET * last_master_rot);
rotation new_rot = MY_ROTATION * last_master_rot;
llSetPos(new_pos);
llSetRot(new_rot);
llOwnerSay("🎥 Moved to new position!");
}
// Cancel retry timer if we got a successful update
llSetTimerEvent(0.0);
}
}
}
else if (llSubStringIndex(msg, "AUTO_CONFIG_RESPONSE|") == 0) {
// Handle automatic configuration response from controller
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 4) {
string responseType = llList2String(parts, 1);
if (responseType == DISPLAY_TYPE) {
vector autoOffset = (vector)llList2String(parts, 2);
rotation autoRotation = (rotation)llList2String(parts, 3);
// Set our position values from auto-detection
MY_OFFSET = autoOffset;
MY_ROTATION = autoRotation;
llOwnerSay("🎆 " + DISPLAY_TYPE + " auto-configured successfully!");
llOwnerSay("Auto-detected offset: " + (string)MY_OFFSET);
llOwnerSay("Auto-detected rotation: " + (string)MY_ROTATION);
llOwnerSay("🟢 Ready and listening for controller movement...");
// Cancel retry timer
llSetTimerEvent(0.0);
// Announce readiness
llRegionSay(MY_CHANNEL, "FOLLOWER_READY|" + DISPLAY_TYPE);
}
}
}
}
}
timer() {
if (CONTROLLER_KEY == NULL_KEY) {
// Still in controller discovery phase
DISCOVERY_ATTEMPTS++;
if (DISCOVERY_ATTEMPTS <= MAX_DISCOVERY_ATTEMPTS) {
llOwnerSay("🔄 [" + DISPLAY_TYPE + "] Retrying controller discovery (attempt " +
(string)DISCOVERY_ATTEMPTS + "/" + (string)MAX_DISCOVERY_ATTEMPTS + ")...");
// Retry controller discovery
llRegionSay(DISCOVERY_CHANNEL, "FIND_CONTROLLER|" + DISPLAY_TYPE);
// Set timer for next retry (exponential backoff)
float retryDelay = 5.0 * DISCOVERY_ATTEMPTS; // 5s, 10s, 15s, etc.
llSetTimerEvent(retryDelay);
} else {
llOwnerSay("❌ [" + DISPLAY_TYPE + "] Controller discovery failed after " +
(string)MAX_DISCOVERY_ATTEMPTS + " attempts");
llOwnerSay(" Operating in legacy mode with owner-based channels");
// Stop timer and initialize with legacy channels
llSetTimerEvent(0.0);
initializeAfterDiscovery(); // This will use legacy channels since CONTROLLER_KEY is NULL
}
} else {
// Controller found, retry auto-configuration request with limit
AUTO_CONFIG_ATTEMPTS++;
if (AUTO_CONFIG_ATTEMPTS <= MAX_AUTO_CONFIG_ATTEMPTS) {
llOwnerSay("🔄 [" + DISPLAY_TYPE + "] No auto-config response - retrying (" +
(string)AUTO_CONFIG_ATTEMPTS + "/" + (string)MAX_AUTO_CONFIG_ATTEMPTS + ")...");
if (MY_DATA_CHANNEL != 0) {
llRegionSay(MY_DATA_CHANNEL, "AUTO_CONFIG_REQUEST|" + DISPLAY_TYPE + "|" +
(string)llGetPos() + "|" + (string)llGetRot());
} else {
llOwnerSay("⚠️ [" + DISPLAY_TYPE + "] Data channel not initialized yet!");
}
// Set timer for next retry (10 seconds)
llSetTimerEvent(10.0);
} else {
llOwnerSay("❌ [" + DISPLAY_TYPE + "] Auto-config failed after " + (string)MAX_AUTO_CONFIG_ATTEMPTS + " attempts");
llOwnerSay("🔄 [" + DISPLAY_TYPE + "] Using default position - ready for manual configuration");
// Stop timer and set default positioning
llSetTimerEvent(0.0);
// Set default positioning values
MY_OFFSET = <0.0, 0.0, 0.0>;
MY_ROTATION = <0.0, 0.0, 0.0, 1.0>;
// Announce readiness with default positioning
llRegionSay(MY_CHANNEL, "FOLLOWER_READY|" + DISPLAY_TYPE);
llOwnerSay("🛫 [" + DISPLAY_TYPE + "] Ready and listening for controller movement (default position)");
}
}
}
on_rez(integer start_param) {
llResetScript();
}
}

View file

@ -1,75 +0,0 @@
// Position Controller - Master Object
// Add this to your controller object
// When it moves, all follower objects will maintain relative positions
// =============================================================================
// DYNAMIC CHANNEL CONFIGURATION
// =============================================================================
// Base channel offset - should match Main.lsl
integer CHANNEL_BASE = -77000;
// Calculate channels dynamically to avoid hardcoded conflicts
integer calculateChannel(integer offset) {
// Use BOTH owner's key AND object's key to make channels unique per game instance
// This prevents interference when same owner has multiple game tables
string ownerStr = (string)llGetOwner();
string objectStr = (string)llGetKey();
string combinedStr = ownerStr + objectStr;
// Create a more unique hash using both keys
string hashStr = llMD5String(combinedStr, 0);
integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0));
integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1));
integer combinedHash = hash1 * 16 + hash2; // Creates 0-255 range
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
integer SYNC_CHANNEL;
vector lastPos;
rotation lastRot;
default {
state_entry() {
// Initialize dynamic channels
SYNC_CHANNEL = calculateChannel(1); // ~-78000 range
llOwnerSay("🔧 [Position Controller] Dynamic channel initialized: " + (string)SYNC_CHANNEL);
llOwnerSay("Position Controller active - other objects will follow when this moves");
lastPos = llGetPos();
lastRot = llGetRot();
llSetTimerEvent(0.5); // Check position every 0.5 seconds
}
moving_start() {
// Optional: notify when starting to move
llOwnerSay("Moving game system...");
}
moving_end() {
// When movement stops, broadcast new position
vector pos = llGetPos();
rotation rot = llGetRot();
llRegionSay(SYNC_CHANNEL, "CONTROLLER_MOVE|" + (string)llGetKey() + "|" + (string)pos + "|" + (string)rot);
llOwnerSay("Game system repositioned");
}
timer() {
vector currentPos = llGetPos();
rotation currentRot = llGetRot();
// Check if position or rotation has changed significantly
if (llVecDist(currentPos, lastPos) > 0.01 ||
llAngleBetween(currentRot, lastRot) > 0.01) {
lastPos = currentPos;
lastRot = currentRot;
llRegionSay(SYNC_CHANNEL, "CONTROLLER_MOVE|" + (string)llGetKey() + "|" + (string)currentPos + "|" + (string)currentRot);
}
}
on_rez(integer start_param) {
llResetScript();
}
}

View file

@ -1,54 +0,0 @@
PERIL GAME POSITION RESET KIT
=============================
If your leaderboard, scoreboard, or dice display gets knocked out of position,
use this simple tool to recalibrate them.
WHAT'S INCLUDED:
- Position_Reset_Tool.lsl (temporary calibration script)
- config (updated configuration notecard)
HOW TO RECALIBRATE POSITIONS:
============================
1. POSITION YOUR DISPLAYS
- Move each display object to where you want it positioned
- Make sure they're all within 20 meters of the controller
2. DROP RESET TOOL IN CONTROLLER
- Put Position_Reset_Tool.lsl into your controller object
3. SCAN POSITIONS
- Click the controller object
- It will scan and give you new config values like:
scoreboard_offset=<1.234, 5.678, 0.000>
leaderboard_offset=<-2.345, 0.000, 1.500>
dice_offset=<3.456, 0.000, 0.000>
4. UPDATE CONFIG NOTECARDS
- Edit the "config" notecard in each display object
- Replace the old offset values with the new ones
- Save the notecard
5. CLEAN UP
- Remove Position_Reset_Tool.lsl from your controller
- Your displays will automatically move to the new positions!
EXAMPLE:
=======
If the reset tool says:
scoreboard_offset=<0.500, 2.800, 0.100>
Then in your scoreboard's config notecard, change:
scoreboard_offset=<0.000, 3.000, 0.000>
to:
scoreboard_offset=<0.500, 2.800, 0.100>
TROUBLESHOOTING:
===============
- Make sure displays are within 20 meters during scanning
- The config notecard must be named exactly "config" (lowercase)
- After editing config, the objects should move automatically
- If they don't move, try resetting the display objects
That's it! No script editing required - just update the config values!

View file

@ -1,105 +0,0 @@
// Position Reset Tool for Peril Game Display Objects
// Drop this script in your CONTROLLER object and click it
// This will generate config values for your notecard
// =============================================================================
// DYNAMIC CHANNEL CONFIGURATION
// =============================================================================
// Base channel offset - should match Main.lsl
integer CHANNEL_BASE = -77000;
// Calculate channels dynamically to avoid hardcoded conflicts
integer calculateChannel(integer offset) {
// Use BOTH owner's key AND object's key to make channels unique per game instance
// This prevents interference when same owner has multiple game tables
string ownerStr = (string)llGetOwner();
string objectStr = (string)llGetKey();
string combinedStr = ownerStr + objectStr;
// Create a more unique hash using both keys
string hashStr = llMD5String(combinedStr, 0);
integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0));
integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1));
integer combinedHash = hash1 * 16 + hash2; // Creates 0-255 range
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
key controller_key;
vector controller_pos;
rotation controller_rot;
// Dynamic channels
integer SCOREBOARD_DATA_CHANNEL;
integer LEADERBOARD_DATA_CHANNEL;
integer DICE_DATA_CHANNEL;
// Listen handle management
integer scoreboardHandle = -1;
integer leaderboardHandle = -1;
integer diceHandle = -1;
default {
state_entry() {
// Initialize dynamic channels
SCOREBOARD_DATA_CHANNEL = calculateChannel(6); // ~-83000 range
LEADERBOARD_DATA_CHANNEL = calculateChannel(7); // ~-84000 range
DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range
controller_key = llGetKey();
llOwnerSay("🔧 [Position Reset Tool] Dynamic channels initialized:");
llOwnerSay(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
llOwnerSay(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
llOwnerSay(" Dice: " + (string)DICE_DATA_CHANNEL);
llOwnerSay("Position Reset Tool ready - Click to scan for display objects");
// Clean up any existing listeners
if (scoreboardHandle != -1) llListenRemove(scoreboardHandle);
if (leaderboardHandle != -1) llListenRemove(leaderboardHandle);
if (diceHandle != -1) llListenRemove(diceHandle);
// Set up managed listeners with dynamic channels
scoreboardHandle = llListen(SCOREBOARD_DATA_CHANNEL, "", "", "");
leaderboardHandle = llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
diceHandle = llListen(DICE_DATA_CHANNEL, "", "", "");
}
touch_start(integer total_number) {
if (llDetectedKey(0) == llGetOwner()) {
controller_pos = llGetPos();
controller_rot = llGetRot();
llOwnerSay("Scanning for display objects within 20 meters...");
// Send scan messages on all data channels to find display objects
llRegionSay(SCOREBOARD_DATA_CHANNEL, "POSITION_SCAN|" + (string)controller_key);
llRegionSay(LEADERBOARD_DATA_CHANNEL, "POSITION_SCAN|" + (string)controller_key);
llRegionSay(DICE_DATA_CHANNEL, "POSITION_SCAN|" + (string)controller_key);
llSetTimerEvent(3.0); // Wait 3 seconds for responses
}
}
listen(integer channel, string name, key id, string msg) {
if ((channel == SCOREBOARD_DATA_CHANNEL || channel == LEADERBOARD_DATA_CHANNEL || channel == DICE_DATA_CHANNEL) &&
llSubStringIndex(msg, "POSITION_RESPONSE|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
string object_type = llList2String(parts, 1);
vector object_pos = (vector)llList2String(parts, 2);
rotation object_rot = (rotation)llList2String(parts, 3);
// Calculate offset and relative rotation
vector offset = (object_pos - controller_pos) / controller_rot;
rotation rel_rot = object_rot / controller_rot;
llOwnerSay("\n=== " + llToUpper(object_type) + " CONFIG ===");
llOwnerSay(object_type + "_offset=" + (string)offset);
llOwnerSay(object_type + "_rotation=" + (string)rel_rot);
}
}
timer() {
llSetTimerEvent(0.0);
llOwnerSay("\nScan complete. Copy values to your 'config' notecard.");
}
}

0
dice
View file

0
main
View file

View file