~Fiona's Script Library~
flip-book.lsl
| 4.46 KB
// Copyright 2025 by Fiona Sweet
// Free to use for any purpose
// Flip-Book Controller (Root prim: Book_Cover)
// Click cover to open. Click right page to advance, left page to go back.
// Closes to front cover after last spread or when going back from first spread.
string TEX_COVER = "FrontCover";
list ORDER = [
"InsideFrontCover","Page_01",
"Page_02","Page_03",
"Page_04","Page_05",
"Page_06","Page_07",
"Page_08","Page_09",
"Page_10","Page_11",
"Page_12","Page_13",
"Page_14","InsideBackCover"
]; // left,right pairs
string NAME_COVER = "Notes From Saturn";
string NAME_LEFT = "Book_Left";
string NAME_RIGHT = "Book_Right";
integer LINK_COVER = -1;
integer LINK_LEFT = -1;
integer LINK_RIGHT = -1;
integer gOpen = FALSE;
integer gSpread = 0;
integer gLastSpread = -1;
vector coverHome; // local pos of cover when closed
vector coverAwayOffset = <0.0, 0.0, -1.0>; // move cover 1m behind when open so clicks hit pages
integer findLink(string pname) {
integer i; integer n = llGetNumberOfPrims();
for (i = 1; i <= n; ++i) if (llGetLinkName(i) == pname) return i;
return -1;
}
integer texExists(string name) { return (llGetInventoryType(name) == INVENTORY_TEXTURE); }
showCover() {
// textures + visibility
llSetLinkPrimitiveParamsFast(LINK_COVER, [
PRIM_TEXTURE, ALL_SIDES, TEX_COVER, <1,1,0>, <0,0,0>, 0.0,
PRIM_COLOR, ALL_SIDES, <1,1,1>, 1.0,
PRIM_POS_LOCAL, coverHome
]);
llSetLinkPrimitiveParamsFast(LINK_LEFT, [PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0]);
llSetLinkPrimitiveParamsFast(LINK_RIGHT, [PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0]);
gOpen = FALSE;
gSpread = 0;
}
applySpread(integer spread) {
integer idx = spread * 2;
string leftTex = llList2String(ORDER, idx);
string rightTex = llList2String(ORDER, idx + 1);
// ensure pages visible
llSetLinkPrimitiveParamsFast(LINK_LEFT, [
PRIM_TEXTURE, ALL_SIDES, leftTex, <1,1,0>, <0,0,0>, 0.0,
PRIM_COLOR, ALL_SIDES, <1,1,1>, 1.0
]);
llSetLinkPrimitiveParamsFast(LINK_RIGHT, [
PRIM_TEXTURE, ALL_SIDES, rightTex, <1,1,0>, <0,0,0>, 0.0,
PRIM_COLOR, ALL_SIDES, <1,1,1>, 1.0
]);
}
openTo(integer spread) {
// move cover out of the way so page prims receive touches
llSetLinkPrimitiveParamsFast(LINK_COVER, [
PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0,
PRIM_POS_LOCAL, coverHome + coverAwayOffset
]);
gOpen = TRUE;
gSpread = spread;
applySpread(gSpread);
}
default
{
state_entry()
{
LINK_COVER = findLink(NAME_COVER);
LINK_LEFT = findLink(NAME_LEFT);
LINK_RIGHT = findLink(NAME_RIGHT);
if (LINK_COVER < 0 || LINK_LEFT < 0 || LINK_RIGHT < 0) {
llOwnerSay("Name prims exactly: 'Book_Cover' (root), 'Book_Left', 'Book_Right'. Link them with cover as root.");
return;
}
// remember cover home position (local)
list p = llGetLinkPrimitiveParams(LINK_COVER, [PRIM_POS_LOCAL]);
coverHome = llList2Vector(p, 0);
// quick check textures (warn only)
integer missing = FALSE;
if (!texExists(TEX_COVER)) { llOwnerSay("Missing texture: " + TEX_COVER); missing = TRUE; }
integer i; integer L = llGetListLength(ORDER);
for (i = 0; i < L; ++i) if (!texExists(llList2String(ORDER,i))) { missing = TRUE; llOwnerSay("Missing texture: " + llList2String(ORDER,i)); }
if (missing) llOwnerSay("Some textures missing; check inventory names.");
gLastSpread = (llGetListLength(ORDER) / 2) - 1; // 16 items -> 8 spreads -> last=7
// Start closed
showCover();
llOwnerSay("📖 Ready. Click the cover to open. Right page = next, Left page = back.");
}
// Click on the cover prim opens the book
touch_start(integer n)
{
if (!gOpen) openTo(0);
}
// Page prims send NEXT/PREV here
link_message(integer sender, integer num, string msg, key id)
{
if (msg == "NEXT") {
if (!gOpen) { openTo(0); return; }
if (gSpread < gLastSpread) {
gSpread++; applySpread(gSpread);
} else {
showCover(); // past the last spread -> close
}
}
else if (msg == "PREV") {
if (!gOpen) { openTo(0); return; }
if (gSpread > 0) {
gSpread--; applySpread(gSpread);
} else {
showCover(); // back from first spread -> close
}
}
}
}