Football improvements

Rewrote all of the ball-to-player interactions
Player collisions are better now
Attacks affect the ball in different ways
Centered camera during the countdown period
Players lose their velocity during the countdown period
This commit is contained in:
MysterD 2022-03-08 21:34:14 -08:00
parent 180fe3a8a0
commit 02ffca130e

View file

@ -13,6 +13,19 @@ ballWaterDrag = 0.9
ballParallelInertia = 0.3 ballParallelInertia = 0.3
ballPerpendicularInertia = 1 ballPerpendicularInertia = 1
ballActionValues = {
[ACT_WATER_PUNCH] = { xz = 20, y = 40, directionless = false },
[ACT_MOVE_PUNCHING] = { xz = 35, y = 0, directionless = false },
[ACT_PUNCHING] = { xz = 35, y = 0, directionless = false },
[ACT_GROUND_POUND] = { xz = 40, y = 40, directionless = true },
[ACT_GROUND_POUND_LAND] = { xz = 40, y = 40, directionless = true },
[ACT_JUMP_KICK] = { xz = 10, y = 32, directionless = false },
[ACT_SLIDE_KICK_SLIDE] = { xz = -7, y = 25, directionless = false },
[ACT_SLIDE_KICK] = { xz = -7, y = 25, directionless = false },
[ACT_LONG_JUMP] = { xz = 0, y = 0, directionless = false },
[ACT_CROUCH_SLIDE] = { xz = 0, y = 0, directionless = false },
}
--------------- ---------------
-- globals -- -- globals --
--------------- ---------------
@ -25,6 +38,17 @@ gInitializeBalls = {}
-- utils -- -- utils --
----------- -----------
function clamp(x, a, b)
if x < a then return a end
if x > b then return b end
return x
end
function vec3f_degrees_between(a, b)
local ansAgain = math.acos(vec3f_dot(a, b) / (vec3f_length(a) * vec3f_length(b)))
return math.deg(ansAgain)
end
function my_global_index() function my_global_index()
return gNetworkPlayers[gMarioStates[0].playerIndex].globalIndex return gNetworkPlayers[gMarioStates[0].playerIndex].globalIndex
end end
@ -182,116 +206,183 @@ function bhv_ball_init(obj)
end end
function bhv_ball_player_collision(obj) function bhv_ball_player_collision(obj)
local alterPos = { x = 0, y = 0, z = 0}
local m = nearest_mario_state_to_object(obj) local m = nearest_mario_state_to_object(obj)
if m == nil then return alterPos end
local player = m.marioObj local player = m.marioObj
if player == nil then return alterPos end
local playerRadius = 37 local playerRadius = 37
local playerHeight = 160 / 2 local playerHeight = 160
local alterPos = { x = 0, y = 0, z = 0 }
local objPoint = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ } local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ }
local playerBallRadius = ballRadius
-- figure out player-to-ball radius -- figure out player-to-ball radius
if (m.action & ACT_FLAG_ATTACKING) ~= 0 then local alterBallFlags = (ACT_FLAG_ATTACKING | ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_DIVING)
local playerBallRadius = ballRadius
if ballActionValues[m.action] ~= nil or (m.action & alterBallFlags) ~= 0 then
playerBallRadius = playerBallRadius + 50 playerBallRadius = playerBallRadius + 50
end end
-- figure out if our height collides ------------------------------------------------
local heightOverlap = math.abs((player.oPosY + playerHeight * 0.5) - obj.oPosY) < (playerHeight + playerBallRadius) -- calculate position and determine collision --
------------------------------------------------
-- figure out if our radius collides -- calculate cylinder values
local xdiff = player.oPosX - obj.oPosX local cylY1 = player.oPosY + playerRadius
local zdiff = player.oPosZ - obj.oPosZ local cylY2 = player.oPosY + playerHeight - playerRadius
local xzmag = math.sqrt(xdiff * xdiff + zdiff * zdiff) local cylPoint = { x = player.oPosX, y = clamp(obj.oPosY, cylY1, cylY2), z = player.oPosZ }
local radiusOverlap = xzmag <= (playerRadius + playerBallRadius) local cylDist = vec3f_dist(cylPoint, objPoint)
local xzdiff = (playerRadius + playerBallRadius) - xzmag
-- check if player should affect ball -- check for collision
if heightOverlap and radiusOverlap then if cylDist > (playerBallRadius + playerRadius) then
-- detect if the local player touched it
if m.playerIndex == 0 then
gBallTouchedLocal = true
end
-- detect hit from top
local heightMag = math.abs(obj.oPosY - player.oPosY - 167.5)
if obj.oVelY <= 0 and (heightMag < xzmag or xzmag < 1) and heightMag < playerBallRadius then
alterPos.y = heightMag + 1
obj.oVelY = obj.oVelY * -ballRestitution
if math.abs(obj.oVelX) < math.abs(xdiff * 0.2) then obj.oVelX = -xdiff * 0.2 end
if obj.oVelY < player.oVelY + 5 then obj.oVelY = player.oVelY + 5 end
if math.abs(obj.oVelZ) < math.abs(zdiff * 0.2) then obj.oVelZ = -zdiff * 0.2 end
return alterPos return alterPos
end end
-- prevent division by zero gBallTouchedLocal = (m.playerIndex == 0)
if xzmag < 1 then
xdiff = 1 local vDifference = { x = objPoint.x - cylPoint.x, y = objPoint.y - cylPoint.y, z = objPoint.z - cylPoint.z }
zdiff = 0 local differenceDir = { x = vDifference.x, y = vDifference.y, z = vDifference.z }
xzmag = 1 vec3f_normalize(differenceDir)
alterPos.x = (cylPoint.x + differenceDir.x * (playerBallRadius + playerRadius + 1)) - objPoint.x
alterPos.y = (cylPoint.y + differenceDir.y * (playerBallRadius + playerRadius + 1)) - objPoint.y
alterPos.z = (cylPoint.z + differenceDir.z * (playerBallRadius + playerRadius + 1)) - objPoint.z
-----------------------------------------
-- figure out player's attack velocity --
-----------------------------------------
local vPlayer = { x = player.oVelX, y = player.oVelY, z = player.oVelZ }
local playerTheta = (m.faceAngle.y / 0x8000) * math.pi
-- have attacks alter velocity further
local alterXz = 0
local alterY = 0
local alterDirectionless = false
if ballActionValues[m.action] ~= nil then
alterXz = ballActionValues[m.action].xz
alterY = ballActionValues[m.action].y
alterDirectionless = ballActionValues[m.action].directionless
elseif ((m.action & (ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_DIVING)) ~= 0) or (m.action == ACT_SLIDE_KICK_SLIDE) or (m.action == ACT_SLIDE_KICK) then
-- dive or slide sends it upward, and slows xz
alterXz = -7
alterY = 25
elseif (m.action & ACT_FLAG_ATTACKING) ~= 0 then
-- other attacks should just do something reasonable
alterXz = 10
alterY = 10
end end
-- reflect the current velocity against the player -- adjust angle
local n = { x = xdiff / xzmag, y = 0, z = zdiff / xzmag } local theta = playerTheta
local perpendicular = vec3f_project(v, n) if alterDirectionless and differenceDir.z ~= 0 then
theta = math.atan2(differenceDir.x, differenceDir.z)
end
vPlayer.x = vPlayer.x + math.sin(theta) * alterXz
vPlayer.z = vPlayer.z + math.cos(theta) * alterXz
if vPlayer.y < alterY then vPlayer.y = vPlayer.y + alterY end
local vPlayerMag = vec3f_length(vPlayer)
-------------------------------------------------
-- figure out which velocity interaction to do --
-------------------------------------------------
local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ }
local doReflection = (vPlayerMag == 0)
-- make sure ball is offset in the vPlayer direction
if not doReflection then
local objCylDir = { x = cylPoint.x - objPoint.x, y = cylPoint.y - objPoint.y, z = cylPoint.z - objPoint.z }
local objCylDirXZ = { x = objCylDir.x, y = 0, z = objCylDir.z }
local objCylDirMag = vec3f_length(objCylDir)
local vPlayerXZ = { x = vPlayer.x, y = 0, z = vPlayer.z }
local vPlayerXZMag = vec3f_length(vPlayerXZ)
if objCylDirMag > 0 and vPlayerXZMag > 0 then
doReflection = (vec3f_degrees_between(vPlayer, objCylDir)) <= 120
and (vec3f_degrees_between(vPlayerXZ, objCylDirXZ)) <= 120
end
end
--------------------------------------
-- calculate velocity (interaction) --
--------------------------------------
if not doReflection then
local vPlayerDir = { x = vPlayer.x, y = vPlayer.y, z = vPlayer.z }
vec3f_normalize(vPlayerDir)
-- split velocity into parallel/perpendicular to normal
local perpendicular = vec3f_project(v, vPlayerDir)
local parallel = { x = v.x - perpendicular.x, y = v.y - perpendicular.y, z = v.z - perpendicular.z } local parallel = { x = v.x - perpendicular.x, y = v.y - perpendicular.y, z = v.z - perpendicular.z }
-- apply friction
vec3f_mul(parallel, 0.5)
local parallelMag = vec3f_length(parallel)
local perpendicularMag = vec3f_length(perpendicular)
if perpendicularMag == 0 or perpendicularMag < vPlayerMag then
vec3f_copy(perpendicular, vPlayer)
end
-- reflect velocity along normal
local reflect = {
x = parallel.x + perpendicular.x,
y = parallel.y + perpendicular.y,
z = parallel.z + perpendicular.z
}
-- set new velocity
obj.oVelX = reflect.x
obj.oVelY = reflect.y
obj.oVelZ = reflect.z
end
-------------------------------------
-- calculate velocity (reflection) --
-------------------------------------
if doReflection then
-- split velocity into parallel/perpendicular to normal
local perpendicular = vec3f_project(v, differenceDir)
local parallel = { x = v.x - perpendicular.x, y = v.y - perpendicular.y, z = v.z - perpendicular.z }
-- apply friction and restitution
vec3f_mul(parallel, ballFriction)
vec3f_mul(perpendicular, ballRestitution)
-- play sounds
local parallelLength = vec3f_length(parallel)
local perpendicularLength = vec3f_length(perpendicular)
if perpendicularLength > 5 then
cur_obj_play_sound_2(SOUND_GENERAL_BOX_LANDING_2)
elseif parallelLength > 3 then
cur_obj_play_sound_2(SOUND_ENV_SLIDING)
end
local pushOutMag = 10
-- reflect velocity along normal
local reflect = { local reflect = {
x = parallel.x - perpendicular.x, x = parallel.x - perpendicular.x,
y = parallel.y - perpendicular.y, y = parallel.y - perpendicular.y,
z = parallel.z - perpendicular.z z = parallel.z - perpendicular.z
} }
obj.oVelX = reflect.x -- set new velocity
obj.oVelZ = reflect.z obj.oVelX = reflect.x + differenceDir.x * pushOutMag
obj.oVelY = reflect.y + differenceDir.y * pushOutMag
-- move ball outside of player obj.oVelZ = reflect.z + differenceDir.z * pushOutMag
alterPos.x = -xdiff / xzmag * (xzdiff + 1)
alterPos.z = -zdiff / xzmag * (xzdiff + 1)
-- when ball is moved outside of player, give it speed in that direction
local addVel = { x = alterPos.x * 0.25, y = 0, z = alterPos.z * 0.25 }
local addVelMag = math.sqrt(addVel.x * addVel.x + addVel.z * addVel.z)
if addVelMag > 10 then
addVel.x = addVel.x / addVelMag * 10
addVel.z = addVel.z / addVelMag * 10
end
-- also obtain the player's speed
addVel.x = addVel.x + player.oVelX
addVel.z = addVel.z + player.oVelZ
-- have attacks alter velocity further
if (m.action == ACT_WATER_PUNCH) then
-- water punch launches it
addVel.x = addVel.x + xdiff / xzmag * -20
addVel.z = addVel.z + zdiff / xzmag * -20
obj.oVelY = obj.oVelY + 40
if obj.oVelY > 40 then obj.oVelY = 40 end
elseif (m.action & ACT_FLAG_ATTACKING) ~= 0 then
if (m.action == ACT_MOVE_PUNCHING) or (m.action == ACT_PUNCHING) or (m.action == ACT_GROUND_POUND) then
-- normal punch or ground pound launches it
addVel.x = addVel.x + xdiff / xzmag * -30
addVel.z = addVel.z + zdiff / xzmag * -30
if m.action == ACT_GROUND_POUND then
obj.oVelY = obj.oVelY + 25
if obj.oVelY > 25 then obj.oVelY = 25 end
end
else
-- other attacks send it upward, and decrease the xz force
addVel.x = addVel.x - player.oVelX * 0.3
addVel.z = addVel.z - player.oVelZ * 0.3
obj.oVelY = obj.oVelY + 25
if obj.oVelY > 25 then obj.oVelY = 25 end
end
end
-- apply attack velocity
-- this isn't the correct way to do this but whatever
addVelMag = math.sqrt(addVel.x * addVel.x + addVel.z * addVel.z)
local v = { x = obj.oVelX, y = 0, z = obj.oVelZ }
if vec3f_length(v) < addVelMag * 1.5 then
obj.oVelX = addVel.x
obj.oVelZ = addVel.z
end
end end
return alterPos return alterPos
@ -346,13 +437,13 @@ function bhv_ball_loop(obj)
-- detect player collisions -- detect player collisions
local alter = bhv_ball_player_collision(obj) local alterPos = bhv_ball_player_collision(obj)
-- alter end-point based on player collisions -- alter end-point based on player collisions
local a = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ } local a = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ } local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ }
local b = { x = v.x, y = v.y, z = v.z } local b = { x = v.x, y = v.y, z = v.z }
vec3f_sum(b, b, alter) vec3f_sum(b, b, alterPos)
-- regular movement -- regular movement
local info = collision_find_surface_on_ray( local info = collision_find_surface_on_ray(
@ -772,8 +863,8 @@ function gamemode_active()
sStateTimer = sOverTimeout sStateTimer = sOverTimeout
else else
-- set sparkle -- set sparkle
if scorerNp ~= nil then
local scorerS = gPlayerSyncTable[scorerNp.localIndex] local scorerS = gPlayerSyncTable[scorerNp.localIndex]
if scorerNp ~= nil and scorerS.team == scoringTeam then
scorerS.sparkle = true scorerS.sparkle = true
end end
gGlobalSyncTable.gameState = GAME_STATE_SCORE gGlobalSyncTable.gameState = GAME_STATE_SCORE
@ -1021,12 +1112,10 @@ function mario_update_local(m)
local np = gNetworkPlayers[m.playerIndex] local np = gNetworkPlayers[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex] local s = gPlayerSyncTable[m.playerIndex]
--if (m.controller.buttonPressed & D_JPAD) ~= 0 then if (m.controller.buttonPressed & D_JPAD) ~= 0 then
-- print(m.pos.x, m.pos.y, m.pos.z) --print(m.pos.x, m.pos.y, m.pos.z)
-- sSoccerBall.oPosX = m.pos.x --sSoccerBall = spawn_or_move_ball(m.pos.x, m.pos.y, m.pos.z)
-- sSoccerBall.oPosY = m.pos.y - 100 end
-- sSoccerBall.oPosZ = m.pos.z
--end
-- force players into certain positions and angles -- force players into certain positions and angles
if gGlobalSyncTable.gameState == GAME_STATE_WAIT then if gGlobalSyncTable.gameState == GAME_STATE_WAIT then
@ -1043,6 +1132,9 @@ function mario_update_local(m)
end end
end end
-- center camera
m.controller.buttonDown = m.controller.buttonDown | L_TRIG
-- figure out spawn position -- figure out spawn position
local teamTheta = (3.14 / -2) local teamTheta = (3.14 / -2)
local teamDistance = 1550 local teamDistance = 1550
@ -1073,11 +1165,13 @@ function mario_update_local(m)
m.vel.x = 0 m.vel.x = 0
m.vel.y = 0 m.vel.y = 0
m.vel.z = 0 m.vel.z = 0
m.forwardVel = 0
m.slideVelX = 0
m.slideVelZ = 0
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 0) set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 0)
-- fix vanilla camera -- fix vanilla camera
if m.area.camera.mode == CAMERA_MODE_WATER_SURFACE then if m.area.camera.mode == CAMERA_MODE_WATER_SURFACE then
print(m.area.camera.mode)
set_camera_mode(m.area.camera, CAMERA_MODE_FREE_ROAM, 1) set_camera_mode(m.area.camera, CAMERA_MODE_FREE_ROAM, 1)
end end
elseif m.action == ACT_READING_AUTOMATIC_DIALOG then elseif m.action == ACT_READING_AUTOMATIC_DIALOG then