--------------------------- -- Simple AnimaL 1.01.00 -- --------------------------- -- i think there was this whole thing about turning globals into locals making Lua faster? local A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P = A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P -- driving frames local Q, R, S = Q, R, S -- attacked, squished, signpost local T, U, V, W, X, Y, Z = T, U, V, W, X, Y, Z -- mod frames. for frames after Z, -- use numbers (starting with 26.) local CURRENT_VERSION = 010100 local ANIMATION_DEFINITIONS = {} local ANIMATION_HELPERS = {} local SEQUENCE_HOOKS = {} -- This script is Reusable content by Togen. As part of its functions, it allows newer versions of the script to overwrite its functions. -- Send me a message if you think somebody's got a weird hack that is doing bad things to other Simple AnimaLs, and I'll shoot them dead. -- Base sequences: -- drift -- drift_left -- drift_right -- goal -- pain -- run -- run_left -- run_right -- spin -- squish -- stand -- stand_left -- stand_right -- trick -- trick_left -- trick_right -- walk -- walk_left -- walk_right -- For anything else more complex, like checking if the player is airborne or underwater, create a sequence hook. All sequence hooks will -- be looped through, and the first result that returns a non-nil value will be used as the sequence value. Simple_AnimaL will handle the -- actual animation itself. function ANIMATION_HELPERS.isCountdown(mobj) -- Checks if we can start working on a start boost. return (leveltime >= starttime-(2*TICRATE) and leveltime <= starttime) end function ANIMATION_HELPERS.isCountdownCharging(mobj) -- Checks if we have any kind of acceleration input during the start. return mobj.player.kartstuff[k_boostcharge] > 0 end function ANIMATION_HELPERS.isMovingForward(mobj) -- Checks if the player is moving forward (conveyors, etc. ignored.) local mymovangle = R_PointToAngle2(0, 0, mobj.player.rmomx, mobj.player.rmomy) local anglediff = (AngleFixed(mymovangle - mobj.angle)/FRACUNIT) % 360 return (mobj.player.speed > 0 and not (anglediff > 90 and anglediff < 270)) end function ANIMATION_HELPERS.isMovingBackward(mobj) -- Checks if the player is moving backward (conveyors, etc. ignored.) local mymovangle = R_PointToAngle2(0, 0, mobj.player.rmomx, mobj.player.rmomy) local anglediff = (AngleFixed(mymovangle - mobj.angle)/FRACUNIT) % 360 return (mobj.player.speed > 0 and anglediff > 90 and anglediff < 270) end function ANIMATION_HELPERS.isTurningLeft(mobj) -- Checks if the player is turning left. return mobj.player.cmd.driftturn > 0 end function ANIMATION_HELPERS.isTurningRight(mobj) -- Checks if the player is turning right. return mobj.player.cmd.driftturn < 0 end function ANIMATION_HELPERS.isDriftingLeft(mobj) -- Checks if the player is drifting right. Current turning input ignored. return mobj.player.kartstuff[k_drift] > 0 end function ANIMATION_HELPERS.isDriftingRight(mobj) -- Checks if the player is drifting right. Current turning input ignored. return mobj.player.kartstuff[k_drift] < 0 end function ANIMATION_HELPERS.isPained(mobj) -- Checks if the player is pained (dying, failed a trick.) return ((mobj.health <= 0) or (mobj.player.trickstun)) end function ANIMATION_HELPERS.isSquished(mobj) -- Checks if the player is squished. return mobj.player.kartstuff[k_squishedtimer] > 0 end function ANIMATION_HELPERS.isSpinning(mobj) -- Checks if the player is spinning. return ((mobj.player.kartstuff[k_spinouttimer] > 0) or (mobj.player.powers[pw_nocontrol] and mobj.player.pflags & PF_SKIDDOWN) or mobj.player.pflags & PF_SLIDING) end function ANIMATION_HELPERS.isTricking(mobj) -- Checks if the player is tricking. return (mobj.player.trickactive or mobj.player.spintimer) and not(G_BattleGametype()) end function ANIMATION_HELPERS.isRunSpeed(mobj) -- Checks if the player is at running speed. return mobj.player.speed > mobj.scale * 20 end function ANIMATION_HELPERS.isWalkSpeed(mobj) -- Checks if the player is at walking speed, but not running speed. return mobj.player.speed > 0 and mobj.player.speed <= mobj.scale * 20 end function ANIMATION_HELPERS.isWalkSpeedMultiplied(mobj, fixed_multiplier) -- Checks if the player's speed is >= their walking speed multiplied (using fixed-point math.) local run = (mobj.scale * 20) return mobj.player.speed >= FixedMul(run, fixed_multiplier) end function ANIMATION_HELPERS.isNoSpeed(mobj) -- Checks if the player lacks innate speed (conveyors, etc. ignored.) return mobj.player.speed == 0 end function ANIMATION_HELPERS.isUnderwater(mobj) -- Checks if the player is underwater. return mobj.eflags & MFE_UNDERWATER end function ANIMATION_HELPERS.isAirborne(mobj) -- Checks if the player is airborne. return not P_IsObjectOnGround(mobj) end function ANIMATION_HELPERS.isAscending(mobj) -- Checks if the player is rising through the air. return mobj.momz > 0 and not P_IsObjectOnGround(mobj) end function ANIMATION_HELPERS.isDescending(mobj) -- Checks if the player is falling through the air. return mobj.momz < 0 and not P_IsObjectOnGround(mobj) end function ANIMATION_HELPERS.isRespawning(mobj) -- Checks if the player is respawning. return mobj.player.kartstuff[k_respawn] > 0 end function ANIMATION_HELPERS.isDropDashing(mobj) -- Checks if the player is charging a drop dash. return mobj.player.kartstuff[k_respawn] > 0 end function ANIMATION_HELPERS.isHyudoro(mobj) -- Checks if the player is ghostly. return mobj.player.kartstuff[k_hyudorotimer] > 0 end function ANIMATION_HELPERS.isGrow(mobj) -- Checks if the player is grown. return mobj.player.kartstuff[k_growshrinktimer] > 0 or (HugeQuest and p.huge > 0) end function ANIMATION_HELPERS.isShrink(mobj) -- Checks if the player is shrunk. return mobj.player.kartstuff[k_growshrinktimer] < 0 end function ANIMATION_HELPERS.isInvincible(mobj) -- Checks if the player is using an Invincibility item. return mobj.player.kartstuff[k_invincibilitytimer] > 0 end function ANIMATION_HELPERS.isSpringing(mobj) -- Checks if the player has bounced off of a map spring or used a Pogo Spring item. return mobj.player.kartstuff[k_pogospring] > 0 end function ANIMATION_HELPERS.isGoalOverlay(mobj) -- Checks if the mobj is a goalpost overlay that we can overwrite. return mobj.type == MT_OVERLAY and mobj.state == S_PLAY_SIGN and _G["SIMPLE_ANIMAL_DEFINITIONS"][mobj.skin] end function ANIMATION_HELPERS.isChainChomp(mobj) -- Checks if the player is riding a Chain Chomp (RollTheDice). return mobj.player.chomptimer > 0 end function ANIMATION_HELPERS.isFinished(mobj) -- Checks if the player has finished the level. return mobj.player.exiting end -- Helpers end here. local function this_Simple_AnimaL_thinker() if CURRENT_VERSION < _G["SIMPLE_ANIMAL_VERSION"] then -- neuter this hook forever, there's a newer version that should work better. return end local use_sequence = _G["SIMPLE_ANIMAL_CHANGER"] for mobj in thinkers.iterate("mobj") do if mobj.type == MT_PLAYER and not _G["SIMPLE_ANIMAL_DEFINITIONS"][mobj.skin] and mobj.sprite == SPR_UNKN then -- panic button to fix a hardcode bug. -- this won't fire if all of the players are AnimaLs, it makes for better testing that way. -- print("[Simple AnimaL] Panic button activated; a non-AnimaL skin tried to use nonexistent frames.") local vanilla_frames = {[S_KART_STND1] = A, [S_KART_STND2] = B, [S_KART_STND1_L] = C, [S_KART_STND2_L] = D, [S_KART_STND1_R] = E, [S_KART_STND2_R] = F, [S_KART_WALK1] = J, [S_KART_WALK2] = G, [S_KART_WALK1_L] = K, [S_KART_WALK2_L] = H, [S_KART_WALK1_R] = L, [S_KART_WALK2_R] = I, [S_KART_RUN1] = A, [S_KART_RUN2] = J, [S_KART_RUN1_L] = C, [S_KART_RUN2_L] = K, [S_KART_RUN1_R] = E, [S_KART_RUN2_R] = L, [S_KART_DRIFT1_L] = M, [S_KART_DRIFT2_L] = N, [S_KART_DRIFT1_R] = O, [S_KART_DRIFT2_R] = P, [S_KART_SPIN] = Q, [S_KART_PAIN] = Q, [S_KART_SQUISH] = R} for i=S_KART_STND1,S_KART_SQUISH do states[i].sprite, states[i].frame = SPR_PLAY, vanilla_frames[i] end states[S_PLAY_SIGN].sprite, states[S_PLAY_SIGN].frame = SPR_PLAY, S mobj.state = S_KART_STND1 -- hide the UNKN sprite this tic. elseif mobj.type == MT_PLAYER and _G["SIMPLE_ANIMAL_DEFINITIONS"][mobj.skin] then local helpers = _G["SIMPLE_ANIMAL_HELPERS"] if helpers.isSquished(mobj) use_sequence(mobj, {"squish", "pain", "spin", "stand"}) elseif helpers.isSpinning(mobj) use_sequence(mobj, {"spin", "stand"}) elseif helpers.isPained(mobj) use_sequence(mobj, {"pain", "spin", "stand"}) elseif helpers.isTricking(mobj) -- Acrobatics. Acrobasics doesn't use trick frames, but this way is much cooler so it stays. if helpers.isTurningRight(mobj) use_sequence(mobj, {"trick_right", "trick", "drift_right", "drift", "walk_right", "walk", "stand_right", "stand"}) else use_sequence(mobj, {"trick_left", "trick", "drift_left", "drift", "walk_left", "walk", "stand_left", "stand"}) end elseif helpers.isNoSpeed(mobj) if helpers.isTurningRight(mobj) use_sequence(mobj, {"stand_right", "stand"}) elseif helpers.isTurningLeft(mobj) use_sequence(mobj, {"stand_left", "stand"}) else use_sequence(mobj, {"stand"}) end elseif helpers.isDriftingLeft(mobj) and not helpers.isAirborne(mobj) use_sequence(mobj, {"drift_left", "drift", "walk_left", "walk", "stand_left", "stand"}) elseif helpers.isDriftingRight(mobj) and not helpers.isAirborne(mobj) use_sequence(mobj, {"drift_right", "drift", "walk_right", "walk", "stand_right", "stand"}) elseif helpers.isRunSpeed(mobj) if helpers.isTurningRight(mobj) use_sequence(mobj, {"run_right", "run", "walk_right", "walk", "stand_right", "stand"}) elseif helpers.isTurningLeft(mobj) use_sequence(mobj, {"run_left", "run", "walk_left", "walk", "stand_left", "stand"}) else use_sequence(mobj, {"run", "walk", "stand"}) end elseif helpers.isWalkSpeed(mobj) if helpers.isTurningRight(mobj) use_sequence(mobj, {"walk_right", "walk", "stand_right", "stand"}) elseif helpers.isTurningLeft(mobj) use_sequence(mobj, {"walk_left", "walk", "stand_left", "stand"}) else use_sequence(mobj, {"walk", "stand"}) end end elseif _G["SIMPLE_ANIMAL_HELPERS"].isGoalOverlay(mobj) then use_sequence(mobj, {"goal"}) end end end local function this_Simple_AnimaL_use_sequence(mobj, sequences) local skin = mobj.skin local last_sequence = mobj.Simple_AnimaL_sequence_last if not _G["SIMPLE_ANIMAL_DEFINITIONS"][skin] return end local validated_sequence for key, hook in pairs(_G["SIMPLE_ANIMAL_HOOKS"]) local result = hook(mobj, sequences) if result ~= nil validated_sequence = result break end end for key, sequence in pairs(sequences) if _G["SIMPLE_ANIMAL_DEFINITIONS"][skin][sequence] and validated_sequence == nil validated_sequence = sequence break end end local validated_sequence_table = _G["SIMPLE_ANIMAL_DEFINITIONS"][skin][validated_sequence] local validated_duration = #validated_sequence_table if (mobj.Simple_AnimaL_timer == nil) or (validated_sequence ~= last_sequence) mobj.Simple_AnimaL_timer = 1 mobj.Simple_AnimaL_sequence_last = validated_sequence elseif (validated_duration > 1) and (mobj.Simple_AnimaL_timer < validated_duration) mobj.Simple_AnimaL_timer = ($ + 1) else mobj.Simple_AnimaL_timer = 1 end local timer = max(1,mobj.Simple_AnimaL_timer) mobj.frame = validated_sequence_table[timer] end if not (_G["SIMPLE_ANIMAL_VERSION"]) then -- Step 1: Check if this is the first Simple AnimaL instance. rawset(_G, "SIMPLE_ANIMAL_VERSION", CURRENT_VERSION) rawset(_G, "SIMPLE_ANIMAL_THINKER", this_Simple_AnimaL_thinker) rawset(_G, "SIMPLE_ANIMAL_CHANGER", this_Simple_AnimaL_use_sequence) rawset(_G, "SIMPLE_ANIMAL_DEFINITIONS", {}) rawset(_G, "SIMPLE_ANIMAL_HELPERS", {}) rawset(_G, "SIMPLE_ANIMAL_HOOKS", {}) addHook("PostThinkFrame", this_Simple_AnimaL_thinker) elseif (_G["SIMPLE_ANIMAL_VERSION"] < CURRENT_VERSION) then print("[Simple AnimaL] Running an update.") if(_G["SIMPLE_ANIMAL_VERSION"] < 10100) -- helpers introduced in 1.01.00 rawset(_G, "SIMPLE_ANIMAL_HELPERS", {}) end -- Step 2: Check if we can update other instances. rawset(_G, "SIMPLE_ANIMAL_VERSION", CURRENT_VERSION) rawset(_G, "SIMPLE_ANIMAL_THINKER", this_Simple_AnimaL_thinker) rawset(_G, "SIMPLE_ANIMAL_CHANGER", this_Simple_AnimaL_use_sequence) addHook("PostThinkFrame", this_Simple_AnimaL_thinker) end -- Get defintions and hooks from this instance, add them to the global variables. for def, value in pairs(ANIMATION_DEFINITIONS) SIMPLE_ANIMAL_DEFINITIONS[def] = value end for key, helper in pairs(ANIMATION_HELPERS) if not SIMPLE_ANIMAL_HELPERS[key] SIMPLE_ANIMAL_HELPERS[key] = helper end end for key, hook in pairs(SEQUENCE_HOOKS) table.insert(SIMPLE_ANIMAL_HOOKS, hook) end