230 lines
6.6 KiB
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")
|