sm64coopdx/mods/arena/arena-player.lua

500 lines
15 KiB
Lua
Raw Normal View History

2022-05-22 06:05:41 +02:00
------------
-- tables --
------------
gMarioStateExtras = {}
for i = 0, (MAX_PLAYERS - 1) do
local np = gNetworkPlayers[i]
gMarioStateExtras[i] = {}
local e = gMarioStateExtras[i]
e.rotAngle = 0
e.rotFrames = 0
e.lastDamagedByGlobal = np.globalIndex
e.attackCooldown = 0
e.prevHurtCounter = 0
e.levelTimer = 0
e.levelTimerLevel = 0
local s = gPlayerSyncTable[i]
s.item = ITEM_NONE
s.ammo = 0
s.kills = 0
s.deaths = 0
s.score = 0
s.team = 0
s.charging = 0
s.metal = false
s.rank = 0
end
------------
-- hammer --
------------
function mario_hammer_is_attack(action)
if action == ACT_PUNCHING then return true end
if action == ACT_MOVE_PUNCHING then return true end
if action == ACT_JUMP_KICK then return true end
if action == ACT_DIVE then return true end
if action == ACT_GROUND_POUND then return true end
return false
end
function mario_hammer_position(m)
local held = gItemHeld[m.playerIndex]
if held == nil then
return { x = m.pos.x, y = m.pos.y, z = m.pos.z }
end
local origin = { x = held.oPosX, y = held.oPosY, z = held.oPosZ }
return set_dist_and_angle(origin, 100, 0x4000 + -held.oFaceAnglePitch, held.oFaceAngleYaw)
end
function mario_hammer_pound(m)
local v = {
x = m.pos.x + sins(m.faceAngle.y) * 200,
y = m.pos.y,
z = m.pos.z + coss(m.faceAngle.y) * 200,
}
spawn_horizontal_stars(v.x, v.y, v.z)
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_HEAVY_LANDING)
cur_obj_shake_screen(SHAKE_POS_MEDIUM)
end
function mario_hammer_on_set_action(m)
if m.action == ACT_PUNCHING or m.action == ACT_MOVE_PUNCHING or m.action == ACT_JUMP_KICK then
play_sound(SOUND_ACTION_TWIRL, m.marioObj.header.gfx.cameraToObject)
elseif m.action == ACT_DIVE_SLIDE or m.action == ACT_GROUND_POUND_LAND then
mario_hammer_pound(m)
end
end
function mario_hammer_update(m)
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
-- override dive animation
if m.action == ACT_DIVE then
set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING)
e.rotFrames = e.rotFrames + 1
if (e.rotFrames) % 7 == 0 then
play_sound(SOUND_ACTION_TWIRL, m.marioObj.header.gfx.cameraToObject)
end
e.rotAngle = e.rotAngle + (0x80 * 60)
if e.rotAngle > 0x10000 then
e.rotAngle = e.rotAngle - 0x10000
end
set_anim_to_frame(m, 10 * e.rotAngle / 0x10000)
elseif m.action == ACT_PUNCHING or m.action == ACT_MOVE_PUNCHING then
local animFrame = m.marioObj.header.gfx.animInfo.animFrame
if animFrame == -1 and m.actionArg > 1 then
mario_hammer_pound(m)
end
if m.actionArg > 2 then m.actionArg = 0 end
end
end
function mario_local_hammer_check(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local savedKb = m.knockbackTimer
m.knockbackTimer = 0
-- check for hammer attacks
for i = 1, (MAX_PLAYERS - 1) do
local m2 = gMarioStates[i]
local np2 = gNetworkPlayers[i]
local s2 = gPlayerSyncTable[i]
if s2.item == ITEM_HAMMER and mario_hammer_is_attack(m2.action) and passes_pvp_interaction_checks(m2, m) ~= 0 and global_index_hurts_mario_state(np2.globalIndex, m) then
local pos = mario_hammer_position(m2)
local dist = vec3f_dist(pos, m.pos)
if dist <= 165 then
local yOffset = 100
if m2.action == ACT_JUMP_KICK then
yOffset = yOffset + 100
end
local vel = {
x = m.pos.x - m2.pos.x,
y = (m.pos.y + yOffset) - m2.pos.y,
z = m.pos.z - m2.pos.z,
}
vec3f_normalize(vel)
vec3f_mul(vel, 75 + 70 * (1 - mario_health_float(m)))
set_mario_action(m, ACT_BACKWARD_AIR_KB, 0)
m.invincTimer = 30
m.knockbackTimer = 10
m.vel.x = vel.x
m.vel.y = vel.y
m.vel.z = vel.z
m.faceAngle.y = atan2s(vel.z, vel.x) + 0x8000
s2.ammo = s2.ammo - 1
send_arena_hammer_hit(np.globalIndex, np2.globalIndex)
e.lastDamagedByGlobal = np2.globalIndex
if m2.action == ACT_PUNCHING or m2.action == ACT_MOVE_PUNCHING or m2.action == ACT_GROUND_POUND then
m.hurtCounter = 12
else
m.hurtCounter = 8
end
end
end
end
m.knockbackTimer = savedKb
end
-----------------
-- fire flower --
-----------------
function mario_fire_flower_use(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
spawn_sync_object(id_bhvArenaFlame, E_MODEL_RED_FLAME, m.pos.x, m.pos.y, m.pos.z,
function (obj)
obj.oArenaFlameGlobalOwner = np.globalIndex
obj.oVelY = m.vel.y + 25
obj.oMoveAngleYaw = m.faceAngle.y
obj.oForwardVel = m.forwardVel + 70
end)
if (m.action & ACT_FLAG_INVULNERABLE) ~= 0 or (m.action & ACT_FLAG_INTANGIBLE) ~= 0 then
-- nothing
elseif (m.action & ACT_FLAG_SWIMMING) ~= 0 then
set_mario_action(m, ACT_WATER_PUNCH, 0)
elseif (m.action & ACT_FLAG_MOVING) ~= 0 then
set_mario_action(m, ACT_MOVE_PUNCHING, 0)
elseif (m.action & ACT_FLAG_AIR) ~= 0 then
set_mario_action(m, ACT_DIVE, 0)
elseif (m.action & ACT_FLAG_STATIONARY) ~= 0 then
set_mario_action(m, ACT_PUNCHING, 0)
end
e.attackCooldown = 15
s.ammo = s.ammo - 1
end
------------
-- bobomb --
------------
function mario_bobomb_use(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
spawn_sync_object(id_bhvArenaBobomb, E_MODEL_BLACK_BOBOMB, m.pos.x, m.pos.y + 50, m.pos.z,
function (obj)
obj.oArenaBobombGlobalOwner = np.globalIndex
obj.oMoveAngleYaw = m.faceAngle.y
obj.oForwardVel = m.forwardVel + 50
end)
if (m.action & ACT_FLAG_INVULNERABLE) ~= 0 or (m.action & ACT_FLAG_INTANGIBLE) ~= 0 then
-- nothing
elseif (m.action & ACT_FLAG_SWIMMING) ~= 0 then
set_mario_action(m, ACT_WATER_PUNCH, 0)
elseif (m.action & ACT_FLAG_MOVING) ~= 0 then
set_mario_action(m, ACT_MOVE_PUNCHING, 0)
elseif (m.action & ACT_FLAG_AIR) ~= 0 then
set_mario_action(m, ACT_DIVE, 0)
elseif (m.action & ACT_FLAG_STATIONARY) ~= 0 then
set_mario_action(m, ACT_PUNCHING, 0)
end
e.attackCooldown = 15
s.ammo = s.ammo - 1
end
----------------
-- cannon box --
----------------
function mario_cannon_box_update(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
if m.playerIndex == 0 and (m.controller.buttonPressed & Y_BUTTON) ~= 0 then
s.charging = get_network_area_timer()
end
if (m.controller.buttonDown & Y_BUTTON) ~= 0 and s.charging > 0 then
local held = gItemHeld[m.playerIndex]
if held ~= nil then
for i = 0, 2 do
spawn_non_sync_object(id_bhvArenaSparkle, E_MODEL_SPARKLES_ANIMATION,
held.oPosX, held.oPosY, held.oPosZ,
function (obj)
obj.oArenaSparkleOwner = m.playerIndex
end)
end
end
elseif m.playerIndex == 0 and s.charging > 0 then
local cannonBallSize = clamp((get_network_area_timer() - s.charging) / (30 * 5) + 0.1, 0, 1)
s.charging = 0
spawn_sync_object(id_bhvArenaCannonBall, E_MODEL_CANNON_BALL, m.pos.x, m.pos.y + 150, m.pos.z,
function (obj)
obj.oArenaCannonBallGlobalOwner = np.globalIndex
obj.oArenaCannonBallSize = cannonBallSize
obj.oMoveAngleYaw = m.faceAngle.y
obj.oForwardVel = m.forwardVel + 150
end)
s.ammo = s.ammo - 1
end
end
-----------
-- hooks --
-----------
function allow_pvp_attack(attacker, victim)
local npAttacker = gNetworkPlayers[attacker.playerIndex]
local sAttacker = gPlayerSyncTable[attacker.playerIndex]
-- hammer attacks are custom
if sAttacker.item == ITEM_HAMMER and mario_hammer_is_attack(attacker.action) then
return false
end
-- check teams
return global_index_hurts_mario_state(npAttacker.globalIndex, victim)
end
function on_pvp_attack(attacker, victim)
if victim.playerIndex == 0 then
local e = gMarioStateExtras[victim.playerIndex]
local npAttacker = gNetworkPlayers[attacker.playerIndex]
e.lastDamagedByGlobal = npAttacker.globalIndex
end
end
function on_interact(interactor, interactee, interactType, interactValue)
if interactor.playerIndex ~= 0 then return end
local bhvId = get_id_from_behavior(interactee.behavior)
if bhvId ~= id_bhvArenaFlame and bhvId ~= id_bhvArenaChildFlame then return end
local e = gMarioStateExtras[interactor.playerIndex]
e.lastDamagedByGlobal = interactee.oArenaFlameGlobalOwner
end
function on_set_mario_action(m)
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
if m.action == ACT_DIVE then
e.rotAngle = 0
e.rotFrames = 0
end
if s.item == ITEM_HAMMER then
mario_hammer_on_set_action(m)
end
end
function mario_local_update(m)
local np = gNetworkPlayers[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
override_camera()
-- decrease cooldown
if e.attackCooldown > 0 then
e.attackCooldown = e.attackCooldown - 1
end
-- use the hammer
mario_local_hammer_check(m)
-- use the fire flower
if e.attackCooldown <= 0 and s.item == ITEM_FIRE_FLOWER and (m.controller.buttonPressed & Y_BUTTON) ~= 0 then
mario_fire_flower_use(m)
end
-- use the bobomb
if e.attackCooldown <= 0 and s.item == ITEM_BOBOMB and (m.controller.buttonPressed & Y_BUTTON) ~= 0 then
mario_bobomb_use(m)
end
-- set metal
s.metal = (m.capTimer > 0)
-- increase damage when holding flag
if is_holding_flag(m) then
if m.hurtCounter > e.prevHurtCounter then
m.hurtCounter = m.hurtCounter * 2
end
end
-- reduce damage when metal
if s.metal then
if m.hurtCounter > e.prevHurtCounter then
m.hurtCounter = m.hurtCounter / 2
end
end
-- discard current item
if s.item ~= ITEM_NONE and (s.ammo <= 0 or (m.controller.buttonPressed & L_TRIG) ~= 0) then
s.item = ITEM_NONE
if gItemHeld[m.playerIndex] ~= nil then
spawn_triangles(gItemHeld[m.playerIndex])
end
play_sound(SOUND_GENERAL_BREAK_BOX, m.marioObj.header.gfx.cameraToObject)
end
e.prevHurtCounter = m.hurtCounter
end
function mario_update(m)
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
local np = gNetworkPlayers[m.playerIndex]
-- clear invincibilities
m.invincTimer = 0
if m.knockbackTimer > 5 then
m.knockbackTimer = 5
end
-- update the local player
if m.playerIndex == 0 then
mario_local_update(m)
end
-- update palette
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-08 00:13:19 +02:00
np.overridePalette = gPalettePresets[7]
2022-05-22 06:05:41 +02: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-08 00:13:19 +02:00
np.overridePalette = gPalettePresets[15]
2022-05-22 06:05:41 +02:00
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-08 00:13:19 +02:00
np.overridePalette = np.palette
2022-05-22 06:05:41 +02:00
end
-- set metal
if s.metal then
m.marioBodyState.modelState = MODEL_STATE_METAL
end
-- update player items
if s.item == ITEM_HAMMER then
mario_hammer_update(m)
elseif s.item == ITEM_CANNON_BOX then
mario_cannon_box_update(m)
end
-- update level timer
if e.levelTimerLevel ~= np.currLevelNum then
e.levelTimer = 0
e.levelTimerLevel = np.currLevelNum
else
e.levelTimer = e.levelTimer + 1
end
end
function player_reset_sync_table(m)
local s = gPlayerSyncTable[m.playerIndex]
s.item = ITEM_NONE
s.ammo = 0
s.kills = 0
s.deaths = 0
s.score = 0
s.charging = 0
s.metal = false
s.rank = 0
s.team = pick_team_on_join(m)
end
function player_respawn(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
-- reset most variables
init_single_mario(m)
-- spawn location/angle
spawn = find_spawn_point()
if spawn ~= nil then
m.pos.x = spawn.pos.x
m.pos.y = spawn.pos.y
m.pos.z = spawn.pos.z
m.faceAngle.y = spawn.yaw
else
m.pos.x = 0
m.pos.y = 0
m.pos.z = 0
end
-- reset the rest of the variables
m.capTimer = 0
m.health = 0x880
soft_reset_camera(m.area.camera)
s.ammo = 0
s.item = ITEM_NONE
e.lastDamagedByGlobal = np.globalIndex
stop_cap_music()
end
function on_death(m)
if m.playerIndex ~= 0 then return end
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
-- inform of death
send_arena_death(np.globalIndex, e.lastDamagedByGlobal)
-- respawn
player_respawn(m)
return false
end
function on_player_connected(m)
local np = gNetworkPlayers[m.playerIndex]
local e = gMarioStateExtras[m.playerIndex]
local s = gPlayerSyncTable[m.playerIndex]
if network_is_server() then
player_reset_sync_table(m)
end
if m.playerIndex == 0 then
e.lastDamagedByGlobal = np.globalIndex
end
end
function on_player_disconnected(m)
local s = gPlayerSyncTable[m.playerIndex]
if network_is_server() then
player_reset_sync_table(m)
end
end
function before_phys_step(m)
local hScale = 1.0
if is_holding_flag(m) then
hScale = 0.9
end
m.vel.x = m.vel.x * hScale
m.vel.z = m.vel.z * hScale
end
hook_event(HOOK_ALLOW_PVP_ATTACK, allow_pvp_attack)
hook_event(HOOK_ON_PVP_ATTACK, on_pvp_attack)
hook_event(HOOK_ON_INTERACT, on_interact)
hook_event(HOOK_ON_SET_MARIO_ACTION, on_set_mario_action)
hook_event(HOOK_MARIO_UPDATE, mario_update)
hook_event(HOOK_ON_DEATH, on_death)
hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected)
hook_event(HOOK_ON_PLAYER_DISCONNECTED, on_player_disconnected)
hook_event(HOOK_BEFORE_PHYS_STEP, before_phys_step)