sound: allow to completelly disable sound
[d2df-sdl.git] / src / game / g_weapons.pas
blob2235f83ece92fe6e1d6ddbf617011472091e12f2
1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
16 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
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 i, h: Integer;
781 function PlayerHit(Team: Byte = 0): Boolean;
783 i: Integer;
784 ChkTeam: Boolean;
785 p: TPlayer;
786 begin
787 Result := False;
788 h := High(gPlayers);
790 if h <> -1 then
791 for i := 0 to h do
792 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
793 begin
794 ChkTeam := True;
795 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
796 begin
797 p := g_Player_Get(SpawnerUID);
798 if p <> nil then
799 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
800 end;
801 if ChkTeam then
802 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
803 begin
804 if t <> HIT_FLAME then
805 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
806 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
807 if t = HIT_BFG then
808 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
809 Result := True;
810 break;
811 end;
812 end;
813 end;
816 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
817 begin
818 result := false; // don't stop
819 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
820 begin
821 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
822 begin
823 if (t <> HIT_FLAME) then
824 begin
825 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
826 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
827 end;
828 result := True;
829 end;
830 end;
831 end;
834 function monsCheckHit (mon: TMonster): Boolean;
835 begin
836 result := false; // don't stop
837 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
838 begin
839 if (t <> HIT_FLAME) then
840 begin
841 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
842 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
843 end;
844 result := true;
845 end;
846 end;
848 function MonsterHit(): Boolean;
849 begin
850 //result := g_Mons_ForEach(monsCheckHit);
851 //FIXME: accelerate this!
852 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
853 end;
855 begin
856 Result := 0;
858 if HitCorpses then
859 begin
860 h := High(gCorpses);
862 if gAdvCorpses and (h <> -1) then
863 for i := 0 to h do
864 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
865 g_Obj_Collide(obj, @gCorpses[i].Obj) then
866 begin
867 // Ðàñïèëèâàåì òðóï:
868 gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
869 (obj^.Vel.Y+obj^.Accel.Y) div 4);
870 Result := 1;
871 end;
872 end;
874 case gGameSettings.GameMode of
875 // Êàìïàíèÿ:
876 GM_COOP, GM_SINGLE:
877 begin
878 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
879 if MonsterHit() then
880 begin
881 Result := 2;
882 Exit;
883 end;
885 // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
886 // (èëè ñíàðÿä îò ìîíñòðà, èëè friendlyfire, èëè friendly_hit_projectile)
887 if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or
888 ([TGameOption.TEAM_DAMAGE, TGameOption.TEAM_HIT_PROJECTILE] <= gGameSettings.Options) then
889 begin
890 if PlayerHit() then
891 begin
892 Result := 1;
893 Exit;
894 end;
895 end;
896 end;
898 // Äåçìàò÷:
899 GM_DM:
900 begin
901 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
902 if PlayerHit() then
903 begin
904 Result := 1;
905 Exit;
906 end;
908 if MonsterHit() then
909 begin
910 Result := 2;
911 Exit;
912 end;
913 end;
915 // Êîìàíäíûå:
916 GM_TDM, GM_CTF:
917 begin
918 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
919 if PlayerHit(2) then
920 begin
921 Result := 1;
922 Exit;
923 end;
925 // Ïîòîì ìîíñòðîâ
926 if MonsterHit() then
927 begin
928 Result := 2;
929 Exit;
930 end;
932 // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
933 // (èëè friendlyfire, èëè friendly_hit_projectile)
934 if [TGameOption.TEAM_DAMAGE, TGameOption.TEAM_HIT_PROJECTILE] <= gGameSettings.Options then
935 begin
936 if PlayerHit(1) then
937 begin
938 Result := 1;
939 Exit;
940 end;
941 end;
942 end;
944 end;
945 end;
947 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
948 begin
949 Result := False;
951 case g_GetUIDType(UID) of
952 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
953 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
954 else Exit;
955 end;
956 end;
958 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
960 r: Integer; // squared radius
962 function monsExCheck (mon: TMonster): Boolean;
964 dx, dy, mm: Integer;
965 begin
966 result := false; // don't stop
967 begin
968 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
969 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
971 if dx > 1000 then dx := 1000;
972 if dy > 1000 then dy := 1000;
974 if (dx*dx+dy*dy < r) then
975 begin
976 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
977 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
979 mm := Max(abs(dx), abs(dy));
980 if mm = 0 then mm := 1;
982 if mon.alive then
983 begin
984 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
985 end;
987 mon.Push((dx*7) div mm, (dy*7) div mm);
988 end;
989 end;
990 end;
993 i, h, dx, dy, m, mm: Integer;
994 _angle: SmallInt;
995 begin
996 result := false;
998 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1000 r := rad*rad;
1002 h := High(gPlayers);
1004 if h <> -1 then
1005 for i := 0 to h do
1006 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1007 with gPlayers[i] do
1008 begin
1009 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1010 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1012 if dx > 1000 then dx := 1000;
1013 if dy > 1000 then dy := 1000;
1015 if dx*dx+dy*dy < r then
1016 begin
1017 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1018 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1020 mm := Max(abs(dx), abs(dy));
1021 if mm = 0 then mm := 1;
1023 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1024 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1025 end;
1026 end;
1028 //g_Mons_ForEach(monsExCheck);
1029 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1031 h := High(gCorpses);
1033 if gAdvCorpses and (h <> -1) then
1034 for i := 0 to h do
1035 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1036 with gCorpses[i] do
1037 begin
1038 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1039 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1041 if dx > 1000 then dx := 1000;
1042 if dy > 1000 then dy := 1000;
1044 if dx*dx+dy*dy < r then
1045 begin
1046 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1047 Obj.Rect.Width, Obj.Rect.Height);
1049 mm := Max(abs(dx), abs(dy));
1050 if mm = 0 then mm := 1;
1052 Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
1053 end;
1054 end;
1056 h := High(gGibs);
1058 if gAdvGibs and (h <> -1) then
1059 for i := 0 to h do
1060 if gGibs[i].alive then
1061 with gGibs[i] do
1062 begin
1063 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1064 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1066 if dx > 1000 then dx := 1000;
1067 if dy > 1000 then dy := 1000;
1069 if dx*dx+dy*dy < r then
1070 begin
1071 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1072 Obj.Rect.Width, Obj.Rect.Height);
1073 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1074 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1076 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1077 positionChanged(); // this updates spatial accelerators
1078 end;
1079 end;
1080 end;
1082 procedure g_Weapon_Init();
1083 begin
1084 CreateWaterMap();
1085 end;
1087 procedure g_Weapon_Free();
1089 i: Integer;
1090 begin
1091 for i := 0 to High(Projectiles) do
1092 Projectiles[i].Animation.Free();
1094 Projectiles := nil;
1095 WaterMap := nil;
1096 end;
1098 procedure g_Weapon_LoadData();
1099 begin
1100 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1102 {$IFDEF ENABLE_SOUND}
1103 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1104 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1105 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1106 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1107 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1118 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1119 g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM');
1132 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1133 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1134 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1135 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1136 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1137 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1138 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1139 {$ENDIF}
1141 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1142 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1143 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1144 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1145 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1146 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1147 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1148 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1149 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1150 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1151 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1152 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1153 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1154 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1155 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1156 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1157 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1158 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1159 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1160 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1161 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1163 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1164 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1166 //wgunMonHash := hashNewIntInt();
1167 wgunHitHeap := TBinaryHeapHitTimes.Create();
1168 end;
1170 procedure g_Weapon_FreeData();
1171 begin
1172 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1174 {$IFDEF ENABLE_SOUND}
1175 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1176 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1177 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1178 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1179 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1180 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1181 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1182 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1183 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1184 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1185 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1186 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1187 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1188 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1189 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1190 g_Sound_Delete('SOUND_FIRE');
1191 g_Sound_Delete('SOUND_IGNITE');
1192 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1193 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1194 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1195 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1196 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1197 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1198 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1199 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1200 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1201 g_Sound_Delete('SOUND_WEAPON_FLAMEON');
1202 g_Sound_Delete('SOUND_WEAPON_FLAMEOFF');
1203 g_Sound_Delete('SOUND_WEAPON_FLAMEWORK');
1204 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1205 g_Sound_Delete('SOUND_PLAYER_JETON');
1206 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1207 g_Sound_Delete('SOUND_PLAYER_CASING1');
1208 g_Sound_Delete('SOUND_PLAYER_CASING2');
1209 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1210 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1211 {$ENDIF}
1213 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1214 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1215 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1216 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1217 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1218 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1219 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1220 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1221 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1222 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1223 g_Frames_DeleteByName('FRAMES_BFGHIT');
1224 g_Frames_DeleteByName('FRAMES_FIRE');
1225 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1226 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1227 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1228 g_Frames_DeleteByName('FRAMES_SMOKE');
1229 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1230 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1232 //wgunMonHash.Destroy();
1233 wgunHitHeap.Destroy();
1234 end;
1237 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1239 i: Integer;
1240 begin
1241 result := false;
1242 for i := 0 to High(gPlayers) do
1243 begin
1244 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1245 begin
1246 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1247 begin
1248 if AllowPush then gPlayers[i].Push(vx, vy);
1249 result := true;
1250 end;
1251 end;
1252 end;
1253 end;
1256 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1258 function monsCheck (mon: TMonster): Boolean;
1259 begin
1260 result := false; // don't stop
1261 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1262 begin
1263 if AllowPush then mon.Push(vx, vy);
1264 result := true;
1265 end;
1266 end;
1268 begin
1269 result := 0;
1270 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1271 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1272 end;
1276 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1278 a: Integer;
1279 x2, y2: Integer;
1280 dx, dy: Integer;
1281 xe, ye: Integer;
1282 xi, yi: Integer;
1283 s, c: Extended;
1284 //vx, vy: Integer;
1285 xx, yy, d: Integer;
1286 i: Integer;
1287 t1, _collide: Boolean;
1288 w, h: Word;
1289 {$IF DEFINED(D2F_DEBUG)}
1290 stt: UInt64;
1291 showTime: Boolean = true;
1292 {$ENDIF}
1293 begin
1294 a := GetAngle(x, y, xd, yd)+180;
1296 SinCos(DegToRad(-a), s, c);
1298 if Abs(s) < 0.01 then s := 0;
1299 if Abs(c) < 0.01 then c := 0;
1301 x2 := x+Round(c*gMapInfo.Width);
1302 y2 := y+Round(s*gMapInfo.Width);
1304 t1 := gWalls <> nil;
1305 _collide := False;
1306 w := gMapInfo.Width;
1307 h := gMapInfo.Height;
1309 xe := 0;
1310 ye := 0;
1311 dx := x2-x;
1312 dy := y2-y;
1314 if (xd = 0) and (yd = 0) then Exit;
1316 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1317 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1319 dx := Abs(dx);
1320 dy := Abs(dy);
1322 if dx > dy then d := dx else d := dy;
1324 //blood vel, for Monster.Damage()
1325 //vx := (dx*10 div d)*xi;
1326 //vy := (dy*10 div d)*yi;
1328 {$IF DEFINED(D2F_DEBUG)}
1329 stt := getTimeMicro();
1330 {$ENDIF}
1332 xx := x;
1333 yy := y;
1335 for i := 1 to d do
1336 begin
1337 xe := xe+dx;
1338 ye := ye+dy;
1340 if xe > d then
1341 begin
1342 xe := xe-d;
1343 xx := xx+xi;
1344 end;
1346 if ye > d then
1347 begin
1348 ye := ye-d;
1349 yy := yy+yi;
1350 end;
1352 if (yy > h) or (yy < 0) then Break;
1353 if (xx > w) or (xx < 0) then Break;
1355 if t1 then
1356 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1357 begin
1358 _collide := True;
1359 {$IF DEFINED(D2F_DEBUG)}
1360 stt := getTimeMicro()-stt;
1361 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1362 showTime := false;
1363 {$ENDIF}
1364 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1365 if g_Game_IsServer and g_Game_IsNet then
1366 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1367 end;
1369 if not _collide then
1370 begin
1371 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1372 end;
1374 if _collide then Break;
1375 end;
1377 {$IF DEFINED(D2F_DEBUG)}
1378 if showTime then
1379 begin
1380 stt := getTimeMicro()-stt;
1381 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1382 end;
1383 {$ENDIF}
1385 if CheckTrigger and g_Game_IsServer then
1386 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1387 end;
1391 //!!!FIXME!!!
1392 procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1394 x0, y0: Integer;
1395 x2, y2: Integer;
1396 xi, yi: Integer;
1397 wallDistSq: Integer = $3fffffff;
1398 spawnerPlr: TPlayer = nil;
1399 dmg: Integer;
1401 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1402 begin
1403 result := false;
1404 if (idx < 0) or (idx > High(gPlayers)) then exit;
1405 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1406 if (spawnerPlr <> nil) then
1407 begin
1408 if (not ([TGameOption.TEAM_HIT_TRACE, TGameOption.TEAM_DAMAGE] <= gGameSettings.Options)) and
1409 (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then
1410 begin
1411 if (spawnerPlr <> gPlayers[idx]) and not (TGameOption.TEAM_ABSORB_DAMAGE in gGameSettings.Options) then
1412 dmg := Max(1, dmg div 2);
1413 exit;
1414 end;
1415 end;
1416 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1417 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1418 {$IF DEFINED(D2F_DEBUG)}
1419 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1420 {$ENDIF}
1421 end;
1423 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1424 begin
1425 result := false;
1426 if (mon = nil) then exit;
1427 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1428 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1429 {$IF DEFINED(D2F_DEBUG)}
1430 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1431 {$ENDIF}
1432 end;
1434 // collect players along hitray
1435 // return `true` if instant hit was detected
1436 function playerPossibleHit (): Boolean;
1438 i: Integer;
1439 px, py, pw, ph: Integer;
1440 inx, iny: Integer;
1441 distSq: Integer;
1442 plr: TPlayer;
1443 begin
1444 result := false;
1445 for i := 0 to High(gPlayers) do
1446 begin
1447 plr := gPlayers[i];
1448 if (plr <> nil) and plr.alive then
1449 begin
1450 plr.getMapBox(px, py, pw, ph);
1451 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1452 begin
1453 distSq := distanceSq(x, y, inx, iny);
1454 if (distSq = 0) then
1455 begin
1456 // contains
1457 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1459 else if (distSq < wallDistSq) then
1460 begin
1461 appendHitTimePlr(distSq, i, inx, iny);
1462 end;
1463 end;
1464 end;
1465 end;
1466 end;
1468 procedure sqchecker (mon: TMonster);
1470 mx, my, mw, mh: Integer;
1471 inx, iny: Integer;
1472 distSq: Integer;
1473 begin
1474 mon.getMapBox(mx, my, mw, mh);
1475 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1476 begin
1477 distSq := distanceSq(x0, y0, inx, iny);
1478 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1479 end;
1480 end;
1483 a: Integer;
1484 dx, dy: Integer;
1485 xe, ye: Integer;
1486 s, c: Extended;
1487 i: Integer;
1488 wallHitFlag: Boolean = false;
1489 wallHitX: Integer = 0;
1490 wallHitY: Integer = 0;
1491 didHit: Boolean = false;
1492 {$IF DEFINED(D2F_DEBUG)}
1493 stt: UInt64;
1494 {$ENDIF}
1495 mit: PMonster;
1496 it: TMonsterGrid.Iter;
1497 begin
1499 if not gwep_debug_fast_trace then
1500 begin
1501 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1502 exit;
1503 end;
1506 if (xd = 0) and (yd = 0) then exit;
1508 if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1509 spawnerPlr := g_Player_Get(SpawnerUID);
1511 dmg := indmg;
1513 //wgunMonHash.reset(); //FIXME: clear hash on level change
1514 wgunHitHeap.clear();
1515 wgunHitTimeUsed := 0;
1517 a := GetAngle(x, y, xd, yd)+180;
1519 SinCos(DegToRad(-a), s, c);
1521 if Abs(s) < 0.01 then s := 0;
1522 if Abs(c) < 0.01 then c := 0;
1524 x0 := x;
1525 y0 := y;
1526 x2 := x+Round(c*gMapInfo.Width);
1527 y2 := y+Round(s*gMapInfo.Width);
1529 dx := x2-x;
1530 dy := y2-y;
1532 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1533 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1535 {$IF DEFINED(D2F_DEBUG)}
1536 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1537 stt := getTimeMicro();
1538 {$ENDIF}
1540 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1541 if wallHitFlag then
1542 begin
1543 x2 := wallHitX;
1544 y2 := wallHitY;
1545 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1547 else
1548 begin
1549 wallHitX := x2;
1550 wallHitY := y2;
1551 end;
1553 if playerPossibleHit() then exit; // instant hit
1555 // collect monsters
1556 //g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1558 it := monsGrid.forEachAlongLine(x, y, x2, y2, -1);
1559 for mit in it do sqchecker(mit^);
1560 it.release();
1562 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1563 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1564 while (wgunHitHeap.count > 0) do
1565 begin
1566 // has some entities to check, do it
1567 i := wgunHitHeap.front;
1568 wgunHitHeap.popFront();
1569 // hitpoint
1570 xe := wgunHitTime[i].x;
1571 ye := wgunHitTime[i].y;
1572 // check if it is not behind the wall
1573 if (wgunHitTime[i].mon <> nil) then
1574 begin
1575 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1577 else
1578 begin
1579 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1580 end;
1581 if didHit then
1582 begin
1583 // need new coords for trigger
1584 wallHitX := xe;
1585 wallHitY := ye;
1586 wallHitFlag := false; // no sparks
1587 break;
1588 end;
1589 end;
1591 // need sparks?
1592 if wallHitFlag then
1593 begin
1594 {$IF DEFINED(D2F_DEBUG)}
1595 stt := getTimeMicro()-stt;
1596 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1597 {$ENDIF}
1598 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1599 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1601 else
1602 begin
1603 {$IF DEFINED(D2F_DEBUG)}
1604 stt := getTimeMicro()-stt;
1605 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1606 {$ENDIF}
1607 end;
1609 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1610 end;
1613 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1615 obj: TObj;
1616 begin
1617 obj.X := X;
1618 obj.Y := Y;
1619 obj.rect.X := 0;
1620 obj.rect.Y := 0;
1621 obj.rect.Width := 39;
1622 obj.rect.Height := 52;
1623 obj.Vel.X := 0;
1624 obj.Vel.Y := 0;
1625 obj.Accel.X := 0;
1626 obj.Accel.Y := 0;
1628 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1629 begin
1630 {$IFDEF ENABLE_SOUND}
1631 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1632 {$ENDIF}
1634 else
1635 begin
1636 {$IFDEF ENABLE_SOUND}
1637 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1638 {$ENDIF}
1639 end;
1640 end;
1642 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1644 obj: TObj;
1645 begin
1646 obj.X := X;
1647 obj.Y := Y;
1648 obj.rect.X := 0;
1649 obj.rect.Y := 0;
1650 obj.rect.Width := 32;
1651 obj.rect.Height := 52;
1652 obj.Vel.X := 0;
1653 obj.Vel.Y := 0;
1654 obj.Accel.X := 0;
1655 obj.Accel.Y := 0;
1657 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1658 end;
1660 function g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1661 compat: Boolean): SizeInt;
1663 dx, dy: Integer;
1664 begin
1665 if WID < 0 then
1666 Result := FindProjectileSlot()
1667 else
1668 begin
1669 Result := WID;
1670 if Result >= High(Projectiles) then
1671 SetLength(Projectiles, Result + 64)
1672 end;
1674 with Projectiles[Result] do
1675 begin
1676 g_Obj_Init(@Obj);
1678 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1679 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1681 if compat
1682 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1683 else dx := -(Obj.Rect.Width div 2);
1684 dy := -(Obj.Rect.Height div 2);
1686 ShotType := WEAPON_ROCKETLAUNCHER;
1687 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 12);
1689 Animation := nil;
1690 triggers := nil;
1691 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1692 end;
1694 Projectiles[Result].SpawnerUID := SpawnerUID;
1696 {$IFDEF ENABLE_SOUND}
1697 if not Silent then
1698 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1699 {$ENDIF}
1700 end;
1702 function g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: SizeInt;
1703 Silent: Boolean): SizeInt;
1705 FramesID: DWORD;
1706 dx, dy: Integer;
1707 begin
1708 if WID < 0 then
1709 Result := FindProjectileSlot()
1710 else
1711 begin
1712 Result := WID;
1713 if Result >= High(Projectiles) then
1714 SetLength(Projectiles, Result + 64)
1715 end;
1717 with Projectiles[Result] do
1718 begin
1719 g_Obj_Init(@Obj);
1721 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1722 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1724 dx := -(Obj.Rect.Width div 2);
1725 dy := -(Obj.Rect.Height div 2);
1727 ShotType := WEAPON_SKEL_FIRE;
1728 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 12);
1730 triggers := nil;
1731 target := TargetUID;
1732 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1733 Animation := TAnimation.Create(FramesID, True, 5);
1734 end;
1736 Projectiles[Result].SpawnerUID := SpawnerUID;
1738 {$IFDEF ENABLE_SOUND}
1739 if not Silent then
1740 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1741 {$ENDIF}
1742 end;
1744 function g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1745 compat: Boolean): SizeInt;
1747 FramesID: DWORD;
1748 dx, dy: Integer;
1749 begin
1750 if WID < 0 then
1751 Result := FindProjectileSlot()
1752 else
1753 begin
1754 Result := WID;
1755 if Result >= High(Projectiles) then
1756 SetLength(Projectiles, Result + 64);
1757 end;
1759 with Projectiles[Result] do
1760 begin
1761 g_Obj_Init(@Obj);
1763 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1764 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1766 if compat
1767 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1768 else dx := -(Obj.Rect.Width div 2);
1769 dy := -(Obj.Rect.Height div 2);
1771 ShotType := WEAPON_PLASMA;
1772 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1774 triggers := nil;
1775 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1776 Animation := TAnimation.Create(FramesID, True, 5);
1777 end;
1779 Projectiles[Result].SpawnerUID := SpawnerUID;
1781 {$IFDEF ENABLE_SOUND}
1782 if not Silent then
1783 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1784 {$ENDIF}
1785 end;
1787 function g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1788 compat: Boolean): SizeInt;
1790 dx, dy: Integer;
1791 begin
1792 if WID < 0 then
1793 Result := FindProjectileSlot()
1794 else
1795 begin
1796 Result := WID;
1797 if Result >= High(Projectiles) then
1798 SetLength(Projectiles, Result + 64);
1799 end;
1801 with Projectiles[Result] do
1802 begin
1803 g_Obj_Init(@Obj);
1805 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1806 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1808 if compat
1809 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1810 else dx := -(Obj.Rect.Width div 2);
1811 dy := -(Obj.Rect.Height div 2);
1813 ShotType := WEAPON_FLAMETHROWER;
1814 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1816 triggers := nil;
1817 Animation := nil;
1818 TextureID := 0;
1819 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1820 end;
1822 Projectiles[Result].SpawnerUID := SpawnerUID;
1824 // if not Silent then
1825 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1826 end;
1828 function g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1829 compat: Boolean): SizeInt;
1831 FramesID: DWORD;
1832 dx, dy: Integer;
1833 begin
1834 if WID < 0 then
1835 Result := FindProjectileSlot()
1836 else
1837 begin
1838 Result := WID;
1839 if Result >= High(Projectiles) then
1840 SetLength(Projectiles, Result + 64)
1841 end;
1843 with Projectiles[Result] do
1844 begin
1845 g_Obj_Init(@Obj);
1847 Obj.Rect.Width := 16;
1848 Obj.Rect.Height := 16;
1850 if compat
1851 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1852 else dx := -(Obj.Rect.Width div 2);
1853 dy := -(Obj.Rect.Height div 2);
1855 ShotType := WEAPON_IMP_FIRE;
1856 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1858 triggers := nil;
1859 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1860 Animation := TAnimation.Create(FramesID, True, 4);
1861 end;
1863 Projectiles[Result].SpawnerUID := SpawnerUID;
1865 {$IFDEF ENABLE_SOUND}
1866 if not Silent then
1867 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1868 {$ENDIF}
1869 end;
1871 function g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1872 compat: Boolean): SizeInt;
1874 FramesID: DWORD;
1875 dx, dy: Integer;
1876 begin
1877 if WID < 0 then
1878 Result := FindProjectileSlot()
1879 else
1880 begin
1881 Result := WID;
1882 if Result >= High(Projectiles) then
1883 SetLength(Projectiles, Result + 64)
1884 end;
1886 with Projectiles[Result] do
1887 begin
1888 g_Obj_Init(@Obj);
1890 Obj.Rect.Width := 16;
1891 Obj.Rect.Height := 16;
1893 if compat
1894 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1895 else dx := -(Obj.Rect.Width div 2);
1896 dy := -(Obj.Rect.Height div 2);
1898 ShotType := WEAPON_CACO_FIRE;
1899 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1901 triggers := nil;
1902 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1903 Animation := TAnimation.Create(FramesID, True, 4);
1904 end;
1906 Projectiles[Result].SpawnerUID := SpawnerUID;
1908 {$IFDEF ENABLE_SOUND}
1909 if not Silent then
1910 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1911 {$ENDIF}
1912 end;
1914 function g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1915 compat: Boolean): SizeInt;
1917 FramesID: DWORD;
1918 dx, dy: Integer;
1919 begin
1920 if WID < 0 then
1921 Result := FindProjectileSlot()
1922 else
1923 begin
1924 Result := WID;
1925 if Result >= High(Projectiles) then
1926 SetLength(Projectiles, Result + 64)
1927 end;
1929 with Projectiles[Result] do
1930 begin
1931 g_Obj_Init(@Obj);
1933 Obj.Rect.Width := 16;
1934 Obj.Rect.Height := 16;
1936 if compat
1937 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1938 else dx := -(Obj.Rect.Width div 2);
1939 dy := -(Obj.Rect.Height div 2);
1941 ShotType := WEAPON_BARON_FIRE;
1942 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1944 triggers := nil;
1945 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1946 Animation := TAnimation.Create(FramesID, True, 4);
1947 end;
1949 Projectiles[Result].SpawnerUID := SpawnerUID;
1951 {$IFDEF ENABLE_SOUND}
1952 if not Silent then
1953 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1954 {$ENDIF}
1955 end;
1957 function g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
1958 compat: Boolean): SizeInt;
1960 FramesID: DWORD;
1961 dx, dy: Integer;
1962 begin
1963 if WID < 0 then
1964 Result := FindProjectileSlot()
1965 else
1966 begin
1967 Result := WID;
1968 if Result >= High(Projectiles) then
1969 SetLength(Projectiles, Result + 64)
1970 end;
1972 with Projectiles[Result] do
1973 begin
1974 g_Obj_Init(@Obj);
1976 Obj.Rect.Width := 16;
1977 Obj.Rect.Height := 16;
1979 if compat
1980 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1981 else dx := -(Obj.Rect.Width div 2);
1982 dy := -(Obj.Rect.Height div 2);
1984 ShotType := WEAPON_BSP_FIRE;
1985 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
1987 triggers := nil;
1989 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1990 Animation := TAnimation.Create(FramesID, True, 4);
1991 end;
1993 Projectiles[Result].SpawnerUID := SpawnerUID;
1995 {$IFDEF ENABLE_SOUND}
1996 if not Silent then
1997 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1998 {$ENDIF}
1999 end;
2001 function g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
2002 compat: Boolean): SizeInt;
2004 FramesID: DWORD;
2005 dx, dy: Integer;
2006 begin
2007 if WID < 0 then
2008 Result := FindProjectileSlot()
2009 else
2010 begin
2011 Result := WID;
2012 if Result >= High(Projectiles) then
2013 SetLength(Projectiles, Result + 64)
2014 end;
2016 with Projectiles[Result] do
2017 begin
2018 g_Obj_Init(@Obj);
2020 Obj.Rect.Width := 32;
2021 Obj.Rect.Height := 32;
2023 if compat
2024 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
2025 else dx := -(Obj.Rect.Width div 2);
2026 dy := -(Obj.Rect.Height div 2);
2028 ShotType := WEAPON_MANCUB_FIRE;
2029 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
2031 triggers := nil;
2033 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
2034 Animation := TAnimation.Create(FramesID, True, 4);
2035 end;
2037 Projectiles[Result].SpawnerUID := SpawnerUID;
2039 {$IFDEF ENABLE_SOUND}
2040 if not Silent then
2041 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2042 {$ENDIF}
2043 end;
2045 function g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: SizeInt; Silent: Boolean;
2046 compat: Boolean): SizeInt;
2048 FramesID: DWORD;
2049 dx, dy: Integer;
2050 begin
2051 if WID < 0 then
2052 Result := FindProjectileSlot()
2053 else
2054 begin
2055 Result := WID;
2056 if Result >= High(Projectiles) then
2057 SetLength(Projectiles, Result + 64)
2058 end;
2060 with Projectiles[Result] do
2061 begin
2062 g_Obj_Init(@Obj);
2064 Obj.Rect.Width := SHOT_BFG_WIDTH;
2065 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2067 if compat
2068 then dx := IfThen(xd > x, -Obj.Rect.Width, 0)
2069 else dx := -(Obj.Rect.Width div 2);
2070 dy := -(Obj.Rect.Height div 2);
2072 ShotType := WEAPON_BFG;
2073 throw(Result, x+dx, y+dy, xd+dx, yd+dy, 16);
2075 triggers := nil;
2076 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2077 Animation := TAnimation.Create(FramesID, True, 6);
2078 end;
2080 Projectiles[Result].SpawnerUID := SpawnerUID;
2082 {$IFDEF ENABLE_SOUND}
2083 if not Silent then
2084 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2085 {$ENDIF}
2086 end;
2088 procedure g_Weapon_bfghit(x, y: Integer);
2090 ID: DWORD;
2091 Anim: TAnimation;
2092 begin
2093 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2094 begin
2095 Anim := TAnimation.Create(ID, False, 4);
2096 g_GFX_OnceAnim(x-32, y-32, Anim);
2097 Anim.Destroy();
2098 end;
2099 end;
2101 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2102 begin
2103 {$IFDEF ENABLE_SOUND}
2104 if not Silent then
2105 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2106 {$ENDIF}
2108 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2109 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2110 begin
2111 if ABS(x-xd) >= ABS(y-yd) then
2112 begin
2113 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2114 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2116 else
2117 begin
2118 g_Weapon_gun(x+1, y, xd+1, yd, 1, 3, SpawnerUID, False);
2119 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2120 end;
2121 end;
2122 end;
2124 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2125 begin
2126 {$IFDEF ENABLE_SOUND}
2127 if not Silent then
2128 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2129 {$ENDIF}
2131 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2132 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2133 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2134 begin
2135 if ABS(x-xd) >= ABS(y-yd) then
2136 begin
2137 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2138 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2140 else
2141 begin
2142 g_Weapon_gun(x+1, y, xd+1, yd, 1, 2, SpawnerUID, False);
2143 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2144 end;
2145 end;
2146 end;
2148 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2150 i, j, k: Integer;
2151 begin
2152 {$IFDEF ENABLE_SOUND}
2153 if not Silent then
2154 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2155 {$ENDIF}
2157 for i := 0 to 9 do
2158 begin
2159 j := 0; k := 0;
2160 if ABS(x-xd) >= ABS(y-yd) then j := Random(17) - 8 else k := Random(17) - 8; // -8 .. 8
2161 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2162 end;
2163 end;
2165 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean);
2167 a, i, j, k: Integer;
2168 begin
2169 {$IFDEF ENABLE_SOUND}
2170 if not Silent then
2171 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2172 {$ENDIF}
2174 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]
2175 then a := 25
2176 else a := 20;
2178 for i := 0 to a do
2179 begin
2180 j := 0; k := 0;
2181 if ABS(x-xd) >= ABS(y-yd) then j := Random(41) - 20 else k := Random(41) - 20; // -20 .. 20
2182 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2183 end;
2184 end;
2186 procedure g_Weapon_PreUpdate();
2188 i: Integer;
2189 begin
2190 for i := 0 to High(Projectiles) do
2191 if Projectiles[i].ShotType <> 0 then
2192 begin
2193 Projectiles[i].Obj.oldX := Projectiles[i].Obj.X;
2194 Projectiles[i].Obj.oldY := Projectiles[i].Obj.Y;
2195 end;
2196 end;
2198 procedure g_Weapon_Update();
2200 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2201 _id: DWORD;
2202 Anim: TAnimation;
2203 t: DWArray;
2204 st: Word;
2205 s: String;
2206 o: TObj;
2207 spl: Boolean;
2208 Loud: Boolean;
2209 tcx, tcy: Integer;
2210 label
2211 finish_update; // uhh, FreePascal doesn't have a 'break' for 'case of'
2212 begin
2213 for i := 0 to High(Projectiles) do
2214 begin
2215 if Projectiles[i].ShotType = 0 then
2216 Continue;
2218 Loud := True;
2220 with Projectiles[i] do
2221 begin
2222 Timeout -= 1;
2223 oldvx := Obj.Vel.X;
2224 oldvy := Obj.Vel.Y;
2225 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2226 if (Stopped = 0) and g_Game_IsServer then
2227 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2228 SpawnerUID, ACTIVATE_SHOT, triggers)
2229 else
2230 t := nil;
2232 if t <> nil then
2233 begin
2234 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2235 if triggers = nil then
2236 triggers := t
2237 else
2238 begin
2239 h := High(t);
2241 for a := 0 to h do
2242 if not InDWArray(t[a], triggers) then
2243 begin
2244 SetLength(triggers, Length(triggers)+1);
2245 triggers[High(triggers)] := t[a];
2246 end;
2247 end;
2248 end;
2250 // Àíèìàöèÿ ñíàðÿäà:
2251 if Animation <> nil then
2252 Animation.Update();
2254 // Äâèæåíèå:
2255 spl := not (ShotType in [WEAPON_PLASMA, WEAPON_BFG, WEAPON_BSP_FIRE, WEAPON_FLAMETHROWER]);
2257 if Stopped = 0
2258 then st := g_Obj_Move_Projectile(@Obj, False, spl)
2259 else st := 0;
2260 positionChanged(); // this updates spatial accelerators
2262 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2263 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2264 begin
2265 ShotType := 0;
2266 //Goto finish_update;
2267 FreeAndNil(Animation);
2268 Continue; // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2269 end;
2271 cx := Obj.X + (Obj.Rect.Width div 2);
2272 cy := Obj.Y + (Obj.Rect.Height div 2);
2274 case ShotType of
2275 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2276 begin
2277 // Âûëåòåëà èç âîäû:
2278 if WordBool(st and MOVE_HITAIR) then
2279 g_Obj_SetSpeed(@Obj, 12);
2281 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2282 if WordBool(st and MOVE_INWATER) then
2283 begin
2284 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2286 else if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2287 begin
2288 Anim := TAnimation.Create(_id, False, 3);
2289 Anim.Alpha := 150;
2290 g_GFX_OnceAnim(Obj.X-14+Random(9), cy-20+Random(9),
2291 Anim, ONCEANIM_SMOKE);
2292 Anim.Destroy();
2293 end;
2295 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2296 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2297 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2298 (Timeout < 1) then
2299 begin
2300 Obj.Vel.X := 0;
2301 Obj.Vel.Y := 0;
2303 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2305 if ShotType = WEAPON_SKEL_FIRE then
2306 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2307 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2308 begin
2309 Anim := TAnimation.Create(TextureID, False, 8);
2310 Anim.Blending := False;
2311 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2312 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2313 Anim.Destroy();
2314 end;
2316 else
2317 begin // Âçðûâ Ðàêåòû
2318 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2319 begin
2320 Anim := TAnimation.Create(TextureID, False, 6);
2321 Anim.Blending := False;
2322 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2323 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2324 Anim.Destroy();
2325 end;
2326 end;
2328 {$IFDEF ENABLE_SOUND}
2329 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2330 {$ENDIF}
2332 ShotType := 0;
2333 end;
2335 if ShotType = WEAPON_SKEL_FIRE then
2336 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2337 if GetPos(target, @o) then
2338 throw(i, Obj.X, Obj.Y,
2339 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2340 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2341 12);
2342 end;
2343 end;
2345 WEAPON_PLASMA, WEAPON_BSP_FIRE:
2346 begin
2347 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2348 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2349 begin
2350 {$IFDEF ENABLE_SOUND}
2351 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2352 {$ENDIF}
2353 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2354 ShotType := 0;
2355 Goto finish_update;
2356 end;
2358 // Âåëè÷èíà óðîíà:
2359 if (ShotType = WEAPON_PLASMA) and (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])
2360 then a := 10
2361 else a := 5;
2363 if ShotType = WEAPON_BSP_FIRE then
2364 a := 10;
2366 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2367 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2368 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2369 (Timeout < 1) then
2370 begin
2371 if ShotType = WEAPON_PLASMA
2372 then s := 'FRAMES_EXPLODE_PLASMA'
2373 else s := 'FRAMES_EXPLODE_BSPFIRE';
2375 // Âçðûâ Ïëàçìû:
2376 if g_Frames_Get(TextureID, s) then
2377 begin
2378 Anim := TAnimation.Create(TextureID, False, 3);
2379 Anim.Blending := False;
2380 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2381 Anim.Destroy();
2382 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2383 end;
2385 {$IFDEF ENABLE_SOUND}
2386 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2387 {$ENDIF}
2389 ShotType := 0;
2390 end;
2391 end;
2393 WEAPON_FLAMETHROWER:
2394 begin
2395 // Ñî âðåìåíåì óìèðàåò
2396 if (Timeout < 1) then
2397 begin
2398 ShotType := 0;
2399 Goto finish_update;
2400 end;
2401 // Ïîä âîäîé òîæå
2402 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2403 begin
2404 if WordBool(st and MOVE_HITWATER) then
2405 begin
2406 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2407 begin
2408 Anim := TAnimation.Create(_id, False, 3);
2409 Anim.Alpha := 0;
2410 tcx := Random(8);
2411 tcy := Random(8);
2412 g_GFX_OnceAnim(
2413 cx-4+tcx-(Anim.Width div 2),
2414 cy-4+tcy-(Anim.Height div 2),
2415 Anim, ONCEANIM_SMOKE
2417 Anim.Destroy();
2418 end;
2420 else
2421 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2423 ShotType := 0;
2424 Goto finish_update;
2425 end;
2427 // Ãðàâèòàöèÿ
2428 if Stopped = 0 then
2429 Obj.Accel.Y += 1;
2431 // Ïîïàëè â ñòåíó èëè â âîäó:
2432 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2433 begin
2434 // Ïðèëèïàåì:
2435 Obj.Vel.X := 0;
2436 Obj.Vel.Y := 0;
2437 Obj.Accel.Y := 0;
2438 if WordBool(st and MOVE_HITWALL) then
2439 Stopped := MOVE_HITWALL
2440 else if WordBool(st and MOVE_HITLAND) then
2441 Stopped := MOVE_HITLAND
2442 else if WordBool(st and MOVE_HITCEIL) then
2443 Stopped := MOVE_HITCEIL;
2444 end;
2446 a := IfThen(Stopped = 0, 10, 1);
2447 // Åñëè â êîãî-òî ïîïàëè
2448 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2449 begin
2450 // HIT_FLAME ñàì ïîäîææåò
2451 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2452 if Stopped = 0 then
2453 ShotType := 0;
2454 end;
2456 if Stopped = 0
2457 then tf := 2
2458 else tf := 3;
2460 if (gTime mod LongWord(tf) = 0) then
2461 begin
2462 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2463 Anim.Alpha := 0;
2464 case Stopped of
2465 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2466 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2467 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2468 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2469 end;
2470 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2471 Anim.Destroy();
2472 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2473 end;
2474 end;
2476 WEAPON_BFG:
2477 begin
2478 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2479 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2480 begin
2481 {$IFDEF ENABLE_SOUND}
2482 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2483 {$ENDIF}
2484 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2485 ShotType := 0;
2486 Goto finish_update;
2487 end;
2489 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2490 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2491 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2492 (Timeout < 1) then
2493 begin
2494 // Ëó÷è BFG:
2495 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2497 // Âçðûâ BFG:
2498 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2499 begin
2500 Anim := TAnimation.Create(TextureID, False, 6);
2501 Anim.Blending := False;
2502 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2503 Anim.Destroy();
2504 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2505 end;
2507 {$IFDEF ENABLE_SOUND}
2508 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2509 {$ENDIF}
2511 ShotType := 0;
2512 end;
2513 end;
2515 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE:
2516 begin
2517 // Âûëåòåë èç âîäû:
2518 if WordBool(st and MOVE_HITAIR) then
2519 g_Obj_SetSpeed(@Obj, 16);
2521 // Âåëè÷èíà óðîíà:
2522 if ShotType = WEAPON_IMP_FIRE then
2523 a := 5
2524 else
2525 if ShotType = WEAPON_CACO_FIRE
2526 then a := 20
2527 else a := 40;
2529 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2530 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2531 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2532 (Timeout < 1) then
2533 begin
2534 if ShotType = WEAPON_IMP_FIRE then
2535 s := 'FRAMES_EXPLODE_IMPFIRE'
2536 else
2537 if ShotType = WEAPON_CACO_FIRE
2538 then s := 'FRAMES_EXPLODE_CACOFIRE'
2539 else s := 'FRAMES_EXPLODE_BARONFIRE';
2541 // Âçðûâ:
2542 if g_Frames_Get(TextureID, s) then
2543 begin
2544 Anim := TAnimation.Create(TextureID, False, 6);
2545 Anim.Blending := False;
2546 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2547 Anim.Destroy();
2548 end;
2550 {$IFDEF ENABLE_SOUND}
2551 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2552 {$ENDIF}
2554 ShotType := 0;
2555 end;
2556 end;
2558 WEAPON_MANCUB_FIRE:
2559 begin
2560 // Âûëåòåë èç âîäû:
2561 if WordBool(st and MOVE_HITAIR) then
2562 g_Obj_SetSpeed(@Obj, 16);
2564 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2565 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2566 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2567 (Timeout < 1) then
2568 begin
2569 // Âçðûâ:
2570 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2571 begin
2572 Anim := TAnimation.Create(TextureID, False, 6);
2573 Anim.Blending := False;
2574 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2575 Anim.Destroy();
2576 end;
2578 {$IFDEF ENABLE_SOUND}
2579 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2580 {$ENDIF}
2582 ShotType := 0;
2583 end;
2584 end;
2585 end;
2587 finish_update:
2588 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2589 if ShotType = 0 then
2590 begin
2591 if gGameSettings.GameType = GT_SERVER then
2592 MH_SEND_DeleteProj(i, Obj.X, Obj.Y, Loud);
2593 FreeAndNil(Animation);
2595 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2596 if gGameSettings.GameType = GT_SERVER then
2597 MH_SEND_UpdateProj(i);
2598 end;
2599 end;
2600 end;
2602 procedure g_Weapon_Draw();
2604 i, fX, fY: Integer;
2605 a: SmallInt;
2606 p: TDFPoint;
2607 begin
2608 for i := 0 to High(Projectiles) do
2609 if Projectiles[i].ShotType <> 0 then
2610 with Projectiles[i] do
2611 begin
2612 if (Projectiles[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BARON_FIRE,
2613 WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE])
2614 then a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2615 else a := 0;
2617 Obj.lerp(gLerpFactor, fX, fY);
2618 p.X := Obj.Rect.Width div 2;
2619 p.Y := Obj.Rect.Height div 2;
2621 if Projectiles[i].ShotType = WEAPON_BFG then
2622 begin
2623 fX -= 6;
2624 fY -= 7;
2625 end;
2627 if Animation <> nil then
2628 begin
2629 if Projectiles[i].ShotType in [WEAPON_BARON_FIRE, WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE]
2630 then Animation.DrawEx(fX, fY, TMirrorType.None, p, a)
2631 else Animation.Draw(fX, fY, TMirrorType.None);
2633 else if TextureID <> 0 then
2634 begin
2635 if (Projectiles[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2636 e_DrawAdv(TextureID, fX, fY, 0, True, False, a, @p, TMirrorType.None)
2637 else if (Projectiles[i].ShotType <> WEAPON_FLAMETHROWER) then
2638 e_Draw(TextureID, fX, fY, 0, True, False);
2639 end;
2641 if g_debug_Frames then
2642 begin
2643 e_DrawQuad(Obj.X+Obj.Rect.X,
2644 Obj.Y+Obj.Rect.Y,
2645 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2646 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2647 0, 255, 0);
2648 end;
2649 end;
2650 end;
2652 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2654 a: Integer;
2655 begin
2656 Result := False;
2658 for a := 0 to High(Projectiles) do
2659 if (Projectiles[a].ShotType <> 0) and (Projectiles[a].SpawnerUID <> UID) then
2660 if ((Projectiles[a].Obj.Vel.Y = 0) and (Projectiles[a].Obj.Vel.X > 0) and (Projectiles[a].Obj.X < X)) or
2661 (Projectiles[a].Obj.Vel.Y = 0) and (Projectiles[a].Obj.Vel.X < 0) and (Projectiles[a].Obj.X > X) then
2662 if (Abs(X-Projectiles[a].Obj.X) < Abs(Projectiles[a].Obj.Vel.X*Time)) and
2663 g_Collide(X, Y, Width, Height, X, Projectiles[a].Obj.Y,
2664 Projectiles[a].Obj.Rect.Width, Projectiles[a].Obj.Rect.Height) and
2665 g_TraceVector(X, Y, Projectiles[a].Obj.X, Projectiles[a].Obj.Y) then
2666 begin
2667 Result := True;
2668 Exit;
2669 end;
2670 end;
2672 procedure g_Weapon_SaveState (st: TStream);
2674 count, i, j: Integer;
2675 begin
2676 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2677 count := 0;
2678 for i := 0 to High(Projectiles) do
2679 if Projectiles[i].ShotType <> 0 then count += 1;
2681 // Êîëè÷åñòâî ñíàðÿäîâ
2682 st.WriteDWordLE(count);
2683 if count = 0 then Exit;
2685 for i := 0 to High(Projectiles) do
2686 begin
2687 if Projectiles[i].ShotType <> 0 then
2688 begin
2689 // Ñèãíàòóðà ñíàðÿäà
2690 utils.writeSign(st, 'SHOT');
2691 st.WriteByte(0); // version
2693 st.WriteByte(Projectiles[i].ShotType); // Òèï ñíàðÿäà
2694 st.WriteWordLE(Projectiles[i].Target); // Öåëü
2695 st.WriteWordLE(Projectiles[i].SpawnerUID); // UID ñòðåëÿâøåãî
2696 st.WriteDWordLE(Length(Projectiles[i].Triggers)); // Ðàçìåð ïîëÿ Triggers
2698 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2699 for j := 0 to High(Projectiles[i].Triggers) do
2700 st.WriteDWordLE(Projectiles[i].Triggers[j]);
2702 Obj_SaveState(st, @Projectiles[i].Obj); // Îáúåêò ñíàðÿäà
2703 st.WriteByte(Projectiles[i].Stopped); // Êîñòûëèíà
2704 end;
2705 end;
2706 end;
2708 procedure g_Weapon_LoadState (st: TStream);
2710 count, tc, i, j: SizeUInt;
2711 dw: LongWord;
2712 begin
2713 if st = nil then Exit;
2715 // Êîëè÷åñòâî ñíàðÿäîâ
2716 count := st.ReadDWordLE();
2717 if count > 1024*1024 then
2718 Raise XStreamError.Create('invalid shots counter');
2720 SetLength(Projectiles, count);
2721 if count = 0 then Exit;
2723 for i := 0 to count-1 do
2724 begin
2725 // Ñèãíàòóðà ñíàðÿäà
2726 if not utils.checkSign(st, 'SHOT') then
2727 Raise XStreamError.Create('invalid shot signature');
2728 if st.ReadByte() <> 0 then
2729 Raise XStreamError.Create('invalid shot version');
2731 Projectiles[i].ShotType := st.ReadByte(); // Òèï ñíàðÿäà:
2732 Projectiles[i].Target := st.ReadWordLE(); // Öåëü
2733 Projectiles[i].SpawnerUID := st.ReadWordLE(); // UID ñòðåëÿâøåãî
2735 // Ðàçìåð ïîëÿ Triggers
2736 tc := st.ReadDWordLE();
2737 if tc > 1024*1024 then
2738 Raise XStreamError.Create('invalid shot triggers counter');
2739 SetLength(Projectiles[i].Triggers, tc);
2741 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2742 for j := 0 to tc-1 do
2743 Projectiles[i].Triggers[j] := st.ReadDWordLE();
2745 Obj_LoadState(@Projectiles[i].Obj, st); // Îáúåêò ïðåäìåòà
2746 Projectiles[i].Stopped := st.ReadByte(); // Êîñòûëèíà
2748 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2749 Projectiles[i].TextureID := DWORD(-1);
2750 Projectiles[i].Animation := nil;
2752 case Projectiles[i].ShotType of
2753 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2754 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Projectiles[i].TextureID);
2755 WEAPON_PLASMA:
2756 begin
2757 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2758 Projectiles[i].Animation := TAnimation.Create(dw, True, 5);
2759 end;
2760 WEAPON_BFG:
2761 begin
2762 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2763 Projectiles[i].Animation := TAnimation.Create(dw, True, 6);
2764 end;
2765 WEAPON_IMP_FIRE:
2766 begin
2767 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2768 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2769 end;
2770 WEAPON_BSP_FIRE:
2771 begin
2772 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2773 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2774 end;
2775 WEAPON_CACO_FIRE:
2776 begin
2777 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2778 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2779 end;
2780 WEAPON_BARON_FIRE:
2781 begin
2782 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2783 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2784 end;
2785 WEAPON_MANCUB_FIRE:
2786 begin
2787 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2788 Projectiles[i].Animation := TAnimation.Create(dw, True, 4);
2789 end;
2790 end;
2791 end;
2792 end;
2794 procedure g_Weapon_DestroyProj(I: Integer; X, Y: Integer; Loud: Boolean);
2796 cx, cy: Integer;
2797 Anim: TAnimation;
2798 s: String;
2799 begin
2800 if (Low(Projectiles) > I) or (High(Projectiles) < I) then
2801 Exit;
2803 with Projectiles[I] do
2804 begin
2805 if ShotType = 0 then Exit;
2806 Obj.X := X;
2807 Obj.Y := Y;
2808 cx := Obj.X + (Obj.Rect.Width div 2);
2809 cy := Obj.Y + (Obj.Rect.Height div 2);
2811 case ShotType of
2812 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2813 begin
2814 if Loud then
2815 begin
2816 if ShotType = WEAPON_SKEL_FIRE then
2817 begin
2818 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2819 begin
2820 Anim := TAnimation.Create(TextureID, False, 8);
2821 Anim.Blending := False;
2822 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2823 Anim.Destroy();
2824 end;
2826 else
2827 begin
2828 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2829 begin
2830 Anim := TAnimation.Create(TextureID, False, 6);
2831 Anim.Blending := False;
2832 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2833 Anim.Destroy();
2834 end;
2835 end;
2836 {$IFDEF ENABLE_SOUND}
2837 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2838 {$ENDIF}
2839 end;
2840 end;
2842 WEAPON_PLASMA, WEAPON_BSP_FIRE:
2843 begin
2844 if ShotType = WEAPON_PLASMA
2845 then s := 'FRAMES_EXPLODE_PLASMA'
2846 else s := 'FRAMES_EXPLODE_BSPFIRE';
2848 if g_Frames_Get(TextureID, s) and loud then
2849 begin
2850 Anim := TAnimation.Create(TextureID, False, 3);
2851 Anim.Blending := False;
2852 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2853 Anim.Destroy();
2855 {$IFDEF ENABLE_SOUND}
2856 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2857 {$ENDIF}
2858 end;
2859 end;
2861 WEAPON_BFG:
2862 begin
2863 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2864 begin
2865 Anim := TAnimation.Create(TextureID, False, 6);
2866 Anim.Blending := False;
2867 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2868 Anim.Destroy();
2870 {$IFDEF ENABLE_SOUND}
2871 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2872 {$ENDIF}
2873 end;
2874 end;
2876 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE:
2877 begin
2878 if ShotType = WEAPON_IMP_FIRE then
2879 s := 'FRAMES_EXPLODE_IMPFIRE'
2880 else
2881 if ShotType = WEAPON_CACO_FIRE
2882 then s := 'FRAMES_EXPLODE_CACOFIRE'
2883 else s := 'FRAMES_EXPLODE_BARONFIRE';
2885 if g_Frames_Get(TextureID, s) and Loud then
2886 begin
2887 Anim := TAnimation.Create(TextureID, False, 6);
2888 Anim.Blending := False;
2889 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2890 Anim.Destroy();
2892 {$IFDEF ENABLE_SOUND}
2893 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2894 {$ENDIF}
2895 end;
2896 end;
2898 WEAPON_MANCUB_FIRE:
2899 begin
2900 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2901 begin
2902 Anim := TAnimation.Create(TextureID, False, 6);
2903 Anim.Blending := False;
2904 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2905 Anim.Destroy();
2907 {$IFDEF ENABLE_SOUND}
2908 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2909 {$ENDIF}
2910 end;
2911 end;
2912 end;
2914 ShotType := 0;
2915 FreeAndNil(Animation);
2916 end;
2917 end;
2920 procedure g_Weapon_AddDynLights();
2922 i: Integer;
2923 begin
2924 for i := 0 to High(Projectiles) do
2925 begin
2926 if Projectiles[i].ShotType = 0 then continue;
2927 if Projectiles[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_PLASMA, WEAPON_BFG,
2928 WEAPON_FLAMETHROWER, WEAPON_IMP_FIRE, WEAPON_BSP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE,
2929 WEAPON_MANCUB_FIRE, WEAPON_SKEL_FIRE] then
2930 begin
2931 if (Projectiles[i].ShotType = WEAPON_PLASMA) then
2932 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2933 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 0, 0.3, 1, 0.4)
2934 else if (Projectiles[i].ShotType = WEAPON_BFG) then
2935 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2936 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
2937 else if (Projectiles[i].ShotType = WEAPON_FLAMETHROWER) then
2938 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2939 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
2940 else
2941 g_AddDynLight(Projectiles[i].Obj.X+(Projectiles[i].Obj.Rect.Width div 2),
2942 Projectiles[i].Obj.Y+(Projectiles[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);
2943 end;
2944 end;
2945 end;
2947 procedure TProjectile.positionChanged ();
2948 begin
2949 end;
2951 end.