sm64coopdx/mods/shell-rush/weapon-shell.lua

230 lines
6.6 KiB
Lua

local shellTimeout = 30 * 30 --- 30 seconds
define_custom_obj_fields({
oWeaponShellType = 'u32',
oWeaponShellGlobalOwner = 'u32',
oWeaponShellDeactivate = 'u32',
})
function bhv_weapon_shell_init(obj)
obj.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
obj.oOpacity = 255
obj.oVelY = 0
obj.oTimer = 0
if UNST22 then
obj.oFlyGuyUnusedJitter = 0
else
obj.oWeaponShellDeactivate = 0
end
obj_scale(obj, 0.9)
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_DAMAGE
hitbox.downOffset = 0
hitbox.damageOrCoinValue = 4
hitbox.health = 1
hitbox.numLootCoins = 1
hitbox.radius = 100
hitbox.height = 70
hitbox.hurtboxRadius = 100
hitbox.hurtboxHeight = 70
obj_set_hitbox(obj, hitbox)
cur_obj_play_sound_2(SOUND_GENERAL_BIG_POUND)
if UNST22 then
network_init_object(obj, false, { 'oFlyGuyUnusedJitter' })
else
network_init_object(obj, false, { 'oWeaponShellDeactivate' })
end
end
function bhv_weapon_shell_destroy(obj)
obj.activeFlags = ACTIVE_FLAG_DEACTIVATED
spawn_triangles(obj)
cur_obj_play_sound_2(SOUND_GENERAL_BREAK_BOX)
if UNST22 then
if obj.oFlyGuyUnusedJitter == 0 then
obj.oFlyGuyUnusedJitter = 1
network_send_object(obj, true)
end
obj.oFlyGuyUnusedJitter = 1
else
if obj.oWeaponShellDeactivate == 0 then
obj.oWeaponShellDeactivate = 1
network_send_object(obj, true)
end
obj.oWeaponShellDeactivate = 1
end
end
function bhv_weapon_shell_move(obj)
local hit = false
local stepHeight = 100
local savedX = obj.oPosX
local savedY = obj.oPosY
local savedZ = obj.oPosZ
-- figure out direction
local v = {
x = sins(obj.oMoveAngleYaw) * obj.oForwardVel,
y = 0,
z = coss(obj.oMoveAngleYaw) * obj.oForwardVel,
}
-- cast ray
local info = collision_find_surface_on_ray(
obj.oPosX, obj.oPosY + stepHeight, obj.oPosZ,
v.x, v.y, v.z)
-- move the shell
obj.oPosX = info.hitPos.x
obj.oPosY = info.hitPos.y
obj.oPosZ = info.hitPos.z
-- figure out how far from floor
local floorHeight = find_floor_height(obj.oPosX, obj.oPosY, obj.oPosZ)
if floorHeight <= -10000.0 then
-- we're OOB
obj.oPosX = savedX
obj.oPosY = savedY
obj.oPosZ = savedZ
obj.oMoveAngleYaw = obj.oMoveAngleYaw + 0x4000
obj.oFaceAngleYaw = obj.oMoveAngleYaw
return true
elseif math.abs(floorHeight - obj.oPosY) > stepHeight * 1.25 then
-- we're in the air
obj.oPosY = obj.oPosY - stepHeight
obj.oVelY = obj.oVelY - 5
obj.oPosY = obj.oPosY + obj.oVelY
if obj.oPosY < floorHeight then
obj.oPosY = floorHeight
end
else
-- we're on the ground
obj.oPosY = floorHeight
obj.oVelY = 0
end
-- figure out if we hit wall
if info.surface ~= nil and math.abs(info.surface.normal.y) < 0.2 then
-- projection
local parallel = vec3f_project(v, info.surface.normal)
local perpendicular = { x = v.x - parallel.x, y = v.y - parallel.y, z = v.z - parallel.z }
-- reflect velocity along normal
local reflect = {
x = perpendicular.x - parallel.x,
y = perpendicular.y - parallel.y,
z = perpendicular.z - parallel.z
}
obj.oPosX = savedX
obj.oPosY = savedY
obj.oPosZ = savedZ
obj.oMoveAngleYaw = atan2s(reflect.z, reflect.x)
obj.oFaceAngleYaw = obj.oMoveAngleYaw
hit = true
spawn_mist(obj)
cur_obj_play_sound_2(SOUND_GENERAL_BOX_LANDING)
end
-- orient to floor
info = collision_find_surface_on_ray(
obj.oPosX, obj.oPosY + stepHeight, obj.oPosZ,
0, stepHeight * -1.5, 0)
if info.surface ~= nil then
obj_orient_graph(obj, info.surface.normal.x, info.surface.normal.y, info.surface.normal.z)
end
-- attach to water
local waterLevel = find_water_level(obj.oPosX, obj.oPosZ)
if obj.oPosY < waterLevel then obj.oPosY = waterLevel end
return hit
end
function bhv_weapon_shell_red_loop(obj, hit)
if hit then
bhv_weapon_shell_destroy(obj)
return
end
-- find target
local target = nil
local targetDist = 1000
local v = {
x = sins(obj.oMoveAngleYaw) * obj.oForwardVel,
y = 0,
z = coss(obj.oMoveAngleYaw) * obj.oForwardVel,
}
for i = 0, (MAX_PLAYERS - 1) do
local m = gMarioStates[i]
local np = gNetworkPlayers[i]
local isntGlobalOwner = (np.globalIndex ~= obj.oWeaponShellGlobalOwner)
if UNST22 then isntGlobalOwner = (np.globalIndex ~= obj.oFlyGuyOscTimer) end
if active_player(m) and isntGlobalOwner then
local dist = dist_between_objects(m.marioObj, obj)
local diff = { x = m.marioObj.oPosX - obj.oPosX, y = 0, z = m.marioObj.oPosZ - obj.oPosZ }
local angleBetween = vec3f_angle_between(diff, v)
if dist < targetDist and angleBetween < 100 then
target = m
targetDist = dist
end
end
end
-- no target found :(
if target == nil then
return
end
-- turn toward target
local turnSpeed = 0x300
local targetYaw = atan2s(target.pos.z - obj.oPosZ, target.pos.x - obj.oPosX)
obj.oMoveAngleYaw = targetYaw - approach_s32(convert_s16(targetYaw - obj.oMoveAngleYaw), 0, turnSpeed, turnSpeed)
obj.oFaceAngleYaw = obj.oMoveAngleYaw
end
function bhv_weapon_shell_loop(obj)
local hit = bhv_weapon_shell_move(obj)
if UNST22 then
if obj.oFlyGuyIdleTimer == 1 then
bhv_weapon_shell_red_loop(obj, hit)
end
if obj.oFlyGuyUnusedJitter ~= 0 then
bhv_weapon_shell_destroy(obj)
return
end
else
if obj.oWeaponShellType == 1 then
bhv_weapon_shell_red_loop(obj, hit)
end
if obj.oWeaponShellDeactivate ~= 0 then
bhv_weapon_shell_destroy(obj)
return
end
end
-- prevent interactions for the first 5 frames
if obj.oTimer < 5 then
obj.oInteractStatus = 0
end
if cur_obj_check_interacted() ~= 0 then
bhv_weapon_shell_destroy(obj)
return
end
if obj.oTimer > shellTimeout then
bhv_weapon_shell_destroy(obj)
return
end
end
id_bhvWeaponShell = hook_behavior(nil, OBJ_LIST_PUSHABLE, true, bhv_weapon_shell_init, bhv_weapon_shell_loop)
E_MODEL_RED_SHELL = smlua_model_util_get_id("red_shell_geo")