-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbombs.lua
324 lines (271 loc) · 9.56 KB
/
bombs.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
-- lots of this code is taken from arena
-- I changed bomb physics a lot and
-- revamped a lot of the code.
-- You will see a lot of code snippets from
-- arena however.
local bombCooldown = 0
-- custom obj fields
define_custom_obj_fields({
oBombOwner = 'u32',
})
---@param i integer
local function can_hold_bomb(i)
local s = gPlayerSyncTable[i]
if gGlobalSyncTable.roundState ~= ROUND_WAIT_PLAYERS
and gGlobalSyncTable.roundState ~= ROUND_ACTIVE
and gGlobalSyncTable.roundState ~= ROUND_HOT_POTATO_INTERMISSION then return false end
-- check juggernaut
if s.state == RUNNER and gGlobalSyncTable.gamemode == JUGGERNAUT then return true end
-- check modifier
if gGlobalSyncTable.modifier ~= MODIFIER_BOMBS then return false end
if gGlobalSyncTable.roundState == ROUND_WAIT_PLAYERS then return true end
-- check if we are a tagger
if s.state ~= TAGGER then return false end
-- check round state
if gGlobalSyncTable.roundState ~= ROUND_ACTIVE then return false end
return true
end
-- bomb explosion
---@param o Object
local function bomb_explosion_init(o)
-- set basic flags
o.oFlags = OBJ_FLAG_COMPUTE_DIST_TO_MARIO | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
-- set hitbox stuff
o.oInteractType = INTERACT_DAMAGE
o.oIntangibleTimer = 0
o.hitboxRadius = 400
o.hitboxHeight = 400
o.hitboxDownOffset = 400
o.oAnimState = -1
-- make object a billboard
obj_set_billboard(o)
-- play explosion sound
cur_obj_play_sound_2(SOUND_GENERAL2_BOBOMB_EXPLOSION)
end
---@param o Object
local function bomb_explosion_loop(o)
-- increase anim state
o.oAnimState = o.oAnimState + 1
if o.oTimer == 9 then
--- if we are in water, spawn a explosion buble, otherwise do normal death smoke
if (find_water_level(o.oPosX, o.oPosZ) > o.oPosY) then
for _ = 0, 40 do
spawn_non_sync_object(id_bhvBobombExplosionBubble, E_MODEL_WHITE_PARTICLE_SMALL, o.oPosX, o.oPosY, o.oPosZ, nil)
end
else
spawn_non_sync_object(id_bhvBobombBullyDeathSmoke, E_MODEL_SMOKE, o.oPosX, o.oPosY, o.oPosZ, nil)
end
o.activeFlags = ACTIVE_FLAG_DEACTIVATED
end
-- get nearest mario
local m = nearest_mario_state_to_object(o)
local localBombOwner = network_local_index_from_global(o.oBombOwner)
-- if we interacted...
if o.oInteractStatus & INT_STATUS_INTERACTED ~= 0 then
if m.playerIndex == localBombOwner then goto interactset end
if m.invincTimer > 0 then goto interactset end
-- run handle_player_pvp for designated gamemode
handle_projectile_pvp(localBombOwner, m.playerIndex)
::interactset::
-- set interact status to 0 to not run this again
o.oInteractStatus = 0
end
-- reduce opacity over time
o.oOpacity = o.oOpacity - 14
-- scale object over time
cur_obj_scale((o.oTimer / 9) + 1)
end
---@param o Object
local function bomb_explode(o)
-- spawn explosion
spawn_sync_object(id_bhvBombExplosion, E_MODEL_EXPLOSION, o.oPosX, o.oPosY, o.oPosZ, function (obj)
obj.oBombOwner = o.oBombOwner
end)
-- delete bomb
obj_mark_for_deletion(o)
end
---@param o Object
---@param rad integer
local function bobomb_intersects_player(o, rad)
-- get owner network player
local ownerNp = network_player_from_global_index(o.oBombOwner)
-- return value
local ret = false
for i = 0, MAX_PLAYERS - 1 do
local np = gNetworkPlayers[i]
if not np.connected
or not np.currAreaSyncValid
or ownerNp == np then goto continue end
local m = gMarioStates[i]
-- get lag compensation mario state
if m.playerIndex == 0 and ownerNp.localIndex ~= 0 then
m = lag_compensation_get_local_state(ownerNp)
end
-- convert positions to vec3f
local pos = { x = o.oPosX, y = o.oPosY, z = o.oPosZ }
local mPos = { x = m.pos.x, y = m.pos.y + 50, z = m.pos.z }
-- get return value
ret = vec3f_dist(pos, mPos) < rad
if ret then
-- goto return ending
goto ending
end
::continue::
end
::ending::
-- return that value
return ret
end
---@param o Object
local function bomb_init(o)
-- set basic flags
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW
-- set hitbox and gravity vars
o.hitboxRadius = 50
o.hitboxHeight = 50
o.oGravity = 5
o.oVelY = 40
-- set animation to bomb walking animation
o.oAnimations = gObjectAnimations.bobomb_seg8_anims_0802396C
-- initialize that animation
cur_obj_init_animation(0)
-- scale the object down
obj_scale(o, 0.75)
-- init sync object
network_init_object(o, true, {
"oBombOwner",
"activeFlags",
"oVelY",
"oPosX",
"oPosY",
"oPosZ",
})
end
---@param o Object
local function bomb_loop(o)
-- step object
local step = object_step_without_floor_orient()
if network_player_from_global_index(o.oBombOwner) == nil then obj_mark_for_deletion(o); return end
-- explode if we hit a wall or land
if (step & AIR_STEP_HIT_WALL ~= 0
or step & AIR_STEP_LANDED ~= 0
or bobomb_intersects_player(o, 100))
and network_local_index_from_global(o.oBombOwner) == 0 then
bomb_explode(o)
end
-- explode if we touch water
if o.oPosY < find_water_level(o.oPosX, o.oPosZ)
and network_local_index_from_global(o.oBombOwner) == 0 then
bomb_explode(o)
end
end
---@param o Object
local function bomb_held_init(o)
-- set basic object flags
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
-- set animation to bomb walking animation
o.oAnimations = gObjectAnimations.bobomb_seg8_anims_0802396C
-- initialize that animation
cur_obj_init_animation(0)
-- scale object down
cur_obj_scale(0.4)
end
---@param o Object
local function bomb_held_loop(o)
-- get network player and mario state
local np = gNetworkPlayers[o.oBombOwner]
local m = gMarioStates[np.localIndex]
-- check whether or not this bomb should be rendered
if np.currLevelNum ~= gNetworkPlayers[0].currLevelNum
or not np.connected or not np.currAreaSyncValid
or not can_hold_bomb(o.oBombOwner) then
cur_obj_disable_rendering()
else
cur_obj_enable_rendering()
end
-- set position
o.oPosX = get_hand_foot_pos_x(m, 0)
o.oPosY = get_hand_foot_pos_y(m, 0) - 25
o.oPosZ = get_hand_foot_pos_z(m, 0)
-- set face angle
o.oFaceAnglePitch = m.faceAngle.x
o.oFaceAngleYaw = m.faceAngle.y
o.oFaceAngleRoll = m.faceAngle.z
-- forward offset
o.oPosX = o.oPosX + sins(m.faceAngle.y) * 25
o.oPosZ = o.oPosZ + coss(m.faceAngle.y) * 25
-- if mario is currently not being rendered, set the objects
-- position to that mario's position instead of his hand position
if m.marioBodyState.updateTorsoTime ~= gMarioStates[0].marioBodyState.updateTorsoTime then
o.oPosX = m.pos.x
o.oPosY = m.pos.y
o.oPosZ = m.pos.z
end
end
local function spawn_bomb()
local m = gMarioStates[0]
local np = gNetworkPlayers[0]
-- spawn bomb
spawn_sync_object(id_bhvBomb, E_MODEL_BLACK_BOBOMB, m.pos.x, m.pos.y + 167, m.pos.z, function (o)
o.oForwardVel = m.forwardVel + 60
if o.oForwardVel < 75 then
o.oForwardVel = 75
end
o.oMoveAngleYaw = m.faceAngle.y
o.oBombOwner = np.globalIndex
end)
-- set mario's action
if m.floorHeight == m.pos.y and m.forwardVel == 0 then
set_mario_action(m, ACT_PUNCHING, 0)
elseif m.floorHeight == m.pos.y and m.forwardVel ~= 0 then
set_mario_action(m, ACT_JUMP_KICK, 0)
elseif m.pos.y < m.waterLevel then
set_mario_action(m, ACT_WATER_PUNCH, 0)
else
set_mario_action(m, ACT_DIVE, 0)
end
end
---@param m MarioState
local function mario_update(m)
if m.playerIndex ~= 0 then return end
if can_hold_bomb(0) then
bombCooldown = bombCooldown + 1
if m.controller.buttonPressed & binds[BIND_BOMBS].btn ~= 0
and bombCooldown >= gGlobalSyncTable.maxBombCooldown then
bombCooldown = 0
spawn_bomb()
end
else
bombCooldown = 0
end
bombCooldown = clampf(bombCooldown, 0, gGlobalSyncTable.maxBombCooldown)
end
local function hud_bombs()
if not can_hold_bomb(0) then return end
local text = ""
if bombCooldown < gGlobalSyncTable.maxBombCooldown then
text = "Reloading (" .. math.floor((gGlobalSyncTable.maxBombCooldown - bombCooldown) / 30 * 10) / 10 .. ")"
else
text = "Throw Bomb (" .. button_to_text(binds[BIND_BOMBS].btn) .. ")"
end
render_bar(text, bombCooldown, 0, gGlobalSyncTable.maxBombCooldown, 242, 143, 36)
end
local function hud_render()
djui_hud_set_font(djui_menu_get_font())
djui_hud_set_resolution(RESOLUTION_DJUI)
hud_bombs()
end
local function level_init()
for i = 0, MAX_PLAYERS - 1 do
local m = gMarioStates[i]
spawn_non_sync_object(id_bhvBombItem, E_MODEL_BLACK_BOBOMB, m.pos.x, m.pos.y, m.pos.z, function (o)
o.oBombOwner = i
end)
end
end
id_bhvBombExplosion = hook_behavior(nil, OBJ_LIST_DESTRUCTIVE, false, bomb_explosion_init, bomb_explosion_loop, nil)
id_bhvBomb = hook_behavior(nil, OBJ_LIST_DEFAULT, false, bomb_init, bomb_loop, nil)
id_bhvBombItem = hook_behavior(nil, OBJ_LIST_DEFAULT, false, bomb_held_init, bomb_held_loop, nil)
hook_event(HOOK_MARIO_UPDATE, mario_update)
hook_event(HOOK_ON_HUD_RENDER, hud_render)
hook_event(HOOK_ON_LEVEL_INIT, level_init)