Peril_Dice/FINAL_LINKSET_PLAN.md

7.5 KiB

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)
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

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:

// 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

Modified Game_Scoreboard_Manager.lsl:

Critical Update Required - Prim Index Mapping:

// 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
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");
        }
    }
}

Modified Leaderboard_Communication_Clean.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();
        }
    }
}

Modified XyzzyText_Dice_Bridge.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)

// 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!