diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6f18c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +sourcemap.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..379ece8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[lua]": { + "editor.defaultFormatter": "JohnnyMorganz.stylua", + "editor.formatOnSave": true + } +} \ No newline at end of file diff --git a/aftman.toml b/aftman.toml new file mode 100644 index 0000000..3775946 --- /dev/null +++ b/aftman.toml @@ -0,0 +1,2 @@ +[tools] +rojo = "rojo-rbx/rojo@7.2.1" \ No newline at end of file diff --git a/default.project.json b/default.project.json new file mode 100644 index 0000000..0060a1c --- /dev/null +++ b/default.project.json @@ -0,0 +1,53 @@ +{ + "name": "Super Mario 64", + + "tree": { + "$className": "DataModel", + "$ignoreUnknownInstances": true, + + "ReplicatedFirst": { + "$className": "ReplicatedFirst", + "$ignoreUnknownInstances": true, + + "SM64": { + "$path": "SM64", + "$ignoreUnknownInstances": true + } + }, + + "ServerScriptService": { + "$className": "ServerScriptService", + "$ignoreUnknownInstances": true, + + "LazyNetworking": { + "$path": "rbx/LazyNetworking.server.lua" + } + }, + + "StarterPlayer": { + "$className": "StarterPlayer", + "$ignoreUnknownInstances": true, + + "StarterCharacterScripts": { + "$className": "StarterCharacterScripts", + "$ignoreUnknownInstances": true, + + "Animate": { + "$className": "Hole" + }, + + "Sound": { + "$className": "Hole" + }, + + "Health": { + "$className": "Hole" + }, + + "Character": { + "$path": "rbx/Character.server.lua" + } + } + } + } +} \ No newline at end of file diff --git a/rbx/Character.server.lua b/rbx/Character.server.lua new file mode 100644 index 0000000..a1934e5 --- /dev/null +++ b/rbx/Character.server.lua @@ -0,0 +1,97 @@ +--!strict +local Players = game:GetService("Players") + +local character: any = script.Parent +local player = Players:GetPlayerFromCharacter(character) + +if not player then + return +end + +local userId = player.UserId +local hDesc: HumanoidDescription? + +local function patchCollision(desc: Instance) + if desc:IsA("BasePart") and desc.CollisionGroupId ~= 1 then + local canCollide = desc:GetPropertyChangedSignal("CanCollide") + desc.CollisionGroup = "Player" + desc.CanQuery = false + desc.CanTouch = false + desc.Massless = true + + canCollide:Connect(function() + desc.CanCollide = false + end) + + desc.CanCollide = false + end +end + +local function patchAllCollision() + for i, desc in character:GetDescendants() do + task.spawn(patchCollision, desc) + end +end + +task.spawn(patchAllCollision) +character.DescendantAdded:Connect(patchCollision) + +local function reload() + for retry = 1, 10 do + local success, result = pcall(function() + return Players:GetHumanoidDescriptionFromUserId(userId) + end) + + if success then + hDesc = result + break + else + task.wait(retry / 2) + end + end + + if hDesc then + hDesc.HeadScale = 1.8 + hDesc.WidthScale = 1.3 + hDesc.DepthScale = 1.4 + hDesc.HeightScale = 1.2 + hDesc.BodyTypeScale = 0 + hDesc.ProportionScale = 0 + else + return + end + + local humanoid = character:WaitForChild("Humanoid") + assert(hDesc) + + if humanoid:IsA("Humanoid") then + while not humanoid.RootPart do + humanoid.Changed:Wait() + end + + local rootPart = humanoid.RootPart + assert(rootPart, "No HumanoidRootPart??") + + local particles = rootPart:FindFirstChild("Particles") + humanoid:ApplyDescription(hDesc) + + if particles and particles:IsA("Attachment") then + local floorDec = humanoid.HipHeight + (rootPart.Size.Y / 2) + local pos = Vector3.new(0, -floorDec, 0) + rootPart.PivotOffset = CFrame.new(pos) + particles.Position = pos + end + end +end + +local reset = Instance.new("RemoteEvent") +reset.Parent = character +reset.Name = "Reset" + +reset.OnServerEvent:Connect(function(player) + if player == Players:GetPlayerFromCharacter(character) then + reload() + end +end) + +task.spawn(reload) diff --git a/rbx/LazyNetworking.server.lua b/rbx/LazyNetworking.server.lua new file mode 100644 index 0000000..9e5fab8 --- /dev/null +++ b/rbx/LazyNetworking.server.lua @@ -0,0 +1,60 @@ +--!strict +local Validators: { [string]: (Player, ...any) -> boolean } = {} +type Echo = () -> () + +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local ReplicatedFirst = game:GetService("ReplicatedFirst") +local PhysicsService = game:GetService("PhysicsService") +local Sounds = require(ReplicatedFirst.SM64.Sounds) + +local lazy = Instance.new("RemoteEvent") +lazy.Parent = ReplicatedStorage +lazy.Name = "LazyNetwork" + +function Validators.PlaySound(player: Player, name: string) + local sound: Instance? = Sounds[name] + + if sound and sound:IsA("Sound") then + return true + end + + return false +end + +function Validators.SetParticle(player: Player, name: string, set: boolean?) + if typeof(name) ~= "string" then + return false + end + + local character = player.Character + + local rootPart = if character then character.PrimaryPart else nil + + if rootPart then + local particles = rootPart:FindFirstChild("Particles") + + local particle = if particles then particles:FindFirstChild(name) else nil + + if particle then + return true + end + end + + return false +end + +function Validators.SetAngle(player: Player, angle: Vector3int16) + return typeof(angle) == "Vector3int16" +end + +local function onNetworkReceive(player: Player, cmd: string, ...) + local validate = Validators[cmd] + + if validate and validate(player, ...) then + lazy:FireAllClients(player, cmd, ...) + end +end + +lazy.OnServerEvent:Connect(onNetworkReceive) +PhysicsService:CreateCollisionGroup("Player") +PhysicsService:CollisionGroupSetCollidable("Default", "Player", false) diff --git a/selene.toml b/selene.toml new file mode 100644 index 0000000..1f1e170 --- /dev/null +++ b/selene.toml @@ -0,0 +1 @@ +std = "roblox" \ No newline at end of file diff --git a/sm64/Animations.lua b/sm64/Animations.lua new file mode 100644 index 0000000..8e459fe --- /dev/null +++ b/sm64/Animations.lua @@ -0,0 +1,229 @@ +--!strict +local System = script.Parent +local Assets = System.Assets +local Anims = Assets.Animations + +local Data = table.freeze({ + SLOW_LEDGE_GRAB = Anims.SLOW_LEDGE_GRAB, + FALL_OVER_BACKWARDS = Anims.FALL_OVER_BACKWARDS, + BACKWARD_AIR_KB = Anims.BACKWARD_AIR_KB, + DYING_ON_BACK = Anims.DYING_ON_BACK, + BACKFLIP = Anims.BACKFLIP, + CLIMB_UP_POLE = Anims.CLIMB_UP_POLE, + GRAB_POLE_SHORT = Anims.GRAB_POLE_SHORT, + GRAB_POLE_SWING_PART1 = Anims.GRAB_POLE_SWING_PART1, + GRAB_POLE_SWING_PART2 = Anims.GRAB_POLE_SWING_PART2, + HANDSTAND_IDLE = Anims.HANDSTAND_IDLE, + HANDSTAND_JUMP = Anims.HANDSTAND_JUMP, + START_HANDSTAND = Anims.START_HANDSTAND, + RETURN_FROM_HANDSTAND = Anims.RETURN_FROM_HANDSTAND, + IDLE_ON_POLE = Anims.IDLE_ON_POLE, + A_POSE = Anims.A_POSE, + SKID_ON_GROUND = Anims.SKID_ON_GROUND, + STOP_SKID = Anims.STOP_SKID, + CROUCH_FROM_FAST_LONGJUMP = Anims.CROUCH_FROM_FAST_LONGJUMP, + CROUCH_FROM_SLOW_LONGJUMP = Anims.CROUCH_FROM_SLOW_LONGJUMP, + FAST_LONGJUMP = Anims.FAST_LONGJUMP, + SLOW_LONGJUMP = Anims.SLOW_LONGJUMP, + AIRBORNE_ON_STOMACH = Anims.AIRBORNE_ON_STOMACH, + WALK_WITH_LIGHT_OBJ = Anims.WALK_WITH_LIGHT_OBJ, + RUN_WITH_LIGHT_OBJ = Anims.RUN_WITH_LIGHT_OBJ, + SLOW_WALK_WITH_LIGHT_OBJ = Anims.SLOW_WALK_WITH_LIGHT_OBJ, + SHIVERING_WARMING_HAND = Anims.SHIVERING_WARMING_HAND, + SHIVERING_RETURN_TO_IDLE = Anims.SHIVERING_RETURN_TO_IDLE, + SHIVERING = Anims.SHIVERING, + CLIMB_DOWN_LEDGE = Anims.CLIMB_DOWN_LEDGE, + CREDITS_WAVING = Anims.CREDITS_WAVING, + CREDITS_LOOK_UP = Anims.CREDITS_LOOK_UP, + CREDITS_RETURN_FROM_LOOK_UP = Anims.CREDITS_RETURN_FROM_LOOK_UP, + CREDITS_RAISE_HAND = Anims.CREDITS_RAISE_HAND, + CREDITS_LOWER_HAND = Anims.CREDITS_LOWER_HAND, + CREDITS_TAKE_OFF_CAP = Anims.CREDITS_TAKE_OFF_CAP, + CREDITS_START_WALK_LOOK_UP = Anims.CREDITS_START_WALK_LOOK_UP, + CREDITS_LOOK_BACK_THEN_RUN = Anims.CREDITS_LOOK_BACK_THEN_RUN, + -- FINAL_BOWSER_RAISE_HAND_SPIN = Anims.FINAL_BOWSER_RAISE_HAND_SPIN; + -- FINAL_BOWSER_WING_CAP_TAKE_OFF = Anims.FINAL_BOWSER_WING_CAP_TAKE_OFF; + CREDITS_PEACE_SIGN = Anims.CREDITS_PEACE_SIGN, + STAND_UP_FROM_LAVA_BOOST = Anims.STAND_UP_FROM_LAVA_BOOST, + FIRE_LAVA_BURN = Anims.FIRE_LAVA_BURN, + WING_CAP_FLY = Anims.WING_CAP_FLY, + HANG_ON_OWL = Anims.HANG_ON_OWL, + LAND_ON_STOMACH = Anims.LAND_ON_STOMACH, + FORWARD_AIR_KB = Anims.FORWARD_AIR_KB, + DYING_ON_STOMACH = Anims.DYING_ON_STOMACH, + SUFFOCATING = Anims.SUFFOCATING, + COUGHING = Anims.COUGHING, + THROW_CATCH_KEY = Anims.THROW_CATCH_KEY, + DYING_FALL_OVER = Anims.DYING_FALL_OVER, + IDLE_ON_LEDGE = Anims.IDLE_ON_LEDGE, + FAST_LEDGE_GRAB = Anims.FAST_LEDGE_GRAB, + HANG_ON_CEILING = Anims.HANG_ON_CEILING, + PUT_CAP_ON = Anims.PUT_CAP_ON, + TAKE_CAP_OFF_THEN_ON = Anims.TAKE_CAP_OFF_THEN_ON, + QUICKLY_PUT_CAP_ON = Anims.QUICKLY_PUT_CAP_ON, -- unused + HEAD_STUCK_IN_GROUND = Anims.HEAD_STUCK_IN_GROUND, + GROUND_POUND_LANDING = Anims.GROUND_POUND_LANDING, + TRIPLE_JUMP_GROUND_POUND = Anims.TRIPLE_JUMP_GROUND_POUND, + START_GROUND_POUND = Anims.START_GROUND_POUND, + GROUND_POUND = Anims.GROUND_POUND, + BOTTOM_STUCK_IN_GROUND = Anims.BOTTOM_STUCK_IN_GROUND, + IDLE_WITH_LIGHT_OBJ = Anims.IDLE_WITH_LIGHT_OBJ, + JUMP_LAND_WITH_LIGHT_OBJ = Anims.JUMP_LAND_WITH_LIGHT_OBJ, + JUMP_WITH_LIGHT_OBJ = Anims.JUMP_WITH_LIGHT_OBJ, + FALL_LAND_WITH_LIGHT_OBJ = Anims.FALL_LAND_WITH_LIGHT_OBJ, + FALL_WITH_LIGHT_OBJ = Anims.FALL_WITH_LIGHT_OBJ, + FALL_FROM_SLIDING_WITH_LIGHT_OBJ = Anims.FALL_FROM_SLIDING_WITH_LIGHT_OBJ, + SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ = Anims.SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ, + STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ = Anims.STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ, + RIDING_SHELL = Anims.RIDING_SHELL, + WALKING = Anims.WALKING, + FORWARD_FLIP = Anims.FORWARD_FLIP, -- unused + JUMP_RIDING_SHELL = Anims.JUMP_RIDING_SHELL, + LAND_FROM_DOUBLE_JUMP = Anims.LAND_FROM_DOUBLE_JUMP, + DOUBLE_JUMP_FALL = Anims.DOUBLE_JUMP_FALL, + SINGLE_JUMP = Anims.SINGLE_JUMP, + LAND_FROM_SINGLE_JUMP = Anims.LAND_FROM_SINGLE_JUMP, + AIR_KICK = Anims.AIR_KICK, + DOUBLE_JUMP_RISE = Anims.DOUBLE_JUMP_RISE, + START_FORWARD_SPINNING = Anims.START_FORWARD_SPINNING, -- unused + THROW_LIGHT_OBJECT = Anims.THROW_LIGHT_OBJECT, + FALL_FROM_SLIDE_KICK = Anims.FALL_FROM_SLIDE_KICK, + BEND_KNESS_RIDING_SHELL = Anims.BEND_KNESS_RIDING_SHELL, -- unused + LEGS_STUCK_IN_GROUND = Anims.LEGS_STUCK_IN_GROUND, + GENERAL_FALL = Anims.GENERAL_FALL, + GENERAL_LAND = Anims.GENERAL_LAND, + BEING_GRABBED = Anims.BEING_GRABBED, + GRAB_HEAVY_OBJECT = Anims.GRAB_HEAVY_OBJECT, + SLOW_LAND_FROM_DIVE = Anims.SLOW_LAND_FROM_DIVE, + FLY_FROM_CANNON = Anims.FLY_FROM_CANNON, + MOVE_ON_WIRE_NET_RIGHT = Anims.MOVE_ON_WIRE_NET_RIGHT, + MOVE_ON_WIRE_NET_LEFT = Anims.MOVE_ON_WIRE_NET_LEFT, + MISSING_CAP = Anims.MISSING_CAP, + PULL_DOOR_WALK_IN = Anims.PULL_DOOR_WALK_IN, + PUSH_DOOR_WALK_IN = Anims.PUSH_DOOR_WALK_IN, + UNLOCK_DOOR = Anims.UNLOCK_DOOR, + START_REACH_POCKET = Anims.START_REACH_POCKET, -- unused, reaching keys maybe? + REACH_POCKET = Anims.REACH_POCKET, -- unused + STOP_REACH_POCKET = Anims.STOP_REACH_POCKET, -- unused + GROUND_THROW = Anims.GROUND_THROW, + GROUND_KICK = Anims.GROUND_KICK, + FIRST_PUNCH = Anims.FIRST_PUNCH, + SECOND_PUNCH = Anims.SECOND_PUNCH, + FIRST_PUNCH_FAST = Anims.FIRST_PUNCH_FAST, + SECOND_PUNCH_FAST = Anims.SECOND_PUNCH_FAST, + PICK_UP_LIGHT_OBJ = Anims.PICK_UP_LIGHT_OBJ, + PUSHING = Anims.PUSHING, + START_RIDING_SHELL = Anims.START_RIDING_SHELL, + PLACE_LIGHT_OBJ = Anims.PLACE_LIGHT_OBJ, + FORWARD_SPINNING = Anims.FORWARD_SPINNING, + BACKWARD_SPINNING = Anims.BACKWARD_SPINNING, + BREAKDANCE = Anims.BREAKDANCE, + RUNNING = Anims.RUNNING, + RUNNING_UNUSED = Anims.RUNNING_UNUSED, -- unused duplicate, originally part 2? + SOFT_BACK_KB = Anims.SOFT_BACK_KB, + SOFT_FRONT_KB = Anims.SOFT_FRONT_KB, + DYING_IN_QUICKSAND = Anims.DYING_IN_QUICKSAND, + IDLE_IN_QUICKSAND = Anims.IDLE_IN_QUICKSAND, + MOVE_IN_QUICKSAND = Anims.MOVE_IN_QUICKSAND, + ELECTROCUTION = Anims.ELECTROCUTION, + SHOCKED = Anims.SHOCKED, + BACKWARD_KB = Anims.BACKWARD_KB, + FORWARD_KB = Anims.FORWARD_KB, + IDLE_HEAVY_OBJ = Anims.IDLE_HEAVY_OBJ, + -- STAND_AGAINST_WALL = Anims.STAND_AGAINST_WALL; + SIDESTEP_LEFT = Anims.SIDESTEP_LEFT, + SIDESTEP_RIGHT = Anims.SIDESTEP_RIGHT, + START_SLEEP_IDLE = Anims.START_SLEEP_IDLE, + START_SLEEP_SCRATCH = Anims.START_SLEEP_SCRATCH, + START_SLEEP_YAWN = Anims.START_SLEEP_YAWN, + START_SLEEP_SITTING = Anims.START_SLEEP_SITTING, + SLEEP_IDLE = Anims.SLEEP_IDLE, + SLEEP_START_LYING = Anims.SLEEP_START_LYING, + SLEEP_LYING = Anims.SLEEP_LYING, + DIVE = Anims.DIVE, + SLIDE_DIVE = Anims.SLIDE_DIVE, + GROUND_BONK = Anims.GROUND_BONK, + STOP_SLIDE_LIGHT_OBJ = Anims.STOP_SLIDE_LIGHT_OBJ, + SLIDE_KICK = Anims.SLIDE_KICK, + CROUCH_FROM_SLIDE_KICK = Anims.CROUCH_FROM_SLIDE_KICK, + SLIDE_MOTIONLESS = Anims.SLIDE_MOTIONLESS, -- unused + STOP_SLIDE = Anims.STOP_SLIDE, + FALL_FROM_SLIDE = Anims.FALL_FROM_SLIDE, + SLIDE = Anims.SLIDE, + TIPTOE = Anims.TIPTOE, + TWIRL_LAND = Anims.TWIRL_LAND, + TWIRL = Anims.TWIRL, + START_TWIRL = Anims.START_TWIRL, + STOP_CROUCHING = Anims.STOP_CROUCHING, + START_CROUCHING = Anims.START_CROUCHING, + CROUCHING = Anims.CROUCHING, + CRAWLING = Anims.CRAWLING, + STOP_CRAWLING = Anims.STOP_CRAWLING, + START_CRAWLING = Anims.START_CRAWLING, + SUMMON_STAR = Anims.SUMMON_STAR, + RETURN_STAR_APPROACH_DOOR = Anims.RETURN_STAR_APPROACH_DOOR, + BACKWARDS_WATER_KB = Anims.BACKWARDS_WATER_KB, + SWIM_WITH_OBJ_PART1 = Anims.SWIM_WITH_OBJ_PART1, + SWIM_WITH_OBJ_PART2 = Anims.SWIM_WITH_OBJ_PART2, + FLUTTERKICK_WITH_OBJ = Anims.FLUTTERKICK_WITH_OBJ, + WATER_ACTION_END_WITH_OBJ = Anims.WATER_ACTION_END_WITH_OBJ, -- either swimming or flutterkicking + STOP_GRAB_OBJ_WATER = Anims.STOP_GRAB_OBJ_WATER, + WATER_IDLE_WITH_OBJ = Anims.WATER_IDLE_WITH_OBJ, + DROWNING_PART1 = Anims.DROWNING_PART1, + DROWNING_PART2 = Anims.DROWNING_PART2, + WATER_DYING = Anims.WATER_DYING, + WATER_FORWARD_KB = Anims.WATER_FORWARD_KB, + FALL_FROM_WATER = Anims.FALL_FROM_WATER, + SWIM_PART1 = Anims.SWIM_PART1, + SWIM_PART2 = Anims.SWIM_PART2, + FLUTTERKICK = Anims.FLUTTERKICK, + WATER_ACTION_END = Anims.WATER_ACTION_END, -- either swimming or flutterkicking + WATER_PICK_UP_OBJ = Anims.WATER_PICK_UP_OBJ, + WATER_GRAB_OBJ_PART2 = Anims.WATER_GRAB_OBJ_PART2, + WATER_GRAB_OBJ_PART1 = Anims.WATER_GRAB_OBJ_PART1, + WATER_THROW_OBJ = Anims.WATER_THROW_OBJ, + WATER_IDLE = Anims.WATER_IDLE, + WATER_STAR_DANCE = Anims.WATER_STAR_DANCE, + RETURN_FROM_WATER_STAR_DANCE = Anims.RETURN_FROM_WATER_STAR_DANCE, + -- GRAB_BOWSER = Anims.GRAB_BOWSER; + -- SWINGING_BOWSER = Anims.SWINGING_BOWSER; + -- RELEASE_BOWSER = Anims.RELEASE_BOWSER; + -- HOLDING_BOWSER = Anims.HOLDING_BOWSER; + HEAVY_THROW = Anims.HEAVY_THROW, + WALK_PANTING = Anims.WALK_PANTING, + WALK_WITH_HEAVY_OBJ = Anims.WALK_WITH_HEAVY_OBJ, + TURNING_PART1 = Anims.TURNING_PART1, + TURNING_PART2 = Anims.TURNING_PART2, + SLIDEFLIP_LAND = Anims.SLIDEFLIP_LAND, + SLIDEFLIP = Anims.SLIDEFLIP, + TRIPLE_JUMP_LAND = Anims.TRIPLE_JUMP_LAND, + TRIPLE_JUMP = Anims.TRIPLE_JUMP, + FIRST_PERSON = Anims.FIRST_PERSON, + IDLE_HEAD_LEFT = Anims.IDLE_HEAD_LEFT, + IDLE_HEAD_RIGHT = Anims.IDLE_HEAD_RIGHT, + IDLE_HEAD_CENTER = Anims.IDLE_HEAD_CENTER, + HANDSTAND_LEFT = Anims.HANDSTAND_LEFT, + HANDSTAND_RIGHT = Anims.HANDSTAND_RIGHT, + WAKE_FROM_SLEEP = Anims.WAKE_FROM_SLEEP, + WAKE_FROM_LYING = Anims.WAKE_FROM_LYING, + START_TIPTOE = Anims.START_TIPTOE, + SLIDEJUMP = Anims.SLIDEJUMP, -- pole jump and wall kick + START_WALLKICK = Anims.START_WALLKICK, + STAR_DANCE = Anims.STAR_DANCE, + RETURN_FROM_STAR_DANCE = Anims.RETURN_FROM_STAR_DANCE, + FORWARD_SPINNING_FLIP = Anims.FORWARD_SPINNING_FLIP, + TRIPLE_JUMP_FLY = Anims.TRIPLE_JUMP_FLY, +}) + +task.spawn(function() + local ContentProvider = game:GetService("ContentProvider") + local preload = {} + + for name, anim in pairs(Data) do + table.insert(preload, anim) + end + + ContentProvider:PreloadAsync(preload) +end) + +return Data diff --git a/sm64/Assets/Animations.model.json b/sm64/Assets/Animations.model.json new file mode 100644 index 0000000..8cb4173 --- /dev/null +++ b/sm64/Assets/Animations.model.json @@ -0,0 +1,2639 @@ +{ + "className": "Folder", + + "children": [ + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "5722F4" + }, + "name": "IDLE_HEAD_LEFT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "546DE8" + }, + "name": "BACKWARD_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 153, + "Loop": false, + "UploadHash": "", + "StartAddress": "520594" + }, + "name": "BOTTOM_STUCK_IN_GROUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 111, + "Loop": true, + "UploadHash": "", + "StartAddress": "4F4A64" + }, + "name": "IDLE_ON_POLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 138, + "Loop": false, + "UploadHash": "", + "StartAddress": "512B4C" + }, + "name": "THROW_CATCH_KEY" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "51FB98" + }, + "name": "TRIPLE_JUMP_GROUND_POUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 147, + "Loop": false, + "UploadHash": "", + "StartAddress": "54320C" + }, + "name": "MOVE_IN_QUICKSAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 60, + "Loop": true, + "UploadHash": "", + "StartAddress": "52338C" + }, + "name": "IDLE_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "53C6B4" + }, + "name": "FIRST_PUNCH_FAST" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 33, + "Loop": false, + "UploadHash": "", + "StartAddress": "4EC690" + }, + "name": "SLOW_LEDGE_GRAB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F56EC" + }, + "name": "CROUCH_FROM_FAST_LONGJUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 26, + "Loop": true, + "UploadHash": "", + "StartAddress": "53D4BC" + }, + "name": "PUSHING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "535C9C" + }, + "name": "PUSH_DOOR_WALK_IN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F2520" + }, + "name": "GRAB_POLE_SWING_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "557AEC" + }, + "name": "START_CROUCHING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F6A78" + }, + "name": "SLOW_LONGJUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "575844" + }, + "name": "WAKE_FROM_LYING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 114, + "Loop": true, + "UploadHash": "", + "StartAddress": "55593C" + }, + "name": "TIPTOE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 76, + "Loop": true, + "UploadHash": "", + "StartAddress": "525D48" + }, + "name": "WALKING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 96, + "Loop": false, + "UploadHash": "", + "StartAddress": "56D244" + }, + "name": "WALK_WITH_HEAVY_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "5760EC" + }, + "name": "START_TIPTOE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F5C98" + }, + "name": "CROUCH_FROM_SLOW_LONGJUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 88, + "Loop": false, + "UploadHash": "", + "StartAddress": "511504" + }, + "name": "COUGHING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": false, + "UploadHash": "", + "StartAddress": "5627F4" + }, + "name": "WATER_DYING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FCDE8" + }, + "name": "CLIMB_DOWN_LEDGE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "51A754" + }, + "name": "TAKE_CAP_OFF_THEN_ON" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "55C9F0" + }, + "name": "BACKWARDS_WATER_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FD880" + }, + "name": "CREDITS_LOOK_UP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "554A94" + }, + "name": "SLIDE_MOTIONLESS" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 8, + "Loop": false, + "UploadHash": "", + "StartAddress": "52826C" + }, + "name": "DOUBLE_JUMP_FALL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "527BEC" + }, + "name": "LAND_FROM_DOUBLE_JUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "554540" + }, + "name": "CROUCH_FROM_SLIDE_KICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 79, + "Loop": false, + "UploadHash": "", + "StartAddress": "4ED1E8" + }, + "name": "BACKWARD_AIR_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": false, + "UploadHash": "", + "StartAddress": "50CBA8" + }, + "name": "HANG_ON_OWL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 119, + "Loop": false, + "UploadHash": "", + "StartAddress": "50353C" + }, + "name": "CREDITS_LOOK_BACK_THEN_RUN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": false, + "UploadHash": "", + "StartAddress": "55283C" + }, + "name": "GROUND_BONK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 94, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FE3F4" + }, + "name": "CREDITS_RAISE_HAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F440C" + }, + "name": "START_HANDSTAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 6, + "Loop": false, + "UploadHash": "", + "StartAddress": "55A5F8" + }, + "name": "STOP_CRAWLING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 33, + "Loop": false, + "UploadHash": "", + "StartAddress": "5000DC" + }, + "name": "CREDITS_LOWER_HAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 1, + "Loop": false, + "UploadHash": "", + "StartAddress": "524E10" + }, + "name": "FALL_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "565DD0" + }, + "name": "WATER_ACTION_END" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": true, + "UploadHash": "", + "StartAddress": "53E674" + }, + "name": "FORWARD_SPINNING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": true, + "UploadHash": "", + "StartAddress": "50C5B0" + }, + "name": "WING_CAP_FLY" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "52A460" + }, + "name": "FALL_FROM_SLIDE_KICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": false, + "UploadHash": "", + "StartAddress": "55AD1C" + }, + "name": "SUMMON_STAR" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "50F6F4" + }, + "name": "SUFFOCATING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "527248" + }, + "name": "FORWARD_FLIP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "529824" + }, + "name": "DOUBLE_JUMP_RISE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 79, + "Loop": false, + "UploadHash": "", + "StartAddress": "4ED1D0" + }, + "name": "FALL_OVER_BACKWARDS" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FDF90" + }, + "name": "CREDITS_RETURN_FROM_LOOK_UP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "53E804" + }, + "name": "BREAKDANCE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 75, + "Loop": true, + "UploadHash": "", + "StartAddress": "4F870C" + }, + "name": "RUN_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F43F4" + }, + "name": "HANDSTAND_JUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 36, + "Loop": false, + "UploadHash": "", + "StartAddress": "52FA0C" + }, + "name": "SLOW_LAND_FROM_DIVE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "54141C" + }, + "name": "DYING_IN_QUICKSAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 52, + "Loop": false, + "UploadHash": "", + "StartAddress": "545BF8" + }, + "name": "ELECTROCUTION" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "572BA0" + }, + "name": "IDLE_HEAD_RIGHT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "566628" + }, + "name": "WATER_PICK_UP_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "50BD4C" + }, + "name": "STAND_UP_FROM_LAVA_BOOST" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 51, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F2BF0" + }, + "name": "RETURN_FROM_HANDSTAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 32, + "Loop": false, + "UploadHash": "", + "StartAddress": "53A3E8" + }, + "name": "REACH_POCKET" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 58, + "Loop": false, + "UploadHash": "", + "StartAddress": "54E4F4" + }, + "name": "START_SLEEP_YAWN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "574490" + }, + "name": "HANDSTAND_RIGHT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "546B40" + }, + "name": "SHOCKED" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "54035C" + }, + "name": "SOFT_BACK_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 33, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FD208" + }, + "name": "CREDITS_WAVING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 150, + "Loop": false, + "UploadHash": "", + "StartAddress": "52B360" + }, + "name": "LEGS_STUCK_IN_GROUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 41, + "Loop": false, + "UploadHash": "", + "StartAddress": "578C80" + }, + "name": "FORWARD_SPINNING_FLIP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "540BA4" + }, + "name": "SOFT_FRONT_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "5785B8" + }, + "name": "RETURN_FROM_STAR_DANCE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 45, + "Loop": false, + "UploadHash": "", + "StartAddress": "50EA0C" + }, + "name": "DYING_ON_STOMACH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 61, + "Loop": false, + "UploadHash": "", + "StartAddress": "577064" + }, + "name": "STAR_DANCE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": true, + "UploadHash": "", + "StartAddress": "50C254" + }, + "name": "FIRE_LAVA_BURN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "525318" + }, + "name": "SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 85, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FA618" + }, + "name": "SHIVERING_WARMING_HAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 45, + "Loop": false, + "UploadHash": "", + "StartAddress": "5197CC" + }, + "name": "PUT_CAP_ON" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "524614" + }, + "name": "JUMP_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 91, + "Loop": true, + "UploadHash": "", + "StartAddress": "55897C" + }, + "name": "CRAWLING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 69, + "Loop": false, + "UploadHash": "", + "StartAddress": "5614B8" + }, + "name": "DROWNING_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "56CD54" + }, + "name": "WALK_PANTING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "5769D0" + }, + "name": "SLIDEJUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 36, + "Loop": false, + "UploadHash": "", + "StartAddress": "5534F4" + }, + "name": "STOP_SLIDE_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "55DAAC" + }, + "name": "SWIM_WITH_OBJ_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "573CF8" + }, + "name": "HANDSTAND_LEFT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 70, + "Loop": true, + "UploadHash": "", + "StartAddress": "53F138" + }, + "name": "RUNNING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "57344C" + }, + "name": "IDLE_HEAD_CENTER" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "55ECCC" + }, + "name": "STOP_GRAB_OBJ_WATER" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 35, + "Loop": false, + "UploadHash": "", + "StartAddress": "5701F8" + }, + "name": "TRIPLE_JUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 88, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F08C0" + }, + "name": "CLIMB_UP_POLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "55E164" + }, + "name": "FLUTTERKICK_WITH_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 30, + "Loop": false, + "UploadHash": "", + "StartAddress": "56F750" + }, + "name": "TRIPLE_JUMP_LAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "Loop": false, + "UploadHash": "", + "Flip": true, + "NumFrames": 22, + "StartAddress": "56EEAC" + }, + "name": "SLIDEFLIP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "5634FC" + }, + "name": "WATER_FORWARD_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 70, + "Loop": true, + "UploadHash": "", + "StartAddress": "53F150" + }, + "name": "RUNNING_UNUSED" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "Loop": false, + "UploadHash": "", + "Flip": true, + "NumFrames": 9, + "StartAddress": "56EAA0" + }, + "name": "SLIDEFLIP_LAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "52AD28" + }, + "name": "BEND_KNESS_RIDING_SHELL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 36, + "Loop": false, + "UploadHash": "", + "StartAddress": "528620" + }, + "name": "SINGLE_JUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "53E0F0" + }, + "name": "PLACE_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 79, + "Loop": false, + "UploadHash": "", + "StartAddress": "55FF88" + }, + "name": "DROWNING_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "UploadHash": "", + "StartFrame": 0, + "Loop": false, + "NumFrames": 22, + "StartAddress": "52E090" + }, + "name": "GENERAL_LAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 18, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F4FE0" + }, + "name": "SKID_ON_GROUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 146, + "Loop": false, + "UploadHash": "", + "StartAddress": "536D64" + }, + "name": "UNLOCK_DOOR" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F7494" + }, + "name": "WALK_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "56E37C" + }, + "name": "TURNING_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 36, + "Loop": false, + "UploadHash": "", + "StartAddress": "52ED0C" + }, + "name": "GRAB_HEAVY_OBJECT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 11, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F2078" + }, + "name": "GRAB_POLE_SHORT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "56C42C" + }, + "name": "HEAVY_THROW" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 111, + "Loop": false, + "UploadHash": "", + "StartAddress": "509924" + }, + "name": "CREDITS_PEACE_SIGN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "52E7BC" + }, + "name": "BEING_GRABBED" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "53DAD4" + }, + "name": "START_RIDING_SHELL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 52, + "Loop": false, + "UploadHash": "", + "StartAddress": "518840" + }, + "name": "HANG_ON_CEILING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "51F90C" + }, + "name": "GROUND_POUND_LANDING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "55E610" + }, + "name": "WATER_ACTION_END_WITH_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "557730" + }, + "name": "STOP_CROUCHING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "56A478" + }, + "name": "RETURN_FROM_WATER_STAR_DANCE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 82, + "Loop": false, + "UploadHash": "", + "StartAddress": "568A04" + }, + "name": "WATER_STAR_DANCE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "5681D0" + }, + "name": "WATER_IDLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "539FEC" + }, + "name": "START_REACH_POCKET" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 6, + "Loop": false, + "UploadHash": "", + "StartAddress": "5541A4" + }, + "name": "SLIDE_KICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 6, + "Loop": false, + "UploadHash": "", + "StartAddress": "55571C" + }, + "name": "SLIDE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F2508" + }, + "name": "GRAB_POLE_SWING_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "53CFFC" + }, + "name": "PICK_UP_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "529FDC" + }, + "name": "THROW_LIGHT_OBJECT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F93A0" + }, + "name": "SLOW_WALK_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "56747C" + }, + "name": "WATER_GRAB_OBJ_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FC1A8" + }, + "name": "SHIVERING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "566AF8" + }, + "name": "WATER_GRAB_OBJ_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "UploadHash": "", + "StartFrame": 9, + "Loop": false, + "NumFrames": 9, + "StartAddress": "520178" + }, + "name": "GROUND_POUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "565610" + }, + "name": "FLUTTERKICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 1, + "Loop": false, + "UploadHash": "", + "StartAddress": "5573A0" + }, + "name": "TWIRL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 58, + "Loop": false, + "UploadHash": "", + "StartAddress": "5347C4" + }, + "name": "PULL_DOOR_WALK_IN" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 35, + "Loop": true, + "UploadHash": "", + "StartAddress": "4F38F0" + }, + "name": "HANDSTAND_IDLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "56520C" + }, + "name": "SWIM_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 12, + "Loop": false, + "UploadHash": "", + "StartAddress": "564D2C" + }, + "name": "SWIM_PART1" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": false, + "UploadHash": "", + "StartAddress": "501410" + }, + "name": "CREDITS_START_WALK_LOOK_UP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "564558" + }, + "name": "FALL_FROM_WATER" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 88, + "Loop": false, + "UploadHash": "", + "StartAddress": "570CB0" + }, + "name": "FIRST_PERSON" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 26, + "Loop": false, + "UploadHash": "", + "StartAddress": "500C24" + }, + "name": "CREDITS_TAKE_OFF_CAP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": false, + "UploadHash": "", + "StartAddress": "4EECAC" + }, + "name": "DYING_ON_BACK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F62D4" + }, + "name": "FAST_LONGJUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "55F800" + }, + "name": "WATER_IDLE_WITH_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "4F6FDC" + }, + "name": "AIRBORNE_ON_STOMACH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "518218" + }, + "name": "FAST_LEDGE_GRAB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "55DEC8" + }, + "name": "SWIM_WITH_OBJ_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "55C144" + }, + "name": "RETURN_STAR_APPROACH_DOOR" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 6, + "Loop": false, + "UploadHash": "", + "StartAddress": "55A990" + }, + "name": "START_CRAWLING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 88, + "Loop": false, + "UploadHash": "", + "StartAddress": "557DA0" + }, + "name": "CROUCHING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 11, + "Loop": false, + "UploadHash": "", + "StartAddress": "51C314" + }, + "name": "QUICKLY_PUT_CAP_ON" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": false, + "UploadHash": "", + "StartAddress": "548244" + }, + "name": "IDLE_HEAVY_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "555214" + }, + "name": "FALL_FROM_SLIDE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": false, + "UploadHash": "", + "StartAddress": "54BB6C" + }, + "name": "START_SLEEP_IDLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "552224" + }, + "name": "DIVE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "554AAC" + }, + "name": "STOP_SLIDE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 27, + "Loop": false, + "UploadHash": "", + "StartAddress": "567748" + }, + "name": "WATER_THROW_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "UploadHash": "", + "StartFrame": 27, + "Loop": false, + "NumFrames": 36, + "StartAddress": "528638" + }, + "name": "LAND_FROM_SINGLE_JUMP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "5750C0" + }, + "name": "WAKE_FROM_SLEEP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": true, + "UploadHash": "", + "StartAddress": "549A84" + }, + "name": "SIDESTEP_LEFT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "53AB58" + }, + "name": "STOP_REACH_POCKET" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "UploadHash": "", + "StartFrame": 6, + "Loop": false, + "NumFrames": 18, + "StartAddress": "4F4FF8" + }, + "name": "STOP_SKID" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "557030" + }, + "name": "TWIRL_LAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": true, + "UploadHash": "", + "StartAddress": "551AF4" + }, + "name": "SLEEP_LYING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": false, + "UploadHash": "", + "StartAddress": "550E88" + }, + "name": "SLEEP_START_LYING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 49, + "Loop": true, + "UploadHash": "", + "StartAddress": "5175EC" + }, + "name": "IDLE_ON_LEDGE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": false, + "UploadHash": "", + "StartAddress": "54F888" + }, + "name": "START_SLEEP_SITTING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 32, + "Loop": false, + "UploadHash": "", + "StartAddress": "4EFED4" + }, + "name": "BACKFLIP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 66, + "Loop": false, + "UploadHash": "", + "StartAddress": "54CD54" + }, + "name": "START_SLEEP_SCRATCH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 63, + "Loop": true, + "UploadHash": "", + "StartAddress": "54A9C0" + }, + "name": "SIDESTEP_RIGHT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 6, + "Loop": false, + "UploadHash": "", + "StartAddress": "527870" + }, + "name": "JUMP_RIDING_SHELL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "55223C" + }, + "name": "SLIDE_DIVE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "557504" + }, + "name": "START_TWIRL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 29, + "Loop": false, + "UploadHash": "", + "StartAddress": "547834" + }, + "name": "FORWARD_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 99, + "Loop": true, + "UploadHash": "", + "StartAddress": "542758" + }, + "name": "IDLE_IN_QUICKSAND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": true, + "UploadHash": "", + "StartAddress": "53E68C" + }, + "name": "BACKWARD_SPINNING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "UploadHash": "", + "StartFrame": 6, + "Loop": false, + "NumFrames": 16, + "StartAddress": "56E394" + }, + "name": "TURNING_PART2" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 12, + "Loop": false, + "UploadHash": "", + "StartAddress": "53CAC4" + }, + "name": "SECOND_PUNCH_FAST" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 3, + "Loop": false, + "UploadHash": "", + "StartAddress": "53C44C" + }, + "name": "SECOND_PUNCH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 4, + "Loop": false, + "UploadHash": "", + "StartAddress": "53C1B4" + }, + "name": "FIRST_PUNCH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "53B14C" + }, + "name": "GROUND_THROW" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "4FBC18" + }, + "name": "SHIVERING_RETURN_TO_IDLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 24, + "Loop": false, + "UploadHash": "", + "StartAddress": "53B904" + }, + "name": "GROUND_KICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 79, + "Loop": false, + "UploadHash": "", + "StartAddress": "50D2EC" + }, + "name": "LAND_ON_STOMACH" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 153, + "Loop": false, + "UploadHash": "", + "StartAddress": "531760" + }, + "name": "MISSING_CAP" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 18, + "Loop": false, + "UploadHash": "", + "StartAddress": "5311D4" + }, + "name": "MOVE_ON_WIRE_NET_LEFT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "524F78" + }, + "name": "FALL_FROM_SLIDING_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 1, + "Loop": true, + "UploadHash": "", + "StartAddress": "4F4E7C" + }, + "name": "A_POSE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "530BD4" + }, + "name": "MOVE_ON_WIRE_NET_RIGHT" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 18, + "Loop": false, + "UploadHash": "", + "StartAddress": "5240B8" + }, + "name": "JUMP_LAND_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 22, + "Loop": true, + "UploadHash": "", + "StartAddress": "52E078" + }, + "name": "GENERAL_FALL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "579828" + }, + "name": "TRIPLE_JUMP_FLY" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "5307F0" + }, + "name": "FLY_FROM_CANNON" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 19, + "Loop": false, + "UploadHash": "", + "StartAddress": "5290E4" + }, + "name": "AIR_KICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "5258EC" + }, + "name": "RIDING_SHELL" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "525330" + }, + "name": "STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 15, + "Loop": false, + "UploadHash": "", + "StartAddress": "524940" + }, + "name": "FALL_LAND_WITH_LIGHT_OBJ" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 13, + "Loop": false, + "UploadHash": "", + "StartAddress": "529ADC" + }, + "name": "START_FORWARD_SPINNING" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 9, + "Loop": false, + "UploadHash": "", + "StartAddress": "520160" + }, + "name": "START_GROUND_POUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 153, + "Loop": false, + "UploadHash": "", + "StartAddress": "51C774" + }, + "name": "HEAD_STUCK_IN_GROUND" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 16, + "Loop": false, + "UploadHash": "", + "StartAddress": "5769E8" + }, + "name": "START_WALLKICK" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 39, + "Loop": true, + "UploadHash": "", + "StartAddress": "550C30" + }, + "name": "SLEEP_IDLE" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 79, + "Loop": false, + "UploadHash": "", + "StartAddress": "50D304" + }, + "name": "FORWARD_AIR_KB" + }, + { + "className": "Animation", + "properties": { + "AnimationId": "" + }, + "attributes": { + "NumFrames": 94, + "Loop": false, + "UploadHash": "", + "StartAddress": "515604" + }, + "name": "DYING_FALL_OVER" + } + ] +} \ No newline at end of file diff --git a/sm64/Assets/Sounds.model.json b/sm64/Assets/Sounds.model.json new file mode 100644 index 0000000..fd0b2ef --- /dev/null +++ b/sm64/Assets/Sounds.model.json @@ -0,0 +1,502 @@ +{ + "className": "Folder", + + "children": [ + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_BONK" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_FLYING_FAST" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_HEAVY_LANDING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_HIT" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_METAL_BONK" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_METAL_HEAVY_LANDING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_METAL_LANDING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_METAL_STEP" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_PAT_BACK" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_SIDE_FLIP" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_SPIN" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_BODY_HIT_GROUND" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_DEFAULT" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_GRASS" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_ICE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_METAL" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_SAND" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_SNOW" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_SPOOKY" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_STONE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_LANDING_WATER" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_DEFAULT" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_GRASS" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_ICE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_METAL" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_SAND" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_SNOW" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_SPOOKY" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_STONE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TERRAIN_STEP_WATER" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_THROW" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "ACTION_TWIRL" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_ATTACKED" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_DIE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_DOH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_GROUND_POUND_WAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_HAHA" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_HOO" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_HOOHOO" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_IMA_TIRED" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_JUMP_HOO" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_JUMP_WAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_MAMA_MIA" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_ON_FIRE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_OOOF" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_PANTING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_PUNCH_HOO" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_PUNCH_WAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_PUNCH_YAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_SNORING1" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_SNORING2" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_SNORING3" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_THROW_YAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_UH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_UH2" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_WAAAOOOW" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_WAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_WAHA" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_WHOA" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_YAH" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_YAHOO" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_YAWNING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MARIO_YIPPEE" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MOVING_FLYING" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MOVING_LAVA_BURN" + }, + { + "className": "Sound", + "properties": { + "SoundId": "" + }, + "name": "MOVING_TERRAIN_SLIDE" + }, + { + "className": "Configuration", + "name": "ACTION_TERRAIN_JUMP" + }, + { + "className": "Configuration", + "name": "ACTION_TERRAIN_LANDING" + }, + { + "className": "Configuration", + "name": "ACTION_TERRAIN_STEP" + }, + { + "className": "Configuration", + "name": "MARIO_JUMP" + }, + { + "className": "Configuration", + "attributes": { + "MARIO_WAHA": 1, + "MARIO_YAHOO": 3, + "MARIO_YIPPEE": 1 + }, + "name": "MARIO_YAHOO_WAHA_YIPPEE" + }, + { + "className": "Configuration", + "attributes": { + "MARIO_YAH": 1, + "MARIO_HOO": 1, + "MARIO_WAH": 1 + }, + "name": "MARIO_YAH_WAH_HOO" + } + ] +} \ No newline at end of file diff --git a/sm64/Enums/Action/Flags.lua b/sm64/Enums/Action/Flags.lua new file mode 100644 index 0000000..b5de931 --- /dev/null +++ b/sm64/Enums/Action/Flags.lua @@ -0,0 +1,26 @@ +--!strict + +return { + STATIONARY = bit32.lshift(1, 9), + MOVING = bit32.lshift(1, 10), + AIR = bit32.lshift(1, 11), + INTANGIBLE = bit32.lshift(1, 12), + SWIMMING = bit32.lshift(1, 13), + METAL_WATER = bit32.lshift(1, 14), + SHORT_HITBOX = bit32.lshift(1, 15), + RIDING_SHELL = bit32.lshift(1, 16), + INVULNERABLE = bit32.lshift(1, 17), + BUTT_OR_STOMACH_SLIDE = bit32.lshift(1, 18), + DIVING = bit32.lshift(1, 19), + ON_POLE = bit32.lshift(1, 20), + HANGING = bit32.lshift(1, 21), + IDLE = bit32.lshift(1, 22), + ATTACKING = bit32.lshift(1, 23), + ALLOW_VERTICAL_WIND_ACTION = bit32.lshift(1, 24), + CONTROL_JUMP_HEIGHT = bit32.lshift(1, 25), + ALLOW_FIRST_PERSON = bit32.lshift(1, 26), + PAUSE_EXIT = bit32.lshift(1, 27), + SWIMMING_OR_FLYING = bit32.lshift(1, 28), + WATER_OR_TEXT = bit32.lshift(1, 29), + THROWING = bit32.lshift(1, 31), +} diff --git a/sm64/Enums/Action/Groups.lua b/sm64/Enums/Action/Groups.lua new file mode 100644 index 0000000..16cfdbc --- /dev/null +++ b/sm64/Enums/Action/Groups.lua @@ -0,0 +1,15 @@ +--!strict + +return { + -- Group Flags + STATIONARY = bit32.lshift(0, 6), + MOVING = bit32.lshift(1, 6), + AIRBORNE = bit32.lshift(2, 6), + SUBMERGED = bit32.lshift(3, 6), + CUTSCENE = bit32.lshift(4, 6), + AUTOMATIC = bit32.lshift(5, 6), + OBJECT = bit32.lshift(6, 6), + + -- Mask for capturing these Flags + GROUP_MASK = 0b_000000111000000, +} diff --git a/sm64/Enums/Action/init.lua b/sm64/Enums/Action/init.lua new file mode 100644 index 0000000..ad89a28 --- /dev/null +++ b/sm64/Enums/Action/init.lua @@ -0,0 +1,250 @@ +--!strict + +return { + UNINITIALIZED = 0x00000000, -- (0x000) + + -- group 0x000: stationary actions + IDLE = 0x0C400201, -- (0x001 | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + START_SLEEPING = 0x0C400202, -- (0x002 | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + SLEEPING = 0x0C000203, -- (0x003 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + WAKING_UP = 0x0C000204, -- (0x004 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + PANTING = 0x0C400205, -- (0x005 | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + HOLD_PANTING_UNUSED = 0x08000206, -- (0x006 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + HOLD_IDLE = 0x08000207, -- (0x007 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + HOLD_HEAVY_IDLE = 0x08000208, -- (0x008 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + STANDING_AGAINST_WALL = 0x0C400209, -- (0x009 | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + COUGHING = 0x0C40020A, -- (0x00A | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + SHIVERING = 0x0C40020B, -- (0x00B | FLAG_STATIONARY | FLAG_IDLE | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + IN_QUICKSAND = 0x0002020D, -- (0x00D | FLAG_STATIONARY | FLAG_INVULNERABLE) + UNKNOWN_0002020E = 0x0002020E, -- (0x00E | FLAG_STATIONARY | FLAG_INVULNERABLE) + CROUCHING = 0x0C008220, -- (0x020 | FLAG_STATIONARY | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + START_CROUCHING = 0x0C008221, -- (0x021 | FLAG_STATIONARY | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + STOP_CROUCHING = 0x0C008222, -- (0x022 | FLAG_STATIONARY | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + START_CRAWLING = 0x0C008223, -- (0x023 | FLAG_STATIONARY | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + STOP_CRAWLING = 0x0C008224, -- (0x024 | FLAG_STATIONARY | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + SLIDE_KICK_SLIDE_STOP = 0x08000225, -- (0x025 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + SHOCKWAVE_BOUNCE = 0x00020226, -- (0x026 | FLAG_STATIONARY | FLAG_INVULNERABLE) + FIRST_PERSON = 0x0C000227, -- (0x027 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + BACKFLIP_LAND_STOP = 0x0800022F, -- (0x02F | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + JUMP_LAND_STOP = 0x0C000230, -- (0x030 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + DOUBLE_JUMP_LAND_STOP = 0x0C000231, -- (0x031 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + FREEFALL_LAND_STOP = 0x0C000232, -- (0x032 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + SIDE_FLIP_LAND_STOP = 0x0C000233, -- (0x033 | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + HOLD_JUMP_LAND_STOP = 0x08000234, -- (0x034 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + HOLD_FREEFALL_LAND_STOP = 0x08000235, -- (0x035 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + AIR_THROW_LAND = 0x80000A36, -- (0x036 | FLAG_STATIONARY | FLAG_AIR | FLAG_THROWING) + TWIRL_LAND = 0x18800238, -- (0x038 | FLAG_STATIONARY | FLAG_ATTACKING | FLAG_PAUSE_EXIT | FLAG_SWIMMING_OR_FLYING) + LAVA_BOOST_LAND = 0x08000239, -- (0x039 | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + TRIPLE_JUMP_LAND_STOP = 0x0800023A, -- (0x03A | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + LONG_JUMP_LAND_STOP = 0x0800023B, -- (0x03B | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + GROUND_POUND_LAND = 0x0080023C, -- (0x03C | FLAG_STATIONARY | FLAG_ATTACKING) + BRAKING_STOP = 0x0C00023D, -- (0x03D | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + BUTT_SLIDE_STOP = 0x0C00023E, -- (0x03E | FLAG_STATIONARY | FLAG_ALLOW_FIRST_PERSON | FLAG_PAUSE_EXIT) + HOLD_BUTT_SLIDE_STOP = 0x0800043F, -- (0x03F | FLAG_MOVING | FLAG_PAUSE_EXIT) + + -- group 0x040: moving (ground) actions + WALKING = 0x04000440, -- (0x040 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + HOLD_WALKING = 0x00000442, -- (0x042 | FLAG_MOVING) + TURNING_AROUND = 0x00000443, -- (0x043 | FLAG_MOVING) + FINISH_TURNING_AROUND = 0x00000444, -- (0x044 | FLAG_MOVING) + BRAKING = 0x04000445, -- (0x045 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + RIDING_SHELL_GROUND = 0x20810446, -- (0x046 | FLAG_MOVING | FLAG_RIDING_SHELL | FLAG_ATTACKING | FLAG_WATER_OR_TEXT) + HOLD_HEAVY_WALKING = 0x00000447, -- (0x047 | FLAG_MOVING) + CRAWLING = 0x04008448, -- (0x048 | FLAG_MOVING | FLAG_SHORT_HITBOX | FLAG_ALLOW_FIRST_PERSON) + BURNING_GROUND = 0x00020449, -- (0x049 | FLAG_MOVING | FLAG_INVULNERABLE) + DECELERATING = 0x0400044A, -- (0x04A | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + HOLD_DECELERATING = 0x0000044B, -- (0x04B | FLAG_MOVING) + BEGIN_SLIDING = 0x00000050, -- (0x050) + HOLD_BEGIN_SLIDING = 0x00000051, -- (0x051) + BUTT_SLIDE = 0x00840452, -- (0x052 | FLAG_MOVING | FLAG_BUTT_OR_STOMACH_SLIDE | FLAG_ATTACKING) + STOMACH_SLIDE = 0x008C0453, -- (0x053 | FLAG_MOVING | FLAG_BUTT_OR_STOMACH_SLIDE | FLAG_DIVING | FLAG_ATTACKING) + HOLD_BUTT_SLIDE = 0x00840454, -- (0x054 | FLAG_MOVING | FLAG_BUTT_OR_STOMACH_SLIDE | FLAG_ATTACKING) + HOLD_STOMACH_SLIDE = 0x008C0455, -- (0x055 | FLAG_MOVING | FLAG_BUTT_OR_STOMACH_SLIDE | FLAG_DIVING | FLAG_ATTACKING) + DIVE_SLIDE = 0x00880456, -- (0x056 | FLAG_MOVING | FLAG_DIVING | FLAG_ATTACKING) + MOVE_PUNCHING = 0x00800457, -- (0x057 | FLAG_MOVING | FLAG_ATTACKING) + CROUCH_SLIDE = 0x04808459, -- (0x059 | FLAG_MOVING | FLAG_SHORT_HITBOX | FLAG_ATTACKING | FLAG_ALLOW_FIRST_PERSON) + SLIDE_KICK_SLIDE = 0x0080045A, -- (0x05A | FLAG_MOVING | FLAG_ATTACKING) + HARD_BACKWARD_GROUND_KB = 0x00020460, -- (0x060 | FLAG_MOVING | FLAG_INVULNERABLE) + HARD_FORWARD_GROUND_KB = 0x00020461, -- (0x061 | FLAG_MOVING | FLAG_INVULNERABLE) + BACKWARD_GROUND_KB = 0x00020462, -- (0x062 | FLAG_MOVING | FLAG_INVULNERABLE) + FORWARD_GROUND_KB = 0x00020463, -- (0x063 | FLAG_MOVING | FLAG_INVULNERABLE) + SOFT_BACKWARD_GROUND_KB = 0x00020464, -- (0x064 | FLAG_MOVING | FLAG_INVULNERABLE) + SOFT_FORWARD_GROUND_KB = 0x00020465, -- (0x065 | FLAG_MOVING | FLAG_INVULNERABLE) + GROUND_BONK = 0x00020466, -- (0x066 | FLAG_MOVING | FLAG_INVULNERABLE) + DEATH_EXIT_LAND = 0x00020467, -- (0x067 | FLAG_MOVING | FLAG_INVULNERABLE) + JUMP_LAND = 0x04000470, -- (0x070 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + FREEFALL_LAND = 0x04000471, -- (0x071 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + DOUBLE_JUMP_LAND = 0x04000472, -- (0x072 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + SIDE_FLIP_LAND = 0x04000473, -- (0x073 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + HOLD_JUMP_LAND = 0x00000474, -- (0x074 | FLAG_MOVING) + HOLD_FREEFALL_LAND = 0x00000475, -- (0x075 | FLAG_MOVING) + QUICKSAND_JUMP_LAND = 0x00000476, -- (0x076 | FLAG_MOVING) + HOLD_QUICKSAND_JUMP_LAND = 0x00000477, -- (0x077 | FLAG_MOVING) + TRIPLE_JUMP_LAND = 0x04000478, -- (0x078 | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + LONG_JUMP_LAND = 0x00000479, -- (0x079 | FLAG_MOVING) + BACKFLIP_LAND = 0x0400047A, -- (0x07A | FLAG_MOVING | FLAG_ALLOW_FIRST_PERSON) + + -- group 0x080: airborne actions + JUMP = 0x03000880, -- (0x080 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + DOUBLE_JUMP = 0x03000881, -- (0x081 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + TRIPLE_JUMP = 0x01000882, -- (0x082 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + BACKFLIP = 0x01000883, -- (0x083 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + STEEP_JUMP = 0x03000885, -- (0x085 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + WALL_KICK_AIR = 0x03000886, -- (0x086 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + SIDE_FLIP = 0x01000887, -- (0x087 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + LONG_JUMP = 0x03000888, -- (0x088 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + WATER_JUMP = 0x01000889, -- (0x089 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + DIVE = 0x0188088A, -- (0x08A | FLAG_AIR | FLAG_DIVING | FLAG_ATTACKING | FLAG_ALLOW_VERTICAL_WIND_ACTION) + FREEFALL = 0x0100088C, -- (0x08C | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + TOP_OF_POLE_JUMP = 0x0300088D, -- (0x08D | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + BUTT_SLIDE_AIR = 0x0300088E, -- (0x08E | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + FLYING_TRIPLE_JUMP = 0x03000894, -- (0x094 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + SHOT_FROM_CANNON = 0x00880898, -- (0x098 | FLAG_AIR | FLAG_DIVING | FLAG_ATTACKING) + FLYING = 0x10880899, -- (0x099 | FLAG_AIR | FLAG_DIVING | FLAG_ATTACKING | FLAG_SWIMMING_OR_FLYING) + RIDING_SHELL_JUMP = 0x0281089A, -- (0x09A | FLAG_AIR | FLAG_RIDING_SHELL | FLAG_ATTACKING | FLAG_CONTROL_JUMP_HEIGHT) + RIDING_SHELL_FALL = 0x0081089B, -- (0x09B | FLAG_AIR | FLAG_RIDING_SHELL | FLAG_ATTACKING) + VERTICAL_WIND = 0x1008089C, -- (0x09C | FLAG_AIR | FLAG_DIVING | FLAG_SWIMMING_OR_FLYING) + HOLD_JUMP = 0x030008A0, -- (0x0A0 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + HOLD_FREEFALL = 0x010008A1, -- (0x0A1 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + HOLD_BUTT_SLIDE_AIR = 0x010008A2, -- (0x0A2 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + HOLD_WATER_JUMP = 0x010008A3, -- (0x0A3 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + TWIRLING = 0x108008A4, -- (0x0A4 | FLAG_AIR | FLAG_ATTACKING | FLAG_SWIMMING_OR_FLYING) + FORWARD_ROLLOUT = 0x010008A6, -- (0x0A6 | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + AIR_HIT_WALL = 0x000008A7, -- (0x0A7 | FLAG_AIR) + RIDING_HOOT = 0x000004A8, -- (0x0A8 | FLAG_MOVING) + GROUND_POUND = 0x008008A9, -- (0x0A9 | FLAG_AIR | FLAG_ATTACKING) + SLIDE_KICK = 0x018008AA, -- (0x0AA | FLAG_AIR | FLAG_ATTACKING | FLAG_ALLOW_VERTICAL_WIND_ACTION) + AIR_THROW = 0x830008AB, -- (0x0AB | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT | FLAG_THROWING) + JUMP_KICK = 0x018008AC, -- (0x0AC | FLAG_AIR | FLAG_ATTACKING | FLAG_ALLOW_VERTICAL_WIND_ACTION) + BACKWARD_ROLLOUT = 0x010008AD, -- (0x0AD | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION) + CRAZY_BOX_BOUNCE = 0x000008AE, -- (0x0AE | FLAG_AIR) + SPECIAL_TRIPLE_JUMP = 0x030008AF, -- (0x0AF | FLAG_AIR | FLAG_ALLOW_VERTICAL_WIND_ACTION | FLAG_CONTROL_JUMP_HEIGHT) + BACKWARD_AIR_KB = 0x010208B0, -- (0x0B0 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + FORWARD_AIR_KB = 0x010208B1, -- (0x0B1 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + HARD_FORWARD_AIR_KB = 0x010208B2, -- (0x0B2 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + HARD_BACKWARD_AIR_KB = 0x010208B3, -- (0x0B3 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + BURNING_JUMP = 0x010208B4, -- (0x0B4 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + BURNING_FALL = 0x010208B5, -- (0x0B5 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + SOFT_BONK = 0x010208B6, -- (0x0B6 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + LAVA_BOOST = 0x010208B7, -- (0x0B7 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + GETTING_BLOWN = 0x010208B8, -- (0x0B8 | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + THROWN_FORWARD = 0x010208BD, -- (0x0BD | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + THROWN_BACKWARD = 0x010208BE, -- (0x0BE | FLAG_AIR | FLAG_INVULNERABLE | FLAG_ALLOW_VERTICAL_WIND_ACTION) + + -- group 0x0C0: submerged actions + WATER_IDLE = 0x380022C0, -- (0x0C0 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_PAUSE_EXIT | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + HOLD_WATER_IDLE = 0x380022C1, -- (0x0C1 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_PAUSE_EXIT | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_ACTION_END = 0x300022C2, -- (0x0C2 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + HOLD_WATER_ACTION_END = 0x300022C3, -- (0x0C3 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + DROWNING = 0x300032C4, -- (0x0C4 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + BACKWARD_WATER_KB = 0x300222C5, -- (0x0C5 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_INVULNERABLE | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + FORWARD_WATER_KB = 0x300222C6, -- (0x0C6 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_INVULNERABLE | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_DEATH = 0x300032C7, -- (0x0C7 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_SHOCKED = 0x300222C8, -- (0x0C8 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_INVULNERABLE | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + BREASTSTROKE = 0x300024D0, -- (0x0D0 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + SWIMMING_END = 0x300024D1, -- (0x0D1 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + FLUTTER_KICK = 0x300024D2, -- (0x0D2 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + HOLD_BREASTSTROKE = 0x300024D3, -- (0x0D3 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + HOLD_SWIMMING_END = 0x300024D4, -- (0x0D4 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + HOLD_FLUTTER_KICK = 0x300024D5, -- (0x0D5 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_SHELL_SWIMMING = 0x300024D6, -- (0x0D6 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_THROW = 0x300024E0, -- (0x0E0 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_PUNCH = 0x300024E1, -- (0x0E1 | FLAG_MOVING | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + WATER_PLUNGE = 0x300022E2, -- (0x0E2 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + CAUGHT_IN_WHIRLPOOL = 0x300222E3, -- (0x0E3 | FLAG_STATIONARY | FLAG_SWIMMING | FLAG_INVULNERABLE | FLAG_SWIMMING_OR_FLYING | FLAG_WATER_OR_TEXT) + METAL_WATER_STANDING = 0x080042F0, -- (0x0F0 | FLAG_STATIONARY | FLAG_METAL_WATER | FLAG_PAUSE_EXIT) + HOLD_METAL_WATER_STANDING = 0x080042F1, -- (0x0F1 | FLAG_STATIONARY | FLAG_METAL_WATER | FLAG_PAUSE_EXIT) + METAL_WATER_WALKING = 0x000044F2, -- (0x0F2 | FLAG_MOVING | FLAG_METAL_WATER) + HOLD_METAL_WATER_WALKING = 0x000044F3, -- (0x0F3 | FLAG_MOVING | FLAG_METAL_WATER) + METAL_WATER_FALLING = 0x000042F4, -- (0x0F4 | FLAG_STATIONARY | FLAG_METAL_WATER) + HOLD_METAL_WATER_FALLING = 0x000042F5, -- (0x0F5 | FLAG_STATIONARY | FLAG_METAL_WATER) + METAL_WATER_FALL_LAND = 0x000042F6, -- (0x0F6 | FLAG_STATIONARY | FLAG_METAL_WATER) + HOLD_METAL_WATER_FALL_LAND = 0x000042F7, -- (0x0F7 | FLAG_STATIONARY | FLAG_METAL_WATER) + METAL_WATER_JUMP = 0x000044F8, -- (0x0F8 | FLAG_MOVING | FLAG_METAL_WATER) + HOLD_METAL_WATER_JUMP = 0x000044F9, -- (0x0F9 | FLAG_MOVING | FLAG_METAL_WATER) + METAL_WATER_JUMP_LAND = 0x000044FA, -- (0x0FA | FLAG_MOVING | FLAG_METAL_WATER) + HOLD_METAL_WATER_JUMP_LAND = 0x000044FB, -- (0x0FB | FLAG_MOVING | FLAG_METAL_WATER) + + -- group 0x100: cutscene actions + DISAPPEARED = 0x00001300, -- (0x100 | FLAG_STATIONARY | FLAG_INTANGIBLE) + INTRO_CUTSCENE = 0x04001301, -- (0x101 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_ALLOW_FIRST_PERSON) + STAR_DANCE_EXIT = 0x00001302, -- (0x102 | FLAG_STATIONARY | FLAG_INTANGIBLE) + STAR_DANCE_WATER = 0x00001303, -- (0x103 | FLAG_STATIONARY | FLAG_INTANGIBLE) + FALL_AFTER_STAR_GRAB = 0x00001904, -- (0x104 | FLAG_AIR | FLAG_INTANGIBLE) + READING_AUTOMATIC_DIALOG = 0x20001305, -- (0x105 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_WATER_OR_TEXT) + READING_NPC_DIALOG = 0x20001306, -- (0x106 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_WATER_OR_TEXT) + STAR_DANCE_NO_EXIT = 0x00001307, -- (0x107 | FLAG_STATIONARY | FLAG_INTANGIBLE) + READING_SIGN = 0x00001308, -- (0x108 | FLAG_STATIONARY | FLAG_INTANGIBLE) + JUMBO_STAR_CUTSCENE = 0x00001909, -- (0x109 | FLAG_AIR | FLAG_INTANGIBLE) + WAITING_FOR_DIALOG = 0x0000130A, -- (0x10A | FLAG_STATIONARY | FLAG_INTANGIBLE) + DEBUG_FREE_MOVE = 0x0000130F, -- (0x10F | FLAG_STATIONARY | FLAG_INTANGIBLE) + STANDING_DEATH = 0x00021311, -- (0x111 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + QUICKSAND_DEATH = 0x00021312, -- (0x112 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + ELECTROCUTION = 0x00021313, -- (0x113 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + SUFFOCATION = 0x00021314, -- (0x114 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + DEATH_ON_STOMACH = 0x00021315, -- (0x115 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + DEATH_ON_BACK = 0x00021316, -- (0x116 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + EATEN_BY_BUBBA = 0x00021317, -- (0x117 | FLAG_STATIONARY | FLAG_INTANGIBLE | FLAG_INVULNERABLE) + END_PEACH_CUTSCENE = 0x00001918, -- (0x118 | FLAG_AIR | FLAG_INTANGIBLE) + CREDITS_CUTSCENE = 0x00001319, -- (0x119 | FLAG_STATIONARY | FLAG_INTANGIBLE) + END_WAVING_CUTSCENE = 0x0000131A, -- (0x11A | FLAG_STATIONARY | FLAG_INTANGIBLE) + PULLING_DOOR = 0x00001320, -- (0x120 | FLAG_STATIONARY | FLAG_INTANGIBLE) + PUSHING_DOOR = 0x00001321, -- (0x121 | FLAG_STATIONARY | FLAG_INTANGIBLE) + WARP_DOOR_SPAWN = 0x00001322, -- (0x122 | FLAG_STATIONARY | FLAG_INTANGIBLE) + EMERGE_FROM_PIPE = 0x00001923, -- (0x123 | FLAG_AIR | FLAG_INTANGIBLE) + SPAWN_SPIN_AIRBORNE = 0x00001924, -- (0x124 | FLAG_AIR | FLAG_INTANGIBLE) + SPAWN_SPIN_LANDING = 0x00001325, -- (0x125 | FLAG_STATIONARY | FLAG_INTANGIBLE) + EXIT_AIRBORNE = 0x00001926, -- (0x126 | FLAG_AIR | FLAG_INTANGIBLE) + EXIT_LAND_SAVE_DIALOG = 0x00001327, -- (0x127 | FLAG_STATIONARY | FLAG_INTANGIBLE) + DEATH_EXIT = 0x00001928, -- (0x128 | FLAG_AIR | FLAG_INTANGIBLE) + UNUSED_DEATH_EXIT = 0x00001929, -- (0x129 | FLAG_AIR | FLAG_INTANGIBLE) + FALLING_DEATH_EXIT = 0x0000192A, -- (0x12A | FLAG_AIR | FLAG_INTANGIBLE) + SPECIAL_EXIT_AIRBORNE = 0x0000192B, -- (0x12B | FLAG_AIR | FLAG_INTANGIBLE) + SPECIAL_DEATH_EXIT = 0x0000192C, -- (0x12C | FLAG_AIR | FLAG_INTANGIBLE) + FALLING_EXIT_AIRBORNE = 0x0000192D, -- (0x12D | FLAG_AIR | FLAG_INTANGIBLE) + UNLOCKING_KEY_DOOR = 0x0000132E, -- (0x12E | FLAG_STATIONARY | FLAG_INTANGIBLE) + UNLOCKING_STAR_DOOR = 0x0000132F, -- (0x12F | FLAG_STATIONARY | FLAG_INTANGIBLE) + ENTERING_STAR_DOOR = 0x00001331, -- (0x131 | FLAG_STATIONARY | FLAG_INTANGIBLE) + SPAWN_NO_SPIN_AIRBORNE = 0x00001932, -- (0x132 | FLAG_AIR | FLAG_INTANGIBLE) + SPAWN_NO_SPIN_LANDING = 0x00001333, -- (0x133 | FLAG_STATIONARY | FLAG_INTANGIBLE) + BBH_ENTER_JUMP = 0x00001934, -- (0x134 | FLAG_AIR | FLAG_INTANGIBLE) + BBH_ENTER_SPIN = 0x00001535, -- (0x135 | FLAG_MOVING | FLAG_INTANGIBLE) + TELEPORT_FADE_OUT = 0x00001336, -- (0x136 | FLAG_STATIONARY | FLAG_INTANGIBLE) + TELEPORT_FADE_IN = 0x00001337, -- (0x137 | FLAG_STATIONARY | FLAG_INTANGIBLE) + SHOCKED = 0x00020338, -- (0x138 | FLAG_STATIONARY | FLAG_INVULNERABLE) + SQUISHED = 0x00020339, -- (0x139 | FLAG_STATIONARY | FLAG_INVULNERABLE) + HEAD_STUCK_IN_GROUND = 0x0002033A, -- (0x13A | FLAG_STATIONARY | FLAG_INVULNERABLE) + BUTT_STUCK_IN_GROUND = 0x0002033B, -- (0x13B | FLAG_STATIONARY | FLAG_INVULNERABLE) + FEET_STUCK_IN_GROUND = 0x0002033C, -- (0x13C | FLAG_STATIONARY | FLAG_INVULNERABLE) + PUTTING_ON_CAP = 0x0000133D, -- (0x13D | FLAG_STATIONARY | FLAG_INTANGIBLE) + + -- group 0x140: "automatic" actions + HOLDING_POLE = 0x08100340, -- (0x140 | FLAG_STATIONARY | FLAG_ON_POLE | FLAG_PAUSE_EXIT) + GRAB_POLE_SLOW = 0x00100341, -- (0x141 | FLAG_STATIONARY | FLAG_ON_POLE) + GRAB_POLE_FAST = 0x00100342, -- (0x142 | FLAG_STATIONARY | FLAG_ON_POLE) + CLIMBING_POLE = 0x00100343, -- (0x143 | FLAG_STATIONARY | FLAG_ON_POLE) + TOP_OF_POLE_TRANSITION = 0x00100344, -- (0x144 | FLAG_STATIONARY | FLAG_ON_POLE) + TOP_OF_POLE = 0x00100345, -- (0x145 | FLAG_STATIONARY | FLAG_ON_POLE) + START_HANGING = 0x08200348, -- (0x148 | FLAG_STATIONARY | FLAG_HANGING | FLAG_PAUSE_EXIT) + HANGING = 0x00200349, -- (0x149 | FLAG_STATIONARY | FLAG_HANGING) + HANG_MOVING = 0x0020054A, -- (0x14A | FLAG_MOVING | FLAG_HANGING) + LEDGE_GRAB = 0x0800034B, -- (0x14B | FLAG_STATIONARY | FLAG_PAUSE_EXIT) + LEDGE_CLIMB_SLOW = 0x0000054C, -- (0x14C | FLAG_MOVING) + LEDGE_CLIMB_DOWN = 0x0000054D, -- (0x14D | FLAG_MOVING) + LEDGE_CLIMB_FAST = 0x0000054E, -- (0x14E | FLAG_MOVING) + GRABBED = 0x00020370, -- (0x170 | FLAG_STATIONARY | FLAG_INVULNERABLE) + IN_CANNON = 0x00001371, -- (0x171 | FLAG_STATIONARY | FLAG_INTANGIBLE) + TORNADO_TWIRLING = 0x10020372, -- (0x172 | FLAG_STATIONARY | FLAG_INVULNERABLE | FLAG_SWIMMING_OR_FLYING) + + -- group 0x180: object actions + PUNCHING = 0x00800380, -- (0x180 | FLAG_STATIONARY | FLAG_ATTACKING) + PICKING_UP = 0x00000383, -- (0x183 | FLAG_STATIONARY) + DIVE_PICKING_UP = 0x00000385, -- (0x185 | FLAG_STATIONARY) + STOMACH_SLIDE_STOP = 0x00000386, -- (0x186 | FLAG_STATIONARY) + PLACING_DOWN = 0x00000387, -- (0x187 | FLAG_STATIONARY) + THROWING = 0x80000588, -- (0x188 | FLAG_MOVING | FLAG_THROWING) + HEAVY_THROW = 0x80000589, -- (0x189 | FLAG_MOVING | FLAG_THROWING) + PICKING_UP_BOWSER = 0x00000390, -- (0x190 | FLAG_STATIONARY) + HOLDING_BOWSER = 0x00000391, -- (0x191 | FLAG_STATIONARY) + RELEASING_BOWSER = 0x00000392, -- (0x192 | FLAG_STATIONARY) +} diff --git a/sm64/Enums/Buttons.lua b/sm64/Enums/Buttons.lua new file mode 100644 index 0000000..3e2f576 --- /dev/null +++ b/sm64/Enums/Buttons.lua @@ -0,0 +1,18 @@ +--!strict + +return { + A_BUTTON = 0x8000, + B_BUTTON = 0x4000, + L_TRIG = 0x0020, + R_TRIG = 0x0010, + Z_TRIG = 0x2000, + START_BUTTON = 0x1000, + U_JPAD = 0x0800, + L_JPAD = 0x0200, + R_JPAD = 0x0100, + D_JPAD = 0x0400, + U_CBUTTONS = 0x0008, + L_CBUTTONS = 0x0002, + R_CBUTTONS = 0x0001, + D_CBUTTONS = 0x0004, +} diff --git a/sm64/Enums/FloorType.lua b/sm64/Enums/FloorType.lua new file mode 100644 index 0000000..bf91f3a --- /dev/null +++ b/sm64/Enums/FloorType.lua @@ -0,0 +1,20 @@ +--!strict + +return { + NONZERO_ANALOG = 0x0001, + A_PRESSED = 0x0002, + OFF_FLOOR = 0x0004, + ABOVE_SLIDE = 0x0008, + FIRST_PERSON = 0x0010, + UNKNOWN_5 = 0x0020, + SQUISHED = 0x0040, + A_DOWN = 0x0080, + IN_POISON_GAS = 0x0100, + IN_WATER = 0x0200, + STOMPED = 0x0400, + INTERACT_OBJ_GRABBABLE = 0x0800, + UNKNOWN_12 = 0x1000, + B_PRESSED = 0x2000, + Z_DOWN = 0x4000, + Z_PRESSED = 0x8000, +} diff --git a/sm64/Enums/InputFlags.lua b/sm64/Enums/InputFlags.lua new file mode 100644 index 0000000..bf91f3a --- /dev/null +++ b/sm64/Enums/InputFlags.lua @@ -0,0 +1,20 @@ +--!strict + +return { + NONZERO_ANALOG = 0x0001, + A_PRESSED = 0x0002, + OFF_FLOOR = 0x0004, + ABOVE_SLIDE = 0x0008, + FIRST_PERSON = 0x0010, + UNKNOWN_5 = 0x0020, + SQUISHED = 0x0040, + A_DOWN = 0x0080, + IN_POISON_GAS = 0x0100, + IN_WATER = 0x0200, + STOMPED = 0x0400, + INTERACT_OBJ_GRABBABLE = 0x0800, + UNKNOWN_12 = 0x1000, + B_PRESSED = 0x2000, + Z_DOWN = 0x4000, + Z_PRESSED = 0x8000, +} diff --git a/sm64/Enums/Mario/Cap.lua b/sm64/Enums/Mario/Cap.lua new file mode 100644 index 0000000..13d9267 --- /dev/null +++ b/sm64/Enums/Mario/Cap.lua @@ -0,0 +1,9 @@ +--!strict + +return { + DEFAULT_CAP_ON = 0, + DEFAULT_CAP_OFF = 1, + + WING_CAP_ON = 2, + WING_CAP_OFF = 3, +} diff --git a/sm64/Enums/Mario/Eyes.lua b/sm64/Enums/Mario/Eyes.lua new file mode 100644 index 0000000..b707acd --- /dev/null +++ b/sm64/Enums/Mario/Eyes.lua @@ -0,0 +1,11 @@ +--!strict + +return { + BLINK = 0, + OPEN = 1, + + HALF_CLOSED = 2, + CLOSED = 3, + + DEAD = 4, +} diff --git a/sm64/Enums/Mario/Flags.lua b/sm64/Enums/Mario/Flags.lua new file mode 100644 index 0000000..669a57d --- /dev/null +++ b/sm64/Enums/Mario/Flags.lua @@ -0,0 +1,24 @@ +--!strict + +local MarioFlags = { + NORMAL_CAP = 0x00000001, + VANISH_CAP = 0x00000002, + METAL_CAP = 0x00000004, + WING_CAP = 0x00000008, + CAP_ON_HEAD = 0x00000010, + CAP_IN_HAND = 0x00000020, + METAL_SHOCK = 0x00000040, + TELEPORTING = 0x00000080, + MOVING_UP_IN_AIR = 0x00000100, + ACTION_SOUND_PLAYED = 0x00010000, + MARIO_SOUND_PLAYED = 0x00020000, + FALLING_FAR = 0x00040000, + PUNCHING = 0x00100000, + KICKING = 0x00200000, + TRIPPING = 0x00400000, +} + +MarioFlags.SPECIAL_CAPS = MarioFlags.VANISH_CAP + MarioFlags.METAL_CAP + MarioFlags.WING_CAP +MarioFlags.CAPS = MarioFlags.NORMAL_CAP + MarioFlags.SPECIAL_CAPS + +return MarioFlags diff --git a/sm64/Enums/Mario/Hands.lua b/sm64/Enums/Mario/Hands.lua new file mode 100644 index 0000000..fda4b34 --- /dev/null +++ b/sm64/Enums/Mario/Hands.lua @@ -0,0 +1,10 @@ +--!strict + +return { + FISTS = 0, + OPEN = 1, + PEACE_SIGN = 2, + HOLDING_CAP = 3, + HOLDING_WING_CAP = 4, + RIGHT_OPEN = 5, +} diff --git a/sm64/Enums/Mario/Input.lua b/sm64/Enums/Mario/Input.lua new file mode 100644 index 0000000..ba869fc --- /dev/null +++ b/sm64/Enums/Mario/Input.lua @@ -0,0 +1,19 @@ +--!strict + +return { + NONZERO_ANALOG = 0x0001, + A_PRESSED = 0x0002, + OFF_FLOOR = 0x0004, + ABOVE_SLIDE = 0x0008, + FIRST_PERSON = 0x0010, + UNKNOWN_5 = 0x0020, + SQUISHED = 0x0040, + A_DOWN = 0x0080, + IN_POISON_GAS = 0x0100, + IN_WATER = 0x0200, + STOMPED = 0x0400, + INTERACT_OBJ_GRABBABLE = 0x0800, + B_PRESSED = 0x2000, + Z_DOWN = 0x4000, + Z_PRESSED = 0x8000, +} diff --git a/sm64/Enums/ModelFlags.lua b/sm64/Enums/ModelFlags.lua new file mode 100644 index 0000000..2191b7b --- /dev/null +++ b/sm64/Enums/ModelFlags.lua @@ -0,0 +1,7 @@ +--!strict + +return { + METAL = 0x1, + INVISIBLE = 0x2, + NOISE_ALPHA = 0x4, +} diff --git a/sm64/Enums/ParticleFlags.lua b/sm64/Enums/ParticleFlags.lua new file mode 100644 index 0000000..4f7f24f --- /dev/null +++ b/sm64/Enums/ParticleFlags.lua @@ -0,0 +1,22 @@ +--!strict + +return { + DUST = bit32.lshift(1, 0), + VERTICAL_STAR = bit32.lshift(1, 1), + SPARKLES = bit32.lshift(1, 3), + HORIZONTAL_STAR = bit32.lshift(1, 4), + BUBBLE = bit32.lshift(1, 5), + WATER_SPLASH = bit32.lshift(1, 6), + IDLE_WATER_WAVE = bit32.lshift(1, 7), + SHALLOW_WATER_WAVE = bit32.lshift(1, 8), + PLUNGE_BUBBLE = bit32.lshift(1, 9), + WAVE_TRAIL = bit32.lshift(1, 10), + FIRE = bit32.lshift(1, 11), + SHALLOW_WATER_SPLASH = bit32.lshift(1, 12), + LEAF = bit32.lshift(1, 13), + SNOW = bit32.lshift(1, 14), + DIRT = bit32.lshift(1, 15), + MIST_CIRCLE = bit32.lshift(1, 16), + BREATH = bit32.lshift(1, 17), + TRIANGLE = bit32.lshift(1, 18), +} diff --git a/sm64/Enums/Steps/Air.lua b/sm64/Enums/Steps/Air.lua new file mode 100644 index 0000000..3f1b558 --- /dev/null +++ b/sm64/Enums/Steps/Air.lua @@ -0,0 +1,13 @@ +--!strict + +return { + NONE = 0, + LANDED = 1, + HIT_WALL = 2, + GRABBED_LEDGE = 3, + GRABBED_CEILING = 4, + HIT_LAVA_WALL = 6, + + CHECK_LEDGE_GRAB = 1, + CHECK_HANG = 2, +} diff --git a/sm64/Enums/Steps/Ground.lua b/sm64/Enums/Steps/Ground.lua new file mode 100644 index 0000000..26f9348 --- /dev/null +++ b/sm64/Enums/Steps/Ground.lua @@ -0,0 +1,9 @@ +--!strict + +return { + LEFT_GROUND = 0, + NONE = 1, + HIT_WALL = 2, + HIT_WALL_STOP_QSTEPS = 3, + HIT_WALL_CONTINUE_QSTEPS = 4, +} diff --git a/sm64/Enums/Steps/Water.lua b/sm64/Enums/Steps/Water.lua new file mode 100644 index 0000000..41af447 --- /dev/null +++ b/sm64/Enums/Steps/Water.lua @@ -0,0 +1,9 @@ +--!strict + +return { + NONE = 0, + HIT_FLOOR = 1, + HIT_CEILING = 2, + CANCELLED = 3, + HIT_WALL = 4, +} diff --git a/sm64/Enums/SurfaceClass.lua b/sm64/Enums/SurfaceClass.lua new file mode 100644 index 0000000..a59d64f --- /dev/null +++ b/sm64/Enums/SurfaceClass.lua @@ -0,0 +1,8 @@ +--!strict + +return { + DEFAULT = 0x0000, + VERY_SLIPPERY = 0x0013, + SLIPPERY = 0x0014, + NOT_SLIPPERY = 0x0015, +} diff --git a/sm64/Enums/TerrainType.lua b/sm64/Enums/TerrainType.lua new file mode 100644 index 0000000..b1fcc5d --- /dev/null +++ b/sm64/Enums/TerrainType.lua @@ -0,0 +1,50 @@ +--!strict + +local TerrainType = { + DEFAULT = 0, + GRASS = 1, + WATER = 2, + STONE = 3, + SPOOKY = 4, + SNOW = 5, + ICE = 6, + SAND = 7, + METAL = 8, +} + +TerrainType.FROM_MATERIAL = { + [Enum.Material.Mud] = TerrainType.GRASS, + [Enum.Material.Grass] = TerrainType.GRASS, + [Enum.Material.Ground] = TerrainType.GRASS, + [Enum.Material.LeafyGrass] = TerrainType.GRASS, + + [Enum.Material.Ice] = TerrainType.ICE, + [Enum.Material.Marble] = TerrainType.ICE, + [Enum.Material.Glacier] = TerrainType.ICE, + + [Enum.Material.Wood] = TerrainType.SPOOKY, + [Enum.Material.WoodPlanks] = TerrainType.SPOOKY, + + [Enum.Material.Foil] = TerrainType.METAL, + [Enum.Material.Metal] = TerrainType.METAL, + [Enum.Material.DiamondPlate] = TerrainType.METAL, + [Enum.Material.CorrodedMetal] = TerrainType.METAL, + + [Enum.Material.Rock] = TerrainType.STONE, + [Enum.Material.Salt] = TerrainType.STONE, + [Enum.Material.Brick] = TerrainType.STONE, + [Enum.Material.Slate] = TerrainType.STONE, + [Enum.Material.Basalt] = TerrainType.STONE, + [Enum.Material.Pebble] = TerrainType.STONE, + [Enum.Material.Granite] = TerrainType.STONE, + [Enum.Material.Sandstone] = TerrainType.STONE, + [Enum.Material.Cobblestone] = TerrainType.STONE, + [Enum.Material.CrackedLava] = TerrainType.STONE, + + [Enum.Material.Snow] = TerrainType.SNOW, + [Enum.Material.Sand] = TerrainType.SAND, + [Enum.Material.Water] = TerrainType.WATER, + [Enum.Material.Fabric] = TerrainType.SNOW, +} + +return TerrainType diff --git a/sm64/Enums/init.lua b/sm64/Enums/init.lua new file mode 100644 index 0000000..78c3487 --- /dev/null +++ b/sm64/Enums/init.lua @@ -0,0 +1,42 @@ +--!strict + +local Enums = { + Action = require(script.Action), + Buttons = require(script.Buttons), + ActionFlags = require(script.Action.Flags), + ActionGroups = require(script.Action.Groups), + + MarioCap = require(script.Mario.Cap), + MarioEyes = require(script.Mario.Eyes), + MarioFlags = require(script.Mario.Flags), + MarioHands = require(script.Mario.Hands), + MarioInput = require(script.Mario.Input), + + AirStep = require(script.Steps.Air), + WaterStep = require(script.Steps.Water), + GroundStep = require(script.Steps.Ground), + + InputFlags = require(script.InputFlags), + ModelFlags = require(script.ModelFlags), + SurfaceClass = require(script.SurfaceClass), + TerrainType = require(script.TerrainType), + ParticleFlags = require(script.ParticleFlags), +} + +local nameIndex: { [any]: { [number]: string } } = {} + +function Enums.GetName(map, value): string + if not nameIndex[map] then + local index = {} + + for name, value in pairs(map) do + index[value] = name + end + + nameIndex[map] = index + end + + return nameIndex[map][value] +end + +return table.freeze(Enums) diff --git a/sm64/Mario/Airborne.client.lua b/sm64/Mario/Airborne.client.lua new file mode 100644 index 0000000..968afcb --- /dev/null +++ b/sm64/Mario/Airborne.client.lua @@ -0,0 +1,1292 @@ +--!strict + +local System = require(script.Parent) +local Animations = System.Animations +local Sounds = System.Sounds +local Enums = System.Enums +local Util = System.Util + +local Action = Enums.Action +local ActionFlags = Enums.ActionFlags +local ActionGroup = Enums.ActionGroups + +local AirStep = Enums.AirStep +local MarioEyes = Enums.MarioEyes +local InputFlags = Enums.InputFlags +local MarioFlags = Enums.MarioFlags +local ParticleFlags = Enums.ParticleFlags + +type Mario = System.Mario + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helpers +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local function stopRising(m: Mario) + if m.Velocity.Y > 0 then + m.Velocity *= Vector3.new(1, 0, 1) + end +end + +local function playFlipSounds(m: Mario, frame1: number, frame2: number, frame3: number) + local animFrame = m.AnimFrame + + if animFrame == frame1 or animFrame == frame2 or animFrame == frame3 then + m:PlaySound(Sounds.ACTION_SPIN) + end +end + +local function playFarFallSound(m: Mario) + if m.Flags:Has(MarioFlags.FALLING_FAR) then + return + end + + local action = m.Action + + if action() == Action.TWIRLING then + return + end + + if action() == Action.FLYING then + return + end + + if action:Has(ActionFlags.INVULNERABLE) then + return + end + + if m.PeakHeight - m.Position.Y > 1150 then + m:PlaySound(Sounds.MARIO_WAAAOOOW) + m.Flags:Add(MarioFlags.FALLING_FAR) + end +end + +local function playKnockbackSound(m: Mario) + if m.ActionArg == 0 and math.abs(m.ForwardVel) >= 28 then + m:PlaySoundIfNoFlag(Sounds.MARIO_DOH, MarioFlags.MARIO_SOUND_PLAYED) + else + m:PlaySoundIfNoFlag(Sounds.MARIO_UH, MarioFlags.MARIO_SOUND_PLAYED) + end +end + +local function lavaBoostOnWall(m: Mario) + local wall = m.Wall + + if wall then + local angle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) + m.FaceAngle = Util.SetYint16(m.FaceAngle, angle) + end + + if m.ForwardVel < 24 then + m.ForwardVel = 24 + end + + if not m.Flags:Has(MarioFlags.METAL_CAP) then + m.HurtCounter += if m.Flags:Has(MarioFlags.CAP_ON_HEAD) then 12 else 18 + end + + m:PlaySound(Sounds.MARIO_ON_FIRE) + m:SetAction(Action.LAVA_BOOST, 1) +end + +local function checkFallDamage(m: Mario, hardFallAction: number): boolean + local fallHeight = m.PeakHeight - m.Position.Y + local damageHeight = 1150 + + if m.Action() == Action.TWIRLING then + return false + end + + if m.Velocity.Y < -55 and fallHeight > 3000 then + m.HurtCounter += if m.Flags:Has(MarioFlags.CAP_ON_HEAD) then 16 else 24 + m:PlaySound(Sounds.MARIO_ATTACKED) + m:SetAction(hardFallAction, 4) + elseif fallHeight > damageHeight and not m:FloorIsSlippery() then + m.HurtCounter += if m.Flags:Has(MarioFlags.CAP_ON_HEAD) then 8 else 12 + m:PlaySound(Sounds.MARIO_ATTACKED) + m.SquishTimer = 30 + end + + return false +end + +local function checkKickOrDiveInAir(m: Mario): boolean + if m.Input:Has(InputFlags.B_PRESSED) then + m:SetAction(if m.ForwardVel > 28 then Action.DIVE else Action.JUMP_KICK) + end + + return false +end + +local function updateAirWithTurn(m: Mario) + local dragThreshold = if m.Action() == Action.LONG_JUMP then 48 else 32 + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 0.35) + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + local intendedDYaw = m.IntendedYaw - m.FaceAngle.Y + local intendedMag = m.IntendedMag / 32 + + m.ForwardVel += 1.5 * Util.Coss(intendedDYaw) * intendedMag + m.FaceAngle += Vector3int16.new(0, 512 * Util.Sins(intendedDYaw) * intendedMag, 0) + end + + if m.ForwardVel > dragThreshold then + m.ForwardVel -= 1 + end + + if m.ForwardVel < -16 then + m.ForwardVel += 2 + end + + m.SlideVelX = m.ForwardVel * Util.Sins(m.FaceAngle.Y) + m.SlideVelZ = m.ForwardVel * Util.Coss(m.FaceAngle.Y) + m.Velocity = Vector3.new(m.SlideVelX, m.Velocity.Y, m.SlideVelZ) +end + +local function updateAirWithoutTurn(m: Mario) + local dragThreshold = 32 + + if m.Action() == Action.LONG_JUMP then + dragThreshold = 48 + end + + local sidewaysSpeed = 0 + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 0.35) + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + local intendedDYaw = m.IntendedYaw - m.FaceAngle.Y + local intendedMag = m.IntendedMag / 32 + + m.ForwardVel += intendedMag * Util.Coss(intendedDYaw) * 1.5 + sidewaysSpeed = intendedMag * Util.Sins(intendedDYaw) * 10 + end + + --! Uncapped air speed. Net positive when moving forward. + if m.ForwardVel > dragThreshold then + m.ForwardVel -= 1 + end + + if m.ForwardVel < -16 then + m.ForwardVel += 2 + end + + m.SlideVelX = m.ForwardVel * Util.Sins(m.FaceAngle.Y) + m.SlideVelZ = m.ForwardVel * Util.Coss(m.FaceAngle.Y) + + m.SlideVelX += sidewaysSpeed * Util.Sins(m.FaceAngle.Y + 0x4000) + m.SlideVelZ += sidewaysSpeed * Util.Coss(m.FaceAngle.Y + 0x4000) + + m.Velocity = Vector3.new(m.SlideVelX, m.Velocity.Y, m.SlideVelZ) +end + +local function updateLavaBoostOrTwirling(m: Mario) + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + local intendedDYaw = m.IntendedYaw - m.FaceAngle.Y + local intendedMag = m.IntendedMag / 32 + + m.ForwardVel += Util.Coss(intendedDYaw) * intendedMag + m.FaceAngle += Vector3int16.new(0, Util.Sins(intendedDYaw) * intendedMag * 1024, 0) + + if m.ForwardVel < 0 then + m.FaceAngle += Vector3int16.new(0, 0x8000, 0) + m.ForwardVel *= -1 + end + + if m.ForwardVel > 32 then + m.ForwardVel -= 2 + end + end + + m.SlideVelX = m.ForwardVel * Util.Sins(m.FaceAngle.Y) + m.SlideVelZ = m.ForwardVel * Util.Coss(m.FaceAngle.Y) + + m.Velocity = Vector3.new(m.SlideVelX, m.Velocity.Y, m.SlideVelZ) +end + +local function updateFlyingYaw(m: Mario) + local targetYawVel = -Util.SignedShort(m.Controller.StickX * (m.ForwardVel / 4)) + + if targetYawVel > 0 then + if m.AngleVel.Y < 0 then + m.AngleVel += Vector3int16.new(0, 0x40, 0) + + if m.AngleVel.Y > 0x10 then + m.AngleVel = Util.SetYint16(m.AngleVel, 0x10) + end + else + local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x10, 0x20) + m.AngleVel = Util.SetYint16(m.AngleVel, y) + end + elseif targetYawVel < 0 then + if m.AngleVel.Y > 0 then + m.AngleVel -= Vector3int16.new(0, 0x40, 0) + + if m.AngleVel.Y < -0x10 then + m.AngleVel = Util.SetYint16(m.AngleVel, -0x10) + end + else + local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x20, 0x10) + m.AngleVel = Util.SetYint16(m.AngleVel, y) + end + end + + m.FaceAngle += Vector3int16.new(0, m.AngleVel.Y, 0) + m.FaceAngle = Util.SetZint16(m.FaceAngle, 20 * -m.AngleVel.Y) +end + +local function updateFlyingPitch(m: Mario) + local targetPitchVel = -Util.SignedShort(m.Controller.StickY * (m.ForwardVel / 5)) + + if targetPitchVel > 0 then + if m.AngleVel.X < 0 then + m.AngleVel += Vector3int16.new(0x40, 0, 0) + + if m.AngleVel.X > 0x20 then + m.AngleVel = Util.SetXint16(m.AngleVel, 0x20) + end + else + local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x20, 0x40) + m.AngleVel = Util.SetXint16(m.AngleVel, x) + end + elseif targetPitchVel < 0 then + if m.AngleVel.X > 0 then + m.AngleVel -= Vector3int16.new(0x40, 0, 0) + + if m.AngleVel.X < -0x20 then + m.AngleVel = Util.SetXint16(m.AngleVel, -0x20) + end + else + local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x40, 0x20) + m.AngleVel = Util.SetXint16(m.AngleVel, x) + end + else + local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x40) + m.AngleVel = Util.SetXint16(m.AngleVel, x) + end +end + +local function updateFlying(m: Mario) + updateFlyingPitch(m) + updateFlyingYaw(m) + + m.ForwardVel -= 2 * (m.FaceAngle.X / 0x4000) + 0.1 + m.ForwardVel -= 0.5 * (1 - Util.Coss(m.AngleVel.Y)) + + if m.ForwardVel < 0 then + m.ForwardVel = 0 + end + + if m.ForwardVel > 16 then + m.FaceAngle = Util.SetXint16(m.FaceAngle, (m.ForwardVel - 32) * 6) + elseif m.ForwardVel > 4 then + m.FaceAngle = Util.SetXint16(m.FaceAngle, (m.ForwardVel - 32) * 10) + else + m.FaceAngle -= Vector3int16.new(0x400, 0, 0) + end + + m.FaceAngle += Vector3int16.new(m.AngleVel.X, 0, 0) + + if m.FaceAngle.X > 0x2AAA then + m.FaceAngle = Util.SetXint16(m.FaceAngle, 0x2AAA) + end + + if m.FaceAngle.X < -0x2AAA then + m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + end + + local velX = Util.Coss(m.FaceAngle.X) * Util.Sins(m.FaceAngle.Y) + m.SlideVelX = velX + + local velZ = Util.Coss(m.FaceAngle.X) * Util.Coss(m.FaceAngle.Y) + m.SlideVelZ = velZ + + local velY = Util.Sins(m.FaceAngle.X) + m.Velocity = m.ForwardVel * Vector3.new(velX, velY, velZ) +end + +local function commonAirActionStep(m: Mario, landAction: number, anim: Animation, stepArg: number): number + local stepResult + do + updateAirWithoutTurn(m) + stepResult = m:PerformAirStep(stepArg) + end + + if stepResult == AirStep.NONE then + m:SetAnimation(anim) + elseif stepResult == AirStep.LANDED then + if not checkFallDamage(m, Action.HARD_BACKWARD_GROUND_KB) then + m:SetAction(landAction) + end + elseif stepResult == AirStep.HIT_WALL then + m:SetAnimation(anim) + + if m.ForwardVel > 16 then + m:BonkReflection() + m.FaceAngle += Vector3int16.new(0, 0x8000, 0) + + if m.Wall then + m:SetAction(Action.AIR_HIT_WALL) + else + stopRising(m) + + if m.ForwardVel >= 38 then + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + else + if m.ForwardVel > 8 then + m:SetForwardVel(-8) + end + + m:SetAction(Action.SOFT_BONK) + end + end + else + m:SetForwardVel(0) + end + elseif stepResult == AirStep.GRABBED_LEDGE then + m:SetAnimation(Animations.IDLE_ON_LEDGE) + m:SetAction(Action.LEDGE_GRAB) + elseif stepResult == AirStep.GRABBED_CEILING then + m:SetAction(Action.START_HANGING) + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + return stepResult +end + +local function commonRolloutStep(m: Mario, anim: Animation) + local stepResult + + if m.ActionState == 0 then + m.Velocity = Util.SetY(m.Velocity, 30) + m.ActionState = 1 + end + + m:PlaySound(Sounds.ACTION_TERRAIN_JUMP) + updateAirWithoutTurn(m) + + stepResult = m:PerformAirStep() + + if stepResult == AirStep.NONE then + if m.ActionState == 1 then + if m:SetAnimation(anim) == 4 then + m:PlaySound(Sounds.ACTION_SPIN) + end + else + m:SetAnimation(Animations.GENERAL_FALL) + end + elseif stepResult == AirStep.LANDED then + m:SetAction(Action.FREEFALL_LAND_STOP) + m:PlayLandingSound() + elseif stepResult == AirStep.HIT_WALL then + m:SetForwardVel(0) + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + if m.ActionState == 1 and m:IsAnimPastEnd() then + m.ActionState = 2 + end +end + +local function commonAirKnockbackStep( + m: Mario, + landAction: number, + hardFallAction: number, + anim: Animation, + speed: number +) + local stepResult + do + m:SetForwardVel(speed) + stepResult = m:PerformAirStep() + end + + if stepResult == AirStep.NONE then + m:SetAnimation(anim) + elseif stepResult == AirStep.LANDED then + if not checkFallDamage(m, hardFallAction) then + local action = m.Action() + + if action == Action.THROWN_FORWARD or action == Action.THROWN_BACKWARD then + m:SetAction(landAction, m.HurtCounter) + else + m:SetAction(landAction, m.ActionArg) + end + end + elseif stepResult == AirStep.HIT_WALL then + m:SetAnimation(Animations.BACKWARD_AIR_KB) + m:BonkReflection() + + stopRising(m) + m:SetForwardVel(-speed) + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + return stepResult +end + +local function checkWallKick(m: Mario) + if m.WallKickTimer ~= 0 then + if m.Input:Has(InputFlags.A_PRESSED) then + if m.PrevAction() == Action.AIR_HIT_WALL then + m.FaceAngle += Vector3int16.new(0, 0x8000, 0) + end + end + end + + return false +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Actions +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local AIR_STEP_CHECK_BOTH = bit32.bor(AirStep.CHECK_LEDGE_GRAB, AirStep.CHECK_HANG) +local DEF_ACTION: (number, (Mario) -> boolean) -> () = System.RegisterAction + +DEF_ACTION(Action.JUMP, function(m: Mario) + if checkKickOrDiveInAir(m) then + return true + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + commonAirActionStep(m, Action.JUMP_LAND, Animations.SINGLE_JUMP, AIR_STEP_CHECK_BOTH) + + return false +end) + +DEF_ACTION(Action.DOUBLE_JUMP, function(m: Mario) + local anim = if m.Velocity.Y >= 0 then Animations.DOUBLE_JUMP_RISE else Animations.DOUBLE_JUMP_FALL + + if checkKickOrDiveInAir(m) then + return true + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP, Sounds.MARIO_HOOHOO) + commonAirActionStep(m, Action.DOUBLE_JUMP_LAND, anim, AIR_STEP_CHECK_BOTH) + + return false +end) + +DEF_ACTION(Action.TRIPLE_JUMP, function(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + commonAirActionStep(m, Action.TRIPLE_JUMP_LAND, Animations.TRIPLE_JUMP, 0) + + playFlipSounds(m, 2, 8, 20) + return false +end) + +DEF_ACTION(Action.BACKFLIP, function(m: Mario) + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP, Sounds.MARIO_YAH_WAH_HOO) + commonAirActionStep(m, Action.BACKFLIP_LAND, Animations.BACKFLIP, 0) + + playFlipSounds(m, 2, 3, 17) + return false +end) + +DEF_ACTION(Action.FREEFALL, function(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + local anim + + if m.ActionArg == 0 then + anim = Animations.GENERAL_FALL + elseif m.ActionArg == 1 then + anim = Animations.FALL_FROM_SLIDE + elseif m.ActionArg == 2 then + anim = Animations.FALL_FROM_SLIDE_KICK + end + + commonAirActionStep(m, Action.FREEFALL_LAND, anim, AirStep.CHECK_LEDGE_GRAB) + return false +end) + +DEF_ACTION(Action.SIDE_FLIP, function(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + commonAirActionStep(m, Action.SIDE_FLIP_LAND, Animations.SLIDEFLIP, AirStep.CHECK_LEDGE_GRAB) + + if m.AnimFrame == 6 then + m:PlaySound(Sounds.ACTION_SIDE_FLIP) + end + + return false +end) + +DEF_ACTION(Action.WALL_KICK_AIR, function(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayJumpSound() + commonAirActionStep(m, Action.JUMP_LAND, Animations.SLIDEJUMP, AirStep.CHECK_LEDGE_GRAB) + + return false +end) + +DEF_ACTION(Action.LONG_JUMP, function(m: Mario) + local anim = if m.LongJumpIsSlow then Animations.SLOW_LONGJUMP else Animations.FAST_LONGJUMP + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP, Sounds.MARIO_YAHOO) + commonAirActionStep(m, Action.LONG_JUMP_LAND, anim, AirStep.CHECK_LEDGE_GRAB) + + return false +end) + +DEF_ACTION(Action.TWIRLING, function(m: Mario) + local startTwirlYaw = m.TwirlYaw + local yawVelTarget = 0x1000 + + if m.Input:Has(InputFlags.A_DOWN) then + yawVelTarget = 0x2000 + end + + local yVel = Util.ApproachInt(m.AngleVel.Y, yawVelTarget, 0x200) + m.AngleVel = Util.SetYint16(m.AngleVel, yVel) + m.TwirlYaw += yVel + + m:SetAnimation(if m.ActionArg == 0 then Animations.START_TWIRL else Animations.TWIRL) + + if m:IsAnimPastEnd() then + m.ActionArg = 1 + end + + if startTwirlYaw > m.TwirlYaw then + m:PlaySound(Sounds.ACTION_TWIRL) + end + + local step = m:PerformAirStep() + + if step == AirStep.LANDED then + m:SetAction(Action.TWIRL_LAND) + elseif step == AirStep.HIT_WALL then + m:BonkReflection(false) + elseif step == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + m.GfxAngle += Vector3int16.new(0, m.TwirlYaw, 0) + return false +end) + +DEF_ACTION(Action.DIVE, function(m: Mario) + local airStep + + if m.ActionArg == 0 then + m:PlayMarioSound(Sounds.ACTION_THROW, Sounds.MARIO_HOOHOO) + else + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + end + + m:SetAnimation(Animations.DIVE) + updateAirWithoutTurn(m) + airStep = m:PerformAirStep() + + if airStep == AirStep.NONE then + if m.Velocity.Y < 0 and m.FaceAngle.X > -0x2AAA then + m.FaceAngle -= Vector3int16.new(0x200, 0, 0) + + if m.FaceAngle.X < -0x2AAA then + m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + end + end + + m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) + elseif airStep == AirStep.LANDED then + if not checkFallDamage(m, Action.HARD_FORWARD_GROUND_KB) then + m:SetAction(Action.DIVE_SLIDE) + end + + m.FaceAngle *= Vector3int16.new(0, 1, 1) + elseif airStep == AirStep.HIT_WALL then + m:BonkReflection(true) + m.FaceAngle *= Vector3int16.new(0, 1, 1) + + stopRising(m) + + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + elseif airStep == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + return false +end) + +DEF_ACTION(Action.STEEP_JUMP, function(m: Mario) + local airStep + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + m:SetForwardVel(0.98 * m.ForwardVel) + airStep = m:PerformAirStep() + + if airStep == AirStep.LANDED then + if not checkFallDamage(m, Action.HARD_BACKWARD_GROUND_KB) then + m.FaceAngle *= Vector3int16.new(0, 1, 1) + m:SetAction(if m.ForwardVel < 0 then Action.BEGIN_SLIDING else Action.JUMP_LAND) + end + elseif airStep == AirStep.HIT_WALL then + m:SetForwardVel(0) + elseif airStep == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + m:SetAnimation(Animations.SINGLE_JUMP) + m.GfxAngle = Util.SetYint16(m.GfxAngle, m.SteepJumpYaw) + + return false +end) + +DEF_ACTION(Action.GROUND_POUND, function(m: Mario) + local stepResult + local yOffset + + m:PlaySoundIfNoFlag(Sounds.ACTION_THROW, MarioFlags.ACTION_SOUND_PLAYED) + + if m.ActionState == 0 then + if m.ActionTimer < 10 then + yOffset = 20 - 2 * m.ActionTimer + + if m.Position.Y + yOffset + 160 < m.CeilHeight then + m.Position += Vector3.new(0, yOffset, 0) + m.PeakHeight = m.Position.Y + end + end + + m.Velocity = Util.SetY(m.Velocity, -50) + m:SetForwardVel(0) + + m:SetAnimation(if m.ActionArg == 0 then Animations.START_GROUND_POUND else Animations.TRIPLE_JUMP_GROUND_POUND) + + if m.ActionTimer == 0 then + m:PlaySound(Sounds.ACTION_SPIN) + end + + m.ActionTimer += 1 + + if m.ActionTimer >= m.AnimFrameCount + 4 then + m:PlaySound(Sounds.MARIO_GROUND_POUND_WAH) + m.ActionState = 1 + end + else + m:SetAnimation(Animations.GROUND_POUND) + stepResult = m:PerformAirStep() + + if stepResult == AirStep.LANDED then + m:PlayHeavyLandingSound(Sounds.ACTION_HEAVY_LANDING) + + if not checkFallDamage(m, Action.HARD_BACKWARD_GROUND_KB) then + m.ParticleFlags:Add(ParticleFlags.MIST_CIRCLE, ParticleFlags.HORIZONTAL_STAR) + m:SetAction(Action.GROUND_POUND_LAND) + end + elseif stepResult == AirStep.HIT_WALL then + m:SetForwardVel(-16) + stopRising(m) + + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + end + end + + return false +end) + +DEF_ACTION(Action.BURNING_JUMP, function(m: Mario) + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP) + m:SetForwardVel(m.ForwardVel) + + if m:PerformAirStep() == AirStep.LANDED then + m:PlayLandingSound() + m:SetAction(Action.BURNING_GROUND) + end + + m:SetAnimation(Animations.GENERAL_FALL) + m.ParticleFlags:Add(ParticleFlags.FIRE) + m:PlaySound(Sounds.MOVING_LAVA_BURN) + + m.BurnTimer += 3 + m.Health -= 10 + + if m.Health < 0x100 then + m.Health = 0xFF + end + + return false +end) + +DEF_ACTION(Action.BURNING_FALL, function(m: Mario) + m:SetForwardVel(m.ForwardVel) + + if m:PerformAirStep() == AirStep.LANDED then + m:PlayLandingSound(Sounds.ACTION_TERRAIN_LANDING) + m:SetAction(Action.BURNING_GROUND) + end + + m:SetAnimation(Animations.GENERAL_FALL) + m.ParticleFlags:Add(ParticleFlags.FIRE) + + m.BurnTimer += 3 + m.Health -= 10 + + if m.Health < 0x100 then + m.Health = 0xFF + end + + return false +end) + +DEF_ACTION(Action.BACKWARD_AIR_KB, function(m: Mario) + if checkWallKick(m) then + return true + end + + playKnockbackSound(m) + commonAirKnockbackStep( + m, + Action.BACKWARD_GROUND_KB, + Action.HARD_BACKWARD_GROUND_KB, + Animations.BACKWARD_AIR_KB, + -16 + ) + + return false +end) + +DEF_ACTION(Action.FORWARD_AIR_KB, function(m: Mario) + if checkWallKick(m) then + return true + end + + playKnockbackSound(m) + commonAirKnockbackStep(m, Action.FORWARD_GROUND_KB, Action.HARD_FORWARD_GROUND_KB, Animations.FORWARD_AIR_KB, 16) + + return false +end) + +DEF_ACTION(Action.HARD_BACKWARD_AIR_KB, function(m: Mario) + if checkWallKick(m) then + return true + end + + playKnockbackSound(m) + commonAirKnockbackStep( + m, + Action.HARD_BACKWARD_GROUND_KB, + Action.HARD_BACKWARD_GROUND_KB, + Animations.BACKWARD_AIR_KB, + -16 + ) + + return false +end) + +DEF_ACTION(Action.HARD_FORWARD_AIR_KB, function(m: Mario) + if checkWallKick(m) then + return true + end + + playKnockbackSound(m) + commonAirKnockbackStep( + m, + Action.HARD_FORWARD_GROUND_KB, + Action.HARD_FORWARD_GROUND_KB, + Animations.FORWARD_AIR_KB, + 16 + ) + + return false +end) + +DEF_ACTION(Action.THROWN_BACKWARD, function(m: Mario) + local landAction = if m.ActionArg ~= 0 then Action.HARD_BACKWARD_GROUND_KB else Action.BACKWARD_GROUND_KB + + m:PlaySoundIfNoFlag(Sounds.MARIO_WAAAOOOW, MarioFlags.MARIO_SOUND_PLAYED) + commonAirKnockbackStep(m, landAction, Action.HARD_BACKWARD_GROUND_KB, Animations.BACKWARD_AIR_KB, m.ForwardVel) + + m.ForwardVel *= 0.98 + return false +end) + +DEF_ACTION(Action.THROWN_FORWARD, function(m: Mario) + local landAction = if m.ActionArg ~= 0 then Action.HARD_FORWARD_GROUND_KB else Action.FORWARD_GROUND_KB + + m:PlaySoundIfNoFlag(Sounds.MARIO_WAAAOOOW, MarioFlags.MARIO_SOUND_PLAYED) + + if + commonAirKnockbackStep(m, landAction, Action.HARD_FORWARD_GROUND_KB, Animations.FORWARD_AIR_KB, m.ForwardVel) + == AirStep.NONE + then + local pitch = Util.Atan2s(m.ForwardVel, -m.Velocity.Y) + + if pitch > 0x1800 then + pitch = 0x1800 + end + + m.GfxAngle = Util.SetXint16(m.GfxAngle, pitch + 0x1800) + end + + m.ForwardVel *= 0.98 + return false +end) + +DEF_ACTION(Action.SOFT_BONK, function(m: Mario) + if checkWallKick(m) then + return true + end + + playKnockbackSound(m) + commonAirKnockbackStep( + m, + Action.FREEFALL_LAND, + Action.HARD_BACKWARD_GROUND_KB, + Animations.GENERAL_FALL, + m.ForwardVel + ) + + return false +end) + +DEF_ACTION(Action.AIR_HIT_WALL, function(m: Mario) + m.ActionTimer += 1 + + if m.ActionTimer <= 2 then + if m.Input:Has(InputFlags.A_PRESSED) then + m.Velocity = Util.SetY(m.Velocity, 52) + m.FaceAngle += Vector3int16.new(0, 0x8000, 0) + return m:SetAction(Action.WALL_KICK_AIR) + end + else + m.WallKickTimer = 5 + stopRising(m) + + if m.ForwardVel >= 38 then + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + return m:SetAction(Action.BACKWARD_AIR_KB) + elseif m.ForwardVel > 8 then + m:SetForwardVel(-8) + return m:SetAction(Action.SOFT_BONK) + end + end + + m:SetAnimation(Animations.START_WALLKICK) + return true +end) + +DEF_ACTION(Action.FORWARD_ROLLOUT, function(m: Mario) + commonRolloutStep(m, Animations.FORWARD_SPINNING) + return false +end) + +DEF_ACTION(Action.BACKWARD_ROLLOUT, function(m: Mario) + commonRolloutStep(m, Animations.BACKWARD_SPINNING) + return false +end) + +DEF_ACTION(Action.BUTT_SLIDE_AIR, function(m: Mario) + local stepResult + m.ActionTimer += 1 + + if m.ActionTimer > 30 and m.Position.Y - m.FloorHeight > 500 then + return m:SetAction(Action.FREEFALL, 1) + end + + updateAirWithTurn(m) + stepResult = m:PerformAirStep() + + if stepResult == AirStep.LANDED then + if m.ActionState == 0 and m.Velocity.Y < 0 then + local floor = m.Floor + + if floor and floor.Normal.Y > 0.9848077 then + m.Velocity *= Vector3.new(1, -0.5, 1) + m.ActionState = 1 + else + m:SetAction(Action.BUTT_SLIDE) + end + else + m:SetAction(Action.BUTT_SLIDE) + end + + m:PlayLandingSound() + elseif stepResult == AirStep.HIT_WALL then + stopRising(m) + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + m:SetAnimation(Animations.SLIDE) + return false +end) + +DEF_ACTION(Action.LAVA_BOOST, function(m: Mario) + local stepResult + m:PlaySoundIfNoFlag(Sounds.MARIO_ON_FIRE, MarioFlags.MARIO_SOUND_PLAYED) + + if not m.Input:Has(InputFlags.NONZERO_ANALOG) then + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 0.35) + end + + updateLavaBoostOrTwirling(m) + stepResult = m:PerformAirStep() + + if stepResult == AirStep.LANDED then + local floor = m.Floor + local floorType: Enum.Material? + + if floor then + floorType = floor.Material + end + + if floorType == Enum.Material.CrackedLava then + m.ActionState = 0 + + if not m.Flags:Has(MarioFlags.METAL_CAP) then + m.HurtCounter += if m.Flags:Has(MarioFlags.CAP_ON_HEAD) then 12 else 18 + end + + m.Velocity = Util.SetY(m.Velocity, 84) + m:PlaySound(Sounds.MARIO_ON_FIRE) + else + m:PlayHeavyLandingSound(Sounds.ACTION_TERRAIN_BODY_HIT_GROUND) + + if m.ActionState < 2 and m.Velocity.Y < 0 then + m.Velocity *= Vector3.new(1, -0.4, 1) + m:SetForwardVel(m.ForwardVel / 2) + m.ActionState += 1 + else + m:SetAction(Action.LAVA_BOOST_LAND) + end + end + elseif stepResult == AirStep.HIT_WALL then + m:BonkReflection() + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + m:SetAnimation(Animations.FIRE_LAVA_BURN) + + if not m.Flags:Has(MarioFlags.METAL_CAP) and m.Velocity.Y > 0 then + m.ParticleFlags:Add(ParticleFlags.FIRE) + + if m.ActionState == 0 then + m:PlaySound(Sounds.MOVING_LAVA_BURN) + end + end + + m.BodyState.EyeState = MarioEyes.DEAD + return false +end) + +DEF_ACTION(Action.SLIDE_KICK, function(m: Mario) + local stepResult + + if m.ActionState == 0 and m.ActionTimer == 0 then + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP, Sounds.MARIO_HOOHOO) + m:SetAnimation(Animations.SLIDE_KICK) + end + + m.ActionTimer += 1 + + if m.ActionTimer > 30 and m.Position.Y - m.FloorHeight > 500 then + return m:SetAction(Action.FREEFALL, 2) + end + + updateAirWithoutTurn(m) + stepResult = m:PerformAirStep() + + if stepResult == AirStep.NONE then + if m.ActionState == 0 then + local tilt = Util.Atan2s(m.ForwardVel, -m.Velocity.Y) + + if tilt > 0x1800 then + tilt = 0x1800 + end + + m.GfxAngle = Util.SetXint16(m.GfxAngle, tilt) + end + elseif stepResult == AirStep.LANDED then + if m.ActionState == 0 and m.Velocity.Y < 0 then + m.Velocity *= Vector3.new(1, -0.5, 1) + m.ActionState = 1 + m.ActionTimer = 0 + else + m:SetAction(Action.SLIDE_KICK_SLIDE) + end + + m:PlayLandingSound() + elseif stepResult == AirStep.HIT_WALL then + stopRising(m) + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + return false +end) + +DEF_ACTION(Action.JUMP_KICK, function(m: Mario) + local stepResult + + if m.ActionState == 0 then + m:PlaySoundIfNoFlag(Sounds.MARIO_PUNCH_HOO, MarioFlags.ACTION_SOUND_PLAYED) + m.AnimReset = true + + m:SetAnimation(Animations.AIR_KICK) + m.ActionState = 1 + end + + local animFrame = m.AnimFrame + + if animFrame == 0 then + m.BodyState.PunchType = 2 + m.BodyState.PunchTimer = 6 + end + + if animFrame >= 0 and animFrame < 8 then + m.Flags:Add(MarioFlags.KICKING) + end + + updateAirWithoutTurn(m) + stepResult = m:PerformAirStep() + + if stepResult == AirStep.LANDED then + if not checkFallDamage(m, Action.HARD_BACKWARD_GROUND_KB) then + m:SetAction(Action.FREEFALL_LAND) + end + elseif stepResult == AirStep.HIT_WALL then + m:SetForwardVel(0) + end + + return false +end) + +DEF_ACTION(Action.FLYING, function(m: Mario) + local startPitch = m.FaceAngle.X + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + if not m.Flags:Has(MarioFlags.WING_CAP) then + return m:SetAction(Action.FREEFALL) + end + + if m.ActionState == 0 then + if m.ActionArg == 0 then + m:SetAnimation(Animations.FLY_FROM_CANNON) + else + m:SetAnimation(Animations.FORWARD_SPINNING_FLIP) + + if m.AnimFrame == 1 then + m:PlaySound(Sounds.ACTION_SPIN) + end + end + + if m:IsAnimAtEnd() then + m:SetAnimation(Animations.WING_CAP_FLY) + m.ActionState = 1 + end + end + + local stepResult + do + updateFlying(m) + stepResult = m:PerformAirStep() + end + + if stepResult == AirStep.NONE then + local faceAngle = m.FaceAngle + m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) + m.GfxAngle = Util.SetZint16(m.GfxAngle, m.FaceAngle.Z) + m.ActionTimer = 0 + elseif stepResult == AirStep.LANDED then + m:SetAction(Action.DIVE_SLIDE) + m:SetAnimation(Animations.DIVE) + + m:SetAnimToFrame(7) + m.FaceAngle *= Vector3int16.new(0, 1, 1) + elseif stepResult == AirStep.HIT_WALL then + if m.Wall then + m:SetForwardVel(-16) + m.FaceAngle *= Vector3int16.new(0, 1, 1) + + stopRising(m) + m:PlaySound(if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_BONK else Sounds.ACTION_BONK) + + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_AIR_KB) + else + m.ActionTimer += 1 + + if m.ActionTimer == 0 then + m:PlaySound(Sounds.ACTION_HIT) + end + + if m.ActionTimer == 30 then + m.ActionTimer = 0 + end + + m.FaceAngle -= Vector3int16.new(0x200, 0, 0) + + if m.FaceAngle.X < -0x2AAA then + m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + end + + m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) + m.GfxAngle = Util.SetZint16(m.GfxAngle, m.FaceAngle.Z) + end + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + if m.FaceAngle.X > 0x800 and m.ForwardVel >= 48 then + m.ParticleFlags:Add(ParticleFlags.DUST) + end + + if startPitch <= 0 and m.FaceAngle.X > 0 and m.ForwardVel >= 48 then + m:PlaySound(Sounds.ACTION_FLYING_FAST) + m:PlaySound(Sounds.MARIO_YAHOO_WAHA_YIPPEE) + end + + m:PlaySound(Sounds.MOVING_FLYING) + m:AdjustSoundForSpeed() + + return false +end) + +DEF_ACTION(Action.FLYING_TRIPLE_JUMP, function(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.DIVE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.GROUND_POUND) + end + + m:PlayMarioSound(Sounds.ACTION_TERRAIN_JUMP, Sounds.MARIO_YAHOO) + + if m.ActionState == 0 then + m:SetAnimation(Animations.TRIPLE_JUMP_FLY) + + if m.AnimFrame == 7 then + m:PlaySound(Sounds.ACTION_SPIN) + end + + if m:IsAnimPastEnd() then + m:SetAnimation(Animations.FORWARD_SPINNING) + m.ActionState = 1 + end + end + + if m.ActionState == 1 and m.AnimFrame == 1 then + m:PlaySound(Sounds.ACTION_SPIN) + end + + if m.Velocity.Y < 4 then + if m.ForwardVel < 32 then + m:SetForwardVel(32) + end + + m:SetAction(Action.FLYING, 1) + end + + m.ActionTimer += 1 + + local stepResult + do + updateAirWithoutTurn(m) + stepResult = m:PerformAirStep() + end + + if stepResult == AirStep.LANDED then + if not checkFallDamage(m, Action.HARD_BACKWARD_GROUND_KB) then + m:SetAction(Action.DOUBLE_JUMP_LAND) + end + elseif stepResult == AirStep.HIT_WALL then + m:BonkReflection() + elseif stepResult == AirStep.HIT_LAVA_WALL then + lavaBoostOnWall(m) + end + + return false +end) + +DEF_ACTION(Action.SPAWN_SPIN_AIRBORNE, function(m: Mario) + m:SetForwardVel(m.ForwardVel) + + if m:PerformAirStep() == AirStep.LANDED then + m:PlayLandingSound(Sounds.ACTION_TERRAIN_LANDING) + m:SetAction(Action.SPAWN_SPIN_LANDING) + end + + if m.ActionState == 0 and m.Position.Y - m.FloorHeight > 300 then + if m:SetAnimation(Animations.FORWARD_SPINNING) == 0 then + m:PlaySound(Sounds.ACTION_SPIN) + end + else + m.ActionState = 1 + m:SetAnimation(Animations.GENERAL_FALL) + end + + return false +end) + +DEF_ACTION(Action.SPAWN_SPIN_LANDING, function(m: Mario) + m:StopAndSetHeightToFloor() + m:SetAnimation(Animations.GENERAL_LAND) + + if m:IsAnimAtEnd() then + m:SetAction(Action.IDLE) + end + + return false +end) + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/sm64/Mario/Automatic.client.lua b/sm64/Mario/Automatic.client.lua new file mode 100644 index 0000000..b444120 --- /dev/null +++ b/sm64/Mario/Automatic.client.lua @@ -0,0 +1,169 @@ +--!strict + +local System = require(script.Parent) +local Animations = System.Animations +local Sounds = System.Sounds +local Enums = System.Enums +local Util = System.Util + +local Action = Enums.Action +local ActionFlags = Enums.ActionFlags +local ActionGroup = Enums.ActionGroups + +local AirStep = Enums.AirStep +local MarioEyes = Enums.MarioEyes +local InputFlags = Enums.InputFlags +local MarioFlags = Enums.MarioFlags +local ParticleFlags = Enums.ParticleFlags + +type Mario = System.Mario + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helpers +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local function letGoOfLedge(m: Mario) + local floorHeight + m.Velocity *= Vector3.new(1, 0, 1) + m.ForwardVel = -8 + + local x = 60 * Util.Sins(m.FaceAngle.Y) + local z = 60 * Util.Coss(m.FaceAngle.Y) + + m.Position -= Vector3.new(x, 0, z) + floorHeight = Util.FindFloor(m.Position) + + if floorHeight < m.Position.Y - 100 then + m.Position -= (Vector3.yAxis * 100) + else + m.Position = Util.SetY(m.Position, floorHeight) + end + + return m:SetAction(Action.SOFT_BONK) +end + +local function climbUpLedge(m: Mario) + local x = 14 * Util.Sins(m.FaceAngle.Y) + local z = 14 * Util.Coss(m.FaceAngle.Y) + + m:SetAnimation(Animations.IDLE_HEAD_LEFT) + m.Position += Vector3.new(x, 0, z) +end + +local function updateLedgeClimb(m: Mario, anim: Animation, endAction: number) + m:StopAndSetHeightToFloor() + m:SetAnimation(anim) + + if m:IsAnimAtEnd() then + m:SetAction(endAction) + + if endAction == Action.IDLE then + climbUpLedge(m) + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Actions +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local DEF_ACTION: (number, (Mario) -> boolean) -> () = System.RegisterAction + +DEF_ACTION(Action.LEDGE_GRAB, function(m: Mario) + local intendedDYaw = m.IntendedYaw - m.FaceAngle.Y + local hasSpaceForMario = m.CeilHeight - m.FloorHeight >= 160 + + if m.ActionTimer < 10 then + m.ActionTimer += 1 + end + + if m.Floor and m.Floor.Normal.Y < 0.9063078 then + return letGoOfLedge(m) + end + + if m.Input:Has(InputFlags.Z_PRESSED, InputFlags.OFF_FLOOR) then + return letGoOfLedge(m) + end + + if m.Input:Has(InputFlags.A_PRESSED) and hasSpaceForMario then + return m:SetAction(Action.LEDGE_CLIMB_FAST) + end + + if m.Input:Has(InputFlags.STOMPED) then + return letGoOfLedge(m) + end + + if m.ActionTimer == 10 and m.Input:Has(InputFlags.NONZERO_ANALOG) then + if math.abs(intendedDYaw) <= 0x4000 then + if hasSpaceForMario then + return m:SetAction(Action.LEDGE_CLIMB_SLOW) + end + else + return letGoOfLedge(m) + end + end + + local heightAboveFloor = m.Position.Y - m:FindFloorHeightRelativePolar(-0x8000, 30) + + if hasSpaceForMario and heightAboveFloor < 100 then + return m:SetAction(Action.LEDGE_CLIMB_FAST) + end + + if m.ActionArg == 0 then + m:PlaySoundIfNoFlag(Sounds.MARIO_WHOA, MarioFlags.MARIO_SOUND_PLAYED) + end + + m:StopAndSetHeightToFloor() + m:SetAnimation(Animations.IDLE_ON_LEDGE) + + return false +end) + +DEF_ACTION(Action.LEDGE_CLIMB_SLOW, function(m: Mario) + if m.Input:Has(InputFlags.OFF_FLOOR) then + return letGoOfLedge(m) + end + + if m.ActionTimer >= 28 then + if + m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) + then + climbUpLedge(m) + return m:CheckCommonActionExits() + end + end + + if m.ActionTimer == 10 then + m:PlaySoundIfNoFlag(Sounds.MARIO_EEUH, MarioFlags.MARIO_SOUND_PLAYED) + end + + updateLedgeClimb(m, Animations.SLOW_LEDGE_GRAB, Action.IDLE) + return false +end) + +DEF_ACTION(Action.LEDGE_CLIMB_DOWN, function(m: Mario) + if m.Input:Has(InputFlags.OFF_FLOOR) then + return letGoOfLedge(m) + end + + m:PlaySoundIfNoFlag(Sounds.MARIO_WHOA, MarioFlags.MARIO_SOUND_PLAYED) + updateLedgeClimb(m, Animations.CLIMB_DOWN_LEDGE, Action.LEDGE_GRAB) + + m.ActionArg = 1 + return false +end) + +DEF_ACTION(Action.LEDGE_CLIMB_FAST, function(m: Mario) + if m.Input:Has(InputFlags.OFF_FLOOR) then + return letGoOfLedge(m) + end + + m:PlaySoundIfNoFlag(Sounds.MARIO_UH2, MarioFlags.MARIO_SOUND_PLAYED) + updateLedgeClimb(m, Animations.FAST_LEDGE_GRAB, Action.IDLE) + + if m.AnimFrame == 8 then + m:PlayLandingSound(Sounds.ACTION_TERRAIN_LANDING) + end + + return false +end) diff --git a/sm64/Mario/Moving.client.lua b/sm64/Mario/Moving.client.lua new file mode 100644 index 0000000..de1ed76 --- /dev/null +++ b/sm64/Mario/Moving.client.lua @@ -0,0 +1,1496 @@ +--!strict + +local System = require(script.Parent) +local Animations = System.Animations +local Sounds = System.Sounds +local Enums = System.Enums +local Util = System.Util + +local Action = Enums.Action +local ActionFlags = Enums.ActionFlags +local ActionGroup = Enums.ActionGroups + +local MarioEyes = Enums.MarioEyes +local GroundStep = Enums.GroundStep +local InputFlags = Enums.InputFlags +local MarioFlags = Enums.MarioFlags +local SurfaceClass = Enums.SurfaceClass +local ParticleFlags = Enums.ParticleFlags + +type Mario = System.Mario + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Landing Actions +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +type LandingAction = { + NumFrames: number, + JumpTimer: number, + EndAction: number, + APressedAction: number, +} + +local sJumpLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 5, + + EndAction = Action.JUMP_LAND_STOP, + APressedAction = Action.DOUBLE_JUMP, +} + +local sFreefallLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 5, + + EndAction = Action.FREEFALL_LAND_STOP, + APressedAction = Action.DOUBLE_JUMP, +} + +local sSideFlipLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 5, + + EndAction = Action.SIDE_FLIP_LAND_STOP, + APressedAction = Action.DOUBLE_JUMP, +} + +local sLongJumpLandAction: LandingAction = { + NumFrames = 6, + JumpTimer = 5, + + EndAction = Action.LONG_JUMP_LAND_STOP, + APressedAction = Action.LONG_JUMP, +} + +local sDoubleJumpLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 5, + + EndAction = Action.DOUBLE_JUMP_LAND_STOP, + APressedAction = Action.JUMP, +} + +local sTripleJumpLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 0, + + EndAction = Action.TRIPLE_JUMP_LAND_STOP, + APressedAction = Action.UNINITIALIZED, +} + +local sBackflipLandAction: LandingAction = { + NumFrames = 4, + JumpTimer = 0, + + EndAction = Action.BACKFLIP_LAND_STOP, + APressedAction = Action.BACKFLIP, +} + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helpers +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local DEF_ACTION: (number, (Mario) -> boolean) -> () = System.RegisterAction +local sPunchingForwardVelocities = { 0, 1, 1, 2, 3, 5, 7, 10 } + +local function tiltBodyRunning(m: Mario) + local pitch = m:FindFloorSlope(0) + pitch = pitch * m.ForwardVel / 40 + + return -pitch +end + +local function playStepSound(m: Mario, frame1: number, frame2: number) + if m:IsAnimPastFrame(frame1) or m:IsAnimPastFrame(frame2) then + if m.Flags:Has(MarioFlags.METAL_CAP) then + m:PlaySoundAndSpawnParticles(Sounds.ACTION_METAL_STEP, 0) + else + m:PlaySoundAndSpawnParticles(Sounds.ACTION_TERRAIN_STEP, 0) + end + end +end + +local function alignWithFloor(m: Mario) + local pos = Util.SetY(m.Position, m.FloorHeight) + m.Position = pos + + local radius = 40 + local minY = -radius * 3 + local yaw = m.FaceAngle.Y + + local p0_x = pos.X + radius * Util.Sins(yaw + 0x2AAA) + local p0_z = pos.Z + radius * Util.Coss(yaw + 0x2AAA) + + local p1_x = pos.X + radius * Util.Sins(yaw + 0x8000) + local p1_z = pos.Z + radius * Util.Coss(yaw + 0x8000) + + local p2_x = pos.X + radius * Util.Sins(yaw + 0xD555) + local p2_z = pos.Z + radius * Util.Coss(yaw + 0xD555) + + local test0 = Vector3.new(p0_x, pos.Y + 150, p0_z) + local test1 = Vector3.new(p1_x, pos.Y + 150, p1_z) + local test2 = Vector3.new(p2_x, pos.Y + 150, p2_z) + + local p0_y = Util.FindFloor(test0) + local p1_y = Util.FindFloor(test1) + local p2_y = Util.FindFloor(test2) + + p0_y = p0_y - pos.Y < minY and pos.Y or p0_y + p1_y = p1_y - pos.Y < minY and pos.Y or p1_y + p2_y = p2_y - pos.Y < minY and pos.Y or p2_y + + local avgY = (p0_y + p1_y + p2_y) / 3 + local forward = Vector3.new(Util.Sins(yaw), 0, Util.Coss(yaw)) + + if avgY >= pos.Y then + pos = Util.SetY(pos, avgY) + end + + local a = Vector3.new(p0_x, p0_y, p0_z) + local b = Vector3.new(p1_x, p1_y, p1_z) + local c = Vector3.new(p2_x, p2_y, p2_z) + + local yColumn = (b - a):Cross(c - a).Unit + local xColumn = yColumn:Cross(forward).Unit + m.ThrowMatrix = CFrame.fromMatrix(pos, xColumn, yColumn) +end + +local function beginWalkingAction(m: Mario, forwardVel: number, action: number, actionArg: number?) + m:SetForwardVel(forwardVel) + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + return m:SetAction(action, actionArg) +end + +local function checkLedgeClimbDown(m: Mario) + if m.ForwardVel < 10 then + local pos, wall = Util.FindWallCollisions(m.Position, -10, 10) + + if wall then + local floorHeight, floor = Util.FindFloor(pos) + + if floor and pos.Y - floorHeight > 160 then + local wallAngle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) + local wallDYaw = wallAngle - m.FaceAngle.Y + + if math.abs(wallDYaw) < 0x4000 then + pos -= Vector3.new(20 * wall.Normal.X, 0, 20 * wall.Normal.Z) + m.Position = pos + + m.FaceAngle *= Vector3int16.new(0, 1, 1) + m.FaceAngle = Util.SetYint16(m.FaceAngle, wallAngle + 0x8000) + + m:SetAction(Action.LEDGE_CLIMB_DOWN) + m:SetAnimation(Animations.CLIMB_DOWN_LEDGE) + end + end + end + end +end + +local function slideBonk(m: Mario, fastAction: number, slowAction: number) + if m.ForwardVel > 16 then + m:BonkReflection(true) + m:SetAction(fastAction) + else + m:SetForwardVel(0) + m:SetAction(slowAction) + end +end + +local function setTripleJumpAction(m: Mario) + if m.Flags:Has(MarioFlags.WING_CAP) then + return m:SetAction(Action.FLYING_TRIPLE_JUMP) + elseif m.ForwardVel > 20 then + return m:SetAction(Action.TRIPLE_JUMP) + else + return m:SetAction(Action.JUMP) + end +end + +local function updateSlidingAngle(m: Mario, accel: number, lossFactor: number) + local newFacingDYaw + local facingDYaw + + local floor = m.Floor + + if not floor then + return + end + + assert(floor) + + local slopeAngle = Util.Atan2s(floor.Normal.Z, floor.Normal.X) + local steepness = math.sqrt(floor.Normal.X ^ 2 + floor.Normal.Z ^ 2) + + m.SlideVelX += accel * steepness * Util.Sins(slopeAngle) + m.SlideVelZ += accel * steepness * Util.Coss(slopeAngle) + + m.SlideVelX *= lossFactor + m.SlideVelZ *= lossFactor + + m.SlideYaw = Util.Atan2s(m.SlideVelZ, m.SlideVelX) + + facingDYaw = m.FaceAngle.Y - m.SlideYaw + newFacingDYaw = facingDYaw + + if newFacingDYaw > 0 and newFacingDYaw <= 0x4000 then + newFacingDYaw -= 0x200 + + if newFacingDYaw < 0 then + newFacingDYaw = 0 + end + elseif newFacingDYaw > -0x4000 and newFacingDYaw < 0 then + newFacingDYaw += 0x200 + + if newFacingDYaw > 0 then + newFacingDYaw = 0 + end + elseif newFacingDYaw > 0x4000 and newFacingDYaw < 0x8000 then + newFacingDYaw += 0x200 + + if newFacingDYaw > 0x8000 then + newFacingDYaw = 0x8000 + end + elseif newFacingDYaw > -0x8000 and newFacingDYaw < -0x4000 then + newFacingDYaw -= 0x200 + + if newFacingDYaw < -0x8000 then + newFacingDYaw = -0x8000 + end + end + + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.SlideYaw + newFacingDYaw) + m.Velocity = Vector3.new(m.SlideVelX, 0, m.SlideVelZ) + + --! Speed is capped a frame late (butt slide HSG) + m.ForwardVel = math.sqrt(m.SlideVelX ^ 2 + m.SlideVelZ ^ 2) + + if m.ForwardVel > 100 then + m.SlideVelX = m.SlideVelX * 100 / m.ForwardVel + m.SlideVelZ = m.SlideVelZ * 100 / m.ForwardVel + end + + if newFacingDYaw < -0x4000 or newFacingDYaw > 0x4000 then + m.ForwardVel *= -1 + end +end + +local function updateSliding(m: Mario, stopSpeed: number) + local intendedDYaw = m.IntendedYaw - m.SlideYaw + local forward = Util.Coss(intendedDYaw) + local sideward = Util.Sins(intendedDYaw) + + --! 10k glitch + if forward < 0 and m.ForwardVel > 0 then + forward *= 0.5 + 0.5 * m.ForwardVel / 100 + end + + local floorClass = m:GetFloorClass() + local lossFactor + local accel + + if floorClass == SurfaceClass.VERY_SLIPPERY then + accel = 10 + lossFactor = m.IntendedMag / 32 * forward * 0.02 + 0.98 + elseif floorClass == SurfaceClass.SLIPPERY then + accel = 8 + lossFactor = m.IntendedMag / 32 * forward * 0.02 + 0.96 + elseif floorClass == SurfaceClass.DEFAULT then + accel = 7 + lossFactor = m.IntendedMag / 32 * forward * 0.02 + 0.92 + elseif floorClass == SurfaceClass.NOT_SLIPPERY then + accel = 5 + lossFactor = m.IntendedMag / 32 * forward * 0.02 + 0.92 + end + + local oldSpeed = math.sqrt(m.SlideVelX ^ 2 + m.SlideVelZ ^ 2) + + --! This is attempting to use trig derivatives to rotate Mario's speed. + -- It is slightly off/asymmetric since it uses the new X speed, but the old + -- Z speed. + + m.SlideVelX += m.SlideVelZ * (m.IntendedMag / 32) * sideward * 0.05 + m.SlideVelZ -= m.SlideVelX * (m.IntendedMag / 32) * sideward * 0.05 + + local newSpeed = math.sqrt(m.SlideVelX ^ 2 + m.SlideVelZ ^ 2) + + if oldSpeed > 0 and newSpeed > 0 then + m.SlideVelX *= oldSpeed / newSpeed + m.SlideVelZ *= oldSpeed / newSpeed + end + + local stopped = false + updateSlidingAngle(m, accel, lossFactor) + + if not m:FloorIsSlope() and m.ForwardVel ^ 2 < stopSpeed ^ 2 then + m:SetForwardVel(0) + stopped = true + end + + return stopped +end + +local function applySlopeAccel(m: Mario) + local floor = m.Floor + local floorNormal: Vector3 + + if floor then + floorNormal = floor.Normal + else + floorNormal = Vector3.yAxis + end + + local floorDYaw = m.FloorAngle - m.FaceAngle.Y + local steepness = math.sqrt(floorNormal.X ^ 2 + floorNormal.Z ^ 2) + + if m:FloorIsSlope() then + local slopeClass = 0 + local slopeAccel + + if m.Action() ~= Action.SOFT_BACKWARD_GROUND_KB then + if m.Action() ~= Action.SOFT_FORWARD_GROUND_KB then + slopeClass = m:GetFloorClass() + end + end + + if slopeClass == SurfaceClass.VERY_SLIPPERY then + slopeAccel = 5.3 + elseif slopeClass == SurfaceClass.SLIPPERY then + slopeAccel = 2.7 + elseif slopeClass == SurfaceClass.DEFAULT then + slopeAccel = 1.7 + else + slopeAccel = 0 + end + + if floorDYaw > -0x4000 and floorDYaw < 0x4000 then + m.ForwardVel += slopeAccel * steepness + else + m.ForwardVel -= slopeAccel * steepness + end + end + + m.SlideYaw = m.FaceAngle.Y + m.SlideVelX = m.ForwardVel * Util.Sins(m.FaceAngle.Y) + m.SlideVelZ = m.ForwardVel * Util.Coss(m.FaceAngle.Y) + m.Velocity = Vector3.new(m.SlideVelX, 0, m.SlideVelZ) +end + +local function applyLandingAccel(m: Mario, frictionFactor: number) + local stopped = false + applySlopeAccel(m) + + if not m:FloorIsSlope() then + m.ForwardVel *= frictionFactor + + if m.ForwardVel ^ 2 < 1 then + m:SetForwardVel(0) + stopped = true + end + end + + return stopped +end + +local function applySlopeDecel(m: Mario, decelCoef: number) + local decel + local stopped = false + local floorClass = m:GetFloorClass() + + if floorClass == SurfaceClass.VERY_SLIPPERY then + decel = decelCoef * 0.2 + elseif floorClass == SurfaceClass.SLIPPERY then + decel = decelCoef * 0.7 + elseif floorClass == SurfaceClass.DEFAULT then + decel = decelCoef * 2 + elseif floorClass == SurfaceClass.NOT_SLIPPERY then + decel = decelCoef * 3 + end + + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, decel) + + if m.ForwardVel == 0 then + stopped = true + end + + applySlopeAccel(m) + return stopped +end + +local function updateDeceleratingSpeed(m: Mario) + local stopped = false + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 1) + + if m.ForwardVel == 0 then + stopped = true + end + + m:SetForwardVel(m.ForwardVel) + return stopped +end + +local function updateWalkingSpeed(m: Mario) + local maxTargetSpeed = 32 + local floor = m.Floor + + local targetSpeed = if m.IntendedMag < maxTargetSpeed then m.IntendedMag else maxTargetSpeed + + if m.ForwardVel < 0 then + m.ForwardVel += 1.1 + elseif m.ForwardVel <= targetSpeed then + m.ForwardVel += 1.1 - m.ForwardVel / 43 + elseif floor and floor.Normal.Y >= 0.95 then + m.ForwardVel -= 1 + end + + if m.ForwardVel > 48 then + m.ForwardVel = 48 + end + + local currY = Util.SignedShort(m.IntendedYaw - m.FaceAngle.Y) + local faceY = m.IntendedYaw - Util.ApproachInt(currY, 0, 0x800) + + m.FaceAngle = Util.SetYint16(m.FaceAngle, faceY) + applySlopeAccel(m) +end + +local function shouldBeginSliding(m: Mario) + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + if m.ForwardVel < -1 or m:FacingDownhill() then + return true + end + end + + return false +end + +local function analogStickHeldBack(m: Mario) + local intendedDYaw = Util.SignedShort(m.IntendedYaw - m.FaceAngle.Y) + return math.abs(intendedDYaw) > 0x471C +end + +local function checkGroundDiveOrPunch(m: Mario) + if m.Input:Has(InputFlags.B_PRESSED) then + --! Speed kick (shoutouts to SimpleFlips) + if m.ForwardVel >= 29 and m.Controller.StickMag > 48 then + m.Velocity = Util.SetY(m.Velocity, 20) + return m:SetAction(Action.DIVE, 1) + end + + return m:SetAction(Action.MOVE_PUNCHING) + end + + return false +end + +local function beginBrakingAction(m: Mario) + if m.ActionState == 1 then + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.ActionArg) + return m:SetAction(Action.STANDING_AGAINST_WALL) + end + + if m.ForwardVel > 16 then + local floor = m.Floor + + if floor and floor.Normal.Y >= 0.17364818 then + return m:SetAction(Action.BRAKING) + end + end + + return m:SetAction(Action.DECELERATING) +end + +local function animAndAudioForWalk(m: Mario) + local baseAccel = if m.IntendedMag > m.ForwardVel then m.IntendedMag else m.ForwardVel + + if baseAccel < 4 then + baseAccel = 4 + end + + local targetPitch = 0 + local accel + + while true do + if m.ActionTimer == 0 then + if baseAccel > 8 then + m.ActionTimer = 2 + else + accel = baseAccel / 4 * 0x10000 + + if accel < 0x1000 then + accel = 0x1000 + end + + m:SetAnimationWithAccel(Animations.START_TIPTOE, accel) + playStepSound(m, 7, 22) + + if m:IsAnimPastFrame(23) then + m.ActionTimer = 2 + end + + break + end + elseif m.ActionTimer == 1 then + if baseAccel > 8 then + m.ActionTimer = 2 + else + accel = baseAccel * 0x10000 + + if accel < 0x1000 then + accel = 0x1000 + end + + m:SetAnimationWithAccel(Animations.TIPTOE, accel) + playStepSound(m, 14, 72) + + break + end + elseif m.ActionTimer == 2 then + if baseAccel < 5 then + m.ActionTimer = 1 + elseif baseAccel > 22 then + m.ActionTimer = 3 + else + accel = baseAccel / 4 * 0x10000 + m:SetAnimationWithAccel(Animations.WALKING, accel) + playStepSound(m, 10, 49) + break + end + elseif m.ActionTimer == 3 then + if baseAccel < 18 then + m.ActionTimer = 2 + else + accel = baseAccel / 4 * 0x10000 + m:SetAnimationWithAccel(Animations.RUNNING, accel) + + playStepSound(m, 9, 45) + targetPitch = tiltBodyRunning(m) + + break + end + end + end + + local walkingPitch = Util.ApproachInt(m.WalkingPitch, targetPitch, 0x800) + walkingPitch = Util.SignedShort(walkingPitch) + + m.WalkingPitch = walkingPitch + m.GfxAngle = Util.SetXint16(m.GfxAngle, walkingPitch) +end + +local function pushOrSidleWall(m: Mario, startPos: Vector3) + local wallAngle: number + local dWallAngle: number + + local dx = m.Position.X - startPos.X + local dz = m.Position.Z - startPos.Z + + local movedDist = math.sqrt(dx ^ 2 + dz ^ 2) + local accel = movedDist * 2 * 0x10000 + + if m.ForwardVel > 6 then + m:SetForwardVel(6) + end + + local wall = m.Wall + + if wall then + wallAngle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) + dWallAngle = Util.SignedShort(assert(wallAngle) - m.FaceAngle.Y) + end + + if wall == nil or math.abs(dWallAngle) >= 0x71C8 then + m:SetAnimation(Animations.PUSHING) + playStepSound(m, 6, 18) + else + if dWallAngle < 0 then + m:SetAnimationWithAccel(Animations.SIDESTEP_RIGHT, accel) + else + m:SetAnimationWithAccel(Animations.SIDESTEP_LEFT, accel) + end + + if m.AnimFrame < 20 then + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + m.ParticleFlags:Add(ParticleFlags.DUST) + end + + m.ActionState = 1 + m.ActionArg = Util.SignedShort(wallAngle + 0x8000) + + m.GfxAngle = Util.SetYint16(m.GfxAngle, m.ActionArg) + m.GfxAngle = Util.SetZint16(m.GfxAngle, m:FindFloorSlope(0x4000)) + end +end + +local function tiltBodyWalking(m: Mario, startYaw: number) + local anim = m.AnimCurrent + local bodyState = m.BodyState + + if anim == Animations.WALKING or anim == Animations.RUNNING then + local dYaw = m.FaceAngle.Y - startYaw + + local tiltZ = -math.clamp(dYaw * m.ForwardVel / 12, -0x1555, 0x1555) + local tiltX = math.clamp(m.ForwardVel * 170, 0, 0x1555) + + local torsoAngle = bodyState.TorsoAngle + tiltZ = Util.ApproachInt(torsoAngle.Z, tiltZ, 0x400) + tiltX = Util.ApproachInt(torsoAngle.X, tiltX, 0x400) + + bodyState.TorsoAngle = Vector3int16.new(tiltX, torsoAngle.Y, tiltZ) + else + bodyState.TorsoAngle *= Vector3int16.new(0, 1, 0) + end +end + +local function tiltBodyButtSlide(m: Mario) + local intendedDYaw = m.IntendedYaw - m.FaceAngle.Y + local bodyState = m.BodyState + + local tiltX = 5461.3335 * m.IntendedMag / 32 * Util.Coss(intendedDYaw) + local tiltZ = -(5461.3335 * m.IntendedMag / 32 * Util.Sins(intendedDYaw)) + + local torsoAngle = bodyState.TorsoAngle + bodyState.TorsoAngle = Vector3int16.new(tiltX, torsoAngle.Y, tiltZ) +end + +local function commonSlideAction(m: Mario, endAction: number, airAction: number, anim: Animation) + local pos = m.Position + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + m:AdjustSoundForSpeed() + + local step = m:PerformGroundStep() + + if step == GroundStep.LEFT_GROUND then + m:SetAction(airAction) + + if math.abs(m.ForwardVel) >= 50 then + m:PlaySound(Sounds.MARIO_HOOHOO) + end + elseif step == GroundStep.NONE then + m:SetAnimation(anim) + alignWithFloor(m) + + m.ParticleFlags:Add(ParticleFlags.DUST) + elseif step == GroundStep.HIT_WALL then + local wall = m.Wall + + if not m:FloorIsSlippery() then + if m.ForwardVel > 16 then + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + end + + slideBonk(m, Action.GROUND_BONK, endAction) + elseif wall then + local wallAngle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) + local slideSpeed = math.sqrt(m.SlideVelX ^ 2 * m.SlideVelZ ^ 2) * 0.9 + + if slideSpeed < 4 then + slideSpeed = 4 + end + + local slideYaw = Util.SignedShort(m.SlideYaw - wallAngle) + m.SlideYaw = Util.SignedShort(wallAngle - slideYaw + 0x8000) + m.SlideVelX = slideSpeed * Util.Sins(m.SlideYaw) + m.SlideVelZ = slideSpeed * Util.Coss(m.SlideYaw) + m.Velocity = Vector3.new(m.SlideVelX, m.Velocity.Y, m.SlideVelZ) + end + + alignWithFloor(m) + end +end + +local function commonSlideActionWithJump(m: Mario, stopAction: number, airAction: number, anim: Animation) + if m.ActionTimer == 5 then + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetJumpingAction(Action.JUMP) + end + else + m.ActionTimer += 1 + end + + if updateSliding(m, 4) then + m:SetAction(stopAction) + end + + commonSlideAction(m, stopAction, airAction, anim) + return false +end + +local function commonLandingCancels( + m: Mario, + landingAction: LandingAction, + setAPressAction: (Mario, number, any) -> any +) + local floor = m.Floor + + if floor and floor.Normal.Y < 0.2923717 then + return m:PushOffSteepFloor(Action.FREEFALL) + end + + m.DoubleJumpTimer = landingAction.JumpTimer + + if shouldBeginSliding(m) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.FIRST_PERSON) then + return m:SetAction(landingAction.EndAction) + end + + m.ActionTimer += 1 + + if m.ActionTimer >= landingAction.NumFrames then + return m:SetAction(landingAction.EndAction) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return setAPressAction(m, landingAction.APressedAction, 0) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + return false +end + +local function stomachSlideAction(m: Mario, stopAction: number, airAction: number, anim: Animation) + if m.ActionTimer == 5 then + if not m.Input:Has(InputFlags.ABOVE_SLIDE) and m.Input:Has(InputFlags.A_PRESSED, InputFlags.B_PRESSED) then + return m:SetAction(if m.ForwardVel >= 0 then Action.FORWARD_ROLLOUT else Action.BACKWARD_ROLLOUT) + end + else + m.ActionTimer += 1 + end + + if updateSliding(m, 4) then + return m:SetAction(stopAction) + end + + commonSlideAction(m, stopAction, airAction, anim) + return false +end + +local function commonGroundKnockbackAction( + m: Mario, + anim: Animation, + minFrame: number, + playHeavyLanding: boolean, + attacked: number +) + local animFrame + + if playHeavyLanding then + m:PlayHeavyLandingSoundOnce(Sounds.ACTION_TERRAIN_BODY_HIT_GROUND) + end + + if attacked > 0 then + m:PlaySoundIfNoFlag(Sounds.MARIO_ATTACKED, MarioFlags.MARIO_SOUND_PLAYED) + else + m:PlaySoundIfNoFlag(Sounds.MARIO_OOOF, MarioFlags.MARIO_SOUND_PLAYED) + end + + m.ForwardVel = math.clamp(m.ForwardVel, -32, 32) + animFrame = m:SetAnimation(anim) + + if animFrame < minFrame then + applyLandingAccel(m, 0.9) + elseif m.ForwardVel > 0 then + m:SetForwardVel(0.1) + else + m:SetForwardVel(-0.1) + end + + if m:PerformGroundStep() == GroundStep.LEFT_GROUND then + if m.ForwardVel >= 0 then + m:SetAction(Action.FORWARD_AIR_KB, attacked) + else + m:SetAction(Action.BACKWARD_AIR_KB, attacked) + end + elseif m:IsAnimAtEnd() then + if m.Health < 0x100 then + m:SetAction(Action.STANDING_DEATH) + else + if attacked > 0 then + m.InvincTimer = 30 + end + + m:SetAction(Action.IDLE) + end + end + + return animFrame +end + +local function commonLandingAction(m: Mario, anim: Animation) + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + applyLandingAccel(m, 0.98) + elseif m.ForwardVel > 16 then + applySlopeDecel(m, 2) + else + m.Velocity *= Vector3.new(1, 0, 1) + end + + local stepResult = m:PerformGroundStep() + + if stepResult == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif stepResult == GroundStep.HIT_WALL then + m:SetAnimation(Animations.PUSHING) + end + + if m.ForwardVel > 16 then + m.ParticleFlags:Add(ParticleFlags.DUST) + end + + m:SetAnimation(anim) + m:PlayLandingSoundOnce(Sounds.ACTION_TERRAIN_LANDING) + + return stepResult +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +DEF_ACTION(Action.WALKING, function(m: Mario) + local startPos + local startYaw = m.FaceAngle.Y + + if shouldBeginSliding(m) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.FIRST_PERSON) then + return beginBrakingAction(m) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetJumpFromLanding() + end + + if checkGroundDiveOrPunch(m) then + return true + end + + if m.Input:Has(InputFlags.UNKNOWN_5) then + return beginBrakingAction(m) + end + + if analogStickHeldBack(m) and m.ForwardVel >= 16 then + return m:SetAction(Action.TURNING_AROUND) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.CROUCH_SLIDE) + end + + local step + do + m.ActionState = 0 + startPos = m.Position + + updateWalkingSpeed(m) + step = m:PerformGroundStep() + end + + if step == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + m:SetAnimation(Animations.GENERAL_FALL) + elseif step == GroundStep.NONE then + animAndAudioForWalk(m) + + if m.IntendedMag - m.ForwardVel > 16 then + m.ParticleFlags:Add(ParticleFlags.DUST) + end + elseif step == GroundStep.HIT_WALL then + pushOrSidleWall(m, startPos) + m.ActionTimer = 0 + end + + checkLedgeClimbDown(m) + tiltBodyWalking(m, startYaw) + + return false +end) + +DEF_ACTION(Action.MOVE_PUNCHING, function(m: Mario) + if shouldBeginSliding(m) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.ActionState == 0 and m.Input:Has(InputFlags.A_DOWN) then + return m:SetAction(Action.JUMP_KICK) + end + + m.ActionState = 1 + m:UpdatePunchSequence() + + if m.ForwardVel > 0 then + applySlopeDecel(m, 0.5) + else + m.ForwardVel += 8 + + if m.ForwardVel >= 0 then + m.ForwardVel = 0 + end + + applySlopeAccel(m) + end + + local step = m:PerformGroundStep() + + if step == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif step == GroundStep.NONE then + m.ParticleFlags:Add(ParticleFlags.DUST) + end + + return false +end) + +DEF_ACTION(Action.TURNING_AROUND, function(m: Mario) + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.SIDE_FLIP) + end + + if not analogStickHeldBack(m) then + return m:SetAction(Action.WALKING) + end + + if applySlopeDecel(m, 2) then + return beginWalkingAction(m, 8, Action.FINISH_TURNING_AROUND) + end + + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + m:AdjustSoundForSpeed() + + local step = m:PerformGroundStep() + + if step == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif step == GroundStep.NONE then + m.ParticleFlags:Add(ParticleFlags.DUST) + end + + if m.ForwardVel >= 18 then + m:SetAnimation(Animations.TURNING_PART1) + else + m:SetAnimation(Animations.TURNING_PART2) + + if m:IsAnimAtEnd() then + if m.ForwardVel > 0 then + beginWalkingAction(m, -m.ForwardVel, Action.WALKING) + else + beginWalkingAction(m, 8, Action.WALKING) + end + + m.AnimSkipInterp = true + end + end + + return false +end) + +DEF_ACTION(Action.FINISH_TURNING_AROUND, function(m: Mario) + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.SIDE_FLIP) + end + + updateWalkingSpeed(m) + m:SetAnimation(Animations.TURNING_PART2) + + if m:PerformGroundStep() == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + end + + if m:IsAnimAtEnd() then + m:SetAction(Action.WALKING) + end + + m.GfxAngle += Vector3int16.new(0, 0x8000, 0) + return false +end) + +DEF_ACTION(Action.BRAKING, function(m: Mario) + if not m.Input:Has(InputFlags.FIRST_PERSON) then + if + m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) + then + return m:CheckCommonActionExits() + end + end + + if applySlopeDecel(m, 2) then + return m:SetAction(Action.BRAKING_STOP) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.MOVE_PUNCHING) + end + + local stepResult = m:PerformGroundStep() + + if stepResult == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif stepResult == GroundStep.NONE then + m.ParticleFlags:Add(ParticleFlags.DUST) + elseif stepResult == GroundStep.HIT_WALL then + slideBonk(m, Action.BACKWARD_GROUND_KB, Action.BRAKING_STOP) + end + + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + m:SetAnimation(Animations.SKID_ON_GROUND) + m:AdjustSoundForSpeed() + + return false +end) + +DEF_ACTION(Action.DECELERATING, function(m: Mario) + if not m.Input:Has(InputFlags.FIRST_PERSON) then + if shouldBeginSliding(m) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetJumpFromLanding() + end + + if checkGroundDiveOrPunch(m) then + return true + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + return m:SetAction(Action.CROUCH_SLIDE) + end + + if m.Input:Has(InputFlags.Z_PRESSED) then + return m:SetAction(Action.CROUCH_SLIDE) + end + end + + if updateDeceleratingSpeed(m) then + return m:SetAction(Action.IDLE) + end + + local slopeClass = m:GetFloorClass() + local stepResult = m:PerformGroundStep() + + if stepResult == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif stepResult == GroundStep.HIT_WALL then + if slopeClass == SurfaceClass.VERY_SLIPPERY then + m:BonkReflection(true) + else + m:SetForwardVel(0) + end + end + + if slopeClass == SurfaceClass.VERY_SLIPPERY then + m:SetAnimation(Animations.IDLE_HEAD_LEFT) + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + + m:AdjustSoundForSpeed() + m.ParticleFlags:Add(ParticleFlags.DUST) + else + local accel = m.ForwardVel / 4 * 0x10000 + + if accel < 0x1000 then + accel = 0x1000 + end + + m:SetAnimationWithAccel(Animations.WALKING, accel) + playStepSound(m, 10, 49) + end + + return false +end) + +DEF_ACTION(Action.CRAWLING, function(m: Mario) + if shouldBeginSliding(m) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.FIRST_PERSON) then + return m:SetAction(Action.STOP_CRAWLING) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetJumpingAction(Action.JUMP) + end + + if checkGroundDiveOrPunch(m) then + return true + end + + if m.Input:Has(InputFlags.UNKNOWN_5) then + return m:SetAction(Action.STOP_CRAWLING) + end + + if not m.Input:Has(InputFlags.Z_DOWN) then + return m:SetAction(Action.STOP_CRAWLING) + end + + m.IntendedMag *= 0.1 + updateWalkingSpeed(m) + + local stepResult = m:PerformGroundStep() + + if stepResult == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL) + elseif stepResult == GroundStep.HIT_WALL then + if m.ForwardVel > 10 then + m:SetForwardVel(10) + end + + alignWithFloor(m) + elseif stepResult == GroundStep.NONE then + alignWithFloor(m) + end + + local accel = m.IntendedMag * 2 * 0x10000 + m:SetAnimationWithAccel(Animations.CRAWLING, accel) + playStepSound(m, 26, 79) + + return false +end) + +DEF_ACTION(Action.BURNING_GROUND, function(m: Mario) + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.BURNING_JUMP) + end + + m.BurnTimer += 2 + + if m.BurnTimer > 160 then + return m:SetAction(Action.WALKING) + end + + if m.ForwardVel < 8 then + m.ForwardVel = 8 + end + + if m.ForwardVel > 48 then + m.ForwardVel = 48 + end + + m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 32, 4, 1) + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + local faceY = m.IntendedYaw - Util.ApproachFloat(m.IntendedYaw - m.FaceAngle.Y, 0, 0x600) + m.FaceAngle = Util.SetYint16(m.FaceAngle, faceY) + end + + applySlopeAccel(m) + + if m:PerformGroundStep() == GroundStep.LEFT_GROUND then + m:SetAction(Action.BURNING_FALL) + end + + local accel = m.ForwardVel / 2 * 0x10000 + m:SetAnimationWithAccel(Animations.RUNNING, accel) + playStepSound(m, 9, 45) + + m.ParticleFlags:Add(ParticleFlags.FIRE) + m:PlaySound(Sounds.MOVING_LAVA_BURN) + + m.Health -= 10 + + if m.Health < 0x100 then + m:SetAction(Action.STANDING_DEATH) + end + + m.BodyState.EyeState = MarioEyes.DEAD + return false +end) + +DEF_ACTION(Action.BUTT_SLIDE, function(m: Mario) + local cancel = commonSlideActionWithJump(m, Action.BUTT_SLIDE_STOP, Action.BUTT_SLIDE_AIR, Animations.SLIDE) + tiltBodyButtSlide(m) + + return cancel +end) + +DEF_ACTION(Action.CROUCH_SLIDE, function(m: Mario) + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BUTT_SLIDE) + end + + if m.ActionTimer < 30 then + m.ActionTimer += 1 + + if m.Input:Has(InputFlags.A_PRESSED) then + if m.ForwardVel > 10 then + return m:SetJumpingAction(Action.LONG_JUMP) + end + end + end + + if m.Input:Has(InputFlags.B_PRESSED) then + if m.ForwardVel >= 10 then + m:SetAction(Action.SLIDE_KICK) + else + m:SetAction(Action.MOVE_PUNCHING, 9) + end + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.JUMP) + end + + if m.Input:Has(InputFlags.FIRST_PERSON) then + return m:SetAction(Action.BRAKING) + end + + return commonSlideActionWithJump(m, Action.CROUCHING, Action.FREEFALL, Animations.START_CROUCHING) +end) + +DEF_ACTION(Action.SLIDE_KICK_SLIDE, function(m: Mario) + local step + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.FORWARD_ROLLOUT) + end + + m:SetAnimation(Animations.SLIDE_KICK) + + if m:IsAnimAtEnd() and m.ForwardVel < 1 then + return m:SetAction(Action.SLIDE_KICK_SLIDE_STOP) + end + + updateSliding(m, 1) + step = m:PerformGroundStep() + + if step == GroundStep.LEFT_GROUND then + m:SetAction(Action.FREEFALL, 2) + elseif step == GroundStep.HIT_WALL then + m:BonkReflection(true) + m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR) + m:SetAction(Action.BACKWARD_GROUND_KB) + end + + m:PlaySound(Sounds.MOVING_TERRAIN_SLIDE) + m.ParticleFlags:Add(ParticleFlags.DUST) + + return false +end) + +DEF_ACTION(Action.STOMACH_SLIDE, function(m: Mario) + if m.ActionTimer == 5 then + if not m.Input:Has(InputFlags.ABOVE_SLIDE) and m.Input:Has(InputFlags.A_PRESSED, InputFlags.B_PRESSED) then + return m:SetAction(if m.ForwardVel >= 0 then Action.FORWARD_ROLLOUT else Action.BACKWARD_ROLLOUT) + end + else + m.ActionTimer += 1 + end + + if updateSliding(m, 4) then + return m:SetAction(Action.STOMACH_SLIDE_STOP) + end + + commonSlideAction(m, Action.STOMACH_SLIDE_STOP, Action.FREEFALL, Animations.SLIDE_DIVE) + return false +end) + +DEF_ACTION(Action.DIVE_SLIDE, function(m: Mario) + if not m.Input:Has(InputFlags.ABOVE_SLIDE) and m.Input:Has(InputFlags.A_PRESSED, InputFlags.B_PRESSED) then + return m:SetAction(if m.ForwardVel >= 0 then Action.FORWARD_ROLLOUT else Action.BACKWARD_ROLLOUT) + end + + m:PlayLandingSoundOnce(Sounds.ACTION_TERRAIN_BODY_HIT_GROUND) + + if updateSliding(m, 8) and m:IsAnimAtEnd() then + m:SetForwardVel(0) + m:SetAction(Action.STOMACH_SLIDE_STOP) + end + + commonSlideAction(m, Action.STOMACH_SLIDE_STOP, Action.FREEFALL, Animations.DIVE) + return false +end) + +DEF_ACTION(Action.HARD_BACKWARD_GROUND_KB, function(m: Mario) + local animFrame = commonGroundKnockbackAction(m, Animations.FALL_OVER_BACKWARDS, 43, true, m.ActionArg) + + if animFrame == 43 and m.Health < 0x100 then + m:SetAction(Action.DEATH_ON_BACK) + end + + if animFrame == 54 and m.PrevAction() == Action.SPECIAL_DEATH_EXIT then + m:PlaySound(Sounds.MARIO_MAMA_MIA) + end + + if animFrame == 69 then + m:PlayLandingSoundOnce(Sounds.ACTION_TERRAIN_LANDING) + end + + return false +end) + +DEF_ACTION(Action.HARD_FORWARD_GROUND_KB, function(m: Mario) + local animFrame = commonGroundKnockbackAction(m, Animations.LAND_ON_STOMACH, 21, true, m.ActionArg) + + if animFrame == 23 and m.Health < 0x100 then + m:SetAction(Action.DEATH_ON_STOMACH) + end + + return false +end) + +DEF_ACTION(Action.BACKWARD_GROUND_KB, function(m: Mario) + commonGroundKnockbackAction(m, Animations.BACKWARD_KB, 22, true, m.ActionArg) + return false +end) + +DEF_ACTION(Action.FORWARD_GROUND_KB, function(m: Mario) + commonGroundKnockbackAction(m, Animations.FORWARD_KB, 20, true, m.ActionArg) + return false +end) + +DEF_ACTION(Action.SOFT_BACKWARD_GROUND_KB, function(m: Mario) + commonGroundKnockbackAction(m, Animations.SOFT_BACK_KB, 100, false, m.ActionArg) + return false +end) + +DEF_ACTION(Action.SOFT_FORWARD_GROUND_KB, function(m: Mario) + commonGroundKnockbackAction(m, Animations.SOFT_FRONT_KB, 100, false, m.ActionArg) + return false +end) + +DEF_ACTION(Action.GROUND_BONK, function(m: Mario) + local animFrame = commonGroundKnockbackAction(m, Animations.GROUND_BONK, 32, true, m.ActionArg) + + if animFrame == 32 then + m:PlayLandingSound(Sounds.ACTION_TERRAIN_LANDING) + end + + return false +end) + +DEF_ACTION(Action.JUMP_LAND, function(m: Mario) + if commonLandingCancels(m, sJumpLandAction, m.SetJumpingAction) then + return true + end + + commonLandingAction(m, Animations.LAND_FROM_SINGLE_JUMP) + return false +end) + +DEF_ACTION(Action.FREEFALL_LAND, function(m: Mario) + if commonLandingCancels(m, sFreefallLandAction, m.SetJumpingAction) then + return true + end + + commonLandingAction(m, Animations.GENERAL_LAND) + return false +end) + +DEF_ACTION(Action.SIDE_FLIP_LAND, function(m: Mario) + if commonLandingCancels(m, sSideFlipLandAction, m.SetJumpingAction) then + return true + end + + if commonLandingAction(m, Animations.SLIDEFLIP_LAND) ~= GroundStep.HIT_WALL then + --m.GfxAngle += Vector3int16.new(0, 0x8000, 0) + end + + return false +end) + +DEF_ACTION(Action.LONG_JUMP_LAND, function(m: Mario) + if not m.Input:Has(InputFlags.Z_DOWN) then + m.Input:Remove(InputFlags.A_PRESSED) + end + + if commonLandingCancels(m, sLongJumpLandAction, m.SetJumpingAction) then + return true + end + + if not m.Input:Has(InputFlags.NONZERO_ANALOG) then + m:PlaySoundIfNoFlag(Sounds.MARIO_UH, MarioFlags.MARIO_SOUND_PLAYED) + end + + commonLandingAction( + m, + if m.LongJumpIsSlow then Animations.CROUCH_FROM_FAST_LONGJUMP else Animations.CROUCH_FROM_SLOW_LONGJUMP + ) + + return false +end) + +DEF_ACTION(Action.DOUBLE_JUMP_LAND, function(m: Mario) + if commonLandingCancels(m, sDoubleJumpLandAction, setTripleJumpAction) then + return true + end + + commonLandingAction(m, Animations.LAND_FROM_DOUBLE_JUMP) + return false +end) + +DEF_ACTION(Action.TRIPLE_JUMP_LAND, function(m: Mario) + m.Input:Remove(InputFlags.A_PRESSED) + + if commonLandingCancels(m, sTripleJumpLandAction, m.SetJumpingAction) then + return true + end + + if not m.Input:Has(InputFlags.NONZERO_ANALOG) then + m:PlaySoundIfNoFlag(Sounds.MARIO_HAHA, MarioFlags.MARIO_SOUND_PLAYED) + end + + commonLandingAction(m, Animations.TRIPLE_JUMP_LAND) + return false +end) + +DEF_ACTION(Action.BACKFLIP_LAND, function(m: Mario) + if not m.Input:Has(InputFlags.Z_DOWN) then + m.Input:Remove(InputFlags.A_PRESSED) + end + + if commonLandingCancels(m, sBackflipLandAction, m.SetJumpingAction) then + return true + end + + if not m.Input:Has(InputFlags.NONZERO_ANALOG) then + m:PlaySoundIfNoFlag(Sounds.MARIO_HAHA, MarioFlags.MARIO_SOUND_PLAYED) + end + + commonLandingAction(m, Animations.TRIPLE_JUMP_LAND) + return false +end) + +DEF_ACTION(Action.PUNCHING, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) then + return m:CheckCommonActionExits() + end + + if m.ActionState and m.Input:Has(InputFlags.A_DOWN) then + return m:SetAction(Action.JUMP_KICK) + end + + m.ActionState = 1 + + if m.ActionArg == 0 then + m.ActionTimer = 7 + end + + m:SetForwardVel(sPunchingForwardVelocities[m.ActionTimer + 1]) + + if m.ActionTimer > 0 then + m.ActionTimer -= 1 + end + + m:UpdatePunchSequence() + m:PerformGroundStep() + + return false +end) diff --git a/sm64/Mario/Stationary.client.lua b/sm64/Mario/Stationary.client.lua new file mode 100644 index 0000000..bbb342e --- /dev/null +++ b/sm64/Mario/Stationary.client.lua @@ -0,0 +1,677 @@ +--!strict + +local System = require(script.Parent) +local Animations = System.Animations +local Sounds = System.Sounds +local Enums = System.Enums +local Util = System.Util + +local Action = Enums.Action +local ActionFlags = Enums.ActionFlags +local ActionGroup = Enums.ActionGroups + +local AirStep = Enums.AirStep +local MarioEyes = Enums.MarioEyes +local InputFlags = Enums.InputFlags +local MarioFlags = Enums.MarioFlags +local ParticleFlags = Enums.ParticleFlags + +type Mario = System.Mario + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helpers +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local DEF_ACTION: (number, (Mario) -> boolean) -> () = System.RegisterAction + +local function checkCommonIdleCancels(m: Mario) + local floor = m.Floor + + if floor and floor.Normal.Y < 0.29237169 then + return m:PushOffSteepFloor(Action.FREEFALL, 0) + end + + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetJumpingAction(Action.JUMP, 0) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + return m:SetAction(Action.WALKING) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.PUNCHING) + end + + if m.Input:Has(InputFlags.Z_DOWN) then + return m:SetAction(Action.START_CROUCHING) + end + + return false +end + +local function playAnimSound(m: Mario, actionState: number, animFrame: number, sound: Sound) + if m.ActionState == actionState and m.AnimFrame == animFrame then + m:PlaySound(sound) + end +end + +local function stoppingStep(m: Mario, anim: Animation, action: number) + m:StationaryGroundStep() + m:SetAnimation(anim) + + if m:IsAnimPastEnd() then + m:SetAction(action) + end +end + +local function landingStep(m: Mario, anim: Animation, action: number) + stoppingStep(m, anim, action) + return false +end + +local function checkCommonLandingCancels(m: Mario, action: number) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + if action == 0 then + m:SetJumpFromLanding() + else + m:SetJumpingAction(action, 0) + end + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) then + return m:CheckCommonActionExits() + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.PUNCHING) + end + + return false +end + +local function animatedStationaryGroundStep(m: Mario, anim: Animation, endAction: number) + m:StationaryGroundStep() + m:SetAnimation(anim) + + if m:IsAnimAtEnd() then + m:SetAction(endAction) + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Actions +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +DEF_ACTION(Action.IDLE, function(m: Mario) + if not bit32.btest(m.ActionArg, 1) and m.Health < 0x300 then + return m:SetAction(Action.PANTING) + end + + if checkCommonIdleCancels(m) then + return true + end + + if m.ActionState == 3 then + return m:SetAction(Action.START_SLEEPING) + end + + if bit32.btest(m.ActionArg, 1) then + m:SetAnimation(Animations.STAND_AGAINST_WALL) + else + if m.ActionState == 0 then + m:SetAnimation(Animations.IDLE_HEAD_LEFT) + elseif m.ActionState == 1 then + m:SetAnimation(Animations.IDLE_HEAD_RIGHT) + elseif m.ActionState == 2 then + m:SetAnimation(Animations.IDLE_HEAD_CENTER) + end + + if m:IsAnimAtEnd() then + m.ActionState += 1 + + if m.ActionState == 3 then + local deltaYOfFloorBehindMario = m.Position.Y - m:FindFloorHeightRelativePolar(-0x8000, 60) + + if 24 < math.abs(deltaYOfFloorBehindMario) then + m.ActionState = 0 + else + m.ActionTimer += 1 + + if m.ActionTimer < 10 then + m.ActionState = 0 + end + end + end + end + end + + m:StationaryGroundStep() + return false +end) + +DEF_ACTION(Action.START_SLEEPING, function(m: Mario) + local animFrame + + if checkCommonIdleCancels(m) then + return true + end + + if m.ActionState == 4 then + m:SetAction(Action.SLEEPING) + end + + if m.ActionState == 0 then + animFrame = m:SetAnimation(Animations.START_SLEEP_IDLE) + elseif m.ActionState == 1 then + animFrame = m:SetAnimation(Animations.START_SLEEP_SCRATCH) + elseif m.ActionState == 2 then + animFrame = m:SetAnimation(Animations.START_SLEEP_YAWN) + m.BodyState.EyeState = MarioEyes.HALF_CLOSED + elseif m.ActionState == 3 then + animFrame = m:SetAnimation(Animations.START_SLEEP_SITTING) + m.BodyState.EyeState = MarioEyes.HALF_CLOSED + end + + playAnimSound(m, 1, 41, Sounds.ACTION_PAT_BACK) + playAnimSound(m, 1, 49, Sounds.ACTION_PAT_BACK) + + if m:IsAnimAtEnd() then + m.ActionState += 1 + end + + if m.ActionState == 2 and animFrame == -1 then + m:PlaySound(Sounds.MARIO_YAWNING) + end + + if m.ActionState == 1 and animFrame == -1 then + m:PlaySound(Sounds.MARIO_IMA_TIRED) + end + + m:StationaryGroundStep() + return false +end) + +DEF_ACTION(Action.SLEEPING, function(m: Mario) + local animFrame + + if + m.Input:Has( + InputFlags.NONZERO_ANALOG, + InputFlags.A_PRESSED, + InputFlags.OFF_FLOOR, + InputFlags.ABOVE_SLIDE, + InputFlags.FIRST_PERSON, + InputFlags.STOMPED, + InputFlags.B_PRESSED, + InputFlags.Z_PRESSED + ) + then + return m:SetAction(Action.WAKING_UP, m.ActionState) + end + + if m.Position.Y - m:FindFloorHeightRelativePolar(-0x8000, 60) > 24 then + return m:SetAction(Action.WAKING_UP, m.ActionState) + end + + m.BodyState.EyeState = MarioEyes.CLOSED + m:StationaryGroundStep() + + if m.ActionState == 0 then + animFrame = m:SetAnimation(Animations.SLEEP_IDLE) + + if animFrame == 2 then + m:PlaySound(Sounds.MARIO_SNORING1) + end + + if animFrame == 20 then + m:PlaySound(Sounds.MARIO_SNORING2) + end + + if m:IsAnimAtEnd() then + m.ActionTimer += 1 + + if m.ActionTimer > 45 then + m.ActionState += 1 + end + end + elseif m.ActionState == 1 then + if m:SetAnimation(Animations.SLEEP_START_LYING) == 18 then + m:PlayHeavyLandingSound(Sounds.ACTION_TERRAIN_BODY_HIT_GROUND) + end + + if m:IsAnimAtEnd() then + m.ActionState += 1 + end + elseif m.ActionState == 2 then + m:SetAnimation(Animations.SLEEP_LYING) + m:PlaySoundIfNoFlag(Sounds.MARIO_SNORING3, MarioFlags.ACTION_SOUND_PLAYED) + end + + return false +end) + +DEF_ACTION(Action.WAKING_UP, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + m.ActionTimer += 1 + + if m.ActionTimer > 20 then + return m:SetAction(Action.IDLE) + end + + m:StationaryGroundStep() + m:SetAnimation(if m.ActionArg == 0 then Animations.WAKE_FROM_SLEEP else Animations.WAKE_FROM_LYING) + + return false +end) + +DEF_ACTION(Action.STANDING_AGAINST_WALL, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) then + return m:CheckCommonActionExits() + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.PUNCHING) + end + + m:SetAnimation(Animations.A_POSE) + m:StationaryGroundStep() + + return false +end) + +DEF_ACTION(Action.CROUCHING, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.BACKFLIP) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + if not m.Input:Has(InputFlags.Z_DOWN) then + return m:SetAction(Action.STOP_CROUCHING) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + return m:SetAction(Action.START_CRAWLING) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.PUNCHING, 9) + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.CROUCHING) + + return false +end) + +DEF_ACTION(Action.PANTING, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Health >= 0x500 then + return m:SetAction(Action.IDLE) + end + + if checkCommonIdleCancels(m) then + return true + end + + if m:SetAnimation(Animations.PANTING) == 1 then + m:PlaySound(Sounds.MARIO_PANTING) + end + + m:StationaryGroundStep() + m.BodyState.EyeState = MarioEyes.HALF_CLOSED + + return false +end) + +DEF_ACTION(Action.BRAKING_STOP, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + return m:SetAction(Action.PUNCHING) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) then + return m:CheckCommonActionExits() + end + + stoppingStep(m, Animations.STOP_SKID, Action.IDLE) + return false +end) + +DEF_ACTION(Action.BUTT_SLIDE_STOP, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED, InputFlags.OFF_FLOOR, InputFlags.ABOVE_SLIDE) then + return m:CheckCommonActionExits() + end + + stoppingStep(m, Animations.STOP_SLIDE, Action.IDLE) + + if m.AnimFrame == 6 then + m:PlayLandingSound() + end + + return false +end) + +DEF_ACTION(Action.SLIDE_KICK_SLIDE_STOP, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + stoppingStep(m, Animations.CROUCH_FROM_SLIDE_KICK, Action.CROUCHING) + return false +end) + +DEF_ACTION(Action.START_CROUCHING, function(m: Mario) + if m:CheckCommonActionExits() then + return true + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.START_CROUCHING) + + if m:IsAnimPastEnd() then + m:SetAction(Action.CROUCHING) + end + + return false +end) + +DEF_ACTION(Action.STOP_CROUCHING, function(m: Mario) + if m:CheckCommonActionExits() then + return true + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.START_CROUCHING) + + if m:IsAnimPastEnd() then + m:SetAction(Action.IDLE) + end + + return false +end) + +DEF_ACTION(Action.START_CRAWLING, function(m: Mario) + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.START_CRAWLING) + + if m:IsAnimPastEnd() then + m:SetAction(Action.CRAWLING) + end + + return false +end) + +DEF_ACTION(Action.STOP_CRAWLING, function(m: Mario) + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.STOP_CRAWLING) + + if m:IsAnimPastEnd() then + m:SetAction(Action.CROUCHING) + end + + return false +end) + +DEF_ACTION(Action.SHOCKWAVE_BOUNCE, function(m: Mario) + m.ActionTimer += 1 + + if m.ActionTimer == 48 then + m:SetAction(Action.IDLE) + end + + local sp1E = bit32.lshift(m.ActionTimer % 16, 12) + local sp18 = ((6 - m.ActionTimer / 8) * 8) + 4 + + m:SetForwardVel(0) + m.Velocity = Vector3.zero + + if Util.Sins(sp1E) >= 0 then + m.Position = Util.SetY(m.Position, Util.Sins(sp1E) * sp18 + m.FloorHeight) + else + m.Position = Util.SetY(m.Position, m.FloorHeight - Util.Sins(sp1E) * sp18) + end + + m:SetAnimation(Animations.A_POSE) + return false +end) + +DEF_ACTION(Action.JUMP_LAND_STOP, function(m: Mario) + if checkCommonLandingCancels(m, 0) then + return true + end + + landingStep(m, Animations.LAND_FROM_SINGLE_JUMP, Action.IDLE) + return false +end) + +DEF_ACTION(Action.DOUBLE_JUMP_LAND_STOP, function(m: Mario) + if checkCommonLandingCancels(m, 0) then + return true + end + + landingStep(m, Animations.LAND_FROM_DOUBLE_JUMP, Action.IDLE) + return false +end) + +DEF_ACTION(Action.SIDE_FLIP_LAND_STOP, function(m: Mario) + if checkCommonLandingCancels(m, 0) then + return true + end + + landingStep(m, Animations.SLIDEFLIP_LAND, Action.IDLE) + return false +end) + +DEF_ACTION(Action.FREEFALL_LAND_STOP, function(m: Mario) + if checkCommonLandingCancels(m, 0) then + return true + end + + landingStep(m, Animations.GENERAL_LAND, Action.IDLE) + return false +end) + +DEF_ACTION(Action.TRIPLE_JUMP_LAND_STOP, function(m: Mario) + if checkCommonLandingCancels(m, Action.JUMP) then + return true + end + + landingStep(m, Animations.GENERAL_LAND, Action.IDLE) + return false +end) + +DEF_ACTION(Action.BACKFLIP_LAND_STOP, function(m: Mario) + if not m.Input:Has(InputFlags.Z_DOWN) and m.AnimFrame >= 6 then + m.Input:Remove(InputFlags.A_PRESSED) + end + + if checkCommonLandingCancels(m, Action.BACKFLIP) then + return true + end + + landingStep(m, Animations.TRIPLE_JUMP_LAND, Action.IDLE) + return false +end) + +DEF_ACTION(Action.LAVA_BOOST_LAND, function(m: Mario) + m.Input:Remove(InputFlags.FIRST_PERSON, InputFlags.B_PRESSED) + + if checkCommonLandingCancels(m, 0) then + return true + end + + landingStep(m, Animations.STAND_UP_FROM_LAVA_BOOST, Action.IDLE) + return false +end) + +DEF_ACTION(Action.LONG_JUMP_LAND_STOP, function(m: Mario) + m.Input:Remove(InputFlags.B_PRESSED) + + if checkCommonLandingCancels(m, Action.JUMP) then + return true + end + + landingStep( + m, + if m.LongJumpIsSlow then Animations.CROUCH_FROM_FAST_LONGJUMP else Animations.CROUCH_FROM_SLOW_LONGJUMP, + Action.CROUCHING + ) + + return false +end) + +DEF_ACTION(Action.TWIRL_LAND, function(m: Mario) + m.ActionState = 1 + + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + m:StationaryGroundStep() + m:SetAnimation(Animations.TWIRL_LAND) + + if m.AngleVel.Y > 0 then + m.AngleVel -= Vector3int16.new(0, 0x400, 0) + + if m.AngleVel.Y < 0 then + m.AngleVel *= Vector3int16.new(1, 0, 1) + end + + m.TwirlYaw += m.AngleVel.Y + end + + m.GfxAngle += Vector3int16.new(0, m.TwirlYaw, 0) + + if m:IsAnimAtEnd() and m.AngleVel.Y == 0 then + m.FaceAngle += Vector3int16.new(0, m.TwirlYaw, 0) + m:SetAction(Action.IDLE) + end + + return false +end) + +DEF_ACTION(Action.GROUND_POUND_LAND, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BUTT_SLIDE) + end + + landingStep(m, Animations.GROUND_POUND_LANDING, Action.BUTT_SLIDE_STOP) + return false +end) + +DEF_ACTION(Action.STOMACH_SLIDE_STOP, function(m: Mario) + if m.Input:Has(InputFlags.STOMPED) then + return m:SetAction(Action.SHOCKWAVE_BOUNCE) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + animatedStationaryGroundStep(m, Animations.SLOW_LAND_FROM_DIVE, Action.IDLE) + return false +end) + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/sm64/Mario/init.lua b/sm64/Mario/init.lua new file mode 100644 index 0000000..ec55a90 --- /dev/null +++ b/sm64/Mario/init.lua @@ -0,0 +1,1599 @@ +--!strict +local Mario = {} +Mario.__index = Mario + +local SM64 = script.Parent +local Util = require(SM64.Util) +local Enums = require(SM64.Enums) +local Sounds = require(SM64.Sounds) +local Animations = require(SM64.Animations) + +local Types = require(SM64.Types) +local Flags = Types.Flags + +local Action = Enums.Action +local Buttons = Enums.Buttons +local ActionFlags = Enums.ActionFlags +local ActionGroups = Enums.ActionGroups + +local MarioCap = Enums.MarioCap +local MarioEyes = Enums.MarioEyes +local MarioFlags = Enums.MarioFlags +local MarioHands = Enums.MarioHands + +local InputFlags = Enums.InputFlags +local ModelFlags = Enums.ModelFlags +local TerrainType = Enums.TerrainType +local SurfaceClass = Enums.SurfaceClass +local ParticleFlags = Enums.ParticleFlags + +local AirStep = Enums.AirStep +local WaterStep = Enums.WaterStep +local GroundStep = Enums.GroundStep + +export type BodyState = Types.BodyState +export type Controller = Types.Controller +export type MarioState = Types.MarioState + +export type Mario = typeof(setmetatable({} :: MarioState, Mario)) +export type MarioAction = (Mario) -> boolean +export type Flags = Types.Flags +export type Class = Mario + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- BINDINGS +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local actions: { [number]: MarioAction } = {} +Mario.Animations = Animations +Mario.Actions = actions +Mario.Sounds = Sounds +Mario.Enums = Enums +Mario.Types = Types +Mario.Util = Util + +function Mario.RegisterAction(actionType: number, action: MarioAction) + if actions[actionType] then + warn("Action", Enums.GetName(Action, actionType), "was registered twice!") + end + + actions[actionType] = action +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ANIMATIONS +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.IsAnimAtEnd(m: Mario): boolean + return m.AnimFrame >= m.AnimFrameCount +end + +function Mario.IsAnimPastEnd(m: Mario): boolean + return m.AnimFrame >= m.AnimFrameCount - 2 +end + +function Mario.SetAnimation(m: Mario, anim: Animation): number + if anim and typeof(anim) == "Instance" and anim:IsA("Animation") then + if m.AnimCurrent == anim then + return m.AnimFrame + end + + m.AnimFrameCount = anim:GetAttribute("NumFrames") + m.AnimCurrent = anim + else + warn("Invalid animation provided in SetAnimation:", anim, debug.traceback()) + m.AnimFrameCount = 0 + m.AnimCurrent = nil + end + + local startFrame: number = anim:GetAttribute("StartFrame") or -1 + m.AnimAccelAssist = 0 + m.AnimAccel = 0 + + m.AnimReset = true + m.AnimDirty = true + m.AnimFrame = startFrame + + return startFrame +end + +function Mario.SetAnimationWithAccel(m: Mario, anim: Animation, accel: number) + if m.AnimCurrent ~= anim then + m:SetAnimation(anim) + m.AnimAccelAssist = -accel + m.AnimAccel = accel + end +end + +function Mario.SetAnimToFrame(m: Mario, frame: number) + if m.AnimAccel ~= 0 then + m.AnimAccelAssist = bit32.lshift(frame, 0x10) + m.AnimAccel + m.AnimFrame = bit32.rshift(m.AnimAccelAssist, 0x10) + else + m.AnimFrame = frame + 1 + end + + m.AnimDirty = true + m.AnimSetFrame = m.AnimFrame +end + +function Mario.IsAnimPastFrame(m: Mario, frame: number): boolean + local isPastFrame: boolean = false + local accel = m.AnimAccel + + if accel ~= 0 then + local assist = m.AnimAccelAssist + local accelFrame = bit32.lshift(frame, 0x10) + isPastFrame = (assist > accelFrame and accelFrame >= assist - accel) + else + isPastFrame = (m.AnimFrame == (frame + 1)) + end + + return isPastFrame +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- AUDIO +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.PlaySound(m: Mario, sound: Instance?) + if not sound then + return + end + + assert(sound) + + if sound:IsA("Sound") then + sound:SetAttribute("Play", true) + else + local rollTable = {} + local chances = sound:GetAttributes() + + for name, chance in pairs(chances) do + for i = 1, chance do + table.insert(rollTable, name) + end + end + + if #rollTable > 0 then + local pick = rollTable[math.random(1, #rollTable)] + sound = Sounds[pick] + + if sound then + sound:SetAttribute("Play", true) + end + end + end +end + +function Mario.PlaySoundIfNoFlag(m: Mario, sound: Instance?, flags: number) + if not m.Flags:Has(flags) and sound then + m:PlaySound(sound) + m.Flags:Add(flags) + end +end + +function Mario.PlayJumpSound(m: Mario) + if m.Flags:Has(MarioFlags.MARIO_SOUND_PLAYED) then + return + end + + if m.Action() == Action.TRIPLE_JUMP then + m:PlaySound(Sounds.MARIO_YAHOO_WAHA_YIPPEE) + elseif m.Action() == Action.JUMP_KICK then + m:PlaySound(Sounds.MARIO_PUNCH_HOO) + else + m:PlaySound(Sounds.MARIO_YAH_WAH_HOO) + end + + m.Flags:Add(MarioFlags.MARIO_SOUND_PLAYED) +end + +function Mario.AdjustSoundForSpeed(m: Mario) + local absForwardVel = math.abs(m.ForwardVel) + -- TODO: Adjust Moving Speed Pitch +end + +function Mario.PlaySoundAndSpawnParticles(m: Mario, sound: Instance?, wave: number?) + local particles: number? + + if m.TerrainType == TerrainType.WATER then + if wave ~= 0 then + particles = ParticleFlags.SHALLOW_WATER_SPLASH + else + particles = ParticleFlags.SHALLOW_WATER_WAVE + end + else + if m.TerrainType == TerrainType.SAND then + particles = ParticleFlags.DIRT + elseif m.TerrainType == TerrainType.SNOW then + particles = ParticleFlags.SNOW + end + end + + if particles then + m.ParticleFlags:Add(particles) + end + + if sound then + local terrainType = Enums.GetName(TerrainType, m.TerrainType) + local stepSound = Sounds[sound.Name .. "_" .. terrainType] + + if stepSound then + m:PlaySound(stepSound) + else + m:PlaySound(sound) + end + end +end + +function Mario.PlayActionSound(m: Mario, sound: Instance?, wave: number?) + if not m.Flags:Has(MarioFlags.ACTION_SOUND_PLAYED) then + m:PlaySoundAndSpawnParticles(sound, wave) + m.Flags:Add(MarioFlags.ACTION_SOUND_PLAYED) + end +end + +function Mario.PlayLandingSound(m: Mario, sound: Instance?) + local sound = sound or Sounds.ACTION_TERRAIN_LANDING + + local landSound = if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_LANDING else sound + + m:PlaySoundAndSpawnParticles(landSound, 1) +end + +function Mario.PlayLandingSoundOnce(m: Mario, sound: Instance?) + local landSound = if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_LANDING else sound + + m:PlayActionSound(landSound, 1) +end + +function Mario.PlayHeavyLandingSound(m: Mario, sound: Instance?) + local landSound = if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_HEAVY_LANDING else sound + + m:PlaySoundAndSpawnParticles(landSound, 1) +end + +function Mario.PlayHeavyLandingSoundOnce(m: Mario, sound: Instance?) + local landSound = if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_HEAVY_LANDING else sound + + m:PlayActionSound(landSound, 1) +end + +function Mario.PlayMarioSound(m: Mario, actionSound: Instance, marioSound: Instance?) + if marioSound == nil then + marioSound = Sounds.MARIO_JUMP + end + + if actionSound == Sounds.ACTION_TERRAIN_JUMP then + local sound = if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_JUMP else actionSound + + m:PlayActionSound(sound) + else + m:PlaySoundIfNoFlag(actionSound, MarioFlags.ACTION_SOUND_PLAYED) + end + + if marioSound == Sounds.MARIO_JUMP or marioSound == nil then + m:PlayJumpSound() + elseif marioSound then + m:PlaySoundIfNoFlag(marioSound, MarioFlags.MARIO_SOUND_PLAYED) + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ACTION STATE +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.SetForwardVel(m: Mario, forwardVel: number) + m.ForwardVel = forwardVel + + m.SlideVelX = Util.Sins(m.FaceAngle.Y) * forwardVel + m.SlideVelZ = Util.Coss(m.FaceAngle.Y) * forwardVel + + m.Velocity = Vector3.new(m.SlideVelX, m.Velocity.Y, m.SlideVelZ) +end + +function Mario.GetFloorClass(m: Mario): number + local floor = m.Floor + + if floor then + local hit: BasePart? = floor.Instance + + if hit then + local material = floor.Material + local physics = hit.CustomPhysicalProperties + + if not physics then + physics = PhysicalProperties.new(material) + end + + local friction = physics.Friction + + if friction <= 0.025 then + return SurfaceClass.VERY_SLIPPERY + elseif friction <= 0.5 then + return SurfaceClass.SLIPPERY + elseif friction >= 0.9 then + return SurfaceClass.NOT_SLIPPERY + end + end + end + + return SurfaceClass.DEFAULT +end + +function Mario.GetTerrainType(m: Mario): number + local floor = m.Floor + + if floor then + local material = floor.Material + local value = TerrainType.FROM_MATERIAL[material] + + if value then + return value + end + end + + return TerrainType.DEFAULT +end + +function Mario.GetFloorType(m: Mario): number + -- TODO + return 0 +end + +function Mario.FacingDownhill(m: Mario, turnYaw: boolean?): boolean + local faceAngleYaw = m.FaceAngle.Y + + if turnYaw and m.ForwardVel < 0 then + faceAngleYaw += 0x8000 + end + + return math.abs(m.FloorAngle - faceAngleYaw) < 0x4000 +end + +function Mario.FloorIsSlippery(m: Mario) + local floor = m.Floor + + if floor then + local floorClass = m:GetFloorClass() + local deg: number = 90 + + if floorClass == SurfaceClass.VERY_SLIPPERY then + deg = 10 + elseif floorClass == SurfaceClass.SLIPPERY then + deg = 20 + elseif floorClass == SurfaceClass.NOT_SLIPPERY then + deg = 38 + end + + local rad = math.rad(deg) + return floor.Normal.Y <= math.cos(rad) + end + + return false +end + +function Mario.FloorIsSlope(m: Mario) + local floor = m.Floor + + if floor then + local floorClass = m:GetFloorClass() + local deg: number = 15 + + if floorClass == SurfaceClass.VERY_SLIPPERY then + deg = 5 + elseif floorClass == SurfaceClass.SLIPPERY then + deg = 10 + elseif floorClass == SurfaceClass.NOT_SLIPPERY then + deg = 20 + end + + local rad = math.rad(deg) + return floor.Normal.Y <= math.cos(rad) + end + + return false +end + +function Mario.FloorIsSteep(m: Mario) + local floor = m.Floor + + if floor and not m:FacingDownhill() then + local floorClass = m:GetFloorClass() + local deg: number = 30 + + if floorClass == SurfaceClass.VERY_SLIPPERY then + deg = 15 + elseif floorClass == SurfaceClass.SLIPPERY then + deg = 20 + elseif floorClass == SurfaceClass.NOT_SLIPPERY then + deg = 30 + end + + local rad = math.rad(deg) + return floor.Normal.Y <= math.cos(rad) + end + + return false +end + +function Mario.FindFloorHeightRelativePolar( + m: Mario, + angleFromMario: number, + distFromMario: number +): (number, RaycastResult?) + local y = Util.Sins(m.FaceAngle.Y + angleFromMario) * distFromMario + local x = Util.Coss(m.FaceAngle.Y + angleFromMario) * distFromMario + + local marioPos = m.Position + local testPos = marioPos + Vector3.new(y, 100, x) + + return Util.FindFloor(testPos) +end + +function Mario.FindFloorSlope(m: Mario, yawOffset: number) + local x = Util.Sins(m.FaceAngle.Y + yawOffset) * 5 + local z = Util.Coss(m.FaceAngle.Y + yawOffset) * 5 + + local forwardFloorY = Util.FindFloor(m.Position + Vector3.new(x, 100, z)) + local backwardFloorY = Util.FindFloor(m.Position + Vector3.new(-x, 100, -z)) + local result = 0 + + if forwardFloorY and backwardFloorY then + local forwardYDelta = forwardFloorY - m.Position.Y + local backwardYDelta = m.Position.Y - backwardFloorY + + if forwardYDelta ^ 2 < backwardYDelta ^ 2 then + result = Util.Atan2s(5, forwardYDelta) + else + result = Util.Atan2s(5, backwardYDelta) + end + end + + return result +end + +function Mario.SetSteepJumpAction(m: Mario) + m.SteepJumpYaw = m.FaceAngle.Y + + if m.ForwardVel > 0 then + local angleTemp = m.FloorAngle + 0x8000 + local faceAngleTemp = m.FaceAngle.Y - angleTemp + + local y = Util.Sins(faceAngleTemp) * m.ForwardVel + local x = Util.Coss(faceAngleTemp) * m.ForwardVel * 0.75 + + m.ForwardVel = math.sqrt(y * y + x * x) + m.FaceAngle = Util.SetYint16(m.FaceAngle, Util.Atan2s(x, y) + angleTemp) + end + + m:SetAction(Action.STEEP_JUMP, 0) +end + +function Mario.SetYVelBasedOnFSpeed(m: Mario, initialVelY: number, multiplier: number) + m.Velocity = Util.SetY(m.Velocity, initialVelY + m.ForwardVel * multiplier) + + if m.SquishTimer ~= 0 or m.QuicksandDepth > 1 then + m.Velocity *= Vector3.new(1, 0.5, 1) + end +end + +function Mario.SetActionAirborne(m: Mario, action: number, actionArg: number) + if m.SquishTimer ~= 0 or m.QuicksandDepth > 1 then + if action == Action.DOUBLE_JUMP or action == Action.TWIRLING then + action = Action.JUMP + end + end + + if action == Action.DOUBLE_JUMP then + m:SetYVelBasedOnFSpeed(52, 0.25) + m.ForwardVel *= 0.8 + elseif action == Action.BACKFLIP then + m.AnimReset = true + m.ForwardVel = -16 + m:SetYVelBasedOnFSpeed(62, 0) + elseif action == Action.TRIPLE_JUMP then + m:SetYVelBasedOnFSpeed(69, 0) + m.ForwardVel *= 0.8 + elseif action == Action.FLYING_TRIPLE_JUMP then + m:SetYVelBasedOnFSpeed(82, 0) + elseif action == Action.WATER_JUMP or action == Action.HOLD_WATER_JUMP then + if actionArg == 0 then + m:SetYVelBasedOnFSpeed(42, 0) + end + elseif action == Action.BURNING_JUMP then + m.Velocity = Util.SetY(m.Velocity, 31.5) + m.ForwardVel = 8 + elseif action == Action.RIDING_SHELL_JUMP then + m:SetYVelBasedOnFSpeed(42, 0.25) + elseif action == Action.JUMP or action == Action.HOLD_JUMP then + m.AnimReset = true + m:SetYVelBasedOnFSpeed(42, 0.25) + m.ForwardVel *= 0.8 + elseif action == Action.WALL_KICK_AIR or action == Action.TOP_OF_POLE_JUMP then + m:SetYVelBasedOnFSpeed(62, 0) + + if m.ForwardVel < 24 then + m.ForwardVel = 24 + end + + m.WallKickTimer = 0 + elseif action == Action.SIDE_FLIP then + m:SetYVelBasedOnFSpeed(62, 0) + m.ForwardVel = 8 + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + elseif action == Action.STEEP_JUMP then + m.AnimReset = true + m:SetYVelBasedOnFSpeed(42, 0.25) + m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2000) + elseif action == Action.LAVA_BOOST then + m.Velocity = Util.SetY(m.Velocity, 84) + + if actionArg == 0 then + m.ForwardVel = 0 + end + elseif action == Action.DIVE then + local forwardVel = m.ForwardVel + 15 + + if forwardVel > 48 then + forwardVel = 48 + end + + m:SetForwardVel(forwardVel) + elseif action == Action.LONG_JUMP then + m.AnimReset = true + m:SetYVelBasedOnFSpeed(30, 0) + + m.LongJumpIsSlow = if m.ForwardVel > 16 then false else true + + --! (BLJ's) This properly handles long jumps from getting forward speed with + -- too much velocity, but misses backwards longs allowing high negative speeds. + m.ForwardVel *= 1.5 + + if m.ForwardVel > 48 then + m.ForwardVel = 48 + end + elseif action == Action.SLIDE_KICK then + m.Velocity = Util.SetY(m.Velocity, 12) + + if m.ForwardVel < 32 then + m.ForwardVel = 32 + end + elseif action == Action.JUMP_KICK then + m.Velocity = Util.SetY(m.Velocity, 20) + end + + m.PeakHeight = m.Position.Y + m.Flags:Add(MarioFlags.MOVING_UP_IN_AIR) + + return action +end + +function Mario.SetActionMoving(m: Mario, action: number, actionArg: number): number + local forwardVel = m.ForwardVel + local floorClass = m:GetFloorClass() + local mag = math.min(m.IntendedMag, 8) + + if action == Action.WALKING then + if floorClass ~= SurfaceClass.VERY_SLIPPERY then + if 0.0 <= forwardVel and forwardVel < mag then + m.ForwardVel = mag + end + end + + m.WalkingPitch = 0 + elseif action == Action.HOLD_WALKING then + if 0.0 <= forwardVel and forwardVel < mag / 2 then + m.ForwardVel = mag / 2 + end + elseif action == Action.BEGIN_SLIDING then + if m:FacingDownhill() then + action = Action.BUTT_SLIDE + else + action = Action.STOMACH_SLIDE + end + elseif action == Action.HOLD_BEGIN_SLIDING then + if m:FacingDownhill() then + action = Action.HOLD_BUTT_SLIDE + else + action = Action.HOLD_STOMACH_SLIDE + end + end + + return action +end + +function Mario.SetActionSubmerged(m: Mario, action: number, actionArg: number): number + if action == Action.METAL_WATER_JUMP or action == Action.HOLD_METAL_WATER_JUMP then + m.Velocity = Util.SetY(m.Velocity, 32) + end + + return action +end + +function Mario.SetActionCutscene(m: Mario, action: number, actionArg: number): number + if action == Action.EMERGE_FROM_PIPE then + m.Velocity = Util.SetY(m.Velocity, 52) + elseif action == Action.FALL_AFTER_STAR_GRAB then + m:SetForwardVel(0) + elseif action == Action.SPAWN_SPIN_AIRBORNE then + m:SetForwardVel(2) + elseif action == Action.SPECIAL_EXIT_AIRBORNE or action == Action.SPECIAL_DEATH_EXIT then + m.Velocity = Util.SetY(m.Velocity, 64) + end + + return action +end + +function Mario.SetAction(m: Mario, action: number, actionArg: number?): boolean + local group = bit32.band(action, ActionGroups.GROUP_MASK) + local actionArg: number = actionArg or 0 + + if group == ActionGroups.MOVING then + action = m:SetActionMoving(action, actionArg) + elseif group == ActionGroups.AIRBORNE then + action = m:SetActionAirborne(action, actionArg) + elseif group == ActionGroups.SUBMERGED then + action = m:SetActionSubmerged(action, actionArg) + elseif group == ActionGroups.CUTSCENE then + action = m:SetActionCutscene(action, actionArg) + end + + m.Flags:Remove(MarioFlags.ACTION_SOUND_PLAYED, MarioFlags.MARIO_SOUND_PLAYED) + + if not m.Action:Has(ActionFlags.AIR) then + m.Flags:Remove(MarioFlags.FALLING_FAR) + end + + m.PrevAction:Copy(m.Action) + m.Action:Set(action) + + m.ActionArg = actionArg + m.ActionState = 0 + m.ActionTimer = 0 + + return true +end + +function Mario.SetJumpFromLanding(m: Mario) + if m:FloorIsSteep() then + m:SetSteepJumpAction() + elseif m.DoubleJumpTimer == 0 or m.SquishTimer ~= 0 then + m:SetAction(Action.JUMP, 0) + else + local prev = m.PrevAction() + + if prev == Action.JUMP_LAND then + m:SetAction(Action.DOUBLE_JUMP) + elseif prev == Action.FREEFALL_LAND then + m:SetAction(Action.DOUBLE_JUMP) + elseif prev == Action.SIDE_FLIP_LAND_STOP then + m:SetAction(Action.DOUBLE_JUMP) + elseif prev == Action.DOUBLE_JUMP_LAND then + if m.Flags:Has(MarioFlags.WING_CAP) then + m:SetAction(Action.FLYING_TRIPLE_JUMP) + elseif m.ForwardVel > 20 then + m:SetAction(Action.TRIPLE_JUMP) + else + m:SetAction(Action.JUMP) + end + else + m:SetAction(Action.JUMP) + end + end + + m.DoubleJumpTimer = 0 + return true +end + +function Mario.SetJumpingAction(m: Mario, action: number, actionArg: number?) + if m:FloorIsSteep() then + m:SetSteepJumpAction() + else + m:SetAction(action, actionArg) + end + + return true +end + +function Mario.HurtAndSetAction(m: Mario, action: number, actionArg: number, hurtCounter: number) + m.HurtCounter = hurtCounter + m:SetAction(action, actionArg) +end + +function Mario.CheckCommonActionExits(m: Mario) + if m.Input:Has(InputFlags.A_PRESSED) then + return m:SetAction(Action.JUMP) + end + + if m.Input:Has(InputFlags.OFF_FLOOR) then + return m:SetAction(Action.FREEFALL) + end + + if m.Input:Has(InputFlags.NONZERO_ANALOG) then + return m:SetAction(Action.WALKING) + end + + if m.Input:Has(InputFlags.ABOVE_SLIDE) then + return m:SetAction(Action.BEGIN_SLIDING) + end + + return false +end + +function Mario.UpdatePunchSequence(m: Mario) + local endAction, crouchEndAction + local animFrame + + if m.Action:Has(ActionFlags.MOVING) then + endAction = Action.WALKING + crouchEndAction = Action.CROUCH_SLIDE + else + endAction = Action.IDLE + crouchEndAction = Action.CROUCHING + end + + local actionArg = m.ActionArg + + if actionArg == 0 or actionArg == 1 then + if actionArg == 0 then + m:PlaySound(Sounds.MARIO_PUNCH_YAH) + end + + m:SetAnimation(Animations.FIRST_PUNCH) + m.ActionArg = m:IsAnimAtEnd() and 2 or 1 + + if m.AnimFrame >= 2 then + m.Flags:Add(MarioFlags.PUNCHING) + end + elseif actionArg == 2 then + m:SetAnimation(Animations.FIRST_PUNCH_FAST) + + if m.AnimFrame <= 0 then + m.Flags:Add(MarioFlags.PUNCHING) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + m.ActionArg = 3 + end + + if m:IsAnimAtEnd() then + m:SetAction(endAction) + end + elseif actionArg == 3 or actionArg == 4 then + if actionArg == 3 then + m:PlaySound(Sounds.MARIO_PUNCH_WAH) + end + + m:SetAnimation(Animations.SECOND_PUNCH) + m.ActionArg = m:IsAnimPastEnd() and 5 or 4 + + if m.AnimFrame > 0 then + m.Flags:Add(MarioFlags.PUNCHING) + end + + if m.ActionArg == 5 then + m.BodyState.PunchType = 1 + m.BodyState.PunchTimer = 4 + end + elseif actionArg == 5 then + m:SetAnimation(Animations.SECOND_PUNCH_FAST) + + if m.AnimFrame <= 0 then + m.Flags:Add(MarioFlags.PUNCHING) + end + + if m.Input:Has(InputFlags.B_PRESSED) then + m.ActionArg = 6 + end + + if m:IsAnimAtEnd() then + m:SetAction(endAction) + end + elseif actionArg == 6 then + m:PlayActionSound(Sounds.MARIO_PUNCH_HOO, 1) + animFrame = m:SetAnimation(Animations.GROUND_KICK) + + if animFrame == 0 then + m.BodyState.PunchType = 2 + m.BodyState.PunchTimer = 6 + end + + if animFrame >= 0 and animFrame < 8 then + m.Flags:Add(MarioFlags.KICKING) + end + + if m:IsAnimAtEnd() then + m:SetAction(endAction) + end + elseif actionArg == 9 then + m:PlayActionSound(Sounds.MARIO_PUNCH_HOO, 1) + m:SetAnimation(Animations.BREAKDANCE) + animFrame = m.AnimFrame + + if animFrame >= 2 and animFrame < 8 then + m.Flags:Add(MarioFlags.TRIPPING) + end + + if m:IsAnimAtEnd() then + m:SetAction(crouchEndAction) + end + end + + return false +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- PHYSICS STEP +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.BonkReflection(m: Mario, negateSpeed: boolean?) + local wall = m.Wall + + if wall ~= nil then + local wallAngle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) + m.FaceAngle = Util.SetYint16(m.FaceAngle, wallAngle - (m.FaceAngle.Y - wallAngle)) + m:PlaySound(if m.Flags:Has(MarioFlags.METAL_CAP) then Sounds.ACTION_METAL_BONK else Sounds.ACTION_BONK) + else + m:PlaySound(Sounds.ACTION_HIT) + end + + if negateSpeed then + m:SetForwardVel(-m.ForwardVel) + else + m.FaceAngle += Vector3int16.new(0, 0x8000, 0) + end +end + +function Mario.PushOffSteepFloor(m: Mario, action: number, actionArg: number?) + local floorDYaw = m.FloorAngle - m.FaceAngle.Y + + if floorDYaw > -0x4000 and floorDYaw < 0x4000 then + m.ForwardVel = 16 + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.FloorAngle) + else + m.ForwardVel = -16 + m.FaceAngle = Util.SetYint16(m.FaceAngle, m.FloorAngle + 0x8000) + end + + m:SetAction(action, actionArg) +end + +function Mario.StopAndSetHeightToFloor(m: Mario) + m:SetForwardVel(0) + m.Velocity *= Vector3.new(1, 0, 1) + m.Position = Util.SetY(m.Position, m.FloorHeight) + m.GfxAngle = Vector3int16.new(0, m.FaceAngle.Y, 0) +end + +function Mario.StationaryGroundStep(m: Mario): number + m:SetForwardVel(0) + return m:PerformGroundStep() +end + +function Mario.PerformGroundQuarterStep(m: Mario, nextPos: Vector3): number + local lowerPos, lowerWall = Util.FindWallCollisions(nextPos, 30, 24) + nextPos = lowerPos + + local upperPos, upperWall = Util.FindWallCollisions(nextPos, 60, 50) + nextPos = upperPos + + local floorHeight, floor = Util.FindFloor(nextPos) + local ceilHeight, ceil = Util.FindCeil(nextPos, floorHeight) + + m.Wall = upperWall + + if floor == nil then + return GroundStep.HIT_WALL_STOP_QSTEPS + end + + if nextPos.Y > floorHeight + 100 then + if nextPos.Y + 160 >= ceilHeight then + return GroundStep.HIT_WALL_STOP_QSTEPS + end + + m.Floor = floor + m.FloorHeight = floorHeight + + return GroundStep.LEFT_GROUND + end + + if floorHeight + 160 >= ceilHeight then + return GroundStep.HIT_WALL_STOP_QSTEPS + end + + m.Floor = floor + m.FloorHeight = floorHeight + m.Position = Vector3.new(nextPos.X, floorHeight, nextPos.Z) + + if upperWall then + local wallDYaw = Util.Atan2s(upperWall.Normal.Z, upperWall.Normal.X) - m.FaceAngle.Y + + if math.abs(wallDYaw) >= 0x2AAA and math.abs(wallDYaw) <= 0x5555 then + return GroundStep.NONE + end + + return GroundStep.HIT_WALL_CONTINUE_QSTEPS + end + + return GroundStep.NONE +end + +function Mario.PerformGroundStep(m: Mario): number + local floor = m.Floor + + if not floor then + return GroundStep.NONE + end + + local stepResult: number + assert(floor) + + for i = 1, 4 do + local intendedX = m.Position.X + floor.Normal.Y * (m.Velocity.X / 4) + local intendedZ = m.Position.Z + floor.Normal.Y * (m.Velocity.Z / 4) + local intendedY = m.Position.Y + + local intendedPos = Vector3.new(intendedX, intendedY, intendedZ) + stepResult = m:PerformGroundQuarterStep(intendedPos) + + if stepResult == GroundStep.LEFT_GROUND or stepResult == GroundStep.HIT_WALL_STOP_QSTEPS then + break + end + end + + m.TerrainType = m:GetTerrainType() + m.GfxAngle = Vector3int16.new(0, m.FaceAngle.Y, 0) + + if stepResult == GroundStep.HIT_WALL_CONTINUE_QSTEPS then + stepResult = GroundStep.HIT_WALL + end + + return stepResult +end + +function Mario.CheckLedgeGrab(m: Mario, wall: RaycastResult, intendedPos: Vector3, nextPos: Vector3): boolean + if m.Velocity.Y > 0 then + return false + end + + local dispX = nextPos.X - intendedPos.X + local dispZ = nextPos.Z - intendedPos.Z + + if dispX * m.Velocity.X + dispZ * m.Velocity.Z > 0 then + return false + end + + local ledgeX = nextPos.X - (wall.Normal.X * 60) + local ledgeZ = nextPos.Z - (wall.Normal.Z * 60) + + local ledgePos = Vector3.new(ledgeX, nextPos.Y + 160, ledgeZ) + local ledgeY, ledgeFloor = Util.FindFloor(ledgePos) + + if ledgeY - nextPos.Y < 100 then + return false + end + + if ledgeFloor then + ledgePos = ledgeFloor.Position + m.Position = ledgePos + + m.Floor = ledgeFloor + m.FloorHeight = ledgeY + m.FloorAngle = Util.Atan2s(ledgeFloor.Normal.Z, ledgeFloor.Normal.X) + + m.FaceAngle *= Vector3int16.new(0, 1, 1) + m.FaceAngle = Util.SetYint16(m.FaceAngle, Util.Atan2s(wall.Normal.Z, wall.Normal.X) + 0x8000) + end + + return ledgeFloor ~= nil +end + +function Mario.PerformAirQuarterStep(m: Mario, intendedPos: Vector3, stepArg: number) + local nextPos = intendedPos + + local upperPos, upperWall = Util.FindWallCollisions(nextPos, 150, 50) + nextPos = upperPos + + local lowerPos, lowerWall = Util.FindWallCollisions(nextPos, 30, 50) + nextPos = lowerPos + + local floorHeight, floor = Util.FindFloor(nextPos) + local ceilHeight, ceil = Util.FindCeil(nextPos) + + m.Wall = nil + + if floor == nil then + if nextPos.Y <= m.FloorHeight then + m.Position = Util.SetY(m.Position, m.FloorHeight) + return AirStep.LANDED + end + + m.Position = Util.SetY(m.Position, nextPos.Y) + return AirStep.HIT_WALL + end + + if nextPos.Y <= floorHeight then + if ceilHeight - floorHeight > 160 then + m.Floor = floor + m.FloorHeight = floorHeight + m.Position = Vector3.new(nextPos.X, m.Position.Y, nextPos.Z) + end + + m.Position = Util.SetY(m.Position, floorHeight) + return AirStep.LANDED + end + + if nextPos.Y + 160 > ceilHeight then + if m.Velocity.Y > 0 then + m.Velocity = Util.SetY(m.Velocity, 0) + return AirStep.NONE + end + + if nextPos.Y <= m.FloorHeight then + m.Position = Util.SetY(m.Position, floorHeight) + return AirStep.LANDED + end + + m.Position = Util.SetY(m.Position, nextPos.Y) + return AirStep.HIT_WALL + end + + if bit32.btest(stepArg, AirStep.CHECK_LEDGE_GRAB) and upperWall == nil and lowerWall ~= nil then + if m:CheckLedgeGrab(lowerWall, intendedPos, nextPos) then + return AirStep.GRABBED_LEDGE + end + + m.Floor = floor + m.Position = nextPos + m.FloorHeight = floorHeight + + return AirStep.NONE + end + + m.Floor = floor + m.Position = nextPos + m.FloorHeight = floorHeight + + if upperWall or lowerWall then + local wall = assert(upperWall or lowerWall) + local wallDYaw = Util.Atan2s(wall.Normal.Z, wall.Normal.X) - m.FaceAngle.Y + m.Wall = wall + + if math.abs(wallDYaw) > 0x6000 then + return AirStep.HIT_WALL + end + end + + return AirStep.NONE +end + +function Mario.ApplyTwirlGravity(m: Mario) + local heaviness = 1 + + if m.AngleVel.Y > 1024 then + heaviness = 1024 / m.AngleVel.Y + end + + local terminalVelocity = -75 * heaviness + m.Velocity -= Vector3.new(0, 4 * heaviness, 0) + + if m.Velocity.Y < terminalVelocity then + m.Velocity = Util.SetY(m.Velocity, terminalVelocity) + end +end + +function Mario.ShouldStrengthenGravityForJumpAscent(m: Mario): boolean + if not m.Flags:Has(MarioFlags.MOVING_UP_IN_AIR) then + return false + end + + if m.Action:Has(ActionFlags.INTANGIBLE, ActionFlags.INVULNERABLE) then + return false + end + + if not m.Input:Has(InputFlags.A_DOWN) and m.Velocity.Y > 20 then + return m.Action:Has(ActionFlags.CONTROL_JUMP_HEIGHT) + end + + return false +end + +function Mario.ApplyGravity(m: Mario) + local action = m.Action() + + if action == Action.TWIRLING and m.Velocity.Y < 0 then + m:ApplyTwirlGravity() + elseif action == Action.SHOT_FROM_CANNON then + m.Velocity -= Vector3.yAxis + + if m.Velocity.Y < -75 then + m.Velocity = Util.SetY(m.Velocity, -75) + end + elseif action == Action.LONG_JUMP or action == Action.SLIDE_KICK or action == Action.BBH_ENTER_SPIN then + m.Velocity -= (Vector3.yAxis * 2) + + if m.Velocity.Y < -75 then + m.Velocity = Util.SetY(m.Velocity, -75) + end + elseif action == Action.LAVA_BOOST or action == Action.FALL_AFTER_STAR_GRAB then + m.Velocity -= (Vector3.yAxis * 3.2) + + if m.Velocity.Y < -65 then + m.Velocity = Util.SetY(m.Velocity, -65) + end + elseif m:ShouldStrengthenGravityForJumpAscent() then + m.Velocity *= Vector3.new(1, 0.25, 1) + elseif m.Action:Has(ActionFlags.METAL_WATER) then + m.Velocity -= (Vector3.yAxis * 1.6) + + if m.Velocity.Y < -16 then + m.Velocity = Util.SetY(m.Velocity, -16) + end + elseif m.Flags:Has(MarioFlags.WING_CAP) and m.Velocity.Y < 0 and m.Input:Has(InputFlags.A_DOWN) then + m.BodyState.WingFlutter = true + m.Velocity -= (Vector3.yAxis * 2) + + if m.Velocity.Y < -37.5 then + m.Velocity += (Vector3.yAxis * 4) + + if m.Velocity.Y > -37.5 then + m.Velocity = Util.SetY(m.Velocity, -37.5) + end + end + else + m.Velocity -= (Vector3.yAxis * 4) + + if m.Velocity.Y < -75 then + m.Velocity = Util.SetY(m.Velocity, -75) + end + end +end + +function Mario.PerformAirStep(m: Mario, stepArg: number?) + local stepResult = AirStep.NONE + local stepArg = stepArg or 0 + m.Wall = nil + + for i = 1, 4 do + local intendedPos = m.Position + (m.Velocity / 4) + local result = m:PerformAirQuarterStep(intendedPos, stepArg) + + if result ~= AirStep.NONE then + stepResult = result + end + + if + result == AirStep.LANDED + or result == AirStep.GRABBED_LEDGE + or result == AirStep.GRABBED_CEILING + or result == AirStep.HIT_LAVA_WALL + then + break + end + end + + if m.Velocity.Y >= 0 then + m.PeakHeight = m.Position.Y + end + + m.TerrainType = m:GetTerrainType() + + if m.Action() ~= Action.FLYING then + m:ApplyGravity() + end + + m.GfxAngle = Vector3int16.new(0, m.FaceAngle.Y, 0) + + return stepResult +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- UPDATE ROUTINES +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.UpdateButtonInputs(m: Mario) + if m.Controller.ButtonPressed:Has(Buttons.A_BUTTON) then + m.Input:Add(InputFlags.A_PRESSED) + end + + if m.Controller.ButtonDown:Has(Buttons.A_BUTTON) then + m.Input:Add(InputFlags.A_DOWN) + end + + if m.SquishTimer == 0 then + if m.Controller.ButtonPressed:Has(Buttons.B_BUTTON) then + m.Input:Add(InputFlags.B_PRESSED) + end + + if m.Controller.ButtonDown:Has(Buttons.Z_TRIG) then + m.Input:Add(InputFlags.Z_DOWN) + end + + if m.Controller.ButtonPressed:Has(Buttons.Z_TRIG) then + m.Input:Add(InputFlags.Z_PRESSED) + end + end + + if m.Input:Has(InputFlags.A_PRESSED) then + m.FramesSinceA = 0 + elseif m.FramesSinceA < 255 then + m.FramesSinceA += 1 + end + + if m.Input:Has(InputFlags.B_PRESSED) then + m.FramesSinceB = 0 + elseif m.FramesSinceB < 255 then + m.FramesSinceB += 1 + end +end + +function Mario.UpdateJoystickInputs(m: Mario) + local controller = m.Controller + local mag = ((controller.StickMag / 64) * (controller.StickMag / 64)) * 64 + + if m.SquishTimer == 0 then + m.IntendedMag = mag / 2 + else + m.IntendedMag = mag / 8 + end + + if m.IntendedMag > 0 then + m.IntendedYaw = Util.Atan2s(-controller.StickY, controller.StickX) + m.Input:Add(InputFlags.NONZERO_ANALOG) + else + m.IntendedYaw = m.FaceAngle.Y + end +end + +function Mario.UpdateGeometryInputs(m: Mario) + local floorHeight, floor = Util.FindFloor(m.Position) + local ceilHeight, ceil = Util.FindCeil(m.Position, m.FloorHeight) + + m.FloorHeight = floorHeight + m.CeilHeight = ceilHeight + m.Floor = floor + m.Ceil = ceil + + if floor then + m.FloorAngle = Util.Atan2s(floor.Normal.Z, floor.Normal.X) + m.TerrainType = m:GetTerrainType() + + if m:FloorIsSlippery() then + m.Input:Add(InputFlags.ABOVE_SLIDE) + end + + if ceil then + local ceilToFloorDist = m.CeilHeight - m.FloorHeight + + if 0 < ceilToFloorDist and ceilToFloorDist < 150 then + m.Input:Add(InputFlags.SQUISHED) + end + end + + if m.Position.Y > m.FloorHeight + 100 then + m.Input:Add(InputFlags.OFF_FLOOR) + end + + if m.Position.Y < m.WaterLevel - 10 then + m.Input:Add(InputFlags.IN_WATER) + end + end +end + +function Mario.UpdateInputs(m: Mario) + m.ParticleFlags:Clear() + m.Flags:Band(0xFFFFFF) + m.Input:Clear() + + m:UpdateButtonInputs() + m:UpdateJoystickInputs() + m:UpdateGeometryInputs() + + if not m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED) then + m.Input:Add(InputFlags.UNKNOWN_5) + end + + if m.WallKickTimer > 0 then + m.WallKickTimer -= 1 + end + + if m.DoubleJumpTimer > 0 then + m.DoubleJumpTimer -= 1 + end +end + +function Mario.ResetBodyState(m: Mario) + local bodyState = m.BodyState + bodyState.CapState:Set(MarioCap.DEFAULT_CAP_OFF) + bodyState.EyeState = MarioEyes.BLINK + bodyState.HandState:Set(MarioHands.FISTS) + bodyState.ModelState:Clear() + bodyState.WingFlutter = false + + m.Flags:Remove(MarioFlags.METAL_SHOCK) +end + +function Mario.UpdateCaps(m: Mario): Flags + local flags = m.Flags + local action + + if m.CapTimer > 0 then + action = m.Action + + if m.CapTimer <= 60 then + m.CapTimer -= 1 + end + + if m.CapTimer == 0 then + m.Flags:Remove(MarioFlags.SPECIAL_CAPS) + + if not m.Flags:Has(MarioFlags.CAPS) then + m.Flags:Remove(MarioFlags.CAP_ON_HEAD) + end + end + end + + return flags +end + +function Mario.UpdateModel(m: Mario) + local modelState = Flags.new() + local bodyState = m.BodyState + local flags = m:UpdateCaps() + + if flags:Has(MarioFlags.VANISH_CAP) then + modelState:Add(ModelFlags.NOISE_ALPHA) + end + + if flags:Has(bit32.bor(MarioFlags.METAL_CAP, MarioFlags.METAL_SHOCK)) then + modelState:Add(ModelFlags.METAL) + end + + if m.InvincTimer >= 3 and bit32.band(Util.GlobalTimer, 1) > 0 then + modelState:Add(ModelFlags.INVISIBLE) + end + + if flags:Has(MarioFlags.CAP_IN_HAND) then + if flags:Has(MarioFlags.WING_CAP) then + bodyState.HandState:Set(MarioHands.HOLDING_WING_CAP) + else + bodyState.HandState:Set(MarioHands.HOLDING_CAP) + end + end + + if flags:Has(MarioFlags.CAP_ON_HEAD) then + if flags:Has(MarioFlags.WING_CAP) then + bodyState.CapState:Set(MarioCap.WING_CAP_ON) + else + bodyState.CapState:Set(MarioCap.DEFAULT_CAP_ON) + end + end + + if m.Action:Has(ActionFlags.SHORT_HITBOX) then + m.HitboxHeight = 100 + else + m.HitboxHeight = 160 + end +end + +function Mario.CheckKickOrPunchWall(m: Mario) + if m.Flags:Has(MarioFlags.PUNCHING, MarioFlags.KICKING, MarioFlags.TRIPPING) then + local range = Vector3.new(Util.Sins(m.FaceAngle.Y), 0, Util.Coss(m.FaceAngle.Y)) + local detector = m.Position + range + local disp, wall = Util.FindWallCollisions(detector, 80, 5) + + if wall then + if m.Action() ~= Action.MOVE_PUNCHING or m.ForwardVel >= 0 then + if m.Action() == Action.PUNCHING then + m.Action:Set(Action.MOVE_PUNCHING) + end + + m:SetForwardVel(-48) + m:PlaySound(Sounds.ACTION_HIT) + m.ParticleFlags:Add(ParticleFlags.TRIANGLE) + elseif m.Action:Has(ActionFlags.AIR) then + m:SetForwardVel(-16) + m:PlaySound(Sounds.ACTION_HIT) + m.ParticleFlags:Add(ParticleFlags.TRIANGLE) + end + end + end +end + +function Mario.ProcessInteractions(m: Mario) + if m.InvincTimer > 0 then + m.InvincTimer -= 1 + end + + m:CheckKickOrPunchWall() + m.Flags:Remove(MarioFlags.PUNCHING, MarioFlags.KICKING, MarioFlags.TRIPPING) +end + +function Mario.HandleSpecialFloors(m: Mario) + local floor = m.Floor + + if floor then + local part = floor.Instance :: BasePart + + if not m.Action:Has(ActionFlags.AIR, ActionFlags.SWIMMING) then + if part.Material == Enum.Material.CrackedLava then + if not m.Flags:Has(MarioFlags.METAL_CAP) then + m.HurtCounter += m.Flags:Has(MarioFlags.CAP_ON_HEAD) and 12 or 18 + end + + m:SetAction(Action.LAVA_BOOST) + end + end + end +end + +function Mario.ExecuteAction(m: Mario): number + if m.Action() == 0 then + return 0 + end + + m.AnimFrame += 1 + m.AnimFrame %= (m.AnimFrameCount + 1) + + if m.AnimAccel > 0 then + m.AnimAccelAssist += m.AnimAccel + m.AnimAccelAssist %= bit32.lshift(m.AnimFrameCount + 1, 0x10) + end + + if m.SquishTimer > 0 then + m.SquishTimer -= 1 + end + + m.GfxAngle *= 0 + m.AnimDirty = true + m.ThrowMatrix = nil + m.AnimSkipInterp = false + + m:ResetBodyState() + m:UpdateInputs() + + m:HandleSpecialFloors() + m:ProcessInteractions() + + if m.Floor == nil then + return 0 + end + + while m.Action() > 0 do + local id = m.Action() + local action = actions[id] + + if action then + if not action(m) then + break + end + else + local name = Enums.GetName(Action, id) + + if name then + warn("Unhandled Action:", name) + else + warn("UNKNOWN ACTION:", id) + end + + m.Action:Set(Action.IDLE) + break + end + end + + -- m:SinkInQuicksand() + -- m:SquishModel() + -- m:UpdateHealth() + m:UpdateModel() + + return m.ParticleFlags() +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- INITIALIZATION +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function Mario.new(): Mario + local bodyState: BodyState = { + Action = 0, + CapState = Flags.new(), + EyeState = 0, + HandState = Flags.new(), + WingFlutter = false, + ModelState = Flags.new(), + GrabPos = 0, + PunchType = 0, + PunchTimer = 0, + HeadAngle = Vector3int16.new(), + TorsoAngle = Vector3int16.new(), + HeldObjLastPos = Vector3.zero, + } + + local controller: Controller = { + RawStickX = 0, + RawStickY = 0, + + StickX = 0, + StickY = 0, + StickMag = 0, + + ButtonDown = Flags.new(), + ButtonPressed = Flags.new(), + } + + local state: MarioState = { + Input = Flags.new(), + + Flags = Flags.new(MarioFlags.NORMAL_CAP, MarioFlags.CAP_ON_HEAD), + Action = Flags.new(Action.SPAWN_SPIN_AIRBORNE), + + PrevAction = Flags.new(), + ParticleFlags = Flags.new(), + HitboxHeight = 0, + TerrainType = 0, + + ActionState = 0, + ActionTimer = 0, + ActionArg = 0, + + IntendedMag = 0, + IntendedYaw = 0, + InvincTimer = 0, + + FramesSinceA = 255, + FramesSinceB = 255, + + WallKickTimer = 0, + DoubleJumpTimer = 0, + + FaceAngle = Vector3int16.new(), + GfxAngle = Vector3int16.new(), + AngleVel = Vector3int16.new(), + ThrowMatrix = CFrame.identity, + + SlideYaw = 0, + TwirlYaw = 0, + + Position = Vector3.yAxis * 500, + Velocity = Vector3.zero, + + ForwardVel = 0, + SlideVelX = 0, + SlideVelZ = 0, + + CeilHeight = 0, + FloorHeight = 0, + FloorAngle = 0, + WaterLevel = 0, + + Health = 0x880, + HurtCounter = 0, + HealCounter = 0, + SquishTimer = 0, + + CapTimer = 0, + BurnTimer = 0, + PeakHeight = 0, + SteepJumpYaw = 0, + WalkingPitch = 0, + QuicksandDepth = 0, + LongJumpIsSlow = false, + + BodyState = bodyState, + Controller = controller, + + AnimAccel = 0, + AnimFrame = -1, + AnimSetFrame = -1, + AnimDirty = false, + AnimReset = false, + AnimFrameCount = 0, + AnimAccelAssist = 0, + } + + return setmetatable(state, Mario) +end + +return Mario diff --git a/sm64/Sounds.lua b/sm64/Sounds.lua new file mode 100644 index 0000000..58ad26a --- /dev/null +++ b/sm64/Sounds.lua @@ -0,0 +1,96 @@ +--!strict + +local System = script.Parent +local Assets = System.Assets +local Sounds = Assets.Sounds + +local Data = { + ACTION_BONK = Sounds.ACTION_BONK, + ACTION_FLYING_FAST = Sounds.ACTION_FLYING_FAST, + ACTION_HIT = Sounds.ACTION_HIT, + ACTION_METAL_BONK = Sounds.ACTION_METAL_BONK, + ACTION_METAL_HEAVY_LANDING = Sounds.ACTION_METAL_HEAVY_LANDING, + ACTION_METAL_LANDING = Sounds.ACTION_METAL_LANDING, + ACTION_METAL_STEP = Sounds.ACTION_METAL_STEP, + ACTION_PAT_BACK = Sounds.ACTION_PAT_BACK, + ACTION_SIDE_FLIP = Sounds.ACTION_SIDE_FLIP, + ACTION_SPIN = Sounds.ACTION_SPIN, + ACTION_HEAVY_LANDING = Sounds.ACTION_HEAVY_LANDING, + ACTION_TERRAIN_BODY_HIT_GROUND = Sounds.ACTION_TERRAIN_BODY_HIT_GROUND, + ACTION_TERRAIN_JUMP = Sounds.ACTION_TERRAIN_JUMP, + ACTION_TERRAIN_LANDING = Sounds.ACTION_TERRAIN_LANDING, + ACTION_TERRAIN_STEP = Sounds.ACTION_TERRAIN_STEP, + ACTION_THROW = Sounds.ACTION_THROW, + ACTION_TWIRL = Sounds.ACTION_TWIRL, + + MARIO_ATTACKED = Sounds.MARIO_ATTACKED, + MARIO_DOH = Sounds.MARIO_DOH, + MARIO_GROUND_POUND_WAH = Sounds.MARIO_GROUND_POUND_WAH, + MARIO_HAHA = Sounds.MARIO_HAHA, + MARIO_HOO = Sounds.MARIO_HOO, + MARIO_HOOHOO = Sounds.MARIO_HOOHOO, + MARIO_IMA_TIRED = Sounds.MARIO_IMA_TIRED, + MARIO_MAMA_MIA = Sounds.MARIO_MAMA_MIA, + MARIO_ON_FIRE = Sounds.MARIO_ON_FIRE, + MARIO_OOOF = Sounds.MARIO_OOOF, + MARIO_PANTING = Sounds.MARIO_PANTING, + MARIO_PUNCH_YAH = Sounds.MARIO_PUNCH_YAH, + MARIO_PUNCH_WAH = Sounds.MARIO_PUNCH_WAH, + MARIO_PUNCH_HOO = Sounds.MARIO_PUNCH_HOO, + MARIO_SNORING1 = Sounds.MARIO_SNORING1, + MARIO_SNORING2 = Sounds.MARIO_SNORING2, + MARIO_SNORING3 = Sounds.MARIO_SNORING3, + MARIO_UH = Sounds.MARIO_UH, + MARIO_UH2 = Sounds.MARIO_UH2, + MARIO_WAAAOOOW = Sounds.MARIO_WAAAOOOW, + MARIO_WAH = Sounds.MARIO_WAH, + MARIO_WAHA = Sounds.MARIO_WAHA, + MARIO_WHOA = Sounds.MARIO_WHOA, + MARIO_YAH = Sounds.MARIO_YAH, + MARIO_YAHOO = Sounds.MARIO_YAHOO, + MARIO_YAWNING = Sounds.MARIO_YAWNING, + MARIO_YIPPEE = Sounds.MARIO_YIPPEE, + + MOVING_FLYING = Sounds.MOVING_FLYING, + MOVING_LAVA_BURN = Sounds.MOVING_LAVA_BURN, + MOVING_TERRAIN_SLIDE = Sounds.MOVING_TERRAIN_SLIDE, + + MARIO_JUMP = Sounds.MARIO_JUMP, + MARIO_YAH_WAH_HOO = Sounds.MARIO_YAH_WAH_HOO, + MARIO_YAHOO_WAHA_YIPPEE = Sounds.MARIO_YAHOO_WAHA_YIPPEE, + + ACTION_TERRAIN_STEP_DEFAULT = Sounds.ACTION_TERRAIN_STEP_DEFAULT, + ACTION_TERRAIN_STEP_GRASS = Sounds.ACTION_TERRAIN_STEP_GRASS, + ACTION_TERRAIN_STEP_ICE = Sounds.ACTION_TERRAIN_STEP_ICE, + ACTION_TERRAIN_STEP_METAL = Sounds.ACTION_TERRAIN_STEP_METAL, + ACTION_TERRAIN_STEP_SAND = Sounds.ACTION_TERRAIN_STEP_SAND, + ACTION_TERRAIN_STEP_SNOW = Sounds.ACTION_TERRAIN_STEP_SNOW, + ACTION_TERRAIN_STEP_SPOOKY = Sounds.ACTION_TERRAIN_STEP_SPOOKY, + ACTION_TERRAIN_STEP_STONE = Sounds.ACTION_TERRAIN_STEP_STONE, + + ACTION_TERRAIN_LANDING_DEFAULT = Sounds.ACTION_TERRAIN_LANDING_DEFAULT, + ACTION_TERRAIN_LANDING_GRASS = Sounds.ACTION_TERRAIN_LANDING_GRASS, + ACTION_TERRAIN_LANDING_ICE = Sounds.ACTION_TERRAIN_LANDING_ICE, + ACTION_TERRAIN_LANDING_METAL = Sounds.ACTION_TERRAIN_LANDING_METAL, + ACTION_TERRAIN_LANDING_SAND = Sounds.ACTION_TERRAIN_LANDING_SAND, + ACTION_TERRAIN_LANDING_SNOW = Sounds.ACTION_TERRAIN_LANDING_SNOW, + ACTION_TERRAIN_LANDING_SPOOKY = Sounds.ACTION_TERRAIN_LANDING_SPOOKY, + ACTION_TERRAIN_LANDING_STONE = Sounds.ACTION_TERRAIN_LANDING_STONE, + + ACTION_TERRAIN_JUMP_DEFAULT = Sounds.ACTION_TERRAIN_LANDING_DEFAULT, + ACTION_TERRAIN_JUMP_GRASS = Sounds.ACTION_TERRAIN_LANDING_GRASS, + ACTION_TERRAIN_JUMP_ICE = Sounds.ACTION_TERRAIN_LANDING_ICE, + ACTION_TERRAIN_JUMP_METAL = Sounds.ACTION_TERRAIN_LANDING_METAL, + ACTION_TERRAIN_JUMP_SAND = Sounds.ACTION_TERRAIN_LANDING_SAND, + ACTION_TERRAIN_JUMP_SNOW = Sounds.ACTION_TERRAIN_LANDING_SNOW, + ACTION_TERRAIN_JUMP_SPOOKY = Sounds.ACTION_TERRAIN_LANDING_SPOOKY, + ACTION_TERRAIN_JUMP_STONE = Sounds.ACTION_TERRAIN_LANDING_STONE, +} + +setmetatable(Data, { + __index = function(t, k) + warn("UNKNOWN SOUND:", k) + end, +}) + +return table.freeze(Data) diff --git a/sm64/Types/Flags.lua b/sm64/Types/Flags.lua new file mode 100644 index 0000000..9bc93c4 --- /dev/null +++ b/sm64/Types/Flags.lua @@ -0,0 +1,53 @@ +--!strict + +local Flags = {} +Flags.__index = Flags + +export type Flags = { Value: number } +export type Class = typeof(setmetatable({} :: Flags, Flags)) + +function Flags.new(...: number): Class + local flags = { Value = bit32.bor(...) } + return setmetatable(flags, Flags) +end + +function Flags.__call(self: Class): number + return self.Value +end + +function Flags.Get(self: Class): number + return self.Value +end + +function Flags.Set(self: Class, ...: number) + self.Value = bit32.bor(...) +end + +function Flags.Copy(self: Class, flags: Class) + self.Value = flags.Value +end + +function Flags.Add(self: Class, ...: number) + self.Value = bit32.bor(self.Value, ...) +end + +function Flags.Has(self: Class, ...: number): boolean + local mask = bit32.bor(...) + return bit32.btest(self.Value, mask) +end + +function Flags.Remove(self: Class, ...: number) + local mask = bit32.bor(...) + local invert = bit32.bnot(mask) + self.Value = bit32.band(self.Value, invert) +end + +function Flags.Band(self: Class, ...: number) + self.Value = bit32.band(self.Value, ...) +end + +function Flags.Clear(self: Class) + self.Value = 0 +end + +return Flags diff --git a/sm64/Types/init.lua b/sm64/Types/init.lua new file mode 100644 index 0000000..06ed5f7 --- /dev/null +++ b/sm64/Types/init.lua @@ -0,0 +1,112 @@ +--!strict + +local Flags = require(script.Flags) +export type Flags = Flags.Class + +export type Controller = { + RawStickX: number, + RawStickY: number, + + StickX: number, + StickY: number, + StickMag: number, + + ButtonDown: Flags, + ButtonPressed: Flags, +} + +export type BodyState = { + Action: number, + CapState: Flags, + EyeState: number, + HandState: Flags, + WingFlutter: boolean, + ModelState: Flags, + GrabPos: number, + PunchType: number, + PunchTimer: number, + TorsoAngle: Vector3int16, + HeadAngle: Vector3int16, + HeldObjLastPos: Vector3, +} + +export type MarioState = { + Input: Flags, + Flags: Flags, + + Action: Flags, + PrevAction: Flags, + ParticleFlags: Flags, + HitboxHeight: number, + TerrainType: number, + HeldObj: Instance?, + + ActionState: number, + ActionTimer: number, + ActionArg: number, + + IntendedMag: number, + IntendedYaw: number, + InvincTimer: number, + + FramesSinceA: number, + FramesSinceB: number, + + WallKickTimer: number, + DoubleJumpTimer: number, + + FaceAngle: Vector3int16, + GfxAngle: Vector3int16, + AngleVel: Vector3int16, + ThrowMatrix: CFrame?, + + SlideYaw: number, + TwirlYaw: number, + + Position: Vector3, + Velocity: Vector3, + + ForwardVel: number, + SlideVelX: number, + SlideVelZ: number, + + Wall: RaycastResult?, + Ceil: RaycastResult?, + Floor: RaycastResult?, + + CeilHeight: number, + FloorHeight: number, + FloorAngle: number, + WaterLevel: number, + + BodyState: BodyState, + Controller: Controller, + + Health: number, + HurtCounter: number, + HealCounter: number, + SquishTimer: number, + + CapTimer: number, + BurnTimer: number, + PeakHeight: number, + SteepJumpYaw: number, + WalkingPitch: number, + QuicksandDepth: number, + LongJumpIsSlow: boolean, + + AnimCurrent: Animation?, + AnimFrameCount: number, + + AnimAccel: number, + AnimAccelAssist: number, + + AnimFrame: number, + AnimDirty: boolean, + AnimReset: boolean, + AnimSetFrame: number, + AnimSkipInterp: boolean?, +} + +local result = { Flags = Flags } +return table.freeze(result) diff --git a/sm64/Util/init.lua b/sm64/Util/init.lua new file mode 100644 index 0000000..e46351b --- /dev/null +++ b/sm64/Util/init.lua @@ -0,0 +1,267 @@ +--!strict + +local Util = { + GlobalTimer = 0, + Scale = 1 / 16, +} + +local rayParams = RaycastParams.new() +rayParams.CollisionGroup = "Player" + +local SHORT_TO_RAD = (2 * math.pi) / 0x10000 +local VECTOR3_XZ = Vector3.one - Vector3.yAxis + +local CARDINAL = { + -Vector3.xAxis, + -Vector3.zAxis, + Vector3.xAxis, + Vector3.zAxis, +} + +function Util.SetX(vec: Vector3, x: number): Vector3 + return Vector3.new(x, vec.Y, vec.Z) +end + +function Util.SetXint16(vec: Vector3int16, x: number): Vector3int16 + return Vector3int16.new(x, vec.Y, vec.Z) +end + +function Util.SetY(vec: Vector3, y: number): Vector3 + return Vector3.new(vec.X, y, vec.Z) +end + +function Util.SetYint16(vec: Vector3int16, y: number): Vector3int16 + return Vector3int16.new(vec.X, y, vec.Z) +end + +function Util.SetZ(vec: Vector3, z: number): Vector3 + return Vector3.new(vec.X, vec.Y, z) +end + +function Util.SetZint16(vec: Vector3int16, z: number): Vector3int16 + return Vector3int16.new(vec.X, vec.Y, z) +end + +function Util.ToRoblox(v: Vector3) + return v * Util.Scale +end + +function Util.ToSM64(v: Vector3) + return v / Util.Scale +end + +function Util.ToEulerAngles(v: Vector3int16): Vector3 + return Vector3.new(v.X, v.Y, v.Z) * SHORT_TO_RAD +end + +function Util.ToRotation(v: Vector3int16): CFrame + local angle = Util.ToEulerAngles(v) + + local matrix = CFrame.fromAxisAngle(Vector3.yAxis, angle.Y) + * CFrame.fromAxisAngle(Vector3.xAxis, -angle.X) + * CFrame.fromAxisAngle(Vector3.zAxis, -angle.Z) + + return matrix +end + +function Util.Raycast(pos: Vector3, dir: Vector3, rayParams: RaycastParams?, worldRoot: WorldRoot?): RaycastResult? + local root = worldRoot or workspace + local result: RaycastResult? = root:Raycast(pos, dir) + + if script:GetAttribute("Debug") then + local color = Color3.new(result and 0 or 1, result and 1 or 0, 0) + + local line = Instance.new("LineHandleAdornment") + line.CFrame = CFrame.new(pos, pos + dir) + line.Length = dir.Magnitude + line.Thickness = 3 + line.Color3 = color + line.Adornee = workspace.Terrain + line.Parent = workspace.Terrain + + task.delay(2, line.Destroy, line) + end + + return result +end + +function Util.RaycastSM64(pos: Vector3, dir: Vector3, rayParams: RaycastParams?, worldRoot: WorldRoot?): RaycastResult? + local result: RaycastResult? = Util.Raycast(pos * Util.Scale, dir * Util.Scale, rayParams, worldRoot) + + if result then + -- Cast back to SM64 unit scale. + result = { + Normal = result.Normal, + Material = result.Material, + Instance = result.Instance, + Distance = result.Distance / Util.Scale, + Position = result.Position / Util.Scale, + } :: any + end + + return result +end + +function Util.FindFloor(pos: Vector3): (number, RaycastResult?) + local trunc = Vector3int16.new(pos.X, pos.Y, pos.Z) + local height = -11000 + + if math.abs(trunc.X) >= 0x2000 then + return height, nil + end + + if math.abs(trunc.Z) >= 0x2000 then + return height, nil + end + + local newPos = Vector3.new(trunc.X, trunc.Y, trunc.Z) + local result = Util.RaycastSM64(newPos + (Vector3.yAxis * 100), -Vector3.yAxis * 10000) + + if result then + local height = Util.SignedShort(result.Position.Y) + result.Position = Vector3.new(pos.X, height, pos.Z) + + return height, result + else + return height, nil + end +end + +function Util.FindCeil(pos: Vector3, height: number?): (number, RaycastResult?) + local pos = Vector3.new(pos.X, (height or pos.Y) + 80, pos.Z) + local result = Util.RaycastSM64(pos, Vector3.yAxis * 10000) + + if result then + return result.Position.Y, result + else + return 10000, nil + end +end + +function Util.FindWallCollisions(pos: Vector3, offset: number, radius: number): (Vector3, RaycastResult?) + local origin = pos + Vector3.new(0, offset, 0) + local walls: { RaycastResult } = {} + local lastWall: RaycastResult? + local disp = Vector3.zero + + for i, dir in CARDINAL do + local contact = Util.RaycastSM64(origin, dir * radius) + + if contact then + local normal = contact.Normal + + if math.abs(normal.Y) < 0.01 then + local surface = contact.Position + local offset = (surface - pos) * VECTOR3_XZ + local dist = offset.Magnitude + + if dist < radius then + disp += (contact.Normal * VECTOR3_XZ) * (radius - dist) + lastWall = contact + end + end + end + end + + return pos + disp, lastWall +end + +function Util.SignedShort(x: number) + return -0x8000 + math.floor((x + 0x8000) % 0x10000) +end + +function Util.SignedInt(x: number) + return -0x80000000 + math.floor(x + 0x80000000) % 0x100000000 +end + +function Util.ApproachFloat(current: number, target: number, inc: number, dec: number?): number + if dec == nil then + dec = inc + end + + assert(dec) + + if current < target then + current = math.min(target, current + inc) + else + current = math.max(target, current - dec) + end + + return current +end + +function Util.ApproachInt(current: number, target: number, inc: number, dec: number?): number + if dec == nil then + dec = inc + end + + assert(dec) + + if current < target then + current = Util.SignedInt(current + inc) + current = math.min(target, current) + else + current = Util.SignedInt(current - dec) + current = math.max(target, current) + end + + return Util.SignedInt(current) +end + +function Util.Sins(short: number): number + short = Util.SignedShort(short) + return math.sin(short * SHORT_TO_RAD) +end + +function Util.Coss(short: number): number + short = Util.SignedShort(short) + return math.cos(short * SHORT_TO_RAD) +end + +local function atan2_lookup(y: number, x: number) + return math.atan2(y, x) / SHORT_TO_RAD +end + +function Util.Atan2s(y: number, x: number): number + local ret: number + + if x >= 0 then + if y >= 0 then + if y >= x then + ret = atan2_lookup(x, y) + else + ret = 0x4000 - atan2_lookup(y, x) + end + else + y = -y + + if y < x then + ret = 0x4000 + atan2_lookup(y, x) + else + ret = 0x8000 - atan2_lookup(x, y) + end + end + else + x = -x + + if y < 0 then + y = -y + + if y >= x then + ret = 0x8000 + atan2_lookup(x, y) + else + ret = 0xC000 - atan2_lookup(y, x) + end + else + if y < x then + ret = 0xC000 + atan2_lookup(y, x) + else + ret = -atan2_lookup(x, y) + end + end + end + + return Util.SignedShort(ret) +end + +return table.freeze(Util) diff --git a/sm64/Util/init.meta.json b/sm64/Util/init.meta.json new file mode 100644 index 0000000..066f19e --- /dev/null +++ b/sm64/Util/init.meta.json @@ -0,0 +1,5 @@ +{ + "attributes": { + "Debug": false + } +} \ No newline at end of file diff --git a/sm64/init.client.lua b/sm64/init.client.lua new file mode 100644 index 0000000..da86f8c --- /dev/null +++ b/sm64/init.client.lua @@ -0,0 +1,473 @@ +--!strict + +local Players = game:GetService("Players") :: Players +local RunService = game:GetService("RunService") :: RunService +local StarterGui = game:GetService("StarterGui") :: StarterGui +local TweenService = game:GetService("TweenService") :: TweenService +local UserInputService = game:GetService("UserInputService") :: UserInputService +local ReplicatedStorage = game:GetService("ReplicatedStorage") :: ReplicatedStorage +local ContextActionService = game:GetService("ContextActionService") :: ContextActionService + +local Sounds = require(script.Sounds) +local Enums = require(script.Enums) +local Mario = require(script.Mario) +local Types = require(script.Types) +local Util = require(script.Util) + +local Action = Enums.Action +local Buttons = Enums.Buttons +local InputFlags = Enums.InputFlags +local ParticleFlags = Enums.ParticleFlags + +type InputType = Enum.UserInputType | Enum.KeyCode +type Controller = Types.Controller +type Mario = Mario.Class + +local player: Player = assert(Players.LocalPlayer) +local STEP_RATE = 30 + +local PARTICLE_CLASSES = { + Fire = true, + Smoke = true, + Sparkles = true, + ParticleEmitter = true, +} + +local FLIP = CFrame.Angles(0, math.pi, 0) + +------------------------------------------------------------------------------------------------------------------------------------------------- +-- Input Driver +------------------------------------------------------------------------------------------------------------------------------------------------- + +local MATH_TAU = math.pi * 2 +local BUTTON_FEED: { Enum.UserInputState } = {} +local BUTTON_A = "BTN_" .. Buttons.A_BUTTON + +local function toStrictNumber(str: string): number + local result = tonumber(str) + return assert(result, "Invalid number!") +end + +local function processAction(id: string, state: Enum.UserInputState) + if id == "MarioDebug" then + if state == Enum.UserInputState.Begin then + local isDebug = not script.Util:GetAttribute("Debug") + local character = player.Character + + local rootPart = if character then character.PrimaryPart else nil + + if rootPart then + local action = rootPart:FindFirstChild("Action") + + if action and action:IsA("BillboardGui") then + action.Enabled = isDebug + end + end + + script.Util:SetAttribute("Debug", isDebug) + end + else + local button = toStrictNumber(id:sub(5)) + BUTTON_FEED[button] = state + end +end + +local function bindInput(button: number, label: string, ...: InputType) + local id = "BTN_" .. button + ContextActionService:BindAction(id, processAction, true, ...) + + if UserInputService.TouchEnabled then + ContextActionService:SetTitle(id, label) + end +end + +local function updateCollisions() + for i, player in Players:GetPlayers() do + assert(player:IsA("Player")) + + local rootPart = if player.Character then player.Character.PrimaryPart else nil + + if rootPart then + local parts = rootPart:GetConnectedParts(true) + + for i, part in parts do + if part:IsA("BasePart") then + part.CanCollide = false + end + end + end + end +end + +local function updateController(controller: Controller, humanoid: Humanoid) + local moveDir = humanoid.MoveDirection + local pos = Vector2.new(moveDir.X, -moveDir.Z) + local len = math.min(1, pos.Magnitude) + + controller.StickMag = len * 64 + controller.StickX = pos.X * 64 + controller.StickY = pos.Y * 64 + + humanoid:ChangeState(Enum.HumanoidStateType.Physics) + controller.ButtonPressed:Clear() + + if humanoid.Jump then + BUTTON_FEED[Buttons.A_BUTTON] = Enum.UserInputState.Begin + humanoid.Jump = false + elseif controller.ButtonDown:Has(Buttons.A_BUTTON) then + BUTTON_FEED[Buttons.A_BUTTON] = Enum.UserInputState.End + end + + for button, state in pairs(BUTTON_FEED) do + if state == Enum.UserInputState.Begin then + controller.ButtonDown:Add(button) + controller.ButtonPressed:Add(button) + elseif state == Enum.UserInputState.End then + controller.ButtonDown:Remove(button) + end + end + + table.clear(BUTTON_FEED) +end + +ContextActionService:BindAction("MarioDebug", processAction, false, Enum.KeyCode.P) +bindInput(Buttons.B_BUTTON, "B", Enum.UserInputType.MouseButton1, Enum.KeyCode.ButtonX) +bindInput(Buttons.Z_TRIG, "Z", Enum.KeyCode.LeftShift, Enum.KeyCode.RightShift, Enum.KeyCode.ButtonL2) + +------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Network Dispatch +------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local Commands = {} + +local lazyNetwork = ReplicatedStorage:WaitForChild("LazyNetwork") +assert(lazyNetwork:IsA("RemoteEvent"), "bad lazyNetwork!") + +function Commands.PlaySound(player: Player, name: string) + local sound: Sound? = Sounds[name] + local character = player.Character + + local rootPart = if character then character.PrimaryPart else nil + + if rootPart and sound then + local oldSound: Instance? = rootPart:FindFirstChild(name) + + if oldSound and oldSound:IsA("Sound") and name:find("MARIO") then + oldSound.TimePosition = 0 + else + local newSound: Sound = sound:Clone() + newSound.Parent = rootPart + newSound:Play() + + newSound.Ended:Connect(function() + newSound:Destroy() + end) + end + end +end + +function Commands.SetParticle(player: Player, name: string, set: boolean) + local character = player.Character + + local rootPart = if character then character.PrimaryPart else nil + + if rootPart then + local particles = rootPart:FindFirstChild("Particles") + + local inst = if particles then particles:FindFirstChild(name) else nil + + if inst and PARTICLE_CLASSES[inst.ClassName] then + local particle = inst :: ParticleEmitter + local emit = particle:GetAttribute("Emit") + + if typeof(emit) == "number" then + particle:Emit(emit) + elseif set ~= nil then + particle.Enabled = set + end + end + end +end + +function Commands.SetAngle(player: Player, angle: Vector3int16) + local character = player.Character + + local waist = if character then character:FindFirstChild("Waist", true) else nil + + if waist and waist:IsA("Motor6D") then + local props = { C1 = Util.ToRotation(-angle) + waist.C1.Position } + local tween = TweenService:Create(waist, TweenInfo.new(0.1), props) + tween:Play() + end +end + +local function processCommand(player: Player, cmd: string, ...: any) + local command = Commands[cmd] + + if command then + task.spawn(command, player, ...) + else + warn("Unknown Command:", cmd, ...) + end +end + +local function networkDispatch(cmd: string, ...: any) + lazyNetwork:FireServer(cmd, ...) + processCommand(player, cmd, ...) +end + +local function onNetworkReceive(target: Player, cmd: string, ...: any) + if target ~= player then + processCommand(target, cmd, ...) + end +end + +lazyNetwork.OnClientEvent:Connect(onNetworkReceive) + +------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Mario Driver +------------------------------------------------------------------------------------------------------------------------------------------------------------- + +local lastUpdate = os.clock() +local lastAngle: Vector3int16? + +local mario: Mario = Mario.new() +local controller = mario.Controller + +local enumMap = {} +local goalCF: CFrame +local activeTrack: AnimationTrack? +local peakSpeed = 0 + +local reset = Instance.new("BindableEvent") +reset.Archivable = false +reset.Parent = script +reset.Name = "Reset" + +while not player.Character do + player.CharacterAdded:Wait() +end + +local character = assert(player.Character) +local pivot = character:GetPivot().Position +mario.Position = Util.ToSM64(pivot) + +local function onReset() + local roblox = Vector3.yAxis * 100 + local sm64 = Util.ToSM64(roblox) + local char = player.Character + + if char then + local reset = char:FindFirstChild("Reset") + local cf = CFrame.new(roblox) + + if reset and reset:IsA("RemoteEvent") then + reset:FireServer() + end + + char:PivotTo(cf) + end + + mario.SlideVelX = 0 + mario.SlideVelZ = 0 + mario.ForwardVel = 0 + mario.IntendedYaw = 0 + + mario.Position = sm64 + mario.Velocity = Vector3.zero + mario.FaceAngle = Vector3int16.new() + + mario:SetAction(Action.SPAWN_SPIN_AIRBORNE) +end + +local function update() + local character = player.Character + + if not character then + return + end + + local now = os.clock() + local gfxRot = CFrame.identity + + local humanoid = if character then character:FindFirstChildOfClass("Humanoid") else nil + + local simSpeed = tonumber(script:GetAttribute("TimeScale") or nil) or 1 + local frames = math.floor((now - lastUpdate) * (STEP_RATE * simSpeed)) + + if frames > 0 and humanoid then + lastUpdate = now + updateCollisions() + + for i = 1, math.min(4, frames) do + updateController(mario.Controller, humanoid) + mario:ExecuteAction() + end + + local pos = Util.ToRoblox(mario.Position) + local rot = Util.ToRotation(mario.FaceAngle) + + gfxRot = Util.ToRotation(mario.GfxAngle) + goalCF = CFrame.new(pos) * FLIP * gfxRot + end + + local interp = math.min(1, simSpeed / 2) + + if character and goalCF then + local cf = character:GetPivot() + local rootPart = character.PrimaryPart + local animator = character:FindFirstChildWhichIsA("Animator", true) + + if animator and (mario.AnimDirty or mario.AnimReset) and mario.AnimFrame >= 0 then + local anim = mario.AnimCurrent + local animSpeed = 0.1 / simSpeed + + if activeTrack and (activeTrack.Animation ~= anim or mario.AnimReset) then + activeTrack:Stop(animSpeed) + activeTrack = nil + end + + if not activeTrack and anim then + local track = animator:LoadAnimation(anim) + track:Play(animSpeed, 1, 0) + activeTrack = track + end + + if activeTrack then + local speed = mario.AnimAccel / 0x10000 + + if speed > 0 then + activeTrack:AdjustSpeed(speed * simSpeed) + else + activeTrack:AdjustSpeed(simSpeed) + end + end + + mario.AnimDirty = false + mario.AnimReset = false + end + + if activeTrack and mario.AnimSetFrame > -1 then + activeTrack.TimePosition = mario.AnimSetFrame / STEP_RATE + mario.AnimSetFrame = -1 + end + + if rootPart then + local action = rootPart:FindFirstChild("Action") + local particles = rootPart:FindFirstChild("Particles") + local alignPos = rootPart:FindFirstChildOfClass("AlignPosition") + local alignCF = rootPart:FindFirstChildOfClass("AlignOrientation") + local throw = mario.ThrowMatrix + + if throw then + local pos = Util.ToRoblox(throw.Position) + goalCF = throw.Rotation * FLIP + pos + end + + if alignCF then + cf = cf:Lerp(goalCF, interp) + alignCF.CFrame = cf.Rotation + end + + local debugLabel = if action then action:FindFirstChildOfClass("TextLabel") else nil + + if debugLabel then + local actionId = mario.Action() + + local anim = if activeTrack then activeTrack.Animation else nil + + local animName = if anim then anim.Name else nil + + local debugText = "Action: " + .. Enums.GetName(Action, actionId) + .. "\n" + .. "Animation: " + .. tostring(animName) + .. "\n" + .. "ForwardVel: " + .. string.format("%.2f", mario.ForwardVel) + + debugLabel.Text = debugText + end + + if alignPos then + alignPos.Position = cf.Position + end + + local bodyState = mario.BodyState + local action = mario.Action() + + if action ~= Action.BUTT_SLIDE and action ~= Action.WALKING then + bodyState.TorsoAngle *= 0 + end + + local ang = bodyState.TorsoAngle + + if ang ~= lastAngle then + networkDispatch("SetAngle", ang) + lastAngle = ang + end + + if particles then + for name, flag in pairs(ParticleFlags) do + local inst = particles:FindFirstChild(name) + + if inst and PARTICLE_CLASSES[inst.ClassName] then + local name = inst.Name + local particle = inst :: ParticleEmitter + + local emit = particle:GetAttribute("Emit") + local hasFlag = mario.ParticleFlags:Has(flag) + + if emit then + if hasFlag then + networkDispatch("SetParticle", name) + end + elseif particle.Enabled ~= hasFlag then + networkDispatch("SetParticle", name, hasFlag) + end + end + end + end + + for name: string, sound: Sound in pairs(Sounds) do + local looped = false + + if sound:IsA("Sound") then + if sound.TimeLength == 0 then + continue + end + + looped = sound.Looped + end + + if sound:GetAttribute("Play") then + networkDispatch("PlaySound", sound.Name) + + if not looped then + sound:SetAttribute("Play", false) + end + elseif looped then + sound:Stop() + end + end + + character:PivotTo(cf) + end + end +end + +reset.Event:Connect(onReset) +RunService.Heartbeat:Connect(update) + +while task.wait(1) do + local success = pcall(function() + return StarterGui:SetCore("ResetButtonCallback", reset) + end) + + if success then + break + end +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/tools/ImportAnimations.lua b/tools/ImportAnimations.lua new file mode 100644 index 0000000..bdb5198 --- /dev/null +++ b/tools/ImportAnimations.lua @@ -0,0 +1,33 @@ +local AvatarImportService = game:GetService("AvatarImportService") +local resume = Instance.new("BindableEvent") + +for i, anim in pairs(game.ReplicatedFirst.SM64.Assets.Animations:GetChildren()) do + local path = "C:/Users/clone/Desktop/MarioAnims/" .. anim.Name .. ".fbx" + print("Importing", anim) + + task.defer(function() + local success, err = pcall(function() + AvatarImportService:ImportFBXAnimationFromFilePathUserMayChooseModel(path, workspace.Mario, function() + local bin = game.ServerStorage.AnimSaves + local old = bin:FindFirstChild(anim.Name) + + if old then + old:Destroy() + end + + local kfs = AvatarImportService:ImportLoadedFBXAnimation(false) + kfs.Name = anim.Name + kfs.Parent = bin + + resume:Fire() + end) + end) + + if not success then + warn("ERROR IMPORTING", anim, err) + resume:Fire() + end + end) + + resume.Event:Wait() +end diff --git a/tools/RetargetAnimations.lua b/tools/RetargetAnimations.lua new file mode 100644 index 0000000..f3c3646 --- /dev/null +++ b/tools/RetargetAnimations.lua @@ -0,0 +1,175 @@ +--!strict + +local ServerStorage = game:GetService("ServerStorage") +local StarterCharacter = workspace.StarterCharacter + +local MarioAnim = workspace.MarioAnim +local MarioBase = workspace.MarioBase +local Player = workspace.Player + +local HIERARCHY: { [string]: { [string]: string }? } = { + HumanoidRootPart = { LowerTorso = "Root" }, + + LowerTorso = { + UpperTorso = "Waist", + LeftUpperLeg = "LeftHip", + RightUpperLeg = "RightHip", + }, + + UpperTorso = { + Head = "Neck", + LeftUpperArm = "LeftShoulder", + RightUpperArm = "RightShoulder", + }, + + LeftUpperArm = { LeftLowerArm = "LeftElbow" }, + LeftLowerArm = { LeftHand = "LeftWrist" }, + + RightUpperArm = { RightLowerArm = "RightElbow" }, + RightLowerArm = { RightHand = "RightWrist" }, + + LeftUpperLeg = { LeftLowerLeg = "LeftKnee" }, + LeftLowerLeg = { LeftFoot = "LeftAnkle" }, + + RightUpperLeg = { RightLowerLeg = "RightKnee" }, + RightLowerLeg = { RightFoot = "RightAnkle" }, +} + +local BASE_KEYFRAME = ServerStorage.BASE_KEYFRAME + +local statusHint = Instance.new("Hint") +local statusText = "%s [%d/%d]" +statusHint.Parent = workspace +statusHint.Name = "Status" + +local function updateAnim() + StarterCharacter.Humanoid.Animator:StepAnimations(0) + Player.Humanoid.Animator:StepAnimations(0) + task.wait() +end + +local function clearAnims() + for i, desc: Instance in pairs(workspace:GetDescendants()) do + if desc:IsA("Bone") then + desc.Transform = CFrame.identity + elseif desc:IsA("Motor6D") then + desc.Transform = CFrame.identity + elseif desc:IsA("Animator") then + task.defer(desc.StepAnimations, desc, 0) + end + end + + task.wait() +end + +local function applyMotors(at: Instance) + local name0 = if at:IsA("Keyframe") then "HumanoidRootPart" else at.Name + local part0 = StarterCharacter:FindFirstChild(name0) + local data = HIERARCHY[name0] + + if data and part0 and part0:IsA("BasePart") then + for name1, motorName in data do + local part1 = StarterCharacter:FindFirstChild(name1) + + if part1 and part1:IsA("BasePart") then + local att: Attachment = part1:FindFirstChild(motorName .. "RigAttachment") + local bone: Bone = MarioBase:FindFirstChild(motorName, true) + + local motor: Motor6D = part1:FindFirstChild(motorName) + motor.Transform = att.WorldCFrame:ToObjectSpace(bone.TransformedWorldCFrame) + + local playerMotor = workspace.Player:FindFirstChild(motorName, true) + local pose = at:FindFirstChild(name1) + + if playerMotor and playerMotor:IsA("Motor6D") then + if motorName:find("Left") or motorName:find("Right") then + playerMotor.Transform = motor.Transform.Rotation + else + playerMotor.Transform = motor.Transform + end + end + + updateAnim() + + if pose and pose:IsA("Pose") then + if motorName:find("Left") or motorName:find("Right") then + pose.CFrame = motor.Transform.Rotation + else + pose.CFrame = motor.Transform + end + + applyMotors(pose) + end + end + end + end +end + +local function remapKeyframe(keyframe: Keyframe): Keyframe + clearAnims() + + for i, desc: Instance in pairs(keyframe:GetDescendants()) do + if desc:IsA("Pose") then + local bone: Instance? = MarioAnim:FindFirstChild(desc.Name, true) + + if bone and bone:IsA("Bone") then + bone.Transform = desc.CFrame + end + end + end + + for i, desc in MarioBase:GetDescendants() do + if desc:IsA("Bone") then + local anim = MarioAnim:FindFirstChild(desc.Name, true) + + if anim then + local offset = desc.TransformedWorldCFrame:ToObjectSpace(anim.TransformedWorldCFrame) + desc.Transform = offset + end + end + end + + local newKeyframe = BASE_KEYFRAME:Clone() + newKeyframe.Name = keyframe.Name + newKeyframe.Time = keyframe.Time + applyMotors(newKeyframe) + + return newKeyframe +end + +local function remapKeyframeSequence(kfs: KeyframeSequence): KeyframeSequence + local keyframes = kfs:GetKeyframes() + clearAnims() + + local newKfs = kfs:Clone() + newKfs:ClearAllChildren() + + for i, keyframe in keyframes do + if keyframe:IsA("Keyframe") then + local text = statusText:format(kfs.Name, i, #keyframes) + statusHint.Text = text + + local newKeyframe = remapKeyframe(keyframe) + newKeyframe.Parent = newKfs + end + end + + return newKfs +end + +local animSaves = ServerStorage.AnimSaves:GetChildren() +local animSavesR15 = ServerStorage.AnimSaves_R15 + +table.sort(animSaves, function(a, b) + return a.Name < b.Name +end) + +for i, animSave in animSaves do + if animSave:IsA("KeyframeSequence") then + local kfs = remapKeyframeSequence(animSave) + kfs.Parent = animSavesR15 + end +end + +clearAnims() +statusHint:Destroy() diff --git a/tools/UploadAnimations.lua b/tools/UploadAnimations.lua new file mode 100644 index 0000000..cbe53f9 --- /dev/null +++ b/tools/UploadAnimations.lua @@ -0,0 +1,316 @@ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +-- SHA256 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +--!strict + +local band = bit32.band +local bnot = bit32.bnot +local bxor = bit32.bxor + +local rrotate = bit32.rrotate +local rshift = bit32.rshift + +local primes = { + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2, +} + +local function toHex(str: string): string + local result = str:gsub(".", function(char) + return string.format("%02x", char:byte()) + end) + + return result +end + +local function toBytes(value: number, length: number) + local str = "" + + for i = 1, length do + local rem = value % 256 + str = string.char(rem) .. str + value = (value - rem) / 256 + end + + return str +end + +local function digestBlock(msg: string, i: number, hash: { number }, digest: { number }) + for j = 1, 16 do + local offset = i + (j - 1) * 4 + local a, b, c, d = string.byte(msg, offset, offset + 3) + digest[j] = ((a * 256 + b) * 256 + c) * 256 + d + end + + for j = 17, 64 do + local v = digest[j - 15] + local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) + + v = digest[j - 2] + digest[j] = digest[j - 16] + s0 + digest[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) + end + + local a, b, c, d, e, f, g, h = table.unpack(hash) + + for i = 1, 64 do + local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) + local maj = bxor(band(a, b), band(a, c), band(b, c)) + + local t2 = s0 + maj + local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) + + local ch = bxor(band(e, f), band(bnot(e), g)) + local t1 = h + s1 + ch + primes[i] + digest[i] + + h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2 + end + + hash[1] = band(hash[1] + a) + hash[2] = band(hash[2] + b) + hash[3] = band(hash[3] + c) + hash[4] = band(hash[4] + d) + hash[5] = band(hash[5] + e) + hash[6] = band(hash[6] + f) + hash[7] = band(hash[7] + g) + hash[8] = band(hash[8] + h) +end + +local function sha256(msg: string): string + local extra = 64 - ((#msg + 9) % 64) + local len = toBytes(8 * #msg, 8) + + msg ..= "\128" .. string.rep("\0", extra) .. len + assert(#msg % 64 == 0) + + local hash = { + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19, + } + + local digest = {} + + for i = 1, #msg, 64 do + digestBlock(msg, i, hash, digest) + end + + local result = "" + + for i = 1, 8 do + local value = hash[i] + result = result .. toBytes(value, 4) + end + + return toHex(result) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +-- Upload Animations +------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +local StudioAssetService = game:GetService("StudioAssetService") +local ContentProvider = game:GetService("ContentProvider") +local HttpService = game:GetService("HttpService") + +local FLIP = CFrame.Angles(0, math.pi, 0) +type Params = { [string]: any } + +local function makeQueryString(data: Params) + -- NOTE - This function can be used to create a query string of parameters + -- at the end of url query, or create a application/form-url-encoded post body string + local params = {} + + -- NOTE - Arrays are handled, but generally data is expected to be flat. + for key, value in data do + if value ~= nil then -- for optional params + if type(value) == "table" then + for i = 1, #value do + local str = HttpService:UrlEncode(value[i]) + table.insert(params, key .. "=" .. str) + end + else + local str = HttpService:UrlEncode(value) + table.insert(params, key .. "=" .. str) + end + end + end + + return table.concat(params, "&") +end + +local function uploadAnimation(anim: Animation) + local loop: boolean = not not (anim:GetAttribute("Loop")) + local flip: boolean = not not (anim:GetAttribute("Flip")) + + local kfs: KeyframeSequence = game.ServerStorage.AnimSaves:FindFirstChild(anim.Name) + local startFrame: number = tonumber(anim:GetAttribute("StartFrame") or nil) or -1 + + if kfs and kfs:IsA("KeyframeSequence") then + kfs = kfs:Clone() + else + return + end + + local id = tonumber(anim.AnimationId:match("%d+$")) + local params: Params = {} + local uploadUrl: string + + if id then + uploadUrl = "ide/publish/uploadexistinganimation?" + params.assetID = id + else + uploadUrl = "ide/publish/uploadnewanimation?" + params.allowComments = "True" + params.assetTypeName = "Animation" + params.ispublic = "False" + params.isGamesAsset = "False" + params.description = "" + params.name = anim.Name + params.groupId = "" + params.AllID = 1 + end + + local baseUrl = ContentProvider.BaseUrl + uploadUrl = baseUrl .. uploadUrl .. makeQueryString(params) + + if not loop then + local endAt = kfs:FindFirstChild("End") + + if endAt and endAt:IsA("Keyframe") then + local stall = endAt:Clone() + stall.Name = "Freeze" + stall.Parent = kfs + stall.Time = 9999 + end + end + + if startFrame then + local timeDiff = startFrame / 30 + + for i, kf in kfs:GetChildren() do + if kf:IsA("Keyframe") then + kf.Time -= timeDiff + + if kf.Time < 0 then + kf:Destroy() + end + end + end + end + + if flip then + for i, kf in kfs:GetChildren() do + if kf:IsA("Keyframe") then + local lowerTorso = kf:FindFirstChild("LowerTorso", true) + + if lowerTorso and lowerTorso:IsA("Pose") then + local cf = lowerTorso.CFrame + lowerTorso.CFrame = FLIP * cf + end + end + end + end + + local tmp = Instance.new("Folder") + tmp.Parent = workspace + tmp.Name = "Upload" + kfs.Parent = tmp + + local body = StudioAssetService:SerializeInstances(tmp:GetChildren()) + local newHash = sha256(body) + tmp:Destroy() + + if anim:GetAttribute("UploadHash") ~= newHash then + warn("Uploading Animation", anim.Name .. "...") + + local success, response = pcall(function() + print("POST", uploadUrl) + return game:HttpPostAsync(uploadUrl, body, "application/octet-stream") + end) + + if success then + anim.AnimationId = "rbxassetid://" .. response + anim:SetAttribute("UploadHash", newHash) + + print("\tSuccess!") + else + print("\tError uploading", anim.Name .. ": ", response) + end + else + warn(anim.Name, "is up to date!") + end +end + +for i, anim in game.ReplicatedFirst.SM64.Assets.Animations:GetChildren() do + if anim:IsA("Animation") then + uploadAnimation(anim) + end +end diff --git a/tools/extract_anims.py b/tools/extract_anims.py new file mode 100644 index 0000000..3c90d0c --- /dev/null +++ b/tools/extract_anims.py @@ -0,0 +1,225 @@ +import subprocess + +anim_dict = { + "4EC690": "SLOW_LEDGE_GRAB", + "4ED1D0": "FALL_OVER_BACKWARDS", + "4ED1E8": "BACKWARD_AIR_KB", + "4EECAC": "DYING_ON_BACK", + "4EFED4": "BACKFLIP", + "4F08C0": "CLIMB_UP_POLE", + "4F2078": "GRAB_POLE_SHORT", + "4F2508": "GRAB_POLE_SWING_PART1", + "4F2520": "GRAB_POLE_SWING_PART2", + "4F38F0": "HANDSTAND_IDLE", + "4F43F4": "HANDSTAND_JUMP", + "4F440C": "START_HANDSTAND", + "4F2BF0": "RETURN_FROM_HANDSTAND", + "4F4A64": "IDLE_ON_POLE", + "4F4E7C": "A_POSE", + "4F4FE0": "SKID_ON_GROUND", + "4F4FF8": "STOP_SKID", + "4F56EC": "CROUCH_FROM_FAST_LONGJUMP", + "4F5C98": "CROUCH_FROM_SLOW_LONGJUMP", + "4F62D4": "FAST_LONGJUMP", + "4F6A78": "SLOW_LONGJUMP", + "4F6FDC": "AIRBORNE_ON_STOMACH", + "4F7494": "WALK_WITH_LIGHT_OBJ", + "4F870C": "RUN_WITH_LIGHT_OBJ", + "4F93A0": "SLOW_WALK_WITH_LIGHT_OBJ", + "4FA618": "SHIVERING_WARMING_HAND", + "4FBC18": "SHIVERING_RETURN_TO_IDLE", + "4FC1A8": "SHIVERING", + "4FCDE8": "CLIMB_DOWN_LEDGE", + "4FD208": "CREDITS_WAVING", + "4FD880": "CREDITS_LOOK_UP", + "4FDF90": "CREDITS_RETURN_FROM_LOOK_UP", + "4FE3F4": "CREDITS_RAISE_HAND", + "5000DC": "CREDITS_LOWER_HAND", + "500C24": "CREDITS_TAKE_OFF_CAP", + "501410": "CREDITS_START_WALK_LOOK_UP", + "50353C": "CREDITS_LOOK_BACK_THEN_RUN", + "505AF0": "FINAL_BOWSER_RAISE_HAND_SPIN", + "507B58": "FINAL_BOWSER_WING_CAP_TAKE_OFF", + "509924": "CREDITS_PEACE_SIGN", + "50BD4C": "STAND_UP_FROM_LAVA_BOOST", + "50C254": "FIRE_LAVA_BURN", + "50C5B0": "WING_CAP_FLY", + "50CBA8": "HANG_ON_OWL", + "50D2EC": "LAND_ON_STOMACH", + "50D304": "FORWARD_AIR_KB", + "50EA0C": "DYING_ON_STOMACH", + "50F6F4": "SUFFOCATING", + "511504": "COUGHING", + "512B4C": "THROW_CATCH_KEY", + "515604": "DYING_FALL_OVER", + "5175EC": "IDLE_ON_LEDGE", + "518218": "FAST_LEDGE_GRAB", + "518840": "HANG_ON_CEILING", + "5197CC": "PUT_CAP_ON", + "51A754": "TAKE_CAP_OFF_THEN_ON", + "51C314": "QUICKLY_PUT_CAP_ON", + "51C774": "HEAD_STUCK_IN_GROUND", + "51F90C": "GROUND_POUND_LANDING", + "51FB98": "TRIPLE_JUMP_GROUND_POUND", + "520160": "START_GROUND_POUND", + "520178": "GROUND_POUND", + "520594": "BOTTOM_STUCK_IN_GROUND", + "52338C": "IDLE_WITH_LIGHT_OBJ", + "5240B8": "JUMP_LAND_WITH_LIGHT_OBJ", + "524614": "JUMP_WITH_LIGHT_OBJ", + "524940": "FALL_LAND_WITH_LIGHT_OBJ", + "524E10": "FALL_WITH_LIGHT_OBJ", + "524F78": "FALL_FROM_SLIDING_WITH_LIGHT_OBJ", + "525318": "SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ", + "525330": "STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ", + "5258EC": "RIDING_SHELL", + "525D48": "WALKING", + "527248": "FORWARD_FLIP", + "527870": "JUMP_RIDING_SHELL", + "527BEC": "LAND_FROM_DOUBLE_JUMP", + "52826C": "DOUBLE_JUMP_FALL", + "528620": "SINGLE_JUMP", + "528638": "LAND_FROM_SINGLE_JUMP", + "5290E4": "AIR_KICK", + "529824": "DOUBLE_JUMP_RISE", + "529ADC": "START_FORWARD_SPINNING", + "529FDC": "THROW_LIGHT_OBJECT", + "52A460": "FALL_FROM_SLIDE_KICK", + "52AD28": "BEND_KNESS_RIDING_SHELL", + "52B360": "LEGS_STUCK_IN_GROUND", + "52E078": "GENERAL_FALL", + "52E090": "GENERAL_LAND", + "52E7BC": "BEING_GRABBED", + "52ED0C": "GRAB_HEAVY_OBJECT", + "52FA0C": "SLOW_LAND_FROM_DIVE", + "5307F0": "FLY_FROM_CANNON", + "530BD4": "MOVE_ON_WIRE_NET_RIGHT", + "5311D4": "MOVE_ON_WIRE_NET_LEFT", + "531760": "MISSING_CAP", + "5347C4": "PULL_DOOR_WALK_IN", + "535C9C": "PUSH_DOOR_WALK_IN", + "536D64": "UNLOCK_DOOR", + "539FEC": "START_REACH_POCKET", + "53A3E8": "REACH_POCKET", + "53AB58": "STOP_REACH_POCKET", + "53B14C": "GROUND_THROW", + "53B904": "GROUND_KICK", + "53C1B4": "FIRST_PUNCH", + "53C44C": "SECOND_PUNCH", + "53C6B4": "FIRST_PUNCH_FAST", + "53CAC4": "SECOND_PUNCH_FAST", + "53CFFC": "PICK_UP_LIGHT_OBJ", + "53D4BC": "PUSHING", + "53DAD4": "START_RIDING_SHELL", + "53E0F0": "PLACE_LIGHT_OBJ", + "53E674": "FORWARD_SPINNING", + "53E68C": "BACKWARD_SPINNING", + "53E804": "BREAKDANCE", + "53F138": "RUNNING", + "53F150": "RUNNING_UNUSED", + "54035C": "SOFT_BACK_KB", + "540BA4": "SOFT_FRONT_KB", + "54141C": "DYING_IN_QUICKSAND", + "542758": "IDLE_IN_QUICKSAND", + "54320C": "MOVE_IN_QUICKSAND", + "545BF8": "ELECTROCUTION", + "546B40": "SHOCKED", + "546DE8": "BACKWARD_KB", + "547834": "FORWARD_KB", + "548244": "IDLE_HEAVY_OBJ", + "543B40": "STAND_AGAINST_WALL", + "549A84": "SIDESTEP_LEFT", + "54A9C0": "SIDESTEP_RIGHT", + "54BB6C": "START_SLEEP_IDLE", + "54CD54": "START_SLEEP_SCRATCH", + "54E4F4": "START_SLEEP_YAWN", + "54F888": "START_SLEEP_SITTING", + "550C30": "SLEEP_IDLE", + "550E88": "SLEEP_START_LYING", + "551AF4": "SLEEP_LYING", + "552224": "DIVE", + "55223C": "SLIDE_DIVE", + "55283C": "GROUND_BONK", + "5534F4": "STOP_SLIDE_LIGHT_OBJ", + "5541A4": "SLIDE_KICK", + "554540": "CROUCH_FROM_SLIDE_KICK", + "554A94": "SLIDE_MOTIONLESS", + "554AAC": "STOP_SLIDE", + "555214": "FALL_FROM_SLIDE", + "55571C": "SLIDE", + "55593C": "TIPTOE", + "557030": "TWIRL_LAND", + "5573A0": "TWIRL", + "557504": "START_TWIRL", + "557730": "STOP_CROUCHING", + "557AEC": "START_CROUCHING", + "557DA0": "CROUCHING", + "55897C": "CRAWLING", + "55A5F8": "STOP_CRAWLING", + "55A990": "START_CRAWLING", + "55AD1C": "SUMMON_STAR", + "55C144": "RETURN_STAR_APPROACH_DOOR", + "55C9F0": "BACKWARDS_WATER_KB", + "55DAAC": "SWIM_WITH_OBJ_PART1", + "55DEC8": "SWIM_WITH_OBJ_PART2", + "55E164": "FLUTTERKICK_WITH_OBJ", + "55E610": "WATER_ACTION_END_WITH_OBJ", + "55ECCC": "STOP_GRAB_OBJ_WATER", + "55F800": "WATER_IDLE_WITH_OBJ", + "55FF88": "DROWNING_PART1", + "5614B8": "DROWNING_PART2", + "5627F4": "WATER_DYING", + "5634FC": "WATER_FORWARD_KB", + "564558": "FALL_FROM_WATER", + "564D2C": "SWIM_PART1", + "56520C": "SWIM_PART2", + "565610": "FLUTTERKICK", + "565DD0": "WATER_ACTION_END", + "566628": "WATER_PICK_UP_OBJ", + "566AF8": "WATER_GRAB_OBJ_PART2", + "56747C": "WATER_GRAB_OBJ_PART1", + "567748": "WATER_THROW_OBJ", + "5681D0": "WATER_IDLE", + "568A04": "WATER_STAR_DANCE", + "56A478": "RETURN_FROM_WATER_STAR_DANCE", + "56ACC4": "GRAB_BOWSER", + "56ACDC": "SWINGING_BOWSER", + "56B358": "RELEASE_BOWSER", + "56C010": "HOLDING_BOWSER", + "56C42C": "HEAVY_THROW", + "56CD54": "WALK_PANTING", + "56D244": "WALK_WITH_HEAVY_OBJ", + "56E37C": "TURNING_PART1", + "56E394": "TURNING_PART2", + "56EAA0": "SLIDEFLIP_LAND", + "56EEAC": "SLIDEFLIP", + "56F750": "TRIPLE_JUMP_LAND", + "5701F8": "TRIPLE_JUMP", + "570CB0": "FIRST_PERSON", + "5722F4": "IDLE_HEAD_LEFT", + "572BA0": "IDLE_HEAD_RIGHT", + "57344C": "IDLE_HEAD_CENTER", + "573CF8": "HANDSTAND_LEFT", + "574490": "HANDSTAND_RIGHT", + "5750C0": "WAKE_FROM_SLEEP", + "575844": "WAKE_FROM_LYING", + "5760EC": "START_TIPTOE", + "5769D0": "SLIDEJUMP", + "5769E8": "START_WALLKICK", + "577064": "STAR_DANCE", + "5785B8": "RETURN_FROM_STAR_DANCE", + "578C80": "FORWARD_SPINNING_FLIP", + "579828": "TRIPLE_JUMP_FLY", +} + +for offset, name in anim_dict.items(): + print("WRITING ANIMATION: " + name + " FROM OFFSET: " + offset) + + subprocess.run([ + "C:/Program Files/Blender Foundation/Blender 2.91/blender.exe", + "C:/Users/clone/Desktop/mario_skel.blend", "-b", + "-P", "C:/Users/clone/Desktop/mass_export.py", + "--", offset, name + ]) + + print("DONE!") \ No newline at end of file diff --git a/tools/mass_export.py b/tools/mass_export.py new file mode 100644 index 0000000..9c7a639 --- /dev/null +++ b/tools/mass_export.py @@ -0,0 +1,33 @@ +import bpy +import sys +import time + +from fast64.fast64_internal.sm64 import * +argv = sys.argv + +try: + index = argv.index("--") + 1 +except ValueError: + index = len(argv) + +argv = argv[index:] + +offset = argv[0] +name = argv[1] + +romfileSrc = open("C:/Users/clone/Downloads/Super Mario 64 (USA)/sm64.z64", 'rb') +mario_geo = bpy.data.objects['mario_geo'] + +levelParsed = sm64_level_parser.parseLevelAtPointer(romfileSrc, sm64_level_parser.level_pointers[bpy.context.scene.levelAnimImport]) +segmentData = levelParsed.segmentData + +animStart = int(offset, 16) +sm64_anim.importAnimationToBlender(romfileSrc, animStart, mario_geo, segmentData, True) + +bpy.ops.export_scene.fbx(filepath="C:/Users/clone/Desktop/MarioAnims/" + name + ".fbx", add_leaf_bones=False, global_scale=3) +mario_geo.animation_data_clear() + +print("Exported: " + name) +romfileSrc.close() + +bpy.ops.wm.quit_blender() \ No newline at end of file