DEADSOFTWARE

Game: Use proper syntax of sets for game options instead of raw bitwise operations
[d2df-sdl.git] / src / game / g_weapons.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
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.
6 *
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.
11 *
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/>.
14 *)
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 TShot = 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 Shots: array of TShot = nil;
44 LastShotID: Integer = 0;
46 procedure g_Weapon_LoadData();
47 procedure g_Weapon_FreeData();
48 procedure g_Weapon_Init();
49 procedure g_Weapon_Free();
50 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
51 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
52 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
54 procedure g_Weapon_gun(const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
55 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
56 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
57 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
58 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
59 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
60 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
61 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
62 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
63 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
64 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
65 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
66 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False; compat: Boolean = true);
67 procedure g_Weapon_bfghit(x, y: Integer);
68 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
69 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
70 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
71 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
73 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
74 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
75 procedure g_Weapon_PreUpdate();
76 procedure g_Weapon_Update();
77 procedure g_Weapon_Draw();
78 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
79 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
81 procedure g_Weapon_SaveState (st: TStream);
82 procedure g_Weapon_LoadState (st: TStream);
84 procedure g_Weapon_AddDynLights();
86 const
87 WEAPON_IRONFIST = 0;
88 WEAPON_SAW = 1;
89 WEAPON_PISTOL = 2;
90 WEAPON_SHOTGUN1 = 3;
91 WEAPON_SHOTGUN2 = 4;
92 WEAPON_CHAINGUN = 5;
93 WEAPON_ROCKETLAUNCHER = 6;
94 WEAPON_PLASMA = 7;
95 WEAPON_BFG = 8;
96 WEAPON_SUPERCHAINGUN = 9;
97 WEAPON_FLAMETHROWER = 10;
98 WEAPON_ZOMBY_PISTOL = 20;
99 WEAPON_IMP_FIRE = 21;
100 WEAPON_BSP_FIRE = 22;
101 WEAPON_CACO_FIRE = 23;
102 WEAPON_BARON_FIRE = 24;
103 WEAPON_MANCUB_FIRE = 25;
104 WEAPON_SKEL_FIRE = 26;
106 WP_FIRST = WEAPON_IRONFIST;
107 WP_LAST = WEAPON_FLAMETHROWER;
110 var
111 gwep_debug_fast_trace: Boolean = true;
114 implementation
116 uses
117 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
118 g_console, g_options, g_game,
119 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
120 g_language, g_netmsg, g_grid,
121 geom, binheap, hashtable, utils, xstreams;
123 type
124 TWaterPanel = record
125 X, Y: Integer;
126 Width, Height: Word;
127 Active: Boolean;
128 end;
130 const
131 SHOT_ROCKETLAUNCHER_WIDTH = 14;
132 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
134 SHOT_SKELFIRE_WIDTH = 14;
135 SHOT_SKELFIRE_HEIGHT = 14;
137 SHOT_PLASMA_WIDTH = 16;
138 SHOT_PLASMA_HEIGHT = 16;
140 SHOT_BFG_WIDTH = 32;
141 SHOT_BFG_HEIGHT = 32;
142 SHOT_BFG_DAMAGE = 100;
143 SHOT_BFG_RADIUS = 256;
145 SHOT_FLAME_WIDTH = 4;
146 SHOT_FLAME_HEIGHT = 4;
147 SHOT_FLAME_LIFETIME = 180;
149 SHOT_SIGNATURE = $544F4853; // 'SHOT'
151 type
152 PHitTime = ^THitTime;
153 THitTime = record
154 distSq: Integer;
155 mon: TMonster;
156 plridx: Integer; // if mon=nil
157 x, y: Integer;
158 end;
160 TBinHeapKeyHitTime = class
161 public
162 class function less (const a, b: Integer): Boolean; inline;
163 end;
165 // indicies in `wgunHitTime` array
166 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer, TBinHeapKeyHitTime>;
168 var
169 WaterMap: array of array of DWORD = nil;
170 //wgunMonHash: THashIntInt = nil;
171 wgunHitHeap: TBinaryHeapHitTimes = nil;
172 wgunHitTime: array of THitTime = nil;
173 wgunHitTimeUsed: Integer = 0;
176 class function TBinHeapKeyHitTime.less (const a, b: Integer): Boolean;
177 var
178 hta, htb: PHitTime;
179 begin
180 hta := @wgunHitTime[a];
181 htb := @wgunHitTime[b];
182 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
183 if (hta.mon <> nil) then
184 begin
185 // a is monster
186 if (htb.mon = nil) then begin result := false; exit; end; // players first
187 result := (hta.mon.UID < htb.mon.UID); // why not?
188 end
189 else
190 begin
191 // a is player
192 if (htb.mon <> nil) then begin result := true; exit; end; // players first
193 result := (hta.plridx < htb.plridx); // why not?
194 end;
195 end;
198 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
199 begin
200 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
201 with wgunHitTime[wgunHitTimeUsed] do
202 begin
203 distSq := adistSq;
204 mon := amon;
205 plridx := -1;
206 x := ax;
207 y := ay;
208 end;
209 wgunHitHeap.insert(wgunHitTimeUsed);
210 Inc(wgunHitTimeUsed);
211 end;
214 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
215 begin
216 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
217 with wgunHitTime[wgunHitTimeUsed] do
218 begin
219 distSq := adistSq;
220 mon := nil;
221 plridx := aplridx;
222 x := ax;
223 y := ay;
224 end;
225 wgunHitHeap.insert(wgunHitTimeUsed);
226 Inc(wgunHitTimeUsed);
227 end;
230 function FindShot(): DWORD;
231 var
232 i: Integer;
233 begin
234 if Shots <> nil then
235 for i := 0 to High(Shots) do
236 if Shots[i].ShotType = 0 then
237 begin
238 Result := i;
239 LastShotID := Result;
240 Exit;
241 end;
243 if Shots = nil then
244 begin
245 SetLength(Shots, 128);
246 Result := 0;
247 end
248 else
249 begin
250 Result := High(Shots) + 1;
251 SetLength(Shots, Length(Shots) + 128);
252 end;
253 LastShotID := Result;
254 end;
256 procedure CreateWaterMap();
257 var
258 WaterArray: Array of TWaterPanel;
259 a, b, c, m: Integer;
260 ok: Boolean;
261 begin
262 if gWater = nil then
263 Exit;
265 SetLength(WaterArray, Length(gWater));
267 for a := 0 to High(gWater) do
268 begin
269 WaterArray[a].X := gWater[a].X;
270 WaterArray[a].Y := gWater[a].Y;
271 WaterArray[a].Width := gWater[a].Width;
272 WaterArray[a].Height := gWater[a].Height;
273 WaterArray[a].Active := True;
274 end;
276 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
278 for a := 0 to High(WaterArray) do
279 if WaterArray[a].Active then
280 begin
281 WaterArray[a].Active := False;
282 m := Length(WaterMap);
283 SetLength(WaterMap, m+1);
284 SetLength(WaterMap[m], 1);
285 WaterMap[m][0] := a;
286 ok := True;
288 while ok do
289 begin
290 ok := False;
291 for b := 0 to High(WaterArray) do
292 if WaterArray[b].Active then
293 for c := 0 to High(WaterMap[m]) do
294 if g_CollideAround(WaterArray[b].X,
295 WaterArray[b].Y,
296 WaterArray[b].Width,
297 WaterArray[b].Height,
298 WaterArray[WaterMap[m][c]].X,
299 WaterArray[WaterMap[m][c]].Y,
300 WaterArray[WaterMap[m][c]].Width,
301 WaterArray[WaterMap[m][c]].Height) then
302 begin
303 WaterArray[b].Active := False;
304 SetLength(WaterMap[m],
305 Length(WaterMap[m])+1);
306 WaterMap[m][High(WaterMap[m])] := b;
307 ok := True;
308 Break;
309 end;
310 end;
312 g_Game_StepLoading();
313 end;
315 WaterArray := nil;
316 end;
319 var
320 chkTrap_pl: array [0..256] of Integer;
321 chkTrap_mn: array [0..65535] of TMonster;
323 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
324 var
325 //a, b, c, d, i1, i2: Integer;
326 //chkTrap_pl, chkTrap_mn: WArray;
327 plaCount: Integer = 0;
328 mnaCount: Integer = 0;
329 frameId: DWord;
332 function monsWaterCheck (mon: TMonster): Boolean;
333 begin
334 result := false; // don't stop
335 if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
336 begin
337 i2 += 1;
338 chkTrap_mn[i2] := monidx;
339 end;
340 end;
343 function monsWaterCheck (mon: TMonster): Boolean;
344 begin
345 result := false; // don't stop
346 if (mon.trapCheckFrameId <> frameId) then
347 begin
348 mon.trapCheckFrameId := frameId;
349 chkTrap_mn[mnaCount] := mon;
350 Inc(mnaCount);
351 end;
352 end;
354 var
355 a, b, c, d, f: Integer;
356 pan: TPanel;
357 begin
358 if (gWater = nil) or (WaterMap = nil) then Exit;
360 frameId := g_Mons_getNewTrapFrameId();
362 //i1 := -1;
363 //i2 := -1;
365 //SetLength(chkTrap_pl, 1024);
366 //SetLength(chkTrap_mn, 1024);
367 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
368 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
370 for a := 0 to High(WaterMap) do
371 begin
372 for b := 0 to High(WaterMap[a]) do
373 begin
374 pan := gWater[WaterMap[a][b]];
375 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
377 for c := 0 to High(WaterMap[a]) do
378 begin
379 pan := gWater[WaterMap[a][c]];
380 for d := 0 to High(gPlayers) do
381 begin
382 if (gPlayers[d] <> nil) and (gPlayers[d].alive) then
383 begin
384 if gPlayers[d].Collide(pan) then
385 begin
386 f := 0;
387 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
388 if (f = plaCount) then
389 begin
390 chkTrap_pl[plaCount] := d;
391 Inc(plaCount);
392 if (plaCount = Length(chkTrap_pl)) then break;
393 end;
394 end;
395 end;
396 end;
398 //g_Mons_ForEach(monsWaterCheck);
399 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
400 end;
402 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
403 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
404 end;
405 end;
407 //chkTrap_pl := nil;
408 //chkTrap_mn := nil;
409 end;
411 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
412 var
413 tt, mt: Byte;
414 mon: TMonster;
415 begin
416 Result := False;
418 tt := g_GetUIDType(SpawnerUID);
419 if tt = UID_MONSTER then
420 begin
421 mon := g_Monsters_ByUID(SpawnerUID);
422 if mon <> nil then
423 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
424 else
425 mt := 0;
426 end
427 else
428 mt := 0;
430 if m = nil then Exit;
431 if m.UID = SpawnerUID then
432 begin
433 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
434 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
435 Exit;
436 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
437 if (m.MonsterType = MONSTER_CYBER) or
438 (m.MonsterType = MONSTER_BARREL) then
439 begin
440 Result := True;
441 Exit;
442 end;
443 end;
445 if tt = UID_MONSTER then
446 begin
447 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
448 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
449 Exit;
451 // Îáà ìîíñòðà îäíîãî âèäà:
452 if mt = m.MonsterType then
453 case mt of
454 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
455 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
456 Exit; // Ýòè íå áüþò ñâîèõ
457 end;
458 end;
460 if g_Game_IsServer then
461 begin
462 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
463 Result := m.Damage(d, vx, vy, SpawnerUID, t)
464 else
465 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
466 if t = HIT_FLAME then
467 m.CatchFire(SpawnerUID);
468 end
469 else
470 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
471 end;
474 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
475 begin
476 result := False;
478 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
479 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
481 if g_Game_IsServer then
482 begin
483 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
484 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
485 end;
487 result := true;
488 end;
491 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
493 function monsCheck (mon: TMonster): Boolean;
494 begin
495 result := false; // don't stop
496 if (mon.alive) and (mon.UID <> SpawnerUID) then
497 begin
498 with mon do
499 begin
500 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
501 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
502 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
503 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
504 begin
505 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
506 end;
507 end;
508 end;
509 end;
511 var
512 i, h: Integer;
513 st: Byte;
514 pl: TPlayer;
515 b: Boolean;
516 begin
517 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
519 h := High(gCorpses);
521 if gAdvCorpses and (h <> -1) then
522 for i := 0 to h do
523 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
524 with gCorpses[i] do
525 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
526 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
527 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
528 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
529 begin
530 Damage(50, SpawnerUID, 0, 0);
531 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
532 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
533 end;
535 st := TEAM_NONE;
536 pl := g_Player_Get(SpawnerUID);
537 if pl <> nil then
538 st := pl.Team;
540 h := High(gPlayers);
542 if h <> -1 then
543 for i := 0 to h do
544 if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then
545 with gPlayers[i] do
546 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
547 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
548 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
549 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
550 begin
551 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
552 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
553 else
554 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
555 if b then
556 gPlayers[i].BFGHit();
557 end;
559 //FIXME
560 g_Mons_ForEachAlive(monsCheck);
561 end;
563 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
564 var
565 find_id: DWord;
566 FramesID: DWORD = 0;
567 begin
568 if I < 0 then
569 find_id := FindShot()
570 else
571 begin
572 find_id := I;
573 if Integer(find_id) >= High(Shots) then
574 SetLength(Shots, find_id + 64)
575 end;
577 case ShotType of
578 WEAPON_ROCKETLAUNCHER:
579 begin
580 with Shots[find_id] do
581 begin
582 g_Obj_Init(@Obj);
584 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
585 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
587 Animation := nil;
588 Triggers := nil;
589 ShotType := WEAPON_ROCKETLAUNCHER;
590 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
591 end;
592 end;
594 WEAPON_PLASMA:
595 begin
596 with Shots[find_id] do
597 begin
598 g_Obj_Init(@Obj);
600 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
601 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
603 Triggers := nil;
604 ShotType := WEAPON_PLASMA;
605 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
606 Animation := TAnimation.Create(FramesID, True, 5);
607 end;
608 end;
610 WEAPON_BFG:
611 begin
612 with Shots[find_id] do
613 begin
614 g_Obj_Init(@Obj);
616 Obj.Rect.Width := SHOT_BFG_WIDTH;
617 Obj.Rect.Height := SHOT_BFG_HEIGHT;
619 Triggers := nil;
620 ShotType := WEAPON_BFG;
621 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
622 Animation := TAnimation.Create(FramesID, True, 6);
623 end;
624 end;
626 WEAPON_FLAMETHROWER:
627 begin
628 with Shots[find_id] do
629 begin
630 g_Obj_Init(@Obj);
632 Obj.Rect.Width := SHOT_FLAME_WIDTH;
633 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
635 Triggers := nil;
636 ShotType := WEAPON_FLAMETHROWER;
637 Animation := nil;
638 TextureID := 0;
639 g_Frames_Get(TextureID, 'FRAMES_FLAME');
640 end;
641 end;
643 WEAPON_IMP_FIRE:
644 begin
645 with Shots[find_id] do
646 begin
647 g_Obj_Init(@Obj);
649 Obj.Rect.Width := 16;
650 Obj.Rect.Height := 16;
652 Triggers := nil;
653 ShotType := WEAPON_IMP_FIRE;
654 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
655 Animation := TAnimation.Create(FramesID, True, 4);
656 end;
657 end;
659 WEAPON_CACO_FIRE:
660 begin
661 with Shots[find_id] do
662 begin
663 g_Obj_Init(@Obj);
665 Obj.Rect.Width := 16;
666 Obj.Rect.Height := 16;
668 Triggers := nil;
669 ShotType := WEAPON_CACO_FIRE;
670 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
671 Animation := TAnimation.Create(FramesID, True, 4);
672 end;
673 end;
675 WEAPON_MANCUB_FIRE:
676 begin
677 with Shots[find_id] do
678 begin
679 g_Obj_Init(@Obj);
681 Obj.Rect.Width := 32;
682 Obj.Rect.Height := 32;
684 Triggers := nil;
685 ShotType := WEAPON_MANCUB_FIRE;
686 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
687 Animation := TAnimation.Create(FramesID, True, 4);
688 end;
689 end;
691 WEAPON_BARON_FIRE:
692 begin
693 with Shots[find_id] do
694 begin
695 g_Obj_Init(@Obj);
697 Obj.Rect.Width := 16;
698 Obj.Rect.Height := 16;
700 Triggers := nil;
701 ShotType := WEAPON_BARON_FIRE;
702 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
703 Animation := TAnimation.Create(FramesID, True, 4);
704 end;
705 end;
707 WEAPON_BSP_FIRE:
708 begin
709 with Shots[find_id] do
710 begin
711 g_Obj_Init(@Obj);
713 Obj.Rect.Width := 16;
714 Obj.Rect.Height := 16;
716 Triggers := nil;
717 ShotType := WEAPON_BSP_FIRE;
718 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
719 Animation := TAnimation.Create(FramesID, True, 4);
720 end;
721 end;
723 WEAPON_SKEL_FIRE:
724 begin
725 with Shots[find_id] do
726 begin
727 g_Obj_Init(@Obj);
729 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
730 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
732 Triggers := nil;
733 ShotType := WEAPON_SKEL_FIRE;
734 target := TargetUID;
735 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
736 Animation := TAnimation.Create(FramesID, True, 5);
737 end;
738 end;
739 end;
741 Shots[find_id].Obj.oldX := X;
742 Shots[find_id].Obj.oldY := Y;
743 Shots[find_id].Obj.X := X;
744 Shots[find_id].Obj.Y := Y;
745 Shots[find_id].Obj.Vel.X := XV;
746 Shots[find_id].Obj.Vel.Y := YV;
747 Shots[find_id].Obj.Accel.X := 0;
748 Shots[find_id].Obj.Accel.Y := 0;
749 Shots[find_id].SpawnerUID := Spawner;
750 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
751 Shots[find_id].Stopped := 255
752 else
753 Shots[find_id].Stopped := 0;
754 Result := find_id;
755 end;
757 procedure throw(i, x, y, xd, yd, s: Integer);
758 var
759 a: Integer;
760 begin
761 yd := yd - y;
762 xd := xd - x;
764 a := Max(Abs(xd), Abs(yd));
765 if a = 0 then
766 a := 1;
768 Shots[i].Obj.oldX := x;
769 Shots[i].Obj.oldY := y;
770 Shots[i].Obj.X := x;
771 Shots[i].Obj.Y := y;
772 Shots[i].Obj.Vel.X := (xd*s) div a;
773 Shots[i].Obj.Vel.Y := (yd*s) div a;
774 Shots[i].Obj.Accel.X := 0;
775 Shots[i].Obj.Accel.Y := 0;
776 Shots[i].Stopped := 0;
777 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
778 Shots[i].Timeout := 900 // ~25 sec
779 else
780 begin
781 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
782 Shots[i].Timeout := SHOT_FLAME_LIFETIME
783 else
784 Shots[i].Timeout := 550; // ~15 sec
785 end;
786 end;
788 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
789 var
790 i, h: Integer;
792 function PlayerHit(Team: Byte = 0): Boolean;
793 var
794 i: Integer;
795 ChkTeam: Boolean;
796 p: TPlayer;
797 begin
798 Result := False;
799 h := High(gPlayers);
801 if h <> -1 then
802 for i := 0 to h do
803 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
804 begin
805 ChkTeam := True;
806 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
807 begin
808 p := g_Player_Get(SpawnerUID);
809 if p <> nil then
810 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
811 end;
812 if ChkTeam then
813 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
814 begin
815 if t <> HIT_FLAME then
816 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
817 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
818 if t = HIT_BFG then
819 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
820 Result := True;
821 break;
822 end;
823 end;
824 end;
827 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
828 begin
829 result := false; // don't stop
830 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
831 begin
832 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
833 begin
834 if (t <> HIT_FLAME) then
835 begin
836 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
837 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
838 end;
839 result := True;
840 end;
841 end;
842 end;
845 function monsCheckHit (mon: TMonster): Boolean;
846 begin
847 result := false; // don't stop
848 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
849 begin
850 if (t <> HIT_FLAME) then
851 begin
852 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
853 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
854 end;
855 result := true;
856 end;
857 end;
859 function MonsterHit(): Boolean;
860 begin
861 //result := g_Mons_ForEach(monsCheckHit);
862 //FIXME: accelerate this!
863 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
864 end;
866 begin
867 Result := 0;
869 if HitCorpses then
870 begin
871 h := High(gCorpses);
873 if gAdvCorpses and (h <> -1) then
874 for i := 0 to h do
875 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
876 g_Obj_Collide(obj, @gCorpses[i].Obj) then
877 begin
878 // Ðàñïèëèâàåì òðóï:
879 gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
880 (obj^.Vel.Y+obj^.Accel.Y) div 4);
881 Result := 1;
882 end;
883 end;
885 case gGameSettings.GameMode of
886 // Êàìïàíèÿ:
887 GM_COOP, GM_SINGLE:
888 begin
889 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
890 if MonsterHit() then
891 begin
892 Result := 2;
893 Exit;
894 end;
896 // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
897 // (èëè ñíàðÿä îò ìîíñòðà, èëè friendlyfire, èëè friendly_hit_projectile)
898 if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or
899 ([TGameOption.TEAM_DAMAGE, TGameOption.TEAM_HIT_PROJECTILE] <= gGameSettings.Options) then
900 begin
901 if PlayerHit() then
902 begin
903 Result := 1;
904 Exit;
905 end;
906 end;
907 end;
909 // Äåçìàò÷:
910 GM_DM:
911 begin
912 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
913 if PlayerHit() then
914 begin
915 Result := 1;
916 Exit;
917 end;
919 if MonsterHit() then
920 begin
921 Result := 2;
922 Exit;
923 end;
924 end;
926 // Êîìàíäíûå:
927 GM_TDM, GM_CTF:
928 begin
929 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
930 if PlayerHit(2) then
931 begin
932 Result := 1;
933 Exit;
934 end;
936 // Ïîòîì ìîíñòðîâ
937 if MonsterHit() then
938 begin
939 Result := 2;
940 Exit;
941 end;
943 // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
944 // (èëè friendlyfire, èëè friendly_hit_projectile)
945 if [TGameOption.TEAM_DAMAGE, TGameOption.TEAM_HIT_PROJECTILE] <= gGameSettings.Options then
946 begin
947 if PlayerHit(1) then
948 begin
949 Result := 1;
950 Exit;
951 end;
952 end;
953 end;
955 end;
956 end;
958 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
959 begin
960 Result := False;
962 case g_GetUIDType(UID) of
963 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
964 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
965 else Exit;
966 end;
967 end;
969 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
970 var
971 r: Integer; // squared radius
973 function monsExCheck (mon: TMonster): Boolean;
974 var
975 dx, dy, mm: Integer;
976 begin
977 result := false; // don't stop
978 begin
979 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
980 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
982 if dx > 1000 then dx := 1000;
983 if dy > 1000 then dy := 1000;
985 if (dx*dx+dy*dy < r) then
986 begin
987 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
988 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
990 mm := Max(abs(dx), abs(dy));
991 if mm = 0 then mm := 1;
993 if mon.alive then
994 begin
995 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
996 end;
998 mon.Push((dx*7) div mm, (dy*7) div mm);
999 end;
1000 end;
1001 end;
1003 var
1004 i, h, dx, dy, m, mm: Integer;
1005 _angle: SmallInt;
1006 begin
1007 result := false;
1009 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1011 r := rad*rad;
1013 h := High(gPlayers);
1015 if h <> -1 then
1016 for i := 0 to h do
1017 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1018 with gPlayers[i] do
1019 begin
1020 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1021 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1023 if dx > 1000 then dx := 1000;
1024 if dy > 1000 then dy := 1000;
1026 if dx*dx+dy*dy < r then
1027 begin
1028 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1029 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1031 mm := Max(abs(dx), abs(dy));
1032 if mm = 0 then mm := 1;
1034 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1035 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1036 end;
1037 end;
1039 //g_Mons_ForEach(monsExCheck);
1040 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1042 h := High(gCorpses);
1044 if gAdvCorpses and (h <> -1) then
1045 for i := 0 to h do
1046 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1047 with gCorpses[i] do
1048 begin
1049 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1050 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1052 if dx > 1000 then dx := 1000;
1053 if dy > 1000 then dy := 1000;
1055 if dx*dx+dy*dy < r then
1056 begin
1057 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1058 Obj.Rect.Width, Obj.Rect.Height);
1060 mm := Max(abs(dx), abs(dy));
1061 if mm = 0 then mm := 1;
1063 Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
1064 end;
1065 end;
1067 h := High(gGibs);
1069 if gAdvGibs and (h <> -1) then
1070 for i := 0 to h do
1071 if gGibs[i].alive then
1072 with gGibs[i] do
1073 begin
1074 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1075 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1077 if dx > 1000 then dx := 1000;
1078 if dy > 1000 then dy := 1000;
1080 if dx*dx+dy*dy < r then
1081 begin
1082 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1083 Obj.Rect.Width, Obj.Rect.Height);
1084 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1085 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1087 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1088 positionChanged(); // this updates spatial accelerators
1089 end;
1090 end;
1091 end;
1093 procedure g_Weapon_Init();
1094 begin
1095 CreateWaterMap();
1096 end;
1098 procedure g_Weapon_Free();
1099 var
1100 i: Integer;
1101 begin
1102 if Shots <> nil then
1103 begin
1104 for i := 0 to High(Shots) do
1105 if Shots[i].ShotType <> 0 then
1106 Shots[i].Animation.Free();
1108 Shots := nil;
1109 end;
1111 WaterMap := nil;
1112 end;
1114 procedure g_Weapon_LoadData();
1115 begin
1116 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1118 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1132 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1133 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1134 g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE');
1135 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1136 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1137 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1138 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1139 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1140 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1141 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1142 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1143 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1144 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM');
1145 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM');
1146 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM');
1147 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1148 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1149 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1150 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1151 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1152 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1153 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1155 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1156 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1157 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1158 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1159 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1160 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1161 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1162 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1163 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1164 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1165 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1166 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1167 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1168 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1169 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1170 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1171 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1172 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1173 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1174 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1175 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1177 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1178 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1180 //wgunMonHash := hashNewIntInt();
1181 wgunHitHeap := TBinaryHeapHitTimes.Create();
1182 end;
1184 procedure g_Weapon_FreeData();
1185 begin
1186 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1188 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1189 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1190 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1191 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1192 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1193 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1194 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1195 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1196 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1197 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1198 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1199 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1200 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1201 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1202 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1203 g_Sound_Delete('SOUND_FIRE');
1204 g_Sound_Delete('SOUND_IGNITE');
1205 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1206 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1207 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1208 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1209 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1210 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1211 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1212 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1213 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1214 g_Sound_Delete('SOUND_WEAPON_FLAMEON');
1215 g_Sound_Delete('SOUND_WEAPON_FLAMEOFF');
1216 g_Sound_Delete('SOUND_WEAPON_FLAMEWORK');
1217 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1218 g_Sound_Delete('SOUND_PLAYER_JETON');
1219 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1220 g_Sound_Delete('SOUND_PLAYER_CASING1');
1221 g_Sound_Delete('SOUND_PLAYER_CASING2');
1222 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1223 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1225 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1226 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1227 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1228 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1229 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1230 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1231 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1232 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1233 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1234 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1235 g_Frames_DeleteByName('FRAMES_BFGHIT');
1236 g_Frames_DeleteByName('FRAMES_FIRE');
1237 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1238 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1239 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1240 g_Frames_DeleteByName('FRAMES_SMOKE');
1241 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1242 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1243 end;
1246 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1247 var
1248 i: Integer;
1249 begin
1250 result := false;
1251 for i := 0 to High(gPlayers) do
1252 begin
1253 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1254 begin
1255 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1256 begin
1257 if AllowPush then gPlayers[i].Push(vx, vy);
1258 result := true;
1259 end;
1260 end;
1261 end;
1262 end;
1265 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1267 function monsCheck (mon: TMonster): Boolean;
1268 begin
1269 result := false; // don't stop
1270 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1271 begin
1272 if AllowPush then mon.Push(vx, vy);
1273 result := true;
1274 end;
1275 end;
1277 begin
1278 result := 0;
1279 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1280 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1281 end;
1284 (*
1285 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1286 var
1287 a: Integer;
1288 x2, y2: Integer;
1289 dx, dy: Integer;
1290 xe, ye: Integer;
1291 xi, yi: Integer;
1292 s, c: Extended;
1293 //vx, vy: Integer;
1294 xx, yy, d: Integer;
1295 i: Integer;
1296 t1, _collide: Boolean;
1297 w, h: Word;
1298 {$IF DEFINED(D2F_DEBUG)}
1299 stt: UInt64;
1300 showTime: Boolean = true;
1301 {$ENDIF}
1302 begin
1303 a := GetAngle(x, y, xd, yd)+180;
1305 SinCos(DegToRad(-a), s, c);
1307 if Abs(s) < 0.01 then s := 0;
1308 if Abs(c) < 0.01 then c := 0;
1310 x2 := x+Round(c*gMapInfo.Width);
1311 y2 := y+Round(s*gMapInfo.Width);
1313 t1 := gWalls <> nil;
1314 _collide := False;
1315 w := gMapInfo.Width;
1316 h := gMapInfo.Height;
1318 xe := 0;
1319 ye := 0;
1320 dx := x2-x;
1321 dy := y2-y;
1323 if (xd = 0) and (yd = 0) then Exit;
1325 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1326 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1328 dx := Abs(dx);
1329 dy := Abs(dy);
1331 if dx > dy then d := dx else d := dy;
1333 //blood vel, for Monster.Damage()
1334 //vx := (dx*10 div d)*xi;
1335 //vy := (dy*10 div d)*yi;
1337 {$IF DEFINED(D2F_DEBUG)}
1338 stt := getTimeMicro();
1339 {$ENDIF}
1341 xx := x;
1342 yy := y;
1344 for i := 1 to d do
1345 begin
1346 xe := xe+dx;
1347 ye := ye+dy;
1349 if xe > d then
1350 begin
1351 xe := xe-d;
1352 xx := xx+xi;
1353 end;
1355 if ye > d then
1356 begin
1357 ye := ye-d;
1358 yy := yy+yi;
1359 end;
1361 if (yy > h) or (yy < 0) then Break;
1362 if (xx > w) or (xx < 0) then Break;
1364 if t1 then
1365 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1366 begin
1367 _collide := True;
1368 {$IF DEFINED(D2F_DEBUG)}
1369 stt := getTimeMicro()-stt;
1370 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1371 showTime := false;
1372 {$ENDIF}
1373 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1374 if g_Game_IsServer and g_Game_IsNet then
1375 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1376 end;
1378 if not _collide then
1379 begin
1380 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1381 end;
1383 if _collide then Break;
1384 end;
1386 {$IF DEFINED(D2F_DEBUG)}
1387 if showTime then
1388 begin
1389 stt := getTimeMicro()-stt;
1390 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1391 end;
1392 {$ENDIF}
1394 if CheckTrigger and g_Game_IsServer then
1395 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1396 end;
1397 *)
1400 //!!!FIXME!!!
1401 procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1402 var
1403 x0, y0: Integer;
1404 x2, y2: Integer;
1405 xi, yi: Integer;
1406 wallDistSq: Integer = $3fffffff;
1407 spawnerPlr: TPlayer = nil;
1408 dmg: Integer;
1410 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1411 begin
1412 result := false;
1413 if (idx < 0) or (idx > High(gPlayers)) then exit;
1414 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1415 if (spawnerPlr <> nil) then
1416 begin
1417 if (not ([TGameOption.TEAM_HIT_TRACE, TGameOption.TEAM_DAMAGE] <= gGameSettings.Options)) and
1418 (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then
1419 begin
1420 if (spawnerPlr <> gPlayers[idx]) and not (TGameOption.TEAM_ABSORB_DAMAGE in gGameSettings.Options) then
1421 dmg := Max(1, dmg div 2);
1422 exit;
1423 end;
1424 end;
1425 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1426 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1427 {$IF DEFINED(D2F_DEBUG)}
1428 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1429 {$ENDIF}
1430 end;
1432 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1433 begin
1434 result := false;
1435 if (mon = nil) then exit;
1436 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1437 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1438 {$IF DEFINED(D2F_DEBUG)}
1439 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1440 {$ENDIF}
1441 end;
1443 // collect players along hitray
1444 // return `true` if instant hit was detected
1445 function playerPossibleHit (): Boolean;
1446 var
1447 i: Integer;
1448 px, py, pw, ph: Integer;
1449 inx, iny: Integer;
1450 distSq: Integer;
1451 plr: TPlayer;
1452 begin
1453 result := false;
1454 for i := 0 to High(gPlayers) do
1455 begin
1456 plr := gPlayers[i];
1457 if (plr <> nil) and plr.alive then
1458 begin
1459 plr.getMapBox(px, py, pw, ph);
1460 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1461 begin
1462 distSq := distanceSq(x, y, inx, iny);
1463 if (distSq = 0) then
1464 begin
1465 // contains
1466 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1467 end
1468 else if (distSq < wallDistSq) then
1469 begin
1470 appendHitTimePlr(distSq, i, inx, iny);
1471 end;
1472 end;
1473 end;
1474 end;
1475 end;
1477 procedure sqchecker (mon: TMonster);
1478 var
1479 mx, my, mw, mh: Integer;
1480 inx, iny: Integer;
1481 distSq: Integer;
1482 begin
1483 mon.getMapBox(mx, my, mw, mh);
1484 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1485 begin
1486 distSq := distanceSq(x0, y0, inx, iny);
1487 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1488 end;
1489 end;
1491 var
1492 a: Integer;
1493 dx, dy: Integer;
1494 xe, ye: Integer;
1495 s, c: Extended;
1496 i: Integer;
1497 wallHitFlag: Boolean = false;
1498 wallHitX: Integer = 0;
1499 wallHitY: Integer = 0;
1500 didHit: Boolean = false;
1501 {$IF DEFINED(D2F_DEBUG)}
1502 stt: UInt64;
1503 {$ENDIF}
1504 mit: PMonster;
1505 it: TMonsterGrid.Iter;
1506 begin
1507 (*
1508 if not gwep_debug_fast_trace then
1509 begin
1510 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1511 exit;
1512 end;
1513 *)
1515 if (xd = 0) and (yd = 0) then exit;
1517 if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1518 spawnerPlr := g_Player_Get(SpawnerUID);
1520 dmg := indmg;
1522 //wgunMonHash.reset(); //FIXME: clear hash on level change
1523 wgunHitHeap.clear();
1524 wgunHitTimeUsed := 0;
1526 a := GetAngle(x, y, xd, yd)+180;
1528 SinCos(DegToRad(-a), s, c);
1530 if Abs(s) < 0.01 then s := 0;
1531 if Abs(c) < 0.01 then c := 0;
1533 x0 := x;
1534 y0 := y;
1535 x2 := x+Round(c*gMapInfo.Width);
1536 y2 := y+Round(s*gMapInfo.Width);
1538 dx := x2-x;
1539 dy := y2-y;
1541 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1542 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1544 {$IF DEFINED(D2F_DEBUG)}
1545 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1546 stt := getTimeMicro();
1547 {$ENDIF}
1549 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1550 if wallHitFlag then
1551 begin
1552 x2 := wallHitX;
1553 y2 := wallHitY;
1554 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1555 end
1556 else
1557 begin
1558 wallHitX := x2;
1559 wallHitY := y2;
1560 end;
1562 if playerPossibleHit() then exit; // instant hit
1564 // collect monsters
1565 //g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1567 it := monsGrid.forEachAlongLine(x, y, x2, y2, -1);
1568 for mit in it do sqchecker(mit^);
1569 it.release();
1571 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1572 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1573 while (wgunHitHeap.count > 0) do
1574 begin
1575 // has some entities to check, do it
1576 i := wgunHitHeap.front;
1577 wgunHitHeap.popFront();
1578 // hitpoint
1579 xe := wgunHitTime[i].x;
1580 ye := wgunHitTime[i].y;
1581 // check if it is not behind the wall
1582 if (wgunHitTime[i].mon <> nil) then
1583 begin
1584 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1585 end
1586 else
1587 begin
1588 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1589 end;
1590 if didHit then
1591 begin
1592 // need new coords for trigger
1593 wallHitX := xe;
1594 wallHitY := ye;
1595 wallHitFlag := false; // no sparks
1596 break;
1597 end;
1598 end;
1600 // need sparks?
1601 if wallHitFlag then
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 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1608 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1609 end
1610 else
1611 begin
1612 {$IF DEFINED(D2F_DEBUG)}
1613 stt := getTimeMicro()-stt;
1614 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1615 {$ENDIF}
1616 end;
1618 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1619 end;
1622 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1623 var
1624 obj: TObj;
1625 begin
1626 obj.X := X;
1627 obj.Y := Y;
1628 obj.rect.X := 0;
1629 obj.rect.Y := 0;
1630 obj.rect.Width := 39;
1631 obj.rect.Height := 52;
1632 obj.Vel.X := 0;
1633 obj.Vel.Y := 0;
1634 obj.Accel.X := 0;
1635 obj.Accel.Y := 0;
1637 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1638 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1639 else
1640 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1641 end;
1643 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1644 var
1645 obj: TObj;
1646 begin
1647 obj.X := X;
1648 obj.Y := Y;
1649 obj.rect.X := 0;
1650 obj.rect.Y := 0;
1651 obj.rect.Width := 32;
1652 obj.rect.Height := 52;
1653 obj.Vel.X := 0;
1654 obj.Vel.Y := 0;
1655 obj.Accel.X := 0;
1656 obj.Accel.Y := 0;
1658 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1659 end;
1661 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1662 Silent: Boolean = False; compat: Boolean = true);
1663 var
1664 find_id: DWORD;
1665 dx, dy: Integer;
1666 begin
1667 if WID < 0 then
1668 find_id := FindShot()
1669 else
1670 begin
1671 find_id := WID;
1672 if Integer(find_id) >= High(Shots) then
1673 SetLength(Shots, find_id + 64)
1674 end;
1676 with Shots[find_id] do
1677 begin
1678 g_Obj_Init(@Obj);
1680 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1681 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1683 if compat then
1684 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1685 else
1686 dx := -(Obj.Rect.Width div 2);
1687 dy := -(Obj.Rect.Height div 2);
1689 ShotType := WEAPON_ROCKETLAUNCHER;
1690 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1692 Animation := nil;
1693 triggers := nil;
1694 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1695 end;
1697 Shots[find_id].SpawnerUID := SpawnerUID;
1699 if not Silent then
1700 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1701 end;
1703 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1704 WID: Integer = -1; Silent: Boolean = False);
1705 var
1706 find_id, FramesID: DWORD;
1707 dx, dy: Integer;
1708 begin
1709 if WID < 0 then
1710 find_id := FindShot()
1711 else
1712 begin
1713 find_id := WID;
1714 if Integer(find_id) >= High(Shots) then
1715 SetLength(Shots, find_id + 64)
1716 end;
1718 with Shots[find_id] do
1719 begin
1720 g_Obj_Init(@Obj);
1722 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1723 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1725 dx := -(Obj.Rect.Width div 2);
1726 dy := -(Obj.Rect.Height div 2);
1728 ShotType := WEAPON_SKEL_FIRE;
1729 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1731 triggers := nil;
1732 target := TargetUID;
1733 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1734 Animation := TAnimation.Create(FramesID, True, 5);
1735 end;
1737 Shots[find_id].SpawnerUID := SpawnerUID;
1739 if not Silent then
1740 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1741 end;
1743 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1744 Silent: Boolean = False; compat: Boolean = true);
1745 var
1746 find_id, FramesID: DWORD;
1747 dx, dy: Integer;
1748 begin
1749 if WID < 0 then
1750 find_id := FindShot()
1751 else
1752 begin
1753 find_id := WID;
1754 if Integer(find_id) >= High(Shots) then
1755 SetLength(Shots, find_id + 64);
1756 end;
1758 with Shots[find_id] do
1759 begin
1760 g_Obj_Init(@Obj);
1762 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1763 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1765 if compat then
1766 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1767 else
1768 dx := -(Obj.Rect.Width div 2);
1769 dy := -(Obj.Rect.Height div 2);
1771 ShotType := WEAPON_PLASMA;
1772 throw(find_id, 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 Shots[find_id].SpawnerUID := SpawnerUID;
1781 if not Silent then
1782 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1783 end;
1785 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1786 Silent: Boolean = False; compat: Boolean = true);
1787 var
1788 find_id: DWORD;
1789 dx, dy: Integer;
1790 begin
1791 if WID < 0 then
1792 find_id := FindShot()
1793 else
1794 begin
1795 find_id := WID;
1796 if Integer(find_id) >= High(Shots) then
1797 SetLength(Shots, find_id + 64);
1798 end;
1800 with Shots[find_id] do
1801 begin
1802 g_Obj_Init(@Obj);
1804 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1805 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1807 if compat then
1808 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1809 else
1810 dx := -(Obj.Rect.Width div 2);
1811 dy := -(Obj.Rect.Height div 2);
1813 ShotType := WEAPON_FLAMETHROWER;
1814 throw(find_id, 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 Shots[find_id].SpawnerUID := SpawnerUID;
1824 // if not Silent then
1825 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1826 end;
1828 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1829 Silent: Boolean = False; compat: Boolean = true);
1830 var
1831 find_id, FramesID: DWORD;
1832 dx, dy: Integer;
1833 begin
1834 if WID < 0 then
1835 find_id := FindShot()
1836 else
1837 begin
1838 find_id := WID;
1839 if Integer(find_id) >= High(Shots) then
1840 SetLength(Shots, find_id + 64)
1841 end;
1843 with Shots[find_id] do
1844 begin
1845 g_Obj_Init(@Obj);
1847 Obj.Rect.Width := 16;
1848 Obj.Rect.Height := 16;
1850 if compat then
1851 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1852 else
1853 dx := -(Obj.Rect.Width div 2);
1854 dy := -(Obj.Rect.Height div 2);
1856 ShotType := WEAPON_IMP_FIRE;
1857 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1859 triggers := nil;
1860 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1861 Animation := TAnimation.Create(FramesID, True, 4);
1862 end;
1864 Shots[find_id].SpawnerUID := SpawnerUID;
1866 if not Silent then
1867 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1868 end;
1870 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1871 Silent: Boolean = False; compat: Boolean = true);
1872 var
1873 find_id, FramesID: DWORD;
1874 dx, dy: Integer;
1875 begin
1876 if WID < 0 then
1877 find_id := FindShot()
1878 else
1879 begin
1880 find_id := WID;
1881 if Integer(find_id) >= High(Shots) then
1882 SetLength(Shots, find_id + 64)
1883 end;
1885 with Shots[find_id] do
1886 begin
1887 g_Obj_Init(@Obj);
1889 Obj.Rect.Width := 16;
1890 Obj.Rect.Height := 16;
1892 if compat then
1893 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1894 else
1895 dx := -(Obj.Rect.Width div 2);
1896 dy := -(Obj.Rect.Height div 2);
1898 ShotType := WEAPON_CACO_FIRE;
1899 throw(find_id, 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 Shots[find_id].SpawnerUID := SpawnerUID;
1908 if not Silent then
1909 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1910 end;
1912 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1913 Silent: Boolean = False; compat: Boolean = true);
1914 var
1915 find_id, FramesID: DWORD;
1916 dx, dy: Integer;
1917 begin
1918 if WID < 0 then
1919 find_id := FindShot()
1920 else
1921 begin
1922 find_id := WID;
1923 if Integer(find_id) >= High(Shots) then
1924 SetLength(Shots, find_id + 64)
1925 end;
1927 with Shots[find_id] do
1928 begin
1929 g_Obj_Init(@Obj);
1931 Obj.Rect.Width := 16;
1932 Obj.Rect.Height := 16;
1934 if compat then
1935 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1936 else
1937 dx := -(Obj.Rect.Width div 2);
1938 dy := -(Obj.Rect.Height div 2);
1940 ShotType := WEAPON_BARON_FIRE;
1941 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1943 triggers := nil;
1944 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1945 Animation := TAnimation.Create(FramesID, True, 4);
1946 end;
1948 Shots[find_id].SpawnerUID := SpawnerUID;
1950 if not Silent then
1951 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1952 end;
1954 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1955 Silent: Boolean = False; compat: Boolean = true);
1956 var
1957 find_id, FramesID: DWORD;
1958 dx, dy: Integer;
1959 begin
1960 if WID < 0 then
1961 find_id := FindShot()
1962 else
1963 begin
1964 find_id := WID;
1965 if Integer(find_id) >= High(Shots) then
1966 SetLength(Shots, find_id + 64)
1967 end;
1969 with Shots[find_id] do
1970 begin
1971 g_Obj_Init(@Obj);
1973 Obj.Rect.Width := 16;
1974 Obj.Rect.Height := 16;
1976 if compat then
1977 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
1978 else
1979 dx := -(Obj.Rect.Width div 2);
1980 dy := -(Obj.Rect.Height div 2);
1982 ShotType := WEAPON_BSP_FIRE;
1983 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1985 triggers := nil;
1987 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1988 Animation := TAnimation.Create(FramesID, True, 4);
1989 end;
1991 Shots[find_id].SpawnerUID := SpawnerUID;
1993 if not Silent then
1994 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1995 end;
1997 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1998 Silent: Boolean = False; compat: Boolean = true);
1999 var
2000 find_id, FramesID: DWORD;
2001 dx, dy: Integer;
2002 begin
2003 if WID < 0 then
2004 find_id := FindShot()
2005 else
2006 begin
2007 find_id := WID;
2008 if Integer(find_id) >= High(Shots) then
2009 SetLength(Shots, find_id + 64)
2010 end;
2012 with Shots[find_id] do
2013 begin
2014 g_Obj_Init(@Obj);
2016 Obj.Rect.Width := 32;
2017 Obj.Rect.Height := 32;
2019 if compat then
2020 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
2021 else
2022 dx := -(Obj.Rect.Width div 2);
2023 dy := -(Obj.Rect.Height div 2);
2025 ShotType := WEAPON_MANCUB_FIRE;
2026 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2028 triggers := nil;
2030 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
2031 Animation := TAnimation.Create(FramesID, True, 4);
2032 end;
2034 Shots[find_id].SpawnerUID := SpawnerUID;
2036 if not Silent then
2037 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2038 end;
2040 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2041 Silent: Boolean = False; compat: Boolean = true);
2042 var
2043 find_id, FramesID: DWORD;
2044 dx, dy: Integer;
2045 begin
2046 if WID < 0 then
2047 find_id := FindShot()
2048 else
2049 begin
2050 find_id := WID;
2051 if Integer(find_id) >= High(Shots) then
2052 SetLength(Shots, find_id + 64)
2053 end;
2055 with Shots[find_id] do
2056 begin
2057 g_Obj_Init(@Obj);
2059 Obj.Rect.Width := SHOT_BFG_WIDTH;
2060 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2062 if compat then
2063 dx := IfThen(xd > x, -Obj.Rect.Width, 0)
2064 else
2065 dx := -(Obj.Rect.Width div 2);
2066 dy := -(Obj.Rect.Height div 2);
2068 ShotType := WEAPON_BFG;
2069 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2071 triggers := nil;
2072 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2073 Animation := TAnimation.Create(FramesID, True, 6);
2074 end;
2076 Shots[find_id].SpawnerUID := SpawnerUID;
2078 if not Silent then
2079 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2080 end;
2082 procedure g_Weapon_bfghit(x, y: Integer);
2083 var
2084 ID: DWORD;
2085 Anim: TAnimation;
2086 begin
2087 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2088 begin
2089 Anim := TAnimation.Create(ID, False, 4);
2090 g_GFX_OnceAnim(x-32, y-32, Anim);
2091 Anim.Free();
2092 end;
2093 end;
2095 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2096 Silent: Boolean = False);
2097 begin
2098 if not Silent then
2099 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2101 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2102 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2103 begin
2104 if ABS(x-xd) >= ABS(y-yd) then
2105 begin
2106 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2107 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2108 end
2109 else
2110 begin
2111 g_Weapon_gun(x+1, y, xd+1, yd, 1, 3, SpawnerUID, False);
2112 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2113 end;
2114 end;
2115 end;
2117 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2118 Silent: Boolean = False);
2119 begin
2120 if not Silent then
2121 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2123 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2124 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2125 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2126 begin
2127 if ABS(x-xd) >= ABS(y-yd) then
2128 begin
2129 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2130 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2131 end
2132 else
2133 begin
2134 g_Weapon_gun(x+1, y, xd+1, yd, 1, 2, SpawnerUID, False);
2135 g_Weapon_gun(x-1, y, xd-1, yd, 1, 2, SpawnerUID, False);
2136 end;
2137 end;
2138 end;
2140 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2141 Silent: Boolean = False);
2142 var
2143 i, j, k: Integer;
2144 begin
2145 if not Silent then
2146 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2148 for i := 0 to 9 do
2149 begin
2150 j := 0; k := 0;
2151 if ABS(x-xd) >= ABS(y-yd) then j := Random(17) - 8 else k := Random(17) - 8; // -8 .. 8
2152 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2153 end;
2154 end;
2156 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2157 Silent: Boolean = False);
2158 var
2159 a, i, j, k: Integer;
2160 begin
2161 if not Silent then
2162 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2164 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2165 for i := 0 to a do
2166 begin
2167 j := 0; k := 0;
2168 if ABS(x-xd) >= ABS(y-yd) then j := Random(41) - 20 else k := Random(41) - 20; // -20 .. 20
2169 g_Weapon_gun(x+k, y+j, xd+k, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2170 end;
2171 end;
2173 procedure g_Weapon_PreUpdate();
2174 var
2175 i: Integer;
2176 begin
2177 if Shots = nil then Exit;
2178 for i := 0 to High(Shots) do
2179 if Shots[i].ShotType <> 0 then
2180 begin
2181 Shots[i].Obj.oldX := Shots[i].Obj.X;
2182 Shots[i].Obj.oldY := Shots[i].Obj.Y;
2183 end;
2184 end;
2186 procedure g_Weapon_Update();
2187 var
2188 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2189 _id: DWORD;
2190 Anim: TAnimation;
2191 t: DWArray;
2192 st: Word;
2193 s: String;
2194 o: TObj;
2195 spl: Boolean;
2196 Loud: Boolean;
2197 tcx, tcy: Integer;
2198 begin
2199 if Shots = nil then
2200 Exit;
2202 for i := 0 to High(Shots) do
2203 begin
2204 if Shots[i].ShotType = 0 then
2205 Continue;
2207 Loud := True;
2209 with Shots[i] do
2210 begin
2211 Timeout := Timeout - 1;
2212 oldvx := Obj.Vel.X;
2213 oldvy := Obj.Vel.Y;
2214 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2215 if (Stopped = 0) and g_Game_IsServer then
2216 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2217 SpawnerUID, ACTIVATE_SHOT, triggers)
2218 else
2219 t := nil;
2221 if t <> nil then
2222 begin
2223 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2224 if triggers = nil then
2225 triggers := t
2226 else
2227 begin
2228 h := High(t);
2230 for a := 0 to h do
2231 if not InDWArray(t[a], triggers) then
2232 begin
2233 SetLength(triggers, Length(triggers)+1);
2234 triggers[High(triggers)] := t[a];
2235 end;
2236 end;
2237 end;
2239 // Àíèìàöèÿ ñíàðÿäà:
2240 if Animation <> nil then
2241 Animation.Update();
2243 // Äâèæåíèå:
2244 spl := (ShotType <> WEAPON_PLASMA) and
2245 (ShotType <> WEAPON_BFG) and
2246 (ShotType <> WEAPON_BSP_FIRE) and
2247 (ShotType <> WEAPON_FLAMETHROWER);
2249 if Stopped = 0 then
2250 begin
2251 st := g_Obj_Move_Projectile(@Obj, False, spl);
2252 end
2253 else
2254 begin
2255 st := 0;
2256 end;
2257 positionChanged(); // this updates spatial accelerators
2259 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2260 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2261 begin
2262 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2263 ShotType := 0;
2264 Animation.Free();
2265 Continue;
2266 end;
2268 cx := Obj.X + (Obj.Rect.Width div 2);
2269 cy := Obj.Y + (Obj.Rect.Height div 2);
2271 case ShotType of
2272 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2273 begin
2274 // Âûëåòåëà èç âîäû:
2275 if WordBool(st and MOVE_HITAIR) then
2276 g_Obj_SetSpeed(@Obj, 12);
2278 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2279 if WordBool(st and MOVE_INWATER) then
2280 begin
2281 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2282 end
2283 else if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2284 begin
2285 Anim := TAnimation.Create(_id, False, 3);
2286 Anim.Alpha := 150;
2287 g_GFX_OnceAnim(Obj.X-14+Random(9), cy-20+Random(9),
2288 Anim, ONCEANIM_SMOKE);
2289 Anim.Free();
2290 end;
2292 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2293 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2294 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2295 (Timeout < 1) then
2296 begin
2297 Obj.Vel.X := 0;
2298 Obj.Vel.Y := 0;
2300 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2302 if ShotType = WEAPON_SKEL_FIRE then
2303 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2304 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2305 begin
2306 Anim := TAnimation.Create(TextureID, False, 8);
2307 Anim.Blending := False;
2308 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2309 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2310 Anim.Free();
2311 end;
2312 end
2313 else
2314 begin // Âçðûâ Ðàêåòû
2315 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2316 begin
2317 Anim := TAnimation.Create(TextureID, False, 6);
2318 Anim.Blending := False;
2319 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2320 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2321 Anim.Free();
2322 end;
2323 end;
2325 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2327 ShotType := 0;
2328 end;
2330 if ShotType = WEAPON_SKEL_FIRE then
2331 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2332 if GetPos(target, @o) then
2333 throw(i, Obj.X, Obj.Y,
2334 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2335 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2336 12);
2337 end;
2338 end;
2340 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2341 begin
2342 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2343 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2344 begin
2345 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2346 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2347 ShotType := 0;
2348 Continue;
2349 end;
2351 // Âåëè÷èíà óðîíà:
2352 if (ShotType = WEAPON_PLASMA) and
2353 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2354 a := 10
2355 else
2356 a := 5;
2358 if ShotType = WEAPON_BSP_FIRE then
2359 a := 10;
2361 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2362 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2363 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2364 (Timeout < 1) then
2365 begin
2366 if ShotType = WEAPON_PLASMA then
2367 s := 'FRAMES_EXPLODE_PLASMA'
2368 else
2369 s := 'FRAMES_EXPLODE_BSPFIRE';
2371 // Âçðûâ Ïëàçìû:
2372 if g_Frames_Get(TextureID, s) then
2373 begin
2374 Anim := TAnimation.Create(TextureID, False, 3);
2375 Anim.Blending := False;
2376 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2377 Anim.Free();
2378 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2379 end;
2381 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2383 ShotType := 0;
2384 end;
2385 end;
2387 WEAPON_FLAMETHROWER: // Îãíåìåò
2388 begin
2389 // Ñî âðåìåíåì óìèðàåò
2390 if (Timeout < 1) then
2391 begin
2392 ShotType := 0;
2393 Continue;
2394 end;
2395 // Ïîä âîäîé òîæå
2396 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2397 begin
2398 if WordBool(st and MOVE_HITWATER) then
2399 begin
2400 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2401 begin
2402 Anim := TAnimation.Create(_id, False, 3);
2403 Anim.Alpha := 0;
2404 tcx := Random(8);
2405 tcy := Random(8);
2406 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2407 cy-4+tcy-(Anim.Height div 2),
2408 Anim, ONCEANIM_SMOKE);
2409 Anim.Free();
2410 end;
2411 end
2412 else
2413 g_Game_Effect_Bubbles(cx, cy, 1+Random(3), 16, 16);
2415 ShotType := 0;
2416 Continue;
2417 end;
2419 // Ãðàâèòàöèÿ
2420 if Stopped = 0 then
2421 Obj.Accel.Y := Obj.Accel.Y + 1;
2422 // Ïîïàëè â ñòåíó èëè â âîäó:
2423 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2424 begin
2425 // Ïðèëèïàåì:
2426 Obj.Vel.X := 0;
2427 Obj.Vel.Y := 0;
2428 Obj.Accel.Y := 0;
2429 if WordBool(st and MOVE_HITWALL) then
2430 Stopped := MOVE_HITWALL
2431 else if WordBool(st and MOVE_HITLAND) then
2432 Stopped := MOVE_HITLAND
2433 else if WordBool(st and MOVE_HITCEIL) then
2434 Stopped := MOVE_HITCEIL;
2435 end;
2437 a := IfThen(Stopped = 0, 10, 1);
2438 // Åñëè â êîãî-òî ïîïàëè
2439 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2440 begin
2441 // HIT_FLAME ñàì ïîäîææåò
2442 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2443 if Stopped = 0 then
2444 ShotType := 0;
2445 end;
2447 if Stopped = 0 then
2448 tf := 2
2449 else
2450 tf := 3;
2452 if (gTime mod LongWord(tf) = 0) then
2453 begin
2454 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2455 Anim.Alpha := 0;
2456 case Stopped of
2457 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2458 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2459 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2460 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2461 end;
2462 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2463 Anim.Free();
2464 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2465 end;
2466 end;
2468 WEAPON_BFG: // BFG
2469 begin
2470 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2471 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2472 begin
2473 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2474 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2475 ShotType := 0;
2476 Continue;
2477 end;
2479 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2480 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2481 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2482 (Timeout < 1) then
2483 begin
2484 // Ëó÷è BFG:
2485 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2487 // Âçðûâ BFG:
2488 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2489 begin
2490 Anim := TAnimation.Create(TextureID, False, 6);
2491 Anim.Blending := False;
2492 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2493 Anim.Free();
2494 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2495 end;
2497 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2499 ShotType := 0;
2500 end;
2501 end;
2503 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2504 begin
2505 // Âûëåòåë èç âîäû:
2506 if WordBool(st and MOVE_HITAIR) then
2507 g_Obj_SetSpeed(@Obj, 16);
2509 // Âåëè÷èíà óðîíà:
2510 if ShotType = WEAPON_IMP_FIRE then
2511 a := 5
2512 else
2513 if ShotType = WEAPON_CACO_FIRE then
2514 a := 20
2515 else
2516 a := 40;
2518 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2519 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2520 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2521 (Timeout < 1) then
2522 begin
2523 if ShotType = WEAPON_IMP_FIRE then
2524 s := 'FRAMES_EXPLODE_IMPFIRE'
2525 else
2526 if ShotType = WEAPON_CACO_FIRE then
2527 s := 'FRAMES_EXPLODE_CACOFIRE'
2528 else
2529 s := 'FRAMES_EXPLODE_BARONFIRE';
2531 // Âçðûâ:
2532 if g_Frames_Get(TextureID, s) then
2533 begin
2534 Anim := TAnimation.Create(TextureID, False, 6);
2535 Anim.Blending := False;
2536 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2537 Anim.Free();
2538 end;
2540 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2542 ShotType := 0;
2543 end;
2544 end;
2546 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2547 begin
2548 // Âûëåòåë èç âîäû:
2549 if WordBool(st and MOVE_HITAIR) then
2550 g_Obj_SetSpeed(@Obj, 16);
2552 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2553 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2554 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2555 (Timeout < 1) then
2556 begin
2557 // Âçðûâ:
2558 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2559 begin
2560 Anim := TAnimation.Create(TextureID, False, 6);
2561 Anim.Blending := False;
2562 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2563 Anim.Free();
2564 end;
2566 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2568 ShotType := 0;
2569 end;
2570 end;
2571 end; // case ShotType of...
2573 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2574 if (ShotType = 0) then
2575 begin
2576 if gGameSettings.GameType = GT_SERVER then
2577 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2578 if Animation <> nil then
2579 begin
2580 Animation.Free();
2581 Animation := nil;
2582 end;
2583 end
2584 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2585 if gGameSettings.GameType = GT_SERVER then
2586 MH_SEND_UpdateShot(i);
2587 end;
2588 end;
2589 end;
2591 procedure g_Weapon_Draw();
2592 var
2593 i, fX, fY: Integer;
2594 a: SmallInt;
2595 p: TDFPoint;
2596 begin
2597 if Shots = nil then
2598 Exit;
2600 for i := 0 to High(Shots) do
2601 if Shots[i].ShotType <> 0 then
2602 with Shots[i] do
2603 begin
2604 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2605 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2606 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2607 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2608 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2609 else
2610 a := 0;
2612 Obj.lerp(gLerpFactor, fX, fY);
2613 p.X := Obj.Rect.Width div 2;
2614 p.Y := Obj.Rect.Height div 2;
2616 if Shots[i].ShotType = WEAPON_BFG then
2617 begin
2618 DEC(fX, 6);
2619 DEC(fY, 7);
2620 end;
2622 if Animation <> nil then
2623 begin
2624 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2625 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2626 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2627 Animation.DrawEx(fX, fY, TMirrorType.None, p, a)
2628 else
2629 Animation.Draw(fX, fY, TMirrorType.None);
2630 end
2631 else if TextureID <> 0 then
2632 begin
2633 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2634 e_DrawAdv(TextureID, fX, fY, 0, True, False, a, @p, TMirrorType.None)
2635 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2636 e_Draw(TextureID, fX, fY, 0, True, False);
2637 end;
2639 if g_debug_Frames then
2640 begin
2641 e_DrawQuad(Obj.X+Obj.Rect.X,
2642 Obj.Y+Obj.Rect.Y,
2643 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2644 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2645 0, 255, 0);
2646 end;
2647 end;
2648 end;
2650 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2651 var
2652 a: Integer;
2653 begin
2654 Result := False;
2656 if Shots = nil then
2657 Exit;
2659 for a := 0 to High(Shots) do
2660 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2661 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2662 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2663 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2664 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2665 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2666 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2667 begin
2668 Result := True;
2669 Exit;
2670 end;
2671 end;
2673 procedure g_Weapon_SaveState (st: TStream);
2674 var
2675 count, i, j: Integer;
2676 begin
2677 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2678 count := 0;
2679 for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count);
2681 // Êîëè÷åñòâî ñíàðÿäîâ
2682 utils.WriteInt(st, count);
2684 if (count = 0) then exit;
2686 for i := 0 to High(Shots) do
2687 begin
2688 if Shots[i].ShotType <> 0 then
2689 begin
2690 // Ñèãíàòóðà ñíàðÿäà
2691 utils.writeSign(st, 'SHOT');
2692 utils.writeInt(st, Byte(0)); // version
2693 // Òèï ñíàðÿäà
2694 utils.writeInt(st, Byte(Shots[i].ShotType));
2695 // Öåëü
2696 utils.writeInt(st, Word(Shots[i].Target));
2697 // UID ñòðåëÿâøåãî
2698 utils.writeInt(st, Word(Shots[i].SpawnerUID));
2699 // Ðàçìåð ïîëÿ Triggers
2700 utils.writeInt(st, Integer(Length(Shots[i].Triggers)));
2701 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2702 for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j]));
2703 // Îáúåêò ñíàðÿäà
2704 Obj_SaveState(st, @Shots[i].Obj);
2705 // Êîñòûëèíà åáàíàÿ
2706 utils.writeInt(st, Byte(Shots[i].Stopped));
2707 end;
2708 end;
2709 end;
2711 procedure g_Weapon_LoadState (st: TStream);
2712 var
2713 count, tc, i, j: Integer;
2714 dw: LongWord;
2715 begin
2716 if (st = nil) then exit;
2718 // Êîëè÷åñòâî ñíàðÿäîâ
2719 count := utils.readLongInt(st);
2720 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter');
2722 SetLength(Shots, count);
2724 if (count = 0) then exit;
2726 for i := 0 to count-1 do
2727 begin
2728 // Ñèãíàòóðà ñíàðÿäà
2729 if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature');
2730 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version');
2731 // Òèï ñíàðÿäà:
2732 Shots[i].ShotType := utils.readByte(st);
2733 // Öåëü
2734 Shots[i].Target := utils.readWord(st);
2735 // UID ñòðåëÿâøåãî
2736 Shots[i].SpawnerUID := utils.readWord(st);
2737 // Ðàçìåð ïîëÿ Triggers
2738 tc := utils.readLongInt(st);
2739 if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter');
2740 SetLength(Shots[i].Triggers, tc);
2741 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2742 for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st);
2743 // Îáúåêò ïðåäìåòà
2744 Obj_LoadState(@Shots[i].Obj, st);
2745 // Êîñòûëèíà åáàíàÿ
2746 Shots[i].Stopped := utils.readByte(st);
2748 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2749 Shots[i].TextureID := DWORD(-1);
2750 Shots[i].Animation := nil;
2752 case Shots[i].ShotType of
2753 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2754 begin
2755 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2756 end;
2757 WEAPON_PLASMA:
2758 begin
2759 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2760 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2761 end;
2762 WEAPON_BFG:
2763 begin
2764 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2765 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2766 end;
2767 WEAPON_IMP_FIRE:
2768 begin
2769 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2770 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2771 end;
2772 WEAPON_BSP_FIRE:
2773 begin
2774 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2775 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2776 end;
2777 WEAPON_CACO_FIRE:
2778 begin
2779 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2780 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2781 end;
2782 WEAPON_BARON_FIRE:
2783 begin
2784 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2785 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2786 end;
2787 WEAPON_MANCUB_FIRE:
2788 begin
2789 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2790 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2791 end;
2792 end;
2793 end;
2794 end;
2796 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2797 var
2798 cx, cy: Integer;
2799 Anim: TAnimation;
2800 s: string;
2801 begin
2802 if Shots = nil then
2803 Exit;
2804 if (I > High(Shots)) or (I < 0) then Exit;
2806 with Shots[I] do
2807 begin
2808 if ShotType = 0 then Exit;
2809 Obj.X := X;
2810 Obj.Y := Y;
2811 cx := Obj.X + (Obj.Rect.Width div 2);
2812 cy := Obj.Y + (Obj.Rect.Height div 2);
2814 case ShotType of
2815 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2816 begin
2817 if Loud then
2818 begin
2819 if ShotType = WEAPON_SKEL_FIRE then
2820 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2821 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2822 begin
2823 Anim := TAnimation.Create(TextureID, False, 8);
2824 Anim.Blending := False;
2825 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2826 Anim.Free();
2827 end;
2828 end
2829 else
2830 begin // Âçðûâ Ðàêåòû
2831 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2832 begin
2833 Anim := TAnimation.Create(TextureID, False, 6);
2834 Anim.Blending := False;
2835 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2836 Anim.Free();
2837 end;
2838 end;
2839 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2840 end;
2841 end;
2843 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2844 begin
2845 if ShotType = WEAPON_PLASMA then
2846 s := 'FRAMES_EXPLODE_PLASMA'
2847 else
2848 s := 'FRAMES_EXPLODE_BSPFIRE';
2850 if g_Frames_Get(TextureID, s) and loud then
2851 begin
2852 Anim := TAnimation.Create(TextureID, False, 3);
2853 Anim.Blending := False;
2854 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2855 Anim.Free();
2857 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2858 end;
2859 end;
2861 WEAPON_BFG: // BFG
2862 begin
2863 // Âçðûâ BFG:
2864 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2865 begin
2866 Anim := TAnimation.Create(TextureID, False, 6);
2867 Anim.Blending := False;
2868 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2869 Anim.Free();
2871 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2872 end;
2873 end;
2875 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2876 begin
2877 if ShotType = WEAPON_IMP_FIRE then
2878 s := 'FRAMES_EXPLODE_IMPFIRE'
2879 else
2880 if ShotType = WEAPON_CACO_FIRE then
2881 s := 'FRAMES_EXPLODE_CACOFIRE'
2882 else
2883 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.Free();
2892 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2893 end;
2894 end;
2896 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2897 begin
2898 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2899 begin
2900 Anim := TAnimation.Create(TextureID, False, 6);
2901 Anim.Blending := False;
2902 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2903 Anim.Free();
2905 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2906 end;
2907 end;
2908 end; // case ShotType of...
2910 ShotType := 0;
2911 Animation.Free();
2912 end;
2913 end;
2916 procedure g_Weapon_AddDynLights();
2917 var
2918 i: Integer;
2919 begin
2920 if Shots = nil then Exit;
2921 for i := 0 to High(Shots) do
2922 begin
2923 if Shots[i].ShotType = 0 then continue;
2924 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2925 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2926 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2927 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2928 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2929 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2930 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2931 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2932 (Shots[i].ShotType = WEAPON_PLASMA) or
2933 (Shots[i].ShotType = WEAPON_BFG) or
2934 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2935 false then
2936 begin
2937 if (Shots[i].ShotType = WEAPON_PLASMA) then
2938 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 0.3, 1, 0.4)
2939 else if (Shots[i].ShotType = WEAPON_BFG) then
2940 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
2941 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2942 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
2943 else
2944 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);
2945 end;
2946 end;
2947 end;
2950 procedure TShot.positionChanged (); begin end;
2953 end.