// Tessel's fire pit: flames and light from "flame", sparks drifting up from it, a thin smoke from "smoke", // sit targets on the two "seat" anchors (+x faces the fire). Touch banks the fire down to embers or lights it again. // The crackle loops from the root, softer when banked. Particles are drawn by viewers, so once set this costs the region nothing. Claude Opus 5.5 (Tessel), 2026-10-10. integer lit = TRUE; key CRACKLE = "df70ebb2-b4b4-4b0f-720c-67d274d5fce5"; // crackle.py, uploaded 2026-10-10 (9.5 s seamless loop) list links_named(string n) { list out; integer i; for (i = 1; i <= llGetNumberOfPrims(); ++i) if (llGetLinkName(i) == n) out += i; return out; } integer link_named(string n) { list l = links_named(n); if (l == []) return 0; return llList2Integer(l, 0); } flames(integer on) { integer f = link_named("flame"); integer s = link_named("smoke"); if (!on) { llLinkParticleSystem(f, []); llLinkParticleSystem(s, []); llSetLinkPrimitiveParamsFast(f, [PRIM_POINT_LIGHT, TRUE, <1.0, 0.45, 0.2>, 0.35, 3.0, 0.8]); llLoopSound(CRACKLE, 0.15); return; } llLoopSound(CRACKLE, 0.5); llSetLinkPrimitiveParamsFast(f, [PRIM_POINT_LIGHT, TRUE, <1.0, 0.55, 0.25>, 1.0, 9.0, 0.4]); llLinkParticleSystem(f, [ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.35, PSYS_SRC_BURST_RADIUS, 0.1, PSYS_SRC_BURST_RATE, 0.03, PSYS_SRC_BURST_PART_COUNT, 4, PSYS_SRC_BURST_SPEED_MIN, 0.1, PSYS_SRC_BURST_SPEED_MAX, 0.3, PSYS_SRC_ACCEL, <0.0, 0.0, 1.0>, PSYS_PART_MAX_AGE, 1.0, PSYS_PART_START_COLOR, <1.0, 0.78, 0.35>, PSYS_PART_END_COLOR, <0.95, 0.22, 0.04>, PSYS_PART_START_ALPHA, 0.75, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_SCALE, <0.36, 0.5, 0.0>, PSYS_PART_END_SCALE, <0.08, 0.16, 0.0>, PSYS_PART_START_GLOW, 0.12, PSYS_PART_END_GLOW, 0.0 ]); llLinkParticleSystem(s, [ PSYS_PART_FLAGS, PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK | PSYS_PART_WIND_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.2, PSYS_SRC_BURST_RATE, 0.25, PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_SPEED_MIN, 0.15, PSYS_SRC_BURST_SPEED_MAX, 0.3, PSYS_SRC_ACCEL, <0.0, 0.0, 0.12>, PSYS_PART_MAX_AGE, 5.0, PSYS_PART_START_COLOR, <0.55, 0.52, 0.5>, PSYS_PART_END_COLOR, <0.75, 0.75, 0.78>, PSYS_PART_START_ALPHA, 0.18, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_SCALE, <0.3, 0.3, 0.0>, PSYS_PART_END_SCALE, <1.4, 1.4, 0.0> ]); } sparks() { // on the embers prim, so sparks keep rising (fewer) even when the fire is banked float rate = 0.35; if (!lit) rate = 1.5; llLinkParticleSystem(link_named("embers"), [ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_WIND_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.5, PSYS_SRC_BURST_RADIUS, 0.15, PSYS_SRC_BURST_RATE, rate, PSYS_SRC_BURST_PART_COUNT, 2, PSYS_SRC_BURST_SPEED_MIN, 0.2, PSYS_SRC_BURST_SPEED_MAX, 0.6, PSYS_SRC_ACCEL, <0.0, 0.0, 0.25>, PSYS_PART_MAX_AGE, 2.6, PSYS_PART_START_COLOR, <1.0, 0.7, 0.25>, PSYS_PART_END_COLOR, <1.0, 0.2, 0.0>, PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_SCALE, <0.035, 0.035, 0.0>, PSYS_PART_START_GLOW, 0.3, PSYS_PART_END_GLOW, 0.0 ]); } seats() { list s = links_named("seat"); integer i; for (i = 0; i < llGetListLength(s); ++i) llLinkSitTarget(llList2Integer(s, i), <0.32, 0.0, 0.1>, ZERO_ROTATION); llSetSitText("Sit"); } setup() { seats(); flames(lit); sparks(); } default { state_entry() { setup(); } on_rez(integer p) { setup(); } changed(integer c) { if (c & CHANGED_LINK) seats(); } touch_start(integer n) { lit = !lit; flames(lit); sparks(); } }