## [2.4.0] - 2025-08-08
42
CHANGELOG.md
|
|
@ -1,6 +1,48 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to Peril Dice will be documented in this file.
|
All notable changes to Peril Dice will be documented in this file.
|
||||||
|
|
||||||
|
## [2.4.0] - 2025-08-08
|
||||||
|
|
||||||
|
### 📍 Position Management System
|
||||||
|
- **Master-Follower Architecture**: Complete position synchronization system for all game components
|
||||||
|
- **Position Controller Script**: Main controller object manages movement of scoreboard, leaderboard, and displays
|
||||||
|
- **Follower Script Template**: Generic follower script that reads position settings from config notecard
|
||||||
|
- **Config-Based Positioning**: Position offsets and rotations defined in "config" notecard for easy customization
|
||||||
|
- **Automatic Movement Sync**: When controller moves, all linked objects maintain relative positions
|
||||||
|
- **Position Reset Tools**: Built-in recalibration system for repositioning knocked-out components
|
||||||
|
- **Multi-Object Coordination**: Seamless movement of entire game system as single unit
|
||||||
|
|
||||||
|
### 🧹 Production Code Cleanup
|
||||||
|
- **Debug Code Removal**: Complete removal of all debug logging statements and test messages
|
||||||
|
- Removed `llOwnerSay("DEBUG: ...)` calls throughout Main.lsl (lines 147, 161, 479, 517, 523, 527, 874, 1163)
|
||||||
|
- Eliminated development-time diagnostic messages and empty debug blocks
|
||||||
|
- Cleaner, more professional game experience without debug clutter
|
||||||
|
- **Syntax Error Fixes**:
|
||||||
|
- Fixed critical missing closing brace on line 835 in Main.lsl causing compilation failure
|
||||||
|
- Resolved LSL syntax errors that prevented script execution
|
||||||
|
- All scripts now compile cleanly without warnings or errors
|
||||||
|
|
||||||
|
### 🛠️ Development Quality Improvements
|
||||||
|
- **Production Readiness**: Scripts optimized for live deployment without development artifacts
|
||||||
|
- **Performance Enhancement**: Eliminated unnecessary debug output reducing execution overhead
|
||||||
|
- **Memory Optimization**: Reduced script memory footprint by removing debug string operations
|
||||||
|
- **Code Maintainability**: Cleaner codebase structure for future development
|
||||||
|
- **Professional Polish**: Game system ready for production deployment
|
||||||
|
|
||||||
|
### 🔧 Technical Improvements
|
||||||
|
- **LSL Compliance**: All scripts pass Second Life LSL compilation standards
|
||||||
|
- **Execution Efficiency**: Faster script performance without debug processing overhead
|
||||||
|
- **Position Synchronization**: Robust communication system for coordinated object movement
|
||||||
|
- **Config System**: Flexible notecard-based configuration for easy position adjustments
|
||||||
|
- **Error Resolution**: Fixed all syntax issues preventing proper game operation
|
||||||
|
|
||||||
|
### 📚 Documentation Updates
|
||||||
|
- **Position System Guide**: Comprehensive instructions for setting up position management
|
||||||
|
- **Reset Tools Documentation**: Step-by-step guide for recalibrating positions
|
||||||
|
- **Version Tracking**: Updated to semantic versioning 2.4.0
|
||||||
|
- **Change History**: Detailed tracking of all modifications and improvements
|
||||||
|
|
||||||
## [2.3.0] - 2025-08-05
|
## [2.3.0] - 2025-08-05
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
190
CHANGELOG_Notecard.txt
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
PERIL DICE CHANGELOG
|
||||||
|
===================
|
||||||
|
|
||||||
|
All notable changes to Peril Dice are documented in this file.
|
||||||
|
|
||||||
|
[2.4.0] - 2025-08-08
|
||||||
|
====================
|
||||||
|
|
||||||
|
📍 POSITION MANAGEMENT SYSTEM
|
||||||
|
- Master-Follower Architecture: Complete position synchronization system for all game components
|
||||||
|
- Position Controller Script: Main controller object manages movement of scoreboard, leaderboard, and displays
|
||||||
|
- Follower Script Template: Generic follower script that reads position settings from config notecard
|
||||||
|
- Config-Based Positioning: Position offsets and rotations defined in "config" notecard for easy customization
|
||||||
|
- Automatic Movement Sync: When controller moves, all linked objects maintain relative positions
|
||||||
|
- Position Reset Tools: Built-in recalibration system for repositioning knocked-out components
|
||||||
|
- Multi-Object Coordination: Seamless movement of entire game system as single unit
|
||||||
|
|
||||||
|
🧹 PRODUCTION CODE CLEANUP
|
||||||
|
- Debug Code Removal: Complete removal of all debug logging statements and test messages
|
||||||
|
* Removed llOwnerSay("DEBUG: ...) calls throughout Main.lsl (lines 147, 161, 479, 517, 523, 527, 874, 1163)
|
||||||
|
* Eliminated development-time diagnostic messages and empty debug blocks
|
||||||
|
* Cleaner, more professional game experience without debug clutter
|
||||||
|
- Syntax Error Fixes:
|
||||||
|
* Fixed critical missing closing brace on line 835 in Main.lsl causing compilation failure
|
||||||
|
* Resolved LSL syntax errors that prevented script execution
|
||||||
|
* All scripts now compile cleanly without warnings or errors
|
||||||
|
|
||||||
|
🛠️ DEVELOPMENT QUALITY IMPROVEMENTS
|
||||||
|
- Production Readiness: Scripts optimized for live deployment without development artifacts
|
||||||
|
- Performance Enhancement: Eliminated unnecessary debug output reducing execution overhead
|
||||||
|
- Memory Optimization: Reduced script memory footprint by removing debug string operations
|
||||||
|
- Code Maintainability: Cleaner codebase structure for future development
|
||||||
|
- Professional Polish: Game system ready for production deployment
|
||||||
|
|
||||||
|
🔧 TECHNICAL IMPROVEMENTS
|
||||||
|
- LSL Compliance: All scripts pass Second Life LSL compilation standards
|
||||||
|
- Execution Efficiency: Faster script performance without debug processing overhead
|
||||||
|
- Position Synchronization: Robust communication system for coordinated object movement
|
||||||
|
- Config System: Flexible notecard-based configuration for easy position adjustments
|
||||||
|
- Error Resolution: Fixed all syntax issues preventing proper game operation
|
||||||
|
|
||||||
|
📚 DOCUMENTATION UPDATES
|
||||||
|
- Position System Guide: Comprehensive instructions for setting up position management
|
||||||
|
- Reset Tools Documentation: Step-by-step guide for recalibrating positions
|
||||||
|
- Version Tracking: Updated to semantic versioning 2.4.0
|
||||||
|
- Change History: Detailed tracking of all modifications and improvements
|
||||||
|
|
||||||
|
[2.3.0] - 2025-08-05
|
||||||
|
====================
|
||||||
|
|
||||||
|
🏆 VISUAL SCOREBOARD SYSTEM
|
||||||
|
- Dynamic Player Grid: Real-time visual scoreboard displaying all players with profile pictures and heart count
|
||||||
|
- Profile Picture Integration: Automatic fetching of Second Life avatar profile pictures via HTTP requests
|
||||||
|
- Heart Texture Display: Visual life representation using custom heart textures (3, 2, 1, 0 hearts)
|
||||||
|
- Bot Profile Support: Special profile texture for TestBot players
|
||||||
|
- Immediate Updates: Hearts update instantly when lives change, before any dialogs appear
|
||||||
|
|
||||||
|
🎭 ENHANCED STATUS DISPLAY SYSTEM
|
||||||
|
- Visual Status Actions: Large action prim displaying current game status with custom textures
|
||||||
|
- Specific Status Messages:
|
||||||
|
* Direct Hit: When peril player picks their own rolled number
|
||||||
|
* No Shield: When nobody picks the rolled number
|
||||||
|
* Plot Twist: When peril switches to a new player
|
||||||
|
* Elimination: When a player is eliminated
|
||||||
|
* Victory: When someone wins the game
|
||||||
|
* Peril Selected: When a new peril player is chosen
|
||||||
|
* Title: Default/idle state
|
||||||
|
|
||||||
|
⚡ PERFECT STATUS TIMING
|
||||||
|
- Protected Display Time: Each status shows for 8 seconds with automatic clearing
|
||||||
|
- Strategic Delays: 2-second delays after status messages prevent overwriting
|
||||||
|
- Sequential Flow: Elimination → 6.4s delay → Victory → 9s delay → Reset
|
||||||
|
- Immediate Visual Feedback: Hearts and status update instantly, delays protect display time
|
||||||
|
|
||||||
|
🎯 COMPREHENSIVE LEADERBOARD
|
||||||
|
- Persistent Win Tracking: Player victories saved across game sessions
|
||||||
|
- XyzzyText Integration: Three-prim text display system for leaderboard
|
||||||
|
- Automatic Sorting: Top players by win count with formatted display
|
||||||
|
- Separate Reset Options: Game reset vs. leaderboard reset vs. complete reset
|
||||||
|
|
||||||
|
🎮 ENHANCED USER INTERFACE
|
||||||
|
- Categorized Owner Menus: Organized into Game Control, Player Management, Reset Options, etc.
|
||||||
|
- Dialog Recovery: Players can recover lost dialogs by touching the controller
|
||||||
|
- Admin Menu Access: Owner can access admin functions during gameplay
|
||||||
|
- Improved Menu Flow: Streamlined navigation with clear categorization
|
||||||
|
|
||||||
|
🔧 TECHNICAL IMPROVEMENTS
|
||||||
|
- Multi-Object Architecture: Scoreboard and leaderboard as separate linked objects
|
||||||
|
- HTTP Profile Fetching: Robust system for retrieving avatar profile pictures
|
||||||
|
- Texture Caching: Profile pictures cached to avoid repeated HTTP requests
|
||||||
|
- Region-Based Communication: Cross-object messaging using llRegionSay
|
||||||
|
- Elimination Sequence: Visual 0-hearts display before player removal
|
||||||
|
|
||||||
|
🐛 CRITICAL FIXES
|
||||||
|
- Heart Update Timing: Fixed hearts not updating until after next-turn dialog
|
||||||
|
- Victory Overwriting Elimination: Added proper delay between elimination and victory status
|
||||||
|
- Status Message Conflicts: All status messages now have protective delays
|
||||||
|
- Profile Picture Fallbacks: Proper handling of failed HTTP requests
|
||||||
|
- Scoreboard Reset: Fixed scoreboard not clearing when game resets
|
||||||
|
|
||||||
|
[2.2.0] - 2025-08-04
|
||||||
|
====================
|
||||||
|
|
||||||
|
🎭 MAJOR UX/PRESENTATION IMPROVEMENTS
|
||||||
|
- Dramatic Messaging System: Complete overhaul of game messages with thematic styling
|
||||||
|
- Public Announcements: Key game events now visible to all players in public chat
|
||||||
|
- Context-Rich Roll Messages: Dice rolls now show both dice type and result
|
||||||
|
- Split Hit Messages: Different messages for "picked own doom" vs "no shield" scenarios
|
||||||
|
|
||||||
|
🎮 ENHANCED PLAYER EXPERIENCE
|
||||||
|
- Player Join Messages: "💀 PlayerName has entered the deadly game! Welcome to your potential doom! 💀"
|
||||||
|
- Ready State Messages:
|
||||||
|
* "👑 PlayerName steps forward as the game master - automatically ready for the deadly challenge! 👑"
|
||||||
|
* "⚔️ PlayerName steels themselves for the deadly challenge ahead! ⚔️"
|
||||||
|
* "🏃 PlayerName loses their nerve and backs away from the challenge! 🏃"
|
||||||
|
- Pick Announcements: "🎯 PlayerName stakes their life on numbers: 1, 2, 3 🎲"
|
||||||
|
- Game Start: "⚡ ALL PARTICIPANTS READY! THE DEADLY PERIL DICE GAME BEGINS! ⚡"
|
||||||
|
|
||||||
|
🎲 IMPROVED ROLL MESSAGES
|
||||||
|
- Main Roll: "🎲 THE D6 OF FATE! PlayerName rolled a 5 on the 6-sided die! 🎲"
|
||||||
|
- Plot Twist: "⚡ PLOT TWIST! PlayerName picked 5 (rolled on d6) and is now in ULTIMATE PERIL! ⚡"
|
||||||
|
- Direct Hit: "🩸 DIRECT HIT! PlayerName picked their own doom - the d6 landed on 5! 🩸"
|
||||||
|
- No Shield: "🩸 NO SHIELD! Nobody picked 5 - PlayerName takes the hit from the d6! 🩸"
|
||||||
|
|
||||||
|
🔧 CRITICAL BUG FIXES
|
||||||
|
- StatFloat Duplication: Fixed bug where new StatFloat objects were created on every life loss instead of updating existing ones
|
||||||
|
- Duplicate Message Handling: Removed duplicate HUMAN_PICKED handlers that caused double processing
|
||||||
|
- Malformed Entry Warnings: Fixed LSL parsing issues with empty picks entries
|
||||||
|
- Cleanup Optimization: Reduced excessive StatFloat cleanup messages during reset
|
||||||
|
|
||||||
|
🛠️ TECHNICAL IMPROVEMENTS
|
||||||
|
- Debug Message Cleanup: Removed excessive debug output while preserving essential error reporting
|
||||||
|
- Message Handler Optimization: Streamlined communication between scripts
|
||||||
|
- LSL Parsing Fixes: Better handling of trailing empty elements in data parsing
|
||||||
|
- Memory Management: Improved StatFloat lifecycle management
|
||||||
|
|
||||||
|
[2.1.0] - 2025-08-03
|
||||||
|
====================
|
||||||
|
|
||||||
|
✨ 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
DEVELOPMENT NOTES
|
||||||
|
================
|
||||||
|
|
||||||
|
CREDITS
|
||||||
|
- Original Game Design: Noose the Bunny (djmusica28) - Second Life
|
||||||
|
- Automation & Development: Enhanced for automated gameplay
|
||||||
|
- Architecture: Modular LSL system for scalability and maintainability
|
||||||
101
Corrected_Leaderboard_Listener.lsl
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Corrected Leaderboard Listener - uses link messages for XyzzyText v2.1
|
||||||
|
// Place one of these in each leaderboard object alongside XyzzyText v2.1
|
||||||
|
// Configure MY_BANK for each object: 0, 1, 2, or 3
|
||||||
|
|
||||||
|
integer MY_BANK = 0; // Change this for each bank: 0, 1, 2, or 3
|
||||||
|
integer LEADERBOARD_CHANNEL = -12346; // Match the scoreboard manager channel
|
||||||
|
integer DISPLAY_STRING = 204000; // XyzzyText constant
|
||||||
|
|
||||||
|
// Map bank numbers to message prefixes that scoreboard actually sends
|
||||||
|
list MESSAGE_PREFIXES = ["LEFT_TEXT", "MIDDLE_LEFT_TEXT", "MIDDLE_RIGHT_TEXT", "RIGHT_TEXT"];
|
||||||
|
|
||||||
|
default
|
||||||
|
{
|
||||||
|
state_entry()
|
||||||
|
{
|
||||||
|
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
||||||
|
string prefix = llList2String(MESSAGE_PREFIXES, MY_BANK);
|
||||||
|
llOwnerSay("Corrected Leaderboard Listener Bank " + (string)MY_BANK + " ready");
|
||||||
|
llOwnerSay("Listening on channel " + (string)LEADERBOARD_CHANNEL);
|
||||||
|
llOwnerSay("Looking for messages starting with: " + prefix);
|
||||||
|
llOwnerSay("Will send link messages to XyzzyText on channel " + (string)DISPLAY_STRING);
|
||||||
|
|
||||||
|
// Test XyzzyText with simple text first
|
||||||
|
llSetTimerEvent(3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer()
|
||||||
|
{
|
||||||
|
llSetTimerEvent(0.0); // Stop timer
|
||||||
|
llOwnerSay("=== TESTING XyzzyText with simple text ===");
|
||||||
|
|
||||||
|
// Send simple test: "HELLO " (10 chars) to first prim only
|
||||||
|
llMessageLinked(LINK_SET, DISPLAY_STRING, "HELLO ", (key)((string)0));
|
||||||
|
llOwnerSay("Sent test text: 'HELLO ' to XyzzyText bank 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(integer channel, string name, key id, string message)
|
||||||
|
{
|
||||||
|
if (channel != LEADERBOARD_CHANNEL) return;
|
||||||
|
|
||||||
|
// Get the expected message prefix for this bank
|
||||||
|
string expectedPrefix = llList2String(MESSAGE_PREFIXES, MY_BANK);
|
||||||
|
|
||||||
|
// Check if this message is for our bank
|
||||||
|
if (llSubStringIndex(message, expectedPrefix + "|") == 0)
|
||||||
|
{
|
||||||
|
// Extract the text part (everything after the prefix and |)
|
||||||
|
string text = llGetSubString(message, llStringLength(expectedPrefix) + 1, -1);
|
||||||
|
|
||||||
|
llOwnerSay("=== BANK " + (string)MY_BANK + " RECEIVED ===");
|
||||||
|
llOwnerSay("Message: " + message);
|
||||||
|
llOwnerSay("Raw text: '" + text + "'");
|
||||||
|
llOwnerSay("Text length: " + (string)llStringLength(text));
|
||||||
|
|
||||||
|
// XyzzyText v2.1 expects one continuous string without newlines
|
||||||
|
// Split the multi-line text and concatenate into one string
|
||||||
|
list lines = llParseString2List(text, ["\n"], []);
|
||||||
|
string continuousText = "";
|
||||||
|
|
||||||
|
llOwnerSay("Processing " + (string)llGetListLength(lines) + " lines for XyzzyText");
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < llGetListLength(lines); i++) {
|
||||||
|
string line = llList2String(lines, i);
|
||||||
|
|
||||||
|
// Ensure each line is exactly 10 characters (pad or truncate)
|
||||||
|
if (llStringLength(line) > 10) {
|
||||||
|
line = llGetSubString(line, 0, 9); // Truncate to 10 chars
|
||||||
|
} else if (llStringLength(line) < 10) {
|
||||||
|
// Pad with spaces to make exactly 10 characters
|
||||||
|
while (llStringLength(line) < 10) {
|
||||||
|
line += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this 10-character line to the continuous string
|
||||||
|
continuousText += line;
|
||||||
|
llOwnerSay("Line " + (string)i + ": '" + line + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
llOwnerSay("Sending continuous text (" + (string)llStringLength(continuousText) + " chars) to XyzzyText");
|
||||||
|
|
||||||
|
// Send the continuous string to XyzzyText with bank 0
|
||||||
|
llMessageLinked(LINK_SET, DISPLAY_STRING, continuousText, (key)((string)0));
|
||||||
|
llOwnerSay("Sent continuous text to XyzzyText - bank: 0");
|
||||||
|
}
|
||||||
|
else if (message == "CLEAR_LEADERBOARD")
|
||||||
|
{
|
||||||
|
// Clear display for this bank - send blank lines
|
||||||
|
llOwnerSay("=== BANK " + (string)MY_BANK + " CLEARING ===");
|
||||||
|
string clearText = " ";
|
||||||
|
integer i;
|
||||||
|
for (i = 1; i < 12; i++) {
|
||||||
|
clearText += " "; // 12 lines of 10 spaces each = 120 chars total
|
||||||
|
}
|
||||||
|
|
||||||
|
llMessageLinked(LINK_SET, DISPLAY_STRING, clearText, (key)((string)0));
|
||||||
|
llOwnerSay("Sent clear message to XyzzyText");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
Follower_Script_Template.lsl
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
llOwnerSay("Starting " + DISPLAY_TYPE + " follower - reading config...");
|
||||||
|
|
||||||
|
// Start reading config notecard
|
||||||
|
if (llGetInventoryType("config") == INVENTORY_NOTECARD) {
|
||||||
|
config_query = llGetNotecardLine("config", 0);
|
||||||
|
config_line = 0;
|
||||||
|
} else {
|
||||||
|
llOwnerSay("ERROR: No 'config' notecard found!");
|
||||||
|
llOwnerSay("Please add a config notecard to this object.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataserver(key query_id, string data) {
|
||||||
|
if (query_id == config_query) {
|
||||||
|
if (data == EOF) {
|
||||||
|
// Finished reading config
|
||||||
|
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") {
|
||||||
|
MY_CHANNEL = (integer)value;
|
||||||
|
} else if (config_key == DISPLAY_TYPE + "_data_channel") {
|
||||||
|
MY_DATA_CHANNEL = (integer)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on_rez(integer start_param) {
|
||||||
|
llResetScript();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,6 +35,7 @@ list playerProfiles = []; // Maps to profile texture UUIDs
|
||||||
// Leaderboard data (persistent - stored in linkset data)
|
// Leaderboard data (persistent - stored in linkset data)
|
||||||
list leaderboardNames = []; // Loaded from linkset data at startup
|
list leaderboardNames = []; // Loaded from linkset data at startup
|
||||||
list leaderboardWins = []; // Loaded from linkset data at startup
|
list leaderboardWins = []; // Loaded from linkset data at startup
|
||||||
|
list leaderboardLosses = []; // Loaded from linkset data at startup
|
||||||
|
|
||||||
// HTTP tracking
|
// HTTP tracking
|
||||||
list profileRequests = []; // Track which profile requests belong to which player
|
list profileRequests = []; // Track which profile requests belong to which player
|
||||||
|
|
@ -47,17 +48,17 @@ integer profile_key_prefix_length;
|
||||||
integer profile_img_prefix_length;
|
integer profile_img_prefix_length;
|
||||||
|
|
||||||
// Grid layout settings - dual prim approach
|
// Grid layout settings - dual prim approach
|
||||||
float PRIM_WIDTH = 0.2; // Smaller width for individual prims
|
float PRIM_WIDTH = .4; // Smaller width for individual prims
|
||||||
float PRIM_HEIGHT = 0.3;
|
float PRIM_HEIGHT = .4;
|
||||||
float PRIM_DEPTH = 0;
|
float PRIM_DEPTH = 0;
|
||||||
float SPACING_X = 0.7; // Space between player pairs
|
float SPACING_X = 1; // Space between player pairs
|
||||||
float SPACING_Y = 0.25;
|
float SPACING_Y = .5;
|
||||||
float PRIM_OFFSET = 0.16; // Distance between profile and heart prims
|
float PRIM_OFFSET = 0.2; // Distance between profile and heart prims
|
||||||
integer GRID_COLS = 2; // 2 columns, 5 rows for 10 players
|
integer GRID_COLS = 2; // 2 columns, 5 rows for 10 players
|
||||||
|
|
||||||
// Additional prims settings
|
// Additional prims settings
|
||||||
float BACKGROUND_WIDTH = 4.0;
|
float BACKGROUND_WIDTH = 6;
|
||||||
float BACKGROUND_HEIGHT = 3;
|
float BACKGROUND_HEIGHT = 4;
|
||||||
float BACKGROUND_DEPTH = 0.01;
|
float BACKGROUND_DEPTH = 0.01;
|
||||||
float ACTIONS_WIDTH = 4;
|
float ACTIONS_WIDTH = 4;
|
||||||
float ACTIONS_HEIGHT = 0.4;
|
float ACTIONS_HEIGHT = 0.4;
|
||||||
|
|
@ -78,7 +79,6 @@ setupPlayerGrid() {
|
||||||
// Create/position prims: 3 UI prims + 10 player slots (20 prims) = 23 prims total
|
// Create/position prims: 3 UI prims + 10 player slots (20 prims) = 23 prims total
|
||||||
integer totalPrims = llGetNumberOfPrims();
|
integer totalPrims = llGetNumberOfPrims();
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Total prims detected: " + (string)totalPrims);
|
|
||||||
|
|
||||||
// Need 23 prims total: 1 root + 2 UI (background, actions) + 20 player prims (2 per player)
|
// Need 23 prims total: 1 root + 2 UI (background, actions) + 20 player prims (2 per player)
|
||||||
// Note: Leaderboard is now a separate object
|
// Note: Leaderboard is now a separate object
|
||||||
|
|
@ -92,7 +92,6 @@ setupPlayerGrid() {
|
||||||
// Setup UI prims first
|
// Setup UI prims first
|
||||||
setupUIPrims();
|
setupUIPrims();
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Starting to position " + (string)totalPrims + " prims...");
|
|
||||||
|
|
||||||
// Position prims for each player (2 prims per player)
|
// Position prims for each player (2 prims per player)
|
||||||
integer i;
|
integer i;
|
||||||
|
|
@ -101,9 +100,9 @@ setupPlayerGrid() {
|
||||||
integer col = i % GRID_COLS;
|
integer col = i % GRID_COLS;
|
||||||
|
|
||||||
// Calculate base position for this player slot (relative to root prim)
|
// Calculate base position for this player slot (relative to root prim)
|
||||||
vector basePos = <col * SPACING_X - (GRID_COLS-1) * SPACING_X * 1.8,
|
vector basePos = <col * SPACING_X - (GRID_COLS-1) * SPACING_X * 2.2,
|
||||||
0.0,
|
0.0,
|
||||||
row * SPACING_Y - 1.0 * SPACING_Y + -1.5>;
|
row * SPACING_Y - 2.5 * SPACING_Y + -1.5>;
|
||||||
|
|
||||||
// Profile prim (left side)
|
// Profile prim (left side)
|
||||||
vector profilePos = basePos + <-PRIM_OFFSET, 0.0, 0.0>;
|
vector profilePos = basePos + <-PRIM_OFFSET, 0.0, 0.0>;
|
||||||
|
|
@ -115,7 +114,6 @@ setupPlayerGrid() {
|
||||||
|
|
||||||
// Set profile prim properties
|
// Set profile prim properties
|
||||||
if (profilePrimIndex <= totalPrims) {
|
if (profilePrimIndex <= totalPrims) {
|
||||||
llOwnerSay("DEBUG: Setting profile prim " + (string)profilePrimIndex + " for player " + (string)i);
|
|
||||||
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
||||||
PRIM_POSITION, profilePos,
|
PRIM_POSITION, profilePos,
|
||||||
PRIM_ROTATION, <-0.707107, 0.0, 0.0, 0.707107>, // Back to upright portrait rotation
|
PRIM_ROTATION, <-0.707107, 0.0, 0.0, 0.707107>, // Back to upright portrait rotation
|
||||||
|
|
@ -124,8 +122,6 @@ setupPlayerGrid() {
|
||||||
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0, // Fully opaque white
|
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0, // Fully opaque white
|
||||||
PRIM_TEXT, "", <1,1,1>, 0.0
|
PRIM_TEXT, "", <1,1,1>, 0.0
|
||||||
]);
|
]);
|
||||||
} else {
|
|
||||||
llOwnerSay("DEBUG: Skipping profile prim " + (string)profilePrimIndex + " (exceeds total prims)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set hearts prim properties
|
// Set hearts prim properties
|
||||||
|
|
@ -144,8 +140,7 @@ setupPlayerGrid() {
|
||||||
|
|
||||||
setupUIPrims() {
|
setupUIPrims() {
|
||||||
// Background prim - centered behind the player grid
|
// Background prim - centered behind the player grid
|
||||||
vector bgPosition = <0.0, 0.04, -1.6>;
|
vector bgPosition = <0.0, 0.05, -2>;
|
||||||
llOwnerSay("DEBUG: Setting background prim " + (string)BACKGROUND_PRIM + " to position " + (string)bgPosition);
|
|
||||||
llSetLinkPrimitiveParamsFast(BACKGROUND_PRIM, [
|
llSetLinkPrimitiveParamsFast(BACKGROUND_PRIM, [
|
||||||
PRIM_POSITION, bgPosition, // Behind the board and higher up
|
PRIM_POSITION, bgPosition, // Behind the board and higher up
|
||||||
PRIM_ROTATION, <0.0, 0.0, -0.04, 1.0>, // No rotation
|
PRIM_ROTATION, <0.0, 0.0, -0.04, 1.0>, // No rotation
|
||||||
|
|
@ -167,7 +162,6 @@ setupUIPrims() {
|
||||||
|
|
||||||
// Note: Leaderboard is now a separate object
|
// Note: Leaderboard is now a separate object
|
||||||
|
|
||||||
llOwnerSay("DEBUG: UI prims setup complete");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update actions prim with appropriate status texture
|
// Update actions prim with appropriate status texture
|
||||||
|
|
@ -191,7 +185,6 @@ updateActionsPrim(string status) {
|
||||||
} else if (status == "Title") {
|
} else if (status == "Title") {
|
||||||
textureToUse = TEXTURE_TITLE;
|
textureToUse = TEXTURE_TITLE;
|
||||||
} else {
|
} else {
|
||||||
llOwnerSay("DEBUG: Unrecognized status '" + status + "' - keeping current texture");
|
|
||||||
return; // Don't change texture for unrecognized statuses
|
return; // Don't change texture for unrecognized statuses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,86 +197,122 @@ updateActionsPrim(string status) {
|
||||||
// XyzzyText Communication Constants
|
// XyzzyText Communication Constants
|
||||||
integer DISPLAY_STRING = 204000;
|
integer DISPLAY_STRING = 204000;
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
integer DISPLAY_EXTENDED = 204001;
|
||||||
|
integer DICE_CHANNEL = -12347; // Channel for dice roll display
|
||||||
|
|
||||||
// Generate leaderboard text for triple XyzzyText display (15 characters per line split across 3 prims)
|
// Generate leaderboard text for quad XyzzyText display (40 characters per line split across 4 prims)
|
||||||
generateLeaderboardText() {
|
generateLeaderboardText() {
|
||||||
list sortedData = getSortedLeaderboard();
|
list sortedData = getSortedLeaderboard();
|
||||||
integer maxDisplay = llGetListLength(sortedData);
|
integer dataCount = llGetListLength(sortedData);
|
||||||
if (maxDisplay > 11) maxDisplay = 11; // Show top 11 entries to fit in display
|
|
||||||
|
|
||||||
// Triple prim approach - split lines between left, middle, and right prims
|
// Quad prim approach - split lines between left, middle-left, middle-right, and right prims
|
||||||
string leftText = "WINS:";
|
// Header: "LEADERBOARD" (11 chars) + spaces to make 40 total, then split into 10-char chunks
|
||||||
string middleText = " "; // 5 spaces to align with left prim
|
string headerLine = "LEADERBOARD "; // Exactly 40 chars: LEADERBOARD (11) + 29 spaces
|
||||||
string rightText = " "; // 5 spaces to align with left prim
|
|
||||||
|
|
||||||
|
// Split header into 4 parts of 10 characters each
|
||||||
|
string leftText = llGetSubString(headerLine, 0, 9); // Characters 0-9: "LEADERBOAR" (first 10 chars)
|
||||||
|
string middleLeftText = llGetSubString(headerLine, 10, 19); // Characters 10-19: " " (10 spaces)
|
||||||
|
string middleRightText = llGetSubString(headerLine, 20, 29); // Characters 20-29: " " (10 spaces)
|
||||||
|
string rightText = llGetSubString(headerLine, 30, 39); // Characters 30-39: " " (10 spaces)
|
||||||
|
|
||||||
|
// Show positions 1-11, filling in player data where available
|
||||||
integer i;
|
integer i;
|
||||||
for (i = 0; i < maxDisplay; i++) {
|
for (i = 0; i < 11; i++) {
|
||||||
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
string fullLine;
|
||||||
string playerName = llList2String(playerData, 0);
|
|
||||||
string winsText = llList2String(playerData, 1);
|
|
||||||
|
|
||||||
// Create 15-character line: "PlayerNameHere Wins" (player name + padding + wins)
|
if (i < dataCount) {
|
||||||
// Truncate name to max 12 characters to leave room for wins
|
// We have data for this position
|
||||||
if (llStringLength(playerName) > 12) {
|
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
||||||
playerName = llGetSubString(playerName, 0, 11);
|
string playerName = llList2String(playerData, 0);
|
||||||
}
|
string winsText = llList2String(playerData, 1);
|
||||||
|
string lossesText = llList2String(playerData, 2);
|
||||||
|
|
||||||
// Format the complete line (15 chars total)
|
// Format: "1. PlayerName W:xx L:yy"
|
||||||
string fullLine = playerName;
|
// With 40 chars: position(3) + name(27) + stats(10) = 40 total
|
||||||
|
string position;
|
||||||
|
if (i < 9) {
|
||||||
|
position = (string)(i + 1) + ". "; // "1. ", "2. ", etc.
|
||||||
|
} else {
|
||||||
|
position = (string)(i + 1) + "."; // "10.", "11." (no extra space)
|
||||||
|
}
|
||||||
|
|
||||||
// Add padding spaces
|
// Format wins and losses with 2-digit limit
|
||||||
integer padding = 13 - llStringLength(playerName); // Leave 2 chars for wins
|
if (llStringLength(winsText) > 2) {
|
||||||
integer j;
|
winsText = llGetSubString(winsText, 0, 1);
|
||||||
for (j = 0; j < padding; j++) {
|
}
|
||||||
fullLine += " ";
|
if (llStringLength(lossesText) > 2) {
|
||||||
}
|
lossesText = llGetSubString(lossesText, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Add wins (max 2 digits)
|
string statsText = "W:" + winsText + " L:" + lossesText; // e.g., "W:12 L:3"
|
||||||
if (llStringLength(winsText) > 2) {
|
|
||||||
winsText = llGetSubString(winsText, 0, 1); // Truncate to 2 digits
|
|
||||||
}
|
|
||||||
fullLine += winsText;
|
|
||||||
|
|
||||||
// Ensure exactly 15 characters
|
// Calculate max name length (40 - position - stats - padding)
|
||||||
if (llStringLength(fullLine) > 15) {
|
integer maxNameLength = 40 - llStringLength(position) - llStringLength(statsText) - 1; // -1 for space
|
||||||
fullLine = llGetSubString(fullLine, 0, 14);
|
|
||||||
} else if (llStringLength(fullLine) < 15) {
|
if (llStringLength(playerName) > maxNameLength) {
|
||||||
// Pad with spaces if needed
|
playerName = llGetSubString(playerName, 0, maxNameLength - 1);
|
||||||
while (llStringLength(fullLine) < 15) {
|
}
|
||||||
|
|
||||||
|
fullLine = position + playerName;
|
||||||
|
|
||||||
|
// Add padding spaces before stats
|
||||||
|
integer padding = 40 - llStringLength(fullLine) - llStringLength(statsText);
|
||||||
|
integer j;
|
||||||
|
for (j = 0; j < padding && padding > 0; j++) {
|
||||||
|
fullLine += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
fullLine += statsText;
|
||||||
|
} else {
|
||||||
|
// No data for this position, show empty slot
|
||||||
|
string position;
|
||||||
|
if (i < 9) {
|
||||||
|
position = (string)(i + 1) + ". "; // "1. ", "2. ", etc.
|
||||||
|
} else {
|
||||||
|
position = (string)(i + 1) + "."; // "10.", "11." (no extra space)
|
||||||
|
}
|
||||||
|
fullLine = position;
|
||||||
|
// Pad to 40 characters
|
||||||
|
while (llStringLength(fullLine) < 40) {
|
||||||
fullLine += " ";
|
fullLine += " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split the 15-character line: 5 chars each to left, middle, right prims
|
// Ensure exactly 40 characters
|
||||||
string leftPart = llGetSubString(fullLine, 0, 4); // Characters 0-4
|
if (llStringLength(fullLine) > 40) {
|
||||||
string middlePart = llGetSubString(fullLine, 5, 9); // Characters 5-9
|
fullLine = llGetSubString(fullLine, 0, 39);
|
||||||
string rightPart = llGetSubString(fullLine, 10, 14); // Characters 10-14
|
} else if (llStringLength(fullLine) < 40) {
|
||||||
|
while (llStringLength(fullLine) < 40) {
|
||||||
|
fullLine += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the 40-character line: 10 chars each to left, middle-left, middle-right, right prims
|
||||||
|
string leftPart = llGetSubString(fullLine, 0, 9); // Characters 0-9
|
||||||
|
string middleLeftPart = llGetSubString(fullLine, 10, 19); // Characters 10-19
|
||||||
|
string middleRightPart = llGetSubString(fullLine, 20, 29); // Characters 20-29
|
||||||
|
string rightPart = llGetSubString(fullLine, 30, 39); // Characters 30-39
|
||||||
|
|
||||||
leftText += "\n" + leftPart;
|
leftText += "\n" + leftPart;
|
||||||
middleText += "\n" + middlePart;
|
middleLeftText += "\n" + middleLeftPart;
|
||||||
|
middleRightText += "\n" + middleRightPart;
|
||||||
rightText += "\n" + rightPart;
|
rightText += "\n" + rightPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no data, show empty message
|
// We always show exactly 12 lines (header + 11 positions)
|
||||||
if (maxDisplay == 0) {
|
integer totalLines = 12;
|
||||||
leftText = "NO ";
|
integer currentLines = 12; // Header + 11 positions
|
||||||
middleText = "GAMES";
|
|
||||||
rightText = " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send to separate leaderboard object via chat
|
// Send to separate leaderboard object via chat
|
||||||
llOwnerSay("DEBUG: Sending to LEFT XyzzyText: " + leftText);
|
|
||||||
llOwnerSay("DEBUG: Sending to MIDDLE XyzzyText: " + middleText);
|
|
||||||
llOwnerSay("DEBUG: Sending to RIGHT XyzzyText: " + rightText);
|
|
||||||
|
|
||||||
// Send to leaderboard bridge in separate object
|
// Send to leaderboard bridge in separate object
|
||||||
llRegionSay(LEADERBOARD_CHANNEL, "LEFT_TEXT|" + leftText);
|
llRegionSay(LEADERBOARD_CHANNEL, "LEFT_TEXT|" + leftText);
|
||||||
llRegionSay(LEADERBOARD_CHANNEL, "MIDDLE_TEXT|" + middleText);
|
llRegionSay(LEADERBOARD_CHANNEL, "MIDDLE_LEFT_TEXT|" + middleLeftText);
|
||||||
|
llRegionSay(LEADERBOARD_CHANNEL, "MIDDLE_RIGHT_TEXT|" + middleRightText);
|
||||||
llRegionSay(LEADERBOARD_CHANNEL, "RIGHT_TEXT|" + rightText);
|
llRegionSay(LEADERBOARD_CHANNEL, "RIGHT_TEXT|" + rightText);
|
||||||
|
|
||||||
// Also create combined floating text as backup display method
|
// Also create combined floating text as backup display method
|
||||||
string combinedText = "WINS:\n";
|
string combinedText = "Leaderboard:\n";
|
||||||
for (i = 0; i < maxDisplay; i++) {
|
for (i = 0; i < dataCount; i++) {
|
||||||
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
||||||
string playerName = llList2String(playerData, 0);
|
string playerName = llList2String(playerData, 0);
|
||||||
string winsText = llList2String(playerData, 1);
|
string winsText = llList2String(playerData, 1);
|
||||||
|
|
@ -292,11 +321,11 @@ generateLeaderboardText() {
|
||||||
playerName = llGetSubString(playerName, 0, 11);
|
playerName = llGetSubString(playerName, 0, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
combinedText += playerName + ": " + winsText + "\n";
|
combinedText += (string)(i + 1) + ". " + playerName + ": " + winsText + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxDisplay == 0) {
|
if (dataCount == 0) {
|
||||||
combinedText = "No games played yet";
|
combinedText = "Leaderboard: No games played yet";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Leaderboard is now in separate object - no prim to update here
|
// Note: Leaderboard is now in separate object - no prim to update here
|
||||||
|
|
@ -307,6 +336,48 @@ updateLeaderboard(list topPlayers) {
|
||||||
generateLeaderboardText();
|
generateLeaderboardText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate dice roll display text for dual XyzzyText display (20 characters total: 10 chars per prim)
|
||||||
|
generateDiceRollText(string playerName, integer diceValue, string rollType) {
|
||||||
|
string fullLine;
|
||||||
|
|
||||||
|
if (rollType == "CLEAR") {
|
||||||
|
// Clear the dice display
|
||||||
|
fullLine = " "; // 20 spaces
|
||||||
|
} else {
|
||||||
|
// Format: "Bob rolled 6" or "TestBot1 rolled 12" etc.
|
||||||
|
string diceStr = (string)diceValue;
|
||||||
|
string displayName = playerName;
|
||||||
|
|
||||||
|
// Calculate available space: 20 total - " rolled " (8) - dice value (1-2 chars) = 11-10 chars for name
|
||||||
|
integer maxNameLength = 20 - 8 - llStringLength(diceStr); // -8 for " rolled "
|
||||||
|
|
||||||
|
if (llStringLength(displayName) > maxNameLength) {
|
||||||
|
displayName = llGetSubString(displayName, 0, maxNameLength - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullLine = displayName + " rolled " + diceStr;
|
||||||
|
|
||||||
|
// Pad to exactly 20 characters
|
||||||
|
while (llStringLength(fullLine) < 20) {
|
||||||
|
fullLine += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure exactly 20 characters
|
||||||
|
if (llStringLength(fullLine) > 20) {
|
||||||
|
fullLine = llGetSubString(fullLine, 0, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into 2 parts: 10 chars each for left and right prims
|
||||||
|
string leftPart = llGetSubString(fullLine, 0, 9); // Characters 0-9
|
||||||
|
string rightPart = llGetSubString(fullLine, 10, 19); // Characters 10-19
|
||||||
|
|
||||||
|
|
||||||
|
// Send to dice display XyzzyText prims
|
||||||
|
llRegionSay(DICE_CHANNEL, "DICE_LEFT|" + leftPart);
|
||||||
|
llRegionSay(DICE_CHANNEL, "DICE_RIGHT|" + rightPart);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle game won message
|
// Handle game won message
|
||||||
handleGameWon(string winnerName) {
|
handleGameWon(string winnerName) {
|
||||||
integer playerIndex = llListFindList(leaderboardNames, [winnerName]);
|
integer playerIndex = llListFindList(leaderboardNames, [winnerName]);
|
||||||
|
|
@ -314,6 +385,7 @@ handleGameWon(string winnerName) {
|
||||||
if (playerIndex == -1) {
|
if (playerIndex == -1) {
|
||||||
leaderboardNames += [winnerName];
|
leaderboardNames += [winnerName];
|
||||||
leaderboardWins += [1];
|
leaderboardWins += [1];
|
||||||
|
leaderboardLosses += [0]; // No losses for new winner
|
||||||
} else {
|
} else {
|
||||||
integer currentWins = llList2Integer(leaderboardWins, playerIndex);
|
integer currentWins = llList2Integer(leaderboardWins, playerIndex);
|
||||||
leaderboardWins = llListReplaceList(leaderboardWins, [currentWins + 1], playerIndex, playerIndex);
|
leaderboardWins = llListReplaceList(leaderboardWins, [currentWins + 1], playerIndex, playerIndex);
|
||||||
|
|
@ -323,6 +395,30 @@ handleGameWon(string winnerName) {
|
||||||
updateLeaderboard(leaderboardNames);
|
updateLeaderboard(leaderboardNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle game lost message
|
||||||
|
handleGameLost(string loserName) {
|
||||||
|
integer playerIndex = llListFindList(leaderboardNames, [loserName]);
|
||||||
|
|
||||||
|
if (playerIndex == -1) {
|
||||||
|
leaderboardNames += [loserName];
|
||||||
|
leaderboardWins += [0]; // No wins for new loser
|
||||||
|
leaderboardLosses += [1];
|
||||||
|
} else {
|
||||||
|
integer currentLosses = 0;
|
||||||
|
if (playerIndex < llGetListLength(leaderboardLosses)) {
|
||||||
|
currentLosses = llList2Integer(leaderboardLosses, playerIndex);
|
||||||
|
}
|
||||||
|
// Extend losses list if needed
|
||||||
|
while (llGetListLength(leaderboardLosses) <= playerIndex) {
|
||||||
|
leaderboardLosses += [0];
|
||||||
|
}
|
||||||
|
leaderboardLosses = llListReplaceList(leaderboardLosses, [currentLosses + 1], playerIndex, playerIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLeaderboardData();
|
||||||
|
updateLeaderboard(leaderboardNames);
|
||||||
|
}
|
||||||
|
|
||||||
// Sort leaderboard by wins
|
// Sort leaderboard by wins
|
||||||
list getSortedLeaderboard() {
|
list getSortedLeaderboard() {
|
||||||
list combined = [];
|
list combined = [];
|
||||||
|
|
@ -331,7 +427,11 @@ list getSortedLeaderboard() {
|
||||||
for (i = 0; i < llGetListLength(leaderboardNames); i++) {
|
for (i = 0; i < llGetListLength(leaderboardNames); i++) {
|
||||||
string playerName = llList2String(leaderboardNames, i);
|
string playerName = llList2String(leaderboardNames, i);
|
||||||
integer wins = llList2Integer(leaderboardWins, i);
|
integer wins = llList2Integer(leaderboardWins, i);
|
||||||
combined += [playerName + ":" + (string)wins];
|
integer losses = 0;
|
||||||
|
if (i < llGetListLength(leaderboardLosses)) {
|
||||||
|
losses = llList2Integer(leaderboardLosses, i);
|
||||||
|
}
|
||||||
|
combined += [playerName + ":" + (string)wins + ":" + (string)losses];
|
||||||
}
|
}
|
||||||
|
|
||||||
integer n = llGetListLength(combined);
|
integer n = llGetListLength(combined);
|
||||||
|
|
@ -366,8 +466,10 @@ saveLeaderboardData() {
|
||||||
for (i = 0; i < llGetListLength(leaderboardNames); i++) {
|
for (i = 0; i < llGetListLength(leaderboardNames); i++) {
|
||||||
string playerName = llList2String(leaderboardNames, i);
|
string playerName = llList2String(leaderboardNames, i);
|
||||||
integer wins = llList2Integer(leaderboardWins, i);
|
integer wins = llList2Integer(leaderboardWins, i);
|
||||||
|
integer losses = llList2Integer(leaderboardLosses, i);
|
||||||
llLinksetDataWrite("lb_player_" + (string)i, playerName);
|
llLinksetDataWrite("lb_player_" + (string)i, playerName);
|
||||||
llLinksetDataWrite("lb_wins_" + (string)i, (string)wins);
|
llLinksetDataWrite("lb_wins_" + (string)i, (string)wins);
|
||||||
|
llLinksetDataWrite("lb_losses_" + (string)i, (string)losses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,13 +481,16 @@ loadLeaderboardData() {
|
||||||
integer count = (integer)countStr;
|
integer count = (integer)countStr;
|
||||||
leaderboardNames = [];
|
leaderboardNames = [];
|
||||||
leaderboardWins = [];
|
leaderboardWins = [];
|
||||||
|
leaderboardLosses = [];
|
||||||
integer i;
|
integer i;
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
string playerName = llLinksetDataRead("lb_player_" + (string)i);
|
string playerName = llLinksetDataRead("lb_player_" + (string)i);
|
||||||
string winsStr = llLinksetDataRead("lb_wins_" + (string)i);
|
string winsStr = llLinksetDataRead("lb_wins_" + (string)i);
|
||||||
|
string lossesStr = llLinksetDataRead("lb_losses_" + (string)i);
|
||||||
if (playerName != "" && winsStr != "") {
|
if (playerName != "" && winsStr != "") {
|
||||||
leaderboardNames += [playerName];
|
leaderboardNames += [playerName];
|
||||||
leaderboardWins += [(integer)winsStr];
|
leaderboardWins += [(integer)winsStr];
|
||||||
|
leaderboardLosses += [(integer)lossesStr]; // Default to 0 if empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -394,10 +499,12 @@ loadLeaderboardData() {
|
||||||
resetLeaderboard() {
|
resetLeaderboard() {
|
||||||
leaderboardNames = [];
|
leaderboardNames = [];
|
||||||
leaderboardWins = [];
|
leaderboardWins = [];
|
||||||
|
leaderboardLosses = [];
|
||||||
integer i;
|
integer i;
|
||||||
for (i = 0; i < 100; i++) {
|
for (i = 0; i < 100; i++) {
|
||||||
llLinksetDataDelete("lb_player_" + (string)i);
|
llLinksetDataDelete("lb_player_" + (string)i);
|
||||||
llLinksetDataDelete("lb_wins_" + (string)i);
|
llLinksetDataDelete("lb_wins_" + (string)i);
|
||||||
|
llLinksetDataDelete("lb_losses_" + (string)i);
|
||||||
}
|
}
|
||||||
llLinksetDataDelete("lb_count");
|
llLinksetDataDelete("lb_count");
|
||||||
updateLeaderboard([]);
|
updateLeaderboard([]);
|
||||||
|
|
@ -405,7 +512,6 @@ resetLeaderboard() {
|
||||||
|
|
||||||
// Clear all current players from the scoreboard
|
// Clear all current players from the scoreboard
|
||||||
clearAllPlayers() {
|
clearAllPlayers() {
|
||||||
llOwnerSay("DEBUG: Clearing all players from scoreboard");
|
|
||||||
|
|
||||||
// Clear ONLY current game player data lists (NOT leaderboard data)
|
// Clear ONLY current game player data lists (NOT leaderboard data)
|
||||||
playerNames = [];
|
playerNames = [];
|
||||||
|
|
@ -452,6 +558,7 @@ string getHeartTexture(integer lives) {
|
||||||
else return TEXTURE_3_HEARTS; // 3 or more
|
else return TEXTURE_3_HEARTS; // 3 or more
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update player display on the grid
|
// Update player display on the grid
|
||||||
updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
|
updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
|
||||||
// Find existing player or add new one
|
// Find existing player or add new one
|
||||||
|
|
@ -470,14 +577,12 @@ updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
|
||||||
playerLives += [lives];
|
playerLives += [lives];
|
||||||
playerProfiles += [profileUUID];
|
playerProfiles += [profileUUID];
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Added new player " + playerName + " at slot " + (string)playerIndex);
|
|
||||||
} else {
|
} else {
|
||||||
// Update existing player
|
// Update existing player
|
||||||
playerLives = llListReplaceList(playerLives, [lives], playerIndex, playerIndex);
|
playerLives = llListReplaceList(playerLives, [lives], playerIndex, playerIndex);
|
||||||
// Don't overwrite cached profile texture with original UUID on updates
|
// Don't overwrite cached profile texture with original UUID on updates
|
||||||
// playerProfiles = llListReplaceList(playerProfiles, [profileUUID], playerIndex, playerIndex);
|
// playerProfiles = llListReplaceList(playerProfiles, [profileUUID], playerIndex, playerIndex);
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Updated player " + playerName + " at slot " + (string)playerIndex + " with " + (string)lives + " lives");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the physical prims for this player
|
// Update the physical prims for this player
|
||||||
|
|
@ -494,7 +599,6 @@ updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
|
||||||
cachedTexture != TEXTURE_DEFAULT_PROFILE && cachedTexture != TEXTURE_BOT_PROFILE) {
|
cachedTexture != TEXTURE_DEFAULT_PROFILE && cachedTexture != TEXTURE_BOT_PROFILE) {
|
||||||
// We have a previously fetched profile picture, use it
|
// We have a previously fetched profile picture, use it
|
||||||
profileTexture = cachedTexture;
|
profileTexture = cachedTexture;
|
||||||
llOwnerSay("DEBUG: Using cached profile texture for " + playerName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,21 +606,15 @@ updatePlayerDisplay(string playerName, integer lives, string profileUUID) {
|
||||||
if (profileTexture == profileUUID) {
|
if (profileTexture == profileUUID) {
|
||||||
if (isBot(playerName)) {
|
if (isBot(playerName)) {
|
||||||
profileTexture = TEXTURE_BOT_PROFILE;
|
profileTexture = TEXTURE_BOT_PROFILE;
|
||||||
llOwnerSay("DEBUG: Using bot profile texture for " + playerName);
|
|
||||||
} else if (profileTexture == "" || profileTexture == "00000000-0000-0000-0000-000000000000") {
|
} else if (profileTexture == "" || profileTexture == "00000000-0000-0000-0000-000000000000") {
|
||||||
profileTexture = TEXTURE_DEFAULT_PROFILE;
|
profileTexture = TEXTURE_DEFAULT_PROFILE;
|
||||||
llOwnerSay("DEBUG: Using default profile texture for " + playerName);
|
|
||||||
} else {
|
} else {
|
||||||
// Request profile picture via HTTP (the correct method!)
|
// Request profile picture via HTTP (the correct method!)
|
||||||
llOwnerSay("DEBUG: Requesting profile picture via HTTP for avatar UUID: " + profileUUID);
|
|
||||||
string URL_RESIDENT = "https://world.secondlife.com/resident/";
|
string URL_RESIDENT = "https://world.secondlife.com/resident/";
|
||||||
key httpRequestID = llHTTPRequest(URL_RESIDENT + profileUUID, [HTTP_METHOD, "GET"], "");
|
key httpRequestID = llHTTPRequest(URL_RESIDENT + profileUUID, [HTTP_METHOD, "GET"], "");
|
||||||
|
|
||||||
// Store the HTTP request mapping: requestID -> playerIndex
|
// Store the HTTP request mapping: requestID -> playerIndex
|
||||||
httpRequests += [httpRequestID, playerIndex];
|
httpRequests += [httpRequestID, playerIndex];
|
||||||
|
|
||||||
// For now, use the avatar UUID directly as texture
|
|
||||||
llOwnerSay("DEBUG: Setting temporary profile texture: " + profileTexture);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -546,14 +644,15 @@ default {
|
||||||
|
|
||||||
llListen(SCOREBOARD_CHANNEL, "", "", "");
|
llListen(SCOREBOARD_CHANNEL, "", "", "");
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
||||||
|
llListen(DICE_CHANNEL, "", "", "");
|
||||||
|
llListen(-999, "", "", ""); // Dialog menu channel
|
||||||
loadLeaderboardData();
|
loadLeaderboardData();
|
||||||
setupPlayerGrid();
|
|
||||||
|
|
||||||
// Generate initial leaderboard texture
|
// Generate initial leaderboard texture
|
||||||
llSetTimerEvent(1.0); // Short delay to ensure prims are set up
|
llSetTimerEvent(1.0); // Short delay for leaderboard generation
|
||||||
|
|
||||||
llOwnerSay("Game Scoreboard Manager ready - Grid layout initialized");
|
llOwnerSay("Game Scoreboard Manager ready - Touch for menu");
|
||||||
llOwnerSay("Touch me to re-run setup.");
|
llOwnerSay("Use dialog menu to setup prims if needed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
timer() {
|
timer() {
|
||||||
|
|
@ -562,11 +661,35 @@ default {
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(integer channel, string senderName, key id, string message) {
|
listen(integer channel, string senderName, key id, string message) {
|
||||||
|
if (channel == -999) {
|
||||||
|
// Handle dialog menu responses
|
||||||
|
if (id == llGetOwner()) {
|
||||||
|
if (message == "Setup Prims") {
|
||||||
|
llOwnerSay("Running prim setup...");
|
||||||
|
setupPlayerGrid();
|
||||||
|
} else if (message == "Clear Game") {
|
||||||
|
llOwnerSay("Clearing game...");
|
||||||
|
clearAllPlayers();
|
||||||
|
updateActionsPrim("Title");
|
||||||
|
llRegionSay(LEADERBOARD_CHANNEL, "CLEAR_LEADERBOARD");
|
||||||
|
generateLeaderboardText();
|
||||||
|
} else if (message == "Reset Board") {
|
||||||
|
llOwnerSay("Resetting leaderboard...");
|
||||||
|
resetLeaderboard();
|
||||||
|
} else if (message == "Gen Leaderboard") {
|
||||||
|
llOwnerSay("Generating leaderboard...");
|
||||||
|
generateLeaderboardText();
|
||||||
|
} else if (message == "Cancel") {
|
||||||
|
llOwnerSay("Menu cancelled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (channel == SCOREBOARD_CHANNEL) {
|
if (channel == SCOREBOARD_CHANNEL) {
|
||||||
if (llSubStringIndex(message, "PLAYER_UPDATE|") == 0) {
|
if (llSubStringIndex(message, "PLAYER_UPDATE|") == 0) {
|
||||||
// Handle player updates here if needed
|
// Handle player updates here if needed
|
||||||
// Format: PLAYER_UPDATE|PlayerName|Lives|ProfileUUID
|
// Format: PLAYER_UPDATE|PlayerName|Lives|ProfileUUID
|
||||||
llOwnerSay("DEBUG: Received player update: " + message);
|
|
||||||
|
|
||||||
list parts = llParseString2List(message, ["|"], []);
|
list parts = llParseString2List(message, ["|"], []);
|
||||||
if (llGetListLength(parts) >= 4) {
|
if (llGetListLength(parts) >= 4) {
|
||||||
|
|
@ -581,6 +704,10 @@ default {
|
||||||
// Reset to default state
|
// Reset to default state
|
||||||
clearAllPlayers(); // Clear all current players
|
clearAllPlayers(); // Clear all current players
|
||||||
updateActionsPrim("Title"); // Reset to title instead of "Game Cleared"
|
updateActionsPrim("Title"); // Reset to title instead of "Game Cleared"
|
||||||
|
|
||||||
|
// Clear the leaderboard display completely
|
||||||
|
llRegionSay(LEADERBOARD_CHANNEL, "CLEAR_LEADERBOARD");
|
||||||
|
|
||||||
generateLeaderboardText(); // Refresh leaderboard display
|
generateLeaderboardText(); // Refresh leaderboard display
|
||||||
}
|
}
|
||||||
else if (llSubStringIndex(message, "GAME_START|") == 0) {
|
else if (llSubStringIndex(message, "GAME_START|") == 0) {
|
||||||
|
|
@ -605,6 +732,24 @@ default {
|
||||||
string winnerName = llList2String(parts, 1);
|
string winnerName = llList2String(parts, 1);
|
||||||
handleGameWon(winnerName);
|
handleGameWon(winnerName);
|
||||||
}
|
}
|
||||||
|
} else if (llSubStringIndex(message, "GAME_LOST|") == 0) {
|
||||||
|
list parts = llParseString2List(message, ["|"], []);
|
||||||
|
if (llGetListLength(parts) >= 2) {
|
||||||
|
string loserName = llList2String(parts, 1);
|
||||||
|
handleGameLost(loserName);
|
||||||
|
}
|
||||||
|
} else if (llSubStringIndex(message, "DICE_ROLL|") == 0) {
|
||||||
|
// Handle dice roll display
|
||||||
|
// Format: DICE_ROLL|PlayerName|DiceValue
|
||||||
|
list parts = llParseString2List(message, ["|"], []);
|
||||||
|
if (llGetListLength(parts) >= 3) {
|
||||||
|
string playerName = llList2String(parts, 1);
|
||||||
|
integer diceValue = (integer)llList2String(parts, 2);
|
||||||
|
generateDiceRollText(playerName, diceValue, "SHOW");
|
||||||
|
}
|
||||||
|
} else if (llSubStringIndex(message, "CLEAR_DICE") == 0) {
|
||||||
|
// Clear dice roll display
|
||||||
|
generateDiceRollText("", 0, "CLEAR");
|
||||||
} else if (llSubStringIndex(message, "RESET_LEADERBOARD") == 0) {
|
} else if (llSubStringIndex(message, "RESET_LEADERBOARD") == 0) {
|
||||||
resetLeaderboard();
|
resetLeaderboard();
|
||||||
}
|
}
|
||||||
|
|
@ -612,10 +757,17 @@ default {
|
||||||
}
|
}
|
||||||
|
|
||||||
touch_start(integer total_number) {
|
touch_start(integer total_number) {
|
||||||
// Owner can touch to manually re-run setup
|
// Owner can access dialog menu
|
||||||
if (llDetectedKey(0) == llGetOwner()) {
|
if (llDetectedKey(0) == llGetOwner()) {
|
||||||
llOwnerSay("Owner touched - re-running setup...");
|
llDialog(llDetectedKey(0), "Scoreboard Manager Options:", [
|
||||||
setupPlayerGrid();
|
"Setup Prims",
|
||||||
|
"Clear Game",
|
||||||
|
"Reset Board",
|
||||||
|
"Gen Leaderboard",
|
||||||
|
"Cancel"
|
||||||
|
], -999);
|
||||||
|
} else {
|
||||||
|
llSay(0, "Only the owner can access this menu.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,11 +779,9 @@ default {
|
||||||
integer playerIndex = llList2Integer(profileRequests, requestIndex + 1);
|
integer playerIndex = llList2Integer(profileRequests, requestIndex + 1);
|
||||||
integer dataType = llList2Integer(profileRequests, requestIndex + 2);
|
integer dataType = llList2Integer(profileRequests, requestIndex + 2);
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Data type " + (string)dataType + " returned: '" + data + "' for player at index " + (string)playerIndex);
|
|
||||||
|
|
||||||
// Check if this looks like a valid texture UUID (36 characters, proper format)
|
// Check if this looks like a valid texture UUID (36 characters, proper format)
|
||||||
if (llStringLength(data) == 36 && llSubStringIndex(data, "-") != -1) {
|
if (llStringLength(data) == 36 && llSubStringIndex(data, "-") != -1) {
|
||||||
llOwnerSay("*** FOUND IT! Data type " + (string)dataType + " returned valid UUID: " + data + " ***");
|
|
||||||
|
|
||||||
// Update the profile texture for this player
|
// Update the profile texture for this player
|
||||||
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
||||||
|
|
@ -641,10 +791,8 @@ default {
|
||||||
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0
|
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0
|
||||||
]);
|
]);
|
||||||
|
|
||||||
llOwnerSay("Profile picture updated successfully for " + llList2String(playerNames, playerIndex));
|
|
||||||
} else {
|
} else {
|
||||||
// Not a valid UUID, continue testing other data types
|
// Not a valid UUID, continue testing other data types
|
||||||
llOwnerSay("Data type " + (string)dataType + " did not return valid UUID");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the completed request (3 elements: requestID, playerIndex, dataType)
|
// Clean up the completed request (3 elements: requestID, playerIndex, dataType)
|
||||||
|
|
@ -658,7 +806,6 @@ default {
|
||||||
if (requestIndex != -1) {
|
if (requestIndex != -1) {
|
||||||
integer playerIndex = llList2Integer(httpRequests, requestIndex + 1);
|
integer playerIndex = llList2Integer(httpRequests, requestIndex + 1);
|
||||||
|
|
||||||
llOwnerSay("DEBUG: HTTP response received for player at index " + (string)playerIndex + ", status: " + (string)status);
|
|
||||||
|
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
// Try to extract profile picture UUID from HTML
|
// Try to extract profile picture UUID from HTML
|
||||||
|
|
@ -669,14 +816,11 @@ default {
|
||||||
// Second try with img tag
|
// Second try with img tag
|
||||||
s1 = llSubStringIndex(body, profile_img_prefix);
|
s1 = llSubStringIndex(body, profile_img_prefix);
|
||||||
s1l = profile_img_prefix_length;
|
s1l = profile_img_prefix_length;
|
||||||
llOwnerSay("DEBUG: Trying img tag method for profile picture extraction");
|
|
||||||
} else {
|
} else {
|
||||||
llOwnerSay("DEBUG: Found meta tag with profile image");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s1 == -1) {
|
if (s1 == -1) {
|
||||||
// Still no match - use default texture
|
// Still no match - use default texture
|
||||||
llOwnerSay("DEBUG: No profile picture found in HTML for player " + llList2String(playerNames, playerIndex));
|
|
||||||
|
|
||||||
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
||||||
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
||||||
|
|
@ -689,14 +833,12 @@ default {
|
||||||
key profileUUID = llGetSubString(body, s1, s1 + 35);
|
key profileUUID = llGetSubString(body, s1, s1 + 35);
|
||||||
|
|
||||||
if (profileUUID == NULL_KEY) {
|
if (profileUUID == NULL_KEY) {
|
||||||
llOwnerSay("DEBUG: Extracted NULL_KEY, using default texture");
|
|
||||||
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
||||||
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
llSetLinkPrimitiveParamsFast(profilePrimIndex, [
|
||||||
PRIM_TEXTURE, ALL_SIDES, TEXTURE_DEFAULT_PROFILE, <1,1,0>, <0,0,0>, 0.0,
|
PRIM_TEXTURE, ALL_SIDES, TEXTURE_DEFAULT_PROFILE, <1,1,0>, <0,0,0>, 0.0,
|
||||||
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0
|
PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
llOwnerSay("*** SUCCESS! Found profile picture UUID: " + (string)profileUUID + " for player " + llList2String(playerNames, playerIndex) + " ***");
|
|
||||||
|
|
||||||
// Cache the profile picture UUID
|
// Cache the profile picture UUID
|
||||||
playerProfiles = llListReplaceList(playerProfiles, [(string)profileUUID], playerIndex, playerIndex);
|
playerProfiles = llListReplaceList(playerProfiles, [(string)profileUUID], playerIndex, playerIndex);
|
||||||
|
|
@ -710,7 +852,6 @@ default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
llOwnerSay("DEBUG: HTTP request failed with status " + (string)status + " for player " + llList2String(playerNames, playerIndex));
|
|
||||||
|
|
||||||
// Use default texture on HTTP failure
|
// Use default texture on HTTP failure
|
||||||
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
integer profilePrimIndex = FIRST_PLAYER_PRIM + (playerIndex * 2);
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
// Leaderboard Bridge - Receives text from Game Scoreboard Manager and sends to XyzzyText
|
|
||||||
// This script goes in the root prim of your leaderboard object (alongside XyzzyText master)
|
|
||||||
|
|
||||||
// Communication channels
|
|
||||||
integer LEADERBOARD_CHANNEL = -12346; // Listen for messages from scoreboard
|
|
||||||
|
|
||||||
// XyzzyText Communication Constants
|
|
||||||
integer DISPLAY_STRING = 204000;
|
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
|
||||||
llOwnerSay("Leaderboard Bridge ready - listening on channel " + (string)LEADERBOARD_CHANNEL);
|
|
||||||
|
|
||||||
// Send initial blank display to all banks
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "0"); // Bank 0 (left)
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "1"); // Bank 1 (middle)
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "2"); // Bank 2 (right)
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string message) {
|
|
||||||
if (channel == LEADERBOARD_CHANNEL) {
|
|
||||||
llOwnerSay("DEBUG: Received leaderboard message: " + message);
|
|
||||||
|
|
||||||
if (llSubStringIndex(message, "LEFT_TEXT|") == 0) {
|
|
||||||
// Extract left column text and send to bank 0
|
|
||||||
string leftText = llGetSubString(message, 10, -1); // Remove "LEFT_TEXT|" prefix
|
|
||||||
llOwnerSay("DEBUG: Sending to LEFT bank (0): " + leftText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, leftText, "0");
|
|
||||||
|
|
||||||
} else if (llSubStringIndex(message, "MIDDLE_TEXT|") == 0) {
|
|
||||||
// Extract middle column text and send to bank 1
|
|
||||||
string middleText = llGetSubString(message, 12, -1); // Remove "MIDDLE_TEXT|" prefix
|
|
||||||
llOwnerSay("DEBUG: Sending to MIDDLE bank (1): " + middleText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, middleText, "1");
|
|
||||||
|
|
||||||
} else if (llSubStringIndex(message, "RIGHT_TEXT|") == 0) {
|
|
||||||
// Extract right column text and send to bank 2
|
|
||||||
string rightText = llGetSubString(message, 11, -1); // Remove "RIGHT_TEXT|" prefix
|
|
||||||
llOwnerSay("DEBUG: Sending to RIGHT bank (2): " + rightText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, rightText, "2");
|
|
||||||
|
|
||||||
} else if (message == "CLEAR_LEADERBOARD") {
|
|
||||||
// Clear all displays
|
|
||||||
llOwnerSay("DEBUG: Clearing all leaderboard displays");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "0");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "1");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, " ", "2");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
llOwnerSay("DEBUG: Unrecognized leaderboard message: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
62
Leaderboard_Communication_Clean.lsl
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// Leaderboard Communication Script - Clean Version
|
||||||
|
// Based on actual link verification results
|
||||||
|
////////////////////////////////////////////
|
||||||
|
|
||||||
|
integer LEADERBOARD_CHANNEL = -12346;
|
||||||
|
integer DISPLAY_STRING = 204000;
|
||||||
|
|
||||||
|
// Bank assignments based on actual link testing:
|
||||||
|
list leftmostLinks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; // Links 1-12 = Leftmost bank
|
||||||
|
list middleLeftLinks = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; // Links 13-24 = Middle-left bank
|
||||||
|
list middleRightLinks = [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]; // Links 25-36 = Middle-right bank
|
||||||
|
list rightmostLinks = [37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48]; // Links 37-48 = Rightmost bank
|
||||||
|
|
||||||
|
string leftText = "";
|
||||||
|
string middleLeftText = "";
|
||||||
|
string middleRightText = "";
|
||||||
|
string rightText = "";
|
||||||
|
|
||||||
|
distributeToBank(string text, list linkNumbers) {
|
||||||
|
list lines = llParseString2List(text, ["\n"], []);
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < llGetListLength(lines) && i < llGetListLength(linkNumbers); i++) {
|
||||||
|
string line = llList2String(lines, i);
|
||||||
|
// Ensure exactly 10 characters
|
||||||
|
if (llStringLength(line) > 10) {
|
||||||
|
line = llGetSubString(line, 0, 9);
|
||||||
|
} else if (llStringLength(line) < 10) {
|
||||||
|
line = line + llGetSubString(" ", 0, 9 - llStringLength(line));
|
||||||
|
}
|
||||||
|
integer linkNum = llList2Integer(linkNumbers, i);
|
||||||
|
llMessageLinked(linkNum, DISPLAY_STRING, line, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
llOwnerSay("Leaderboard Communication Script ready");
|
||||||
|
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(integer channel, string name, key id, string message) {
|
||||||
|
if (channel == LEADERBOARD_CHANNEL) {
|
||||||
|
if (llGetSubString(message, 0, 9) == "LEFT_TEXT|") {
|
||||||
|
leftText = llGetSubString(message, 10, -1);
|
||||||
|
distributeToBank(leftText, leftmostLinks);
|
||||||
|
|
||||||
|
} else if (llGetSubString(message, 0, 16) == "MIDDLE_LEFT_TEXT|") {
|
||||||
|
middleLeftText = llGetSubString(message, 17, -1);
|
||||||
|
distributeToBank(middleLeftText, middleLeftLinks);
|
||||||
|
|
||||||
|
} else if (llGetSubString(message, 0, 17) == "MIDDLE_RIGHT_TEXT|") {
|
||||||
|
middleRightText = llGetSubString(message, 18, -1);
|
||||||
|
distributeToBank(middleRightText, middleRightLinks);
|
||||||
|
|
||||||
|
} else if (llGetSubString(message, 0, 10) == "RIGHT_TEXT|") {
|
||||||
|
rightText = llGetSubString(message, 11, -1);
|
||||||
|
distributeToBank(rightText, rightmostLinks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
// Leaderboard Manager - Shows top 10 players ranked by total wins
|
|
||||||
// Displays as text on a prim face with persistent win tracking
|
|
||||||
|
|
||||||
// Communication
|
|
||||||
integer LEADERBOARD_CHANNEL = -12346;
|
|
||||||
integer MAIN_CHANNEL = -12340;
|
|
||||||
|
|
||||||
// Data storage
|
|
||||||
list playerNames = [];
|
|
||||||
list playerWins = [];
|
|
||||||
|
|
||||||
// Display settings
|
|
||||||
integer DISPLAY_FACE = 0; // Which face to show leaderboard text on
|
|
||||||
vector TEXT_COLOR = <1.0, 1.0, 0.0>; // Yellow text
|
|
||||||
float TEXT_ALPHA = 1.0;
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
// Listen for game results and commands
|
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
|
||||||
llListen(MAIN_CHANNEL, "", "", "");
|
|
||||||
|
|
||||||
// Load persistent data
|
|
||||||
loadLeaderboardData();
|
|
||||||
|
|
||||||
// Display current leaderboard
|
|
||||||
updateLeaderboardDisplay();
|
|
||||||
|
|
||||||
llOwnerSay("Leaderboard Manager ready - Win tracking active");
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string message) {
|
|
||||||
if (channel == LEADERBOARD_CHANNEL) {
|
|
||||||
if (llSubStringIndex(message, "GAME_WON|") == 0) {
|
|
||||||
handleGameWon(message);
|
|
||||||
}
|
|
||||||
else if (message == "RESET_LEADERBOARD") {
|
|
||||||
resetLeaderboard();
|
|
||||||
}
|
|
||||||
else if (message == "SHOW_LEADERBOARD") {
|
|
||||||
updateLeaderboardDisplay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel == MAIN_CHANNEL) {
|
|
||||||
// Listen for game end messages from main controller
|
|
||||||
if (llSubStringIndex(message, "GAME_ENDED|") == 0) {
|
|
||||||
handleGameEnded(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
touch_start(integer total_number) {
|
|
||||||
// Allow touching to refresh leaderboard display
|
|
||||||
updateLeaderboardDisplay();
|
|
||||||
llOwnerSay("Leaderboard refreshed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleGameWon(string message) {
|
|
||||||
// Format: "GAME_WON|PlayerName"
|
|
||||||
list parts = llParseString2List(message, ["|"], []);
|
|
||||||
if (llGetListLength(parts) < 2) return;
|
|
||||||
|
|
||||||
string winnerName = llList2String(parts, 1);
|
|
||||||
addWinToPlayer(winnerName);
|
|
||||||
|
|
||||||
llOwnerSay("Win recorded for: " + winnerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleGameEnded(string message) {
|
|
||||||
// Format: "GAME_ENDED|WinnerName|PlayerList"
|
|
||||||
list parts = llParseString2List(message, ["|"], []);
|
|
||||||
if (llGetListLength(parts) >= 2) {
|
|
||||||
string winnerName = llList2String(parts, 1);
|
|
||||||
if (winnerName != "NONE" && winnerName != "") {
|
|
||||||
addWinToPlayer(winnerName);
|
|
||||||
llOwnerSay("Game ended - Win recorded for: " + winnerName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addWinToPlayer(string playerName) {
|
|
||||||
// Check if player already exists in leaderboard
|
|
||||||
integer playerIndex = llListFindList(playerNames, [playerName]);
|
|
||||||
|
|
||||||
if (playerIndex == -1) {
|
|
||||||
// New player - add to lists
|
|
||||||
playerNames += [playerName];
|
|
||||||
playerWins += [1];
|
|
||||||
} else {
|
|
||||||
// Existing player - increment wins
|
|
||||||
integer currentWins = llList2Integer(playerWins, playerIndex);
|
|
||||||
playerWins = llListReplaceList(playerWins, [currentWins + 1], playerIndex, playerIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save data and update display
|
|
||||||
saveLeaderboardData();
|
|
||||||
updateLeaderboardDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLeaderboardDisplay() {
|
|
||||||
// Create sorted list of top 10 players
|
|
||||||
list sortedData = getSortedLeaderboard();
|
|
||||||
|
|
||||||
// Build display text
|
|
||||||
string displayText = "═══ LEADERBOARD ═══\n";
|
|
||||||
|
|
||||||
integer i;
|
|
||||||
integer maxDisplay = llGetListLength(sortedData);
|
|
||||||
if (maxDisplay > 10) maxDisplay = 10; // Show only top 10
|
|
||||||
|
|
||||||
if (maxDisplay == 0) {
|
|
||||||
displayText += "No games played yet\n";
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < maxDisplay; i++) {
|
|
||||||
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
|
||||||
string name = llList2String(playerData, 0);
|
|
||||||
string wins = llList2String(playerData, 1);
|
|
||||||
|
|
||||||
displayText += (string)(i + 1) + ". " + name + " - " + wins + " wins\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
displayText += "═══════════════════";
|
|
||||||
|
|
||||||
// Set text on prim face
|
|
||||||
llSetText("", <0,0,0>, 0.0); // Clear floating text
|
|
||||||
llSetLinkPrimitiveParamsFast(LINK_THIS, [
|
|
||||||
PRIM_TEXT, displayText, TEXT_COLOR, TEXT_ALPHA
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Also set as texture text if supported
|
|
||||||
// Note: This would require creating text textures dynamically
|
|
||||||
// For now, we use floating text above the prim
|
|
||||||
}
|
|
||||||
|
|
||||||
list getSortedLeaderboard() {
|
|
||||||
// Create combined list of "PlayerName:Wins" for sorting
|
|
||||||
list combined = [];
|
|
||||||
integer i;
|
|
||||||
|
|
||||||
for (i = 0; i < llGetListLength(playerNames); i++) {
|
|
||||||
string playerName = llList2String(playerNames, i);
|
|
||||||
integer wins = llList2Integer(playerWins, i);
|
|
||||||
combined += [playerName + ":" + (string)wins];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by wins (descending) - bubble sort
|
|
||||||
integer n = llGetListLength(combined);
|
|
||||||
integer swapped = TRUE;
|
|
||||||
|
|
||||||
while (swapped) {
|
|
||||||
swapped = FALSE;
|
|
||||||
for (i = 0; i < n - 1; i++) {
|
|
||||||
list data1 = llParseString2List(llList2String(combined, i), [":"], []);
|
|
||||||
list data2 = llParseString2List(llList2String(combined, i + 1), [":"], []);
|
|
||||||
|
|
||||||
integer wins1 = (integer)llList2String(data1, 1);
|
|
||||||
integer wins2 = (integer)llList2String(data2, 1);
|
|
||||||
|
|
||||||
if (wins1 < wins2) {
|
|
||||||
// Swap positions
|
|
||||||
string temp = llList2String(combined, i);
|
|
||||||
combined = llListReplaceList(combined, [llList2String(combined, i + 1)], i, i);
|
|
||||||
combined = llListReplaceList(combined, [temp], i + 1, i + 1);
|
|
||||||
swapped = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveLeaderboardData() {
|
|
||||||
// Save to linkset data for persistence
|
|
||||||
integer i;
|
|
||||||
|
|
||||||
// Save player count
|
|
||||||
llLinksetDataWrite("lb_count", (string)llGetListLength(playerNames));
|
|
||||||
|
|
||||||
// Save each player's data
|
|
||||||
for (i = 0; i < llGetListLength(playerNames); i++) {
|
|
||||||
string playerName = llList2String(playerNames, i);
|
|
||||||
integer wins = llList2Integer(playerWins, i);
|
|
||||||
|
|
||||||
llLinksetDataWrite("lb_player_" + (string)i, playerName);
|
|
||||||
llLinksetDataWrite("lb_wins_" + (string)i, (string)wins);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadLeaderboardData() {
|
|
||||||
// Load from linkset data
|
|
||||||
string countStr = llLinksetDataRead("lb_count");
|
|
||||||
if (countStr == "") return; // No saved data
|
|
||||||
|
|
||||||
integer count = (integer)countStr;
|
|
||||||
playerNames = [];
|
|
||||||
playerWins = [];
|
|
||||||
|
|
||||||
integer i;
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
string playerName = llLinksetDataRead("lb_player_" + (string)i);
|
|
||||||
string winsStr = llLinksetDataRead("lb_wins_" + (string)i);
|
|
||||||
|
|
||||||
if (playerName != "" && winsStr != "") {
|
|
||||||
playerNames += [playerName];
|
|
||||||
playerWins += [(integer)winsStr];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llOwnerSay("Loaded leaderboard data: " + (string)llGetListLength(playerNames) + " players");
|
|
||||||
}
|
|
||||||
|
|
||||||
resetLeaderboard() {
|
|
||||||
// Clear all data
|
|
||||||
playerNames = [];
|
|
||||||
playerWins = [];
|
|
||||||
|
|
||||||
// Clear persistent storage
|
|
||||||
integer i;
|
|
||||||
for (i = 0; i < 100; i++) { // Clear up to 100 possible entries
|
|
||||||
llLinksetDataDelete("lb_player_" + (string)i);
|
|
||||||
llLinksetDataDelete("lb_wins_" + (string)i);
|
|
||||||
}
|
|
||||||
llLinksetDataDelete("lb_count");
|
|
||||||
|
|
||||||
// Update display
|
|
||||||
updateLeaderboardDisplay();
|
|
||||||
|
|
||||||
llOwnerSay("Leaderboard reset - all win data cleared");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public function to get current standings (for other scripts)
|
|
||||||
string getLeaderboardString() {
|
|
||||||
list sortedData = getSortedLeaderboard();
|
|
||||||
string result = "";
|
|
||||||
|
|
||||||
integer i;
|
|
||||||
integer maxDisplay = llGetListLength(sortedData);
|
|
||||||
if (maxDisplay > 10) maxDisplay = 10;
|
|
||||||
|
|
||||||
for (i = 0; i < maxDisplay; i++) {
|
|
||||||
list playerData = llParseString2List(llList2String(sortedData, i), [":"], []);
|
|
||||||
string name = llList2String(playerData, 0);
|
|
||||||
string wins = llList2String(playerData, 1);
|
|
||||||
|
|
||||||
if (i > 0) result += ", ";
|
|
||||||
result += name + "(" + wins + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
13
Main.lsl
|
|
@ -142,9 +142,6 @@ updateHelpers() {
|
||||||
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, serialized, NULL_KEY);
|
llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, serialized, NULL_KEY);
|
||||||
llMessageLinked(LINK_SET, MSG_SYNC_PICKQUEUE, llList2CSV(pickQueue), NULL_KEY);
|
llMessageLinked(LINK_SET, MSG_SYNC_PICKQUEUE, llList2CSV(pickQueue), NULL_KEY);
|
||||||
|
|
||||||
// Send a test message first to verify scoreboard communication
|
|
||||||
llRegionSay(-12345, "GAME_STATUS|Testing communication...");
|
|
||||||
llOwnerSay("DEBUG: Sent test message to scoreboard");
|
|
||||||
|
|
||||||
// Send player updates to scoreboard
|
// Send player updates to scoreboard
|
||||||
integer j;
|
integer j;
|
||||||
|
|
@ -158,7 +155,6 @@ updateHelpers() {
|
||||||
|
|
||||||
// Send PLAYER_UPDATE message to scoreboard
|
// Send PLAYER_UPDATE message to scoreboard
|
||||||
string updateMsg = "PLAYER_UPDATE|" + playerName + "|" + (string)playerLives + "|" + profileUUID;
|
string updateMsg = "PLAYER_UPDATE|" + playerName + "|" + (string)playerLives + "|" + profileUUID;
|
||||||
llOwnerSay("DEBUG: Sending to scoreboard: " + updateMsg);
|
|
||||||
llRegionSay(-12345, updateMsg);
|
llRegionSay(-12345, updateMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -476,7 +472,6 @@ if (toucher == llGetOwner() && roundStarted) {
|
||||||
|
|
||||||
// If no active dialogs, just show admin menu directly
|
// If no active dialogs, just show admin menu directly
|
||||||
if (!hasActiveDialog) {
|
if (!hasActiveDialog) {
|
||||||
llOwnerSay("DEBUG: Sending MSG_SHOW_MENU for owner during gameplay");
|
|
||||||
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|0", toucher);
|
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|0", toucher);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -514,17 +509,14 @@ if (toucher == llGetOwner() && roundStarted) {
|
||||||
// If there's a human player before the owner, owner is not starter
|
// If there's a human player before the owner, owner is not starter
|
||||||
if (llSubStringIndex(existingName, "TestBot") != 0) {
|
if (llSubStringIndex(existingName, "TestBot") != 0) {
|
||||||
isStarter = FALSE;
|
isStarter = FALSE;
|
||||||
llOwnerSay("DEBUG: Found human player before owner at index " + (string)k + ": " + existingName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Special case: if owner is at index 0, they are definitely the starter
|
// Special case: if owner is at index 0, they are definitely the starter
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
isStarter = TRUE;
|
isStarter = TRUE;
|
||||||
llOwnerSay("DEBUG: Owner is at index 0, definitely starter");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Show the owner menu with the appropriate starter flag
|
// Show the owner menu with the appropriate starter flag
|
||||||
llOwnerSay("DEBUG: Sending MSG_SHOW_MENU for owner, isStarter=" + (string)isStarter + ", idx=" + (string)idx);
|
|
||||||
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|" + (string)isStarter, toucher);
|
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|" + (string)isStarter, toucher);
|
||||||
} else if (idx != -1) {
|
} else if (idx != -1) {
|
||||||
// For non-owner players, check if they're the first human player
|
// For non-owner players, check if they're the first human player
|
||||||
|
|
@ -840,7 +832,6 @@ resetGame();
|
||||||
integer perilStillInGame = llListFindList(names, [perilPlayer]) != -1;
|
integer perilStillInGame = llListFindList(names, [perilPlayer]) != -1;
|
||||||
if (perilStillInGame) {
|
if (perilStillInGame) {
|
||||||
return; // Don't process this sync update
|
return; // Don't process this sync update
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -871,9 +862,6 @@ resetGame();
|
||||||
string newPerilPlayer = llList2String(parts, 2);
|
string newPerilPlayer = llList2String(parts, 2);
|
||||||
list newNames = llCSV2List(llList2String(parts, 3));
|
list newNames = llCSV2List(llList2String(parts, 3));
|
||||||
|
|
||||||
// Debug peril player changes
|
|
||||||
if (perilPlayer != newPerilPlayer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update our game state with the new data
|
// Update our game state with the new data
|
||||||
lives = newLives;
|
lives = newLives;
|
||||||
|
|
@ -1160,7 +1148,6 @@ resetGame();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (msg == "🔧 Admin Menu") {
|
else if (msg == "🔧 Admin Menu") {
|
||||||
llOwnerSay("DEBUG: Sending MSG_SHOW_MENU for owner admin during gameplay");
|
|
||||||
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|0", id);
|
llMessageLinked(LINK_SET, MSG_SHOW_MENU, "owner|0", id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
Position_Controller_Timer.lsl
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Position Controller - Master Object
|
||||||
|
// Add this to your controller object
|
||||||
|
// When it moves, all follower objects will maintain relative positions
|
||||||
|
|
||||||
|
integer SYNC_CHANNEL = -54321;
|
||||||
|
vector lastPos;
|
||||||
|
rotation lastRot;
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
88
Position_Follower_Dev.lsl
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Position Follower - Development Version
|
||||||
|
// This calculates the offset automatically and reports it
|
||||||
|
// Use this to find your perfect spacing, then hardcode the result
|
||||||
|
|
||||||
|
key MASTER_KEY;
|
||||||
|
vector MY_OFFSET;
|
||||||
|
integer SYNC_CHANNEL = -54321;
|
||||||
|
integer SETUP_DONE = FALSE;
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
llListen(SYNC_CHANNEL, "", "", "");
|
||||||
|
llOwnerSay("Looking for Controller object...");
|
||||||
|
|
||||||
|
// Find the master object (controller)
|
||||||
|
llSensor("", "", ACTIVE, 96.0, PI); // Scan for all objects
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor(integer num_detected) {
|
||||||
|
if (!SETUP_DONE) {
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < num_detected; i++) {
|
||||||
|
string name = llDetectedName(i);
|
||||||
|
// Look for object with "Controller" or "controller" in the name
|
||||||
|
if (llSubStringIndex(name, "Controller") >= 0 ||
|
||||||
|
llSubStringIndex(name, "controller") >= 0 ||
|
||||||
|
llSubStringIndex(name, "CONTROLLER") >= 0) {
|
||||||
|
|
||||||
|
MASTER_KEY = llDetectedKey(i);
|
||||||
|
|
||||||
|
// Calculate my offset from master's current position
|
||||||
|
vector master_pos = llDetectedPos(i);
|
||||||
|
rotation master_rot = llDetectedRot(i);
|
||||||
|
vector my_pos = llGetPos();
|
||||||
|
|
||||||
|
// Calculate offset in master's local coordinate system
|
||||||
|
MY_OFFSET = (my_pos - master_pos) / master_rot;
|
||||||
|
|
||||||
|
// Get my initial rotation
|
||||||
|
rotation my_rot = llGetRot();
|
||||||
|
|
||||||
|
SETUP_DONE = TRUE;
|
||||||
|
|
||||||
|
// IMPORTANT: This is what you need to hardcode in the final version
|
||||||
|
llOwnerSay("=== OFFSET CALCULATED ===");
|
||||||
|
llOwnerSay("MY_OFFSET = " + (string)MY_OFFSET + ";");
|
||||||
|
llOwnerSay("Copy this line into your final script!");
|
||||||
|
llOwnerSay("========================");
|
||||||
|
llOwnerSay("=== ROTATION INFO ===");
|
||||||
|
llOwnerSay("Controller rotation: " + (string)master_rot);
|
||||||
|
llOwnerSay("My initial rotation: " + (string)my_rot);
|
||||||
|
llOwnerSay("=====================");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SETUP_DONE) {
|
||||||
|
llOwnerSay("Controller object not found! Make sure it has 'controller' in the name.");
|
||||||
|
llSetTimerEvent(5.0); // Try again in 5 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer() {
|
||||||
|
llSetTimerEvent(0.0);
|
||||||
|
llSensor("", "", ACTIVE, 96.0, PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
no_sensor() {
|
||||||
|
llOwnerSay("No objects detected nearby. Make sure Controller is within 96m.");
|
||||||
|
llSetTimerEvent(5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(integer channel, string name, key id, string message) {
|
||||||
|
if (SETUP_DONE && id == MASTER_KEY && llSubStringIndex(message, "CONTROLLER_MOVED|") == 0) {
|
||||||
|
list parts = llParseString2List(message, ["|"], []);
|
||||||
|
vector master_pos = (vector)llList2String(parts, 1);
|
||||||
|
rotation master_rot = (rotation)llList2String(parts, 2);
|
||||||
|
|
||||||
|
vector new_pos = master_pos + (MY_OFFSET * master_rot);
|
||||||
|
llSetPos(new_pos);
|
||||||
|
llSetRot(master_rot);
|
||||||
|
|
||||||
|
llOwnerSay("Following controller to: " + (string)new_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Position_Reset_Instructions.txt
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
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!
|
||||||
55
Position_Reset_Tool.lsl
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
key controller_key;
|
||||||
|
vector controller_pos;
|
||||||
|
rotation controller_rot;
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
controller_key = llGetKey();
|
||||||
|
llOwnerSay("Position Reset Tool ready - Click to scan for display objects");
|
||||||
|
llListen(-12345, "", "", ""); // Scoreboard responses
|
||||||
|
llListen(-12346, "", "", ""); // Leaderboard responses
|
||||||
|
llListen(-12347, "", "", ""); // Dice responses
|
||||||
|
}
|
||||||
|
|
||||||
|
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(-12345, "POSITION_SCAN|" + (string)controller_key); // Scoreboard
|
||||||
|
llRegionSay(-12346, "POSITION_SCAN|" + (string)controller_key); // Leaderboard
|
||||||
|
llRegionSay(-12347, "POSITION_SCAN|" + (string)controller_key); // Dice
|
||||||
|
llSetTimerEvent(3.0); // Wait 3 seconds for responses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(integer channel, string name, key id, string msg) {
|
||||||
|
if ((channel == -12345 || channel == -12346 || channel == -12347) &&
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
29
README.md
|
|
@ -33,6 +33,7 @@ Peril Dice is a multiplayer elimination game where each player selects numbers b
|
||||||
- **⚡ Performance Optimized**: Reduced lag with selective particle effects
|
- **⚡ Performance Optimized**: Reduced lag with selective particle effects
|
||||||
- **🛡️ Robust Error Handling**: Comprehensive game state synchronization
|
- **🛡️ Robust Error Handling**: Comprehensive game state synchronization
|
||||||
- **🚫 Game Protection**: Prevents joining games in progress
|
- **🚫 Game Protection**: Prevents joining games in progress
|
||||||
|
- **📍 Position Management System**: Coordinated movement of all game components
|
||||||
|
|
||||||
## Game Rules
|
## Game Rules
|
||||||
|
|
||||||
|
|
@ -86,20 +87,33 @@ Dice type is automatically chosen to ensure at least 3 picks per player:
|
||||||
| 5–6 | d20 |
|
| 5–6 | d20 |
|
||||||
| 7–10 | d30 |
|
| 7–10 | d30 |
|
||||||
|
|
||||||
## Recent Improvements (v2.3.0)
|
## Recent Improvements (v2.4.0)
|
||||||
|
|
||||||
### 🏆 Visual Scoreboard Revolution
|
### 📍 Position Management System
|
||||||
|
- **Master-Follower Architecture**: Controller object manages position of all game components
|
||||||
|
- **Automatic Position Sync**: Scoreboard, leaderboard, and displays move with main controller
|
||||||
|
- **Config-Based Setup**: Position offsets and rotations defined in notecard configuration
|
||||||
|
- **Position Reset Tools**: Easy recalibration system for repositioning game components
|
||||||
|
- **Coordinated Movement**: All objects maintain relative positions when game is moved
|
||||||
|
|
||||||
|
### 🧹 Production Code Cleanup
|
||||||
|
- **Debug Code Removal**: Complete removal of all debug logging and test messages for production deployment
|
||||||
|
- **Syntax Error Fixes**: Resolved critical syntax errors including missing braces in conditional blocks
|
||||||
|
- **Code Optimization**: Cleaner, more maintainable codebase without development artifacts
|
||||||
|
- **Performance Enhancement**: Reduced script memory usage and execution overhead
|
||||||
|
|
||||||
|
### 🏆 Visual Scoreboard Revolution (v2.3.0)
|
||||||
- **Real-Time Visual Display**: Complete visual overhaul with player grid showing profile pictures and hearts
|
- **Real-Time Visual Display**: Complete visual overhaul with player grid showing profile pictures and hearts
|
||||||
- **Instant Heart Updates**: Hearts change immediately when lives are lost, before any dialogs appear
|
- **Instant Heart Updates**: Hearts change immediately when lives are lost, before any dialogs appear
|
||||||
- **Profile Picture Fetching**: Automatic HTTP requests to get actual Second Life avatar pictures
|
- **Profile Picture Fetching**: Automatic HTTP requests to get actual Second Life avatar pictures
|
||||||
- **0-Hearts Display**: Shows elimination sequence visually before player removal
|
- **0-Hearts Display**: Shows elimination sequence visually before player removal
|
||||||
|
|
||||||
### 🎭 Enhanced Status System
|
### 🎭 Enhanced Status System (v2.3.0)
|
||||||
- **Specific Status Types**: Separate textures for Direct Hit, No Shield, Plot Twist, Elimination, Victory
|
- **Specific Status Types**: Separate textures for Direct Hit, No Shield, Plot Twist, Elimination, Victory
|
||||||
- **Perfect Timing**: 8-second display with 2-second protective delays prevent status overwriting
|
- **Perfect Timing**: 8-second display with 2-second protective delays prevent status overwriting
|
||||||
- **Visual Impact**: Large action prim displays current game status with custom textures
|
- **Visual Impact**: Large action prim displays current game status with custom textures
|
||||||
|
|
||||||
### 🎯 Comprehensive Leaderboard
|
### 🎯 Comprehensive Leaderboard (v2.3.0)
|
||||||
- **Persistent Win Tracking**: Player victories saved across game sessions using linkset data
|
- **Persistent Win Tracking**: Player victories saved across game sessions using linkset data
|
||||||
- **Professional Display**: XyzzyText 3-prim system for clean leaderboard presentation
|
- **Professional Display**: XyzzyText 3-prim system for clean leaderboard presentation
|
||||||
- **Flexible Reset Options**: Separate commands for game reset, leaderboard reset, or complete reset
|
- **Flexible Reset Options**: Separate commands for game reset, leaderboard reset, or complete reset
|
||||||
|
|
@ -108,12 +122,13 @@ Dice type is automatically chosen to ensure at least 3 picks per player:
|
||||||
- **Heart Update Timing**: Fixed hearts not updating until after next-turn dialog
|
- **Heart Update Timing**: Fixed hearts not updating until after next-turn dialog
|
||||||
- **Status Conflicts**: All status messages now have protective delays to prevent overwriting
|
- **Status Conflicts**: All status messages now have protective delays to prevent overwriting
|
||||||
- **Victory/Elimination Flow**: Proper 6.4-second delay between elimination and victory status
|
- **Victory/Elimination Flow**: Proper 6.4-second delay between elimination and victory status
|
||||||
|
- **Syntax Errors**: Fixed missing braces and other LSL compilation issues
|
||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
**Current Version**: 2.3.0
|
**Current Version**: 2.4.0
|
||||||
**Last Updated**: August 5, 2025
|
**Last Updated**: August 8, 2025
|
||||||
**Status**: Stable - Complete visual scoreboard system with enhanced status display
|
**Status**: Production Ready - Debug-free codebase with comprehensive visual systems
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
126
README_Notecard.txt
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
🎲 PERIL DICE — MODULAR GAME SYSTEM FOR SECOND LIFE
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
OVERVIEW
|
||||||
|
--------
|
||||||
|
Peril Dice is a multiplayer elimination game where each player selects numbers before a die is rolled. If the peril player's number is rolled, they lose a life. Players are eliminated when they reach zero lives.
|
||||||
|
|
||||||
|
KEY FEATURES
|
||||||
|
============
|
||||||
|
|
||||||
|
🏆 VISUAL SCOREBOARD SYSTEM
|
||||||
|
- Real-Time Player Grid: Visual scoreboard showing all players with profile pictures and hearts
|
||||||
|
- Profile Picture Integration: Automatic avatar profile fetching from Second Life
|
||||||
|
- Heart Texture Display: Visual life representation (3, 2, 1, 0 hearts)
|
||||||
|
- Instant Updates: Hearts change immediately when lives are lost
|
||||||
|
|
||||||
|
🎭 ENHANCED STATUS DISPLAY
|
||||||
|
- Visual Status Actions: Large status prim with custom textures for each game event
|
||||||
|
- Specific Status Types: Direct Hit, No Shield, Plot Twist, Elimination, Victory, etc.
|
||||||
|
- Perfect Timing: 8-second display time with protective delays to prevent overwriting
|
||||||
|
|
||||||
|
🎯 COMPREHENSIVE LEADERBOARD
|
||||||
|
- Persistent Win Tracking: Player victories saved across sessions
|
||||||
|
- XyzzyText Display: Professional 3-prim text system for leaderboard
|
||||||
|
- Automatic Sorting: Top players by win count
|
||||||
|
|
||||||
|
🎮 CORE GAME FEATURES
|
||||||
|
- 🎯 Dynamic Player Management: Players can join at runtime (owner and other avatars)
|
||||||
|
- 🤖 Bot Support: Add AI bots for testing and gameplay variety
|
||||||
|
- 📱 Floating HUD Display: Real-time stats for each player
|
||||||
|
- 🎮 Intelligent Dice Sizing: Automatic dice size based on player count
|
||||||
|
- 🔄 Ready State System: Players must be ready before games start
|
||||||
|
- 🎭 Dramatic Messaging: Immersive thematic announcements visible to all players
|
||||||
|
- 🎲 Context-Rich Rolls: Detailed dice information with type and result
|
||||||
|
- ⚡ Performance Optimized: Reduced lag with selective particle effects
|
||||||
|
- 🛡️ Robust Error Handling: Comprehensive game state synchronization
|
||||||
|
- 🚫 Game Protection: Prevents joining games in progress
|
||||||
|
- 📍 Position Management System: Coordinated movement of all game components
|
||||||
|
|
||||||
|
GAME RULES
|
||||||
|
==========
|
||||||
|
1. Setup: 2-10 players join the game
|
||||||
|
2. Ready Phase: All players except starter must mark themselves ready
|
||||||
|
3. Picking Phase: Each player picks 1-3 numbers (based on peril player's remaining lives)
|
||||||
|
4. Rolling Phase: Peril player rolls the dice
|
||||||
|
5. Resolution:
|
||||||
|
- If rolled number matches another player's pick → that player becomes new peril player (⚡ PLOT TWIST!)
|
||||||
|
- If peril player picked the rolled number → they lose a life (🩸 DIRECT HIT!)
|
||||||
|
- If nobody picked the rolled number → peril player loses a life (🩸 NO SHIELD!)
|
||||||
|
6. Elimination: Players with 0 lives are eliminated
|
||||||
|
7. Victory: Last player standing wins! 🏆
|
||||||
|
|
||||||
|
HOW TO PLAY
|
||||||
|
===========
|
||||||
|
|
||||||
|
FOR OWNER:
|
||||||
|
- Touch object → Access owner menu
|
||||||
|
- Add Bot → Add AI players for testing
|
||||||
|
- Start Game → Begin when all players are ready
|
||||||
|
- Reset Game → Reset to initial state
|
||||||
|
- Manage Picks → View/modify player selections
|
||||||
|
|
||||||
|
FOR PLAYERS:
|
||||||
|
- Touch object → Register and join game
|
||||||
|
- Ready/Not Ready → Toggle your ready state
|
||||||
|
- Pick Numbers → Select your numbers when prompted
|
||||||
|
- Roll Dice → Roll when you're the peril player
|
||||||
|
|
||||||
|
ARCHITECTURE
|
||||||
|
============
|
||||||
|
The system uses a modular LSL architecture with inter-script communication:
|
||||||
|
- Main Controller: Core game logic and state management
|
||||||
|
- Dialog Handlers: User interface and input processing
|
||||||
|
- Roll Module: Dice rolling and confetti effects
|
||||||
|
- Bot Manager: AI player behavior
|
||||||
|
- Floater Manager: Floating HUD display management
|
||||||
|
- Game Helpers: Utility functions and dice type calculation
|
||||||
|
|
||||||
|
DICE SCALING
|
||||||
|
============
|
||||||
|
Dice type is automatically chosen to ensure at least 3 picks per player:
|
||||||
|
|
||||||
|
Player Count | Dice Type
|
||||||
|
-------------|----------
|
||||||
|
1–2 | d6
|
||||||
|
3–4 | d12
|
||||||
|
5–6 | d20
|
||||||
|
7–10 | d30
|
||||||
|
|
||||||
|
RECENT IMPROVEMENTS (V2.4.0)
|
||||||
|
============================
|
||||||
|
|
||||||
|
📍 POSITION MANAGEMENT SYSTEM
|
||||||
|
- Master-Follower Architecture: Controller object manages position of all game components
|
||||||
|
- Automatic Position Sync: Scoreboard, leaderboard, and displays move with main controller
|
||||||
|
- Config-Based Setup: Position offsets and rotations defined in notecard configuration
|
||||||
|
- Position Reset Tools: Easy recalibration system for repositioning game components
|
||||||
|
- Coordinated Movement: All objects maintain relative positions when game is moved
|
||||||
|
|
||||||
|
🧹 PRODUCTION CODE CLEANUP
|
||||||
|
- Debug Code Removal: Complete removal of all debug logging and test messages for production deployment
|
||||||
|
- Syntax Error Fixes: Resolved critical syntax errors including missing braces in conditional blocks
|
||||||
|
- Code Optimization: Cleaner, more maintainable codebase without development artifacts
|
||||||
|
- Performance Enhancement: Reduced script memory usage and execution overhead
|
||||||
|
|
||||||
|
VERSION INFORMATION
|
||||||
|
===================
|
||||||
|
Current Version: 2.4.0
|
||||||
|
Last Updated: August 8, 2025
|
||||||
|
Status: Production Ready - Debug-free codebase with comprehensive visual systems
|
||||||
|
|
||||||
|
ORIGINAL GAME RULES CREDIT
|
||||||
|
==========================
|
||||||
|
Game rules were created by Noose the Bunny (djmusica28) in Second Life. This automated version builds upon their original manual gameplay concept.
|
||||||
|
|
||||||
|
ORIGINAL MANUAL RULES SUMMARY:
|
||||||
|
- Each player starts with 3 lives
|
||||||
|
- Players pick numbers based on peril player's remaining lives:
|
||||||
|
- 3 Lives → Pick 1 number
|
||||||
|
- 2 Lives → Pick 2 numbers
|
||||||
|
- 1 Life → Pick 3 numbers
|
||||||
|
- Roll dice to determine outcome
|
||||||
|
- Last player with lives wins
|
||||||
|
|
||||||
|
---
|
||||||
|
Peril Dice provides hours of entertainment for Second Life communities with its blend of strategy, luck, and social interaction!
|
||||||
|
|
@ -120,6 +120,9 @@ default {
|
||||||
// Send message to main controller to get the player's key and show dialog
|
// Send message to main controller to get the player's key and show dialog
|
||||||
llMessageLinked(LINK_SET, 997, "START_NEXT_ROUND_DIALOG|" + playerName, NULL_KEY);
|
llMessageLinked(LINK_SET, 997, "START_NEXT_ROUND_DIALOG|" + playerName, NULL_KEY);
|
||||||
} else {
|
} else {
|
||||||
|
// Clear dice display before showing new roll dialog
|
||||||
|
llRegionSay(-12345, "CLEAR_DICE");
|
||||||
|
|
||||||
llOwnerSay("🎲 Prompting " + str + " to roll the dice.");
|
llOwnerSay("🎲 Prompting " + str + " to roll the dice.");
|
||||||
// Check if this is a bot (TestBot names)
|
// Check if this is a bot (TestBot names)
|
||||||
if (llSubStringIndex(str, "TestBot") == 0) {
|
if (llSubStringIndex(str, "TestBot") == 0) {
|
||||||
|
|
@ -139,6 +142,9 @@ default {
|
||||||
string resultStr = (string)result;
|
string resultStr = (string)result;
|
||||||
llSay(0, "🎲 THE D" + (string)diceType + " OF FATE! " + perilPlayer + " rolled a " + resultStr + " on the " + (string)diceType + "-sided die! 🎲");
|
llSay(0, "🎲 THE D" + (string)diceType + " OF FATE! " + perilPlayer + " rolled a " + resultStr + " on the " + (string)diceType + "-sided die! 🎲");
|
||||||
|
|
||||||
|
// Send dice roll to scoreboard for display on dice screen
|
||||||
|
llRegionSay(-12345, "DICE_ROLL|" + perilPlayer + "|" + resultStr);
|
||||||
|
|
||||||
string newPeril = "";
|
string newPeril = "";
|
||||||
integer matched = FALSE;
|
integer matched = FALSE;
|
||||||
list matchedPlayers = [];
|
list matchedPlayers = [];
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
// XyzzyText Debug Helper
|
|
||||||
// This script helps debug issues with XyzzyText display
|
|
||||||
|
|
||||||
// Constants for XyzzyText communication
|
|
||||||
integer DISPLAY_STRING = 204000;
|
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
|
||||||
integer RESCAN_LINKSET = 204008;
|
|
||||||
|
|
||||||
// Communication channel from scoreboard
|
|
||||||
integer LEADERBOARD_CHANNEL = -12346;
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
// Listen for messages from scoreboard
|
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
|
||||||
llOwnerSay("XyzzyText Debug Helper started");
|
|
||||||
|
|
||||||
// Check linkset info
|
|
||||||
integer numPrims = llGetNumberOfPrims();
|
|
||||||
llOwnerSay("DEBUG: Linkset has " + (string)numPrims + " prims");
|
|
||||||
|
|
||||||
// List all prim names to verify XyzzyText setup
|
|
||||||
integer i;
|
|
||||||
for (i = 1; i <= numPrims; i++) {
|
|
||||||
string primName = llGetLinkName(i);
|
|
||||||
llOwnerSay("DEBUG: Prim " + (string)i + " name: '" + primName + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger a rescan of the linkset for XyzzyText
|
|
||||||
llOwnerSay("DEBUG: Triggering XyzzyText rescan");
|
|
||||||
llMessageLinked(LINK_SET, RESCAN_LINKSET, "", "");
|
|
||||||
|
|
||||||
// Send test text to each bank
|
|
||||||
llSleep(1.0); // Wait for rescan to complete
|
|
||||||
llOwnerSay("DEBUG: Sending test text to banks");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST0", "0"); // Bank 0 (left)
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST1", "1"); // Bank 1 (middle)
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST2", "2"); // Bank 2 (right)
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string message) {
|
|
||||||
if (channel == LEADERBOARD_CHANNEL) {
|
|
||||||
llOwnerSay("DEBUG: Received message on channel " + (string)channel + ": " + message);
|
|
||||||
|
|
||||||
if (llSubStringIndex(message, "LEFT_TEXT|") == 0) {
|
|
||||||
string leftText = llGetSubString(message, 10, -1);
|
|
||||||
llOwnerSay("DEBUG: Got LEFT text: " + leftText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, leftText, "0");
|
|
||||||
}
|
|
||||||
else if (llSubStringIndex(message, "MIDDLE_TEXT|") == 0) {
|
|
||||||
string middleText = llGetSubString(message, 12, -1);
|
|
||||||
llOwnerSay("DEBUG: Got MIDDLE text: " + middleText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, middleText, "1");
|
|
||||||
}
|
|
||||||
else if (llSubStringIndex(message, "RIGHT_TEXT|") == 0) {
|
|
||||||
string rightText = llGetSubString(message, 11, -1);
|
|
||||||
llOwnerSay("DEBUG: Got RIGHT text: " + rightText);
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, rightText, "2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
touch_start(integer total_number) {
|
|
||||||
// When touched, refresh debug info and test displays
|
|
||||||
llOwnerSay("--- XyzzyText Debug Refresh ---");
|
|
||||||
|
|
||||||
// Check linkset info
|
|
||||||
integer numPrims = llGetNumberOfPrims();
|
|
||||||
llOwnerSay("DEBUG: Linkset has " + (string)numPrims + " prims");
|
|
||||||
|
|
||||||
// List all prim names
|
|
||||||
integer i;
|
|
||||||
for (i = 1; i <= numPrims; i++) {
|
|
||||||
string primName = llGetLinkName(i);
|
|
||||||
llOwnerSay("DEBUG: Prim " + (string)i + " name: '" + primName + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger rescan and test displays
|
|
||||||
llOwnerSay("DEBUG: Triggering XyzzyText rescan");
|
|
||||||
llMessageLinked(LINK_SET, RESCAN_LINKSET, "", "");
|
|
||||||
|
|
||||||
llSleep(1.0);
|
|
||||||
llOwnerSay("DEBUG: Sending test text to banks");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST0", "0");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST1", "1");
|
|
||||||
llMessageLinked(LINK_SET, DISPLAY_STRING, "TEST2", "2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
118
XyzzyText_Dice_Bridge.lsl
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
// XyzzyText Bridge Script - Dice Display Bridge
|
||||||
|
// This script goes in the ROOT prim of the linked dice display XyzzyText object
|
||||||
|
// Works with official XyzzyText v2.1 (10-char) script
|
||||||
|
// Handles dice roll display across 1 bank (20 characters total)
|
||||||
|
|
||||||
|
integer DICE_CHANNEL = -12347; // Channel for dice roll display
|
||||||
|
integer DISPLAY_STRING = 204000;
|
||||||
|
integer DISPLAY_EXTENDED = 204001;
|
||||||
|
|
||||||
|
// Bank configuration for XyzzyText v2.1
|
||||||
|
// Single bank with 2 prims: xyzzytext-0-0 (chars 1-10), xyzzytext-0-1 (chars 11-20)
|
||||||
|
integer DICE_BANK = 0; // Single bank for dice display (20 characters total)
|
||||||
|
|
||||||
|
// Storage for combining left and right parts
|
||||||
|
string currentLeftText = "";
|
||||||
|
string currentRightText = "";
|
||||||
|
|
||||||
|
// Function to combine and display the dice text
|
||||||
|
displayDiceText() {
|
||||||
|
string fullText = currentLeftText + currentRightText;
|
||||||
|
|
||||||
|
// Ensure exactly 20 characters
|
||||||
|
while (llStringLength(fullText) < 20) {
|
||||||
|
fullText += " ";
|
||||||
|
}
|
||||||
|
if (llStringLength(fullText) > 20) {
|
||||||
|
fullText = llGetSubString(fullText, 0, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send to XyzzyText bank
|
||||||
|
llMessageLinked(LINK_THIS, DISPLAY_STRING, fullText, (key)((string)DICE_BANK));
|
||||||
|
|
||||||
|
|
||||||
|
// Clear the stored parts for next message
|
||||||
|
currentLeftText = "";
|
||||||
|
currentRightText = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
default {
|
||||||
|
state_entry() {
|
||||||
|
// Listen for dice display messages
|
||||||
|
llListen(DICE_CHANNEL, "", "", "");
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
currentLeftText = "";
|
||||||
|
currentRightText = "";
|
||||||
|
|
||||||
|
// Initialize XyzzyText bank with blank text (20 spaces)
|
||||||
|
llMessageLinked(LINK_THIS, DISPLAY_STRING, " ", (key)((string)DICE_BANK));
|
||||||
|
|
||||||
|
llOwnerSay("Dice Display Bridge ready - listening on channel " + (string)DICE_CHANNEL);
|
||||||
|
llOwnerSay("Using dice bank: " + (string)DICE_BANK + " (20 characters across 2 prims)");
|
||||||
|
llOwnerSay("Make sure prims are named: xyzzytext-0-0, xyzzytext-0-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(integer channel, string senderName, key id, string message) {
|
||||||
|
if (channel == DICE_CHANNEL) {
|
||||||
|
if (llSubStringIndex(message, "DICE_LEFT|") == 0) {
|
||||||
|
// Store the left part (characters 0-9)
|
||||||
|
currentLeftText = llGetSubString(message, 10, -1); // Remove "DICE_LEFT|" prefix
|
||||||
|
|
||||||
|
// Pad to exactly 10 characters
|
||||||
|
while (llStringLength(currentLeftText) < 10) {
|
||||||
|
currentLeftText += " ";
|
||||||
|
}
|
||||||
|
if (llStringLength(currentLeftText) > 10) {
|
||||||
|
currentLeftText = llGetSubString(currentLeftText, 0, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If we have both parts, display them
|
||||||
|
if (currentRightText != "") {
|
||||||
|
displayDiceText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (llSubStringIndex(message, "DICE_RIGHT|") == 0) {
|
||||||
|
// Store the right part (characters 10-19)
|
||||||
|
currentRightText = llGetSubString(message, 11, -1); // Remove "DICE_RIGHT|" prefix
|
||||||
|
|
||||||
|
// Pad to exactly 10 characters
|
||||||
|
while (llStringLength(currentRightText) < 10) {
|
||||||
|
currentRightText += " ";
|
||||||
|
}
|
||||||
|
if (llStringLength(currentRightText) > 10) {
|
||||||
|
currentRightText = llGetSubString(currentRightText, 0, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If we have both parts, display them
|
||||||
|
if (currentLeftText != "") {
|
||||||
|
displayDiceText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_start(integer total_number) {
|
||||||
|
// Touch for debugging - show current state and test display
|
||||||
|
if (llDetectedKey(0) == llGetOwner()) {
|
||||||
|
llOwnerSay("Dice Display Bridge - listening on channel " + (string)DICE_CHANNEL);
|
||||||
|
llOwnerSay("Total prims in linkset: " + (string)llGetNumberOfPrims());
|
||||||
|
llOwnerSay("Using dice bank: " + (string)DICE_BANK);
|
||||||
|
|
||||||
|
// Test display - 20 characters total
|
||||||
|
llMessageLinked(LINK_THIS, DISPLAY_STRING, "TEST: Player1 -> 6 ", (key)((string)DICE_BANK));
|
||||||
|
|
||||||
|
llOwnerSay("Test display sent. Touch again to clear.");
|
||||||
|
llSetTimerEvent(3.0); // Clear test after 3 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer() {
|
||||||
|
llSetTimerEvent(0.0); // Stop timer
|
||||||
|
// Clear test display (20 spaces)
|
||||||
|
llMessageLinked(LINK_THIS, DISPLAY_STRING, " ", (key)((string)DICE_BANK));
|
||||||
|
llOwnerSay("Test display cleared.");
|
||||||
|
}
|
||||||
|
}
|
||||||
25
config
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Peril Game Display Configuration
|
||||||
|
# Edit the values below to adjust your display positions
|
||||||
|
# Use the Position Reset Tool to generate new values if needed
|
||||||
|
|
||||||
|
# Scoreboard display position and rotation
|
||||||
|
scoreboard_offset=<0.000, 3.000, 0.000>
|
||||||
|
scoreboard_rotation=<0.000, 0.000, 0.000, 1.000>
|
||||||
|
|
||||||
|
# Leaderboard display position and rotation
|
||||||
|
leaderboard_offset=<-3.000, 0.000, 1.500>
|
||||||
|
leaderboard_rotation=<0.000, 0.000, 0.000, 1.000>
|
||||||
|
|
||||||
|
# Dice display position and rotation
|
||||||
|
dice_offset=<3.000, 0.000, 0.000>
|
||||||
|
dice_rotation=<0.000, 0.000, 0.000, 1.000>
|
||||||
|
|
||||||
|
# Channel settings (usually don't need to change these)
|
||||||
|
scoreboard_channel=-54321
|
||||||
|
leaderboard_channel=-54321
|
||||||
|
dice_channel=-54321
|
||||||
|
|
||||||
|
# Game data channels (separate from position sync)
|
||||||
|
scoreboard_data_channel=-12345
|
||||||
|
leaderboard_data_channel=-12346
|
||||||
|
dice_data_channel=-12347
|
||||||
BIN
images/Bot player profile pic.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/Direct Hit.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
images/Elimination.png
Normal file
|
After Width: | Height: | Size: 3 MiB |
BIN
images/No Shiled.png
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
images/Peril Selected.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
images/PlotTwist.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
images/Punishment Time.png
Normal file
|
After Width: | Height: | Size: 3 MiB |
|
Before Width: | Height: | Size: 174 KiB |
BIN
images/Victory.png
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
|
|
@ -1,611 +0,0 @@
|
||||||
////////////////////////////////////////////
|
|
||||||
// XyzzyText v2.1 Script (Set Line Color) by Huney Jewell
|
|
||||||
// XyzzyText v2.0 Script (5 Face, Single Texture)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Edited trivially by Joel Cloquet on 1/2011 to use the (relatively)
|
|
||||||
// new llSetLinkPrimitiveParamsFast function, thereby removing the need in
|
|
||||||
// some cases to use the slave script.
|
|
||||||
//
|
|
||||||
// Heavily Modified by Thraxis Epsilon, Gigs Taggart 5/2007 and Strife Onizuka 8/2007
|
|
||||||
// Rewrite to allow one-script-per-object operation w/ optional slaves
|
|
||||||
// Enable prim-label functionality
|
|
||||||
// Enabled Banking
|
|
||||||
//
|
|
||||||
// Modified by Kermitt Quirk 19/01/2006
|
|
||||||
// To add support for 5 face prim instead of 3
|
|
||||||
//
|
|
||||||
// Core XyText Originally Written by Xylor Baysklef
|
|
||||||
//
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
/////////////// CONSTANTS ///////////////////
|
|
||||||
// XyText Message Map.
|
|
||||||
integer DISPLAY_STRING = 204000;
|
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
|
||||||
integer REMAP_INDICES = 204002;
|
|
||||||
integer RESET_INDICES = 204003;
|
|
||||||
integer SET_FADE_OPTIONS = 204004;
|
|
||||||
integer SET_FONT_TEXTURE = 204005;
|
|
||||||
integer SET_LINE_COLOR = 204006;
|
|
||||||
integer SET_COLOR = 204007;
|
|
||||||
integer RESCAN_LINKSET = 204008;
|
|
||||||
|
|
||||||
//internal API
|
|
||||||
integer REGISTER_SLAVE = 205000;
|
|
||||||
integer SLAVE_RECOGNIZED = 205001;
|
|
||||||
integer SLAVE_DISPLAY = 205003;
|
|
||||||
integer SLAVE_DISPLAY_EXTENDED = 205004;
|
|
||||||
integer SLAVE_RESET = 205005;
|
|
||||||
|
|
||||||
// This is an extended character escape sequence.
|
|
||||||
string ESCAPE_SEQUENCE = "\e";
|
|
||||||
|
|
||||||
// This is used to get an index for the extended character.
|
|
||||||
string EXTENDED_INDEX = "12345";
|
|
||||||
|
|
||||||
// Face numbers.
|
|
||||||
integer FACE_1 = 3;
|
|
||||||
integer FACE_2 = 7;
|
|
||||||
integer FACE_3 = 4;
|
|
||||||
integer FACE_4 = 6;
|
|
||||||
integer FACE_5 = 1;
|
|
||||||
|
|
||||||
// Used to hide the text after a fade-out.
|
|
||||||
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
|
|
||||||
key null_key = NULL_KEY;
|
|
||||||
///////////// END CONSTANTS ////////////////
|
|
||||||
|
|
||||||
///////////// GLOBAL VARIABLES ///////////////
|
|
||||||
// This is the key of the font we are displaying.
|
|
||||||
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
|
|
||||||
// All displayable characters. Default to ASCII order.
|
|
||||||
string gCharIndex;
|
|
||||||
|
|
||||||
// This is whether or not to use the fade in/out special effect.
|
|
||||||
integer gCellUseFading = FALSE;
|
|
||||||
// This is how long to display the text before fading out (if using
|
|
||||||
// fading special effect).
|
|
||||||
// Note: < 0 means don't fade out.
|
|
||||||
float gCellHoldDelay = 1.0;
|
|
||||||
|
|
||||||
integer gSlaveRegistered;
|
|
||||||
list gSlaveNames;
|
|
||||||
|
|
||||||
integer BANK_STRIDE=3; //offset, length, highest_dirty
|
|
||||||
list gBankingData;
|
|
||||||
|
|
||||||
/////////// END GLOBAL VARIABLES ////////////
|
|
||||||
|
|
||||||
ResetCharIndex() {
|
|
||||||
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
|
|
||||||
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
|
|
||||||
gCharIndex += "\n\n\n\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
vector GetGridOffset(integer index) {
|
|
||||||
// Calculate the offset needed to display this character.
|
|
||||||
integer Row = index / 10;
|
|
||||||
integer Col = index % 10;
|
|
||||||
|
|
||||||
// Return the offset in the texture.
|
|
||||||
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowChars(integer link,vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
|
|
||||||
// Set the primitive textures directly.
|
|
||||||
|
|
||||||
// <-0.256, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <0.130, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <-0.74, 0, 0>
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderString(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderWithEffects(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// First set the alpha to the lowest possible.
|
|
||||||
llSetLinkAlpha(link,0.05, ALL_SIDES);
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); // Now turn up the alpha until it is at full strength.
|
|
||||||
float Alpha = 0.10;
|
|
||||||
for (; Alpha <= 1.0; Alpha += 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// See if we want to fade out as well.
|
|
||||||
if (gCellHoldDelay < 0.0)
|
|
||||||
// No, bail out. (Just keep showing the string at full strength).
|
|
||||||
return;
|
|
||||||
// Hold the text for a while.
|
|
||||||
llSleep(gCellHoldDelay);
|
|
||||||
// Now fade out.
|
|
||||||
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// Make the text transparent to fully hide it.
|
|
||||||
llSetLinkTexture(link,TRANSPARENT, ALL_SIDES);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer RenderExtended(integer link, string str, integer render) {
|
|
||||||
// Look for escape sequences.
|
|
||||||
integer length = 0;
|
|
||||||
list Parsed = llParseString2List(str, [], (list)ESCAPE_SEQUENCE);
|
|
||||||
integer ParsedLen = llGetListLength(Parsed);
|
|
||||||
|
|
||||||
// Create a list of index values to work with.
|
|
||||||
list Indices;
|
|
||||||
// We start with room for 5 indices.
|
|
||||||
integer IndicesLeft = 5;
|
|
||||||
|
|
||||||
string Token;
|
|
||||||
integer Clipped;
|
|
||||||
integer LastWasEscapeSequence = FALSE;
|
|
||||||
// Work from left to right.
|
|
||||||
integer i = 0;
|
|
||||||
for (; i < ParsedLen && IndicesLeft > 0; ++i) {
|
|
||||||
Token = llList2String(Parsed, i);
|
|
||||||
|
|
||||||
// If this is an escape sequence, just set the flag and move on.
|
|
||||||
if (Token == ESCAPE_SEQUENCE) {
|
|
||||||
LastWasEscapeSequence = TRUE;
|
|
||||||
}
|
|
||||||
else { // Token != ESCAPE_SEQUENCE
|
|
||||||
// Otherwise this is a normal token. Check its length.
|
|
||||||
Clipped = FALSE;
|
|
||||||
integer TokenLength = llStringLength(Token);
|
|
||||||
// Clip if necessary.
|
|
||||||
if (TokenLength > IndicesLeft) {
|
|
||||||
TokenLength = llStringLength(Token = llGetSubString(Token, 0, IndicesLeft - 1));
|
|
||||||
IndicesLeft = 0;
|
|
||||||
Clipped = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IndicesLeft -= TokenLength;
|
|
||||||
|
|
||||||
// Was the previous token an escape sequence?
|
|
||||||
if (LastWasEscapeSequence) {
|
|
||||||
// Yes, the first character is an escape character, the rest are normal.
|
|
||||||
length += 2 + TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
// This is the extended character.
|
|
||||||
Indices += (llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95);
|
|
||||||
|
|
||||||
// These are the normal characters.
|
|
||||||
integer j = 1;
|
|
||||||
for (; j < TokenLength; ++j)
|
|
||||||
{
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // Normal string.
|
|
||||||
// Just add the characters normally.
|
|
||||||
length += TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
integer j = 0;
|
|
||||||
for (; j < TokenLength; ++j)
|
|
||||||
{
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unset this flag, since this was not an escape sequence.
|
|
||||||
LastWasEscapeSequence = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the indices to create grid positions.
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
integer ConvertIndex(integer index) {
|
|
||||||
// This converts from an ASCII based index to our indexing scheme.
|
|
||||||
if (index >= 32) // ' ' or higher
|
|
||||||
index -= 32;
|
|
||||||
else { // index < 32
|
|
||||||
// Quick bounds check.
|
|
||||||
if (index > 15)
|
|
||||||
index = 15;
|
|
||||||
|
|
||||||
index += 94; // extended characters
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PassToRender(integer render,string message, integer bank)
|
|
||||||
{
|
|
||||||
// float time;
|
|
||||||
integer extendedlen = 0;
|
|
||||||
integer link;
|
|
||||||
integer i = 0;
|
|
||||||
integer msgLen = llStringLength(message);
|
|
||||||
string TextToRender;
|
|
||||||
integer num_slaves=llGetListLength(gSlaveNames);
|
|
||||||
string slave_name; //avoids unnecessary casts, keeping it as a string
|
|
||||||
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer bank_offset=llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_length=llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
integer bank_highest_dirty=llList2Integer(gBankingData, (bank * BANK_STRIDE) + 2);
|
|
||||||
|
|
||||||
integer x = 0;
|
|
||||||
for (;x < msgLen;x = x + 5)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (i >= bank_length) //we don't want to run off the end of the bank
|
|
||||||
{
|
|
||||||
//set the dirty to max, and bail out, we're done
|
|
||||||
gBankingData=llListReplaceList(gBankingData, (list)bank_length, (bank * BANK_STRIDE) + 2, (bank * BANK_STRIDE) + 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
link = unpack(gXyTextPrims,(i + bank_offset));
|
|
||||||
TextToRender = llGetSubString(message, x, x + 15);
|
|
||||||
|
|
||||||
if(gSlaveRegistered && (link % (num_slaves + 1)))
|
|
||||||
{
|
|
||||||
slave_name=llList2String(gSlaveNames, (link % (num_slaves + 1)) - 1);
|
|
||||||
if (render == 1)
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_DISPLAY, TextToRender, (key)((string)link + "," + slave_name));
|
|
||||||
if (render == 2)
|
|
||||||
{
|
|
||||||
//time = llGetAndResetTime();
|
|
||||||
if(llSubStringIndex(TextToRender,"\e") > x)
|
|
||||||
extendedlen = 5;
|
|
||||||
else
|
|
||||||
extendedlen = RenderExtended(link,TextToRender,0);
|
|
||||||
if (extendedlen > 5)
|
|
||||||
{
|
|
||||||
x += extendedlen - 5;
|
|
||||||
}
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_DISPLAY_EXTENDED, TextToRender, (key)((string)link + "," + slave_name));
|
|
||||||
// llOwnerSay((string)llGetAndResetTime());
|
|
||||||
}
|
|
||||||
//sorry, no fade effect with slave
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (render == 1)
|
|
||||||
RenderString(link,TextToRender);
|
|
||||||
if (render == 2)
|
|
||||||
{
|
|
||||||
extendedlen = RenderExtended(link,TextToRender,1);
|
|
||||||
if (extendedlen > 5)
|
|
||||||
{
|
|
||||||
x += extendedlen - 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (render == 3)
|
|
||||||
RenderWithEffects(link,TextToRender);
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bank_highest_dirty==0)
|
|
||||||
bank_highest_dirty=bank_length;
|
|
||||||
|
|
||||||
integer current_highest_dirty=i;
|
|
||||||
while (i < bank_highest_dirty)
|
|
||||||
{
|
|
||||||
link = unpack(gXyTextPrims,(i + bank_offset));
|
|
||||||
|
|
||||||
if(gSlaveRegistered && (link % (num_slaves+1) != 0))
|
|
||||||
{
|
|
||||||
slave_name=llList2String(gSlaveNames, (link % (num_slaves + 1)) - 1);
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_DISPLAY, " ", (key)((string)link + "," + slave_name));
|
|
||||||
//sorry, no fade effect with slave
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RenderString(link," ");
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
gBankingData=llListReplaceList(gBankingData, (list)current_highest_dirty, (bank * BANK_STRIDE) + 2, (bank * BANK_STRIDE) + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bitwise Voodoo by Gigs Taggart and optimized by Strife Onizuka
|
|
||||||
list gXyTextPrims;
|
|
||||||
|
|
||||||
|
|
||||||
integer get_number_of_prims()
|
|
||||||
{//ignores avatars.
|
|
||||||
integer a = llGetNumberOfPrims();
|
|
||||||
//Mono tweak
|
|
||||||
vector size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
while(size.z > 0)
|
|
||||||
{
|
|
||||||
--a;
|
|
||||||
size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
//functions to pack 8-bit shorts into ints
|
|
||||||
list pack_and_insert(list in_list, integer pos, integer value)
|
|
||||||
{
|
|
||||||
// //figure out the bitpack position
|
|
||||||
// integer pack = pos & 3; //4 bytes per int
|
|
||||||
// pos=pos >> 2;
|
|
||||||
// integer shifted = value << (pack << 3);
|
|
||||||
// integer old_value = llList2Integer(in_list, pos);
|
|
||||||
// shifted = old_value | shifted;
|
|
||||||
// in_list = llListReplaceList(in_list, (list)shifted, pos, pos);
|
|
||||||
// return in_list;
|
|
||||||
//Safe optimized version
|
|
||||||
integer index = pos >> 2;
|
|
||||||
return llListReplaceList(in_list, (list)(llList2Integer(in_list, index) | (value << ((pos & 3) << 3))), index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer unpack(list in_list, integer pos)
|
|
||||||
{
|
|
||||||
return (llList2Integer(in_list, pos >> 2) >> ((pos & 3) << 3)) & 0x000000FF;//unsigned
|
|
||||||
// return (llList2Integer(in_list, pos >> 2) << (((~pos) & 3) << 3)) >> 24;//signed
|
|
||||||
}
|
|
||||||
|
|
||||||
change_color(vector color)
|
|
||||||
{
|
|
||||||
integer num_prims=llGetListLength(gXyTextPrims) << 2;
|
|
||||||
|
|
||||||
integer i = 0;
|
|
||||||
|
|
||||||
for (; i<=num_prims; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
change_line_color(integer bank, vector color)
|
|
||||||
{
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer i = llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_end = i + llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
|
|
||||||
for (; i < bank_end; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init()
|
|
||||||
{
|
|
||||||
integer num_prims=get_number_of_prims();
|
|
||||||
string link_name;
|
|
||||||
integer bank=0;
|
|
||||||
integer prims_pointer=0; //"pointer" to the next entry to be used in the gXyTextPrims list.
|
|
||||||
|
|
||||||
list temp_bank = [];
|
|
||||||
integer temp_bank_stride=2;
|
|
||||||
|
|
||||||
//FIXME: font texture might should be per-bank
|
|
||||||
llMessageLinked(LINK_THIS, SET_FONT_TEXTURE, "", gFontTexture);
|
|
||||||
|
|
||||||
// moving this before the prim scan so that the slaves properly configure themseves before
|
|
||||||
// any requests to display
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_RESET, "" , null_key);
|
|
||||||
|
|
||||||
gXyTextPrims=[];
|
|
||||||
integer x=0;
|
|
||||||
for (;x<64;++x)
|
|
||||||
{
|
|
||||||
gXyTextPrims = (gXyTextPrims = []) + gXyTextPrims + 0; //we need to pad out the list to make it easier to add things in any order later
|
|
||||||
}
|
|
||||||
|
|
||||||
gBankingData = [];
|
|
||||||
@loop;
|
|
||||||
{
|
|
||||||
//loop over all prims, looking for ones in the current bank
|
|
||||||
for(x=0;x<=num_prims;++x)
|
|
||||||
{
|
|
||||||
link_name=llGetLinkName(x);
|
|
||||||
list tmp = llParseString2List(link_name, (list)"-", []);
|
|
||||||
if(llList2String(tmp,0) == "xyzzytext")
|
|
||||||
{
|
|
||||||
if (llList2Integer(tmp,1) == bank)
|
|
||||||
{
|
|
||||||
temp_bank += llList2Integer(tmp,2) + (list)x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_bank != [])
|
|
||||||
{
|
|
||||||
//sort the current bank
|
|
||||||
temp_bank = llListSort(temp_bank, temp_bank_stride, TRUE);
|
|
||||||
|
|
||||||
integer temp_len = llGetListLength(temp_bank);
|
|
||||||
|
|
||||||
//store metadata
|
|
||||||
gBankingData += [prims_pointer, temp_len/temp_bank_stride, 0];
|
|
||||||
|
|
||||||
//repack the bank into the prim list
|
|
||||||
for (x = 0; x < temp_len; x += temp_bank_stride)
|
|
||||||
{
|
|
||||||
gXyTextPrims = pack_and_insert(gXyTextPrims, prims_pointer, llList2Integer(temp_bank, x + 1));
|
|
||||||
++prims_pointer;
|
|
||||||
}
|
|
||||||
++bank;
|
|
||||||
temp_bank=[];
|
|
||||||
jump loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//llOwnerSay((string)llGetFreeMemory());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
// Initialize the character index.
|
|
||||||
ResetCharIndex();
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
on_rez(integer num)
|
|
||||||
{
|
|
||||||
llResetScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
link_message(integer sender, integer channel, string data, key id) {
|
|
||||||
if (id==null_key)
|
|
||||||
id="0";
|
|
||||||
|
|
||||||
if (channel == DISPLAY_STRING) {
|
|
||||||
PassToRender(1,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == DISPLAY_EXTENDED) {
|
|
||||||
PassToRender(2,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == REMAP_INDICES) {
|
|
||||||
// Parse the message, splitting it up into index values.
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
integer i = 0;
|
|
||||||
// Go through the list and swap each pair of indices.
|
|
||||||
for (; i < llGetListLength(Parsed); i += 2) {
|
|
||||||
integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
|
|
||||||
integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
|
|
||||||
|
|
||||||
// Swap these index values.
|
|
||||||
string Value1 = llGetSubString(gCharIndex, Index1, Index1);
|
|
||||||
string Value2 = llGetSubString(gCharIndex, Index2, Index2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index1, Value2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index2, Value1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel == RESCAN_LINKSET)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
else if (channel == RESET_INDICES) {
|
|
||||||
// Restore the character index back to default settings.
|
|
||||||
ResetCharIndex();
|
|
||||||
}
|
|
||||||
else if (channel == SET_FADE_OPTIONS) {
|
|
||||||
// Change the channel we listen to for cell commands, the
|
|
||||||
// starting character position to extract from, and
|
|
||||||
// special effect attributes.
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
gCellUseFading = (integer) llList2String(Parsed, 0);
|
|
||||||
gCellHoldDelay = (float) llList2String(Parsed, 1);
|
|
||||||
}
|
|
||||||
else if (channel == SET_FONT_TEXTURE) {
|
|
||||||
// Use the new texture instead of the current one.
|
|
||||||
gFontTexture = id;
|
|
||||||
}
|
|
||||||
else if (channel == SET_COLOR) {
|
|
||||||
change_color((vector)data);
|
|
||||||
}
|
|
||||||
else if (channel == SET_LINE_COLOR) {
|
|
||||||
change_line_color((integer)((string)id), (vector)data);
|
|
||||||
}
|
|
||||||
else if (channel == REGISTER_SLAVE)
|
|
||||||
{
|
|
||||||
if(!~llListFindList(gSlaveNames, (list)data))
|
|
||||||
{//isn't registered yet
|
|
||||||
gSlaveNames += data;
|
|
||||||
gSlaveRegistered=TRUE;
|
|
||||||
//llOwnerSay((string)llGetListLength(gSlaveNames) + " Slave(s) Recognized: " + data);
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
// {//it already exists
|
|
||||||
// llOwnerSay((string)llGetListLength(gSlaveNames) + " Slave, Existing Slave Recognized: " + data);
|
|
||||||
// }
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_RECOGNIZED, data , null_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
changed(integer change)
|
|
||||||
{
|
|
||||||
if(change&CHANGED_INVENTORY)
|
|
||||||
{
|
|
||||||
if(gSlaveRegistered)
|
|
||||||
{
|
|
||||||
//by using negative indexes they don't need to be adjusted when an entry is deleted.
|
|
||||||
integer x = ~llGetListLength(gSlaveNames);
|
|
||||||
while(++x)
|
|
||||||
{
|
|
||||||
if (!~llGetInventoryType(llList2String(gSlaveNames, x)))
|
|
||||||
{
|
|
||||||
//llOwnerSay("Slave Removed: " + llList2String(gSlaveNames, x));
|
|
||||||
gSlaveNames = llDeleteSubList(gSlaveNames, x, x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gSlaveRegistered = !(gSlaveNames == []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,521 +0,0 @@
|
||||||
////////////////////////////////////////////
|
|
||||||
// XyzzyText v2.1 Script (Set Line Color) by Huney Jewell
|
|
||||||
// Modified to listen directly to scoreboard channel
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
/////////////// CONSTANTS ///////////////////
|
|
||||||
// XyText Message Map.
|
|
||||||
integer DISPLAY_STRING = 204000;
|
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
|
||||||
integer REMAP_INDICES = 204002;
|
|
||||||
integer RESET_INDICES = 204003;
|
|
||||||
integer SET_FADE_OPTIONS = 204004;
|
|
||||||
integer SET_FONT_TEXTURE = 204005;
|
|
||||||
integer SET_LINE_COLOR = 204006;
|
|
||||||
integer SET_COLOR = 204007;
|
|
||||||
integer RESCAN_LINKSET = 204008;
|
|
||||||
|
|
||||||
//internal API
|
|
||||||
integer REGISTER_SLAVE = 205000;
|
|
||||||
integer SLAVE_RECOGNIZED = 205001;
|
|
||||||
integer SLAVE_DISPLAY = 205003;
|
|
||||||
integer SLAVE_DISPLAY_EXTENDED = 205004;
|
|
||||||
integer SLAVE_RESET = 205005;
|
|
||||||
|
|
||||||
// Communication channel from scoreboard
|
|
||||||
integer LEADERBOARD_CHANNEL = -12346;
|
|
||||||
|
|
||||||
// This is an extended character escape sequence.
|
|
||||||
string ESCAPE_SEQUENCE = "\\e";
|
|
||||||
|
|
||||||
// This is used to get an index for the extended character.
|
|
||||||
string EXTENDED_INDEX = "12345";
|
|
||||||
|
|
||||||
// Face numbers.
|
|
||||||
integer FACE_1 = 3;
|
|
||||||
integer FACE_2 = 7;
|
|
||||||
integer FACE_3 = 4;
|
|
||||||
integer FACE_4 = 6;
|
|
||||||
integer FACE_5 = 1;
|
|
||||||
|
|
||||||
// Used to hide the text after a fade-out.
|
|
||||||
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
|
|
||||||
key null_key = NULL_KEY;
|
|
||||||
///////////// END CONSTANTS ////////////////
|
|
||||||
|
|
||||||
///////////// GLOBAL VARIABLES ///////////////
|
|
||||||
// This is the key of the font we are displaying.
|
|
||||||
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
|
|
||||||
// All displayable characters. Default to ASCII order.
|
|
||||||
string gCharIndex;
|
|
||||||
|
|
||||||
// This is whether or not to use the fade in/out special effect.
|
|
||||||
integer gCellUseFading = FALSE;
|
|
||||||
// This is how long to display the text before fading out (if using
|
|
||||||
// fading special effect).
|
|
||||||
// Note: < 0 means don't fade out.
|
|
||||||
float gCellHoldDelay = 1.0;
|
|
||||||
|
|
||||||
integer gSlaveRegistered;
|
|
||||||
list gSlaveNames;
|
|
||||||
|
|
||||||
integer BANK_STRIDE=3; //offset, length, highest_dirty
|
|
||||||
list gBankingData;
|
|
||||||
|
|
||||||
list gXyTextPrims; //rather than make it generic, we just reuse this from the original script
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
// *** IMPORTANT: SET THIS FOR EACH XYZZYTEXT OBJECT ***
|
|
||||||
//
|
|
||||||
// Bank 0 = LEFT display (set MY_BANK = 0;)
|
|
||||||
// Bank 1 = MIDDLE display (set MY_BANK = 1;)
|
|
||||||
// Bank 2 = RIGHT display (set MY_BANK = 2;)
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
integer MY_BANK = 1; // ← CHANGE THIS NUMBER FOR EACH OBJECT!
|
|
||||||
|
|
||||||
/////////// END GLOBAL VARIABLES ////////////
|
|
||||||
|
|
||||||
ResetCharIndex() {
|
|
||||||
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
|
|
||||||
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
|
|
||||||
gCharIndex += "\\n\\n\\n\\n\\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
vector GetGridOffset(integer index) {
|
|
||||||
// Calculate the offset needed to display this character.
|
|
||||||
integer Row = index / 10;
|
|
||||||
integer Col = index % 10;
|
|
||||||
|
|
||||||
// Return the offset in the texture.
|
|
||||||
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowChars(integer link,vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
|
|
||||||
// Set the primitive textures directly.
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderString(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderWithEffects(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// First set the alpha to the lowest possible.
|
|
||||||
llSetLinkAlpha(link,0.05, ALL_SIDES);
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); // Now turn up the alpha until it is at full strength.
|
|
||||||
float Alpha = 0.10;
|
|
||||||
for (; Alpha <= 1.0; Alpha += 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// See if we want to fade out as well.
|
|
||||||
if (gCellHoldDelay < 0.0)
|
|
||||||
// No, bail out. (Just keep showing the string at full strength).
|
|
||||||
return;
|
|
||||||
// Hold the text for a while.
|
|
||||||
llSleep(gCellHoldDelay);
|
|
||||||
// Now fade out.
|
|
||||||
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// Make the text transparent to fully hide it.
|
|
||||||
llSetLinkTexture(link,TRANSPARENT, ALL_SIDES);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer RenderExtended(integer link, string str, integer render) {
|
|
||||||
// Look for escape sequences.
|
|
||||||
integer length = 0;
|
|
||||||
list Parsed = llParseString2List(str, [], (list)ESCAPE_SEQUENCE);
|
|
||||||
integer ParsedLen = llGetListLength(Parsed);
|
|
||||||
|
|
||||||
// Create a list of index values to work with.
|
|
||||||
list Indices;
|
|
||||||
// We start with room for 5 indices.
|
|
||||||
integer IndicesLeft = 5;
|
|
||||||
|
|
||||||
string Token;
|
|
||||||
integer Clipped;
|
|
||||||
integer LastWasEscapeSequence = FALSE;
|
|
||||||
// Work from left to right.
|
|
||||||
integer i = 0;
|
|
||||||
for (; i < ParsedLen && IndicesLeft > 0; ++i) {
|
|
||||||
Token = llList2String(Parsed, i);
|
|
||||||
|
|
||||||
// If this is an escape sequence, just set the flag and move on.
|
|
||||||
if (Token == ESCAPE_SEQUENCE) {
|
|
||||||
LastWasEscapeSequence = TRUE;
|
|
||||||
}
|
|
||||||
else { // Token != ESCAPE_SEQUENCE
|
|
||||||
// Otherwise this is a normal token. Check its length.
|
|
||||||
Clipped = FALSE;
|
|
||||||
integer TokenLength = llStringLength(Token);
|
|
||||||
// Clip if necessary.
|
|
||||||
if (TokenLength > IndicesLeft) {
|
|
||||||
TokenLength = llStringLength(Token = llGetSubString(Token, 0, IndicesLeft - 1));
|
|
||||||
IndicesLeft = 0;
|
|
||||||
Clipped = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IndicesLeft -= TokenLength;
|
|
||||||
|
|
||||||
// Was the previous token an escape sequence?
|
|
||||||
if (LastWasEscapeSequence) {
|
|
||||||
// Yes, the first character is an escape character, the rest are normal.
|
|
||||||
length += 2 + TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
// This is the extended character.
|
|
||||||
Indices += (llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95);
|
|
||||||
|
|
||||||
// These are the normal characters.
|
|
||||||
integer j = 1;
|
|
||||||
for(; j < TokenLength; ++j) {
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LastWasEscapeSequence = FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Nope, just normal characters.
|
|
||||||
length += TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
// Just add the characters normally.
|
|
||||||
integer j = 0;
|
|
||||||
for(; j < TokenLength; ++j) {
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail out if this was a clipped token.
|
|
||||||
if (Clipped)
|
|
||||||
i = ParsedLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(render) {
|
|
||||||
// Make sure we have 5 indices.
|
|
||||||
integer IndicesCount = llGetListLength(Indices);
|
|
||||||
for (; IndicesCount < 5; ++IndicesCount) {
|
|
||||||
Indices += (llSubStringIndex(gCharIndex, " "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the indices to create grid positions.
|
|
||||||
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
integer ConvertIndex(integer index) {
|
|
||||||
// This function is used to convert from an ASCII based index to our
|
|
||||||
// internal representation.
|
|
||||||
if (index >= 32)
|
|
||||||
// ASCII character, subtract 32.
|
|
||||||
return index - 32;
|
|
||||||
else if (index == 10)
|
|
||||||
// Newline character.
|
|
||||||
return 94;
|
|
||||||
else
|
|
||||||
// Everything else is a space.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PassToRender(integer render_type, string message, integer bank) {
|
|
||||||
if(bank != MY_BANK) {
|
|
||||||
// This message is not for our bank, ignore it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
list lines = llParseString2List(message, ["\n"], []);
|
|
||||||
integer line_count = llGetListLength(lines);
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer i = llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_end = i + llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
|
|
||||||
for(; i < bank_end && (i - llList2Integer(gBankingData, (bank * BANK_STRIDE))) < line_count; ++i) {
|
|
||||||
integer link = unpack(gXyTextPrims, i);
|
|
||||||
if(link) {
|
|
||||||
string line = llList2String(lines, i - llList2Integer(gBankingData, (bank * BANK_STRIDE)));
|
|
||||||
|
|
||||||
if(render_type == 1) {
|
|
||||||
RenderString(link, line);
|
|
||||||
} else if(render_type == 2) {
|
|
||||||
RenderExtended(link, line, TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
integer get_number_of_prims()
|
|
||||||
{//ignores avatars.
|
|
||||||
integer a = llGetNumberOfPrims();
|
|
||||||
//Mono tweak
|
|
||||||
vector size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
while(size.z > 0)
|
|
||||||
{
|
|
||||||
--a;
|
|
||||||
size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
//functions to pack 8-bit shorts into ints
|
|
||||||
list pack_and_insert(list in_list, integer pos, integer value)
|
|
||||||
{
|
|
||||||
//Safe optimized version
|
|
||||||
integer index = pos >> 2;
|
|
||||||
return llListReplaceList(in_list, (list)(llList2Integer(in_list, index) | (value << ((pos & 3) << 3))), index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer unpack(list in_list, integer pos)
|
|
||||||
{
|
|
||||||
return (llList2Integer(in_list, pos >> 2) >> ((pos & 3) << 3)) & 0x000000FF;//unsigned
|
|
||||||
}
|
|
||||||
|
|
||||||
change_color(vector color)
|
|
||||||
{
|
|
||||||
integer num_prims=llGetListLength(gXyTextPrims) << 2;
|
|
||||||
|
|
||||||
integer i = 0;
|
|
||||||
|
|
||||||
for (; i<=num_prims; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
change_line_color(integer bank, vector color)
|
|
||||||
{
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer i = llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_end = i + llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
|
|
||||||
for (; i < bank_end; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init()
|
|
||||||
{
|
|
||||||
integer num_prims=get_number_of_prims();
|
|
||||||
string link_name;
|
|
||||||
integer prims_pointer=0;
|
|
||||||
|
|
||||||
list temp_bank = [];
|
|
||||||
integer temp_bank_stride=2;
|
|
||||||
|
|
||||||
//FIXME: font texture might should be per-bank
|
|
||||||
llMessageLinked(LINK_THIS, SET_FONT_TEXTURE, "", gFontTexture);
|
|
||||||
|
|
||||||
// moving this before the prim scan so that the slaves properly configure themseves before
|
|
||||||
// any requests to display
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_RESET, "" , null_key);
|
|
||||||
|
|
||||||
gXyTextPrims=[];
|
|
||||||
integer x=0;
|
|
||||||
for (;x<64;++x)
|
|
||||||
{
|
|
||||||
gXyTextPrims = (gXyTextPrims = []) + gXyTextPrims + 0; //we need to pad out the list to make it easier to add things in any order later
|
|
||||||
}
|
|
||||||
|
|
||||||
gBankingData = [];
|
|
||||||
|
|
||||||
// Only look for prims that match MY_BANK
|
|
||||||
for(x=0;x<=num_prims;++x)
|
|
||||||
{
|
|
||||||
link_name=llGetLinkName(x);
|
|
||||||
|
|
||||||
list tmp = llParseString2List(link_name, (list)"-", []);
|
|
||||||
if(llList2String(tmp,0) == "xyzzytext")
|
|
||||||
{
|
|
||||||
integer prim_bank = llList2Integer(tmp,1);
|
|
||||||
integer prim_position = llList2Integer(tmp,2);
|
|
||||||
|
|
||||||
if (prim_bank == MY_BANK)
|
|
||||||
{
|
|
||||||
temp_bank += prim_position + (list)x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_bank != [])
|
|
||||||
{
|
|
||||||
//sort the current bank by position
|
|
||||||
temp_bank = llListSort(temp_bank, temp_bank_stride, TRUE);
|
|
||||||
|
|
||||||
integer temp_len = llGetListLength(temp_bank);
|
|
||||||
|
|
||||||
//store metadata for MY_BANK at position MY_BANK in the banking data
|
|
||||||
while(llGetListLength(gBankingData) <= (MY_BANK * BANK_STRIDE + 2)) {
|
|
||||||
gBankingData += [0, 0, 0]; // pad the list
|
|
||||||
}
|
|
||||||
|
|
||||||
gBankingData = llListReplaceList(gBankingData, [prims_pointer, temp_len/temp_bank_stride, 0],
|
|
||||||
MY_BANK * BANK_STRIDE, MY_BANK * BANK_STRIDE + 2);
|
|
||||||
|
|
||||||
//repack the bank into the prim list
|
|
||||||
for (x = 0; x < temp_len; x += temp_bank_stride)
|
|
||||||
{
|
|
||||||
integer link_num = llList2Integer(temp_bank, x + 1);
|
|
||||||
gXyTextPrims = pack_and_insert(gXyTextPrims, prims_pointer, link_num);
|
|
||||||
++prims_pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llOwnerSay("XyzzyText Bank " + (string)MY_BANK + " ready with " + (string)prims_pointer + " prims");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
// Initialize the character index.
|
|
||||||
ResetCharIndex();
|
|
||||||
|
|
||||||
// Listen for messages from scoreboard
|
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
on_rez(integer num)
|
|
||||||
{
|
|
||||||
llResetScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string message) {
|
|
||||||
if (channel == LEADERBOARD_CHANNEL) {
|
|
||||||
if (MY_BANK == 0 && llSubStringIndex(message, "LEFT_TEXT|") == 0) {
|
|
||||||
// This is bank 0 (left), process LEFT_TEXT messages
|
|
||||||
string leftText = llGetSubString(message, 10, -1);
|
|
||||||
PassToRender(1, leftText, 0);
|
|
||||||
|
|
||||||
} else if (MY_BANK == 1 && llSubStringIndex(message, "MIDDLE_TEXT|") == 0) {
|
|
||||||
// This is bank 1 (middle), process MIDDLE_TEXT messages
|
|
||||||
string middleText = llGetSubString(message, 12, -1);
|
|
||||||
PassToRender(1, middleText, 1);
|
|
||||||
|
|
||||||
} else if (MY_BANK == 2 && llSubStringIndex(message, "RIGHT_TEXT|") == 0) {
|
|
||||||
// This is bank 2 (right), process RIGHT_TEXT messages
|
|
||||||
string rightText = llGetSubString(message, 11, -1);
|
|
||||||
PassToRender(1, rightText, 2);
|
|
||||||
|
|
||||||
} else if (message == "CLEAR_LEADERBOARD") {
|
|
||||||
// Clear display for this bank
|
|
||||||
PassToRender(1, " ", MY_BANK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
link_message(integer sender, integer channel, string data, key id) {
|
|
||||||
if (id==null_key)
|
|
||||||
id="0";
|
|
||||||
|
|
||||||
if (channel == DISPLAY_STRING) {
|
|
||||||
PassToRender(1,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == DISPLAY_EXTENDED) {
|
|
||||||
PassToRender(2,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == REMAP_INDICES) {
|
|
||||||
// Parse the message, splitting it up into index values.
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
integer i = 0;
|
|
||||||
// Go through the list and swap each pair of indices.
|
|
||||||
for (; i < llGetListLength(Parsed); i += 2) {
|
|
||||||
integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
|
|
||||||
integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
|
|
||||||
|
|
||||||
// Swap these index values.
|
|
||||||
string Value1 = llGetSubString(gCharIndex, Index1, Index1);
|
|
||||||
string Value2 = llGetSubString(gCharIndex, Index2, Index2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index1, Value2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index2, Value1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel == RESCAN_LINKSET)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
else if (channel == RESET_INDICES) {
|
|
||||||
// Restore the character index back to default settings.
|
|
||||||
ResetCharIndex();
|
|
||||||
}
|
|
||||||
else if (channel == SET_FADE_OPTIONS) {
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
gCellUseFading = llList2Integer(Parsed, 0);
|
|
||||||
gCellHoldDelay = llList2Float(Parsed, 1);
|
|
||||||
}
|
|
||||||
else if (channel == SET_FONT_TEXTURE) {
|
|
||||||
gFontTexture = id;
|
|
||||||
}
|
|
||||||
else if (channel == SET_LINE_COLOR) {
|
|
||||||
change_line_color((integer)data, (vector)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == SET_COLOR) {
|
|
||||||
change_color((vector)data);
|
|
||||||
}
|
|
||||||
else if (channel == REGISTER_SLAVE) {
|
|
||||||
gSlaveNames += data;
|
|
||||||
llMessageLinked(sender, SLAVE_RECOGNIZED, "", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,540 +0,0 @@
|
||||||
////////////////////////////////////////////
|
|
||||||
// XyzzyText v2.1 Script (Set Line Color) by Huney Jewell
|
|
||||||
// Modified to listen directly to scoreboard channel
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
/////////////// CONSTANTS ///////////////////
|
|
||||||
// XyText Message Map.
|
|
||||||
integer DISPLAY_STRING = 204000;
|
|
||||||
integer DISPLAY_EXTENDED = 204001;
|
|
||||||
integer REMAP_INDICES = 204002;
|
|
||||||
integer RESET_INDICES = 204003;
|
|
||||||
integer SET_FADE_OPTIONS = 204004;
|
|
||||||
integer SET_FONT_TEXTURE = 204005;
|
|
||||||
integer SET_LINE_COLOR = 204006;
|
|
||||||
integer SET_COLOR = 204007;
|
|
||||||
integer RESCAN_LINKSET = 204008;
|
|
||||||
|
|
||||||
//internal API
|
|
||||||
integer REGISTER_SLAVE = 205000;
|
|
||||||
integer SLAVE_RECOGNIZED = 205001;
|
|
||||||
integer SLAVE_DISPLAY = 205003;
|
|
||||||
integer SLAVE_DISPLAY_EXTENDED = 205004;
|
|
||||||
integer SLAVE_RESET = 205005;
|
|
||||||
|
|
||||||
// Communication channel from scoreboard
|
|
||||||
integer LEADERBOARD_CHANNEL = -12346;
|
|
||||||
|
|
||||||
// This is an extended character escape sequence.
|
|
||||||
string ESCAPE_SEQUENCE = "\\e";
|
|
||||||
|
|
||||||
// This is used to get an index for the extended character.
|
|
||||||
string EXTENDED_INDEX = "12345";
|
|
||||||
|
|
||||||
// Face numbers.
|
|
||||||
integer FACE_1 = 3;
|
|
||||||
integer FACE_2 = 7;
|
|
||||||
integer FACE_3 = 4;
|
|
||||||
integer FACE_4 = 6;
|
|
||||||
integer FACE_5 = 1;
|
|
||||||
|
|
||||||
// Used to hide the text after a fade-out.
|
|
||||||
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
|
|
||||||
key null_key = NULL_KEY;
|
|
||||||
///////////// END CONSTANTS ////////////////
|
|
||||||
|
|
||||||
///////////// GLOBAL VARIABLES ///////////////
|
|
||||||
// This is the key of the font we are displaying.
|
|
||||||
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
|
|
||||||
// All displayable characters. Default to ASCII order.
|
|
||||||
string gCharIndex;
|
|
||||||
|
|
||||||
// This is whether or not to use the fade in/out special effect.
|
|
||||||
integer gCellUseFading = FALSE;
|
|
||||||
// This is how long to display the text before fading out (if using
|
|
||||||
// fading special effect).
|
|
||||||
// Note: < 0 means don't fade out.
|
|
||||||
float gCellHoldDelay = 1.0;
|
|
||||||
|
|
||||||
integer gSlaveRegistered;
|
|
||||||
list gSlaveNames;
|
|
||||||
|
|
||||||
integer BANK_STRIDE=3; //offset, length, highest_dirty
|
|
||||||
list gBankingData;
|
|
||||||
|
|
||||||
list gXyTextPrims; //rather than make it generic, we just reuse this from the original script
|
|
||||||
|
|
||||||
// Bank configuration - SET THIS FOR EACH OBJECT
|
|
||||||
integer MY_BANK = 1; // Change this: 0 for left, 1 for middle, 2 for right
|
|
||||||
|
|
||||||
/////////// END GLOBAL VARIABLES ////////////
|
|
||||||
|
|
||||||
ResetCharIndex() {
|
|
||||||
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
|
|
||||||
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
|
|
||||||
gCharIndex += "\\n\\n\\n\\n\\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
vector GetGridOffset(integer index) {
|
|
||||||
// Calculate the offset needed to display this character.
|
|
||||||
integer Row = index / 10;
|
|
||||||
integer Col = index % 10;
|
|
||||||
|
|
||||||
// Return the offset in the texture.
|
|
||||||
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowChars(integer link,vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
|
|
||||||
// Set the primitive textures directly.
|
|
||||||
|
|
||||||
// <-0.256, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <0.130, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <-0.74, 0, 0>
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderString(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderWithEffects(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// First set the alpha to the lowest possible.
|
|
||||||
llSetLinkAlpha(link,0.05, ALL_SIDES);
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); // Now turn up the alpha until it is at full strength.
|
|
||||||
float Alpha = 0.10;
|
|
||||||
for (; Alpha <= 1.0; Alpha += 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// See if we want to fade out as well.
|
|
||||||
if (gCellHoldDelay < 0.0)
|
|
||||||
// No, bail out. (Just keep showing the string at full strength).
|
|
||||||
return;
|
|
||||||
// Hold the text for a while.
|
|
||||||
llSleep(gCellHoldDelay);
|
|
||||||
// Now fade out.
|
|
||||||
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
|
|
||||||
llSetLinkAlpha(link,Alpha, ALL_SIDES);
|
|
||||||
// Make the text transparent to fully hide it.
|
|
||||||
llSetLinkTexture(link,TRANSPARENT, ALL_SIDES);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer RenderExtended(integer link, string str, integer render) {
|
|
||||||
// Look for escape sequences.
|
|
||||||
integer length = 0;
|
|
||||||
list Parsed = llParseString2List(str, [], (list)ESCAPE_SEQUENCE);
|
|
||||||
integer ParsedLen = llGetListLength(Parsed);
|
|
||||||
|
|
||||||
// Create a list of index values to work with.
|
|
||||||
list Indices;
|
|
||||||
// We start with room for 5 indices.
|
|
||||||
integer IndicesLeft = 5;
|
|
||||||
|
|
||||||
string Token;
|
|
||||||
integer Clipped;
|
|
||||||
integer LastWasEscapeSequence = FALSE;
|
|
||||||
// Work from left to right.
|
|
||||||
integer i = 0;
|
|
||||||
for (; i < ParsedLen && IndicesLeft > 0; ++i) {
|
|
||||||
Token = llList2String(Parsed, i);
|
|
||||||
|
|
||||||
// If this is an escape sequence, just set the flag and move on.
|
|
||||||
if (Token == ESCAPE_SEQUENCE) {
|
|
||||||
LastWasEscapeSequence = TRUE;
|
|
||||||
}
|
|
||||||
else { // Token != ESCAPE_SEQUENCE
|
|
||||||
// Otherwise this is a normal token. Check its length.
|
|
||||||
Clipped = FALSE;
|
|
||||||
integer TokenLength = llStringLength(Token);
|
|
||||||
// Clip if necessary.
|
|
||||||
if (TokenLength > IndicesLeft) {
|
|
||||||
TokenLength = llStringLength(Token = llGetSubString(Token, 0, IndicesLeft - 1));
|
|
||||||
IndicesLeft = 0;
|
|
||||||
Clipped = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IndicesLeft -= TokenLength;
|
|
||||||
|
|
||||||
// Was the previous token an escape sequence?
|
|
||||||
if (LastWasEscapeSequence) {
|
|
||||||
// Yes, the first character is an escape character, the rest are normal.
|
|
||||||
length += 2 + TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
// This is the extended character.
|
|
||||||
Indices += (llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95);
|
|
||||||
|
|
||||||
// These are the normal characters.
|
|
||||||
integer j = 1;
|
|
||||||
for(; j < TokenLength; ++j) {
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LastWasEscapeSequence = FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Nope, just normal characters.
|
|
||||||
length += TokenLength;
|
|
||||||
if(render)
|
|
||||||
{
|
|
||||||
// Just add the characters normally.
|
|
||||||
integer j = 0;
|
|
||||||
for(; j < TokenLength; ++j) {
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail out if this was a clipped token.
|
|
||||||
if (Clipped)
|
|
||||||
i = ParsedLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(render) {
|
|
||||||
// Make sure we have 5 indices.
|
|
||||||
integer IndicesCount = llGetListLength(Indices);
|
|
||||||
for (; IndicesCount < 5; ++IndicesCount) {
|
|
||||||
Indices += (llSubStringIndex(gCharIndex, " "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the indices to create grid positions.
|
|
||||||
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
integer ConvertIndex(integer index) {
|
|
||||||
// This function is used to convert from an ASCII based index to our
|
|
||||||
// internal representation.
|
|
||||||
if (index >= 32)
|
|
||||||
// ASCII character, subtract 32.
|
|
||||||
return index - 32;
|
|
||||||
else if (index == 10)
|
|
||||||
// Newline character.
|
|
||||||
return 94;
|
|
||||||
else
|
|
||||||
// Everything else is a space.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PassToRender(integer render_type, string message, integer bank) {
|
|
||||||
if(bank != MY_BANK) {
|
|
||||||
// This message is not for our bank, ignore it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Bank " + (string)MY_BANK + " received message: " + message);
|
|
||||||
|
|
||||||
list lines = llParseString2List(message, ["\n"], []);
|
|
||||||
integer line_count = llGetListLength(lines);
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer i = llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_end = i + llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
|
|
||||||
for(; i < bank_end && (i - llList2Integer(gBankingData, (bank * BANK_STRIDE))) < line_count; ++i) {
|
|
||||||
integer link = unpack(gXyTextPrims, i);
|
|
||||||
if(link) {
|
|
||||||
string line = llList2String(lines, i - llList2Integer(gBankingData, (bank * BANK_STRIDE)));
|
|
||||||
|
|
||||||
if(render_type == 1) {
|
|
||||||
RenderString(link, line);
|
|
||||||
} else if(render_type == 2) {
|
|
||||||
RenderExtended(link, line, TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
integer get_number_of_prims()
|
|
||||||
{//ignores avatars.
|
|
||||||
integer a = llGetNumberOfPrims();
|
|
||||||
//Mono tweak
|
|
||||||
vector size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
while(size.z > 0)
|
|
||||||
{
|
|
||||||
--a;
|
|
||||||
size = llGetAgentSize(llGetLinkKey(a));
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
//functions to pack 8-bit shorts into ints
|
|
||||||
list pack_and_insert(list in_list, integer pos, integer value)
|
|
||||||
{
|
|
||||||
//Safe optimized version
|
|
||||||
integer index = pos >> 2;
|
|
||||||
return llListReplaceList(in_list, (list)(llList2Integer(in_list, index) | (value << ((pos & 3) << 3))), index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer unpack(list in_list, integer pos)
|
|
||||||
{
|
|
||||||
return (llList2Integer(in_list, pos >> 2) >> ((pos & 3) << 3)) & 0x000000FF;//unsigned
|
|
||||||
}
|
|
||||||
|
|
||||||
change_color(vector color)
|
|
||||||
{
|
|
||||||
integer num_prims=llGetListLength(gXyTextPrims) << 2;
|
|
||||||
|
|
||||||
integer i = 0;
|
|
||||||
|
|
||||||
for (; i<=num_prims; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
change_line_color(integer bank, vector color)
|
|
||||||
{
|
|
||||||
|
|
||||||
//get the bank offset and length
|
|
||||||
integer i = llList2Integer(gBankingData, (bank * BANK_STRIDE));
|
|
||||||
integer bank_end = i + llList2Integer(gBankingData, (bank * BANK_STRIDE) + 1);
|
|
||||||
|
|
||||||
for (; i < bank_end; ++i)
|
|
||||||
{
|
|
||||||
integer link = unpack(gXyTextPrims,i);
|
|
||||||
if (!link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_COLOR, FACE_1, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_2, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_3, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_4, color, 1.0,
|
|
||||||
PRIM_COLOR, FACE_5, color, 1.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init()
|
|
||||||
{
|
|
||||||
integer num_prims=get_number_of_prims();
|
|
||||||
string link_name;
|
|
||||||
integer prims_pointer=0;
|
|
||||||
|
|
||||||
list temp_bank = [];
|
|
||||||
integer temp_bank_stride=2;
|
|
||||||
|
|
||||||
//FIXME: font texture might should be per-bank
|
|
||||||
llMessageLinked(LINK_THIS, SET_FONT_TEXTURE, "", gFontTexture);
|
|
||||||
|
|
||||||
// moving this before the prim scan so that the slaves properly configure themseves before
|
|
||||||
// any requests to display
|
|
||||||
llMessageLinked(LINK_THIS, SLAVE_RESET, "" , null_key);
|
|
||||||
|
|
||||||
gXyTextPrims=[];
|
|
||||||
integer x=0;
|
|
||||||
for (;x<64;++x)
|
|
||||||
{
|
|
||||||
gXyTextPrims = (gXyTextPrims = []) + gXyTextPrims + 0; //we need to pad out the list to make it easier to add things in any order later
|
|
||||||
}
|
|
||||||
|
|
||||||
gBankingData = [];
|
|
||||||
|
|
||||||
// Only look for prims that match MY_BANK
|
|
||||||
llOwnerSay("DEBUG: Scanning " + (string)num_prims + " prims for bank " + (string)MY_BANK);
|
|
||||||
|
|
||||||
for(x=0;x<=num_prims;++x)
|
|
||||||
{
|
|
||||||
link_name=llGetLinkName(x);
|
|
||||||
llOwnerSay("DEBUG: Prim " + (string)x + " name: '" + link_name + "'");
|
|
||||||
|
|
||||||
list tmp = llParseString2List(link_name, (list)"-", []);
|
|
||||||
if(llList2String(tmp,0) == "xyzzytext")
|
|
||||||
{
|
|
||||||
integer prim_bank = llList2Integer(tmp,1);
|
|
||||||
integer prim_position = llList2Integer(tmp,2);
|
|
||||||
|
|
||||||
llOwnerSay("DEBUG: Found xyzzytext prim - bank: " + (string)prim_bank + ", position: " + (string)prim_position);
|
|
||||||
|
|
||||||
if (prim_bank == MY_BANK)
|
|
||||||
{
|
|
||||||
llOwnerSay("DEBUG: Adding prim " + (string)x + " to bank " + (string)MY_BANK);
|
|
||||||
temp_bank += prim_position + (list)x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_bank != [])
|
|
||||||
{
|
|
||||||
llOwnerSay("DEBUG: Found " + (string)(llGetListLength(temp_bank)/temp_bank_stride) + " prims for bank " + (string)MY_BANK);
|
|
||||||
|
|
||||||
//sort the current bank by position
|
|
||||||
temp_bank = llListSort(temp_bank, temp_bank_stride, TRUE);
|
|
||||||
|
|
||||||
integer temp_len = llGetListLength(temp_bank);
|
|
||||||
|
|
||||||
//store metadata for MY_BANK at position MY_BANK in the banking data
|
|
||||||
while(llGetListLength(gBankingData) <= (MY_BANK * BANK_STRIDE + 2)) {
|
|
||||||
gBankingData += [0, 0, 0]; // pad the list
|
|
||||||
}
|
|
||||||
|
|
||||||
gBankingData = llListReplaceList(gBankingData, [prims_pointer, temp_len/temp_bank_stride, 0],
|
|
||||||
MY_BANK * BANK_STRIDE, MY_BANK * BANK_STRIDE + 2);
|
|
||||||
|
|
||||||
//repack the bank into the prim list
|
|
||||||
for (x = 0; x < temp_len; x += temp_bank_stride)
|
|
||||||
{
|
|
||||||
integer link_num = llList2Integer(temp_bank, x + 1);
|
|
||||||
llOwnerSay("DEBUG: Packing prim " + (string)link_num + " at position " + (string)prims_pointer);
|
|
||||||
gXyTextPrims = pack_and_insert(gXyTextPrims, prims_pointer, link_num);
|
|
||||||
++prims_pointer;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
llOwnerSay("DEBUG: No prims found for bank " + (string)MY_BANK);
|
|
||||||
}
|
|
||||||
|
|
||||||
llOwnerSay("XyzzyText initialized for bank " + (string)MY_BANK + " with " + (string)prims_pointer + " prims");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
// Initialize the character index.
|
|
||||||
ResetCharIndex();
|
|
||||||
|
|
||||||
// Listen for messages from scoreboard
|
|
||||||
llListen(LEADERBOARD_CHANNEL, "", "", "");
|
|
||||||
llOwnerSay("XyzzyText Bank " + (string)MY_BANK + " ready - listening on channel " + (string)LEADERBOARD_CHANNEL);
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
on_rez(integer num)
|
|
||||||
{
|
|
||||||
llResetScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string message) {
|
|
||||||
if (channel == LEADERBOARD_CHANNEL) {
|
|
||||||
llOwnerSay("DEBUG: Bank " + (string)MY_BANK + " heard: " + message);
|
|
||||||
|
|
||||||
if (MY_BANK == 0 && llSubStringIndex(message, "LEFT_TEXT|") == 0) {
|
|
||||||
// This is bank 0 (left), process LEFT_TEXT messages
|
|
||||||
string leftText = llGetSubString(message, 10, -1);
|
|
||||||
llOwnerSay("DEBUG: Bank 0 processing LEFT text: " + leftText);
|
|
||||||
PassToRender(1, leftText, 0);
|
|
||||||
|
|
||||||
} else if (MY_BANK == 1 && llSubStringIndex(message, "MIDDLE_TEXT|") == 0) {
|
|
||||||
// This is bank 1 (middle), process MIDDLE_TEXT messages
|
|
||||||
string middleText = llGetSubString(message, 12, -1);
|
|
||||||
llOwnerSay("DEBUG: Bank 1 processing MIDDLE text: " + middleText);
|
|
||||||
PassToRender(1, middleText, 1);
|
|
||||||
|
|
||||||
} else if (MY_BANK == 2 && llSubStringIndex(message, "RIGHT_TEXT|") == 0) {
|
|
||||||
// This is bank 2 (right), process RIGHT_TEXT messages
|
|
||||||
string rightText = llGetSubString(message, 11, -1);
|
|
||||||
llOwnerSay("DEBUG: Bank 2 processing RIGHT text: " + rightText);
|
|
||||||
PassToRender(1, rightText, 2);
|
|
||||||
|
|
||||||
} else if (message == "CLEAR_LEADERBOARD") {
|
|
||||||
// Clear display for this bank
|
|
||||||
llOwnerSay("DEBUG: Bank " + (string)MY_BANK + " clearing display");
|
|
||||||
PassToRender(1, " ", MY_BANK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
link_message(integer sender, integer channel, string data, key id) {
|
|
||||||
if (id==null_key)
|
|
||||||
id="0";
|
|
||||||
|
|
||||||
if (channel == DISPLAY_STRING) {
|
|
||||||
PassToRender(1,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == DISPLAY_EXTENDED) {
|
|
||||||
PassToRender(2,data, (integer)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == REMAP_INDICES) {
|
|
||||||
// Parse the message, splitting it up into index values.
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
integer i = 0;
|
|
||||||
// Go through the list and swap each pair of indices.
|
|
||||||
for (; i < llGetListLength(Parsed); i += 2) {
|
|
||||||
integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
|
|
||||||
integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
|
|
||||||
|
|
||||||
// Swap these index values.
|
|
||||||
string Value1 = llGetSubString(gCharIndex, Index1, Index1);
|
|
||||||
string Value2 = llGetSubString(gCharIndex, Index2, Index2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index1, Value2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index2, Value1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel == RESCAN_LINKSET)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
else if (channel == RESET_INDICES) {
|
|
||||||
// Restore the character index back to default settings.
|
|
||||||
ResetCharIndex();
|
|
||||||
}
|
|
||||||
else if (channel == SET_FADE_OPTIONS) {
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
gCellUseFading = llList2Integer(Parsed, 0);
|
|
||||||
gCellHoldDelay = llList2Float(Parsed, 1);
|
|
||||||
}
|
|
||||||
else if (channel == SET_FONT_TEXTURE) {
|
|
||||||
gFontTexture = id;
|
|
||||||
}
|
|
||||||
else if (channel == SET_LINE_COLOR) {
|
|
||||||
change_line_color((integer)data, (vector)((string)id));
|
|
||||||
}
|
|
||||||
else if (channel == SET_COLOR) {
|
|
||||||
change_color((vector)data);
|
|
||||||
}
|
|
||||||
else if (channel == REGISTER_SLAVE) {
|
|
||||||
gSlaveNames += data;
|
|
||||||
llMessageLinked(sender, SLAVE_RECOGNIZED, "", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
281
xytext_slave.lsl
|
|
@ -1,281 +0,0 @@
|
||||||
////////////////////////////////////////////
|
|
||||||
// XyText v2.0 SLAVE Script (5 Face, Single Texture)
|
|
||||||
//
|
|
||||||
// Modified by Thraxis Epsilon and Gigs Taggart 5/2007
|
|
||||||
// Rewrite to allow one-script-per-object operation
|
|
||||||
//
|
|
||||||
// Modified by Kermitt Quirk 19/01/2006
|
|
||||||
// To add support for 5 face prim instead of 3
|
|
||||||
//
|
|
||||||
// Originally Written by Xylor Baysklef
|
|
||||||
//
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
integer REMAP_INDICES = 204002;
|
|
||||||
integer RESET_INDICES = 204003;
|
|
||||||
|
|
||||||
//internal API
|
|
||||||
integer REGISTER_SLAVE = 205000;
|
|
||||||
integer SLAVE_RECOGNIZED = 205001;
|
|
||||||
integer SLAVE_DISPLAY = 205003;
|
|
||||||
integer SET_FONT_TEXTURE = 204005;
|
|
||||||
|
|
||||||
integer SLAVE_DISPLAY_EXTENDED = 205004;
|
|
||||||
integer SLAVE_RESET = 205005;
|
|
||||||
|
|
||||||
|
|
||||||
// This is an extended character escape sequence.
|
|
||||||
string ESCAPE_SEQUENCE = "\e";
|
|
||||||
|
|
||||||
// This is used to get an index for the extended character.
|
|
||||||
string EXTENDED_INDEX = "12345";
|
|
||||||
|
|
||||||
// Face numbers.
|
|
||||||
integer FACE_1 = 3;
|
|
||||||
integer FACE_2 = 7;
|
|
||||||
integer FACE_3 = 4;
|
|
||||||
integer FACE_4 = 6;
|
|
||||||
integer FACE_5 = 1;
|
|
||||||
|
|
||||||
|
|
||||||
///////////// GLOBAL VARIABLES ///////////////
|
|
||||||
// This is the key of the font we are displaying.
|
|
||||||
key gFontTexture = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
|
|
||||||
// All displayable characters. Default to ASCII order.
|
|
||||||
string gCharIndex;
|
|
||||||
|
|
||||||
integer gActive; //if we are recognized, this is true
|
|
||||||
/////////// END GLOBAL VARIABLES ////////////
|
|
||||||
|
|
||||||
ResetCharIndex() {
|
|
||||||
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
|
|
||||||
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
|
|
||||||
gCharIndex += "\n\n\n\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
vector GetGridOffset(integer index) {
|
|
||||||
// Calculate the offset needed to display this character.
|
|
||||||
integer Row = index / 10;
|
|
||||||
integer Col = index % 10;
|
|
||||||
|
|
||||||
// Return the offset in the texture.
|
|
||||||
return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowChars(integer link,vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {
|
|
||||||
// Set the primitive textures directly.
|
|
||||||
|
|
||||||
// <-0.256, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <0.130, 0, 0>
|
|
||||||
// <0, 0, 0>
|
|
||||||
// <-0.74, 0, 0>
|
|
||||||
|
|
||||||
llSetLinkPrimitiveParamsFast( link,[
|
|
||||||
PRIM_TEXTURE, FACE_1, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset1 + <0.037, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_2, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_3, (string)gFontTexture, <-0.74, 0.1, 0>, grid_offset3 - <0.244, 0, 0>, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_4, (string)gFontTexture, <0.05, 0.1, 0>, grid_offset4, 0.0,
|
|
||||||
PRIM_TEXTURE, FACE_5, (string)gFontTexture, <0.126, 0.1, 0>, grid_offset5 - <0.037, 0, 0>, 0.0
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderString(integer link, string str) {
|
|
||||||
// Get the grid positions for each pair of characters.
|
|
||||||
vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderExtended(integer link, string str) {
|
|
||||||
// Look for escape sequences.
|
|
||||||
list Parsed = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
|
|
||||||
integer ParsedLen = llGetListLength(Parsed);
|
|
||||||
|
|
||||||
// Create a list of index values to work with.
|
|
||||||
list Indices;
|
|
||||||
// We start with room for 5 indices.
|
|
||||||
integer IndicesLeft = 5;
|
|
||||||
|
|
||||||
integer i = 0;
|
|
||||||
string Token;
|
|
||||||
integer Clipped;
|
|
||||||
integer LastWasEscapeSequence = FALSE;
|
|
||||||
// Work from left to right.
|
|
||||||
for (; i < ParsedLen && IndicesLeft > 0; ++i) {
|
|
||||||
Token = llList2String(Parsed, i);
|
|
||||||
|
|
||||||
// If this is an escape sequence, just set the flag and move on.
|
|
||||||
if (Token == ESCAPE_SEQUENCE) {
|
|
||||||
LastWasEscapeSequence = TRUE;
|
|
||||||
}
|
|
||||||
else { // Token != ESCAPE_SEQUENCE
|
|
||||||
// Otherwise this is a normal token. Check its length.
|
|
||||||
Clipped = FALSE;
|
|
||||||
integer TokenLength = llStringLength(Token);
|
|
||||||
// Clip if necessary.
|
|
||||||
if (TokenLength > IndicesLeft) {
|
|
||||||
Token = llGetSubString(Token, 0, IndicesLeft - 1);
|
|
||||||
TokenLength = llStringLength(Token);
|
|
||||||
IndicesLeft = 0;
|
|
||||||
Clipped = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IndicesLeft -= TokenLength;
|
|
||||||
|
|
||||||
// Was the previous token an escape sequence?
|
|
||||||
if (LastWasEscapeSequence) {
|
|
||||||
// Yes, the first character is an escape character, the rest are normal.
|
|
||||||
|
|
||||||
// This is the extended character.
|
|
||||||
Indices += (llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95);
|
|
||||||
|
|
||||||
// These are the normal characters.
|
|
||||||
integer j = 1;
|
|
||||||
for (; j < TokenLength; ++j)
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
else { // Normal string.
|
|
||||||
// Just add the characters normally.
|
|
||||||
integer j;
|
|
||||||
for (j = 0; j < TokenLength; ++j)
|
|
||||||
Indices += llSubStringIndex(gCharIndex, llGetSubString(Token, j, j));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unset this flag, since this was not an escape sequence.
|
|
||||||
LastWasEscapeSequence = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the indices to create grid positions.
|
|
||||||
vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0) );
|
|
||||||
vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
|
|
||||||
vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );
|
|
||||||
vector GridOffset4 = GetGridOffset( llList2Integer(Indices, 3) );
|
|
||||||
vector GridOffset5 = GetGridOffset( llList2Integer(Indices, 4) );
|
|
||||||
|
|
||||||
// Use these grid positions to display the correct textures/offsets.
|
|
||||||
ShowChars(link,GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5);
|
|
||||||
}
|
|
||||||
|
|
||||||
integer ConvertIndex(integer index) {
|
|
||||||
// This converts from an ASCII based index to our indexing scheme.
|
|
||||||
if (index >= 32) // ' ' or higher
|
|
||||||
index -= 32;
|
|
||||||
else { // index < 32
|
|
||||||
// Quick bounds check.
|
|
||||||
if (index > 15)
|
|
||||||
index = 15;
|
|
||||||
|
|
||||||
index += 94; // extended characters
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default
|
|
||||||
{
|
|
||||||
state_entry()
|
|
||||||
{
|
|
||||||
// Initialize the character index.
|
|
||||||
ResetCharIndex();
|
|
||||||
llMessageLinked(LINK_THIS, REGISTER_SLAVE, llGetScriptName() , NULL_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
on_rez(integer num)
|
|
||||||
{
|
|
||||||
llResetScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
link_message(integer sender, integer channel, string data, key id)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (channel == SLAVE_RECOGNIZED)
|
|
||||||
{
|
|
||||||
if (data == llGetScriptName())
|
|
||||||
{
|
|
||||||
gActive=TRUE;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel == SLAVE_DISPLAY)
|
|
||||||
{
|
|
||||||
if (!gActive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list params=llCSV2List((string)id);
|
|
||||||
if (llList2String(params, 1) != llGetScriptName())
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
|
||||||
RenderString(llList2Integer(params, 0),data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel == SLAVE_DISPLAY_EXTENDED)
|
|
||||||
{
|
|
||||||
if (!gActive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list params=llCSV2List((string)id);
|
|
||||||
if (llList2String(params, 1) != llGetScriptName())
|
|
||||||
return;
|
|
||||||
|
|
||||||
RenderExtended(llList2Integer(params, 0),data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel == SET_FONT_TEXTURE)
|
|
||||||
{
|
|
||||||
gFontTexture = id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel == REMAP_INDICES) {
|
|
||||||
// Parse the message, splitting it up into index values.
|
|
||||||
list Parsed = llCSV2List(data);
|
|
||||||
integer i;
|
|
||||||
// Go through the list and swap each pair of indices.
|
|
||||||
for (i = 0; i < llGetListLength(Parsed); i += 2) {
|
|
||||||
integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
|
|
||||||
integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
|
|
||||||
|
|
||||||
// Swap these index values.
|
|
||||||
string Value1 = llGetSubString(gCharIndex, Index1, Index1);
|
|
||||||
string Value2 = llGetSubString(gCharIndex, Index2, Index2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index1, Value2);
|
|
||||||
|
|
||||||
gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
|
|
||||||
gCharIndex = llInsertString(gCharIndex, Index2, Value1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (channel == RESET_INDICES) {
|
|
||||||
// Restore the character index back to default settings.
|
|
||||||
ResetCharIndex();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (channel == SLAVE_RESET)
|
|
||||||
{
|
|
||||||
ResetCharIndex();
|
|
||||||
gActive=FALSE;
|
|
||||||
llMessageLinked(LINK_THIS, REGISTER_SLAVE, llGetScriptName() , NULL_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
443
xyzzy_Master_script.lsl
Normal file
|
|
@ -0,0 +1,443 @@
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// XyText v1.0.3 Script (5 Face, Multi Texture)
|
||||||
|
//
|
||||||
|
// Written by Xylor Baysklef
|
||||||
|
//
|
||||||
|
// Modified by Thraxis Epsilon January 20, 2006
|
||||||
|
// Added Support for 5 Face Prim, based on modification
|
||||||
|
// of [[XyText]] v1.1.1 by Kermitt Quick for Single Texture.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////
|
||||||
|
|
||||||
|
/////////////// CONSTANTS ///////////////////
|
||||||
|
// XyText Message Map.
|
||||||
|
integer DISPLAY_STRING = 204000;
|
||||||
|
integer DISPLAY_EXTENDED = 204001;
|
||||||
|
integer REMAP_INDICES = 204002;
|
||||||
|
integer RESET_INDICES = 204003;
|
||||||
|
integer SET_CELL_INFO = 204004;
|
||||||
|
integer SET_THICKNESS = 204006;
|
||||||
|
integer SET_COLOR = 204007;
|
||||||
|
|
||||||
|
// This is an extended character escape sequence.
|
||||||
|
string ESCAPE_SEQUENCE = "\\e";
|
||||||
|
|
||||||
|
// This is used to get an index for the extended character.
|
||||||
|
string EXTENDED_INDEX = "123456789abcdef";
|
||||||
|
|
||||||
|
// Face numbers.
|
||||||
|
integer FACE_1 = 3;
|
||||||
|
integer FACE_2 = 7;
|
||||||
|
integer FACE_3 = 4;
|
||||||
|
integer FACE_4 = 6;
|
||||||
|
integer FACE_5 = 1;
|
||||||
|
|
||||||
|
// Used to hide the text after a fade-out.
|
||||||
|
key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
|
||||||
|
// This is a list of textures for all 2-character combinations.
|
||||||
|
list CHARACTER_GRID = [
|
||||||
|
"00e9f9f7-0669-181c-c192-7f8e67678c8d",
|
||||||
|
"347a5cb6-0031-7ec0-2fcf-f298eebf3c0e",
|
||||||
|
"4e7e689e-37f1-9eca-8596-a958bbd23963",
|
||||||
|
"19ea9c21-67ba-8f6f-99db-573b1b877eb1",
|
||||||
|
"dde7b412-cda1-652f-6fc2-73f4641f96e1",
|
||||||
|
"af6fa3bb-3a6c-9c4f-4bf5-d1c126c830da",
|
||||||
|
"a201d3a2-364b-43b6-8686-5881c0f82a94",
|
||||||
|
"b674dec8-fead-99e5-c28d-2db8e4c51540",
|
||||||
|
"366e05f3-be6b-e5cf-c33b-731dff649caa",
|
||||||
|
"75c4925c-0427-dc0c-c71c-e28674ff4d27",
|
||||||
|
"dcbe166b-6a97-efb2-fc8e-e5bc6a8b1be6",
|
||||||
|
"0dca2feb-fc66-a762-db85-89026a4ecd68",
|
||||||
|
"a0fca76f-503a-946b-9336-0a918e886f7a",
|
||||||
|
"67fb375d-89a1-5a4f-8c7a-0cd1c066ffc4",
|
||||||
|
"300470b2-da34-5470-074c-1b8464ca050c",
|
||||||
|
"d1f8e91c-ce2b-d85e-2120-930d3b630946",
|
||||||
|
"2a190e44-7b29-dadb-0bff-c31adaf5a170",
|
||||||
|
"75d55e71-f6f8-9835-e746-a45f189f30a1",
|
||||||
|
"300fac33-2b30-3da3-26bc-e2d70428ec19",
|
||||||
|
"0747c776-011a-53ce-13ee-8b5bb9e87c1e",
|
||||||
|
"85a855c3-a94f-01ca-33e0-7dde92e727e2",
|
||||||
|
"cbc1dab2-2d61-2986-1949-7a5235c954e1",
|
||||||
|
"f7aef047-f266-9596-16df-641010edd8e1",
|
||||||
|
"4c34ebf7-e5e1-2e1a-579f-e224d9d5e71b",
|
||||||
|
"4a69e98c-26a5-ad05-e92e-b5b906ad9ef9",
|
||||||
|
"462a9226-2a97-91ac-2d89-57ab33334b78",
|
||||||
|
"20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
|
||||||
|
"9b481daa-9ea8-a9fa-1ee4-ab9a0d38e217",
|
||||||
|
"c231dbdc-c842-15b0-7aa6-6da14745cfdc",
|
||||||
|
"c97e3cbb-c9a3-45df-a0ae-955c1f4bf9cf",
|
||||||
|
"f1e7d030-ff80-a242-cb69-f6951d4eae3b",
|
||||||
|
"ed32d6c4-d733-c0f1-f242-6df1d222220d",
|
||||||
|
"88f96a30-dccf-9b20-31ef-da0dfeb23c72",
|
||||||
|
"252f2595-58b8-4bcc-6515-fa274d0cfb65",
|
||||||
|
"f2838c4f-de80-cced-dff8-195dfdf36b2c",
|
||||||
|
"cc2594fe-add2-a3df-cdb3-a61711badf53",
|
||||||
|
"e0ce2972-da00-955c-129e-3289b3676776",
|
||||||
|
"3e0d336d-321f-ddfa-5c1b-e26131766f6a",
|
||||||
|
"d43b1dc4-6b51-76a7-8b90-38865b82bf06",
|
||||||
|
"06d16cbb-1868-fd1d-5c93-eae42164a37d",
|
||||||
|
"dd5d98cf-273e-3fd0-f030-48be58ee3a0b",
|
||||||
|
"0e47c89e-de4a-6233-a2da-cb852aad1b00",
|
||||||
|
"fb9c4a55-0e13-495b-25c4-f0b459dc06de",
|
||||||
|
"e3ce8def-312c-735b-0e48-018b6799c883",
|
||||||
|
"2f713216-4e71-d123-03ed-9c8554710c6b",
|
||||||
|
"4a417d8a-1f4f-404b-9783-6672f8527911",
|
||||||
|
"ca5e21ec-5b20-5909-4c31-3f90d7316b33",
|
||||||
|
"06a4fcc3-e1c4-296d-8817-01f88fbd7367",
|
||||||
|
"130ac084-6f3c-95de-b5b6-d25c80703474",
|
||||||
|
"59d540a0-ae9d-3606-5ae0-4f2842b64cfa",
|
||||||
|
"8612ae9a-f53c-5bf4-2899-8174d7abc4fd",
|
||||||
|
"12467401-e979-2c49-34e0-6ac761542797",
|
||||||
|
"d53c3eaa-0404-3860-0675-3e375596c3e3",
|
||||||
|
"9f5b26bd-81d3-b25e-62fe-5b671d1e3e79",
|
||||||
|
"f57f0b64-a050-d617-ee00-c8e9e3adc9cb",
|
||||||
|
"beff166a-f5f3-f05e-e020-98f2b00e27ed",
|
||||||
|
"02278a65-94ba-6d5e-0d2b-93f2e4f4bf70",
|
||||||
|
"a707197d-449e-5b58-846c-0c850c61f9d6",
|
||||||
|
"021d4b1a-9503-a44f-ee2b-976eb5d80e68",
|
||||||
|
"0ae2ffae-7265-524d-cb76-c2b691992706",
|
||||||
|
"f6e41cf2-1104-bd0b-0190-dffad1bac813",
|
||||||
|
"2b4bb15e-956d-56ae-69f5-d26a20de0ce7",
|
||||||
|
"f816da2c-51f1-612a-2029-a542db7db882",
|
||||||
|
"345fea05-c7be-465c-409f-9dcb3bd2aa07",
|
||||||
|
"b3017e02-c063-5185-acd5-1ef5f9d79b89",
|
||||||
|
"4dcff365-1971-3c2b-d73c-77e1dc54242a"
|
||||||
|
];
|
||||||
|
|
||||||
|
///////////// END CONSTANTS ////////////////
|
||||||
|
|
||||||
|
///////////// GLOBAL VARIABLES ///////////////
|
||||||
|
// All displayable characters. Default to ASCII order.
|
||||||
|
string gCharIndex;
|
||||||
|
// This is the channel to listen on while acting
|
||||||
|
// as a cell in a larger display.
|
||||||
|
integer gCellChannel = -1;
|
||||||
|
// This is the starting character position in the cell channel message
|
||||||
|
// to render.
|
||||||
|
integer gCellCharPosition = 0;
|
||||||
|
// This is whether or not to use the fade in/out special effect.
|
||||||
|
integer gCellUseFading = FALSE;
|
||||||
|
// This is how long to display the text before fading out (if using
|
||||||
|
// fading special effect).
|
||||||
|
// Note: < 0 means don't fade out.
|
||||||
|
float gCellHoldDelay = 1.0;
|
||||||
|
/////////// END GLOBAL VARIABLES ////////////
|
||||||
|
|
||||||
|
ResetCharIndex()
|
||||||
|
{
|
||||||
|
gCharIndex = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
|
||||||
|
// \" <-- Fixes LSL syntax highlighting bug.
|
||||||
|
gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
|
||||||
|
gCharIndex += "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
vector GetGridPos(integer index1, integer index2)
|
||||||
|
{
|
||||||
|
// There are two ways to use the lookup table...
|
||||||
|
integer Col;
|
||||||
|
integer Row;
|
||||||
|
if (index1 >= index2)
|
||||||
|
{
|
||||||
|
// In this case, the row is the index of the first character:
|
||||||
|
Row = index1;
|
||||||
|
// And the col is the index of the second character (x2)
|
||||||
|
Col = index2 * 2;
|
||||||
|
}
|
||||||
|
// Index1 < Index2
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In this case, the row is the index of the second character:
|
||||||
|
Row = index2;
|
||||||
|
// And the col is the index of the first character, x2, offset by 1.
|
||||||
|
Col = index1 * 2 + 1;
|
||||||
|
}
|
||||||
|
return <Col, Row, 0>;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetGridTexture(vector grid_pos)
|
||||||
|
{
|
||||||
|
// Calculate the texture in the grid to use.
|
||||||
|
integer GridCol = llRound(grid_pos.x) / 20;
|
||||||
|
integer GridRow = llRound(grid_pos.y) / 10;
|
||||||
|
|
||||||
|
// Lookup the texture.
|
||||||
|
key Texture = llList2Key(CHARACTER_GRID, GridRow * (GridRow + 1) / 2 + GridCol);
|
||||||
|
return Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector GetGridOffset(vector grid_pos)
|
||||||
|
{
|
||||||
|
// Zoom in on the texture showing our character pair.
|
||||||
|
integer Col = llRound(grid_pos.x) % 20;
|
||||||
|
integer Row = llRound(grid_pos.y) % 10;
|
||||||
|
|
||||||
|
// Return the offset in the texture.
|
||||||
|
return <-0.45 + 0.05 * Col, 0.45 - 0.1 * Row, 0.0>;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5)
|
||||||
|
{
|
||||||
|
// Set the primitive textures directly.
|
||||||
|
|
||||||
|
|
||||||
|
llSetLinkPrimitiveParamsFast(LINK_THIS, [
|
||||||
|
PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
|
||||||
|
PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
|
||||||
|
PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
|
||||||
|
PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
|
||||||
|
PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderString(string str)
|
||||||
|
{
|
||||||
|
// Get the grid positions for each pair of characters.
|
||||||
|
vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
||||||
|
vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
||||||
|
vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );
|
||||||
|
vector GridPos4 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 6, 6)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 7, 7)) );
|
||||||
|
vector GridPos5 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 8, 8)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 9, 9)) );
|
||||||
|
|
||||||
|
// Use these grid positions to display the correct textures/offsets.
|
||||||
|
ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderWithEffects(string str)
|
||||||
|
{
|
||||||
|
// Get the grid positions for each pair of characters.
|
||||||
|
vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
|
||||||
|
vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
|
||||||
|
vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );
|
||||||
|
vector GridPos4 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 6, 6)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 7, 7)) );
|
||||||
|
vector GridPos5 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 8, 8)),
|
||||||
|
llSubStringIndex(gCharIndex, llGetSubString(str, 9, 9)) );
|
||||||
|
|
||||||
|
// First set the alpha to the lowest possible.
|
||||||
|
llSetAlpha(0.05, ALL_SIDES);
|
||||||
|
|
||||||
|
// Use these grid positions to display the correct textures/offsets.
|
||||||
|
ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
|
||||||
|
|
||||||
|
float Alpha;
|
||||||
|
for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
|
||||||
|
llSetAlpha(Alpha, ALL_SIDES);
|
||||||
|
// See if we want to fade out as well.
|
||||||
|
if (gCellHoldDelay < 0.0)
|
||||||
|
// No, bail out. (Just keep showing the string at full strength).
|
||||||
|
return;
|
||||||
|
// Hold the text for a while.
|
||||||
|
llSleep(gCellHoldDelay);
|
||||||
|
// Now fade out.
|
||||||
|
for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
|
||||||
|
llSetAlpha(Alpha, ALL_SIDES);
|
||||||
|
// Make the text transparent to fully hide it.
|
||||||
|
llSetTexture(TRANSPARENT, ALL_SIDES);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderExtended(string str)
|
||||||
|
{
|
||||||
|
// Look for escape sequences.
|
||||||
|
list Parsed = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
|
||||||
|
integer ParsedLen = llGetListLength(Parsed);
|
||||||
|
|
||||||
|
// Create a list of index values to work with.
|
||||||
|
list Indices;
|
||||||
|
// We start with room for 6 indices.
|
||||||
|
integer IndicesLeft = 10;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
string Token;
|
||||||
|
integer Clipped;
|
||||||
|
integer LastWasEscapeSequence = FALSE;
|
||||||
|
// Work from left to right.
|
||||||
|
for (i = 0; i < ParsedLen && IndicesLeft > 0; i++)
|
||||||
|
{
|
||||||
|
Token = llList2String(Parsed, i);
|
||||||
|
|
||||||
|
// If this is an escape sequence, just set the flag and move on.
|
||||||
|
if (Token == ESCAPE_SEQUENCE)
|
||||||
|
{
|
||||||
|
LastWasEscapeSequence = TRUE;
|
||||||
|
}
|
||||||
|
// Token != ESCAPE_SEQUENCE
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise this is a normal token. Check its length.
|
||||||
|
Clipped = FALSE;
|
||||||
|
integer TokenLength = llStringLength(Token);
|
||||||
|
// Clip if necessary.
|
||||||
|
if (TokenLength > IndicesLeft)
|
||||||
|
{
|
||||||
|
Token = llGetSubString(Token, 0, IndicesLeft - 1);
|
||||||
|
TokenLength = llStringLength(Token);
|
||||||
|
IndicesLeft = 0;
|
||||||
|
Clipped = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
IndicesLeft -= TokenLength;
|
||||||
|
|
||||||
|
// Was the previous token an escape sequence?
|
||||||
|
if (LastWasEscapeSequence)
|
||||||
|
{
|
||||||
|
// Yes, the first character is an escape character, the rest are normal.
|
||||||
|
|
||||||
|
// This is the extended character.
|
||||||
|
Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];
|
||||||
|
|
||||||
|
// These are the normal characters.
|
||||||
|
integer j;
|
||||||
|
for (j = 1; j < TokenLength; j++)
|
||||||
|
{
|
||||||
|
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Normal string.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Just add the characters normally.
|
||||||
|
integer j;
|
||||||
|
for (j = 0; j < TokenLength; j++)
|
||||||
|
Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset this flag, since this was not an escape sequence.
|
||||||
|
LastWasEscapeSequence = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the indices to create grid positions.
|
||||||
|
vector GridPos1 = GetGridPos( llList2Integer(Indices, 0), llList2Integer(Indices, 1) );
|
||||||
|
vector GridPos2 = GetGridPos( llList2Integer(Indices, 2), llList2Integer(Indices, 3) );
|
||||||
|
vector GridPos3 = GetGridPos( llList2Integer(Indices, 4), llList2Integer(Indices, 5) );
|
||||||
|
vector GridPos4 = GetGridPos( llList2Integer(Indices, 6), llList2Integer(Indices, 7) );
|
||||||
|
vector GridPos5 = GetGridPos( llList2Integer(Indices, 8), llList2Integer(Indices, 9) );
|
||||||
|
|
||||||
|
// Use these grid positions to display the correct textures/offsets.
|
||||||
|
ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
|
||||||
|
}
|
||||||
|
|
||||||
|
integer ConvertIndex(integer index)
|
||||||
|
{
|
||||||
|
// This converts from an ASCII based index to our indexing scheme.
|
||||||
|
if (index >= 32) // ' ' or higher
|
||||||
|
index -= 32;
|
||||||
|
// index < 32
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Quick bounds check.
|
||||||
|
if (index > 15)
|
||||||
|
index = 15;
|
||||||
|
|
||||||
|
index += 94; // extended characters
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
default
|
||||||
|
{
|
||||||
|
state_entry()
|
||||||
|
{
|
||||||
|
// Initialize the character index.
|
||||||
|
ResetCharIndex();
|
||||||
|
|
||||||
|
//llSay(0, "Free Memory: " + (string) llGetFreeMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
link_message(integer sender, integer channel, string data, key id)
|
||||||
|
{
|
||||||
|
if (channel == DISPLAY_STRING)
|
||||||
|
{
|
||||||
|
RenderString(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == DISPLAY_EXTENDED)
|
||||||
|
{
|
||||||
|
RenderExtended(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == gCellChannel)
|
||||||
|
{
|
||||||
|
// Extract the characters we are interested in, and use those to render.
|
||||||
|
string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 9);
|
||||||
|
if (gCellUseFading)
|
||||||
|
RenderWithEffects( TextToRender );
|
||||||
|
else // !gCellUseFading
|
||||||
|
RenderString( TextToRender );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == REMAP_INDICES)
|
||||||
|
{
|
||||||
|
// Parse the message, splitting it up into index values.
|
||||||
|
list Parsed = llCSV2List(data);
|
||||||
|
integer i;
|
||||||
|
// Go through the list and swap each pair of indices.
|
||||||
|
for (i = 0; i < llGetListLength(Parsed); i += 2)
|
||||||
|
{
|
||||||
|
integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
|
||||||
|
integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
|
||||||
|
|
||||||
|
// Swap these index values.
|
||||||
|
string Value1 = llGetSubString(gCharIndex, Index1, Index1);
|
||||||
|
string Value2 = llGetSubString(gCharIndex, Index2, Index2);
|
||||||
|
|
||||||
|
gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
|
||||||
|
gCharIndex = llInsertString(gCharIndex, Index1, Value2);
|
||||||
|
|
||||||
|
gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
|
||||||
|
gCharIndex = llInsertString(gCharIndex, Index2, Value1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == RESET_INDICES)
|
||||||
|
{
|
||||||
|
// Restore the character index back to default settings.
|
||||||
|
ResetCharIndex();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == SET_CELL_INFO)
|
||||||
|
{
|
||||||
|
// Change the channel we listen to for cell commands, and the
|
||||||
|
// starting character position to extract from.
|
||||||
|
list Parsed = llCSV2List(data);
|
||||||
|
gCellChannel = (integer) llList2String(Parsed, 0);
|
||||||
|
gCellCharPosition = (integer) llList2String(Parsed, 1);
|
||||||
|
gCellUseFading = (integer) llList2String(Parsed, 2);
|
||||||
|
gCellHoldDelay = (float) llList2String(Parsed, 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == SET_THICKNESS)
|
||||||
|
{
|
||||||
|
// Set our z scale to thickness, while staying fixed
|
||||||
|
// in position relative the prim below us.
|
||||||
|
vector Scale = llGetScale();
|
||||||
|
float Thickness = (float) data;
|
||||||
|
// Reposition only if this isn't the root prim.
|
||||||
|
integer ThisLink = llGetLinkNumber();
|
||||||
|
if (ThisLink != 0 || ThisLink != 1)
|
||||||
|
{
|
||||||
|
// This is not the root prim.
|
||||||
|
vector Up = llRot2Up(llGetLocalRot());
|
||||||
|
float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
|
||||||
|
vector Pos = llGetLocalPos();
|
||||||
|
llSetPos(Pos + DistanceToMove * Up);
|
||||||
|
}
|
||||||
|
// Apply the new thickness.
|
||||||
|
Scale.z = Thickness;
|
||||||
|
llSetScale(Scale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channel == SET_COLOR) {
|
||||||
|
vector newColor = (vector)data;
|
||||||
|
llSetColor(newColor, ALL_SIDES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||