--!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)
}


--!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,
}


--!strict

return {
	NONZERO_ANALOG = 0x0001,
	A_PRESSED = 0x0002,
	OFF_FLOOR = 0x0004,
	ABOVE_SLIDE = 0x0008,
	FIRST_PERSON = 0x0010,
	NO_MOVEMENT = 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,
}


--!strict

return {
	DEFAULT_CAP_ON = 0,
	DEFAULT_CAP_OFF = 1,

	WING_CAP_ON = 2,
	WING_CAP_OFF = 3,
}


--!strict

return {
	BLINK = 0,
	OPEN = 1,

	HALF_CLOSED = 2,
	CLOSED = 3,

	DEAD = 4,
}


--!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


--!strict

return {
	FISTS = 0,
	OPEN = 1,
	PEACE_SIGN = 2,
	HOLDING_CAP = 3,
	HOLDING_WING_CAP = 4,
	RIGHT_OPEN = 5,
}


--!strict

return {
	NONZERO_ANALOG = 0x0001,
	A_PRESSED = 0x0002,
	OFF_FLOOR = 0x0004,
	ABOVE_SLIDE = 0x0008,
	FIRST_PERSON = 0x0010,
	NO_MOVEMENT = 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,
}


--!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),
}


--!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,
}


--!strict

return {
	LEFT_GROUND = 0,
	NONE = 1,
	HIT_WALL = 2,
	HIT_WALL_STOP_QSTEPS = 2,
	HIT_WALL_CONTINUE_QSTEPS = 3,
}


--!strict

return {
	NONE = 0,
	HIT_FLOOR = 1,
	HIT_CEILING = 2,
	CANCELLED = 3,
	HIT_WALL = 4,
}


--!strict

return {
	DEFAULT = 0x0000,
	VERY_SLIPPERY = 0x0013,
	SLIPPERY = 0x0014,
	NOT_SLIPPERY = 0x0015,
}


--!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


--!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),

	FloorType = require(script.FloorType),
	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)


--!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 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 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.SetY(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
		return 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.SetY(m.AngleVel, 0x10)
			end
		else
			local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x10, 0x20)
			m.AngleVel = Util.SetY(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.SetY(m.AngleVel, -0x10)
			end
		else
			local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x20, 0x10)
			m.AngleVel = Util.SetY(m.AngleVel, y)
		end
	else
		local y = Util.ApproachInt(m.AngleVel.Y, 0, 0x40)
		m.AngleVel = Util.SetY(m.AngleVel, y)
	end

	m.FaceAngle += Vector3int16.new(0, m.AngleVel.Y, 0)
	m.FaceAngle = Util.SetZ(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.SetX(m.AngleVel, 0x20)
			end
		else
			local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x20, 0x40)
			m.AngleVel = Util.SetX(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.SetX(m.AngleVel, -0x20)
			end
		else
			local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x40, 0x20)
			m.AngleVel = Util.SetX(m.AngleVel, x)
		end
	else
		local x = Util.ApproachInt(m.AngleVel.X, 0, 0x40)
		m.AngleVel = Util.SetX(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 += Vector3int16.new((m.ForwardVel - 32) * 6, 0, 0)
	elseif m.ForwardVel > 4 then
		m.FaceAngle += Vector3int16.new((m.ForwardVel - 32) * 10, 0, 0)
	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.SetX(m.FaceAngle, 0x2AAA)
	end

	if m.FaceAngle.X < -0x2AAA then
		m.FaceAngle = Util.SetX(m.FaceAngle, -0x2AAA)
	end

	local velX = Util.Coss(m.FaceAngle.X) * Util.Sins(m.FaceAngle.Y)
	m.SlideVelX = m.ForwardVel * velX

	local velZ = Util.Coss(m.FaceAngle.X) * Util.Coss(m.FaceAngle.Y)
	m.SlideVelZ = m.ForwardVel * 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, 0)
	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.SetY(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.SetX(m.FaceAngle, -0x2AAA)
			end
		end

		m.GfxAngle = Util.SetX(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.WATER_JUMP, function(m: Mario)
	if m.ForwardVel < 15 then
		m:SetForwardVel(15)
	end

	m:PlaySound(Sounds.ACTION_WATER_EXIT)
	m:SetAnimation(Animations.SINGLE_JUMP)

	local step = m:PerformAirStep(AirStep.CHECK_LEDGE_GRAB)

	if step == AirStep.LANDED then
		m:SetAction(Action.JUMP_LAND)
	elseif step == AirStep.HIT_WALL then
		m:SetForwardVel(15)
	elseif step == AirStep.GRABBED_LEDGE then
		m:SetAnimation(Animations.IDLE_ON_LEDGE)
		m:SetAction(Action.LEDGE_GRAB)
	elseif step == 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.SetY(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
		m.GfxAngle = Vector3int16.new(0, m.FaceAngle.Y, 0)

		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.SetX(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
	elseif m.ForwardVel >= 38 then
		m.WallKickTimer = 5

		if m.Velocity.Y > 0 then
			m.Velocity = Util.SetY(m.Velocity, 0)
		end

		m.ParticleFlags:Add(ParticleFlags.VERTICAL_STAR)
		return m:SetAction(Action.BACKWARD_AIR_KB)
	else
		m.WallKickTimer = 5

		if m.Velocity.Y > 0 then
			m.Velocity = Util.SetY(m.Velocity, 0)
		end

		if m.ForwardVel > 8 then
			m:SetForwardVel(-8)
		end

		return m:SetAction(Action.SOFT_BONK)
	end

	return m:SetAnimation(Animations.START_WALLKICK) > 0
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.SetX(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.MARIO_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
		m.GfxAngle = Util.SetX(m.GfxAngle, -m.FaceAngle.X)
		m.GfxAngle = Util.SetZ(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.SetX(m.FaceAngle, -0x2AAA)
			end

			m.GfxAngle = Util.SetX(m.GfxAngle, -m.FaceAngle.X)
			m.GfxAngle = Util.SetZ(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)

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


--!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)


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 AirStep = Enums.AirStep
local WaterStep = Enums.WaterStep
local GroundStep = Enums.GroundStep
local InputFlags = Enums.InputFlags
local MarioFlags = Enums.MarioFlags
local ActionFlags = Enums.ActionFlags
local ParticleFlags = Enums.ParticleFlags

local MIN_SWIM_STRENGTH = 160
local MIN_SWIM_SPEED = 16

local sWasAtSurface = false
local sSwimStrength = MIN_SWIM_STRENGTH

local sBobTimer = 0
local sBobIncrement = 0
local sBobHeight = 0

type Mario = System.Mario

local function setSwimmingAtSurfaceParticles(m: Mario, particleFlag: number)
	local atSurface = m.Position.Y >= m.WaterLevel - 130

	if atSurface then
		m.ParticleFlags:Add(particleFlag)

		if atSurface ~= sWasAtSurface then
			m:PlaySound(Sounds.ACTION_UNKNOWN431)
		end
	end

	sWasAtSurface = atSurface
end

local function swimmingNearSurface(m: Mario)
	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return false
	end

	return (m.WaterLevel - 80) - m.Position.Y < 400
end

local function getBuoyancy(m: Mario)
	local buoyancy = 0

	if m.Flags:Has(MarioFlags.METAL_CAP) then
		if m.Action:Has(ActionFlags.INVULNERABLE) then
			buoyancy = -2
		else
			buoyancy = -18
		end
	elseif swimmingNearSurface(m) then
		buoyancy = 1.25
	elseif not m.Action:Has(ActionFlags.MOVING) then
		buoyancy = -2
	end

	return buoyancy
end

local function performWaterFullStep(m: Mario, nextPos: Vector3)
	local adjusted, wall = Util.FindWallCollisions(nextPos, 10, 110)
	nextPos = adjusted

	local floorHeight, floor = Util.FindFloor(nextPos)
	local ceilHeight = Util.FindCeil(nextPos, floorHeight)

	if floor == nil then
		return WaterStep.CANCELLED
	end

	if nextPos.Y >= floorHeight then
		if ceilHeight - nextPos.Y >= 160 then
			m.Position = nextPos
			m.Floor = floor
			m.FloorHeight = floorHeight

			if wall then
				return WaterStep.HIT_WALL
			else
				return WaterStep.NONE
			end
		end

		if ceilHeight - floorHeight < 160 then
			return WaterStep.CANCELLED
		end

		--! Water ceiling downwarp
		m.Position = Util.SetY(nextPos, ceilHeight - 160)
		m.Floor = floor
		m.FloorHeight = floorHeight
		return WaterStep.HIT_CEILING
	else
		if ceilHeight - floorHeight < 160 then
			return WaterStep.CANCELLED
		end

		m.Position = Util.SetY(nextPos, floorHeight)
		m.Floor = floor
		m.FloorHeight = floorHeight
		return WaterStep.HIT_FLOOR
	end
end

local function applyWaterCurrent(m: Mario, step: Vector3): Vector3
	-- TODO: Implement if actually needed.
	--       This normally handles whirlpools and moving
	--       water, neither of which I think I'll be using.

	return step
end

local function performWaterStep(m: Mario)
	local nextPos = m.Position
	local step = m.Velocity

	if m.Action:Has(ActionFlags.SWIMMING) then
		step = applyWaterCurrent(m, step)
	end

	nextPos += step

	if nextPos.Y > m.WaterLevel - 80 then
		nextPos = Util.SetY(nextPos, m.WaterLevel - 80)
		m.Velocity *= Vector3.new(1, 0, 1)
	end

	local stepResult = performWaterFullStep(m, nextPos)
	m.GfxAngle = m.FaceAngle * Vector3int16.new(-1, 1, 1)
	m.GfxPos = m.Position

	return stepResult
end

local function updateWaterPitch(m: Mario)
	local gfxAngle = m.GfxAngle

	if gfxAngle.X > 0 then
		local angle = 60 * Util.Sins(gfxAngle.X) * Util.Sins(gfxAngle.X)
		m.GfxPos += Vector3.new(0, angle, 0)
	end

	if gfxAngle.X < 0 then
		local x = gfxAngle.X * 6 / 10
		gfxAngle = Util.SetX(gfxAngle, x)
	end

	if gfxAngle.X > 0 then
		local x = gfxAngle.X * 10 / 8
		gfxAngle = Util.SetX(gfxAngle, x)
	end

	m.GfxAngle = gfxAngle
end

local function stationarySlowDown(m: Mario)
	local buoyancy = getBuoyancy(m)
	m.AngleVel *= Vector3int16.new(0, 0, 1)
	m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 1, 1)

	local faceY = m.FaceAngle.Y
	local faceX = Util.ApproachInt(m.FaceAngle.X, 0, 0x200, 0x200)
	local faceZ = Util.ApproachInt(m.FaceAngle.Z, 0, 0x100, 0x100)

	local velY = Util.ApproachFloat(m.Velocity.Y, buoyancy, 2, 1)
	local velX = m.ForwardVel * Util.Coss(faceX) * Util.Sins(faceY)
	local velZ = m.ForwardVel * Util.Coss(faceX) * Util.Coss(faceY)

	m.FaceAngle = Vector3int16.new(faceX, faceY, faceZ)
	m.Velocity = Vector3.new(velX, velY, velZ)
end

local function updateSwimmingSpeed(m: Mario, maybeDecelThreshold: number?)
	local buoyancy = getBuoyancy(m)
	local decelThreshold = maybeDecelThreshold or MIN_SWIM_SPEED

	if m.Action:Has(ActionFlags.STATIONARY) then
		m.ForwardVel -= 2
	end

	m.ForwardVel = math.clamp(m.ForwardVel, 0, 28)

	if m.ForwardVel > decelThreshold then
		m.ForwardVel -= 0.5
	end

	m.Velocity = Vector3.new(
		m.ForwardVel * Util.Coss(m.FaceAngle.X) * Util.Sins(m.FaceAngle.Y),
		m.ForwardVel * Util.Sins(m.FaceAngle.X) + buoyancy,
		m.ForwardVel * Util.Coss(m.FaceAngle.X) * Util.Coss(m.FaceAngle.Y)
	)
end

local function updateSwimmingYaw(m: Mario)
	local targetYawVel = -Util.SignedShort(10 * m.Controller.StickX)

	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.SetY(m.AngleVel, 0x10)
			end
		else
			local velY = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x10, 0x20)
			m.AngleVel = Util.SetY(m.AngleVel, velY)
		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.SetY(m.AngleVel, -0x10)
			end
		else
			local velY = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x20, 0x10)
			m.AngleVel = Util.SetY(m.AngleVel, velY)
		end
	else
		local velY = Util.ApproachInt(m.AngleVel.Y, 0, 0x40, 0x40)
		m.AngleVel = Util.SetY(m.AngleVel, velY)
	end

	m.FaceAngle += Vector3int16.new(0, m.AngleVel.Y, 0)
	m.FaceAngle = Util.SetZ(m.FaceAngle, -m.AngleVel.Y * 8)
end

local function updateSwimmingPitch(m: Mario)
	local targetPitch = -Util.SignedShort(252 * m.Controller.StickY)

	-- stylua: ignore
	local pitchVel = if m.FaceAngle.X < 0
		then 0x100
		else 0x200

	if m.FaceAngle.X < targetPitch then
		m.FaceAngle += Vector3int16.new(pitchVel, 0, 0)

		if m.FaceAngle.X > targetPitch then
			m.FaceAngle = Util.SetX(m.FaceAngle, targetPitch)
		end
	elseif m.FaceAngle.X > targetPitch then
		m.FaceAngle -= Vector3int16.new(pitchVel, 0, 0)

		if m.FaceAngle.X < targetPitch then
			m.FaceAngle = Util.SetX(m.FaceAngle, targetPitch)
		end
	end
end

local function commonIdleStep(m: Mario, anim: Animation, maybeAccel: number?)
	local accel = maybeAccel or 0
	local bodyState = m.BodyState
	local headAngleX = bodyState.HeadAngle.X

	updateSwimmingYaw(m)
	updateSwimmingPitch(m)
	updateSwimmingSpeed(m)
	performWaterStep(m)
	updateWaterPitch(m)

	if m.FaceAngle.X > 0 then
		headAngleX = Util.ApproachInt(headAngleX, m.FaceAngle.X / 2, 0x80, 0x200)
	else
		headAngleX = Util.ApproachInt(headAngleX, 0, 0x200, 0x200)
	end

	if accel == 0 then
		m:SetAnimation(anim)
	else
		m:SetAnimationWithAccel(anim, accel)
	end

	setSwimmingAtSurfaceParticles(m, ParticleFlags.IDLE_WATER_WAVE)
end

local function resetBobVariables(m: Mario)
	sBobTimer = 0
	sBobIncrement = 0x800
	sBobHeight = m.FaceAngle.X / 256 + 20
end

local function surfaceSwimBob(m: Mario)
	if sBobIncrement ~= 0 and m.Position.Y > m.WaterLevel - 85 and m.FaceAngle.Y >= 0 then
		sBobTimer += sBobIncrement

		if sBobTimer >= 0 then
			m.GfxPos += Vector3.new(0, sBobHeight * Util.Sins(sBobTimer), 0)
			return
		end
	end

	sBobIncrement = 0
end

local function commonSwimmingStep(m: Mario, swimStrength: number)
	local waterStep
	updateSwimmingYaw(m)
	updateSwimmingPitch(m)
	updateSwimmingSpeed(m, swimStrength / 10)

	-- do water step
	waterStep = performWaterStep(m)

	if waterStep == WaterStep.HIT_FLOOR then
		local floorPitch = -m:FindFloorSlope(-0x8000)

		if m.FaceAngle.X < floorPitch then
			m.FaceAngle = Util.SetX(m.FaceAngle, floorPitch)
		end
	elseif waterStep == WaterStep.HIT_CEILING then
		if m.FaceAngle.Y > -0x3000 then
			m.FaceAngle -= Vector3int16.new(0, 0x100, 0)
		end
	elseif waterStep == WaterStep.HIT_WALL then
		if m.Controller.StickY == 0 then
			if m.FaceAngle.X > 0 then
				m.FaceAngle += Vector3int16.new(0x200, 0, 0)

				if m.FaceAngle.X > 0x3F00 then
					m.FaceAngle = Util.SetX(m.FaceAngle, 0x3F00)
				end
			else
				m.FaceAngle -= Vector3int16.new(0x200, 0, 0)

				if m.FaceAngle.X < -0x3F00 then
					m.FaceAngle = Util.SetX(m.FaceAngle, -0x3F00)
				end
			end
		end
	end

	local headAngle = m.BodyState.HeadAngle
	updateWaterPitch(m)

	local angleX = Util.ApproachInt(headAngle.X, 0, 0x200, 0x200)
	m.BodyState.HeadAngle = Util.SetX(headAngle, angleX)

	surfaceSwimBob(m)
	setSwimmingAtSurfaceParticles(m, ParticleFlags.WAVE_TRAIL)
end

local function playSwimmingNoise(m: Mario)
	local animFrame = m.AnimFrame

	if animFrame == 0 or animFrame == 12 then
		m:PlaySound(Sounds.ACTION_SWIM_KICK)
	end
end

local function checkWaterJump(m: Mario)
	local probe = Util.SignedInt(m.Position.Y + 1.5)

	if m.Input:Has(InputFlags.A_PRESSED) then
		if probe >= m.WaterLevel - 80 and m.FaceAngle.X >= 0 and m.Controller.StickY < -60 then
			m.AngleVel = Vector3int16.new()
			m.Velocity = Util.SetY(m.Velocity, 62)

			return m:SetAction(Action.WATER_JUMP)
		end
	end

	return false
end

local function playMetalWaterJumpingSound(m: Mario, landing: boolean)
	if not m.Flags:Has(MarioFlags.ACTION_SOUND_PLAYED) then
		m.ParticleFlags:Add(ParticleFlags.MIST_CIRCLE)
	end

	m:PlaySoundIfNoFlag(
		landing and Sounds.ACTION_METAL_LAND_WATER or Sounds.ACTION_METAL_JUMP_WATER,
		MarioFlags.ACTION_SOUND_PLAYED
	)
end

local function playMetalWaterWalkingSound(m: Mario)
	if m:IsAnimPastFrame(10) or m:IsAnimPastFrame(49) then
		m:PlaySound(Sounds.ACTION_METAL_STEP_WATER)
		m.ParticleFlags:Add(ParticleFlags.DUST)
	end
end

local function updateMetalWaterWalkingSpeed(m: Mario)
	local val = m.IntendedMag / 1.5
	local floor = m.Floor

	if m.ForwardVel <= 0 then
		m.ForwardVel += 1.1
	elseif m.ForwardVel <= val then
		m.ForwardVel += 1.1 - m.ForwardVel / 43
	elseif floor and floor.Normal.Y >= 0.95 then
		m.ForwardVel -= 1
	end

	if m.ForwardVel > 32 then
		m.ForwardVel = 32
	end

	local faceY = m.IntendedYaw - Util.ApproachInt(Util.SignedShort(m.IntendedYaw - m.FaceAngle.Y), 0, 0x800, 0x800)
	m.FaceAngle = Util.SetY(m.FaceAngle, faceY)

	m.SlideVelX = m.ForwardVel * Util.Sins(faceY)
	m.SlideVelZ = m.ForwardVel * Util.Coss(faceY)

	m.Velocity = Vector3.new(m.SlideVelX, 0, m.SlideVelZ)
end

local function updateMetalWaterJumpSpeed(m: Mario)
	local waterSurface = m.WaterLevel - 100

	if m.Velocity.Y > 0 and m.Position.Y > waterSurface then
		return true
	end

	if m.Input:Has(InputFlags.NONZERO_ANALOG) then
		local intendedDYaw = Util.SignedShort(m.IntendedYaw - m.FaceAngle.Y)
		m.ForwardVel += 0.8 * Util.Coss(intendedDYaw)
		m.FaceAngle += Vector3int16.new(0, 0x200 * Util.Sins(intendedDYaw), 0)
	else
		m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 0, 0.25, 0.25)
	end

	if m.ForwardVel > 16 then
		m.ForwardVel -= 1
	end

	if m.ForwardVel < 0 then
		m.ForwardVel += 2
	end

	local velY = m.Velocity.Y
	local velX = m.ForwardVel * Util.Sins(m.FaceAngle.Y)
	local velZ = m.ForwardVel * Util.Coss(m.FaceAngle.Y)

	m.SlideVelX = velX
	m.SlideVelZ = velZ
	m.Velocity = Vector3.new(velX, velY, velZ)

	return false
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

local DEF_ACTION: (number, (Mario) -> boolean) -> () = System.RegisterAction

DEF_ACTION(Action.WATER_IDLE, function(m: Mario)
	local val = 0x10000

	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.METAL_WATER_FALLING, 1)
	end

	if m.Input:Has(InputFlags.B_PRESSED) then
		return m:SetAction(Action.WATER_PUNCH)
	end

	if m.Input:Has(InputFlags.A_PRESSED) then
		return m:SetAction(Action.BREASTSTROKE)
	end

	if m.FaceAngle.X < -0x1000 then
		val = 0x30000
	end

	commonIdleStep(m, Animations.WATER_IDLE, val)
	return false
end)

DEF_ACTION(Action.WATER_ACTION_END, function(m: Mario)
	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.METAL_WATER_FALLING, 1)
	end

	if m.Input:Has(InputFlags.B_PRESSED) then
		return m:SetAction(Action.WATER_PUNCH)
	end

	if m.Input:Has(InputFlags.A_PRESSED) then
		return m:SetAction(Action.BREASTSTROKE)
	end

	commonIdleStep(m, Animations.WATER_ACTION_END)

	if m:IsAnimAtEnd() then
		m:SetAction(Action.WATER_IDLE)
	end

	return false
end)

DEF_ACTION(Action.BREASTSTROKE, function(m: Mario)
	if m.ActionArg == 0 then
		sSwimStrength = MIN_SWIM_STRENGTH
	end

	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.METAL_WATER_FALLING, 1)
	end

	if m.Input:Has(InputFlags.B_PRESSED) then
		return m:SetAction(Action.WATER_PUNCH)
	end

	m.ActionTimer += 1

	if m.ActionTimer == 14 then
		return m:SetAction(Action.FLUTTER_KICK)
	end

	if checkWaterJump(m) then
		return true
	end

	if m.ActionTimer < 6 then
		m.ForwardVel += 0.5
	end

	if m.ActionTimer >= 9 then
		m.ForwardVel += 1.5
	end

	if m.ActionTimer >= 2 then
		if m.ActionTimer < 6 and m.Input:Has(InputFlags.A_PRESSED) then
			m.ActionState = 1
		end

		if m.ActionTimer == 9 and m.ActionState == 1 then
			m:SetAnimToFrame(0)
			m.ActionState = 0
			m.ActionTimer = 1
			sSwimStrength = MIN_SWIM_STRENGTH
		end
	end

	if m.ActionTimer == 1 then
		m:PlaySound(sSwimStrength == MIN_SWIM_STRENGTH and Sounds.ACTION_SWIM or Sounds.ACTION_SWIM_FAST)
		resetBobVariables(m)
	end

	m:SetAnimation(Animations.SWIM_PART1)
	commonSwimmingStep(m, sSwimStrength)

	return false
end)

DEF_ACTION(Action.SWIMMING_END, function(m: Mario)
	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.METAL_WATER_FALLING, 1)
	end

	if m.Input:Has(InputFlags.B_PRESSED) then
		return m:SetAction(Action.WATER_PUNCH)
	end

	if m.ActionTimer >= 15 then
		return m:SetAction(Action.WATER_ACTION_END)
	end

	if checkWaterJump(m) then
		return true
	end

	if m.Input:Has(InputFlags.A_DOWN) and m.ActionTimer >= 7 then
		if m.ActionTimer == 7 and sSwimStrength < 280 then
			sSwimStrength += 10
		end

		return m:SetAction(Action.BREASTSTROKE, 1)
	end

	if m.ActionTimer >= 7 then
		sSwimStrength = MIN_SWIM_STRENGTH
	end

	m.ActionTimer += 1
	m.ForwardVel -= 0.25

	m:SetAnimation(Animations.SWIM_PART2)
	commonSwimmingStep(m, sSwimStrength)

	return false
end)

DEF_ACTION(Action.FLUTTER_KICK, function(m: Mario)
	if m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.METAL_WATER_FALLING, 1)
	end

	if m.Input:Has(InputFlags.B_PRESSED) then
		return m:SetAction(Action.WATER_PUNCH)
	end

	if not m.Input:Has(InputFlags.A_DOWN) then
		if m.ActionTimer == 0 and sSwimStrength < 280 then
			sSwimStrength += 10
		end

		return m:SetAction(Action.SWIMMING_END)
	end

	m.ForwardVel = Util.ApproachFloat(m.ForwardVel, 12, 0.1, 0.15)
	m.ActionTimer = 1
	sSwimStrength = MIN_SWIM_STRENGTH

	if m.ForwardVel < 14 then
		playSwimmingNoise(m)
		m:SetAnimation(Animations.FLUTTERKICK)
	end

	commonSwimmingStep(m, sSwimStrength)
	return false
end)

DEF_ACTION(Action.WATER_PUNCH, function(m: Mario)
	if m.ForwardVel < 7 then
		m.ForwardVel += 1
	end

	updateSwimmingYaw(m)
	updateSwimmingPitch(m)
	updateSwimmingSpeed(m)
	performWaterStep(m)
	updateWaterPitch(m)

	local headAngle = m.BodyState.HeadAngle
	local angleX = Util.ApproachInt(headAngle.X, 0, 0x200, 0x200)

	m.BodyState.HeadAngle = Util.SetX(headAngle, angleX)
	m:PlaySoundIfNoFlag(Sounds.ACTION_SWIM, MarioFlags.ACTION_SOUND_PLAYED)

	if m.ActionState == 0 then
		m:SetAnimation(Animations.WATER_GRAB_OBJ_PART1)

		if m:IsAnimAtEnd() then
			m.ActionState = 1
		end
	elseif m.ActionState == 1 then
		m:SetAnimation(Animations.WATER_GRAB_OBJ_PART2)

		if m:IsAnimAtEnd() then
			m:SetAction(Action.WATER_ACTION_END)
		end
	end

	return false
end)

DEF_ACTION(Action.WATER_PLUNGE, function(m: Mario)
	local stepResult
	local endVSpeed = swimmingNearSurface(m) and 0 or -5

	local hasMetalCap = m.Flags:Has(MarioFlags.METAL_CAP)
	local isDiving = m.PrevAction:Has(ActionFlags.DIVING) or m.Input:Has(InputFlags.A_DOWN)

	m.ActionTimer += 1
	stationarySlowDown(m)
	stepResult = performWaterStep(m)

	if m.ActionState == 0 then
		m:PlaySound(Sounds.ACTION_WATER_ENTER)

		if m.PeakHeight - m.Position.Y > 1150 then
			m:PlaySound(Sounds.MARIO_HAHA)
		end

		m.ParticleFlags:Add(ParticleFlags.WATER_SPLASH)
		m.ActionState = 1
	end

	if stepResult == WaterStep.HIT_FLOOR or m.Velocity.Y >= endVSpeed or m.ActionTimer > 20 then
		if hasMetalCap then
			m:SetAction(Action.METAL_WATER_FALLING)
		elseif isDiving then
			m:SetAction(Action.FLUTTER_KICK)
		else
			m:SetAction(Action.WATER_ACTION_END)
		end

		sBobIncrement = 0
	end

	if hasMetalCap then
		m:SetAnimation(Animations.GENERAL_FALL)
	elseif isDiving then
		m:SetAnimation(Animations.FLUTTERKICK)
	else
		m:SetAnimation(Animations.WATER_ACTION_END)
	end

	m.Flags:Add(ParticleFlags.PLUNGE_BUBBLE)
	return false
end)

DEF_ACTION(Action.METAL_WATER_STANDING, function(m: Mario)
	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if m.Input:Has(InputFlags.A_PRESSED) then
		return m:SetAction(Action.METAL_WATER_JUMP)
	end

	if m.Input:Has(InputFlags.NONZERO_ANALOG) then
		return m:SetAction(Action.METAL_WATER_WALKING)
	end

	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
			m.ActionState = 0
		end
	end

	m:StopAndSetHeightToFloor()

	if m.Position.Y >= m.WaterLevel - 150 then
		m.ParticleFlags:Add(ParticleFlags.IDLE_WATER_WAVE)
	end

	return false
end)

DEF_ACTION(Action.METAL_WATER_WALKING, function(m: Mario)
	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if m.Input:Has(InputFlags.A_PRESSED) then
		return m:SetAction(Action.METAL_WATER_JUMP)
	end

	if m.Input:Has(InputFlags.NO_MOVEMENT) then
		return m:SetAction(Action.METAL_WATER_STANDING)
	end

	local accel = Util.SignedInt(m.ForwardVel / 4 * 0x10000)
	local groundStep

	if accel < 0x1000 then
		accel = 0x1000
	end

	m:SetAnimationWithAccel(Animations.WALKING, accel)
	playMetalWaterWalkingSound(m)
	updateMetalWaterWalkingSpeed(m)
	groundStep = m:PerformGroundStep()

	if groundStep == GroundStep.LEFT_GROUND then
		m:SetAction(Action.METAL_WATER_FALLING, 1)
	elseif groundStep == GroundStep.HIT_WALL then
		m.ForwardVel = 0
	end

	return false
end)

DEF_ACTION(Action.METAL_WATER_JUMP, function(m: Mario)
	local airStep

	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if updateMetalWaterJumpSpeed(m) then
		return m:SetAction(Action.WATER_JUMP, 1)
	end

	playMetalWaterJumpingSound(m, false)
	m:SetAnimation(Animations.SINGLE_JUMP)
	airStep = m:PerformAirStep()

	if airStep == AirStep.LANDED then
		m:SetAction(Action.METAL_WATER_JUMP_LAND)
	elseif airStep == AirStep.HIT_WALL then
		m.ForwardVel = 0
	end

	return false
end)

DEF_ACTION(Action.METAL_WATER_FALLING, function(m: Mario)
	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if m.Input:Has(InputFlags.NONZERO_ANALOG) then
		m.FaceAngle += Vector3int16.new(0, 0x400 * Util.Sins(m.IntendedYaw - m.FaceAngle.Y), 0)
	end

	m:SetAnimation(m.ActionArg == 0 and Animations.GENERAL_FALL or Animations.FALL_FROM_WATER)
	stationarySlowDown(m)

	if bit32.btest(performWaterStep(m), WaterStep.HIT_FLOOR) then
		m:SetAction(Action.METAL_WATER_FALL_LAND)
	end

	return false
end)

DEF_ACTION(Action.METAL_WATER_JUMP_LAND, function(m: Mario)
	playMetalWaterJumpingSound(m, true)

	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if m.Input:Has(InputFlags.NONZERO_ANALOG) then
		return m:SetAction(Action.METAL_WATER_WALKING)
	end

	m:StopAndSetHeightToFloor()
	m:SetAnimation(Animations.LAND_FROM_SINGLE_JUMP)

	if m:IsAnimAtEnd() then
		return m:SetAction(Action.METAL_WATER_STANDING)
	end

	return false
end)

DEF_ACTION(Action.METAL_WATER_FALL_LAND, function(m: Mario)
	playMetalWaterJumpingSound(m, true)

	if not m.Flags:Has(MarioFlags.METAL_CAP) then
		return m:SetAction(Action.WATER_IDLE)
	end

	if m.Input:Has(InputFlags.NONZERO_ANALOG) then
		return m:SetAction(Action.METAL_WATER_WALKING)
	end

	m:StopAndSetHeightToFloor()
	m:SetAnimation(Animations.GENERAL_LAND)

	if m:IsAnimAtEnd() then
		return m:SetAction(Action.METAL_WATER_STANDING)
	end

	return false
end)


--!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


--!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,
	AngleVel: Vector3int16,
	ThrowMatrix: CFrame?,

	GfxAngle: Vector3int16,
	GfxPos: Vector3,

	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: number,
}

return table.freeze({
	Flags = Flags,
})