Implemented swimming.

This commit is contained in:
Max 2023-07-17 19:19:03 -05:00
parent d4000b30fa
commit cfb04d9325
16 changed files with 1311 additions and 147 deletions

View file

@ -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,

View file

@ -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,

View file

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

View file

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

View file

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

View file

@ -0,0 +1,6 @@
{
"properties": {
"Disabled": true,
"RunContext": "Client"
}
}

View file

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

View file

@ -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,

View file

@ -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,

View file

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

View file

@ -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 = `<font color="#FF0000">NULL</font>`
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)

View file

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

View file

@ -181,6 +181,35 @@
<NumberSequence name="Transparency">0 1 0 0.1 0.75 0 0.5 0.75 0 1 1 0 </NumberSequence>
</Properties>
</Item>
<Item class="ParticleEmitter" referent="RBX16">
<Properties>
<Vector3 name="Acceleration">
<X>0</X>
<Y>-1</Y>
<Z>0</Z>
</Vector3>
<BinaryString name="AttributesSerialize">AQAAAAQAAABFbWl0BgAAAAAAADlA</BinaryString>
<ColorSequence name="Color">0 0.807843 0.807843 0.807843 0 1 0.807843 0.807843 0.807843 0 </ColorSequence>
<float name="Drag">3</float>
<token name="EmissionDirection">5</token>
<bool name="Enabled">false</bool>
<NumberRange name="Lifetime">2 2 </NumberRange>
<float name="LightEmission">1</float>
<float name="LightInfluence">1</float>
<string name="Name">PLUNGE_BUBBLE</string>
<float name="Rate">100</float>
<NumberRange name="RotSpeed">-360 360 </NumberRange>
<NumberRange name="Rotation">0 360 </NumberRange>
<NumberSequence name="Size">0 2 0 1 2 0 </NumberSequence>
<NumberRange name="Speed">20 20 </NumberRange>
<Vector2 name="SpreadAngle">
<X>360</X>
<Y>360</Y>
</Vector2>
<Content name="Texture"><url>rbxassetid://425366688</url></Content>
<NumberSequence name="Transparency">0 1 0 0.1 0.75 0 0.5 0.75 0 1 1 0 </NumberSequence>
</Properties>
</Item>
<Item class="Vector3Value" referent="RBX10">
<Properties>
<string name="Name">OriginalPosition</string>

View file

@ -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": {

View file

@ -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",
@ -105,6 +116,17 @@
}
},
{
"name": "ACTION_METAL_JUMP_WATER",
"className": "Sound",
"properties": {
"RollOffMinDistance": 8,
"RollOffMaxDistance": 128,
"SoundId": "rbxassetid://9116519870"
}
},
{
"name": "ACTION_METAL_STEP",
"className": "Sound",
@ -149,6 +171,48 @@
}
},
{
"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",
"className": "Sound",
@ -406,6 +470,38 @@
}
},
{
"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",
"className": "Sound",

View file

@ -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,