Remove noreturns from functions that throw
[d2df-sdl.git] / src / game / g_weapons.pas
blob2ded932a832e97cbfac7134f68f9b007b50aeed7
1 (* Copyright (C) 2016 - The Doom2D.org team & involved community members <http://www.doom2d.org>.
2 * This file is part of Doom2D Forever.
4 * This program is free software: you can redistribute it and/or modify it under the terms of
5 * the GNU General Public License as published by the Free Software Foundation, version 3 of
6 * the License ONLY.
8 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 * See the GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License along with this program.
13 * If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_weapons;
19 interface
21 uses
22 SysUtils, Classes, mempool,
23 g_textures, g_basic, e_graphics, g_phys, xprofiler;
26 type
27 TProjectile = record
28 ShotType: Byte;
29 Target: Word;
30 SpawnerUID: Word;
31 Triggers: DWArray;
32 Obj: TObj;
33 Animation: TAnimation;
34 TextureID: DWORD;
35 Timeout: DWORD;
36 Stopped: Byte;
38 procedure positionChanged (); // WARNING! call this after monster position was changed, or coldet will not work right!
39 end;
42 var
43 Projectiles: array of TProjectile;
45 procedure g_Weapon_LoadData();
46 procedure g_Weapon_FreeData();
47 procedure g_Weapon_Init();
48 procedure g_Weapon_Free();
49 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
50 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
51 function g_Weapon_CreateProj(I: SizeInt; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): SizeInt;
53 procedure g_Weapon_gun(const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
54 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
55 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
56 function g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
57 function g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: SizeInt = -1; Silent: Boolean = False): SizeInt;
58 function g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
59 function g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
60 function g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
61 function g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
62 function g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
63 function g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
64 function g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
65 function g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt = -1; Silent: Boolean = False; compat: Boolean = True): SizeInt;
66 procedure g_Weapon_bfghit(x, y: Integer);
67 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
68 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
69 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
70 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
72 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
73 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
74 procedure g_Weapon_PreUpdate();
75 procedure g_Weapon_Update();
76 procedure g_Weapon_Draw();
77 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
78 procedure g_Weapon_DestroyProj(I: Integer; X, Y: Integer; Loud: Boolean = True);
80 procedure g_Weapon_SaveState (st: TStream);
81 procedure g_Weapon_LoadState (st: TStream);
83 procedure g_Weapon_AddDynLights();
85 const
86 WEAPON_IRONFIST = 0;
87 WEAPON_SAW = 1;
88 WEAPON_PISTOL = 2;
89 WEAPON_SHOTGUN1 = 3;
90 WEAPON_SHOTGUN2 = 4;
91 WEAPON_CHAINGUN = 5;
92 WEAPON_ROCKETLAUNCHER = 6;
93 WEAPON_PLASMA = 7;
94 WEAPON_BFG = 8;
95 WEAPON_SUPERCHAINGUN = 9;
96 WEAPON_FLAMETHROWER = 10;
97 WEAPON_ZOMBY_PISTOL = 20;
98 WEAPON_IMP_FIRE = 21;
99 WEAPON_BSP_FIRE = 22;
100 WEAPON_CACO_FIRE = 23;
101 WEAPON_BARON_FIRE = 24;
102 WEAPON_MANCUB_FIRE = 25;
103 WEAPON_SKEL_FIRE = 26;
105 WP_FIRST = WEAPON_IRONFIST;
106 WP_LAST = WEAPON_FLAMETHROWER;
110 gwep_debug_fast_trace: Boolean = true;
113 implementation
115 uses
116 {$IFDEF ENABLE_SOUND}
117 g_sound,
118 {$ENDIF}
119 Math, g_map, g_player, g_gfx, g_main, g_panel,
120 g_console, g_options, g_game,
121 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
122 g_language, g_netmsg, g_grid,
123 geom, binheap, hashtable, utils, xstreams;
125 type
126 TWaterPanel = record
127 X, Y: Integer;
128 Width, Height: Word;
129 Active: Boolean;
130 end;
132 const
133 SHOT_ROCKETLAUNCHER_WIDTH = 14;
134 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
136 SHOT_SKELFIRE_WIDTH = 14;
137 SHOT_SKELFIRE_HEIGHT = 14;
139 SHOT_PLASMA_WIDTH = 16;
140 SHOT_PLASMA_HEIGHT = 16;
142 SHOT_BFG_WIDTH = 32;
143 SHOT_BFG_HEIGHT = 32;
144 SHOT_BFG_DAMAGE = 100;
145 SHOT_BFG_RADIUS = 256;
147 SHOT_FLAME_WIDTH = 4;
148 SHOT_FLAME_HEIGHT = 4;
149 SHOT_FLAME_LIFETIME = 180;
151 SHOT_SIGNATURE = $544F4853; // 'SHOT'
153 type
154 PHitTime = ^THitTime;
155 THitTime = record
156 distSq: Integer;
157 mon: TMonster;
158 plridx: Integer; // if mon=nil
159 x, y: Integer;
160 end;
162 TBinHeapKeyHitTime = class
163 public
164 class function less (const a, b: Integer): Boolean; inline;
165 end;
167 // indicies in `wgunHitTime` array
168 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer, TBinHeapKeyHitTime>;
171 WaterMap: array of array of DWORD;
172 //wgunMonHash: THashIntInt;
173 wgunHitHeap: TBinaryHeapHitTimes;
174 wgunHitTime: array of THitTime;
175 wgunHitTimeUsed: Integer;
178 class function TBinHeapKeyHitTime.less (const a, b: Integer): Boolean;
180 hta, htb: PHitTime;
181 begin
182 hta := @wgunHitTime[a];
183 htb := @wgunHitTime[b];
184 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
185 if (hta.mon <> nil) then
186 begin
187 // a is monster
188 if (htb.mon = nil) then begin result := false; exit; end; // players first
189 result := (hta.mon.UID < htb.mon.UID); // why not?
191 else
192 begin
193 // a is player
194 if (htb.mon <> nil) then begin result := true; exit; end; // players first
195 result := (hta.plridx < htb.plridx); // why not?
196 end;
197 end;
200 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
201 begin
202 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
203 with wgunHitTime[wgunHitTimeUsed] do
204 begin
205 distSq := adistSq;
206 mon := amon;
207 plridx := -1;
208 x := ax;
209 y := ay;
210 end;
211 wgunHitHeap.insert(wgunHitTimeUsed);
212 Inc(wgunHitTimeUsed);
213 end;
216 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
217 begin
218 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
219 with wgunHitTime[wgunHitTimeUsed] do
220 begin
221 distSq := adistSq;
222 mon := nil;
223 plridx := aplridx;
224 x := ax;
225 y := ay;
226 end;
227 wgunHitHeap.insert(wgunHitTimeUsed);
228 Inc(wgunHitTimeUsed);
229 end;
232 function FindProjectileSlot(): SizeInt;
233 begin
234 for Result := 0 to High(Projectiles) do
235 if Projectiles[Result].ShotType = 0 then
236 Exit;
238 if Projectiles = nil
239 then Result := 0
240 else Result := Length(Projectiles);
242 SetLength(Projectiles, Result + 128);
243 end;
245 procedure CreateWaterMap();
247 WaterArray: Array of TWaterPanel;
248 a, b, c, m: Integer;
249 ok: Boolean;
250 begin
251 if gWater = nil then
252 Exit;
254 SetLength(WaterArray, Length(gWater));
256 for a := 0 to High(gWater) do
257 begin
258 WaterArray[a].X := gWater[a].X;
259 WaterArray[a].Y := gWater[a].Y;
260 WaterArray[a].Width := gWater[a].Width;
261 WaterArray[a].Height := gWater[a].Height;
262 WaterArray[a].Active := True;
263 end;
265 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
267 for a := 0 to High(WaterArray) do
268 if WaterArray[a].Active then
269 begin
270 WaterArray[a].Active := False;
271 m := Length(WaterMap);
272 SetLength(WaterMap, m+1);
273 SetLength(WaterMap[m], 1);
274 WaterMap[m][0] := a;
275 ok := True;
277 while ok do
278 begin
279 ok := False;
280 for b := 0 to High(WaterArray) do
281 if WaterArray[b].Active then
282 for c := 0 to High(WaterMap[m]) do
283 if g_CollideAround(WaterArray[b].X,
284 WaterArray[b].Y,
285 WaterArray[b].Width,
286 WaterArray[b].Height,
287 WaterArray[WaterMap[m][c]].X,
288 WaterArray[WaterMap[m][c]].Y,
289 WaterArray[WaterMap[m][c]].Width,
290 WaterArray[WaterMap[m][c]].Height) then
291 begin
292 WaterArray[b].Active := False;
293 SetLength(WaterMap[m],
294 Length(WaterMap[m])+1);
295 WaterMap[m][High(WaterMap[m])] := b;
296 ok := True;
297 Break;
298 end;
299 end;
301 g_Game_StepLoading();
302 end;
304 WaterArray := nil;
305 end;
309 chkTrap_pl: array [0..256] of Integer;
310 chkTrap_mn: array [0..65535] of TMonster;
312 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
314 //a, b, c, d, i1, i2: Integer;
315 //chkTrap_pl, chkTrap_mn: WArray;
316 plaCount: Integer = 0;
317 mnaCount: Integer = 0;
318 frameId: DWord;
321 function monsWaterCheck (mon: TMonster): Boolean;
322 begin
323 result := false; // don't stop
324 if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
325 begin
326 i2 += 1;
327 chkTrap_mn[i2] := monidx;
328 end;
329 end;
332 function monsWaterCheck (mon: TMonster): Boolean;
333 begin
334 result := false; // don't stop
335 if (mon.trapCheckFrameId <> frameId) then
336 begin
337 mon.trapCheckFrameId := frameId;
338 chkTrap_mn[mnaCount] := mon;
339 Inc(mnaCount);
340 end;
341 end;
344 a, b, c, d, f: Integer;
345 pan: TPanel;
346 begin
347 if (gWater = nil) or (WaterMap = nil) then Exit;
349 frameId := g_Mons_getNewTrapFrameId();
351 //i1 := -1;
352 //i2 := -1;
354 //SetLength(chkTrap_pl, 1024);
355 //SetLength(chkTrap_mn, 1024);
356 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
357 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
359 for a := 0 to High(WaterMap) do
360 begin
361 for b := 0 to High(WaterMap[a]) do
362 begin
363 pan := gWater[WaterMap[a][b]];
364 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Projectiles[ID].Obj) then continue;
366 for c := 0 to High(WaterMap[a]) do
367 begin
368 pan := gWater[WaterMap[a][c]];
369 for d := 0 to High(gPlayers) do
370 begin
371 if (gPlayers[d] <> nil) and (gPlayers[d].alive) then
372 begin
373 if gPlayers[d].Collide(pan) then
374 begin
375 f := 0;
376 while (f < plaCount) and (chkTrap_pl[f] <> d) do f += 1;
377 if (f = plaCount) then
378 begin
379 chkTrap_pl[plaCount] := d;
380 plaCount += 1;
381 if (plaCount = Length(chkTrap_pl)) then break;
382 end;
383 end;
384 end;
385 end;
387 //g_Mons_ForEach(monsWaterCheck);
388 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
389 end;
391 for f := 0 to plaCount-1 do
392 gPlayers[chkTrap_pl[f]].Damage(dm, Projectiles[ID].SpawnerUID, 0, 0, t);
393 for f := 0 to mnaCount-1 do
394 chkTrap_mn[f].Damage(dm, 0, 0, Projectiles[ID].SpawnerUID, t);
395 end;
396 end;
398 //chkTrap_pl := nil;
399 //chkTrap_mn := nil;
400 end;
402 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
404 tt, mt: Byte;
405 mon: TMonster;
406 begin
407 Result := False;
409 tt := g_GetUIDType(SpawnerUID);
410 if tt = UID_MONSTER then
411 begin
412 mon := g_Monsters_ByUID(SpawnerUID);
413 if mon <> nil then
414 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
415 else
416 mt := 0;
418 else
419 mt := 0;
421 if m = nil then Exit;
422 if m.UID = SpawnerUID then
423 begin
424 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
425 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
426 Exit;
427 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
428 if (m.MonsterType = MONSTER_CYBER) or
429 (m.MonsterType = MONSTER_BARREL) then
430 begin
431 Result := True;
432 Exit;
433 end;
434 end;
436 if tt = UID_MONSTER then
437 begin
438 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
439 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
440 Exit;
442 // Îáà ìîíñòðà îäíîãî âèäà:
443 if mt = m.MonsterType then
444 case mt of
445 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
446 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
447 Exit; // Ýòè íå áüþò ñâîèõ
448 end;
449 end;
451 if g_Game_IsServer then
452 begin
453 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
454 Result := m.Damage(d, vx, vy, SpawnerUID, t)
455 else
456 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
457 if t = HIT_FLAME then
458 m.CatchFire(SpawnerUID);
460 else
461 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
462 end;
465 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
466 begin
467 result := False;
469 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
470 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
472 if g_Game_IsServer then
473 begin
474 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
475 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
476 end;
478 result := true;
479 end;
482 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
484 function monsCheck (mon: TMonster): Boolean;
485 begin
486 result := false; // don't stop
487 if (mon.alive) and (mon.UID <> SpawnerUID) then
488 begin
489 with mon do
490 begin
491 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
492 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
493 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
494 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
495 begin
496 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
497 end;
498 end;
499 end;
500 end;
503 i, h: Integer;
504 st: Byte;
505 pl: TPlayer;
506 b: Boolean;
507 begin
508 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
510 h := High(gCorpses);
512 if gAdvCorpses and (h <> -1) then
513 for i := 0 to h do
514 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
515 with gCorpses[i] do
516 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
517 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
518 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
519 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
520 begin
521 Damage(50, SpawnerUID, 0, 0);
522 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
523 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
524 end;
526 st := TEAM_NONE;
527 pl := g_Player_Get(SpawnerUID);
528 if pl <> nil then
529 st := pl.Team;
531 h := High(gPlayers);
533 if h <> -1 then
534 for i := 0 to h do
535 if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then
536 with gPlayers[i] do
537 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
538 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
539 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
540 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
541 begin
542 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
543 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
544 else
545 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
546 if b then
547 gPlayers[i].BFGHit();
548 end;
550 //FIXME
551 g_Mons_ForEachAlive(monsCheck);
552 end;
554 function g_Weapon_CreateProj(I: SizeInt; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): SizeInt;
556 FramesID: DWORD = 0;
557 begin
558 if I < 0 then
559 Result := FindProjectileSlot()
560 else
561 begin
562 Result := I;
563 if Result >= High(Projectiles) then
564 SetLength(Projectiles, Result + 64)
565 end;
567 case ShotType of
568 WEAPON_ROCKETLAUNCHER:
569 begin
570 with Projectiles[Result] do
571 begin
572 g_Obj_Init(@Obj);
574 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
575 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
577 Animation := nil;
578 Triggers := nil;
579 ShotType := WEAPON_ROCKETLAUNCHER;
580 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
581 end;
582 end;
584 WEAPON_PLASMA:
585 begin
586 with Projectiles[Result] do
587 begin
588 g_Obj_Init(@Obj);
590 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
591 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
593 Triggers := nil;
594 ShotType := WEAPON_PLASMA;
595 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
596 Animation := TAnimation.Create(FramesID, True, 5);
597 end;
598 end;
600 WEAPON_BFG:
601 begin
602 with Projectiles[Result] do
603 begin
604 g_Obj_Init(@Obj);
606 Obj.Rect.Width := SHOT_BFG_WIDTH;
607 Obj.Rect.Height := SHOT_BFG_HEIGHT;
609 Triggers := nil;
610 ShotType := WEAPON_BFG;
611 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
612 Animation := TAnimation.Create(FramesID, True, 6);
613 end;
614 end;
616 WEAPON_FLAMETHROWER:
617 begin
618 with Projectiles[Result] do
619 begin
620 g_Obj_Init(@Obj);
622 Obj.Rect.Width := SHOT_FLAME_WIDTH;
623 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
625 Triggers := nil;
626 ShotType := WEAPON_FLAMETHROWER;
627 Animation := nil;
628 TextureID := 0;
629 g_Frames_Get(TextureID, 'FRAMES_FLAME');
630 end;
631 end;
633 WEAPON_IMP_FIRE:
634 begin
635 with Projectiles[Result] do
636 begin
637 g_Obj_Init(@Obj);
639 Obj.Rect.Width := 16;
640 Obj.Rect.Height := 16;
642 Triggers := nil;
643 ShotType := WEAPON_IMP_FIRE;
644 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
645 Animation := TAnimation.Create(FramesID, True, 4);
646 end;
647 end;
649 WEAPON_CACO_FIRE:
650 begin
651 with Projectiles[Result] do
652 begin
653 g_Obj_Init(@Obj);
655 Obj.Rect.Width := 16;
656 Obj.Rect.Height := 16;
658 Triggers := nil;
659 ShotType := WEAPON_CACO_FIRE;
660 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
661 Animation := TAnimation.Create(FramesID, True, 4);
662 end;
663 end;
665 WEAPON_MANCUB_FIRE:
666 begin
667 with Projectiles[Result] do
668 begin
669 g_Obj_Init(@Obj);
671 Obj.Rect.Width := 32;
672 Obj.Rect.Height := 32;
674 Triggers := nil;
675 ShotType := WEAPON_MANCUB_FIRE;
676 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
677 Animation := TAnimation.Create(FramesID, True, 4);
678 end;
679 end;
681 WEAPON_BARON_FIRE:
682 begin
683 with Projectiles[Result] do
684 begin
685 g_Obj_Init(@Obj);
687 Obj.Rect.Width := 16;
688 Obj.Rect.Height := 16;
690 Triggers := nil;
691 ShotType := WEAPON_BARON_FIRE;
692 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
693 Animation := TAnimation.Create(FramesID, True, 4);
694 end;
695 end;
697 WEAPON_BSP_FIRE:
698 begin
699 with Projectiles[Result] do
700 begin
701 g_Obj_Init(@Obj);
703 Obj.Rect.Width := 16;
704 Obj.Rect.Height := 16;
706 Triggers := nil;
707 ShotType := WEAPON_BSP_FIRE;
708 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
709 Animation := TAnimation.Create(FramesID, True, 4);
710 end;
711 end;
713 WEAPON_SKEL_FIRE:
714 begin
715 with Projectiles[Result] do
716 begin
717 g_Obj_Init(@Obj);
719 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
720 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
722 Triggers := nil;
723 ShotType := WEAPON_SKEL_FIRE;
724 target := TargetUID;
725 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
726 Animation := TAnimation.Create(FramesID, True, 5);
727 end;
728 end;
729 end;
731 Projectiles[Result].Obj.oldX := X;
732 Projectiles[Result].Obj.oldY := Y;
733 Projectiles[Result].Obj.X := X;
734 Projectiles[Result].Obj.Y := Y;
735 Projectiles[Result].Obj.Vel.X := XV;
736 Projectiles[Result].Obj.Vel.Y := YV;
737 Projectiles[Result].Obj.Accel.X := 0;
738 Projectiles[Result].Obj.Accel.Y := 0;
739 Projectiles[Result].SpawnerUID := Spawner;
741 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0)
742 then Projectiles[Result].Stopped := 255
743 else Projectiles[Result].Stopped := 0;
744 end;
746 procedure throw(i, x, y, xd, yd, s: Integer);
748 a: Integer;
749 begin
750 yd := yd - y;
751 xd := xd - x;
753 a := Max(Abs(xd), Abs(yd));
754 if a = 0 then
755 a := 1;
757 Projectiles[i].Obj.oldX := x;
758 Projectiles[i].Obj.oldY := y;
759 Projectiles[i].Obj.X := x;
760 Projectiles[i].Obj.Y := y;
761 Projectiles[i].Obj.Vel.X := (xd*s) div a;
762 Projectiles[i].Obj.Vel.Y := (yd*s) div a;
763 Projectiles[i].Obj.Accel.X := 0;
764 Projectiles[i].Obj.Accel.Y := 0;
765 Projectiles[i].Stopped := 0;
767 if Projectiles[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
768 Projectiles[i].Timeout := 900 // ~25 sec
769 else
770 begin
771 if Projectiles[i].ShotType = WEAPON_FLAMETHROWER
772 then Projectiles[i].Timeout := SHOT_FLAME_LIFETIME
773 else Projectiles[i].Timeout := 550; // ~15 sec
774 end;
775 end;
777 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
779 function PlayerHit(Team: Byte = 0): Boolean;
781 i: SizeInt;
782 ChkTeam: Boolean;
783 p: TPlayer;
784 begin
785 Result := False;
786 for i := 0 to High(gPlayers) do
787 begin
788 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
789 begin
790 ChkTeam := True;
792 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
793 begin
794 p := g_Player_Get(SpawnerUID);
795 if p <> nil then
796 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
797 end;
799 if ChkTeam and HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
800 begin
801 if t <> HIT_FLAME then
802 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
803 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
804 if t = HIT_BFG then
805 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
806 Result := True;
807 break;
808 end;
809 end;
810 end;
811 end;
814 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
815 begin
816 Result := False; // don't stop
817 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
818 begin
819 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
820 begin
821 if t <> HIT_FLAME then
822 begin
823 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
824 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
825 end;
826 Result := True;
827 end;
828 end;
829 end;
832 function monsCheckHit (mon: TMonster): Boolean;
833 begin
834 Result := False; // don't stop
835 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
836 begin
837 if t <> HIT_FLAME then
838 begin
839 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
840 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
841 end;
842 Result := True;
843 end;
844 end;
846 function MonsterHit(): Boolean;
847 begin
848 //Result := g_Mons_ForEach(monsCheckHit);
849 //FIXME: accelerate this!
850 Result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
851 end;
854 k: SizeInt;
855 begin
856 Result := 0;
858 if HitCorpses and gAdvCorpses then
859 begin
860 for k := 0 to High(gCorpses) do
861 if (gCorpses[k] <> nil) and (gCorpses[k].State <> CORPSE_STATE_REMOVEME) and
862 g_Obj_Collide(obj, @gCorpses[k].Obj) then
863 begin
864 // Ðàñïèëèâàåì òðóï:
865 gCorpses[k].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
866 (obj^.Vel.Y+obj^.Accel.Y) div 4);
867 Result := 1;
868 end;
869 end;
871 case gGameSettings.GameMode of
872 // Êàìïàíèÿ:
873 GM_COOP, GM_SINGLE: begin
874 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü
875 if MonsterHit() then Exit(2);
877 // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî èëè ñíàðÿä îò ìîíñòðà
878 if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or (gGameSettings.Options *
879 [TGameOption.FRIENDLY_FIRE, TGameOption.TEAM_HIT_PROJECTILE] <> []) then
880 begin
881 if PlayerHit() then Exit(1);
882 end;
883 end;
885 // Äåçìàò÷:
886 GM_DM: begin
887 if PlayerHit() then Exit(1); // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü
888 if MonsterHit() then Exit(2); // ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
889 end;
891 // Êîìàíäíûå:
892 GM_TDM, GM_CTF: begin
893 if PlayerHit(2) then Exit(1); // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
894 if MonsterHit() then Exit(2); // Ïîòîì ìîíñòðîâ
896 // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
897 // (èëè friendlyfire, èëè friendly_hit_projectile)
898 if gGameSettings.Options * [TGameOption.FRIENDLY_FIRE, TGameOption.TEAM_HIT_PROJECTILE] <>
899 [] then
900 begin
901 if PlayerHit(1) then Exit(1);
902 end;
903 end;
905 end;
906 end;
908 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
909 begin
910 Result := False;
912 case g_GetUIDType(UID) of
913 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
914 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
915 else Exit;
916 end;
917 end;
919 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
921 r: Integer; // squared radius
923 function monsExCheck (mon: TMonster): Boolean;
925 dx, dy, mm: Integer;
926 begin
927 result := false; // don't stop
928 begin
929 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
930 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
932 if dx > 1000 then dx := 1000;
933 if dy > 1000 then dy := 1000;
935 if (dx*dx+dy*dy < r) then
936 begin
937 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
938 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
940 mm := Max(abs(dx), abs(dy));
941 if mm = 0 then mm := 1;
943 if mon.alive then
944 begin
945 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
946 end;
948 mon.Push((dx*7) div mm, (dy*7) div mm);
949 end;
950 end;
951 end;
954 i, h, dx, dy, m, mm: Integer;
955 _angle: SmallInt;
956 begin
957 result := false;
959 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
961 r := rad*rad;
963 h := High(gPlayers);
965 if h <> -1 then
966 for i := 0 to h do
967 if (gPlayers[i] <> nil) and gPlayers[i].alive then
968 with gPlayers[i] do
969 begin
970 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
971 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
973 if dx > 1000 then dx := 1000;
974 if dy > 1000 then dy := 1000;
976 if dx*dx+dy*dy < r then
977 begin
978 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
979 // PLAYER_RECT.Width, PLAYER_RECT.Height);
981 mm := Max(abs(dx), abs(dy));
982 if mm = 0 then mm := 1;
984 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
985 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
986 end;
987 end;
989 //g_Mons_ForEach(monsExCheck);
990 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
992 h := High(gCorpses);
994 if gAdvCorpses and (h <> -1) then
995 for i := 0 to h do
996 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
997 with gCorpses[i] do
998 begin
999 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1000 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1002 if dx > 1000 then dx := 1000;
1003 if dy > 1000 then dy := 1000;
1005 if dx*dx+dy*dy < r then
1006 begin
1007 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1008 Obj.Rect.Width, Obj.Rect.Height);
1010 mm := Max(abs(dx), abs(dy));
1011 if mm = 0 then mm := 1;
1013 Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
1014 end;
1015 end;
1017 h := High(gGibs);
1019 if gAdvGibs and (h <> -1) then
1020 for i := 0 to h do
1021 if gGibs[i].alive then
1022 with gGibs[i] do
1023 begin
1024 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1025 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1027 if dx > 1000 then dx := 1000;
1028 if dy > 1000 then dy := 1000;
1030 if dx*dx+dy*dy < r then
1031 begin
1032 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1033 Obj.Rect.Width, Obj.Rect.Height);
1034 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1035 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1037 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1038 positionChanged(); // this updates spatial accelerators
1039 end;
1040 end;
1041 end;
1043 procedure g_Weapon_Init();
1044 begin
1045 CreateWaterMap();
1046 end;
1048 procedure g_Weapon_Free();
1050 i: Integer;
1051 begin
1052 for i := 0 to High(Projectiles) do
1053 Projectiles[i].Animation.Free();
1055 Projectiles := nil;
1056 WaterMap := nil;
1057 end;
1059 procedure g_Weapon_LoadData();
1060 begin
1061 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1063 {$IFDEF ENABLE_SOUND}
1064 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1065 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1066 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1067 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1068 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1069 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1070 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1071 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1072 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1073 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1074 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1075 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1076 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1077 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1078 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1079 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1080 g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE');
1081 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1082 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1083 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1084 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1085 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1086 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1087 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1088 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1089 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1090 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM');
1091 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM');
1092 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM');
1093 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1094 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1095 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1096 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1097 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1098 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1099 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1100 {$ENDIF}
1102 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1103 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1104 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1105 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1106 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1107 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1108 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1109 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1110 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1111 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1112 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1113 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1114 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1115 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1116 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1117 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1118 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1119 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1120 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1121 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1122 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1124 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1125 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1127 //wgunMonHash := hashNewIntInt();
1128 wgunHitHeap := TBinaryHeapHitTimes.Create();
1129 end;
1131 procedure g_Weapon_FreeData();
1132 begin
1133 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1135 {$IFDEF ENABLE_SOUND}
1136 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1137 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1138 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1139 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1140 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1141 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1142 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1143 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1144 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1145 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1146 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1147 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1148 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1149 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1150 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1151 g_Sound_Delete('SOUND_FIRE');
1152 g_Sound_Delete('SOUND_IGNITE');
1153 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1154 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1155 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1156 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1157 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1158 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1159 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1160 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1161 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1162 g_Sound_Delete('SOUND_WEAPON_FLAMEON');
1163 g_Sound_Delete('SOUND_WEAPON_FLAMEOFF');
1164 g_Sound_Delete('SOUND_WEAPON_FLAMEWORK');
1165 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1166 g_Sound_Delete('SOUND_PLAYER_JETON');
1167 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1168 g_Sound_Delete('SOUND_PLAYER_CASING1');
1169 g_Sound_Delete('SOUND_PLAYER_CASING2');
1170 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1171 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1172 {$ENDIF}
1174 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1175 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1176 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1177 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1178 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1179 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1180 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1181 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1182 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1183 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1184 g_Frames_DeleteByName('FRAMES_BFGHIT');
1185 g_Frames_DeleteByName('FRAMES_FIRE');
1186 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1187 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1188 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1189 g_Frames_DeleteByName('FRAMES_SMOKE');
1190 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1191 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1193 //wgunMonHash.Destroy();
1194 wgunHitHeap.Destroy();
1195 end;
1198 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1200 i: Integer;
1201 begin
1202 result := false;
1203 for i := 0 to High(gPlayers) do
1204 begin
1205 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1206 begin
1207 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1208 begin
1209 if AllowPush then gPlayers[i].Push(vx, vy);
1210 result := true;
1211 end;
1212 end;
1213 end;
1214 end;
1217 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1219 function monsCheck (mon: TMonster): Boolean;
1220 begin
1221 result := false; // don't stop
1222 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1223 begin
1224 if AllowPush then mon.Push(vx, vy);
1225 result := true;
1226 end;
1227 end;
1229 begin
1230 result := 0;
1231 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1232 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1233 end;
1237 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1239 a: Integer;
1240 x2, y2: Integer;
1241 dx, dy: Integer;
1242 xe, ye: Integer;
1243 xi, yi: Integer;
1244 s, c: Extended;
1245 //vx, vy: Integer;
1246 xx, yy, d: Integer;
1247 i: Integer;
1248 t1, _collide: Boolean;
1249 w, h: Word;
1250 {$IF DEFINED(D2F_DEBUG)}
1251 stt: UInt64;
1252 showTime: Boolean = true;
1253 {$ENDIF}
1254 begin
1255 a := GetAngle(x, y, xd, yd)+180;
1257 SinCos(DegToRad(-a), s, c);
1259 if Abs(s) < 0.01 then s := 0;
1260 if Abs(c) < 0.01 then c := 0;
1262 x2 := x+Round(c*gMapInfo.Width);
1263 y2 := y+Round(s*gMapInfo.Width);
1265 t1 := gWalls <> nil;
1266 _collide := False;
1267 w := gMapInfo.Width;
1268 h := gMapInfo.Height;
1270 xe := 0;
1271 ye := 0;
1272 dx := x2-x;
1273 dy := y2-y;
1275 if (xd = 0) and (yd = 0) then Exit;
1277 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1278 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1280 dx := Abs(dx);
1281 dy := Abs(dy);
1283 if dx > dy then d := dx else d := dy;
1285 //blood vel, for Monster.Damage()
1286 //vx := (dx*10 div d)*xi;
1287 //vy := (dy*10 div d)*yi;
1289 {$IF DEFINED(D2F_DEBUG)}
1290 stt := getTimeMicro();
1291 {$ENDIF}
1293 xx := x;
1294 yy := y;
1296 for i := 1 to d do
1297 begin
1298 xe := xe+dx;
1299 ye := ye+dy;
1301 if xe > d then
1302 begin
1303 xe := xe-d;
1304 xx := xx+xi;
1305 end;
1307 if ye > d then
1308 begin
1309 ye := ye-d;
1310 yy := yy+yi;
1311 end;
1313 if (yy > h) or (yy < 0) then Break;
1314 if (xx > w) or (xx < 0) then Break;
1316 if t1 then
1317 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1318 begin
1319 _collide := True;
1320 {$IF DEFINED(D2F_DEBUG)}
1321 stt := getTimeMicro()-stt;
1322 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1323 showTime := false;
1324 {$ENDIF}
1325 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1326 if g_Game_IsServer and g_Game_IsNet then
1327 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1328 end;
1330 if not _collide then
1331 begin
1332 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1333 end;
1335 if _collide then Break;
1336 end;
1338 {$IF DEFINED(D2F_DEBUG)}
1339 if showTime then
1340 begin
1341 stt := getTimeMicro()-stt;
1342 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1343 end;
1344 {$ENDIF}
1346 if CheckTrigger and g_Game_IsServer then
1347 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1348 end;
1352 //!!!FIXME!!!
1353 procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1355 x0, y0: Integer;
1356 x2, y2: Integer;
1357 xi, yi: Integer;
1358 wallDistSq: Integer = $3fffffff;
1359 spawnerPlr: TPlayer = nil;
1360 dmg: Integer;
1362 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1363 begin
1364 Result := False;
1365 if (idx < 0) or (idx > High(gPlayers)) then Exit;
1366 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then Exit;
1368 // TODO: Simplify. This is just a very long condition split down into several nested IF checks.
1369 if spawnerPlr <> nil then
1370 begin
1371 if (gGameSettings.Options * [TGameOption.TEAM_HIT_TRACE, TGameOption.FRIENDLY_FIRE] = []) and
1372 (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then
1373 begin
1374 if (spawnerPlr <> gPlayers[idx]) and (TGameOption.TEAM_ABSORB_ATTACKS in gGameSettings.Options) then
1375 dmg := Max(1, dmg div 2);
1376 exit;
1377 end;
1378 end;
1380 Result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1381 if Result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1382 {$IF DEFINED(D2F_DEBUG)}
1383 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1384 {$ENDIF}
1385 end;
1387 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1388 begin
1389 result := false;
1390 if (mon = nil) then exit;
1391 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1392 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1393 {$IF DEFINED(D2F_DEBUG)}
1394 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1395 {$ENDIF}
1396 end;
1398 // collect players along hitray
1399 // return `true` if instant hit was detected
1400 function playerPossibleHit (): Boolean;
1402 i: Integer;
1403 px, py, pw, ph: Integer;
1404 inx, iny: Integer;
1405 distSq: Integer;
1406 plr: TPlayer;
1407 begin
1408 result := false;
1409 for i := 0 to High(gPlayers) do
1410 begin
1411 plr := gPlayers[i];
1412 if (plr <> nil) and plr.alive then
1413 begin
1414 plr.getMapBox(px, py, pw, ph);
1415 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1416 begin
1417 distSq := distanceSq(x, y, inx, iny);
1418 if (distSq = 0) then
1419 begin
1420 // contains
1421 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1423 else if (distSq < wallDistSq) then
1424 begin
1425 appendHitTimePlr(distSq, i, inx, iny);
1426 end;
1427 end;
1428 end;
1429 end;
1430 end;
1432 procedure sqchecker (mon: TMonster);
1434 mx, my, mw, mh: Integer;
1435 inx, iny: Integer;
1436 distSq: Integer;
1437 begin
1438 mon.getMapBox(mx, my, mw, mh);
1439 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1440 begin
1441 distSq := distanceSq(x0, y0, inx, iny);
1442 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1443 end;
1444 end;
1447 a: Integer;
1448 dx, dy: Integer;
1449 xe, ye: Integer;
1450 s, c: Extended;
1451 i: Integer;
1452 wallHitFlag: Boolean = false;
1453 wallHitX: Integer = 0;
1454 wallHitY: Integer = 0;
1455 didHit: Boolean = false;
1456 {$IF DEFINED(D2F_DEBUG)}
1457 stt: UInt64;
1458 {$ENDIF}
1459 mit: PMonster;
1460 it: TMonsterGrid.Iter;
1461 begin
1463 if not gwep_debug_fast_trace then
1464 begin
1465 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1466 exit;
1467 end;
1470 if (xd = 0) and (yd = 0) then exit;
1472 if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1473 spawnerPlr := g_Player_Get(SpawnerUID);
1475 dmg := indmg;
1477 //wgunMonHash.reset(); //FIXME: clear hash on level change
1478 wgunHitHeap.clear();
1479 wgunHitTimeUsed := 0;
1481 a := GetAngle(x, y, xd, yd)+180;
1483 SinCos(DegToRad(-a), s, c);
1485 if Abs(s) < 0.01 then s := 0;
1486 if Abs(c) < 0.01 then c := 0;
1488 x0 := x;
1489 y0 := y;
1490 x2 := x+Round(c*gMapInfo.Width);
1491 y2 := y+Round(s*gMapInfo.Width);
1493 dx := x2-x;
1494 dy := y2-y;
1496 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1497 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1499 {$IF DEFINED(D2F_DEBUG)}
1500 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1501 stt := getTimeMicro();
1502 {$ENDIF}
1504 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1505 if wallHitFlag then
1506 begin
1507 x2 := wallHitX;
1508 y2 := wallHitY;
1509 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1511 else
1512 begin
1513 wallHitX := x2;
1514 wallHitY := y2;
1515 end;
1517 if playerPossibleHit() then exit; // instant hit
1519 // collect monsters
1520 //g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1522 it := monsGrid.forEachAlongLine(x, y, x2, y2, -1);
1523 for mit in it do sqchecker(mit^);
1524 it.release();
1526 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1527 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1528 while (wgunHitHeap.count > 0) do
1529 begin
1530 // has some entities to check, do it
1531 i := wgunHitHeap.front;
1532 wgunHitHeap.popFront();
1533 // hitpoint
1534 xe := wgunHitTime[i].x;
1535 ye := wgunHitTime[i].y;
1536 // check if it is not behind the wall
1537 if (wgunHitTime[i].mon <> nil) then
1538 begin
1539 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1541 else
1542 begin
1543 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1544 end;
1545 if didHit then
1546 begin
1547 // need new coords for trigger
1548 wallHitX := xe;
1549 wallHitY := ye;
1550 wallHitFlag := false; // no sparks
1551 break;
1552 end;
1553 end;
1555 // need sparks?
1556 if wallHitFlag then
1557 begin
1558 {$IF DEFINED(D2F_DEBUG)}
1559 stt := getTimeMicro()-stt;
1560 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1561 {$ENDIF}
1562 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1563 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1565 else
1566 begin
1567 {$IF DEFINED(D2F_DEBUG)}
1568 stt := getTimeMicro()-stt;
1569 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1570 {$ENDIF}
1571 end;
1573 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1574 end;
1577 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1579 obj: TObj;
1580 begin
1581 obj.X := X;
1582 obj.Y := Y;
1583 obj.rect.X := 0;
1584 obj.rect.Y := 0;
1585 obj.rect.Width := 39;
1586 obj.rect.Height := 52;
1587 obj.Vel.X := 0;
1588 obj.Vel.Y := 0;
1589 obj.Accel.X := 0;
1590 obj.Accel.Y := 0;
1592 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1593 begin
1594 {$IFDEF ENABLE_SOUND}
1595 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1596 {$ENDIF}
1598 else
1599 begin
1600 {$IFDEF ENABLE_SOUND}
1601 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1602 {$ENDIF}
1603 end;
1604 end;
1606 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1608 obj: TObj;
1609 begin
1610 obj.X := X;
1611 obj.Y := Y;
1612 obj.rect.X := 0;
1613 obj.rect.Y := 0;
1614 obj.rect.Width := 32;
1615 obj.rect.Height := 52;
1616 obj.Vel.X := 0;
1617 obj.Vel.Y := 0;
1618 obj.Accel.X := 0;
1619 obj.Accel.Y := 0;
1621 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1622 end;
1624 function g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1625 compat: Boolean): SizeInt;
1627 dx, dy: Integer;
1628 begin
1629 if WID < 0 then
1630 Result := FindProjectileSlot()
1631 else
1632 begin
1633 Result := WID;
1634 if Result >= High(Projectiles) then
1635 SetLength(Projectiles, Result + 64)
1636 end;
1638 with Projectiles[Result] do
1639 begin
1640 g_Obj_Init(@Obj);
1642 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1643 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1645 if compat
1646 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1647 else dx := -(Obj.Rect.Width div 2);
1648 dy := -(Obj.Rect.Height div 2);
1650 ShotType := WEAPON_ROCKETLAUNCHER;
1651 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 12);
1653 Animation := nil;
1654 triggers := nil;
1655 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1656 end;
1658 Projectiles[Result].SpawnerUID := SpawnerUID;
1660 {$IFDEF ENABLE_SOUND}
1661 if not Silent then
1662 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1663 {$ENDIF}
1664 end;
1666 function g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: SizeInt;
1667 Silent: Boolean): SizeInt;
1669 FramesID: DWORD;
1670 dx, dy: Integer;
1671 begin
1672 if WID < 0 then
1673 Result := FindProjectileSlot()
1674 else
1675 begin
1676 Result := WID;
1677 if Result >= High(Projectiles) then
1678 SetLength(Projectiles, Result + 64)
1679 end;
1681 with Projectiles[Result] do
1682 begin
1683 g_Obj_Init(@Obj);
1685 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1686 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1688 dx := -(Obj.Rect.Width div 2);
1689 dy := -(Obj.Rect.Height div 2);
1691 ShotType := WEAPON_SKEL_FIRE;
1692 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 12);
1694 triggers := nil;
1695 target := TargetUID;
1696 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1697 Animation := TAnimation.Create(FramesID, True, 5);
1698 end;
1700 Projectiles[Result].SpawnerUID := SpawnerUID;
1702 {$IFDEF ENABLE_SOUND}
1703 if not Silent then
1704 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1705 {$ENDIF}
1706 end;
1708 function g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1709 compat: Boolean): SizeInt;
1711 FramesID: DWORD;
1712 dx, dy: Integer;
1713 begin
1714 if WID < 0 then
1715 Result := FindProjectileSlot()
1716 else
1717 begin
1718 Result := WID;
1719 if Result >= High(Projectiles) then
1720 SetLength(Projectiles, Result + 64);
1721 end;
1723 with Projectiles[Result] do
1724 begin
1725 g_Obj_Init(@Obj);
1727 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1728 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1730 if compat
1731 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1732 else dx := -(Obj.Rect.Width div 2);
1733 dy := -(Obj.Rect.Height div 2);
1735 ShotType := WEAPON_PLASMA;
1736 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1738 triggers := nil;
1739 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1740 Animation := TAnimation.Create(FramesID, True, 5);
1741 end;
1743 Projectiles[Result].SpawnerUID := SpawnerUID;
1745 {$IFDEF ENABLE_SOUND}
1746 if not Silent then
1747 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1748 {$ENDIF}
1749 end;
1751 function g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1752 compat: Boolean): SizeInt;
1754 dx, dy: Integer;
1755 begin
1756 if WID < 0 then
1757 Result := FindProjectileSlot()
1758 else
1759 begin
1760 Result := WID;
1761 if Result >= High(Projectiles) then
1762 SetLength(Projectiles, Result + 64);
1763 end;
1765 with Projectiles[Result] do
1766 begin
1767 g_Obj_Init(@Obj);
1769 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1770 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1772 if compat
1773 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1774 else dx := -(Obj.Rect.Width div 2);
1775 dy := -(Obj.Rect.Height div 2);
1777 ShotType := WEAPON_FLAMETHROWER;
1778 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1780 triggers := nil;
1781 Animation := nil;
1782 TextureID := 0;
1783 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1784 end;
1786 Projectiles[Result].SpawnerUID := SpawnerUID;
1788 // if not Silent then
1789 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1790 end;
1792 function g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1793 compat: Boolean): SizeInt;
1795 FramesID: DWORD;
1796 dx, dy: Integer;
1797 begin
1798 if WID < 0 then
1799 Result := FindProjectileSlot()
1800 else
1801 begin
1802 Result := WID;
1803 if Result >= High(Projectiles) then
1804 SetLength(Projectiles, Result + 64)
1805 end;
1807 with Projectiles[Result] do
1808 begin
1809 g_Obj_Init(@Obj);
1811 Obj.Rect.Width := 16;
1812 Obj.Rect.Height := 16;
1814 if compat
1815 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1816 else dx := -(Obj.Rect.Width div 2);
1817 dy := -(Obj.Rect.Height div 2);
1819 ShotType := WEAPON_IMP_FIRE;
1820 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1822 triggers := nil;
1823 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1824 Animation := TAnimation.Create(FramesID, True, 4);
1825 end;
1827 Projectiles[Result].SpawnerUID := SpawnerUID;
1829 {$IFDEF ENABLE_SOUND}
1830 if not Silent then
1831 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1832 {$ENDIF}
1833 end;
1835 function g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1836 compat: Boolean): SizeInt;
1838 FramesID: DWORD;
1839 dx, dy: Integer;
1840 begin
1841 if WID < 0 then
1842 Result := FindProjectileSlot()
1843 else
1844 begin
1845 Result := WID;
1846 if Result >= High(Projectiles) then
1847 SetLength(Projectiles, Result + 64)
1848 end;
1850 with Projectiles[Result] do
1851 begin
1852 g_Obj_Init(@Obj);
1854 Obj.Rect.Width := 16;
1855 Obj.Rect.Height := 16;
1857 if compat
1858 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1859 else dx := -(Obj.Rect.Width div 2);
1860 dy := -(Obj.Rect.Height div 2);
1862 ShotType := WEAPON_CACO_FIRE;
1863 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1865 triggers := nil;
1866 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1867 Animation := TAnimation.Create(FramesID, True, 4);
1868 end;
1870 Projectiles[Result].SpawnerUID := SpawnerUID;
1872 {$IFDEF ENABLE_SOUND}
1873 if not Silent then
1874 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1875 {$ENDIF}
1876 end;
1878 function g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1879 compat: Boolean): SizeInt;
1881 FramesID: DWORD;
1882 dx, dy: Integer;
1883 begin
1884 if WID < 0 then
1885 Result := FindProjectileSlot()
1886 else
1887 begin
1888 Result := WID;
1889 if Result >= High(Projectiles) then
1890 SetLength(Projectiles, Result + 64)
1891 end;
1893 with Projectiles[Result] do
1894 begin
1895 g_Obj_Init(@Obj);
1897 Obj.Rect.Width := 16;
1898 Obj.Rect.Height := 16;
1900 if compat
1901 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1902 else dx := -(Obj.Rect.Width div 2);
1903 dy := -(Obj.Rect.Height div 2);
1905 ShotType := WEAPON_BARON_FIRE;
1906 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1908 triggers := nil;
1909 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1910 Animation := TAnimation.Create(FramesID, True, 4);
1911 end;
1913 Projectiles[Result].SpawnerUID := SpawnerUID;
1915 {$IFDEF ENABLE_SOUND}
1916 if not Silent then
1917 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1918 {$ENDIF}
1919 end;
1921 function g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1922 compat: Boolean): SizeInt;
1924 FramesID: DWORD;
1925 dx, dy: Integer;
1926 begin
1927 if WID < 0 then
1928 Result := FindProjectileSlot()
1929 else
1930 begin
1931 Result := WID;
1932 if Result >= High(Projectiles) then
1933 SetLength(Projectiles, Result + 64)
1934 end;
1936 with Projectiles[Result] do
1937 begin
1938 g_Obj_Init(@Obj);
1940 Obj.Rect.Width := 16;
1941 Obj.Rect.Height := 16;
1943 if compat
1944 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1945 else dx := -(Obj.Rect.Width div 2);
1946 dy := -(Obj.Rect.Height div 2);
1948 ShotType := WEAPON_BSP_FIRE;
1949 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1951 triggers := nil;
1953 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1954 Animation := TAnimation.Create(FramesID, True, 4);
1955 end;
1957 Projectiles[Result].SpawnerUID := SpawnerUID;
1959 {$IFDEF ENABLE_SOUND}
1960 if not Silent then
1961 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1962 {$ENDIF}
1963 end;
1965 function g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1966 compat: Boolean): SizeInt;
1968 FramesID: DWORD;
1969 dx, dy: Integer;
1970 begin
1971 if WID < 0 then
1972 Result := FindProjectileSlot()
1973 else
1974 begin
1975 Result := WID;
1976 if Result >= High(Projectiles) then
1977 SetLength(Projectiles, Result + 64)
1978 end;
1980 with Projectiles[Result] do
1981 begin
1982 g_Obj_Init(@Obj);
1984 Obj.Rect.Width := 32;
1985 Obj.Rect.Height := 32;
1987 if compat
1988 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1989 else dx := -(Obj.Rect.Width div 2);
1990 dy := -(Obj.Rect.Height div 2);
1992 ShotType := WEAPON_MANCUB_FIRE;
1993 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1995 triggers := nil;
1997 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1998 Animation := TAnimation.Create(FramesID, True, 4);
1999 end;
2001 Projectiles[Result].SpawnerUID := SpawnerUID;
2003 {$IFDEF ENABLE_SOUND}
2004 if not Silent then
2005 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2006 {$ENDIF}
2007 end;
2009 function g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
2010 compat: Boolean): SizeInt;
2012 FramesID: DWORD;
2013 dx, dy: Integer;
2014 begin
2015 if WID < 0 then
2016 Result := FindProjectileSlot()
2017 else
2018 begin
2019 Result := WID;
2020 if Result >= High(Projectiles) then
2021 SetLength(Projectiles, Result + 64)
2022 end;
2024 with Projectiles[Result] do
2025 begin
2026 g_Obj_Init(@Obj);
2028 Obj.Rect.Width := SHOT_BFG_WIDTH;
2029 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2031 if compat
2032 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
2033 else dx := -(Obj.Rect.Width div 2);
2034 dy := -(Obj.Rect.Height div 2);
2036 ShotType := WEAPON_BFG;
2037 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
2039 triggers := nil;
2040 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2041 Animation := TAnimation.Create(FramesID, True, 6);
2042 end;
2044 Projectiles[Result].SpawnerUID := SpawnerUID;
2046 {$IFDEF ENABLE_SOUND}
2047 if not Silent then
2048 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2049 {$ENDIF}
2050 end;
2052 procedure g_Weapon_bfghit(x, y: Integer);
2054 ID: DWORD;
2055 Anim: TAnimation;
2056 begin
2057 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2058 begin
2059 Anim := TAnimation.Create(ID, False, 4);
2060 g_GFX_OnceAnim(x-32, y-32, Anim);
2061 Anim.Destroy();
2062 end;
2063 end;
2065 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2066 begin
2067 {$IFDEF ENABLE_SOUND}
2068 if not Silent then
2069 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2070 {$ENDIF}
2072 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2073 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2074 begin
2075 if ABS(x-xd) >= ABS(y-yd) then
2076 begin
2077 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2078 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2080 else
2081 begin
2082 g_Weapon_gun(x+1, y, xd+1, yd, 1, 3, SpawnerUID, False);
2083 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2084 end;
2085 end;
2086 end;
2088 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2089 begin
2090 {$IFDEF ENABLE_SOUND}
2091 if not Silent then
2092 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2093 {$ENDIF}
2095 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2096 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2097 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2098 begin
2099 if ABS(x-xd) >= ABS(y-yd) then
2100 begin
2101 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2102 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2104 else
2105 begin
2106 g_Weapon_gun(x+1, y, xd+1, yd, 1, 2, SpawnerUID, False);
2107 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2108 end;
2109 end;
2110 end;
2112 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2114 i, j, k: Integer;
2115 begin
2116 {$IFDEF ENABLE_SOUND}
2117 if not Silent then
2118 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2119 {$ENDIF}
2121 for i := 0 to 9 do
2122 begin
2123 j := 0; k := 0;
2124 if ABS(x-xd) >= ABS(y-yd) then j := Random(17) - 8 else k := Random(17) - 8; // -8 .. 8
2125 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2126 end;
2127 end;
2129 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2131 a, i, j, k: Integer;
2132 begin
2133 {$IFDEF ENABLE_SOUND}
2134 if not Silent then
2135 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2136 {$ENDIF}
2138 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]
2139 then a := 25
2140 else a := 20;
2142 for i := 0 to a do
2143 begin
2144 j := 0; k := 0;
2145 if ABS(x-xd) >= ABS(y-yd) then j := Random(41) - 20 else k := Random(41) - 20; // -20 .. 20
2146 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2147 end;
2148 end;
2150 procedure g_Weapon_PreUpdate();
2152 i: Integer;
2153 begin
2154 for i := 0 to High(Projectiles) do
2155 if Projectiles[i].ShotType <> 0 then
2156 begin
2157 Projectiles[i].Obj.oldX := Projectiles[i].Obj.X;
2158 Projectiles[i].Obj.oldY := Projectiles[i].Obj.Y;
2159 end;
2160 end;
2162 procedure g_Weapon_Update();
2164 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2165 _id: DWORD;
2166 Anim: TAnimation;
2167 t: DWArray;
2168 st: Word;
2169 s: String;
2170 o: TObj;
2171 spl: Boolean;
2172 Loud: Boolean;
2173 tcx, tcy: Integer;
2174 label
2175 finish_update; // uhh, FreePascal doesn't have a 'break' for 'case of'
2176 begin
2177 for i := 0 to High(Projectiles) do
2178 begin
2179 if Projectiles[i].ShotType = 0 then
2180 Continue;
2182 Loud := True;
2184 with Projectiles[i] do
2185 begin
2186 Timeout -= 1;
2187 oldvx := Obj.Vel.X;
2188 oldvy := Obj.Vel.Y;
2189 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2190 if (Stopped = 0) and g_Game_IsServer then
2191 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2192 SpawnerUID, ACTIVATE_SHOT, triggers)
2193 else
2194 t := nil;
2196 if t <> nil then
2197 begin
2198 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2199 if triggers = nil then
2200 triggers := t
2201 else
2202 begin
2203 h := High(t);
2205 for a := 0 to h do
2206 if not InDWArray(t[a], triggers) then
2207 begin
2208 SetLength(triggers, Length(triggers)+1);
2209 triggers[High(triggers)] := t[a];
2210 end;
2211 end;
2212 end;
2214 // Àíèìàöèÿ ñíàðÿäà:
2215 if Animation <> nil then
2216 Animation.Update();
2218 // Äâèæåíèå:
2219 spl := not (ShotType in [WEAPON_PLASMA, WEAPON_BFG, WEAPON_BSP_FIRE, WEAPON_FLAMETHROWER]);
2221 if Stopped = 0
2222 then st := g_Obj_Move_Projectile(@Obj, False, spl)
2223 else st := 0;
2224 positionChanged(); // this updates spatial accelerators
2226 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2227 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2228 begin
2229 ShotType := 0;
2230 //Goto finish_update;
2231 FreeAndNil(Animation);
2232 Continue; // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2233 end;
2235 cx := Obj.X + (Obj.Rect.Width div 2);
2236 cy := Obj.Y + (Obj.Rect.Height div 2);
2238 case ShotType of
2239 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2240 begin
2241 // Âûëåòåëà èç âîäû:
2242 if WordBool(st and MOVE_HITAIR) then
2243 g_Obj_SetSpeed(@Obj, 12);
2245 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2246 if WordBool(st and MOVE_INWATER) then
2247 begin
2248 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2250 else if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2251 begin
2252 Anim := TAnimation.Create(_id, False, 3);
2253 Anim.Alpha := 150;
2254 g_GFX_OnceAnim(Obj.X-14+Random(9), cy-20+Random(9),
2255 Anim, ONCEANIM_SMOKE);
2256 Anim.Destroy();
2257 end;
2259 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2260 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2261 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2262 (Timeout < 1) then
2263 begin
2264 Obj.Vel.X := 0;
2265 Obj.Vel.Y := 0;
2267 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2269 if ShotType = WEAPON_SKEL_FIRE then
2270 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2271 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2272 begin
2273 Anim := TAnimation.Create(TextureID, False, 8);
2274 Anim.Blending := False;
2275 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2276 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2277 Anim.Destroy();
2278 end;
2280 else
2281 begin // Âçðûâ Ðàêåòû
2282 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2283 begin
2284 Anim := TAnimation.Create(TextureID, False, 6);
2285 Anim.Blending := False;
2286 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2287 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2288 Anim.Destroy();
2289 end;
2290 end;
2292 {$IFDEF ENABLE_SOUND}
2293 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2294 {$ENDIF}
2296 ShotType := 0;
2297 end;
2299 if ShotType = WEAPON_SKEL_FIRE then
2300 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2301 if GetPos(target, @o) then
2302 throw(i, Obj.X, Obj.Y,
2303 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2304 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2305 12);
2306 end;
2307 end;
2309 WEAPON_PLASMA, WEAPON_BSP_FIRE:
2310 begin
2311 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2312 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2313 begin
2314 {$IFDEF ENABLE_SOUND}
2315 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2316 {$ENDIF}
2317 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2318 ShotType := 0;
2319 Goto finish_update;
2320 end;
2322 // Âåëè÷èíà óðîíà:
2323 if (ShotType = WEAPON_PLASMA) and (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])
2324 then a := 10
2325 else a := 5;
2327 if ShotType = WEAPON_BSP_FIRE then
2328 a := 10;
2330 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2331 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2332 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2333 (Timeout < 1) then
2334 begin
2335 if ShotType = WEAPON_PLASMA
2336 then s := 'FRAMES_EXPLODE_PLASMA'
2337 else s := 'FRAMES_EXPLODE_BSPFIRE';
2339 // Âçðûâ Ïëàçìû:
2340 if g_Frames_Get(TextureID, s) then
2341 begin
2342 Anim := TAnimation.Create(TextureID, False, 3);
2343 Anim.Blending := False;
2344 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2345 Anim.Destroy();
2346 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2347 end;
2349 {$IFDEF ENABLE_SOUND}
2350 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2351 {$ENDIF}
2353 ShotType := 0;
2354 end;
2355 end;
2357 WEAPON_FLAMETHROWER:
2358 begin
2359 // Ñî âðåìåíåì óìèðàåò
2360 if (Timeout < 1) then
2361 begin
2362 ShotType := 0;
2363 Goto finish_update;
2364 end;
2365 // Ïîä âîäîé òîæå
2366 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2367 begin
2368 if WordBool(st and MOVE_HITWATER) then
2369 begin
2370 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2371 begin
2372 Anim := TAnimation.Create(_id, False, 3);
2373 Anim.Alpha := 0;
2374 tcx := Random(8);
2375 tcy := Random(8);
2376 g_GFX_OnceAnim(
2377 cx-4+tcx-(Anim.Width div 2),
2378 cy-4+tcy-(Anim.Height div 2),
2379 Anim, ONCEANIM_SMOKE
2381 Anim.Destroy();
2382 end;
2384 else
2385 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2387 ShotType := 0;
2388 Goto finish_update;
2389 end;
2391 // Ãðàâèòàöèÿ
2392 if Stopped = 0 then
2393 Obj.Accel.Y += 1;
2395 // Ïîïàëè â ñòåíó èëè â âîäó:
2396 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2397 begin
2398 // Ïðèëèïàåì:
2399 Obj.Vel.X := 0;
2400 Obj.Vel.Y := 0;
2401 Obj.Accel.Y := 0;
2402 if WordBool(st and MOVE_HITWALL) then
2403 Stopped := MOVE_HITWALL
2404 else if WordBool(st and MOVE_HITLAND) then
2405 Stopped := MOVE_HITLAND
2406 else if WordBool(st and MOVE_HITCEIL) then
2407 Stopped := MOVE_HITCEIL;
2408 end;
2410 a := IfThen(Stopped = 0, 10, 1);
2411 // Åñëè â êîãî-òî ïîïàëè
2412 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2413 begin
2414 // HIT_FLAME ñàì ïîäîææåò
2415 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2416 if Stopped = 0 then
2417 ShotType := 0;
2418 end;
2420 if Stopped = 0
2421 then tf := 2
2422 else tf := 3;
2424 if (gTime mod LongWord(tf) = 0) then
2425 begin
2426 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2427 Anim.Alpha := 0;
2428 case Stopped of
2429 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2430 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2431 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2432 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2433 end;
2434 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2435 Anim.Destroy();
2436 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2437 end;
2438 end;
2440 WEAPON_BFG:
2441 begin
2442 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2443 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2444 begin
2445 {$IFDEF ENABLE_SOUND}
2446 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2447 {$ENDIF}
2448 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2449 ShotType := 0;
2450 Goto finish_update;
2451 end;
2453 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2454 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2455 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2456 (Timeout < 1) then
2457 begin
2458 // Ëó÷è BFG:
2459 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2461 // Âçðûâ BFG:
2462 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2463 begin
2464 Anim := TAnimation.Create(TextureID, False, 6);
2465 Anim.Blending := False;
2466 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2467 Anim.Destroy();
2468 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2469 end;
2471 {$IFDEF ENABLE_SOUND}
2472 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2473 {$ENDIF}
2475 ShotType := 0;
2476 end;
2477 end;
2479 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE:
2480 begin
2481 // Âûëåòåë èç âîäû:
2482 if WordBool(st and MOVE_HITAIR) then
2483 g_Obj_SetSpeed(@Obj, 16);
2485 // Âåëè÷èíà óðîíà:
2486 if ShotType = WEAPON_IMP_FIRE then
2487 a := 5
2488 else
2489 if ShotType = WEAPON_CACO_FIRE
2490 then a := 20
2491 else a := 40;
2493 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2494 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2495 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2496 (Timeout < 1) then
2497 begin
2498 if ShotType = WEAPON_IMP_FIRE then
2499 s := 'FRAMES_EXPLODE_IMPFIRE'
2500 else
2501 if ShotType = WEAPON_CACO_FIRE
2502 then s := 'FRAMES_EXPLODE_CACOFIRE'
2503 else s := 'FRAMES_EXPLODE_BARONFIRE';
2505 // Âçðûâ:
2506 if g_Frames_Get(TextureID, s) then
2507 begin
2508 Anim := TAnimation.Create(TextureID, False, 6);
2509 Anim.Blending := False;
2510 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2511 Anim.Destroy();
2512 end;
2514 {$IFDEF ENABLE_SOUND}
2515 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2516 {$ENDIF}
2518 ShotType := 0;
2519 end;
2520 end;
2522 WEAPON_MANCUB_FIRE:
2523 begin
2524 // Âûëåòåë èç âîäû:
2525 if WordBool(st and MOVE_HITAIR) then
2526 g_Obj_SetSpeed(@Obj, 16);
2528 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2529 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2530 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2531 (Timeout < 1) then
2532 begin
2533 // Âçðûâ:
2534 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2535 begin
2536 Anim := TAnimation.Create(TextureID, False, 6);
2537 Anim.Blending := False;
2538 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2539 Anim.Destroy();
2540 end;
2542 {$IFDEF ENABLE_SOUND}
2543 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2544 {$ENDIF}
2546 ShotType := 0;
2547 end;
2548 end;
2549 end;
2551 finish_update:
2552 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2553 if ShotType = 0 then
2554 begin
2555 if gGameSettings.GameType = GT_SERVER then
2556 MH_SEND_DeleteProj(i, Obj.X, Obj.Y, Loud);
2557 FreeAndNil(Animation);
2559 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2560 if gGameSettings.GameType = GT_SERVER then
2561 MH_SEND_UpdateProj(i);
2562 end;
2563 end;
2564 end;
2566 procedure g_Weapon_Draw();
2568 i, fX, fY: Integer;
2569 a: SmallInt;
2570 p: TDFPoint;
2571 begin
2572 for i := 0 to High(Projectiles) do
2573 if Projectiles[i].ShotType <> 0 then
2574 with Projectiles[i] do
2575 begin
2576 if (Projectiles[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BARON_FIRE,
2577 WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE])
2578 then a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2579 else a := 0;
2581 Obj.lerp(gLerpFactor, fX, fY);
2582 p.X := Obj.Rect.Width div 2;
2583 p.Y := Obj.Rect.Height div 2;
2585 if Projectiles[i].ShotType = WEAPON_BFG then
2586 begin
2587 fX -= 6;
2588 fY -= 7;
2589 end;
2591 if Animation <> nil then
2592 begin
2593 if Projectiles[i].ShotType in [WEAPON_BARON_FIRE, WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE]
2594 then Animation.DrawEx(fX, fY, TMirrorType.None, p, a)
2595 else Animation.Draw(fX, fY, TMirrorType.None);
2597 else if TextureID <> 0 then
2598 begin
2599 if (Projectiles[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2600 e_DrawAdv(TextureID, fX, fY, 0, True, False, a, @p, TMirrorType.None)
2601 else if (Projectiles[i].ShotType <> WEAPON_FLAMETHROWER) then
2602 e_Draw(TextureID, fX, fY, 0, True, False);
2603 end;
2605 if g_debug_Frames then
2606 begin
2607 e_DrawQuad(Obj.X+Obj.Rect.X,
2608 Obj.Y+Obj.Rect.Y,
2609 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2610 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2611 0, 255, 0);
2612 end;
2613 end;
2614 end;
2616 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2618 a: Integer;
2619 begin
2620 Result := False;
2622 for a := 0 to High(Projectiles) do
2623 if (Projectiles[a].ShotType <> 0) and (Projectiles[a].SpawnerUID <> UID) then
2624 if ((Projectiles[a].Obj.Vel.Y = 0) and (Projectiles[a].Obj.Vel.X > 0) and (Projectiles[a].Obj.X < X)) or
2625 (Projectiles[a].Obj.Vel.Y = 0) and (Projectiles[a].Obj.Vel.X < 0) and (Projectiles[a].Obj.X > X) then
2626 if (Abs(X-Projectiles[a].Obj.X) < Abs(Projectiles[a].Obj.Vel.X*Time)) and
2627 g_Collide(X, Y, Width, Height, X, Projectiles[a].Obj.Y,
2628 Projectiles[a].Obj.Rect.Width, Projectiles[a].Obj.Rect.Height) and
2629 g_TraceVector(X, Y, Projectiles[a].Obj.X, Projectiles[a].Obj.Y) then
2630 begin
2631 Result := True;
2632 Exit;
2633 end;
2634 end;
2636 procedure g_Weapon_SaveState (st: TStream);
2638 count: SizeUInt;
2639 i, j: SizeInt;
2640 begin
2641 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2642 count := 0;
2643 for i := 0 to High(Projectiles) do
2644 if Projectiles[i].ShotType <> 0 then count += 1;
2646 // Êîëè÷åñòâî ñíàðÿäîâ
2647 st.WriteDWordLE(count);
2648 if count = 0 then Exit;
2650 for i := 0 to High(Projectiles) do
2651 begin
2652 if Projectiles[i].ShotType <> 0 then
2653 begin
2654 // Ñèãíàòóðà ñíàðÿäà
2655 utils.writeSign(st, 'SHOT');
2656 st.WriteByte(0); // version
2658 st.WriteByte(Projectiles[i].ShotType); // Òèï ñíàðÿäà
2659 st.WriteWordLE(Projectiles[i].Target); // Öåëü
2660 st.WriteWordLE(Projectiles[i].SpawnerUID); // UID ñòðåëÿâøåãî
2661 st.WriteDWordLE(Length(Projectiles[i].Triggers)); // Ðàçìåð ïîëÿ Triggers
2663 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2664 for j := 0 to High(Projectiles[i].Triggers) do
2665 st.WriteDWordLE(Projectiles[i].Triggers[j]);
2667 Obj_SaveState(st, @Projectiles[i].Obj); // Îáúåêò ñíàðÿäà
2668 st.WriteByte(Projectiles[i].Stopped); // Êîñòûëèíà
2669 end;
2670 end;
2671 end;
2673 procedure g_Weapon_LoadState (st: TStream);
2675 count, tc, i: SizeUInt;
2676 j: SizeInt;
2677 dw: LongWord;
2678 begin
2679 if st = nil then Exit;
2681 // Êîëè÷åñòâî ñíàðÿäîâ
2682 count := st.ReadDWordLE();
2683 if count > 1024*1024 then
2684 Raise XStreamError.Create('invalid shots counter');
2686 SetLength(Projectiles, count);
2687 if count = 0 then Exit;
2689 for i := 0 to count-1 do
2690 begin
2691 // Ñèãíàòóðà ñíàðÿäà
2692 if not utils.checkSign(st, 'SHOT') then
2693 Raise XStreamError.Create('invalid shot signature');
2694 if st.ReadByte() <> 0 then
2695 Raise XStreamError.Create('invalid shot version');
2697 Projectiles[i].ShotType := st.ReadByte(); // Òèï ñíàðÿäà:
2698 Projectiles[i].Target := st.ReadWordLE(); // Öåëü
2699 Projectiles[i].SpawnerUID := st.ReadWordLE(); // UID ñòðåëÿâøåãî
2701 // Ðàçìåð ïîëÿ Triggers
2702 tc := st.ReadDWordLE();
2703 if tc > 1024*1024 then
2704 Raise XStreamError.Create('invalid shot triggers counter');
2705 SetLength(Projectiles[i].Triggers, tc);
2707 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2708 for j := 0 to tc - 1 do
2709 Projectiles[i].Triggers[j] := st.ReadDWordLE();
2711 Obj_LoadState(@Projectiles[i].Obj, st); // Îáúåêò ïðåäìåòà
2712 Projectiles[i].Stopped := st.ReadByte(); // Êîñòûëèíà
2714 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2715 Projectiles[i].TextureID := DWORD(-1);
2716 Projectiles[i].Animation := nil;
2718 case Projectiles[i].ShotType of
2719 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2720 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Projectiles[i].TextureID);
2721 WEAPON_PLASMA:
2722 begin
2723 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2724 Projectiles[i].Animation := TAnimation.Create(dw, True, 5);
2725 end;
2726 WEAPON_BFG:
2727 begin
2728 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2729 Projectiles[i].Animation := TAnimation.Create(dw, True, 6);
2730 end;
2731 WEAPON_IMP_FIRE:
2732 begin
2733 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2734 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2735 end;
2736 WEAPON_BSP_FIRE:
2737 begin
2738 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2739 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2740 end;
2741 WEAPON_CACO_FIRE:
2742 begin
2743 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2744 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2745 end;
2746 WEAPON_BARON_FIRE:
2747 begin
2748 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2749 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2750 end;
2751 WEAPON_MANCUB_FIRE:
2752 begin
2753 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2754 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2755 end;
2756 end;
2757 end;
2758 end;
2760 procedure g_Weapon_DestroyProj(I: Integer; X, Y: Integer; Loud: Boolean);
2762 cx, cy: Integer;
2763 Anim: TAnimation;
2764 s: String;
2765 begin
2766 if (Low(Projectiles) > I) or (High(Projectiles) < I) then
2767 Exit;
2769 with Projectiles[I] do
2770 begin
2771 if ShotType = 0 then Exit;
2772 Obj.X := X;
2773 Obj.Y := Y;
2774 cx := Obj.X + (Obj.Rect.Width div 2);
2775 cy := Obj.Y + (Obj.Rect.Height div 2);
2777 case ShotType of
2778 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2779 begin
2780 if Loud then
2781 begin
2782 if ShotType = WEAPON_SKEL_FIRE then
2783 begin
2784 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2785 begin
2786 Anim := TAnimation.Create(TextureID, False, 8);
2787 Anim.Blending := False;
2788 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2789 Anim.Destroy();
2790 end;
2792 else
2793 begin
2794 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2795 begin
2796 Anim := TAnimation.Create(TextureID, False, 6);
2797 Anim.Blending := False;
2798 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2799 Anim.Destroy();
2800 end;
2801 end;
2802 {$IFDEF ENABLE_SOUND}
2803 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2804 {$ENDIF}
2805 end;
2806 end;
2808 WEAPON_PLASMA, WEAPON_BSP_FIRE:
2809 begin
2810 if ShotType = WEAPON_PLASMA
2811 then s := 'FRAMES_EXPLODE_PLASMA'
2812 else s := 'FRAMES_EXPLODE_BSPFIRE';
2814 if g_Frames_Get(TextureID, s) and loud then
2815 begin
2816 Anim := TAnimation.Create(TextureID, False, 3);
2817 Anim.Blending := False;
2818 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2819 Anim.Destroy();
2821 {$IFDEF ENABLE_SOUND}
2822 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2823 {$ENDIF}
2824 end;
2825 end;
2827 WEAPON_BFG:
2828 begin
2829 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2830 begin
2831 Anim := TAnimation.Create(TextureID, False, 6);
2832 Anim.Blending := False;
2833 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2834 Anim.Destroy();
2836 {$IFDEF ENABLE_SOUND}
2837 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2838 {$ENDIF}
2839 end;
2840 end;
2842 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE:
2843 begin
2844 if ShotType = WEAPON_IMP_FIRE then
2845 s := 'FRAMES_EXPLODE_IMPFIRE'
2846 else
2847 if ShotType = WEAPON_CACO_FIRE
2848 then s := 'FRAMES_EXPLODE_CACOFIRE'
2849 else s := 'FRAMES_EXPLODE_BARONFIRE';
2851 if g_Frames_Get(TextureID, s) and Loud then
2852 begin
2853 Anim := TAnimation.Create(TextureID, False, 6);
2854 Anim.Blending := False;
2855 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2856 Anim.Destroy();
2858 {$IFDEF ENABLE_SOUND}
2859 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2860 {$ENDIF}
2861 end;
2862 end;
2864 WEAPON_MANCUB_FIRE:
2865 begin
2866 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2867 begin
2868 Anim := TAnimation.Create(TextureID, False, 6);
2869 Anim.Blending := False;
2870 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2871 Anim.Destroy();
2873 {$IFDEF ENABLE_SOUND}
2874 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2875 {$ENDIF}
2876 end;
2877 end;
2878 end;
2880 ShotType := 0;
2881 FreeAndNil(Animation);
2882 end;
2883 end;
2886 procedure g_Weapon_AddDynLights();
2888 i: Integer;
2889 begin
2890 for i := 0 to High(Projectiles) do
2891 begin
2892 if Projectiles[i].ShotType = 0 then continue;
2893 if Projectiles[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_PLASMA, WEAPON_BFG,
2894 WEAPON_FLAMETHROWER, WEAPON_IMP_FIRE, WEAPON_BSP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE,
2895 WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE] then
2896 begin
2897 if (Projectiles[i].ShotType = WEAPON_PLASMA) then
2898 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2899 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 0, 0.3, 1, 0.4)
2900 else if (Projectiles[i].ShotType = WEAPON_BFG) then
2901 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2902 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
2903 else if (Projectiles[i].ShotType = WEAPON_FLAMETHROWER) then
2904 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2905 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
2906 else
2907 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2908 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);
2909 end;
2910 end;
2911 end;
2913 procedure TProjectile.positionChanged ();
2914 begin
2915 end;
2917 end.