// Kamehameha (wearable). Wear on Chest; say "kamehameha" in chat (or touch it) and a ball of light charges in // front of you, then a beam fires with the word flying out ahead of it. Made for a friend, 2026-10-11. // Two prims: root = the ball, child "words" = a second emitter for the flying text. Claude Opus 5.5 (Tessel). key WORDS_TEX = "3e434748-227d-ea6a-b650-181991ca772f"; integer WORDS; float t0; integer busy; integer fired; float CHARGE = 2.4; float FIRE = 2.2; vector HIP = <0.22, -0.2, -0.36>; // ball between the cupped hands at the right hip (chest frame) vector FRONT = <0.62, 0, -0.04>; // and out in front of the palms when it fires string ANIM; // the pose, if one is in the contents (kamehameha.json) integer link_named(string n) { integer i = llGetNumberOfPrims(); for (; i > 1; --i) if (llGetLinkName(i) == n) return i; return 0; } ball(float size, float alpha, float glow, vector at) { list where = []; if (llGetAttached()) where = [PRIM_POS_LOCAL, at]; llSetLinkPrimitiveParamsFast(LINK_ROOT, where + [PRIM_SIZE, , PRIM_COLOR, ALL_SIDES, <0.75, 0.92, 1.0>, alpha, PRIM_GLOW, ALL_SIDES, glow, PRIM_FULLBRIGHT, ALL_SIDES, TRUE]); } rest() { busy = FALSE; llSetTimerEvent(0); llLinkParticleSystem(LINK_ROOT, []); llLinkParticleSystem(WORDS, []); ball(0.1, 0.0, 0.0, FRONT); } charge_fx() // sparks drawn in toward the ball { llLinkParticleSystem(LINK_ROOT, [ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_TARGET_POS_MASK | PSYS_PART_INTERP_COLOR_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE, PSYS_SRC_BURST_RADIUS, 0.8, PSYS_SRC_TARGET_KEY, llGetKey(), PSYS_SRC_BURST_RATE, 0.03, PSYS_SRC_BURST_PART_COUNT, 6, PSYS_SRC_BURST_SPEED_MIN, 0.0, PSYS_SRC_BURST_SPEED_MAX, 0.0, PSYS_PART_MAX_AGE, 0.7, PSYS_PART_START_SCALE, <0.07, 0.07, 0>, PSYS_PART_START_COLOR, <0.5, 0.8, 1.0>, PSYS_PART_END_COLOR, <1, 1, 1>, PSYS_PART_START_ALPHA, 0.2, PSYS_PART_END_ALPHA, 1.0, PSYS_PART_START_GLOW, 0.3, PSYS_PART_END_GLOW, 0.3]); } fire_fx() { llLinkParticleSystem(LINK_ROOT, [ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.03, PSYS_SRC_BURST_RATE, 0.01, PSYS_SRC_BURST_PART_COUNT, 6, PSYS_SRC_BURST_SPEED_MIN, 13.0, PSYS_SRC_BURST_SPEED_MAX, 14.0, PSYS_PART_MAX_AGE, 1.4, PSYS_PART_START_SCALE, <0.5, 0.5, 0>, PSYS_PART_END_SCALE, <1.0, 1.0, 0>, PSYS_PART_START_COLOR, <0.9, 0.98, 1.0>, PSYS_PART_END_COLOR, <0.3, 0.65, 1.0>, PSYS_PART_START_ALPHA, 0.9, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_GLOW, 0.35, PSYS_PART_END_GLOW, 0.1]); llLinkParticleSystem(WORDS, [ PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_SCALE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.0, PSYS_SRC_TEXTURE, WORDS_TEX, PSYS_SRC_BURST_RATE, 0.45, PSYS_SRC_BURST_PART_COUNT, 1, PSYS_SRC_BURST_SPEED_MIN, 4.5, PSYS_SRC_BURST_SPEED_MAX, 4.5, PSYS_PART_MAX_AGE, 3.5, PSYS_PART_START_SCALE, <2.4, 0.6, 0>, PSYS_PART_END_SCALE, <3.6, 0.9, 0>, PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0]); } go() { if (busy) return; busy = TRUE; fired = FALSE; t0 = llGetTime(); if (ANIM != "" && (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION)) llStartAnimation(ANIM); charge_fx(); llSetTimerEvent(0.1); } default { state_entry() { WORDS = link_named("words"); ANIM = llGetInventoryName(INVENTORY_ANIMATION, 0); if (llGetAttached()) llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION); llListen(0, "", llGetOwner(), ""); rest(); } on_rez(integer n) { llResetScript(); } changed(integer c) { if (c & CHANGED_INVENTORY) llResetScript(); } attach(key id) { // a hand's length in front of the chest, emitters (local +z) pointing forward if (id != NULL_KEY) { llSetPrimitiveParams([PRIM_POS_LOCAL, FRONT, PRIM_ROT_LOCAL, llEuler2Rot(<0, PI_BY_TWO, 0>)]); llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION); // automatic for attachments } } listen(integer ch, string name, key id, string msg) { if (llSubStringIndex(llToLower(msg), "kamehameha") >= 0) go(); } touch_start(integer n) { if (llDetectedKey(0) == llGetOwner()) go(); } timer() { float t = llGetTime() - t0; if (t < CHARGE) { float s = t / CHARGE; ball(0.08 + 0.3 * s, 0.25 + 0.6 * s, 0.2 + 0.5 * s, HIP); } else if (t < CHARGE + FIRE) { if (!fired) { fired = TRUE; fire_fx(); ball(0.45, 0.9, 0.8, FRONT); } float u = (t - CHARGE) / FIRE; if (u > 0.7) ball(0.45 * (1 - (u - 0.7) / 0.3) + 0.05, 0.9, 0.8, FRONT); } else rest(); } }