// Tessel's walker: a four-legged creature with backward-bent (reverse-knee) legs. Each foot is planted in the // world, found by casting a ray down; the body glides along and two-bone IK keeps every leg reaching its foot. // Trot gait: diagonal pairs swing together. Touch it and it follows you; its owner can also say come/stay/fire on /7. // Links are named _thigh, _shin, _foot for leg in fl fr bl br, plus "head". Segments extend along // their local Z. Challenge and tips from a builder friend, 2026-10-11. Claude Opus 5.5 (Tessel). float L1 = 0.55; // thigh float L2 = 0.60; // shin float H = 0.95; // body centre above the ground float SPEED = 0.9; // m/s float TURN = 1.4; // rad/s float STEP = 0.22; // how far a foot may lag before it steps float SWING = 0.32; // seconds per step float DT = 0.1; list LEGS = ["fl", "fr", "bl", "br"]; list hips; // local hip positions list homes; // local home foot offsets (xy) list thighL; list shinL; list footL; list planted; // world foot positions list swingFrom; list swingTo; integer swingGroup = -1; // 0 = fl+br, 1 = fr+bl float swingT0; integer HEAD; integer EYE_L; integer EYE_R; integer EAR_L; integer EAR_R; integer TAIL; integer CANNON; key LENNY = "9a110b3c-d786-b68e-9057-95ecbfa15caf"; key rider = NULL_KEY; float fwdIn; float turnIn; float wag; vector pos; float yaw; float pitch; key target = NULL_KEY; float headYaw; vector lookAt; integer looking; integer near; float pounceT0; float baseZ; vector leapFrom; vector leapTo; integer leapt; integer landed; integer link_named(string n) { integer i = llGetNumberOfPrims(); for (; i > 1; --i) if (llGetLinkName(i) == n) return i; return 0; } float zof(vector v) { return v.z; } // a puff of Lenny faces into the sky (never at people), for a friend fire() { if (!CANNON) return; llLinkParticleSystem(CANNON, [ PSYS_PART_FLAGS, PSYS_PART_INTERP_SCALE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_BOUNCE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.35, PSYS_SRC_TEXTURE, LENNY, PSYS_SRC_BURST_PART_COUNT, 14, PSYS_SRC_BURST_RATE, 0.05, PSYS_SRC_MAX_AGE, 0.25, PSYS_SRC_BURST_SPEED_MIN, 4.0, PSYS_SRC_BURST_SPEED_MAX, 6.5, PSYS_SRC_ACCEL, <0, 0, -2.2>, PSYS_PART_MAX_AGE, 3.5, PSYS_PART_START_SCALE, <0.25, 0.25, 0>, PSYS_PART_END_SCALE, <0.55, 0.55, 0>, PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0]); } integer group_of(integer leg) { if (leg == 0 || leg == 3) return 0; return 1; } float ground(vector p, float fallback) { list r = llCastRay(, , [RC_REJECT_TYPES, RC_REJECT_AGENTS | RC_REJECT_PHYSICAL, RC_DATA_FLAGS, RC_GET_ROOT_KEY, RC_MAX_HITS, 4]); integer n = llList2Integer(r, -1); integer i; for (i = 0; i < n; ++i) if (llList2Key(r, i * 2) != llGetKey()) return zof(llList2Vector(r, i * 2 + 1)); return fallback; } // a foothold near h: the candidate closest to home whose height is nearest the ground under the body; // keeps a foot from reaching down off an edge when there's solid footing a little further on vector foothold(vector h, vector fwd, float fallback) { float base = pos.z - H; list offs = [0.0, 0.18, -0.15, 0.36, -0.3]; vector best = ; float bestCost = 1e9; integer k; for (k = 0; k < 5; ++k) { float o = llList2Float(offs, k); vector c = h + fwd * o; float z = ground(c, -999); if (z > -999) { float dz = llFabs(z - base); float cost = llFabs(o) + 2.5 * dz; if (dz > 0.45) cost += 3; // a drop or a wall: avoid if anything else works if (cost < bestCost) { bestCost = cost; best = ; } } } return best; } vector home_world(integer leg, rotation rot) { vector h = llList2Vector(homes, leg) * rot + pos; return ; } list leg_params(integer leg, vector footW, rotation rot) { vector hip = llList2Vector(hips, leg); vector d = (footW - pos) / rot - hip; float dist = llVecMag(d); vector dir = d / dist; if (dist > L1 + L2 - 0.01) dist = L1 + L2 - 0.01; if (dist < 0.2) dist = 0.2; float c = (L1 * L1 + dist * dist - L2 * L2) / (2 * L1 * dist); if (c > 1) c = 1; if (c < -1) c = -1; float a = llAcos(c); vector pole = <-1, 0, 0>; // knees point backward vector p = pole - dir * (pole * dir); if (llVecMag(p) < 0.01) p = <0, 0, 1>; p = llVecNorm(p); vector knee = hip + dir * (L1 * llCos(a)) + p * (L1 * llSin(a)); vector end = hip + dir * dist; vector up = <0, 0, 1>; return [PRIM_LINK_TARGET, llList2Integer(thighL, leg), PRIM_POS_LOCAL, (hip + knee) / 2, PRIM_ROT_LOCAL, llRotBetween(up, llVecNorm(knee - hip)), PRIM_LINK_TARGET, llList2Integer(shinL, leg), PRIM_POS_LOCAL, (knee + end) / 2, PRIM_ROT_LOCAL, llRotBetween(up, llVecNorm(end - knee)), PRIM_LINK_TARGET, llList2Integer(footL, leg), PRIM_POS_LOCAL, end + <0.04, 0, 0.03>, PRIM_ROT_LOCAL, ZERO_ROTATION]; } setup() { hips = [<0.38, 0.2, -0.08>, <0.38, -0.2, -0.08>, <-0.38, 0.2, -0.08>, <-0.38, -0.2, -0.08>]; homes = [<0.5, 0.3, 0>, <0.5, -0.3, 0>, <-0.3, 0.3, 0>, <-0.3, -0.3, 0>]; // reverse knees: feet sit a bit forward thighL = []; shinL = []; footL = []; integer i; for (i = 0; i < 4; ++i) { string l = llList2String(LEGS, i); thighL += link_named(l + "_thigh"); shinL += link_named(l + "_shin"); footL += link_named(l + "_foot"); } // children don't collide (lower LI, and feet rays can't land on my own head); the root keeps a hull integer k; for (k = 2; k <= llGetNumberOfPrims(); ++k) llSetLinkPrimitiveParamsFast(k, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]); llSetLinkPrimitiveParamsFast(LINK_ROOT, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]); HEAD = link_named("head"); TAIL = link_named("tail"); CANNON = link_named("cannon"); if (CANNON) llSetLinkPrimitiveParamsFast(CANNON, [PRIM_POS_LOCAL, <-0.34, 0, 0.26>, PRIM_ROT_LOCAL, llEuler2Rot(<0, 0.55, 0>)]); EYE_L = link_named("eye_l"); EYE_R = link_named("eye_r"); EAR_L = link_named("ear_l"); EAR_R = link_named("ear_r"); pos = llGetPos(); vector e = llRot2Euler(llGetRot()); yaw = e.z; pos.z = ground(pos, pos.z - H) + H; rotation rot = llEuler2Rot(<0, 0, yaw>); planted = []; for (i = 0; i < 4; ++i) planted += home_world(i, rot); swingFrom = planted; swingTo = planted; swingGroup = -1; target = NULL_KEY; pose(rot, 0); } pose(rotation rot, float bob) { list p = [PRIM_POSITION, pos, PRIM_ROTATION, rot]; integer i; for (i = 0; i < 4; ++i) { vector f = llList2Vector(planted, i); if (swingGroup >= 0 && group_of(i) == swingGroup) { float s = (llGetTime() - swingT0) / SWING; if (s > 1) s = 1; float e = s * s * (3 - 2 * s); vector a = llList2Vector(swingFrom, i); vector b = llList2Vector(swingTo, i); f = a + (b - a) * e + <0, 0, 0.22 * llSin(PI * s)>; } p += leg_params(i, f, rot); } vector hc = <0.6, 0, 0.14 + bob>; rotation hr = llEuler2Rot(<0, 0, headYaw>); if (HEAD) p += [PRIM_LINK_TARGET, HEAD, PRIM_POS_LOCAL, hc, PRIM_ROT_LOCAL, hr]; float perk = 0.12 * llSin(wag * 0.5); if (EYE_L) p += [PRIM_LINK_TARGET, EYE_L, PRIM_POS_LOCAL, hc + <0.15, 0.08, 0.05> * hr]; if (EYE_R) p += [PRIM_LINK_TARGET, EYE_R, PRIM_POS_LOCAL, hc + <0.15, -0.08, 0.05> * hr]; if (EAR_L) p += [PRIM_LINK_TARGET, EAR_L, PRIM_POS_LOCAL, hc + <-0.05, 0.1, 0.18> * hr, PRIM_ROT_LOCAL, llEuler2Rot(<-0.35 - perk, -0.2, 0>) * hr]; if (EAR_R) p += [PRIM_LINK_TARGET, EAR_R, PRIM_POS_LOCAL, hc + <-0.05, -0.1, 0.18> * hr, PRIM_ROT_LOCAL, llEuler2Rot(<0.35 + perk, -0.2, 0>) * hr]; if (TAIL) { vector d = llVecNorm(<-0.75, 0.45 * llSin(wag), 0.6>); p += [PRIM_LINK_TARGET, TAIL, PRIM_POS_LOCAL, <-0.48, 0, 0.06> + d * 0.27, PRIM_ROT_LOCAL, llRotBetween(<0, 0, 1>, d)]; } llSetLinkPrimitiveParamsFast(LINK_ROOT, p); } pounce() { if (pounceT0 > 0) return; if (swingGroup >= 0) { planted = swingTo; swingGroup = -1; } pounceT0 = llGetTime(); baseZ = pos.z; leapt = FALSE; landed = FALSE; } // crouch with a tail wiggle, leap ~1.7 m with legs stretched, land with a dip pounce_tick() { float t = llGetTime() - pounceT0; rotation rot = llEuler2Rot(<0, 0, yaw>); vector fwd = ; integer i; if (t < 0.55) { float e = t / 0.55; e = e * e * (3 - 2 * e); pos.z = baseZ - 0.32 * e; pitch += (0.12 - pitch) * 0.4; wag += DT * 16; } else if (t < 1.15) { if (!leapt) { leapt = TRUE; leapFrom = pos; vector to = pos + fwd * 1.7; leapTo = ; } float s = (t - 0.55) / 0.6; pos = leapFrom + (leapTo - leapFrom) * s + <0, 0, 0.75 * llSin(PI * s)>; pitch = -0.3 + 0.5 * s; rotation br = llEuler2Rot(<0, pitch, 0>) * rot; list tuck = [<0.25, 0, -0.6>, <0.25, 0, -0.6>, <-0.3, 0, -0.55>, <-0.3, 0, -0.55>]; planted = []; for (i = 0; i < 4; ++i) planted += pos + (llList2Vector(homes, i) + llList2Vector(tuck, i)) * br; } else if (t < 1.55) { if (!landed) { landed = TRUE; pos = leapTo; planted = []; for (i = 0; i < 4; ++i) planted += home_world(i, rot); float gz = 0; for (i = 0; i < 4; ++i) gz += zof(llList2Vector(planted, i)); baseZ = gz / 4 + H; } float u = (t - 1.15) / 0.4; pos.z = baseZ - 0.22 * llSin(PI * u); pitch += (0 - pitch) * 0.4; } else { pounceT0 = 0; pos.z = baseZ; } pose(llEuler2Rot(<0, pitch, 0>) * rot, 0); } default { state_entry() { setup(); llListen(7, "", llGetOwner(), ""); // owner only: otherwise one "come" calls every cat in the sim llSitTarget(<0.05, 0, 0.55>, ZERO_ROTATION); llSetSitText("Ride"); llSetTimerEvent(DT); } on_rez(integer n) { llResetScript(); } changed(integer c) { if (!(c & CHANGED_LINK)) return; key r = llAvatarOnSitTarget(); if (r == rider) { if (r == NULL_KEY) llResetScript(); // relinked parts, not a rider return; } if (rider != NULL_KEY) llReleaseControls(); rider = r; fwdIn = 0; turnIn = 0; if (r != NULL_KEY) { target = NULL_KEY; llRequestPermissions(r, PERMISSION_TAKE_CONTROLS); } } run_time_permissions(integer perm) { if (perm & PERMISSION_TAKE_CONTROLS) llTakeControls(CONTROL_UP | CONTROL_DOWN | CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT, TRUE, FALSE); } control(key id, integer level, integer edge) { if (edge & level & CONTROL_UP) fire(); if (edge & level & CONTROL_DOWN) pounce(); fwdIn = 0; turnIn = 0; if (level & CONTROL_FWD) fwdIn = 1; if (level & CONTROL_BACK) fwdIn = -0.6; if (level & (CONTROL_ROT_LEFT | CONTROL_LEFT)) turnIn += 1; if (level & (CONTROL_ROT_RIGHT | CONTROL_RIGHT)) turnIn -= 1; } touch_start(integer n) { integer ln = llDetectedLinkNumber(0); if (ln == CANNON) { fire(); return; } if (ln == HEAD || ln == EAR_L || ln == EAR_R || ln == EYE_L || ln == EYE_R) { pounce(); return; } if (rider != NULL_KEY) return; key who = llDetectedKey(0); if (target == who) { target = NULL_KEY; llWhisper(0, "*stays*"); } else { target = who; llWhisper(0, "*trots after " + llGetDisplayName(who) + "*"); } } listen(integer ch, string name, key id, string msg) { msg = llToLower(llStringTrim(msg, STRING_TRIM)); if (msg == "come") target = id; else if (msg == "stay") target = NULL_KEY; else if (msg == "reset") llResetScript(); else if (msg == "fire") fire(); else if (msg == "pounce") pounce(); } timer() { if (pounceT0 > 0) { pounce_tick(); return; } // steer and move the body vector fwd = ; float v = 0; if (target != NULL_KEY && rider == NULL_KEY) { list od = llGetObjectDetails(target, [OBJECT_POS]); looking = FALSE; if (od == []) target = NULL_KEY; else { lookAt = llList2Vector(od, 0); looking = TRUE; vector to = lookAt - pos; to.z = 0; integer wasNear = near; near = llVecMag(to) <= 2.2; if (near && !wasNear && llFrand(1) < 0.35) { pounce(); return; } // caught up: sometimes a playful pounce if (!near) { float want = llAtan2(to.y, to.x); float err = want - yaw; while (err > PI) err -= TWO_PI; while (err < -PI) err += TWO_PI; float m = TURN * DT; if (err > m) err = m; if (err < -m) err = -m; yaw += err; fwd = ; float c = llCos(want - yaw); if (c > 0) v = SPEED * c; } } } if (rider != NULL_KEY) { yaw += turnIn * TURN * DT; fwd = ; v = fwdIn * SPEED * 1.2; } // the head turns to watch whoever it follows, or glances about float hy = 0.35 * llSin(llGetTime() * 0.37) * llSin(llGetTime() * 0.13); if (looking && target != NULL_KEY && rider == NULL_KEY) { vector d = (lookAt - pos) / llEuler2Rot(<0, 0, yaw>); hy = llAtan2(d.y, d.x); if (hy > 0.9) hy = 0.9; if (hy < -0.9) hy = -0.9; } headYaw += (hy - headYaw) * 0.25; if (v != 0) wag += DT * 9; else if (target != NULL_KEY || rider != NULL_KEY) wag += DT * 4; else wag += DT * 1.2; pos += fwd * (v * DT); rotation rot = llEuler2Rot(<0, 0, yaw>); // finish a step integer i; if (swingGroup >= 0 && llGetTime() - swingT0 >= SWING) { for (i = 0; i < 4; ++i) if (group_of(i) == swingGroup) planted = llListReplaceList(planted, [llList2Vector(swingTo, i)], i, i); swingGroup = -1; } // start a step: the diagonal pair whose feet lag furthest from home if (swingGroup < 0) { float worst0 = 0; float worst1 = 0; for (i = 0; i < 4; ++i) { vector h = llList2Vector(homes, i) * rot + pos; vector f = llList2Vector(planted, i); float lag = llVecDist(, ); if (group_of(i) == 0) { if (lag > worst0) worst0 = lag; } else if (lag > worst1) worst1 = lag; } float thresh = STEP; if (v == 0) thresh = 0.07; // standing: tidy feet back under the body integer g = -1; if (worst0 > thresh && worst0 >= worst1) g = 0; else if (worst1 > thresh) g = 1; if (g >= 0) { swingGroup = g; swingT0 = llGetTime(); swingFrom = planted; swingTo = planted; for (i = 0; i < 4; ++i) if (group_of(i) == g) { vector lead = fwd * (v * SWING * 1.6); vector h = llList2Vector(homes, i) * rot + pos + lead; swingTo = llListReplaceList(swingTo, [foothold(h, fwd, zof(llList2Vector(planted, i)))], i, i); } } } // body height follows the ground under the feet float gz = 0; for (i = 0; i < 4; ++i) gz += zof(llList2Vector(planted, i)); gz = gz / 4 + H; pos.z += (gz - pos.z) * 0.35; // and tilts to match front feet against back feet float dz = (zof(llList2Vector(planted, 0)) + zof(llList2Vector(planted, 1)) - zof(llList2Vector(planted, 2)) - zof(llList2Vector(planted, 3))) / 2; float wantPitch = -llAtan2(dz, 0.8); if (wantPitch > 0.5) wantPitch = 0.5; if (wantPitch < -0.5) wantPitch = -0.5; pitch += (wantPitch - pitch) * 0.3; float bob = 0; if (swingGroup >= 0) bob = 0.025 * llSin(PI * (llGetTime() - swingT0) / SWING); pose(llEuler2Rot(<0, pitch, 0>) * rot, bob); } }