From cfb04d93251c1728c5f170853aabc0eaab907f29 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Jul 2023 19:19:03 -0500 Subject: [PATCH] Implemented swimming. --- client/Enums/InputFlags.lua | 2 +- client/Enums/Mario/Input.lua | 2 +- client/Mario/Airborne/init.server.lua | 99 +- client/Mario/Moving/init.server.lua | 22 +- client/Mario/Stationary/init.server.lua | 2 +- client/Mario/Submerged/init.meta.json | 6 + client/Mario/Submerged/init.server.lua | 857 ++++++++++++++++++ client/Mario/init.lua | 123 ++- client/Types/init.lua | 4 +- client/Util/init.lua | 87 +- client/init.server.lua | 95 +- server/LazyNetworking.server.lua | 6 +- .../StarterCharacter/HumanoidRootPart.rbxmx | 29 + shared/Animations.model.json | 20 +- shared/Sounds.model.json | 96 ++ shared/init.lua | 8 + 16 files changed, 1311 insertions(+), 147 deletions(-) create mode 100644 client/Mario/Submerged/init.meta.json create mode 100644 client/Mario/Submerged/init.server.lua diff --git a/client/Enums/InputFlags.lua b/client/Enums/InputFlags.lua index bf91f3a..e029ff9 100644 --- a/client/Enums/InputFlags.lua +++ b/client/Enums/InputFlags.lua @@ -6,7 +6,7 @@ return { OFF_FLOOR = 0x0004, ABOVE_SLIDE = 0x0008, FIRST_PERSON = 0x0010, - UNKNOWN_5 = 0x0020, + NO_MOVEMENT = 0x0020, SQUISHED = 0x0040, A_DOWN = 0x0080, IN_POISON_GAS = 0x0100, diff --git a/client/Enums/Mario/Input.lua b/client/Enums/Mario/Input.lua index ba869fc..2871984 100644 --- a/client/Enums/Mario/Input.lua +++ b/client/Enums/Mario/Input.lua @@ -6,7 +6,7 @@ return { OFF_FLOOR = 0x0004, ABOVE_SLIDE = 0x0008, FIRST_PERSON = 0x0010, - UNKNOWN_5 = 0x0020, + NO_MOVEMENT = 0x0020, SQUISHED = 0x0040, A_DOWN = 0x0080, IN_POISON_GAS = 0x0100, diff --git a/client/Mario/Airborne/init.server.lua b/client/Mario/Airborne/init.server.lua index 1897d0e..3c77a77 100644 --- a/client/Mario/Airborne/init.server.lua +++ b/client/Mario/Airborne/init.server.lua @@ -35,31 +35,6 @@ local function playFlipSounds(m: Mario, frame1: number, frame2: number, frame3: 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) @@ -73,7 +48,7 @@ local function lavaBoostOnWall(m: Mario) if wall then local angle = Util.Atan2s(wall.Normal.Z, wall.Normal.X) - m.FaceAngle = Util.SetYint16(m.FaceAngle, angle) + m.FaceAngle = Util.SetY(m.FaceAngle, angle) end if m.ForwardVel < 24 then @@ -210,30 +185,30 @@ local function updateFlyingYaw(m: Mario) m.AngleVel += Vector3int16.new(0, 0x40, 0) if m.AngleVel.Y > 0x10 then - m.AngleVel = Util.SetYint16(m.AngleVel, 0x10) + m.AngleVel = Util.SetY(m.AngleVel, 0x10) end else local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x10, 0x20) - m.AngleVel = Util.SetYint16(m.AngleVel, y) + 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.SetYint16(m.AngleVel, -0x10) + m.AngleVel = Util.SetY(m.AngleVel, -0x10) end else local y = Util.ApproachInt(m.AngleVel.Y, targetYawVel, 0x20, 0x10) - m.AngleVel = Util.SetYint16(m.AngleVel, y) + m.AngleVel = Util.SetY(m.AngleVel, y) end else local y = Util.ApproachInt(m.AngleVel.Y, 0, 0x40) - m.AngleVel = Util.SetYint16(m.AngleVel, y) + m.AngleVel = Util.SetY(m.AngleVel, y) end m.FaceAngle += Vector3int16.new(0, m.AngleVel.Y, 0) - m.FaceAngle = Util.SetZint16(m.FaceAngle, 20 * -m.AngleVel.Y) + m.FaceAngle = Util.SetZ(m.FaceAngle, 20 * -m.AngleVel.Y) end local function updateFlyingPitch(m: Mario) @@ -244,26 +219,26 @@ local function updateFlyingPitch(m: Mario) m.AngleVel += Vector3int16.new(0x40, 0, 0) if m.AngleVel.X > 0x20 then - m.AngleVel = Util.SetXint16(m.AngleVel, 0x20) + m.AngleVel = Util.SetX(m.AngleVel, 0x20) end else local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x20, 0x40) - m.AngleVel = Util.SetXint16(m.AngleVel, x) + 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.SetXint16(m.AngleVel, -0x20) + m.AngleVel = Util.SetX(m.AngleVel, -0x20) end else local x = Util.ApproachInt(m.AngleVel.X, targetPitchVel, 0x40, 0x20) - m.AngleVel = Util.SetXint16(m.AngleVel, x) + m.AngleVel = Util.SetX(m.AngleVel, x) end else local x = Util.ApproachInt(m.AngleVel.X, 0, 0x40) - m.AngleVel = Util.SetXint16(m.AngleVel, x) + m.AngleVel = Util.SetX(m.AngleVel, x) end end @@ -289,11 +264,11 @@ local function updateFlying(m: Mario) m.FaceAngle += Vector3int16.new(m.AngleVel.X, 0, 0) if m.FaceAngle.X > 0x2AAA then - m.FaceAngle = Util.SetXint16(m.FaceAngle, 0x2AAA) + m.FaceAngle = Util.SetX(m.FaceAngle, 0x2AAA) end if m.FaceAngle.X < -0x2AAA then - m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + m.FaceAngle = Util.SetX(m.FaceAngle, -0x2AAA) end local velX = Util.Coss(m.FaceAngle.X) * Util.Sins(m.FaceAngle.Y) @@ -584,7 +559,7 @@ DEF_ACTION(Action.TWIRLING, function(m: Mario) end local yVel = Util.ApproachInt(m.AngleVel.Y, yawVelTarget, 0x200) - m.AngleVel = Util.SetYint16(m.AngleVel, yVel) + m.AngleVel = Util.SetY(m.AngleVel, yVel) m.TwirlYaw += yVel m:SetAnimation(if m.ActionArg == 0 then Animations.START_TWIRL else Animations.TWIRL) @@ -629,11 +604,11 @@ DEF_ACTION(Action.DIVE, function(m: Mario) m.FaceAngle -= Vector3int16.new(0x200, 0, 0) if m.FaceAngle.X < -0x2AAA then - m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + m.FaceAngle = Util.SetX(m.FaceAngle, -0x2AAA) end end - m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) + 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) @@ -655,6 +630,30 @@ DEF_ACTION(Action.DIVE, function(m: Mario) 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 @@ -678,7 +677,7 @@ DEF_ACTION(Action.STEEP_JUMP, function(m: Mario) end m:SetAnimation(Animations.SINGLE_JUMP) - m.GfxAngle = Util.SetYint16(m.GfxAngle, m.SteepJumpYaw) + m.GfxAngle = Util.SetY(m.GfxAngle, m.SteepJumpYaw) return false end) @@ -869,7 +868,7 @@ DEF_ACTION(Action.THROWN_FORWARD, function(m: Mario) pitch = 0x1800 end - m.GfxAngle = Util.SetXint16(m.GfxAngle, pitch + 0x1800) + m.GfxAngle = Util.SetX(m.GfxAngle, pitch + 0x1800) end m.ForwardVel *= 0.98 @@ -1060,7 +1059,7 @@ DEF_ACTION(Action.SLIDE_KICK, function(m: Mario) tilt = 0x1800 end - m.GfxAngle = Util.SetXint16(m.GfxAngle, tilt) + m.GfxAngle = Util.SetX(m.GfxAngle, tilt) end elseif stepResult == AirStep.LANDED then if m.ActionState == 0 and m.Velocity.Y < 0 then @@ -1154,8 +1153,8 @@ DEF_ACTION(Action.FLYING, function(m: Mario) end if stepResult == AirStep.NONE then - m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) - m.GfxAngle = Util.SetZint16(m.GfxAngle, m.FaceAngle.Z) + 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) @@ -1187,11 +1186,11 @@ DEF_ACTION(Action.FLYING, function(m: Mario) m.FaceAngle -= Vector3int16.new(0x200, 0, 0) if m.FaceAngle.X < -0x2AAA then - m.FaceAngle = Util.SetXint16(m.FaceAngle, -0x2AAA) + m.FaceAngle = Util.SetX(m.FaceAngle, -0x2AAA) end - m.GfxAngle = Util.SetXint16(m.GfxAngle, -m.FaceAngle.X) - m.GfxAngle = Util.SetZint16(m.GfxAngle, m.FaceAngle.Z) + 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) diff --git a/client/Mario/Moving/init.server.lua b/client/Mario/Moving/init.server.lua index b3bfe4b..a89089b 100644 --- a/client/Mario/Moving/init.server.lua +++ b/client/Mario/Moving/init.server.lua @@ -157,7 +157,7 @@ end local function beginWalkingAction(m: Mario, forwardVel: number, action: number, actionArg: number?) m:SetForwardVel(forwardVel) - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + m.FaceAngle = Util.SetY(m.FaceAngle, m.IntendedYaw) return m:SetAction(action, actionArg) end @@ -177,7 +177,7 @@ local function checkLedgeClimbDown(m: Mario) m.Position = pos m.FaceAngle *= Vector3int16.new(0, 1, 1) - m.FaceAngle = Util.SetYint16(m.FaceAngle, wallAngle + 0x8000) + m.FaceAngle = Util.SetY(m.FaceAngle, wallAngle + 0x8000) m:SetAction(Action.LEDGE_CLIMB_DOWN) m:SetAnimation(Animations.CLIMB_DOWN_LEDGE) @@ -259,7 +259,7 @@ local function updateSlidingAngle(m: Mario, accel: number, lossFactor: number) end end - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.SlideYaw + newFacingDYaw) + m.FaceAngle = Util.SetY(m.FaceAngle, m.SlideYaw + newFacingDYaw) m.Velocity = Vector3.new(m.SlideVelX, 0, m.SlideVelZ) --! Speed is capped a frame late (butt slide HSG) @@ -450,7 +450,7 @@ local function updateWalkingSpeed(m: Mario) 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) + m.FaceAngle = Util.SetY(m.FaceAngle, faceY) applySlopeAccel(m) end @@ -485,7 +485,7 @@ end local function beginBrakingAction(m: Mario) if m.ActionState == 1 then - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.ActionArg) + m.FaceAngle = Util.SetY(m.FaceAngle, m.ActionArg) return m:SetAction(Action.STANDING_AGAINST_WALL) end @@ -575,7 +575,7 @@ local function animAndAudioForWalk(m: Mario) walkingPitch = Util.SignedShort(walkingPitch) m.WalkingPitch = walkingPitch - m.GfxAngle = Util.SetXint16(m.GfxAngle, walkingPitch) + m.GfxAngle = Util.SetX(m.GfxAngle, walkingPitch) end local function pushOrSidleWall(m: Mario, startPos: Vector3) @@ -617,8 +617,8 @@ local function pushOrSidleWall(m: Mario, startPos: Vector3) 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)) + m.GfxAngle = Util.SetY(m.GfxAngle, m.ActionArg) + m.GfxAngle = Util.SetZ(m.GfxAngle, m:FindFloorSlope(0x4000)) end end @@ -871,7 +871,7 @@ DEF_ACTION(Action.WALKING, function(m: Mario) return true end - if m.Input:Has(InputFlags.UNKNOWN_5) then + if m.Input:Has(InputFlags.NO_MOVEMENT) then return beginBrakingAction(m) end @@ -1128,7 +1128,7 @@ DEF_ACTION(Action.CRAWLING, function(m: Mario) return true end - if m.Input:Has(InputFlags.UNKNOWN_5) then + if m.Input:Has(InputFlags.NO_MOVEMENT) then return m:SetAction(Action.STOP_CRAWLING) end @@ -1183,7 +1183,7 @@ DEF_ACTION(Action.BURNING_GROUND, function(m: Mario) 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) + m.FaceAngle = Util.SetY(m.FaceAngle, faceY) end applySlopeAccel(m) diff --git a/client/Mario/Stationary/init.server.lua b/client/Mario/Stationary/init.server.lua index 7c73f54..362f2fa 100644 --- a/client/Mario/Stationary/init.server.lua +++ b/client/Mario/Stationary/init.server.lua @@ -43,7 +43,7 @@ local function checkCommonIdleCancels(m: Mario) end if m.Input:Has(InputFlags.NONZERO_ANALOG) then - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + m.FaceAngle = Util.SetY(m.FaceAngle, m.IntendedYaw) return m:SetAction(Action.WALKING) end diff --git a/client/Mario/Submerged/init.meta.json b/client/Mario/Submerged/init.meta.json new file mode 100644 index 0000000..d3f1b48 --- /dev/null +++ b/client/Mario/Submerged/init.meta.json @@ -0,0 +1,6 @@ +{ + "properties": { + "Disabled": true, + "RunContext": "Client" + } +} \ No newline at end of file diff --git a/client/Mario/Submerged/init.server.lua b/client/Mario/Submerged/init.server.lua new file mode 100644 index 0000000..b9fee6f --- /dev/null +++ b/client/Mario/Submerged/init.server.lua @@ -0,0 +1,857 @@ +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(sBobHeight * Util.Sins(sBobTimer)) + 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) diff --git a/client/Mario/init.lua b/client/Mario/init.lua index 0dbb912..62b955c 100644 --- a/client/Mario/init.lua +++ b/client/Mario/init.lua @@ -479,7 +479,7 @@ function Mario.SetSteepJumpAction(m: Mario) 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) + m.FaceAngle = Util.SetY(m.FaceAngle, Util.Atan2s(x, y) + angleTemp) end m:SetAction(Action.STEEP_JUMP, 0) @@ -536,11 +536,11 @@ function Mario.SetActionAirborne(m: Mario, action: number, actionArg: number) elseif action == Action.SIDE_FLIP then m:SetYVelBasedOnFSpeed(62, 0) m.ForwardVel = 8 - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.IntendedYaw) + m.FaceAngle = Util.SetY(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) + m.FaceAngle = Util.SetX(m.FaceAngle, -0x2000) elseif action == Action.LAVA_BOOST then m.Velocity = Util.SetY(m.Velocity, 84) @@ -847,7 +847,7 @@ function Mario.BonkReflection(m: Mario, negateSpeed: boolean?) 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.FaceAngle = Util.SetY(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) @@ -865,10 +865,10 @@ function Mario.PushOffSteepFloor(m: Mario, action: number, actionArg: number?) if floorDYaw > -0x4000 and floorDYaw < 0x4000 then m.ForwardVel = 16 - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.FloorAngle) + m.FaceAngle = Util.SetY(m.FaceAngle, m.FloorAngle) else m.ForwardVel = -16 - m.FaceAngle = Util.SetYint16(m.FaceAngle, m.FloorAngle + 0x8000) + m.FaceAngle = Util.SetY(m.FaceAngle, m.FloorAngle + 0x8000) end m:SetAction(action, actionArg) @@ -998,7 +998,7 @@ function Mario.CheckLedgeGrab(m: Mario, wall: RaycastResult, intendedPos: Vector 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) + m.FaceAngle = Util.SetY(m.FaceAngle, Util.Atan2s(wall.Normal.Z, wall.Normal.X) + 0x8000) end return ledgeFloor ~= nil @@ -1014,7 +1014,7 @@ function Mario.PerformAirQuarterStep(m: Mario, intendedPos: Vector3, stepArg: nu nextPos = lowerPos local floorHeight, floor = Util.FindFloor(nextPos) - local ceilHeight, _ceil = Util.FindCeil(nextPos, floorHeight) + local ceilHeight = Util.FindCeil(nextPos, floorHeight) m.Wall = nil @@ -1310,7 +1310,7 @@ function Mario.UpdateInputs(m: Mario) m:UpdateGeometryInputs() if not m.Input:Has(InputFlags.NONZERO_ANALOG, InputFlags.A_PRESSED) then - m.Input:Add(InputFlags.UNKNOWN_5) + m.Input:Add(InputFlags.NO_MOVEMENT) end if m.WallKickTimer > 0 then @@ -1335,11 +1335,8 @@ 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 @@ -1365,7 +1362,7 @@ function Mario.UpdateModel(m: Mario) modelState:Add(ModelFlags.NOISE_ALPHA) end - if flags:Has(bit32.bor(MarioFlags.METAL_CAP, MarioFlags.METAL_SHOCK)) then + if flags:Has(MarioFlags.METAL_CAP, MarioFlags.METAL_SHOCK) then modelState:Add(ModelFlags.METAL) end @@ -1438,21 +1435,59 @@ 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) + if floor and not m.Action:Has(ActionFlags.AIR, ActionFlags.SWIMMING) then + if floor.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 +function Mario.SetWaterPlungeAction(m: Mario) + m.ForwardVel /= 4 + m.Velocity *= Vector3.new(1, 0.5, 1) + + -- This behavior sucks, feel free to enable if you want. + -- m.Position = Util.SetY(m.Position, m.WaterLevel - 100) + + m.FaceAngle *= Vector3int16.new(1, 1, 0) + m.AngleVel *= 0 + + if not m.Action:Has(ActionFlags.DIVING) then + m.FaceAngle *= Vector3int16.new(0, 1, 1) + end + + return m:SetAction(Action.WATER_PLUNGE) +end + +function Mario.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 + function Mario.ExecuteAction(m: Mario): number if m.Action() == 0 then return 0 @@ -1490,7 +1525,43 @@ function Mario.ExecuteAction(m: Mario): number local action = actions[id] if action then - if not action(m) then + local group = bit32.band(id, ActionGroups.GROUP_MASK) + local cancel + + if group ~= ActionGroups.SUBMERGED and m.Position.Y < m.WaterLevel - 100 then + cancel = m:SetWaterPlungeAction() + else + if group == ActionGroups.AIRBORNE then + m:PlayFarFallSound() + elseif group == ActionGroups.SUBMERGED then + if m.Position.Y > m.WaterLevel - 80 then + if m.WaterLevel - 80 > m.FloorHeight then + m.Position = Util.SetY(m.Position, m.WaterLevel - 80) + else + m.AngleVel *= 0 + cancel = m:SetAction(Action.WALKING) + end + end + + m.QuicksandDepth = 0 + m.BodyState.HeadAngle *= Vector3int16.new(1, 0, 0) + end + + if cancel == nil then + cancel = action(m) + end + end + + if not cancel then + if m.Input:Has(InputFlags.IN_WATER) then + if group == ActionGroups.MOVING then + m.ParticleFlags:Add(ParticleFlags.WAVE_TRAIL) + m.ParticleFlags:Remove(ParticleFlags.DUST) + elseif group == ActionGroups.STATIONARY then + m.ParticleFlags:Add(ParticleFlags.IDLE_WATER_WAVE) + end + end + break end else @@ -1573,10 +1644,12 @@ function Mario.new(): Mario DoubleJumpTimer = 0, FaceAngle = Vector3int16.new(), - GfxAngle = Vector3int16.new(), AngleVel = Vector3int16.new(), ThrowMatrix = CFrame.identity, + GfxAngle = Vector3int16.new(), + GfxPos = Vector3.zero, + SlideYaw = 0, TwirlYaw = 0, diff --git a/client/Types/init.lua b/client/Types/init.lua index d89726b..5bcf3df 100644 --- a/client/Types/init.lua +++ b/client/Types/init.lua @@ -56,10 +56,12 @@ export type MarioState = { DoubleJumpTimer: number, FaceAngle: Vector3int16, - GfxAngle: Vector3int16, AngleVel: Vector3int16, ThrowMatrix: CFrame?, + GfxAngle: Vector3int16, + GfxPos: Vector3, + SlideYaw: number, TwirlYaw: number, diff --git a/client/Util/init.lua b/client/Util/init.lua index 8f09e05..8896a64 100644 --- a/client/Util/init.lua +++ b/client/Util/init.lua @@ -16,6 +16,19 @@ local VECTOR3_XZ = Vector3.one - Vector3.yAxis local TweenService = game:GetService("TweenService") local fadeOut = TweenInfo.new(0.5) +local waterPlane = Instance.new("BoxHandleAdornment") +waterPlane.Size = Vector3.new(48, 0, 48) +waterPlane.Adornee = workspace.Terrain +waterPlane.Transparency = 0.5 +waterPlane.Name = "WaterPlane" + +local focalPlane = waterPlane:Clone() +focalPlane.Size = Vector3.new(4, 0, 4) +focalPlane.Color3 = Color3.new(1, 0, 1) +focalPlane.Name = "FocalPlane" +focalPlane.Transparency = 0.1 +focalPlane.Parent = waterPlane + local CARDINAL = { -Vector3.xAxis, -Vector3.zAxis, @@ -23,29 +36,33 @@ local CARDINAL = { Vector3.zAxis, } -function Util.SetX(vec: Vector3, x: number): Vector3 - return Vector3.new(x, vec.Y, vec.Z) +local CONSTRUCTORS = { + Vector3 = Vector3.new, + Vector3int16 = Vector3int16.new, +} + +-- stylua: ignore +local function vectorModifier(getArgs: (Vector3 | Vector3int16, number) -> (number, number, number)): + ((vec: Vector3, value: number) -> Vector3) & + ((vec: Vector3int16, value: number) -> Vector3int16) + + return function (vector, new) + local constructor = CONSTRUCTORS[typeof(vector)] + return constructor(getArgs(vector, new)) + end end -function Util.SetXint16(vec: Vector3int16, x: number): Vector3int16 - return Vector3int16.new(x, vec.Y, vec.Z) -end +Util.SetX = vectorModifier(function(vector, x) + return x, vector.Y, vector.Z +end) -function Util.SetY(vec: Vector3, y: number): Vector3 - return Vector3.new(vec.X, y, vec.Z) -end +Util.SetY = vectorModifier(function(vector, y) + return vector.X, y, vector.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 +Util.SetZ = vectorModifier(function(vector, z) + return vector.X, vector.Y, z +end) function Util.ToRoblox(v: Vector3) return v * Util.Scale @@ -62,13 +79,32 @@ end function Util.ToRotation(v: Vector3int16): CFrame local angle = Util.ToEulerAngles(v) + -- stylua: ignore local matrix = CFrame.fromAxisAngle(Vector3.yAxis, angle.Y) - * CFrame.fromAxisAngle(Vector3.xAxis, -angle.X) - * CFrame.fromAxisAngle(Vector3.zAxis, -angle.Z) + * CFrame.fromAxisAngle(Vector3.xAxis, -angle.X) + * CFrame.fromAxisAngle(Vector3.zAxis, -angle.Z) return matrix end +function Util.DebugWater(waterLevel: number) + if script:GetAttribute("Debug") then + local robloxLevel = (waterLevel * Util.Scale) + 0.01 + local focus = workspace.CurrentCamera.Focus + + local x = math.floor(focus.X / 4) * 4 + local z = math.floor(focus.Z / 4) * 4 + + local cf = CFrame.new(x, robloxLevel, z) + waterPlane.Parent = script + + focalPlane.CFrame = cf + waterPlane.CFrame = cf + else + waterPlane.Parent = nil + end +end + function Util.Raycast(pos: Vector3, dir: Vector3, maybeParams: RaycastParams?, worldRoot: WorldRoot?): RaycastResult? local root = worldRoot or workspace local params = maybeParams or rayParams @@ -96,8 +132,9 @@ function Util.Raycast(pos: Vector3, dir: Vector3, maybeParams: RaycastParams?, w 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) +-- stylua: ignore +function Util.RaycastSM64(pos: Vector3, dir: Vector3, maybeParams: RaycastParams?, worldRoot: WorldRoot?): RaycastResult? + local result: RaycastResult? = Util.Raycast(pos * Util.Scale, dir * Util.Scale, maybeParams or rayParams, worldRoot) if result then -- Cast back to SM64 unit scale. @@ -131,7 +168,7 @@ function Util.FindFloor(pos: Vector3): (number, RaycastResult?) newPos = Vector3.new(trunc.X, trunc.Y, trunc.Z) end - local result = Util.RaycastSM64(newPos + (Vector3.yAxis * 100), -Vector3.yAxis * 10000) + local result = Util.RaycastSM64(newPos + (Vector3.yAxis * 100), -Vector3.yAxis * 10000, rayParams) if result then height = Util.SignedShort(result.Position.Y) @@ -159,7 +196,7 @@ function Util.FindCeil(pos: Vector3, height: number?): (number, RaycastResult?) end local head = Vector3.new(pos.X, (height or pos.Y) + 80, pos.Z) - local result = Util.RaycastSM64(head, Vector3.yAxis * 10000) + local result = Util.RaycastSM64(head, Vector3.yAxis * 10000, rayParams) if result then newHeight = result.Position.Y diff --git a/client/init.server.lua b/client/init.server.lua index 7fdd9e8..719be83 100644 --- a/client/init.server.lua +++ b/client/init.server.lua @@ -37,10 +37,11 @@ type Controller = Types.Controller type Mario = Mario.Class local player: Player = assert(Players.LocalPlayer) -local FLIP = CFrame.Angles(0, math.pi, 0) +local mario: Mario = Mario.new() local STEP_RATE = 30 local NULL_TEXT = `NULL` +local FLIP = CFrame.Angles(0, math.pi, 0) local debugStats = Instance.new("BoolValue") debugStats.Name = "DebugStats" @@ -70,6 +71,7 @@ local AUTO_STATS = { "CeilHeight", "FloorHeight", + "WaterLevel", } local ControlModule: { @@ -213,8 +215,10 @@ local function updateController(controller: Controller, humanoid: Humanoid?) local character = humanoid.Parent if (character and character:GetAttribute("TAS")) or Core:GetAttribute("ToolAssistedInput") then - if controller.ButtonDown:Has(Buttons.A_BUTTON) then - controller.ButtonPressed:Set(Buttons.A_BUTTON) + if not mario.Action:Has(Enums.ActionFlags.SWIMMING) then + if controller.ButtonDown:Has(Buttons.A_BUTTON) then + controller.ButtonPressed:Set(Buttons.A_BUTTON) + end end end end @@ -268,7 +272,10 @@ function Commands.PlaySound(player: Player, name: string) if oldSound and oldSound:IsA("Sound") then canPlay = false - if name:sub(1, 5) == "MARIO" then + if name:sub(1, 6) == "MOVING" or sound:GetAttribute("Decay") then + -- Keep decaying audio alive. + stepDecay(oldSound) + elseif name:sub(1, 5) == "MARIO" then -- Restart mario sound if a 30hz interval passed. local now = os.clock() local lastPlay = oldSound:GetAttribute("LastPlay") or 0 @@ -277,9 +284,6 @@ function Commands.PlaySound(player: Player, name: string) oldSound.TimePosition = 0 oldSound:SetAttribute("LastPlay", now) end - elseif name:sub(1, 6) == "MOVING" then - -- Keep decaying audio alive. - stepDecay(oldSound) else -- Allow stacking. canPlay = true @@ -322,11 +326,13 @@ function Commands.SetParticle(player: Player, name: string, set: boolean) elseif set ~= nil then particle.Enabled = set end + else + warn("particle not found:", name) end end end -function Commands.SetAngle(player: Player, angle: Vector3int16) +function Commands.SetTorsoAngle(player: Player, angle: Vector3int16) local character = player.Character local waist = character and character:FindFirstChild("Waist", true) @@ -337,6 +343,17 @@ function Commands.SetAngle(player: Player, angle: Vector3int16) end end +function Commands.SetHeadAngle(player: Player, angle: Vector3int16) + local character = player.Character + local neck = character and character:FindFirstChild("Neck", true) + + if neck and neck:IsA("Motor6D") then + local props = { C1 = Util.ToRotation(-angle) + neck.C1.Position } + local tween = TweenService:Create(neck, TweenInfo.new(0.1), props) + tween:Play() + end +end + function Commands.SetCamera(player: Player, cf: CFrame?) local camera = workspace.CurrentCamera @@ -376,8 +393,8 @@ lazyNetwork.OnClientEvent:Connect(onNetworkReceive) ------------------------------------------------------------------------------------------------------------------------------------------------------------- local lastUpdate = os.clock() -local lastAngle: Vector3int16? -local mario: Mario = Mario.new() +local lastHeadAngle: Vector3int16? +local lastTorsoAngle: Vector3int16? local activeScale = 1 local subframe = 0 -- 30hz subframe @@ -452,6 +469,30 @@ local function onReset() mario:SetAction(Action.SPAWN_SPIN_AIRBORNE) end +local function getWaterLevel(pos: Vector3) + local terrain = workspace.Terrain + local voxelPos = terrain:WorldToCellPreferSolid(pos) + + local voxelRegion = Region3.new(voxelPos * 4, (voxelPos + Vector3.one + (Vector3.yAxis * 3)) * 4) + voxelRegion = voxelRegion:ExpandToGrid(4) + + local materials, occupancies = terrain:ReadVoxels(voxelRegion, 4) + local size: Vector3 = occupancies.Size + local waterLevel = -11000 + + for y = 1, size.Y do + local occupancy = occupancies[1][y][1] + local material = materials[1][y][1] + + if occupancy >= 0.9 and material == Enum.Material.Water then + local top = ((voxelPos.Y * 4) + (4 * y + 2)) + waterLevel = math.max(waterLevel, top / Util.Scale) + end + end + + return waterLevel +end + local function update() local character = player.Character @@ -474,18 +515,20 @@ local function update() -- Disabled for now because this causes parallel universes to break. -- TODO: Find a better way to do two-way syncing between these values. - --[[ - local pos = character:GetPivot().Position - local dist = (Util.ToRoblox(mario.Position) - pos).Magnitude + -- local pos = character:GetPivot().Position + -- local dist = (Util.ToRoblox(mario.Position) - pos).Magnitude - if dist > (scale * 20) then - mario.Position = Util.ToSM64(pos) - end - ]] + -- if dist > (scale * 20) then + -- mario.Position = Util.ToSM64(pos) + -- end local humanoid = character:FindFirstChildOfClass("Humanoid") local simSpeed = tonumber(character:GetAttribute("TimeScale") or nil) or 1 + local robloxPos = Util.ToRoblox(mario.Position) + mario.WaterLevel = getWaterLevel(robloxPos) + Util.DebugWater(mario.WaterLevel) + subframe += (now - lastUpdate) * (STEP_RATE * simSpeed) lastUpdate = now @@ -632,15 +675,21 @@ local function update() end local bodyState = mario.BodyState - local ang = bodyState.TorsoAngle + local headAngle = bodyState.HeadAngle + local torsoAngle = bodyState.TorsoAngle if actionId ~= Action.BUTT_SLIDE and actionId ~= Action.WALKING then bodyState.TorsoAngle *= 0 end - if ang ~= lastAngle then - networkDispatch("SetAngle", ang) - lastAngle = ang + if torsoAngle ~= lastTorsoAngle then + networkDispatch("SetTorsoAngle", torsoAngle) + lastTorsoAngle = torsoAngle + end + + if headAngle ~= lastHeadAngle then + networkDispatch("SetHeadAngle", headAngle) + lastHeadAngle = headAngle end if particles then @@ -652,6 +701,10 @@ local function update() local emit = particle:GetAttribute("Emit") local hasFlag = mario.ParticleFlags:Has(flag) + if hasFlag then + print("SetParticle", name) + end + if emit then if hasFlag then networkDispatch("SetParticle", name) diff --git a/server/LazyNetworking.server.lua b/server/LazyNetworking.server.lua index 2d2dbc6..e68f082 100644 --- a/server/LazyNetworking.server.lua +++ b/server/LazyNetworking.server.lua @@ -43,7 +43,11 @@ function Validators.SetParticle(player: Player, name: string, set: boolean?) return false end -function Validators.SetAngle(player: Player, angle: Vector3int16) +function Validators.SetTorsoAngle(player: Player, angle: Vector3int16) + return typeof(angle) == "Vector3int16" +end + +function Validators.SetHeadAngle(player: Player, angle: Vector3int16) return typeof(angle) == "Vector3int16" end diff --git a/server/StarterCharacter/HumanoidRootPart.rbxmx b/server/StarterCharacter/HumanoidRootPart.rbxmx index f9e5576..a404715 100644 --- a/server/StarterCharacter/HumanoidRootPart.rbxmx +++ b/server/StarterCharacter/HumanoidRootPart.rbxmx @@ -181,6 +181,35 @@ 0 1 0 0.1 0.75 0 0.5 0.75 0 1 1 0 + + + + 0 + -1 + 0 + + AQAAAAQAAABFbWl0BgAAAAAAADlA + 0 0.807843 0.807843 0.807843 0 1 0.807843 0.807843 0.807843 0 + 3 + 5 + false + 2 2 + 1 + 1 + PLUNGE_BUBBLE + 100 + -360 360 + 0 360 + 0 2 0 1 2 0 + 20 20 + + 360 + 360 + + rbxassetid://425366688 + 0 1 0 0.1 0.75 0 0.5 0.75 0 1 1 0 + + OriginalPosition diff --git a/shared/Animations.model.json b/shared/Animations.model.json index 4ba57e4..1e7ed4f 100644 --- a/shared/Animations.model.json +++ b/shared/Animations.model.json @@ -637,7 +637,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101766690" }, "attributes": { @@ -787,11 +787,11 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101764246" }, "attributes": { - "Loop": false, + "Loop": true, "NumFrames": 22, "StartAddress": "565610" } @@ -2517,12 +2517,12 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101761759" }, "attributes": { "Loop": false, - "NumFrames": 12, + "NumFrames": 16, "StartAddress": "564D2C" } }, @@ -2532,7 +2532,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101760003" }, "attributes": { @@ -2862,7 +2862,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101757679" }, "attributes": { @@ -2922,7 +2922,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101753873" }, "attributes": { @@ -2937,7 +2937,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101755961" }, "attributes": { @@ -2952,7 +2952,7 @@ "className": "Animation", "properties": { - "AnimationId": "" + "AnimationId": "rbxassetid://14101750346" }, "attributes": { diff --git a/shared/Sounds.model.json b/shared/Sounds.model.json index a459868..49f3e20 100644 --- a/shared/Sounds.model.json +++ b/shared/Sounds.model.json @@ -94,6 +94,17 @@ } }, + { + "name": "ACTION_METAL_LAND_WATER", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9116519870" + } + }, + { "name": "ACTION_METAL_JUMP", "className": "Sound", @@ -104,6 +115,17 @@ "SoundId": "rbxassetid://9116519870" } }, + + { + "name": "ACTION_METAL_JUMP_WATER", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9116519870" + } + }, { "name": "ACTION_METAL_STEP", @@ -148,6 +170,48 @@ "SoundId": "rbxassetid://6552141879" } }, + + { + "name": "ACTION_SWIM_KICK", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9117823333", + "Volume": 0.05 + } + }, + + { + "name": "ACTION_SWIM_FAST", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9117822492", + "Volume": 0.1, + + "PlaybackRegion": { + "NumberRange": [1, 2.5] + }, + + "PlaybackRegionsEnabled": true + } + }, + + { + "name": "ACTION_SWIM", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9120554658", + "Volume": 0.5 + } + }, { "name": "ACTION_TERRAIN_BODY_HIT_GROUND", @@ -405,6 +469,38 @@ "SoundId": "" } }, + + { + "name": "ACTION_WATER_ENTER", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxassetid://9117822587", + + "PlaybackRegion": { + "NumberRange": [0, 4] + }, + + "PlaybackRegionsEnabled": true + } + }, + + { + "name": "ACTION_WATER_EXIT", + "className": "Sound", + + "properties": { + "RollOffMinDistance": 8, + "RollOffMaxDistance": 128, + "SoundId": "rbxasset://sounds/impact_water.mp3" + }, + + "attributes": { + "Decay": true + } + }, { "name": "MARIO_ATTACKED", diff --git a/shared/init.lua b/shared/init.lua index 3524bc8..147e065 100644 --- a/shared/init.lua +++ b/shared/init.lua @@ -234,6 +234,14 @@ local SoundTable = { ACTION_THROW = Sounds.ACTION_THROW, ACTION_TWIRL = Sounds.ACTION_TWIRL, + ACTION_METAL_LAND_WATER = Sounds.ACTION_METAL_LAND_WATER, + ACTION_METAL_JUMP_WATER = Sounds.ACTION_METAL_LAND_WATER, + ACTION_WATER_ENTER = Sounds.ACTION_WATER_ENTER, + ACTION_WATER_EXIT = Sounds.ACTION_WATER_EXIT, + ACTION_SWIM_KICK = Sounds.ACTION_SWIM_KICK, + ACTION_SWIM_FAST = Sounds.ACTION_SWIM_FAST, + ACTION_SWIM = Sounds.ACTION_SWIM, + MARIO_ATTACKED = Sounds.MARIO_ATTACKED, MARIO_DOH = Sounds.MARIO_DOH, MARIO_GROUND_POUND_WAH = Sounds.MARIO_GROUND_POUND_WAH,