2022-03-08 06:23:05 +00:00
|
|
|
-- name: Football (soccer)
|
|
|
|
-- description: Play football in the castle grounds.\nOnly use a save that has lowered the water in the moat.
|
2022-03-13 00:05:19 +00:00
|
|
|
-- incompatible: gamemode
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
---------------
|
|
|
|
-- constants --
|
|
|
|
---------------
|
|
|
|
|
|
|
|
ballGravity = 1.5
|
|
|
|
ballRadius = 50
|
|
|
|
ballFriction = 0.988
|
|
|
|
ballRestitution = 0.65
|
|
|
|
ballWaterDrag = 0.9
|
|
|
|
ballParallelInertia = 0.3
|
|
|
|
ballPerpendicularInertia = 1
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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 },
|
|
|
|
}
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
---------------
|
|
|
|
-- globals --
|
|
|
|
---------------
|
|
|
|
|
|
|
|
gBallTouchedLocal = false
|
|
|
|
gCachedBalls = {}
|
|
|
|
|
|
|
|
-----------
|
|
|
|
-- utils --
|
|
|
|
-----------
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
function my_global_index()
|
|
|
|
return gNetworkPlayers[gMarioStates[0].playerIndex].globalIndex
|
|
|
|
end
|
|
|
|
|
|
|
|
function my_location()
|
|
|
|
local np = gNetworkPlayers[gMarioStates[0].playerIndex]
|
|
|
|
return tostring(np.currCourseNum) .. '-' .. tostring(np.currLevelNum) .. '-' .. tostring(np.currAreaIndex) .. '-' .. tostring(np.currActNum)
|
|
|
|
end
|
|
|
|
|
|
|
|
function active_player(m)
|
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
|
|
|
if m.playerIndex == 0 then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
if not np.connected then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
return is_player_active(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
function get_cached_ball(obj)
|
|
|
|
local key = my_location() .. '-' .. tostring(obj.oSyncID)
|
|
|
|
if gCachedBalls[key] == nil then
|
|
|
|
local cb = {}
|
|
|
|
cb.oGlobalOwner = obj.oGlobalOwner
|
|
|
|
cb.oHitTime = obj.oHitTime
|
|
|
|
cb.oNetworkTime = obj.oNetworkTime
|
|
|
|
cb.oPosX = obj.oPosX
|
|
|
|
cb.oPosY = obj.oPosY
|
|
|
|
cb.oPosZ = obj.oPosZ
|
|
|
|
cb.oVelX = obj.oVelX
|
|
|
|
cb.oVelY = obj.oVelY
|
|
|
|
cb.oVelZ = obj.oVelZ
|
|
|
|
gCachedBalls[key] = cb
|
|
|
|
end
|
|
|
|
return gCachedBalls[key]
|
|
|
|
end
|
|
|
|
|
|
|
|
function should_reject_packet(obj)
|
|
|
|
local cb = get_cached_ball(obj)
|
|
|
|
if obj.oHitTime < cb.oHitTime then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
if obj.oHitTime == cb.oHitTime and obj.oGlobalOwner > cb.oGlobalOwner then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
if obj.oHitTime == cb.oHitTime and obj.oGlobalOwner == cb.oGlobalOwner and obj.oNetworkTime < cb.oNetworkTime then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
function find_ball()
|
|
|
|
local obj = obj_get_first(OBJ_LIST_DEFAULT)
|
|
|
|
while obj ~= nil do
|
|
|
|
if get_id_from_behavior(obj.behavior) == id_bhvBall then
|
|
|
|
return obj
|
|
|
|
end
|
|
|
|
obj = obj_get_next(obj)
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
function spawn_or_move_ball(x, y, z)
|
|
|
|
-- search for ball
|
|
|
|
local obj = find_ball()
|
|
|
|
if obj ~= nil then
|
|
|
|
-- move ball
|
|
|
|
obj.oPosX = x
|
|
|
|
obj.oPosY = y
|
|
|
|
obj.oPosZ = z
|
|
|
|
obj.oVelX = 0
|
|
|
|
obj.oVelY = 0
|
|
|
|
obj.oVelZ = 0
|
|
|
|
|
|
|
|
obj.oGlobalOwner = my_global_index()
|
2022-03-10 03:08:41 +00:00
|
|
|
obj.oHitTime = get_network_area_timer()
|
|
|
|
obj.oNetworkTime = get_network_area_timer()
|
2022-03-08 06:23:05 +00:00
|
|
|
network_send_object(obj, false)
|
|
|
|
|
|
|
|
return obj
|
|
|
|
end
|
|
|
|
|
|
|
|
-- don't spawn unless server
|
|
|
|
if my_global_index() ~= 0 then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
-- spawn ball
|
|
|
|
return spawn_sync_object(
|
|
|
|
id_bhvBall,
|
|
|
|
E_MODEL_SPINY_BALL,
|
|
|
|
x, y, z,
|
|
|
|
|
|
|
|
function(obj)
|
|
|
|
obj.oGlobalOwner = my_global_index()
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
--------------
|
|
|
|
-- behavior --
|
|
|
|
--------------
|
|
|
|
|
|
|
|
-- define ball's custom fields
|
|
|
|
define_custom_obj_fields({
|
|
|
|
oNetworkTime = 'u32',
|
|
|
|
oHitTime = 'u32',
|
|
|
|
oGlobalOwner = 'u32',
|
|
|
|
oFrozen = 'u32',
|
|
|
|
})
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
2022-03-10 02:59:55 +00:00
|
|
|
function bhv_ball_particle_trail(obj)
|
|
|
|
local spi = obj_get_temp_spawn_particles_info(E_MODEL_SPARKLES)
|
|
|
|
if spi == nil then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
spi.behParam = 2
|
|
|
|
spi.count = 1
|
|
|
|
spi.offsetY = -1 * ballRadius
|
|
|
|
spi.forwardVelBase = 8
|
|
|
|
spi.forwardVelRange = 0
|
|
|
|
spi.velYBase = 6
|
|
|
|
spi.velYRange = 0
|
|
|
|
spi.gravity = 0
|
|
|
|
spi.dragStrength = 5
|
|
|
|
spi.sizeBase = 10
|
|
|
|
spi.sizeRange = 30
|
|
|
|
|
|
|
|
cur_obj_spawn_particles(spi)
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
2022-03-10 02:59:55 +00:00
|
|
|
function bhv_ball_particle_bounce(obj)
|
|
|
|
local spi = obj_get_temp_spawn_particles_info(E_MODEL_MIST)
|
|
|
|
if spi == nil then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
spi.behParam = 3
|
|
|
|
spi.count = 5
|
|
|
|
spi.offsetY = -1 * ballRadius
|
|
|
|
spi.forwardVelBase = 6
|
|
|
|
spi.forwardVelRange = -6
|
|
|
|
spi.velYBase = 6
|
|
|
|
spi.velYRange = -6
|
|
|
|
spi.gravity = 0
|
|
|
|
spi.dragStrength = 5
|
|
|
|
spi.sizeBase = 8
|
|
|
|
spi.sizeRange = 13
|
|
|
|
|
|
|
|
cur_obj_spawn_particles(spi)
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
2022-03-08 06:23:05 +00:00
|
|
|
function bhv_ball_init(obj)
|
|
|
|
-- flags and such
|
|
|
|
obj.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
|
|
|
|
obj.oGraphYOffset = 0
|
|
|
|
cur_obj_scale(1.4)
|
|
|
|
|
|
|
|
-- physics
|
|
|
|
obj.oWallHitboxRadius = 40.00
|
|
|
|
obj.oGravity = 2.50
|
|
|
|
obj.oBounciness = -0.75
|
|
|
|
obj.oDragStrength = 0.00
|
|
|
|
obj.oFriction = 0.99
|
|
|
|
obj.oBuoyancy = -2.00
|
|
|
|
|
|
|
|
-- hitbox
|
|
|
|
obj.hitboxRadius = 100
|
|
|
|
obj.hitboxHeight = 100
|
|
|
|
|
|
|
|
-- custom values
|
|
|
|
obj.oNetworkTime = 0
|
|
|
|
obj.oHitTime = 0
|
|
|
|
obj.oFrozen = 0
|
|
|
|
|
|
|
|
-- cache
|
|
|
|
local cb = get_cached_ball(obj)
|
|
|
|
|
|
|
|
network_init_object(obj, false, {
|
|
|
|
'oPosX',
|
|
|
|
'oPosY',
|
|
|
|
'oPosZ',
|
|
|
|
'oVelX',
|
|
|
|
'oVelY',
|
|
|
|
'oVelZ',
|
|
|
|
'oGlobalOwner',
|
|
|
|
'oNetworkTime',
|
|
|
|
'oHitTime',
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
2022-03-08 06:23:05 +00:00
|
|
|
function bhv_ball_player_collision(obj)
|
2022-03-09 05:34:14 +00:00
|
|
|
local alterPos = { x = 0, y = 0, z = 0}
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
local m = nearest_mario_state_to_object(obj)
|
2022-03-09 05:34:14 +00:00
|
|
|
if m == nil then return alterPos end
|
2022-03-08 06:23:05 +00:00
|
|
|
local player = m.marioObj
|
2022-03-09 05:34:14 +00:00
|
|
|
if player == nil then return alterPos end
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
local playerRadius = 37
|
2022-03-09 05:34:14 +00:00
|
|
|
local playerHeight = 160
|
|
|
|
|
|
|
|
local objPoint = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
|
2022-03-08 06:23:05 +00:00
|
|
|
local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ }
|
|
|
|
|
|
|
|
-- figure out player-to-ball radius
|
2022-03-09 05:34:14 +00:00
|
|
|
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
|
2022-03-08 06:23:05 +00:00
|
|
|
playerBallRadius = playerBallRadius + 50
|
|
|
|
end
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
------------------------------------------------
|
|
|
|
-- calculate position and determine collision --
|
|
|
|
------------------------------------------------
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
-- calculate cylinder values
|
|
|
|
local cylY1 = player.oPosY + playerRadius
|
|
|
|
local cylY2 = player.oPosY + playerHeight - playerRadius
|
|
|
|
local cylPoint = { x = player.oPosX, y = clamp(obj.oPosY, cylY1, cylY2), z = player.oPosZ }
|
|
|
|
local cylDist = vec3f_dist(cylPoint, objPoint)
|
|
|
|
|
|
|
|
-- check for collision
|
|
|
|
if cylDist > (playerBallRadius + playerRadius) then
|
|
|
|
return alterPos
|
|
|
|
end
|
|
|
|
|
|
|
|
gBallTouchedLocal = (m.playerIndex == 0)
|
|
|
|
|
|
|
|
local vDifference = { x = objPoint.x - cylPoint.x, y = objPoint.y - cylPoint.y, z = objPoint.z - cylPoint.z }
|
|
|
|
local differenceDir = { x = vDifference.x, y = vDifference.y, z = vDifference.z }
|
2022-03-10 02:59:55 +00:00
|
|
|
if vec3f_length(differenceDir) ~= 0 then
|
|
|
|
vec3f_normalize(differenceDir)
|
|
|
|
end
|
2022-03-09 05:34:14 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
-- adjust angle
|
|
|
|
local theta = playerTheta
|
|
|
|
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
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
2022-03-09 05:34:14 +00:00
|
|
|
end
|
|
|
|
|
2022-03-10 02:59:55 +00:00
|
|
|
-- make sure player has a velocity
|
|
|
|
if not doReflection and vPlayerMag == 0 then
|
|
|
|
doReflection = true
|
|
|
|
end
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
--------------------------------------
|
|
|
|
-- calculate velocity (interaction) --
|
|
|
|
--------------------------------------
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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)
|
2022-03-08 06:23:05 +00:00
|
|
|
local parallel = { x = v.x - perpendicular.x, y = v.y - perpendicular.y, z = v.z - perpendicular.z }
|
2022-03-09 05:34:14 +00:00
|
|
|
|
|
|
|
-- 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
|
2022-03-08 06:23:05 +00:00
|
|
|
local reflect = {
|
2022-03-09 05:34:14 +00:00
|
|
|
x = parallel.x + perpendicular.x,
|
|
|
|
y = parallel.y + perpendicular.y,
|
|
|
|
z = parallel.z + perpendicular.z
|
2022-03-08 06:23:05 +00:00
|
|
|
}
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
-- set new velocity
|
2022-03-08 06:23:05 +00:00
|
|
|
obj.oVelX = reflect.x
|
2022-03-09 05:34:14 +00:00
|
|
|
obj.oVelY = reflect.y
|
2022-03-08 06:23:05 +00:00
|
|
|
obj.oVelZ = reflect.z
|
2022-03-09 05:34:14 +00:00
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
-------------------------------------
|
|
|
|
-- calculate velocity (reflection) --
|
|
|
|
-------------------------------------
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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 }
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
-- apply friction and restitution
|
|
|
|
vec3f_mul(parallel, ballFriction)
|
|
|
|
vec3f_mul(perpendicular, ballRestitution)
|
|
|
|
|
|
|
|
-- play sounds
|
|
|
|
local parallelLength = vec3f_length(parallel)
|
|
|
|
local perpendicularLength = vec3f_length(perpendicular)
|
2022-03-08 06:23:05 +00:00
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
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)
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
2022-03-09 05:34:14 +00:00
|
|
|
|
|
|
|
local pushOutMag = 10
|
|
|
|
|
|
|
|
-- 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 + differenceDir.x * pushOutMag
|
|
|
|
obj.oVelY = reflect.y + differenceDir.y * pushOutMag
|
|
|
|
obj.oVelZ = reflect.z + differenceDir.z * pushOutMag
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return alterPos
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
|
|
|
--- @param offset Vec3f
|
2022-03-08 06:23:05 +00:00
|
|
|
function bhv_ball_resolve(obj, offset)
|
|
|
|
local a = { x = obj.oPosX + 0, y = obj.oPosY + 0, z = obj.oPosZ + 0 }
|
|
|
|
local dir = { x = offset.x * -1.0, y = offset.y * -1.0, z = offset.z * -1.0}
|
|
|
|
|
|
|
|
info = collision_find_surface_on_ray(
|
|
|
|
a.x, a.y, a.z,
|
|
|
|
dir.x, dir.y, dir.z)
|
|
|
|
|
|
|
|
obj.oPosX = info.hitPos.x + offset.x
|
|
|
|
obj.oPosY = info.hitPos.y + offset.y
|
|
|
|
obj.oPosZ = info.hitPos.z + offset.z
|
|
|
|
|
|
|
|
if info.surface == nil then return nil end
|
|
|
|
return { x = info.surface.normal.x, y = info.surface.normal.y, z = info.surface.normal.z }
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param obj Object
|
2022-03-08 06:23:05 +00:00
|
|
|
function bhv_ball_loop(obj)
|
|
|
|
if obj.oFrozen ~= 0 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
gBallTouchedLocal = false
|
|
|
|
local cb = get_cached_ball(obj)
|
|
|
|
|
|
|
|
-- detect when a packet was received
|
|
|
|
if obj.oNetworkTime ~= cb.oNetworkTime then
|
|
|
|
if should_reject_packet(obj) then
|
|
|
|
-- reject packet
|
|
|
|
obj.oGlobalOwner = cb.oGlobalOwner
|
|
|
|
obj.oHitTime = cb.oHitTime
|
|
|
|
obj.oNetworkTime = cb.oNetworkTime
|
|
|
|
obj.oPosX = cb.oPosX
|
|
|
|
obj.oPosY = cb.oPosY
|
|
|
|
obj.oPosZ = cb.oPosZ
|
|
|
|
obj.oVelX = cb.oVelX
|
|
|
|
obj.oVelY = cb.oVelY
|
|
|
|
obj.oVelZ = cb.oVelZ
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local orig = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
|
|
|
|
|
|
|
|
obj.oVelY = obj.oVelY - ballGravity
|
|
|
|
if obj.oVelX == 0 and obj.oVelY == 0 and obj.oVelZ == 0 then
|
|
|
|
obj.oVelY = obj.oVelY + 0.01
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- detect player collisions
|
2022-03-09 05:34:14 +00:00
|
|
|
local alterPos = bhv_ball_player_collision(obj)
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
-- alter end-point based on player collisions
|
|
|
|
local a = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
|
|
|
|
local v = { x = obj.oVelX, y = obj.oVelY, z = obj.oVelZ }
|
|
|
|
local b = { x = v.x, y = v.y, z = v.z }
|
2022-03-09 05:34:14 +00:00
|
|
|
vec3f_sum(b, b, alterPos)
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
-- regular movement
|
|
|
|
local info = collision_find_surface_on_ray(
|
|
|
|
a.x, a.y, a.z,
|
|
|
|
b.x, b.y, b.z)
|
|
|
|
|
|
|
|
obj.oPosX = info.hitPos.x
|
|
|
|
obj.oPosY = info.hitPos.y
|
|
|
|
obj.oPosZ = info.hitPos.z
|
|
|
|
|
|
|
|
-- detect normal along movement vector
|
|
|
|
local vMag = vec3f_length(v)
|
2022-03-10 02:59:55 +00:00
|
|
|
if vMag > 0 then
|
|
|
|
local vNorm = { x = v.x / vMag, y = v.y / vMag, z = v.z / vMag }
|
|
|
|
b = { x = v.x + vNorm.x * (vMag + ballRadius), y = v.y + vNorm.y * (vMag + ballRadius), z = v.z + vNorm.z * (vMag + ballRadius) }
|
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
info = collision_find_surface_on_ray(
|
|
|
|
a.x, a.y, a.z,
|
|
|
|
b.x, b.y, b.z)
|
|
|
|
|
|
|
|
-- figure out the standard normal
|
|
|
|
local colNormals = {}
|
|
|
|
if info.surface ~= nil then
|
|
|
|
table.insert(colNormals, { x = info.surface.normal.x, y = info.surface.normal.y, z = info.surface.normal.z })
|
2022-03-11 02:23:25 +00:00
|
|
|
if vMag > 5 then
|
|
|
|
bhv_ball_particle_bounce(obj)
|
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
else
|
|
|
|
table.insert(colNormals, nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- resolve collisions around ball
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = ballRadius, y = 0, z = 0 }))
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = -ballRadius, y = 0, z = 0 }))
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = 0, y = 0, z = ballRadius }))
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = 0, y = 0, z = -ballRadius }))
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = 0, y = ballRadius, z = 0 }))
|
|
|
|
table.insert(colNormals, bhv_ball_resolve(obj, { x = 0, y = -ballRadius, z = 0 }))
|
|
|
|
|
|
|
|
-- figure out collision normal
|
|
|
|
local collisionN = { x = 0, y = 0, z = 0 }
|
|
|
|
local collisionCount = 0
|
|
|
|
for _, colN in ipairs(colNormals) do
|
|
|
|
if colN ~= nil then
|
|
|
|
vec3f_sum(collisionN, collisionN, colN)
|
|
|
|
collisionCount = collisionCount + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- reflect collisions
|
|
|
|
if collisionCount > 0 then
|
|
|
|
-- calculate total normal
|
|
|
|
vec3f_mul(collisionN, 1.0 / collisionCount)
|
|
|
|
vec3f_normalize(collisionN)
|
|
|
|
|
|
|
|
-- split velocity into parallel/perpendicular to normal
|
|
|
|
local perpendicular = vec3f_project(v, collisionN)
|
|
|
|
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)
|
|
|
|
|
|
|
|
-- stop ball in parallel axis
|
|
|
|
local parallelLength = vec3f_length(parallel)
|
|
|
|
if parallelLength < ballParallelInertia then
|
|
|
|
vec3f_mul(parallel, 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- stop ball in perpendicular axis
|
|
|
|
local perpendicularLength = vec3f_length(perpendicular)
|
|
|
|
if perpendicularLength < ballPerpendicularInertia then
|
|
|
|
vec3f_mul(perpendicular, 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- play sounds
|
|
|
|
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
|
|
|
|
|
|
|
|
-- 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
|
|
|
|
|
|
|
|
-- float in water
|
|
|
|
local waterLevel = find_water_level(obj.oPosX, obj.oPosZ)
|
|
|
|
if obj.oPosY < waterLevel then
|
|
|
|
obj.oVelX = obj.oVelX * ballWaterDrag
|
|
|
|
obj.oVelY = obj.oVelY * ballWaterDrag + 2
|
|
|
|
obj.oVelZ = obj.oVelZ * ballWaterDrag
|
|
|
|
end
|
|
|
|
|
|
|
|
-- sanity check floor
|
|
|
|
local floor = find_floor_height(obj.oPosX, obj.oPosY, obj.oPosZ)
|
|
|
|
if obj.oPosY <= floor or floor <= -10000 then
|
|
|
|
obj.oPosX = orig.x
|
|
|
|
obj.oPosY = orig.y
|
|
|
|
obj.oPosZ = orig.z
|
|
|
|
obj.oVelX = -obj.oVelX
|
|
|
|
obj.oVelY = -obj.oVelY
|
|
|
|
obj.oVelZ = -obj.oVelZ
|
|
|
|
end
|
|
|
|
|
|
|
|
-- update rotation
|
|
|
|
if obj.oVelX ~= 0 or obj.oVelZ ~= 0 then
|
|
|
|
local moveAngle = atan2s(obj.oVelZ * 100, obj.oVelX * 100)
|
|
|
|
local xzMag = math.sqrt(obj.oVelX * obj.oVelX + obj.oVelZ * obj.oVelZ)
|
|
|
|
obj.oFaceAngleYaw = moveAngle
|
|
|
|
obj.oFaceAnglePitch = obj.oFaceAnglePitch + xzMag * 100
|
|
|
|
end
|
|
|
|
|
|
|
|
-- send out object if we touched it
|
2022-03-10 03:08:41 +00:00
|
|
|
local updateRateSend = (obj.oGlobalOwner == my_global_index() and (get_network_area_timer() - obj.oNetworkTime) > 5)
|
2022-03-08 06:23:05 +00:00
|
|
|
if gBallTouchedLocal or updateRateSend then
|
|
|
|
if gBallTouchedLocal then
|
|
|
|
obj.oGlobalOwner = my_global_index()
|
2022-03-10 03:08:41 +00:00
|
|
|
obj.oHitTime = get_network_area_timer()
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
2022-03-10 03:08:41 +00:00
|
|
|
obj.oNetworkTime = get_network_area_timer()
|
2022-03-08 06:23:05 +00:00
|
|
|
network_send_object(obj, false)
|
|
|
|
end
|
|
|
|
|
2022-03-10 02:59:55 +00:00
|
|
|
-- spawn a particle trail
|
|
|
|
if vMag > 50 then
|
|
|
|
bhv_ball_particle_trail(obj)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- hack: make sure we never set velocity to nan
|
|
|
|
if obj.oVelX ~= obj.oVelX then obj.oVelX = 0 end
|
|
|
|
if obj.oVelY ~= obj.oVelY then obj.oVelY = 0 end
|
|
|
|
if obj.oVelZ ~= obj.oVelZ then obj.oVelZ = 0 end
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
-- hack: save pos/vel to detect packets
|
|
|
|
cb.oGlobalOwner = obj.oGlobalOwner
|
|
|
|
cb.oHitTime = obj.oHitTime
|
|
|
|
cb.oNetworkTime = obj.oNetworkTime
|
|
|
|
cb.oPosX = obj.oPosX
|
|
|
|
cb.oPosY = obj.oPosY
|
|
|
|
cb.oPosZ = obj.oPosZ
|
|
|
|
cb.oVelX = obj.oVelX
|
|
|
|
cb.oVelY = obj.oVelY
|
|
|
|
cb.oVelZ = obj.oVelZ
|
|
|
|
end
|
|
|
|
|
|
|
|
id_bhvBall = hook_behavior(nil, OBJ_LIST_DEFAULT, true, bhv_ball_init, bhv_ball_loop)
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
--------------
|
|
|
|
-- gamemode --
|
|
|
|
--------------
|
|
|
|
|
|
|
|
GAME_STATE_WAIT = 0
|
|
|
|
GAME_STATE_ACTIVE = 1
|
|
|
|
GAME_STATE_SCORE = 2
|
|
|
|
GAME_STATE_OOB = 3
|
|
|
|
GAME_STATE_OVER = 4
|
|
|
|
|
|
|
|
sSoccerBall = nil
|
|
|
|
sBallSpawnPos = { x = (4573 + -4634) / 2, y = -50, z = (-2077 + -212) / 2 }
|
|
|
|
sBallHidePos = { x = sBallSpawnPos.x, y = -5000, z = sBallSpawnPos.z }
|
|
|
|
|
|
|
|
sGameModeInitialized = false
|
|
|
|
sWaitTimeout = 30 * 5
|
|
|
|
sOobTimeout = 30 * 5
|
|
|
|
sScoreTimeout = 30 * 5
|
|
|
|
sOverTimeout = 30 * 15
|
|
|
|
sStateTimer = sWaitTimeout
|
|
|
|
sMaxScore = 5
|
|
|
|
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_WAIT
|
|
|
|
gGlobalSyncTable.displayText = ' '
|
|
|
|
gGlobalSyncTable.displayFont = FONT_HUD
|
|
|
|
gGlobalSyncTable.displayColor = 0xFFFFFF
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.scoreRed = 0
|
|
|
|
gGlobalSyncTable.scoreBlue = 0
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
function gamemode_initialize()
|
2022-03-11 02:33:52 +00:00
|
|
|
-- hide the SM64 HUD
|
|
|
|
hud_hide()
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
-- prevent warp doors from working
|
|
|
|
local wasRefreshed = false
|
|
|
|
local obj = obj_get_first(OBJ_LIST_SURFACE)
|
|
|
|
while obj ~= nil do
|
|
|
|
local behaviorId = get_id_from_behavior(obj.behavior)
|
|
|
|
if behaviorId == id_bhvDoorWarp then
|
|
|
|
obj.oInteractType = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
-- hide exclamation box
|
|
|
|
if behaviorId == id_bhvExclamationBox then
|
|
|
|
obj.oPosX = sBallHidePos.x
|
|
|
|
obj.oPosY = sBallHidePos.y
|
|
|
|
obj.oPosZ = sBallHidePos.z
|
|
|
|
end
|
|
|
|
|
|
|
|
if behaviorId == id_bhvLllHexagonalMesh then
|
|
|
|
wasRefreshed = true
|
|
|
|
end
|
|
|
|
obj = obj_get_next(obj)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- server spawns objects
|
|
|
|
if my_global_index() == 0 and not wasRefreshed then
|
|
|
|
-- block vanish cap
|
|
|
|
spawn_sync_object(
|
|
|
|
id_bhvLllHexagonalMesh,
|
|
|
|
E_MODEL_TRAMPOLINE,
|
|
|
|
-3390, -512, -2023,
|
|
|
|
function (obj)
|
|
|
|
obj.oOpacity = 255
|
|
|
|
obj.header.gfx.scale.x = 3
|
|
|
|
obj.header.gfx.scale.z = 3;
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- block basement door
|
|
|
|
spawn_sync_object(
|
|
|
|
id_bhvLllHexagonalMesh,
|
|
|
|
E_MODEL_TRAMPOLINE,
|
|
|
|
3279 + 45, -511, -2937 + 45,
|
|
|
|
function (obj)
|
|
|
|
obj.oOpacity = 255
|
|
|
|
obj.header.gfx.scale.x = 2
|
|
|
|
obj.header.gfx.scale.z = 2
|
|
|
|
obj.oFaceAngleYaw = 0x6000
|
|
|
|
obj.oFaceAngleRoll = 0x4000
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- block area near ramp
|
|
|
|
spawn_sync_object(
|
|
|
|
id_bhvStaticCheckeredPlatform,
|
|
|
|
E_MODEL_CHECKERBOARD_PLATFORM,
|
|
|
|
4430 - 65, 545 + 250 - 5, -6000 - 65,
|
|
|
|
function (obj)
|
|
|
|
obj.oOpacity = 255
|
|
|
|
obj.oFaceAngleYaw = 0x2000
|
|
|
|
obj.oFaceAngleRoll = 0x4000
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- block area near ramp
|
|
|
|
for i=0,6 do
|
|
|
|
spawn_sync_object(
|
|
|
|
id_bhvStaticCheckeredPlatform,
|
|
|
|
E_MODEL_CHECKERBOARD_PLATFORM,
|
|
|
|
4460, 545 + 250, -6000 + i * 305,
|
|
|
|
function (obj)
|
|
|
|
obj.oOpacity = 255
|
|
|
|
obj.oFaceAngleYaw = 0
|
|
|
|
obj.oFaceAngleRoll = 0x4000
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- block cannon
|
|
|
|
spawn_sync_object(
|
|
|
|
id_bhvLllHexagonalMesh,
|
|
|
|
E_MODEL_TRAMPOLINE,
|
|
|
|
2385, 88, 1956,
|
|
|
|
function (obj)
|
|
|
|
obj.oOpacity = 255
|
|
|
|
obj.header.gfx.scale.x = 0.9
|
|
|
|
obj.header.gfx.scale.z = 0.9
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
sGameModeInitialized = true
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_shuffle()
|
|
|
|
local t = {}
|
|
|
|
local count = 0
|
|
|
|
-- create table of players
|
|
|
|
for i = 0, (MAX_PLAYERS-1) do
|
|
|
|
local m = gMarioStates[i]
|
|
|
|
local s = gPlayerSyncTable[i]
|
|
|
|
if active_player(m) then
|
|
|
|
table.insert(t, s)
|
|
|
|
count = count + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- shuffle
|
|
|
|
for i = #t, 2, -1 do
|
|
|
|
local j = math.random(i)
|
|
|
|
t[i], t[j] = t[j], t[i]
|
|
|
|
end
|
|
|
|
|
|
|
|
-- assign teams
|
|
|
|
local team1Count = 0
|
|
|
|
local team2Count = 0
|
|
|
|
local oddS = nil
|
|
|
|
for i, s in ipairs(t) do
|
|
|
|
if (i - 1) < count / 2 then
|
|
|
|
s.team = 1
|
|
|
|
team1Count = team1Count + 1
|
|
|
|
oddS = s
|
|
|
|
else
|
|
|
|
s.team = 2
|
|
|
|
team2Count = team2Count + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- shuffle odd player
|
|
|
|
if team1Count > team2Count then
|
|
|
|
oddS.team = math.random(1, 2)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_wait()
|
|
|
|
sSoccerBall = spawn_or_move_ball(sBallSpawnPos.x, sBallSpawnPos.y, sBallSpawnPos.z)
|
|
|
|
|
|
|
|
-- server only
|
|
|
|
if my_global_index() == 0 then
|
|
|
|
-- claim the ball
|
|
|
|
if sSoccerBall.oGlobalOwner ~= my_global_index() then
|
|
|
|
sSoccerBall.oGlobalOwner = my_global_index()
|
2022-03-10 03:08:41 +00:00
|
|
|
sSoccerBall.oHitTime = get_network_area_timer()
|
|
|
|
sSoccerBall.oNetworkTime = get_network_area_timer()
|
2022-03-08 06:23:05 +00:00
|
|
|
network_send_object(sSoccerBall, false)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- clear sparkles
|
|
|
|
for i=0,(MAX_PLAYERS-1) do
|
|
|
|
local sm = gPlayerSyncTable[i]
|
|
|
|
sm.sparkle = false
|
|
|
|
end
|
|
|
|
|
|
|
|
-- decrement timer
|
|
|
|
sStateTimer = sStateTimer - 1
|
|
|
|
|
|
|
|
-- update the visible timer
|
|
|
|
if math.floor(sStateTimer / 30) ~= math.floor((sStateTimer + 1) / 30) then
|
|
|
|
gGlobalSyncTable.displayFont = FONT_HUD
|
|
|
|
gGlobalSyncTable.displayText = tostring(1 + math.floor(sStateTimer / 30))
|
|
|
|
gGlobalSyncTable.displayColor = 0xFFFFFF
|
|
|
|
end
|
|
|
|
|
|
|
|
-- start the round
|
|
|
|
if sStateTimer <= 0 then
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_ACTIVE
|
|
|
|
gGlobalSyncTable.displayFont = FONT_HUD
|
|
|
|
gGlobalSyncTable.displayText = ' '
|
|
|
|
gGlobalSyncTable.displayColor = 0xFFFFFF
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_active()
|
|
|
|
-- server only
|
|
|
|
if my_global_index() == 0 then
|
|
|
|
local validGoalY = ((sSoccerBall.oPosY + ballRadius) < -550)
|
|
|
|
local validGoalZ = (sSoccerBall.oPosX < sBallSpawnPos.x and sSoccerBall.oPosZ < -2846) or (sSoccerBall.oPosX > sBallSpawnPos.x and sSoccerBall.oPosZ > -1205)
|
|
|
|
if validGoalY and validGoalZ then
|
|
|
|
if sSoccerBall.oPosX < sBallSpawnPos.x and sSoccerBall.oPosZ < -2846 then
|
|
|
|
end
|
|
|
|
|
|
|
|
spawn_sync_object(id_bhvExplosion, E_MODEL_EXPLOSION, sSoccerBall.oPosX, sSoccerBall.oPosY, sSoccerBall.oPosZ, nil)
|
|
|
|
|
|
|
|
local scoringTeam = 0
|
|
|
|
if sSoccerBall.oPosX < sBallSpawnPos.x then
|
|
|
|
scoringTeam = 1
|
|
|
|
else
|
|
|
|
scoringTeam = 2
|
|
|
|
end
|
|
|
|
|
|
|
|
local gameOver = false
|
|
|
|
local displayName = ''
|
|
|
|
local scorerNp = network_player_from_global_index(sSoccerBall.oGlobalOwner)
|
|
|
|
if scorerNp ~= nil then
|
|
|
|
local scorerS = gPlayerSyncTable[scorerNp.localIndex]
|
|
|
|
if scorerS.team == scoringTeam then
|
|
|
|
displayName = ' (' .. scorerNp.name .. ')'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if scoringTeam == 1 then
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.scoreRed = gGlobalSyncTable.scoreRed + 1
|
2022-03-08 06:23:05 +00:00
|
|
|
gGlobalSyncTable.displayFont = FONT_NORMAL
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.displayColor = 0xFF9999
|
|
|
|
if gGlobalSyncTable.scoreRed >= sMaxScore then
|
|
|
|
gGlobalSyncTable.displayText = 'red team wins!'
|
2022-03-08 06:23:05 +00:00
|
|
|
gameOver = true
|
|
|
|
else
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.displayText = 'red team scored' .. displayName
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
|
|
|
else
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.scoreBlue = gGlobalSyncTable.scoreBlue + 1
|
2022-03-08 06:23:05 +00:00
|
|
|
gGlobalSyncTable.displayFont = FONT_NORMAL
|
|
|
|
gGlobalSyncTable.displayColor = 0x9999FF
|
2022-03-11 02:23:25 +00:00
|
|
|
if gGlobalSyncTable.scoreBlue >= sMaxScore then
|
|
|
|
gGlobalSyncTable.displayText = 'blue team wins!'
|
2022-03-08 06:23:05 +00:00
|
|
|
gameOver = true
|
|
|
|
else
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.displayText = 'blue team scored' .. displayName
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if gameOver then
|
|
|
|
-- set sparkle
|
|
|
|
for i=0,(MAX_PLAYERS-1) do
|
|
|
|
local im = gMarioStates[i]
|
|
|
|
local sm = gPlayerSyncTable[i]
|
|
|
|
if active_player(im) then
|
|
|
|
if sm.team == scoringTeam then
|
|
|
|
sm.sparkle = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_OVER
|
|
|
|
sStateTimer = sOverTimeout
|
|
|
|
else
|
|
|
|
-- set sparkle
|
2022-03-09 05:34:14 +00:00
|
|
|
local scorerS = gPlayerSyncTable[scorerNp.localIndex]
|
|
|
|
if scorerNp ~= nil and scorerS.team == scoringTeam then
|
2022-03-08 06:23:05 +00:00
|
|
|
scorerS.sparkle = true
|
|
|
|
end
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_SCORE
|
|
|
|
sStateTimer = sScoreTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- check for oob
|
|
|
|
local ignoreOob = (sSoccerBall.oPosX >= 3230 and sSoccerBall.oPosX <= 4460 and sSoccerBall.oPosZ >= -6230 and sSoccerBall.oPosZ <= -2725) -- ramp near bridge
|
|
|
|
if not ignoreOob and sSoccerBall.oPosY > 0 then
|
|
|
|
local floorHeight = find_floor_height(sSoccerBall.oPosX, sSoccerBall.oPosY, sSoccerBall.oPosZ)
|
|
|
|
if sSoccerBall.oPosY - ballRadius - 10 < floorHeight then
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_OOB
|
|
|
|
sStateTimer = sOobTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- check for other OOB
|
|
|
|
if validGoalY and not validGoalZ then
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_OOB
|
|
|
|
sStateTimer = sOobTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_score()
|
|
|
|
sSoccerBall = spawn_or_move_ball(sBallHidePos.x, sBallHidePos.y, sBallHidePos.z)
|
|
|
|
|
|
|
|
-- server only
|
|
|
|
if my_global_index() == 0 then
|
|
|
|
-- decrement timer
|
|
|
|
sStateTimer = sStateTimer - 1
|
|
|
|
|
|
|
|
-- start the round
|
|
|
|
if sStateTimer <= 0 then
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_WAIT
|
|
|
|
sStateTimer = sWaitTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_oob()
|
|
|
|
sSoccerBall = spawn_or_move_ball(sBallHidePos.x, sBallHidePos.y, sBallHidePos.z)
|
|
|
|
|
|
|
|
-- server only
|
|
|
|
if my_global_index() == 0 then
|
|
|
|
-- decrement timer
|
|
|
|
sStateTimer = sStateTimer - 1
|
|
|
|
|
|
|
|
gGlobalSyncTable.displayFont = FONT_NORMAL
|
|
|
|
gGlobalSyncTable.displayText = 'out of bounds'
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.displayColor = 0xFFFFFF
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
-- start the round
|
|
|
|
if sStateTimer <= 0 then
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_WAIT
|
|
|
|
sStateTimer = sWaitTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_over()
|
|
|
|
sSoccerBall = spawn_or_move_ball(sBallHidePos.x, sBallHidePos.y, sBallHidePos.z)
|
|
|
|
|
|
|
|
-- server only
|
|
|
|
if my_global_index() == 0 then
|
|
|
|
-- decrement timer
|
|
|
|
sStateTimer = sStateTimer - 1
|
|
|
|
|
|
|
|
-- start the round
|
|
|
|
if sStateTimer <= 0 then
|
|
|
|
-- shuffle teams
|
|
|
|
gamemode_shuffle()
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.scoreRed = 0
|
|
|
|
gGlobalSyncTable.scoreBlue = 0
|
2022-03-08 06:23:05 +00:00
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_WAIT
|
|
|
|
sStateTimer = sWaitTimeout
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function gamemode_update()
|
|
|
|
if not sGameModeInitialized then
|
|
|
|
gamemode_initialize()
|
|
|
|
end
|
|
|
|
|
|
|
|
if sSoccerBall == nil then
|
|
|
|
sSoccerBall = find_ball()
|
|
|
|
else
|
|
|
|
if gGlobalSyncTable.gameState == GAME_STATE_WAIT then
|
|
|
|
sSoccerBall.oFrozen = 1
|
|
|
|
else
|
|
|
|
sSoccerBall.oFrozen = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if gGlobalSyncTable.gameState == GAME_STATE_WAIT then
|
|
|
|
gamemode_wait()
|
|
|
|
elseif gGlobalSyncTable.gameState == GAME_STATE_ACTIVE then
|
|
|
|
gamemode_active()
|
|
|
|
elseif gGlobalSyncTable.gameState == GAME_STATE_SCORE then
|
|
|
|
gamemode_score()
|
|
|
|
elseif gGlobalSyncTable.gameState == GAME_STATE_OOB then
|
|
|
|
gamemode_oob()
|
|
|
|
elseif gGlobalSyncTable.gameState == GAME_STATE_OVER then
|
|
|
|
gamemode_over()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param m MarioState
|
2022-03-08 06:23:05 +00:00
|
|
|
function on_player_connected(m)
|
|
|
|
-- only run on server
|
|
|
|
if not network_is_server() then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- figure out team
|
|
|
|
local selectTeam = math.random(1, 2)
|
|
|
|
local playersTeam1 = 0
|
|
|
|
local playersTeam2 = 0
|
|
|
|
for i=0,(MAX_PLAYERS-1) do
|
|
|
|
local im = gMarioStates[i]
|
|
|
|
local sm = gPlayerSyncTable[i]
|
|
|
|
if active_player(im) and i ~= m.playerIndex then
|
|
|
|
if sm.team == 1 then
|
|
|
|
playersTeam1 = playersTeam1 + 1
|
|
|
|
elseif sm.team == 2 then
|
|
|
|
playersTeam2 = playersTeam2 + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if playersTeam1 < playersTeam2 then
|
|
|
|
selectTeam = 1
|
|
|
|
elseif playersTeam2 < playersTeam1 then
|
|
|
|
selectTeam = 2
|
|
|
|
end
|
|
|
|
|
|
|
|
-- set team
|
|
|
|
local s = gPlayerSyncTable[m.playerIndex]
|
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
|
|
|
s.team = selectTeam
|
|
|
|
end
|
|
|
|
|
2022-03-13 07:43:47 +00:00
|
|
|
--- @param m1 MarioState
|
|
|
|
--- @param m2 MarioState
|
|
|
|
function allow_pvp_attack(m1, m2)
|
|
|
|
local s1 = gPlayerSyncTable[m1.playerIndex]
|
|
|
|
local s2 = gPlayerSyncTable[m2.playerIndex]
|
|
|
|
if s1.team == s2.team then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
function hud_score_render()
|
|
|
|
djui_hud_set_font(FONT_HUD)
|
|
|
|
|
|
|
|
-- get width of screen and text
|
|
|
|
local screenWidth = djui_hud_get_screen_width()
|
|
|
|
|
|
|
|
local width = 32
|
|
|
|
local height = 16
|
|
|
|
local x = (screenWidth - width) / 2.0
|
|
|
|
local y = 5
|
|
|
|
local xOffset = 20
|
|
|
|
local textOffset = 8
|
|
|
|
|
|
|
|
if gPlayerSyncTable[0].team == 2 then
|
|
|
|
xOffset = xOffset * -1
|
|
|
|
end
|
|
|
|
|
|
|
|
-- render
|
2022-03-11 02:23:25 +00:00
|
|
|
djui_hud_set_color(255, 100, 100, 180);
|
2022-03-08 06:23:05 +00:00
|
|
|
djui_hud_render_rect(x - xOffset, y, width, height + 4);
|
|
|
|
|
|
|
|
djui_hud_set_color(100, 100, 255, 180);
|
|
|
|
djui_hud_render_rect(x + xOffset, y, width, height + 4);
|
|
|
|
|
|
|
|
djui_hud_set_color(255, 255, 255, 255);
|
2022-03-11 02:23:25 +00:00
|
|
|
djui_hud_print_text(tostring(gGlobalSyncTable.scoreRed), x - xOffset + textOffset, y + 2, 1);
|
|
|
|
djui_hud_print_text(tostring(gGlobalSyncTable.scoreBlue), x + xOffset + textOffset, y + 2, 1);
|
2022-03-08 06:23:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function on_hud_render()
|
|
|
|
-- render to N64 screen space, with the HUD font
|
|
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
|
|
|
|
|
|
hud_score_render()
|
|
|
|
|
|
|
|
if gGlobalSyncTable.displayText == ' ' then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
djui_hud_set_font(gGlobalSyncTable.displayFont)
|
|
|
|
|
|
|
|
-- set text
|
|
|
|
local text = gGlobalSyncTable.displayText
|
|
|
|
|
|
|
|
-- set scale
|
|
|
|
local scale = 1
|
|
|
|
|
|
|
|
local height = 1
|
|
|
|
if gGlobalSyncTable.displayFont == FONT_HUD then
|
|
|
|
height = 16 * scale
|
|
|
|
elseif gGlobalSyncTable.displayFont == FONT_NORMAL then
|
|
|
|
scale = 0.5
|
|
|
|
height = 32 * scale
|
|
|
|
end
|
|
|
|
|
|
|
|
-- get width of screen and text
|
|
|
|
local screenWidth = djui_hud_get_screen_width()
|
|
|
|
local screenHeight = djui_hud_get_screen_height()
|
|
|
|
local width = djui_hud_measure_text(text) * scale
|
|
|
|
|
|
|
|
local x = (screenWidth - width) / 2.0
|
|
|
|
local y = (screenHeight * 0.6 - height) / 2.0
|
|
|
|
|
|
|
|
-- render
|
|
|
|
djui_hud_set_color(0, 0, 0, 128);
|
|
|
|
djui_hud_render_rect(x - 6 * scale, y - 6 * scale, width + 12 * scale, height + 12 * scale);
|
|
|
|
|
|
|
|
local r = (gGlobalSyncTable.displayColor & 0xFF0000) >> (8 * 2)
|
|
|
|
local g = (gGlobalSyncTable.displayColor & 0x00FF00) >> (8 * 1)
|
|
|
|
local b = (gGlobalSyncTable.displayColor & 0x0000FF) >> (8 * 0)
|
|
|
|
djui_hud_set_color(r, g, b, 255);
|
|
|
|
djui_hud_print_text(text, x, y, scale);
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
function on_football_reset_command(msg)
|
|
|
|
if msg == 'ball' then
|
|
|
|
djui_chat_message_create('Resetting the ball.')
|
|
|
|
sSoccerBall = spawn_or_move_ball(sBallSpawnPos.x, sBallSpawnPos.y, sBallSpawnPos.z)
|
|
|
|
return true
|
|
|
|
elseif msg == 'game' then
|
|
|
|
djui_chat_message_create('Resetting the game.')
|
|
|
|
gamemode_shuffle()
|
2022-03-11 02:23:25 +00:00
|
|
|
gGlobalSyncTable.scoreRed = 0
|
|
|
|
gGlobalSyncTable.scoreBlue = 0
|
2022-03-08 06:23:05 +00:00
|
|
|
gGlobalSyncTable.displayText = ' '
|
|
|
|
gGlobalSyncTable.gameState = GAME_STATE_WAIT
|
|
|
|
sStateTimer = sWaitTimeout
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
------------------
|
|
|
|
-- update stuff --
|
|
|
|
------------------
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param m MarioState
|
2022-03-08 06:23:05 +00:00
|
|
|
function mario_update_local(m)
|
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
|
|
|
local s = gPlayerSyncTable[m.playerIndex]
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
if (m.controller.buttonPressed & D_JPAD) ~= 0 then
|
|
|
|
--print(m.pos.x, m.pos.y, m.pos.z)
|
|
|
|
--sSoccerBall = spawn_or_move_ball(m.pos.x, m.pos.y, m.pos.z)
|
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
-- force players into certain positions and angles
|
|
|
|
if gGlobalSyncTable.gameState == GAME_STATE_WAIT then
|
|
|
|
-- figure out team index
|
|
|
|
local teamIndex = 0
|
|
|
|
for i=1,(MAX_PLAYERS-1) do
|
|
|
|
local mi = gMarioStates[i]
|
|
|
|
local ni = gNetworkPlayers[i]
|
|
|
|
local si = gPlayerSyncTable[i]
|
|
|
|
if active_player(mi) and si.team == s.team then
|
|
|
|
if ni.globalIndex < np.globalIndex then
|
|
|
|
teamIndex = teamIndex + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-03-09 05:34:14 +00:00
|
|
|
-- center camera
|
|
|
|
m.controller.buttonDown = m.controller.buttonDown | L_TRIG
|
|
|
|
|
2022-03-08 06:23:05 +00:00
|
|
|
-- figure out spawn position
|
|
|
|
local teamTheta = (3.14 / -2)
|
|
|
|
local teamDistance = 1550
|
|
|
|
if (teamIndex % 2) == 0 then
|
|
|
|
teamTheta = teamTheta - (teamIndex % 4) * 0.1
|
|
|
|
teamDistance = teamDistance + math.floor(teamIndex / 4) * 500
|
|
|
|
else
|
|
|
|
teamTheta = teamTheta + (teamIndex % 4) * 0.1
|
|
|
|
teamDistance = teamDistance + math.floor(teamIndex / 4) * 500
|
|
|
|
end
|
|
|
|
|
|
|
|
-- set spawn position
|
|
|
|
local playerPos = { x = sBallSpawnPos.x, y = -511, z = sBallSpawnPos.z }
|
|
|
|
if s.team == 1 then
|
|
|
|
playerPos.x = playerPos.x - math.sin(teamTheta) * teamDistance
|
|
|
|
playerPos.z = playerPos.z - math.cos(teamTheta) * teamDistance
|
|
|
|
m.faceAngle.y = -0x4000
|
|
|
|
elseif s.team == 2 then
|
|
|
|
playerPos.x = playerPos.x + math.sin(teamTheta) * teamDistance
|
|
|
|
playerPos.z = playerPos.z + math.cos(teamTheta) * teamDistance
|
|
|
|
m.faceAngle.y = 0x4000
|
|
|
|
end
|
|
|
|
m.faceAngle.x = 0
|
|
|
|
m.faceAngle.z = 0
|
|
|
|
m.pos.x = playerPos.x
|
|
|
|
m.pos.y = playerPos.y
|
|
|
|
m.pos.z = playerPos.z
|
|
|
|
m.vel.x = 0
|
|
|
|
m.vel.y = 0
|
|
|
|
m.vel.z = 0
|
2022-03-09 05:34:14 +00:00
|
|
|
m.forwardVel = 0
|
|
|
|
m.slideVelX = 0
|
|
|
|
m.slideVelZ = 0
|
2022-03-08 06:23:05 +00:00
|
|
|
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 0)
|
|
|
|
|
|
|
|
-- fix vanilla camera
|
|
|
|
if m.area.camera.mode == CAMERA_MODE_WATER_SURFACE then
|
|
|
|
set_camera_mode(m.area.camera, CAMERA_MODE_FREE_ROAM, 1)
|
|
|
|
end
|
|
|
|
elseif m.action == ACT_READING_AUTOMATIC_DIALOG then
|
|
|
|
set_mario_action(m, ACT_IDLE, 0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
--- @param m MarioState
|
2022-03-08 06:23:05 +00:00
|
|
|
function mario_update(m)
|
|
|
|
if m.playerIndex == 0 then
|
|
|
|
mario_update_local(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- update pos/angle
|
|
|
|
if m.action == ACT_READING_AUTOMATIC_DIALOG then
|
|
|
|
vec3f_copy(m.marioObj.header.gfx.pos, m.pos)
|
|
|
|
vec3s_set(m.marioObj.header.gfx.angle, -m.faceAngle.x, m.faceAngle.y, m.faceAngle.z)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- set metal state and health
|
|
|
|
local s = gPlayerSyncTable[m.playerIndex]
|
2022-03-11 02:23:25 +00:00
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
2022-03-08 06:23:05 +00:00
|
|
|
if s.team == 2 then
|
Arbitrary shirt, pants, glove colors + settings menu (#145)
* Support for more granular player colors
You can now configure RGB values for shirt, pants, gloves, and shoes.
Due to some limitations, configuring shoes does nothing at the moment.
* Remove paletteIndex and friends
Restructured and filled in some remaining code to account for that.
* Add Edit Palette panel to Player panel
* Change PlayerPalette contents to an enum-indexed array, remove shoes
This gets rid of all the hokey code doing switch cases on the
different parts.
* Fix goof with player model selection box
Should actually have affect now even if a custom palette is being used.
* Fix gap in player color display list commands
The extra space was leftover from when I was trying to get shoes
working. Forgot to clean it up.
* Standardize PlayerParts enum, including for lua constants autogen
* djui_panel_player.c: Properly hook sending palette changes on unpause
Editing the palette and then unpausing should send out the packet to
everyone with the new palette changes (and update the palette preset
selection box), but since we weren't hooking that situation before, it
would stay changed only for you. You would have had to press the Back
button for it to work right.
* Allow Lua mods to continue using `paletteIndex`, `overridePaletteIndex`
This lets mod code like this still work unchanged:
if s.team == 2 then
np.overridePaletteIndex = 7
elseif s.team == 1 then
np.overridePaletteIndex = 15
else
np.overridePaletteIndex = np.paletteIndex
end
It's essentially faked, and would work strangely if the value of either
variable was inspected more closely directly. This should at least
handle the typical use case, though.
Every frame, `overridePaletteIndex` is checked to see if it was modified
from its previous value. If so, `overridePalette` is set to the preset
corresponding to the index. `paletteIndex` contains a special value that
when used to assign to `overridePaletteIndex`, it copies `palette` into
`overridePalette` to restore the real colors, which of course may not
follow the presets at all.
* characters.h: Pack `PlayerPalette` to eliminate size differences between computers
* mario_misc.c: Remove remaining "TODO GAG"
2022-08-07 22:13:19 +00:00
|
|
|
np.overridePalette = gPalettePresets[7]
|
2022-03-11 02:23:25 +00:00
|
|
|
m.marioBodyState.modelState = 0
|
2022-03-08 06:23:05 +00:00
|
|
|
elseif s.team == 1 then
|
Arbitrary shirt, pants, glove colors + settings menu (#145)
* Support for more granular player colors
You can now configure RGB values for shirt, pants, gloves, and shoes.
Due to some limitations, configuring shoes does nothing at the moment.
* Remove paletteIndex and friends
Restructured and filled in some remaining code to account for that.
* Add Edit Palette panel to Player panel
* Change PlayerPalette contents to an enum-indexed array, remove shoes
This gets rid of all the hokey code doing switch cases on the
different parts.
* Fix goof with player model selection box
Should actually have affect now even if a custom palette is being used.
* Fix gap in player color display list commands
The extra space was leftover from when I was trying to get shoes
working. Forgot to clean it up.
* Standardize PlayerParts enum, including for lua constants autogen
* djui_panel_player.c: Properly hook sending palette changes on unpause
Editing the palette and then unpausing should send out the packet to
everyone with the new palette changes (and update the palette preset
selection box), but since we weren't hooking that situation before, it
would stay changed only for you. You would have had to press the Back
button for it to work right.
* Allow Lua mods to continue using `paletteIndex`, `overridePaletteIndex`
This lets mod code like this still work unchanged:
if s.team == 2 then
np.overridePaletteIndex = 7
elseif s.team == 1 then
np.overridePaletteIndex = 15
else
np.overridePaletteIndex = np.paletteIndex
end
It's essentially faked, and would work strangely if the value of either
variable was inspected more closely directly. This should at least
handle the typical use case, though.
Every frame, `overridePaletteIndex` is checked to see if it was modified
from its previous value. If so, `overridePalette` is set to the preset
corresponding to the index. `paletteIndex` contains a special value that
when used to assign to `overridePaletteIndex`, it copies `palette` into
`overridePalette` to restore the real colors, which of course may not
follow the presets at all.
* characters.h: Pack `PlayerPalette` to eliminate size differences between computers
* mario_misc.c: Remove remaining "TODO GAG"
2022-08-07 22:13:19 +00:00
|
|
|
np.overridePalette = gPalettePresets[15]
|
2022-03-08 06:23:05 +00:00
|
|
|
m.marioBodyState.modelState = 0
|
|
|
|
else
|
Arbitrary shirt, pants, glove colors + settings menu (#145)
* Support for more granular player colors
You can now configure RGB values for shirt, pants, gloves, and shoes.
Due to some limitations, configuring shoes does nothing at the moment.
* Remove paletteIndex and friends
Restructured and filled in some remaining code to account for that.
* Add Edit Palette panel to Player panel
* Change PlayerPalette contents to an enum-indexed array, remove shoes
This gets rid of all the hokey code doing switch cases on the
different parts.
* Fix goof with player model selection box
Should actually have affect now even if a custom palette is being used.
* Fix gap in player color display list commands
The extra space was leftover from when I was trying to get shoes
working. Forgot to clean it up.
* Standardize PlayerParts enum, including for lua constants autogen
* djui_panel_player.c: Properly hook sending palette changes on unpause
Editing the palette and then unpausing should send out the packet to
everyone with the new palette changes (and update the palette preset
selection box), but since we weren't hooking that situation before, it
would stay changed only for you. You would have had to press the Back
button for it to work right.
* Allow Lua mods to continue using `paletteIndex`, `overridePaletteIndex`
This lets mod code like this still work unchanged:
if s.team == 2 then
np.overridePaletteIndex = 7
elseif s.team == 1 then
np.overridePaletteIndex = 15
else
np.overridePaletteIndex = np.paletteIndex
end
It's essentially faked, and would work strangely if the value of either
variable was inspected more closely directly. This should at least
handle the typical use case, though.
Every frame, `overridePaletteIndex` is checked to see if it was modified
from its previous value. If so, `overridePalette` is set to the preset
corresponding to the index. `paletteIndex` contains a special value that
when used to assign to `overridePaletteIndex`, it copies `palette` into
`overridePalette` to restore the real colors, which of course may not
follow the presets at all.
* characters.h: Pack `PlayerPalette` to eliminate size differences between computers
* mario_misc.c: Remove remaining "TODO GAG"
2022-08-07 22:13:19 +00:00
|
|
|
np.overridePalette = np.palette
|
2022-03-08 06:23:05 +00:00
|
|
|
m.marioBodyState.modelState = MODEL_STATE_NOISE_ALPHA
|
|
|
|
end
|
|
|
|
m.health = 0x880
|
|
|
|
|
|
|
|
-- update description
|
|
|
|
if s.team == 1 then
|
2022-03-11 02:23:25 +00:00
|
|
|
network_player_set_description(np, "red", 255, 64, 64, 255)
|
2022-03-08 06:23:05 +00:00
|
|
|
elseif s.team == 2 then
|
2022-03-11 02:23:25 +00:00
|
|
|
network_player_set_description(np, "blue", 64, 64, 255, 255)
|
2022-03-08 06:23:05 +00:00
|
|
|
else
|
|
|
|
network_player_set_description(np, "unknown", 64, 64, 64, 255)
|
|
|
|
end
|
|
|
|
|
|
|
|
if gPlayerSyncTable[m.playerIndex].sparkle then
|
|
|
|
m.particleFlags = m.particleFlags | PARTICLE_SPARKLES
|
|
|
|
else
|
|
|
|
m.particleFlags = (m.particleFlags & (~PARTICLE_SPARKLES))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function update()
|
|
|
|
local m = gMarioStates[0]
|
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
|
|
|
|
|
|
|
if np.currAreaSyncValid then
|
|
|
|
gamemode_update()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-----------
|
|
|
|
-- hooks --
|
|
|
|
-----------
|
|
|
|
|
|
|
|
hook_event(HOOK_MARIO_UPDATE, mario_update)
|
|
|
|
hook_event(HOOK_UPDATE, update)
|
|
|
|
hook_event(HOOK_ON_HUD_RENDER, on_hud_render)
|
|
|
|
hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected)
|
2022-03-13 07:43:47 +00:00
|
|
|
hook_event(HOOK_ALLOW_PVP_ATTACK, allow_pvp_attack)
|
2022-05-22 06:07:29 +00:00
|
|
|
if network_is_server() then
|
|
|
|
hook_chat_command('football-reset', "[game|ball] resets the game or ball", on_football_reset_command)
|
|
|
|
end
|
2022-03-08 06:23:05 +00:00
|
|
|
|
|
|
|
for i=0,(MAX_PLAYERS-1) do
|
|
|
|
local s = gPlayerSyncTable[i]
|
|
|
|
s.team = 0
|
|
|
|
s.sparkle = false
|
|
|
|
end
|