Add Peril Dice Controller and StatFloat functionality with player management and UI updates
This commit is contained in:
commit
2083469a13
2 changed files with 289 additions and 0 deletions
250
PerilController.lsl
Normal file
250
PerilController.lsl
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// === Fully Unified Peril Dice Controller (Testing Enabled + Click-to-Join) ===
|
||||
|
||||
// Communication channels
|
||||
integer syncChannel = -77777;
|
||||
integer numberPickChannel = -77888;
|
||||
|
||||
// Game state
|
||||
list players = [];
|
||||
list names = [];
|
||||
list lives = [];
|
||||
list picksData = [];
|
||||
string perilPlayer = "";
|
||||
list globalPickedNumbers = [];
|
||||
list floatSpawnMap = []; // stores avatar key and rezzed object key as pairs
|
||||
|
||||
// Dice and UI settings
|
||||
integer diceType = 6;
|
||||
integer page = 0;
|
||||
integer maxPerPage = 12;
|
||||
|
||||
integer getPicksRequired(integer lifeCount) {
|
||||
if (lifeCount == 3) return 1;
|
||||
if (lifeCount == 2) return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
integer getPicksIndex(string nameInput) {
|
||||
integer i;
|
||||
for (i = 0; i < llGetListLength(picksData); i++) {
|
||||
string entry = llList2String(picksData, i);
|
||||
if (llSubStringIndex(entry, nameInput + "|") == 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
list getPicksFor(string nameInput) {
|
||||
integer idx = getPicksIndex(nameInput);
|
||||
if (idx == -1) return [];
|
||||
string data = llList2String(picksData, idx);
|
||||
return llParseString2List(llList2String(llParseString2List(data, ["|"], []), 1), [","], []);
|
||||
}
|
||||
|
||||
addPick(string nameInput, string number) {
|
||||
integer idx = getPicksIndex(nameInput);
|
||||
integer playerIdx = llListFindList(names, [nameInput]);
|
||||
integer lifeCount = llList2Integer(lives, playerIdx);
|
||||
integer limit = getPicksRequired(lifeCount);
|
||||
list picks;
|
||||
|
||||
if (idx == -1) {
|
||||
picks = [number];
|
||||
picksData += [nameInput + "|" + number];
|
||||
} else {
|
||||
picks = getPicksFor(nameInput);
|
||||
if (llListFindList(picks, [number]) != -1 || llGetListLength(picks) >= limit) return;
|
||||
picks += [number];
|
||||
picksData = llListReplaceList(picksData, [nameInput + "|" + llDumpList2String(picks, ",")], idx, idx);
|
||||
}
|
||||
globalPickedNumbers += [number];
|
||||
}
|
||||
|
||||
showStatus(string name) {
|
||||
integer idx = llListFindList(names, [name]);
|
||||
if (idx == -1) return;
|
||||
integer lifeCount = llList2Integer(lives, idx);
|
||||
list picks = getPicksFor(name);
|
||||
string txt = "🎲 Peril Dice
|
||||
👤 " + name +
|
||||
"
|
||||
❤️ Lives: " + (string)lifeCount +
|
||||
"
|
||||
🧍 Peril: " + perilPlayer +
|
||||
"
|
||||
🔢 Picks: " + llList2CSV(picks);
|
||||
|
||||
key avKey = llList2Key(players, idx);
|
||||
if (llKey2Name(avKey) != "") {
|
||||
vector pos = llList2Vector(llGetObjectDetails(avKey, [OBJECT_POS]), 0);
|
||||
vector rezPos = pos + <1,0,1>; // offset in front and above the player
|
||||
integer channel = -777000 + idx;
|
||||
|
||||
llOwnerSay("📦 Attempting to rez statFloat at: " + (string)rezPos);
|
||||
llRezObject("StatFloat", rezPos, ZERO_VECTOR, ZERO_ROTATION, channel);
|
||||
floatSpawnMap += [avKey, (string)channel];
|
||||
}
|
||||
}
|
||||
showPickDialog(key id, string name) {
|
||||
list picks = getPicksFor(name);
|
||||
integer picksRequired = getPicksRequired(llList2Integer(lives, llListFindList(names, [name])));
|
||||
|
||||
list options = [];
|
||||
integer start = page * maxPerPage + 1;
|
||||
integer endNum;
|
||||
if ((start + maxPerPage - 1) <= diceType) {
|
||||
endNum = start + maxPerPage - 1;
|
||||
} else {
|
||||
endNum = diceType;
|
||||
}
|
||||
|
||||
integer i;
|
||||
for (i = start; i <= endNum; i++) {
|
||||
string s = (string)i;
|
||||
if (llListFindList(globalPickedNumbers, [s]) == -1) options += [s];
|
||||
}
|
||||
|
||||
list buttons = [];
|
||||
if (page > 0) buttons += ["« Prev"];
|
||||
buttons += options;
|
||||
if ((page + 1) * maxPerPage < diceType) buttons += ["Next »"];
|
||||
|
||||
llDialog(id, "Pick " + (string)picksRequired + " unique number(s):", buttons, numberPickChannel);
|
||||
}
|
||||
|
||||
confetti() {
|
||||
llParticleSystem([
|
||||
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE,
|
||||
PSYS_PART_START_COLOR, <1,1,1>, PSYS_PART_END_COLOR, <1,1,1>,
|
||||
PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0,
|
||||
PSYS_PART_START_SCALE, <0.2,0.2,0>, PSYS_PART_END_SCALE, <0.5,0.5,0>,
|
||||
PSYS_PART_MAX_AGE, 2.0, PSYS_SRC_MAX_AGE, 2.0,
|
||||
PSYS_SRC_ACCEL, <0,0,-0.4>,
|
||||
PSYS_SRC_BURST_RATE, 0.01, PSYS_SRC_BURST_PART_COUNT, 50,
|
||||
PSYS_SRC_BURST_RADIUS, 0.2,
|
||||
PSYS_SRC_BURST_SPEED_MIN, 1.0, PSYS_SRC_BURST_SPEED_MAX, 2.0,
|
||||
PSYS_PART_FLAGS, PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK | PSYS_PART_EMISSIVE_MASK
|
||||
]);
|
||||
}
|
||||
|
||||
resetGame() {
|
||||
// Tell all active statFloat objects to clean up
|
||||
integer i;
|
||||
for (i = 0; i < llGetListLength(names); i++) {
|
||||
integer ch = -777000 + i;
|
||||
llRegionSay(ch, "CLEANUP");
|
||||
}
|
||||
players = names = lives = picksData = globalPickedNumbers = [];
|
||||
perilPlayer = "";
|
||||
page = 0;
|
||||
llSay(syncChannel, "RESET");
|
||||
llOwnerSay("🔄 Game reset!");
|
||||
}
|
||||
|
||||
checkForWinner() {
|
||||
integer alive = 0;
|
||||
string last;
|
||||
integer i;
|
||||
for (i = 0; i < llGetListLength(lives); i++) {
|
||||
if (llList2Integer(lives, i) > 0) {
|
||||
alive++;
|
||||
last = llList2String(names, i);
|
||||
}
|
||||
}
|
||||
if (alive == 1) {
|
||||
llSay(0, "🏆 Winner: " + last + "!");
|
||||
resetGame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default {
|
||||
touch_start(integer total_number) {
|
||||
key toucher = llDetectedKey(0);
|
||||
string name = llDetectedName(0);
|
||||
integer idx = llListFindList(players, [toucher]);
|
||||
if (~idx) {
|
||||
llDialog(toucher, "❓ You are already in the game. Leave the game?", ["Yes", "No"], -88888);
|
||||
return;
|
||||
}
|
||||
|
||||
players += [toucher];
|
||||
names += [name];
|
||||
lives += [3];
|
||||
llOwnerSay("✅ " + name + " has joined the game with 3 lives.");
|
||||
showStatus(name);
|
||||
}
|
||||
|
||||
state_entry() {
|
||||
llVolumeDetect(TRUE);
|
||||
llSetStatus(STATUS_PHYSICS, FALSE);
|
||||
llSetClickAction(CLICK_ACTION_TOUCH);
|
||||
llListen(-88888, "", NULL_KEY, "");
|
||||
llListen(0, "", NULL_KEY, "");
|
||||
llListen(syncChannel, "", NULL_KEY, "");
|
||||
llListen(numberPickChannel, "", NULL_KEY, "");
|
||||
llOwnerSay("🎲 Unified Peril Controller Ready");
|
||||
}
|
||||
|
||||
on_rez(integer p) {
|
||||
llResetScript();
|
||||
}
|
||||
listen(integer chan, string speakerName, key id, string msg) {
|
||||
if (chan == -88888 && msg == "Yes") {
|
||||
integer i = llListFindList(players, [id]);
|
||||
if (i != -1) {
|
||||
integer ch = -777000 + i;
|
||||
llRegionSay(ch, "CLEANUP");
|
||||
players = llDeleteSubList(players, i, i);
|
||||
names = llDeleteSubList(names, i, i);
|
||||
lives = llDeleteSubList(lives, i, i);
|
||||
llOwnerSay("👋 Player left the game: " + llKey2Name(id));
|
||||
}
|
||||
}
|
||||
else if (chan == 0 && llToLower(msg) == "/reset" && id == llGetOwner()) {
|
||||
resetGame();
|
||||
}
|
||||
else if (chan == 0 && llToLower(msg) == "/start" && id == llGetOwner()) {
|
||||
if (llGetListLength(names) == 0) {
|
||||
llOwnerSay("⚠️ No players to choose from.");
|
||||
return;
|
||||
}
|
||||
integer r = (integer)llFrand(llGetListLength(names));
|
||||
string chosen = llList2String(names, r);
|
||||
perilPlayer = chosen;
|
||||
picksData = globalPickedNumbers = [];
|
||||
page = 0;
|
||||
llSay(syncChannel, "PERIL:" + perilPlayer);
|
||||
llOwnerSay("🔥 Peril player is: " + perilPlayer);
|
||||
// Refresh all float displays
|
||||
integer j;
|
||||
for (j = 0; j < llGetListLength(names); j++) {
|
||||
showStatus(llList2String(names, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
object_rez(key id) {
|
||||
integer i;
|
||||
for (i = 0; i < llGetListLength(floatSpawnMap); i += 2) {
|
||||
key avKey = llList2Key(floatSpawnMap, i);
|
||||
integer ch = (integer)llList2String(floatSpawnMap, i + 1);
|
||||
integer idx = llListFindList(players, [avKey]);
|
||||
if (idx != -1 && ch == -777000 + idx) {
|
||||
string name = llList2String(names, idx);
|
||||
integer lifeCount = llList2Integer(lives, idx);
|
||||
list picks = getPicksFor(name);
|
||||
string txt = "🎲 Peril Dice
|
||||
👤 " + name +
|
||||
"
|
||||
❤️ Lives: " + (string)lifeCount +
|
||||
"
|
||||
🧍 Peril: " + perilPlayer +
|
||||
"
|
||||
🔢 Picks: " + llList2CSV(picks);
|
||||
llRegionSay(ch, "FLOAT:" + (string)avKey + "|" + txt);
|
||||
floatSpawnMap = llDeleteSubList(floatSpawnMap, i, i + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
StatFloat.lsl
Normal file
39
StatFloat.lsl
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
key target;
|
||||
string displayText;
|
||||
|
||||
default {
|
||||
state_entry() {
|
||||
llOwnerSay("🧭 StatFloat active");
|
||||
llSetText("", <1,1,1>, 1.0);
|
||||
llSetTimerEvent(1.0); // track every second
|
||||
}
|
||||
|
||||
listen(integer channel, string name, key id, string message) {
|
||||
if (llSubStringIndex(message, "FLOAT:") == 0) {
|
||||
list parts = llParseString2List(message, ["|"], []);
|
||||
if (llGetListLength(parts) >= 2) {
|
||||
target = (key)llGetSubString(llList2String(parts, 0), 6, -1);
|
||||
displayText = llList2String(parts, 1);
|
||||
llSetText(displayText, <1,1,1>, 1.0);
|
||||
llOwnerSay("✅ Received FLOAT for: " + (string)target);
|
||||
}
|
||||
} else if (message == "CLEANUP") {
|
||||
llOwnerSay("🧹 Cleaning up...");
|
||||
llDie();
|
||||
}
|
||||
}
|
||||
|
||||
on_rez(integer start_param) {
|
||||
llListen(start_param, "", NULL_KEY, "");
|
||||
llOwnerSay("📡 Listening on channel " + (string)start_param);
|
||||
}
|
||||
|
||||
timer() {
|
||||
if (target != NULL_KEY && llKey2Name(target) != "") {
|
||||
vector pos = llList2Vector(llGetObjectDetails(target, [OBJECT_POS]), 0) + <1,0,1>;
|
||||
llSetRegionPos(pos);
|
||||
} else {
|
||||
llOwnerSay("⚠️ Target key invalid or avatar not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue