## [2.8.6] - 2025-09-16

This commit is contained in:
RebeccaNod1 2025-09-18 12:08:55 -05:00
parent fdd8c761da
commit 2e8494afe8
5 changed files with 111 additions and 49 deletions

View file

@ -6,6 +6,20 @@ All notable changes to Peril Dice are documented in this file.
[2.8.6] - 2025-09-16
====================
🏆 LEADERBOARD SORTING ENHANCEMENT
- Enhanced Tie-Breaking: Fixed leaderboard sorting to properly handle players with identical win counts
* Before: Players with same wins (e.g., 5 wins each) appeared in random/inconsistent order
* After: Players with same wins are sorted by losses ascending (fewer losses = better rank)
* Example: Player A (5W/2L) now ranks higher than Player B (5W/3L) with same wins but more losses
* Fair Ranking: Ensures consistent, logical player rankings that reflect overall performance
- Multi-Level Sorting: Implemented compound sorting key with wins primary, losses secondary
* Primary Sort: Wins descending (higher wins = better rank)
* Tiebreaker Sort: Losses ascending (lower losses = better rank among tied players)
* Technical: Uses padded numeric strings for reliable multi-level sorting in LSL
- LSL Compliance: Fixed variable declaration issues to ensure proper script compilation
* Resolved: Multiple variable redeclaration errors in sorting functions
* Enhanced: Proper LSL variable scoping throughout leaderboard management code
🏆 WINNER GLOW CELEBRATION SYSTEM
- Victory Visual Enhancement: Added comprehensive winner celebration with green glow effects
* Scoreboard Glow: Winner's profile picture and hearts display bright green glow with green tint

View file

@ -360,10 +360,13 @@ default {
// Real avatars: rez next to the avatar with the standard offset
pos = basePos + <1, 0, 1>;
} else {
// Test players: space floats apart and away from the scoreboard/leaderboard area.
// Move them further away (3-5 meters) and spread them out more.
// This prevents them from spawning directly on the leaderboard.
pos = basePos + <-4.0 - (float)idx * 1.0, 2.0 + (float)idx * 0.8, 1>;
// Test players: arrange in a compact 2x5 grid to stay within parcel boundaries
// Grid layout: 2 rows of 5 floaters each, spaced 1.5m apart
integer row = idx / 5; // Row 0 or 1
integer col = idx % 5; // Column 0-4
float xOffset = -3.0 - (float)col * 1.5; // -3.0 to -9.0
float yOffset = 2.0 + (float)row * 2.0; // 2.0 or 4.0
pos = basePos + <xOffset, yOffset, 1>;
}
// Use permanent channel mapping instead of alive list index
integer channelIdx = llListFindList(allPlayerNames, [name]);
@ -454,7 +457,7 @@ default {
string picksDisplay = llList2CSV(picks);
// Check if this player is the winner (only one player with lives > 0)
// Check if this player is the winner (only one player with lives > 0 AND game is active)
integer livingPlayers = 0;
integer isWinner = FALSE;
for (i = 0; i < llGetListLength(lives); i++) {
@ -463,8 +466,9 @@ default {
}
}
// If only one player has lives > 0 and this player has lives > 0, they're the winner
if (livingPlayers == 1 && lifeCount > 0) {
// Winner detection: only trigger if game is active, has multiple total players, and only 1 survivor
// This prevents false winner detection during initial player registration
if (gameActive && livingPlayers == 1 && lifeCount > 0 && llGetListLength(names) >= 2) {
isWinner = TRUE;
}

View file

@ -510,8 +510,10 @@ default {
names = newNames;
// Send lightweight update to Main Controller for floating text (only if substantial change)
// Use the NEW names list count (after elimination has been processed)
if (perilChanged && perilPlayer != "" && perilPlayer != "NONE") {
string lightUpdate = "PERIL_UPDATE|" + perilPlayer + "|" + (string)llGetListLength(names);
integer currentPlayerCount = llGetListLength(newNames); // Use the updated count
string lightUpdate = "PERIL_UPDATE|" + perilPlayer + "|" + (string)currentPlayerCount;
llMessageLinked(LINK_SET, 9070, lightUpdate, NULL_KEY);
}

View file

@ -39,7 +39,7 @@ integer LAST_OVERLAY_PRIM = 11; // Overlay prims end at 11
// Heart texture UUIDs - REPLACE WITH YOUR ACTUAL TEXTURE UUIDs
string TEXTURE_0_HEARTS = "7d8ae121-e171-12ae-f5b6-7cc3c0395c7b"; // 0 hearts (dead)
string TEXTURE_1_HEARTS = "6605d25f-8e2d-2870-eb87-77c58cd47fa9"; // 1 heart
string TEXTURE_2_HEARTS = "7ba6cb1e-f384-25a5-8e88-a90bbd7cc041"; // 2 hearts
string TEXTURE_2_HEARTS = "7ba6cb1e-f384-25a5-8e88-a90bbd7cc041"; // 2 hearts
string TEXTURE_3_HEARTS = "a5d16715-4648-6526-5582-e8068293f792"; // 3 hearts
// Default textures - REPLACE WITH YOUR ACTUAL TEXTURE UUIDs
@ -240,36 +240,56 @@ handleGameLost(string loserName) {
list getSortedLeaderboard() {
list sortedData = [];
integer i;
string name;
integer wins;
integer losses;
string paddedWins;
string paddedLosses;
integer invertedLosses;
string sortKey;
string entry;
list parts;
list cleanedData = [];
string winsStr;
string lossesStr;
// Create combined data for sorting with padded wins for proper numerical sorting
// Create combined data for sorting with padded wins and losses for proper multi-level sorting
for (i = 0; i < llGetListLength(leaderboardNames); i++) {
string name = llList2String(leaderboardNames, i);
integer wins = llList2Integer(leaderboardWins, i);
integer losses = llList2Integer(leaderboardLosses, i);
name = llList2String(leaderboardNames, i);
wins = llList2Integer(leaderboardWins, i);
losses = llList2Integer(leaderboardLosses, i);
// Pad wins to 4 digits for proper string sorting (0000-9999)
string paddedWins = (string)(10000 + wins); // Add 10000 then remove first digit
// Pad wins to 4 digits (higher wins = better rank)
paddedWins = (string)(10000 + wins);
paddedWins = llGetSubString(paddedWins, 1, -1); // Remove the "1" prefix
// Format: "PaddedWins:Name:ActualWins:Losses" for sorting
string entry = paddedWins + ":" + name + ":" + (string)wins + ":" + (string)losses;
// Pad losses to 4 digits, but INVERTED for tiebreaker (lower losses = better rank)
// Use 9999 - losses so lower losses get higher sort value
invertedLosses = 9999 - losses;
paddedLosses = (string)(10000 + invertedLosses);
paddedLosses = llGetSubString(paddedLosses, 1, -1); // Remove the "1" prefix
// Format: "PaddedWins-PaddedInvertedLosses:Name:ActualWins:ActualLosses"
// This sorts first by wins (descending), then by losses (ascending) for tiebreaker
sortKey = paddedWins + "-" + paddedLosses;
entry = sortKey + ":" + name + ":" + (string)wins + ":" + (string)losses;
sortedData += [entry];
}
// Sort by padded wins (descending) - numerical order preserved in string sort
sortedData = llListSort(sortedData, 1, FALSE); // FALSE = descending order
// Sort by combined key (wins descending, losses ascending) - FALSE = descending order
sortedData = llListSort(sortedData, 1, FALSE);
// Clean up the format back to "Name:Wins:Losses" (skip padded wins in position 0)
list cleanedData = [];
cleanedData = [];
for (i = 0; i < llGetListLength(sortedData); i++) {
string entry = llList2String(sortedData, i);
list parts = llParseString2List(entry, [":"], []);
entry = llList2String(sortedData, i);
parts = llParseString2List(entry, [":"], []);
if (llGetListLength(parts) >= 4) {
// parts[0] = paddedWins (skip), parts[1] = name, parts[2] = actualWins, parts[3] = losses
string name = llList2String(parts, 1);
string wins = llList2String(parts, 2);
string losses = llList2String(parts, 3);
cleanedData += [name + ":" + wins + ":" + losses];
name = llList2String(parts, 1);
winsStr = llList2String(parts, 2);
lossesStr = llList2String(parts, 3);
cleanedData += [name + ":" + winsStr + ":" + lossesStr];
}
}
@ -284,15 +304,30 @@ generateLeaderboardText() {
string leaderboardText = " TOP BATTLE RECORDS \n";
integer i;
string playerData;
list playerParts;
string playerName;
string wins;
string losses;
integer rankNumber;
string rank;
string leftPart;
string rightPart;
integer spaceNeeded;
string spacer;
integer s;
string line;
integer actualPlayers;
// Add actual player data (up to 11 players)
for (i = 0; i < llGetListLength(sortedData) && i < 11; i++) {
string playerData = llList2String(sortedData, i);
list playerParts = llParseString2List(playerData, [":"], []);
playerData = llList2String(sortedData, i);
playerParts = llParseString2List(playerData, [":"], []);
if (llGetListLength(playerParts) >= 3) {
string playerName = llList2String(playerParts, 0);
string wins = llList2String(playerParts, 1);
string losses = llList2String(playerParts, 2);
playerName = llList2String(playerParts, 0);
wins = llList2String(playerParts, 1);
losses = llList2String(playerParts, 2);
// Truncate long names to fit better spacing
if (llStringLength(playerName) > 12) {
@ -300,51 +335,49 @@ generateLeaderboardText() {
}
// Format rank
integer rankNumber = i + 1;
string rank = (string)rankNumber;
rankNumber = i + 1;
rank = (string)rankNumber;
if (rankNumber < 10) rank = " " + rank; // Pad single digits
// Create well-spaced line to fill full 40-character width
string leftPart = rank + ". " + playerName;
string rightPart = "W:" + wins + "/L:" + losses;
leftPart = rank + ". " + playerName;
rightPart = "W:" + wins + "/L:" + losses;
// Calculate spaces needed to fill exactly 40 characters
integer spaceNeeded = 40 - llStringLength(leftPart) - llStringLength(rightPart);
spaceNeeded = 40 - llStringLength(leftPart) - llStringLength(rightPart);
if (spaceNeeded < 1) spaceNeeded = 1; // At least 1 space
string spacer = "";
integer s;
spacer = "";
for (s = 0; s < spaceNeeded; s++) {
spacer += " ";
}
string line = leftPart + spacer + rightPart;
line = leftPart + spacer + rightPart;
leaderboardText += line + "\n";
}
}
// Fill remaining positions with placeholders to always show 11 positions
integer actualPlayers = llGetListLength(sortedData);
actualPlayers = llGetListLength(sortedData);
for (i = actualPlayers; i < 11; i++) {
integer rankNumber = i + 1;
string rank = (string)rankNumber;
rankNumber = i + 1;
rank = (string)rankNumber;
if (rankNumber < 10) rank = " " + rank; // Pad single digits
// Create placeholder line to fill full 40-character width
string leftPart = rank + ". --------";
string rightPart = "W:0/L:0";
leftPart = rank + ". --------";
rightPart = "W:0/L:0";
// Calculate spaces needed to fill exactly 40 characters
integer spaceNeeded = 40 - llStringLength(leftPart) - llStringLength(rightPart);
spaceNeeded = 40 - llStringLength(leftPart) - llStringLength(rightPart);
if (spaceNeeded < 1) spaceNeeded = 1; // At least 1 space
string spacer = "";
integer s;
spacer = "";
for (s = 0; s < spaceNeeded; s++) {
spacer += " ";
}
string line = leftPart + spacer + rightPart;
line = leftPart + spacer + rightPart;
leaderboardText += line + "\n";
}

View file

@ -33,7 +33,8 @@ KEY FEATURES
🎯 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
- Smart Multi-Level Sorting: Primary by wins (descending), secondary by losses (ascending)
- Fair Tie-Breaking: Players with same wins ranked by fewer losses for accurate competitive standings
🎮 CORE GAME FEATURES
- 🎯 Dynamic Player Management: Players can join at runtime (owner and other avatars)
@ -101,6 +102,14 @@ Player Count | Dice Type
RECENT IMPROVEMENTS (V2.8.6)
============================
🏆 LEADERBOARD SORTING ENHANCEMENT
- Enhanced Tie-Breaking System: Fixed leaderboard rankings to properly handle players with identical win counts
* Smart Multi-Level Sorting: Primary by wins (descending), then by losses (ascending) for fair tiebreaker
* Example: Player A (5W/2L) now correctly ranks higher than Player B (5W/3L)
* Consistent Rankings: No more random ordering of tied players - losses now serve as logical tiebreaker
- Technical Implementation: Compound sorting key ensures reliable multi-level sorting in LSL environment
- LSL Compliance: Fixed variable declaration issues for proper script compilation
🏆 WINNER GLOW CELEBRATION SYSTEM
- Revolutionary Victory Recognition: Comprehensive winner celebration with bright green glow effects
* Scoreboard Glory: Winner's profile picture and hearts glow bright green with green tint