-- TOGEN'S DR. ROBOTNIK'S -- _____ _ _ ___ _ _ -- / ___(_) | | / _ \ (_) | | -- \ `--. _ _ __ ___ _ __ | | ___ _ __ / /_\ \_ __ _ _ __ ___ __ _| | -- `--. \ | '_ ` _ \| '_ \| |/ _ \ '__| | _ | '_ \| | '_ ` _ \ / _` | | -- /\__/ / | | | | | | |_) | | __/ | | | | | | | | | | | | | | (_| | |____ -- \____/|_|_| |_| |_| .__/|_|\___|_| \_| |_/_| |_|_|_| |_| |_|\__,_\_____/ -- | | -- |_| V1.01.00 local CURRENT_VERSION = 010100 local ANIMATION_DEFINITIONS = {} local ANIMATION_HELPERS = {} local SEQUENCE_HOOKS = {} -- This script is an Open Asset by Togen. As part of its functions, it allows newer versions of the script to overwrite its functions. -- If you think somebody's got a weird hack that is doing bad things to other Simpler AnimaLs, either contact me or make use of the SRB2 MB's Porting rules. -- _ _ _____ _ ______ ___________ ______ _ _ _ _ _____ _____ _____ _____ _ _ _____ -- | | | || ___| | | ___ \ ___| ___ \ | ___| | | | \ | / __ \_ _|_ _| _ | \ | |/ ___| -- | |_| || |__ | | | |_/ / |__ | |_/ / | |_ | | | | \| | / \/ | | | | | | | | \| |\ `--. -- | _ || __|| | | __/| __|| / | _| | | | | . ` | | | | | | | | | | . ` | `--. \ -- | | | || |___| |____| | | |___| |\ \ | | | |_| | |\ | \__/\ | | _| |_\ \_/ / |\ |/\__/ / -- \_| |_/\____/\_____/\_| \____/\_| \_| \_| \___/\_| \_/\____/ \_/ \___/ \___/\_| \_/\____/ function ANIMATION_HELPERS.getPlayer(mobj) if mobj.player then -- the actual player return mobj.player elseif mobj.target if mobj.target.player then -- the target's player for overlays and stuff. return mobj.target.player elseif mobj.target.target if mobj.target.target.player then -- :weary: return mobj.target.target.player end end end end function ANIMATION_HELPERS.getSkin(mobj) if mobj.skin then -- the actual skin return mobj.skin elseif mobj.target if mobj.target.skin then -- the target's skin for secondcolor signpost overlays and stuff. return mobj.target.skin elseif mobj.target.target if mobj.target.target.skin then -- just in case. return mobj.target.target.skin end end end end function ANIMATION_HELPERS.isSpinningWheels(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) local buttons = K_GetKartButtons(player) local onground = P_IsObjectOnGround(player.mo) -- the player object specifically, not signposts! return ((buttons & BT_ACCELERATE) or (onground and player.speed > 0)) end function ANIMATION_HELPERS.isLookingBack(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) local buttons = K_GetKartButtons(player) return (buttons & BT_LOOKBACK) end function ANIMATION_HELPERS.isTurningRight(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) local minturn = KART_FULLTURN/8 return (player.cmd.turning < (minturn * -1)) end function ANIMATION_HELPERS.isTurningLeft(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) local minturn = KART_FULLTURN/8 return (player.cmd.turning > (minturn)) end function ANIMATION_HELPERS.isDriftingRight(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) if (abs(player.aizdriftturn) >= ANGLE_90) then return false elseif (player.curshield == KSHIELD_TOP) then return ANIMATION_HELPERS.isTurningLeft(mobj) else return (player.drift < 0) end end function ANIMATION_HELPERS.isDriftingLeft(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) if (abs(player.aizdriftturn) >= ANGLE_90) then return false elseif (player.curshield == KSHIELD_TOP) then return ANIMATION_HELPERS.isTurningRight(mobj) else return (player.drift > 0) end end function ANIMATION_HELPERS.isFast(mobj) local player = ANIMATION_HELPERS.getPlayer(mobj) local fastspeed = (K_GetKartSpeed(player, false, true) * 17) / 20 local speedthreshold = player.mo.scale / 8 -- specifcally the player object and not a signpost! return ((player.speed >= fastspeed) and (player.speed >= (player.lastspeed - speedthreshold))) end function ANIMATION_HELPERS.isMovingForward(mobj) -- Checks if the player is moving forward (conveyors, etc. ignored.) local player = ANIMATION_HELPERS.getPlayer(mobj) local mymovangle = R_PointToAngle2(0, 0, player.rmomx, player.rmomy) local anglediff = (AngleFixed(mymovangle - mobj.angle)/FRACUNIT) % 360 return (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 player = ANIMATION_HELPERS.getPlayer(mobj) local mymovangle = R_PointToAngle2(0, 0, player.rmomx, player.rmomy) local anglediff = (AngleFixed(mymovangle - mobj.angle)/FRACUNIT) % 360 return (player.speed > 0 and anglediff > 90 and anglediff < 270) 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. local flipped = mobj.eflags & MFE_VERTICALFLIP local fallspeed = mobj.momz if flipped then fallspeed = $ * -1 end return fallspeed > 0 and not P_IsObjectOnGround(mobj) end function ANIMATION_HELPERS.isDescending(mobj) -- Checks if the player is falling through the air. local flipped = mobj.eflags & MFE_VERTICALFLIP local fallspeed = mobj.momz if flipped then fallspeed = $ * -1 end return fallspeed < 0 and not P_IsObjectOnGround(mobj) end function ANIMATION_HELPERS.isTricking(mobj) -- Checks if the player has a trick boost ready. local tricking = ANIMATION_HELPERS.getPlayer(mobj).trickpanel local TRICKSTATE_READY = 1 -- pov your value is predictable but you are not exposed to Lua if tricking then return (tricking > TRICKSTATE_READY) -- people are already talking about porting acro/otrx so let's leave some space here...? end return false end function ANIMATION_HELPERS.squaredSlow(mobj, step) -- Function to slow down animations at low speeds. local player = ANIMATION_HELPERS.getPlayer(mobj) local currentspeed = player.speed local threshold = (K_GetKartSpeed(player, false, true) * 17) / 20 local quarterspeed = currentspeed < threshold / 4 and mobj.Simpler_AnimaL.total_timer %4 ~= 0 local halfspeed = currentspeed < threshold / 2 and mobj.Simpler_AnimaL.total_timer %2 ~= 0 if quarterspeed or halfspeed then mobj.Simpler_AnimaL.timer = $ + step end end ANIMATION_HELPERS.SquaredSlow = ANIMATION_HELPERS.squaredSlow -- this should've had median capitals only but let's keep compatibility -- ___ _ _ ________ ___ ___ _____ _____ _____ _ _ -- / _ \ | \ | |_ _| \/ | / _ \_ _|_ _| _ | \ | | -- / /_\ \| \| | | | | . . |/ /_\ \| | | | | | | | \| | -- | _ || . ` | | | | |\/| || _ || | | | | | | | . ` | -- | | | || |\ |_| |_| | | || | | || | _| |_\ \_/ / |\ | -- \_| |_/\_| \_/\___/\_| |_/\_| |_/\_/ \___/ \___/\_| \_/ local function this_Simpler_AnimaL_setup(...) -- first argument is character name, all others are sprite names. local simple_args = {...} local thischaracter = "" for k, v in ipairs(simple_args) do if k == 1 then _G["SIMPLER_ANIMAL_DEFINITIONS"][v] = {} -- track character name. thischaracter = v elseif (string.sub(v, 1, 5) == "FLAG_") then _G["SIMPLER_ANIMAL_DEFINITIONS"][thischaracter][v] = true -- track Simpler AnimaL flags. else freeslot("SPR2_" + v) -- freeslot sprite name. end end end local function this_Simpler_AnimaL_thinker(mobj) if not (mobj and mobj.valid) then return elseif mobj.skin and _G["SIMPLER_ANIMAL_DEFINITIONS"][mobj.skin] then if not mobj.Simpler_AnimaL then mobj.Simpler_AnimaL = {["timer"] = 0, ["total_timer"] = 0} end local anime = mobj.Simpler_AnimaL anime.last_hook = nil local skin = ANIMATION_HELPERS.getSkin(mobj) local skin_data = skins[skin] local last_sequence = anime.last_sequence local sequence_number = mobj.sprite2 local expected_sequence = spr2names[sequence_number] -- Current vanilla animation. local expected_start = 0 local expected_loop = 0 local expected_dur = 0 local gonzales = mobj.frame & FF_PAPERSPRITE if mobj.hitlag and (not _G["SIMPLER_ANIMAL_DEFINITIONS"][mobj.skin].FLAG_HITLAG) then -- Most people aren't going to want characters to animate during this. if mobj.sprite2 ~= SPR2_DEAD then mobj.sprite2 = spr2names[last_sequence] mobj.frame = anime.last_frame anime.last_hook = "simpler_animal_hitstun_interrupt" -- I don't know why you'd need this, but let's track it anyway. else anime.last_hook = "simpler_animal_death_interrupt" end return end for key, hook in pairs(_G["SIMPLER_ANIMAL_HOOKS"]) do local result, start, loop, dur = hook(mobj) -- Check hooks for return values. if result ~= nil then expected_sequence = result -- Desired custom animation. expected_start = (start or A) expected_loop = (loop or A) expected_dur = (dur or A) sequence_number = spr2names[expected_sequence] anime.last_hook = key break end end anime.total_timer = $ + 1 if (expected_sequence ~= last_sequence) then -- Start a new sequence. anime.timer = expected_start anime.total_timer = 0 -- This value only changes upon a state change, use it instead of leveltime for animation timing. anime.last_sequence = expected_sequence elseif (expected_start > expected_dur) and (expected_start > A) then-- Backing up. anime.timer = ($ - 1) elseif (expected_dur > A) then -- Advance forward. anime.timer = ($ + 1) else anime.timer = 0 anime.total_timer = 0 end local temp_timer = anime.timer % (1 + abs(expected_dur - expected_start)) -- Get progress between start and loop. if temp_timer < 0 then -- Fix an underflow. temp_timer = $ + max(expected_start, expected_dur) end if anime.last_hook then mobj.sprite2 = sequence_number mobj.frame = (min(expected_start, expected_dur) + temp_timer) | gonzales -- Add timer to lower value of end or start, match the "real" sprite's flatness. else anime.total_timer = -1 end anime.last_frame = mobj.frame end end -- _____ ________ _________ ___ _____ ___________ _____ _ _____ _______ __ -- / __ \ _ | \/ || ___ \/ _ \_ _|_ _| ___ \_ _| | |_ _|_ _\ \ / / -- | / \/ | | | . . || |_/ / /_\ \| | | | | |_/ / | | | | | | | | \ V / -- | | | | | | |\/| || __/| _ || | | | | ___ \ | | | | | | | | \ / -- | \__/\ \_/ / | | || | | | | || | _| |_| |_/ /_| |_| |_____| |_ | | | | -- \____/\___/\_| |_/\_| \_| |_/\_/ \___/\____/ \___/\_____/\___/ \_/ \_/ local last_version = nil if not (_G["SIMPLER_ANIMAL_VERSION"]) then -- Step 1: Check if this is the first Simpler AnimaL instance. rawset(_G, "SIMPLER_ANIMAL_VERSION", CURRENT_VERSION) rawset(_G, "SIMPLER_ANIMAL_ALL_VERSIONS", {CURRENT_VERSION}) rawset(_G, "SIMPLER_ANIMAL_SETUP", this_Simpler_AnimaL_setup) rawset(_G, "SIMPLER_ANIMAL_THINKER", this_Simpler_AnimaL_thinker) rawset(_G, "SIMPLER_ANIMAL_DEFINITIONS", {}) -- dictionary full of character names rawset(_G, "SIMPLER_ANIMAL_HELPERS", {}) -- helper functions rawset(_G, "SIMPLER_ANIMAL_HOOKS", {}) -- creator-made hooks elseif (_G["SIMPLER_ANIMAL_VERSION"] < CURRENT_VERSION) then last_version = _G["SIMPLER_ANIMAL_VERSION"] print("[Simpler AnimaL] Running an update.") -- Step 1: Check if we can update other instances. table.insert(SIMPLER_ANIMAL_ALL_VERSIONS, CURRENT_VERSION) rawset(_G, "SIMPLER_ANIMAL_VERSION", CURRENT_VERSION) rawset(_G, "SIMPLER_ANIMAL_SETUP", this_Simpler_AnimaL_setup) rawset(_G, "SIMPLER_ANIMAL_THINKER", this_Simpler_AnimaL_thinker) end if not SIMPLER_ANIMAL_HARDHOOKS then rawset(_G, "SIMPLER_ANIMAL_HARDHOOKS", {}) end local this_player_version = 1 local this_signpiece_version = 1 local update_player = false local update_signpiece = false if (not SIMPLER_ANIMAL_HARDHOOKS.player) update_player = true else update_player = (SIMPLER_ANIMAL_HARDHOOKS.player < this_player_version) end if (not SIMPLER_ANIMAL_HARDHOOKS.signpiece) then update_signpiece = true else update_signpiece = (SIMPLER_ANIMAL_HARDHOOKS.signpiece < this_signpiece_version) end if update_player then SIMPLER_ANIMAL_HARDHOOKS.player = this_player_version addHook("PostThinkFrame", function(mo) if SIMPLER_ANIMAL_HARDHOOKS.player > this_player_version then return end for player in players.iterate do if not (player.valid and player.mo) then continue end SIMPLER_ANIMAL_THINKER(player.mo) end end) end if update_signpiece then SIMPLER_ANIMAL_HARDHOOKS.signpiece = this_signpiece_version addHook("MobjThinker", function(mo) if SIMPLER_ANIMAL_HARDHOOKS.signpiece > this_signpiece_version then return end return SIMPLER_ANIMAL_THINKER(mo) end, MT_SIGN_PIECE) end for key, helper in pairs(ANIMATION_HELPERS) do if not SIMPLER_ANIMAL_HELPERS[key] then SIMPLER_ANIMAL_HELPERS[key] = helper end end -- ______ ___________ _ _ _____ -- | _ \ ___| ___ \ | | | __ \ -- | | | | |__ | |_/ / | | | | \/ -- | | | | __|| ___ \ | | | | __ -- | |/ /| |___| |_/ / |_| | |_\ \ -- |___/ \____/\____/ \___/ \____/ local function this_Simpler_AnimaL_debug() print("Simpler AnimaL versions: " + table.concat(SIMPLER_ANIMAL_ALL_VERSIONS, ", ")) local keys = {} for k, v in pairs(SIMPLER_ANIMAL_DEFINITIONS) do table.insert(keys, k) end print("Simpler AnimaL definitions: " + table.concat(keys, ", ")) end if not SIMPLER_ANIMAL_DEBUG_FUNC then -- Add this console command for some testing purposes. rawset(_G, "SIMPLER_ANIMAL_DEBUG_FUNC", this_Simpler_AnimaL_debug) COM_AddCommand("simpler_animal", function() SIMPLER_ANIMAL_DEBUG_FUNC() end, COM_LOCAL) end