## [2.8.6] - 2025-09-16
This commit is contained in:
parent
fdd8c761da
commit
2e8494afe8
5 changed files with 111 additions and 49 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue