DEADSOFTWARE

new tree-based weapon hitscan tracer (sometimes it is faster than the old one, someti...
[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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
18 unit g_weapons;
20 interface
22 uses
23 g_textures, g_basic, e_graphics, g_phys, BinEditor, xprofiler;
25 const
26 HIT_SOME = 0;
27 HIT_ROCKET = 1;
28 HIT_BFG = 2;
29 HIT_TRAP = 3;
30 HIT_FALL = 4;
31 HIT_WATER = 5;
32 HIT_ACID = 6;
33 HIT_ELECTRO = 7;
34 HIT_FLAME = 8;
35 HIT_SELF = 9;
36 HIT_DISCON = 10;
38 type
39 TShot = record
40 ShotType: Byte;
41 Target: Word;
42 SpawnerUID: Word;
43 Triggers: DWArray;
44 Obj: TObj;
45 Animation: TAnimation;
46 TextureID: DWORD;
47 Timeout: DWORD;
48 Stopped: Byte;
50 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
51 end;
54 var
55 Shots: array of TShot = nil;
56 LastShotID: Integer = 0;
58 procedure g_Weapon_LoadData();
59 procedure g_Weapon_FreeData();
60 procedure g_Weapon_Init();
61 procedure g_Weapon_Free();
62 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
63 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
64 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
66 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
67 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
68 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
69 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
70 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
71 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
74 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
75 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
76 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
77 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
78 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
79 procedure g_Weapon_bfghit(x, y: Integer);
80 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
81 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
82 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
83 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
85 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
86 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
87 procedure g_Weapon_Update();
88 procedure g_Weapon_Draw();
89 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
90 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
92 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
93 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
95 procedure g_Weapon_AddDynLights();
97 const
98 WEAPON_KASTET = 0;
99 WEAPON_SAW = 1;
100 WEAPON_PISTOL = 2;
101 WEAPON_SHOTGUN1 = 3;
102 WEAPON_SHOTGUN2 = 4;
103 WEAPON_CHAINGUN = 5;
104 WEAPON_ROCKETLAUNCHER = 6;
105 WEAPON_PLASMA = 7;
106 WEAPON_BFG = 8;
107 WEAPON_SUPERPULEMET = 9;
108 WEAPON_FLAMETHROWER = 10;
109 WEAPON_ZOMBY_PISTOL = 20;
110 WEAPON_IMP_FIRE = 21;
111 WEAPON_BSP_FIRE = 22;
112 WEAPON_CACO_FIRE = 23;
113 WEAPON_BARON_FIRE = 24;
114 WEAPON_MANCUB_FIRE = 25;
115 WEAPON_SKEL_FIRE = 26;
117 WP_FIRST = WEAPON_KASTET;
118 WP_LAST = WEAPON_FLAMETHROWER;
121 var
122 gwep_debug_fast_trace: Boolean = true;
125 implementation
127 uses
128 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
129 g_console, SysUtils, g_options, g_game,
130 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
131 g_language, g_netmsg,
132 z_aabbtree, binheap, hashtable;
134 type
135 TWaterPanel = record
136 X, Y: Integer;
137 Width, Height: Word;
138 Active: Boolean;
139 end;
141 const
142 SHOT_ROCKETLAUNCHER_WIDTH = 14;
143 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
145 SHOT_SKELFIRE_WIDTH = 14;
146 SHOT_SKELFIRE_HEIGHT = 14;
148 SHOT_PLASMA_WIDTH = 16;
149 SHOT_PLASMA_HEIGHT = 16;
151 SHOT_BFG_WIDTH = 32;
152 SHOT_BFG_HEIGHT = 32;
153 SHOT_BFG_DAMAGE = 100;
154 SHOT_BFG_RADIUS = 256;
156 SHOT_FLAME_WIDTH = 4;
157 SHOT_FLAME_HEIGHT = 4;
158 SHOT_FLAME_LIFETIME = 180;
160 SHOT_SIGNATURE = $544F4853; // 'SHOT'
162 type
163 PHitTime = ^THitTime;
164 THitTime = record
165 time: Single;
166 mon: TMonster;
167 plridx: Integer; // if mon=nil
168 end;
170 // indicies in `wgunHitTime` array
171 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer>;
173 var
174 WaterMap: array of array of DWORD = nil;
175 wgunMonHash: THashIntInt = nil;
176 wgunHitHeap: TBinaryHeapHitTimes = nil;
177 wgunHitTime: array of THitTime = nil;
178 wgunHitTimeUsed: Integer = 0;
181 function hitTimeCompare (a, b: Integer): Boolean;
182 begin
183 if (wgunHitTime[a].time < wgunHitTime[b].time) then begin result := true; exit; end;
184 if (wgunHitTime[a].time > wgunHitTime[b].time) then begin result := false; exit; end;
185 if (wgunHitTime[a].mon <> nil) then
186 begin
187 // a is monster
188 if (wgunHitTime[b].mon = nil) then begin result := false; exit; end; // players first
189 result := (wgunHitTime[a].mon.UID < wgunHitTime[b].mon.UID); // why not?
190 end
191 else
192 begin
193 // a is player
194 if (wgunHitTime[b].mon <> nil) then begin result := true; exit; end; // players first
195 result := (wgunHitTime[a].plridx < wgunHitTime[b].plridx); // why not?
196 end;
197 end;
200 procedure appendHitTimeMon (time: Single; mon: TMonster);
201 begin
202 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
203 wgunHitTime[wgunHitTimeUsed].time := time;
204 wgunHitTime[wgunHitTimeUsed].mon := mon;
205 wgunHitTime[wgunHitTimeUsed].plridx := -1;
206 wgunHitHeap.insert(wgunHitTimeUsed);
207 Inc(wgunHitTimeUsed);
208 end;
211 procedure appendHitTimePlr (time: Single; plridx: Integer);
212 begin
213 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
214 wgunHitTime[wgunHitTimeUsed].time := time;
215 wgunHitTime[wgunHitTimeUsed].mon := nil;
216 wgunHitTime[wgunHitTimeUsed].plridx := plridx;
217 wgunHitHeap.insert(wgunHitTimeUsed);
218 Inc(wgunHitTimeUsed);
219 end;
222 function FindShot(): DWORD;
223 var
224 i: Integer;
225 begin
226 if Shots <> nil then
227 for i := 0 to High(Shots) do
228 if Shots[i].ShotType = 0 then
229 begin
230 Result := i;
231 LastShotID := Result;
232 Exit;
233 end;
235 if Shots = nil then
236 begin
237 SetLength(Shots, 128);
238 Result := 0;
239 end
240 else
241 begin
242 Result := High(Shots) + 1;
243 SetLength(Shots, Length(Shots) + 128);
244 end;
245 LastShotID := Result;
246 end;
248 procedure CreateWaterMap();
249 var
250 WaterArray: Array of TWaterPanel;
251 a, b, c, m: Integer;
252 ok: Boolean;
253 begin
254 if gWater = nil then
255 Exit;
257 SetLength(WaterArray, Length(gWater));
259 for a := 0 to High(gWater) do
260 begin
261 WaterArray[a].X := gWater[a].X;
262 WaterArray[a].Y := gWater[a].Y;
263 WaterArray[a].Width := gWater[a].Width;
264 WaterArray[a].Height := gWater[a].Height;
265 WaterArray[a].Active := True;
266 end;
268 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
270 for a := 0 to High(WaterArray) do
271 if WaterArray[a].Active then
272 begin
273 WaterArray[a].Active := False;
274 m := Length(WaterMap);
275 SetLength(WaterMap, m+1);
276 SetLength(WaterMap[m], 1);
277 WaterMap[m][0] := a;
278 ok := True;
280 while ok do
281 begin
282 ok := False;
283 for b := 0 to High(WaterArray) do
284 if WaterArray[b].Active then
285 for c := 0 to High(WaterMap[m]) do
286 if g_CollideAround(WaterArray[b].X,
287 WaterArray[b].Y,
288 WaterArray[b].Width,
289 WaterArray[b].Height,
290 WaterArray[WaterMap[m][c]].X,
291 WaterArray[WaterMap[m][c]].Y,
292 WaterArray[WaterMap[m][c]].Width,
293 WaterArray[WaterMap[m][c]].Height) then
294 begin
295 WaterArray[b].Active := False;
296 SetLength(WaterMap[m],
297 Length(WaterMap[m])+1);
298 WaterMap[m][High(WaterMap[m])] := b;
299 ok := True;
300 Break;
301 end;
302 end;
304 g_Game_StepLoading();
305 end;
307 WaterArray := nil;
308 end;
311 var
312 chkTrap_pl: array [0..256] of Integer;
313 chkTrap_mn: array [0..65535] of TMonster;
315 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
316 var
317 //a, b, c, d, i1, i2: Integer;
318 //chkTrap_pl, chkTrap_mn: WArray;
319 plaCount: Integer = 0;
320 mnaCount: Integer = 0;
321 frameId: DWord;
324 function monsWaterCheck (mon: TMonster): Boolean;
325 begin
326 result := false; // don't stop
327 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
328 begin
329 i2 += 1;
330 chkTrap_mn[i2] := monidx;
331 end;
332 end;
335 function monsWaterCheck (mon: TMonster): Boolean;
336 begin
337 result := false; // don't stop
338 if (mon.trapCheckFrameId <> frameId) then
339 begin
340 mon.trapCheckFrameId := frameId;
341 chkTrap_mn[mnaCount] := mon;
342 Inc(mnaCount);
343 end;
344 end;
346 var
347 a, b, c, d, f: Integer;
348 pan: TPanel;
349 begin
350 if (gWater = nil) or (WaterMap = nil) then Exit;
352 frameId := g_Mons_getNewTrapFrameId();
354 //i1 := -1;
355 //i2 := -1;
357 //SetLength(chkTrap_pl, 1024);
358 //SetLength(chkTrap_mn, 1024);
359 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
360 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
362 for a := 0 to High(WaterMap) do
363 begin
364 for b := 0 to High(WaterMap[a]) do
365 begin
366 pan := gWater[WaterMap[a][b]];
367 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
369 for c := 0 to High(WaterMap[a]) do
370 begin
371 pan := gWater[WaterMap[a][c]];
372 for d := 0 to High(gPlayers) do
373 begin
374 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
375 begin
376 if gPlayers[d].Collide(pan) then
377 begin
378 f := 0;
379 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
380 if (f = plaCount) then
381 begin
382 chkTrap_pl[plaCount] := d;
383 Inc(plaCount);
384 if (plaCount = Length(chkTrap_pl)) then break;
385 end;
386 end;
387 end;
388 end;
390 //g_Mons_ForEach(monsWaterCheck);
391 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
392 end;
394 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
395 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
396 end;
397 end;
399 //chkTrap_pl := nil;
400 //chkTrap_mn := nil;
401 end;
403 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
404 var
405 tt, mt: Byte;
406 mon: TMonster;
407 begin
408 Result := False;
410 tt := g_GetUIDType(SpawnerUID);
411 if tt = UID_MONSTER then
412 begin
413 mon := g_Monsters_ByUID(SpawnerUID);
414 if mon <> nil then
415 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
416 else
417 mt := 0;
418 end
419 else
420 mt := 0;
422 if m = nil then Exit;
423 if m.UID = SpawnerUID then
424 begin
425 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
426 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
427 Exit;
428 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
429 if (m.MonsterType = MONSTER_CYBER) or
430 (m.MonsterType = MONSTER_BARREL) then
431 begin
432 Result := True;
433 Exit;
434 end;
435 end;
437 if tt = UID_MONSTER then
438 begin
439 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
440 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
441 Exit;
443 // Îáà ìîíñòðà îäíîãî âèäà:
444 if mt = m.MonsterType then
445 case mt of
446 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
447 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
448 Exit; // Ýòè íå áüþò ñâîèõ
449 end;
450 end;
452 if g_Game_IsServer then
453 begin
454 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
455 Result := m.Damage(d, vx, vy, SpawnerUID, t)
456 else
457 Result := True;
458 if t = HIT_FLAME then
459 m.CatchFire(SpawnerUID);
460 end
461 else
462 Result := True;
463 end;
466 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
467 begin
468 result := False;
470 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
471 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
473 if g_Game_IsServer then
474 begin
475 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
476 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
477 end;
479 result := true;
480 end;
483 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
485 function monsCheck (mon: TMonster): Boolean;
486 begin
487 result := false; // don't stop
488 if (mon.Live) and (mon.UID <> SpawnerUID) then
489 begin
490 with mon do
491 begin
492 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
493 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
494 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
495 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
496 begin
497 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
498 end;
499 end;
500 end;
501 end;
503 var
504 i, h: Integer;
505 st: Byte;
506 pl: TPlayer;
507 b: Boolean;
508 begin
509 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
511 h := High(gCorpses);
513 if gAdvCorpses and (h <> -1) then
514 for i := 0 to h do
515 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
516 with gCorpses[i] do
517 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
518 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
519 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
520 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
521 begin
522 Damage(50, 0, 0);
523 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
524 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
525 end;
527 st := TEAM_NONE;
528 pl := g_Player_Get(SpawnerUID);
529 if pl <> nil then
530 st := pl.Team;
532 h := High(gPlayers);
534 if h <> -1 then
535 for i := 0 to h do
536 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
537 with gPlayers[i] do
538 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
539 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
540 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
541 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
542 begin
543 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
544 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
545 else
546 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
547 if b then
548 gPlayers[i].BFGHit();
549 end;
551 //FIXME
552 g_Mons_ForEachAlive(monsCheck);
553 end;
555 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
556 var
557 find_id, FramesID: DWORD;
558 begin
559 if I < 0 then
560 find_id := FindShot()
561 else
562 begin
563 find_id := I;
564 if Integer(find_id) >= High(Shots) then
565 SetLength(Shots, find_id + 64)
566 end;
568 case ShotType of
569 WEAPON_ROCKETLAUNCHER:
570 begin
571 with Shots[find_id] do
572 begin
573 g_Obj_Init(@Obj);
575 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
576 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
578 Animation := nil;
579 Triggers := nil;
580 ShotType := WEAPON_ROCKETLAUNCHER;
581 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
582 end;
583 end;
585 WEAPON_PLASMA:
586 begin
587 with Shots[find_id] do
588 begin
589 g_Obj_Init(@Obj);
591 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
592 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
594 Triggers := nil;
595 ShotType := WEAPON_PLASMA;
596 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
597 Animation := TAnimation.Create(FramesID, True, 5);
598 end;
599 end;
601 WEAPON_BFG:
602 begin
603 with Shots[find_id] do
604 begin
605 g_Obj_Init(@Obj);
607 Obj.Rect.Width := SHOT_BFG_WIDTH;
608 Obj.Rect.Height := SHOT_BFG_HEIGHT;
610 Triggers := nil;
611 ShotType := WEAPON_BFG;
612 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
613 Animation := TAnimation.Create(FramesID, True, 6);
614 end;
615 end;
617 WEAPON_FLAMETHROWER:
618 begin
619 with Shots[find_id] do
620 begin
621 g_Obj_Init(@Obj);
623 Obj.Rect.Width := SHOT_FLAME_WIDTH;
624 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
626 Triggers := nil;
627 ShotType := WEAPON_FLAMETHROWER;
628 Animation := nil;
629 TextureID := 0;
630 g_Frames_Get(TextureID, 'FRAMES_FLAME');
631 end;
632 end;
634 WEAPON_IMP_FIRE:
635 begin
636 with Shots[find_id] do
637 begin
638 g_Obj_Init(@Obj);
640 Obj.Rect.Width := 16;
641 Obj.Rect.Height := 16;
643 Triggers := nil;
644 ShotType := WEAPON_IMP_FIRE;
645 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
646 Animation := TAnimation.Create(FramesID, True, 4);
647 end;
648 end;
650 WEAPON_CACO_FIRE:
651 begin
652 with Shots[find_id] do
653 begin
654 g_Obj_Init(@Obj);
656 Obj.Rect.Width := 16;
657 Obj.Rect.Height := 16;
659 Triggers := nil;
660 ShotType := WEAPON_CACO_FIRE;
661 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
662 Animation := TAnimation.Create(FramesID, True, 4);
663 end;
664 end;
666 WEAPON_MANCUB_FIRE:
667 begin
668 with Shots[find_id] do
669 begin
670 g_Obj_Init(@Obj);
672 Obj.Rect.Width := 32;
673 Obj.Rect.Height := 32;
675 Triggers := nil;
676 ShotType := WEAPON_MANCUB_FIRE;
677 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
678 Animation := TAnimation.Create(FramesID, True, 4);
679 end;
680 end;
682 WEAPON_BARON_FIRE:
683 begin
684 with Shots[find_id] do
685 begin
686 g_Obj_Init(@Obj);
688 Obj.Rect.Width := 32;
689 Obj.Rect.Height := 16;
691 Triggers := nil;
692 ShotType := WEAPON_BARON_FIRE;
693 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
694 Animation := TAnimation.Create(FramesID, True, 4);
695 end;
696 end;
698 WEAPON_BSP_FIRE:
699 begin
700 with Shots[find_id] do
701 begin
702 g_Obj_Init(@Obj);
704 Obj.Rect.Width := 16;
705 Obj.Rect.Height := 16;
707 Triggers := nil;
708 ShotType := WEAPON_BSP_FIRE;
709 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
710 Animation := TAnimation.Create(FramesID, True, 4);
711 end;
712 end;
714 WEAPON_SKEL_FIRE:
715 begin
716 with Shots[find_id] do
717 begin
718 g_Obj_Init(@Obj);
720 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
721 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
723 Triggers := nil;
724 ShotType := WEAPON_SKEL_FIRE;
725 target := TargetUID;
726 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
727 Animation := TAnimation.Create(FramesID, True, 5);
728 end;
729 end;
730 end;
732 Shots[find_id].Obj.X := X;
733 Shots[find_id].Obj.Y := Y;
734 Shots[find_id].Obj.Vel.X := XV;
735 Shots[find_id].Obj.Vel.Y := YV;
736 Shots[find_id].Obj.Accel.X := 0;
737 Shots[find_id].Obj.Accel.Y := 0;
738 Shots[find_id].SpawnerUID := Spawner;
739 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
740 Shots[find_id].Stopped := 255
741 else
742 Shots[find_id].Stopped := 0;
743 Result := find_id;
744 end;
746 procedure throw(i, x, y, xd, yd, s: Integer);
747 var
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 Shots[i].Obj.X := x;
758 Shots[i].Obj.Y := y;
759 Shots[i].Obj.Vel.X := (xd*s) div a;
760 Shots[i].Obj.Vel.Y := (yd*s) div a;
761 Shots[i].Obj.Accel.X := 0;
762 Shots[i].Obj.Accel.Y := 0;
763 Shots[i].Stopped := 0;
764 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
765 Shots[i].Timeout := 900 // ~25 sec
766 else
767 begin
768 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
769 Shots[i].Timeout := SHOT_FLAME_LIFETIME
770 else
771 Shots[i].Timeout := 550; // ~15 sec
772 end;
773 end;
775 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
776 var
777 i, h: Integer;
779 function PlayerHit(Team: Byte = 0): Boolean;
780 var
781 i: Integer;
782 ChkTeam: Boolean;
783 p: TPlayer;
784 begin
785 Result := False;
786 h := High(gPlayers);
788 if h <> -1 then
789 for i := 0 to h do
790 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
791 begin
792 ChkTeam := True;
793 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
794 begin
795 p := g_Player_Get(SpawnerUID);
796 if p <> nil then
797 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
798 end;
799 if ChkTeam then
800 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
801 begin
802 if t <> HIT_FLAME then
803 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
804 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
805 if t = HIT_BFG then
806 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
807 Result := True;
808 break;
809 end;
810 end;
811 end;
814 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
815 begin
816 result := false; // don't stop
817 if mon.Live and g_Obj_Collide(obj, @mon.Obj) then
818 begin
819 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
820 begin
821 if (t <> HIT_FLAME) then
822 begin
823 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
824 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
825 end;
826 result := True;
827 end;
828 end;
829 end;
832 function monsCheckHit (mon: TMonster): Boolean;
833 begin
834 result := false; // don't stop
835 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
836 begin
837 if (t <> HIT_FLAME) then
838 begin
839 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
840 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
841 end;
842 result := true;
843 end;
844 end;
846 function MonsterHit(): Boolean;
847 begin
848 //result := g_Mons_ForEach(monsCheckHit);
849 //FIXME: accelerate this!
850 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
851 end;
853 begin
854 Result := 0;
856 if HitCorpses then
857 begin
858 h := High(gCorpses);
860 if gAdvCorpses and (h <> -1) then
861 for i := 0 to h do
862 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
863 g_Obj_Collide(obj, @gCorpses[i].Obj) then
864 begin
865 // Ðàñïèëèâàåì òðóï:
866 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
867 (obj^.Vel.Y+obj^.Accel.Y) div 4);
868 Result := 1;
869 end;
870 end;
872 case gGameSettings.GameMode of
873 // Êàìïàíèÿ:
874 GM_COOP, GM_SINGLE:
875 begin
876 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
877 if MonsterHit() then
878 begin
879 Result := 2;
880 Exit;
881 end;
883 if PlayerHit() then
884 begin
885 Result := 1;
886 Exit;
887 end;
888 end;
890 // Äåçìàò÷:
891 GM_DM:
892 begin
893 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
894 if PlayerHit() then
895 begin
896 Result := 1;
897 Exit;
898 end;
900 if MonsterHit() then
901 begin
902 Result := 2;
903 Exit;
904 end;
905 end;
907 // Êîìàíäíûå:
908 GM_TDM, GM_CTF:
909 begin
910 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
911 if PlayerHit(2) then
912 begin
913 Result := 1;
914 Exit;
915 end;
917 // Ïîòîì ìîíñòðîâ
918 if MonsterHit() then
919 begin
920 Result := 2;
921 Exit;
922 end;
924 // È â êîíöå ñâîèõ èãðîêîâ
925 if PlayerHit(1) then
926 begin
927 Result := 1;
928 Exit;
929 end;
930 end;
932 end;
933 end;
935 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
936 begin
937 Result := False;
939 case g_GetUIDType(UID) of
940 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
941 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
942 else Exit;
943 end;
944 end;
946 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
947 var
948 r: Integer; // squared radius
950 function monsExCheck (mon: TMonster): Boolean;
951 var
952 dx, dy, mm: Integer;
953 begin
954 result := false; // don't stop
955 begin
956 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
957 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
959 if dx > 1000 then dx := 1000;
960 if dy > 1000 then dy := 1000;
962 if (dx*dx+dy*dy < r) then
963 begin
964 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
965 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
967 mm := Max(abs(dx), abs(dy));
968 if mm = 0 then mm := 1;
970 if mon.Live then
971 begin
972 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
973 end;
975 mon.Push((dx*7) div mm, (dy*7) div mm);
976 end;
977 end;
978 end;
980 var
981 i, h, dx, dy, m, mm: Integer;
982 _angle: SmallInt;
983 begin
984 result := false;
986 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
988 r := rad*rad;
990 h := High(gPlayers);
992 if h <> -1 then
993 for i := 0 to h do
994 if (gPlayers[i] <> nil) and gPlayers[i].Live then
995 with gPlayers[i] do
996 begin
997 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
998 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1000 if dx > 1000 then dx := 1000;
1001 if dy > 1000 then dy := 1000;
1003 if dx*dx+dy*dy < r then
1004 begin
1005 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1006 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1008 mm := Max(abs(dx), abs(dy));
1009 if mm = 0 then mm := 1;
1011 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1012 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1013 end;
1014 end;
1016 //g_Mons_ForEach(monsExCheck);
1017 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1019 h := High(gCorpses);
1021 if gAdvCorpses and (h <> -1) then
1022 for i := 0 to h do
1023 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1024 with gCorpses[i] do
1025 begin
1026 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1027 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1029 if dx > 1000 then dx := 1000;
1030 if dy > 1000 then dy := 1000;
1032 if dx*dx+dy*dy < r then
1033 begin
1034 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1035 Obj.Rect.Width, Obj.Rect.Height);
1037 mm := Max(abs(dx), abs(dy));
1038 if mm = 0 then mm := 1;
1040 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1041 end;
1042 end;
1044 h := High(gGibs);
1046 if gAdvGibs and (h <> -1) then
1047 for i := 0 to h do
1048 if gGibs[i].Live then
1049 with gGibs[i] do
1050 begin
1051 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1052 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1054 if dx > 1000 then dx := 1000;
1055 if dy > 1000 then dy := 1000;
1057 if dx*dx+dy*dy < r then
1058 begin
1059 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1060 Obj.Rect.Width, Obj.Rect.Height);
1061 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1062 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1064 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1065 positionChanged(); // this updates spatial accelerators
1066 end;
1067 end;
1068 end;
1070 procedure g_Weapon_Init();
1071 begin
1072 CreateWaterMap();
1073 end;
1075 procedure g_Weapon_Free();
1076 var
1077 i: Integer;
1078 begin
1079 if Shots <> nil then
1080 begin
1081 for i := 0 to High(Shots) do
1082 if Shots[i].ShotType <> 0 then
1083 Shots[i].Animation.Free();
1085 Shots := nil;
1086 end;
1088 WaterMap := nil;
1089 end;
1091 procedure g_Weapon_LoadData();
1092 begin
1093 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1095 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1096 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1097 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1098 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1099 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1100 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1101 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1102 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1103 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1104 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1105 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1106 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1107 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1110 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1120 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1121 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1122 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1123 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1124 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1125 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1126 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1128 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1129 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1130 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1131 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1132 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1133 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1134 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1135 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1136 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1137 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1138 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1139 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1140 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1141 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1142 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1143 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1144 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1145 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1146 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1147 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1148 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1150 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1151 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1153 wgunMonHash := hashNewIntInt();
1154 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeCompare);
1155 end;
1157 procedure g_Weapon_FreeData();
1158 begin
1159 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1161 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1162 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1163 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1164 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1165 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1166 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1167 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1168 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1169 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1170 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1171 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1172 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1173 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1174 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1175 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1176 g_Sound_Delete('SOUND_FIRE');
1177 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1178 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1179 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1180 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1181 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1182 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1183 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1184 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1185 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1186 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1187 g_Sound_Delete('SOUND_PLAYER_JETON');
1188 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1189 g_Sound_Delete('SOUND_PLAYER_CASING1');
1190 g_Sound_Delete('SOUND_PLAYER_CASING2');
1191 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1192 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1194 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1195 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1196 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1197 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1198 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1199 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1200 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1201 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1202 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1203 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1204 g_Frames_DeleteByName('FRAMES_BFGHIT');
1205 g_Frames_DeleteByName('FRAMES_FIRE');
1206 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1207 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1208 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1209 g_Frames_DeleteByName('FRAMES_SMOKE');
1210 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1211 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1212 end;
1215 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1216 var
1217 i: Integer;
1218 begin
1219 result := false;
1220 for i := 0 to High(gPlayers) do
1221 begin
1222 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
1223 begin
1224 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1225 begin
1226 if AllowPush then gPlayers[i].Push(vx, vy);
1227 result := true;
1228 end;
1229 end;
1230 end;
1231 end;
1234 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1236 function monsCheck (mon: TMonster): Boolean;
1237 begin
1238 result := false; // don't stop
1239 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1240 begin
1241 if AllowPush then mon.Push(vx, vy);
1242 result := true;
1243 end;
1244 end;
1246 begin
1247 result := 0;
1248 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1249 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1250 end;
1253 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1254 var
1255 a: Integer;
1256 x2, y2: Integer;
1257 dx, dy: Integer;
1258 xe, ye: Integer;
1259 xi, yi: Integer;
1260 s, c: Extended;
1261 //vx, vy: Integer;
1262 xx, yy, d: Integer;
1263 i: Integer;
1264 t1, _collide: Boolean;
1265 w, h: Word;
1266 {$IF DEFINED(D2F_DEBUG)}
1267 stt: UInt64;
1268 showTime: Boolean = true;
1269 {$ENDIF}
1270 begin
1271 a := GetAngle(x, y, xd, yd)+180;
1273 SinCos(DegToRad(-a), s, c);
1275 if Abs(s) < 0.01 then s := 0;
1276 if Abs(c) < 0.01 then c := 0;
1278 x2 := x+Round(c*gMapInfo.Width);
1279 y2 := y+Round(s*gMapInfo.Width);
1281 t1 := gWalls <> nil;
1282 _collide := False;
1283 w := gMapInfo.Width;
1284 h := gMapInfo.Height;
1286 xe := 0;
1287 ye := 0;
1288 dx := x2-x;
1289 dy := y2-y;
1291 if (xd = 0) and (yd = 0) then Exit;
1293 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1294 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1296 dx := Abs(dx);
1297 dy := Abs(dy);
1299 if dx > dy then d := dx else d := dy;
1301 //blood vel, for Monster.Damage()
1302 //vx := (dx*10 div d)*xi;
1303 //vy := (dy*10 div d)*yi;
1305 {$IF DEFINED(D2F_DEBUG)}
1306 stt := curTimeMicro();
1307 {$ENDIF}
1309 xx := x;
1310 yy := y;
1312 for i := 1 to d do
1313 begin
1314 xe := xe+dx;
1315 ye := ye+dy;
1317 if xe > d then
1318 begin
1319 xe := xe-d;
1320 xx := xx+xi;
1321 end;
1323 if ye > d then
1324 begin
1325 ye := ye-d;
1326 yy := yy+yi;
1327 end;
1329 if (yy > h) or (yy < 0) then Break;
1330 if (xx > w) or (xx < 0) then Break;
1332 if t1 then
1333 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1334 begin
1335 _collide := True;
1336 {$IF DEFINED(D2F_DEBUG)}
1337 stt := curTimeMicro()-stt;
1338 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1339 showTime := false;
1340 {$ENDIF}
1341 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1342 if g_Game_IsServer and g_Game_IsNet then
1343 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1344 end;
1346 if not _collide then
1347 begin
1348 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1349 end;
1351 if _collide then Break;
1352 end;
1354 {$IF DEFINED(D2F_DEBUG)}
1355 if showTime then
1356 begin
1357 stt := curTimeMicro()-stt;
1358 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1359 end;
1360 {$ENDIF}
1362 if CheckTrigger and g_Game_IsServer then
1363 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1364 end;
1367 (*
1368 procedure g_Weapon_gunComplicated (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1369 const
1370 HHGridSize = 64;
1372 var
1373 hitray: Ray2D;
1374 xi, yi: Integer;
1376 function doPlayerHit (idx: Integer): Boolean;
1377 begin
1378 result := false;
1379 if (idx < 0) or (idx > High(gPlayers)) then exit;
1380 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1381 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1382 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1383 {$IF DEFINED(D2F_DEBUG)}
1384 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1385 {$ENDIF}
1386 end;
1388 function doMonsterHit (mon: TMonster): Boolean;
1389 begin
1390 result := false;
1391 if (mon = nil) then exit;
1392 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1393 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1394 {$IF DEFINED(D2F_DEBUG)}
1395 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1396 {$ENDIF}
1397 end;
1399 // get nearest player along hitray
1400 // return `true` if instant hit was detected
1401 function playerPossibleHit (): Boolean;
1402 var
1403 i: Integer;
1404 aabb: AABB2D;
1405 tmin: Single;
1406 begin
1407 result := false;
1408 for i := 0 to High(gPlayers) do
1409 begin
1410 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1411 begin
1412 aabb := gPlayers[i].mapAABB;
1413 // inside?
1414 if aabb.contains(x, y) then
1415 begin
1416 if doPlayerHit(i) then begin result := true; exit; end;
1417 end
1418 else if (aabb.intersects(hitray, @tmin)) then
1419 begin
1420 // intersect
1421 if (tmin <= 0) then
1422 begin
1423 if doPlayerHit(i) then begin result := true; exit; end;
1424 end
1425 else
1426 begin
1427 appendHitTimePlr(tmin, i);
1428 end;
1429 end;
1430 end;
1431 end;
1432 end;
1434 function monsPossibleHitInstant (mon: TMonster): Boolean;
1435 var
1436 aabb: AABB2D;
1437 begin
1438 result := false; // don't stop
1439 aabb := mon.mapAABB;
1440 if aabb.contains(x, y) then
1441 begin
1442 result := doMonsterHit(mon);
1443 end;
1444 end;
1446 function monsPossibleHit (mon: TMonster): Boolean;
1447 var
1448 aabb: AABB2D;
1449 tmin: Single;
1450 begin
1451 result := false; // don't stop
1452 if not wgunMonHash.put(Integer(mon.UID), 1) then
1453 begin
1454 // new monster; calculate hitpoint
1455 aabb := mon.mapAABB;
1456 if (aabb.intersects(hitray, @tmin)) then
1457 begin
1458 if (tmin < 0) then tmin := 1.0;
1459 appendHitTimeMon(tmin, mon);
1460 end;
1461 end;
1462 end;
1464 var
1465 a: Integer;
1466 x2, y2: Integer;
1467 dx, dy: Integer;
1468 xe, ye: Integer;
1469 s, c: Extended;
1470 xx, yy, d: Integer;
1471 prevX, prevY: Integer;
1472 leftToNextMonsterQuery: Integer = 0;
1473 i: Integer;
1474 t1: Boolean;
1475 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1476 w, h: Word;
1477 {$ENDIF}
1478 wallWasHit: Boolean = false;
1479 wallHitX: Integer = 0;
1480 wallHitY: Integer = 0;
1481 didHit: Boolean = false;
1482 mptWX: Integer = 0;
1483 mptWY: Integer = 0;
1484 mptHit: Integer = -1;
1485 {$IF DEFINED(D2F_DEBUG)}
1486 stt: UInt64;
1487 {$ENDIF}
1488 begin
1489 if not gwep_debug_fast_trace then
1490 begin
1491 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1492 exit;
1493 end;
1495 wgunMonHash.reset(); //FIXME: clear hash on level change
1496 wgunHitHeap.clear();
1497 wgunHitTimeUsed := 0;
1499 a := GetAngle(x, y, xd, yd)+180;
1501 SinCos(DegToRad(-a), s, c);
1503 if Abs(s) < 0.01 then s := 0;
1504 if Abs(c) < 0.01 then c := 0;
1506 x2 := x+Round(c*gMapInfo.Width);
1507 y2 := y+Round(s*gMapInfo.Width);
1509 hitray := Ray2D.Create(x, y, x2, y2);
1511 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1513 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1514 t1 := (gWalls <> nil);
1515 w := gMapInfo.Width;
1516 h := gMapInfo.Height;
1517 {$ENDIF}
1519 dx := x2-x;
1520 dy := y2-y;
1522 if (xd = 0) and (yd = 0) then Exit;
1524 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1525 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1527 // check instant hits
1528 xx := x;
1529 yy := y;
1530 if (dx < 0) then Dec(xx);
1531 if (dy < 0) then Dec(yy);
1533 dx := Abs(dx);
1534 dy := Abs(dy);
1536 if playerPossibleHit() then exit; // instant hit
1537 if g_Mons_ForEachAliveAt(xx, yy, 3, 3, monsPossibleHitInstant) then exit; // instant hit
1539 if dx > dy then d := dx else d := dy;
1541 //blood vel, for Monster.Damage()
1542 //vx := (dx*10 div d)*xi;
1543 //vy := (dy*10 div d)*yi;
1545 {$IF DEFINED(D2F_DEBUG)}
1546 mptHit := g_Map_traceToNearestWall(x, y, x2, y2, @mptWX, @mptWY);
1547 e_WriteLog(Format('tree trace: (%d,%d)', [mptWX, mptWY]), MSG_NOTIFY);
1548 {$ENDIF}
1550 {$IF not DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1551 wallWasHit := (mptHit >= 0);
1552 wallHitX := mptWX;
1553 wallHitY := mptWY;
1554 t1 := false;
1555 {$ENDIF}
1557 {$IF DEFINED(D2F_DEBUG)}
1558 stt := curTimeMicro();
1559 {$ENDIF}
1560 // find wall, collect monsters
1561 begin
1562 xe := 0;
1563 ye := 0;
1564 xx := x;
1565 yy := y;
1566 prevX := xx;
1567 prevY := yy;
1568 for i := 1 to d do
1569 begin
1570 prevX := xx;
1571 prevY := yy;
1572 xe += dx;
1573 ye += dy;
1574 if (xe > d) then begin xe -= d; xx += xi; end;
1575 if (ye > d) then begin ye -= d; yy += yi; end;
1577 // wtf?!
1578 //if (yy > h) or (yy < 0) then break;
1579 //if (xx > w) or (xx < 0) then break;
1581 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1582 if t1 and (xx >= 0) and (yy >= 0) and (xx < w) and (yy < h) then
1583 begin
1584 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1585 begin
1586 wallWasHit := true;
1587 wallHitX := prevX;
1588 wallHitY := prevY;
1589 end;
1590 end;
1591 {$ELSE}
1592 if (abs(prevX-wallHitX) < 2) and (abs(prevY-wallHitY) < 2) then t1 := true;
1593 {$ENDIF}
1595 if (leftToNextMonsterQuery <> 0) and not wallWasHit then
1596 begin
1597 Dec(leftToNextMonsterQuery);
1598 end
1599 else
1600 begin
1601 // check monsters
1602 g_Mons_ForEachAliveAt(xx-HHGridSize div 2, yy-HHGridSize div 2, HHGridSize+HHGridSize div 2, HHGridSize+HHGridSize div 2, monsPossibleHit);
1603 leftToNextMonsterQuery := HHGridSize; // again
1604 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1605 if wallWasHit then break;
1606 {$ELSE}
1607 if t1 then break;
1608 {$ENDIF}
1609 end;
1610 end;
1612 if not wallWasHit then
1613 begin
1614 wallHitX := prevX;
1615 wallHitY := prevY;
1616 end;
1617 end;
1619 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1620 // also, if `wallWasHit` is true, then `wallHitX` and `wallHitY` contains wall coords
1621 while (wgunHitHeap.count > 0) do
1622 begin
1623 // has some entities to check, do it
1624 i := wgunHitHeap.front;
1625 wgunHitHeap.popFront();
1626 hitray.atTime(wgunHitTime[i].time, xe, ye);
1627 // check if it is not behind the wall
1628 if ((xe-x)*(xe-x)+(ye-y)*(ye-y) < (wallHitX-x)*(wallHitX-x)+(wallHitY-y)*(wallHitY-y)) then
1629 begin
1630 if (wgunHitTime[i].mon <> nil) then
1631 begin
1632 didHit := doMonsterHit(wgunHitTime[i].mon);
1633 end
1634 else
1635 begin
1636 didHit := doPlayerHit(wgunHitTime[i].plridx);
1637 end;
1638 if didHit then
1639 begin
1640 // need new coords for trigger
1641 wallHitX := xe;
1642 wallHitY := ye;
1643 wallWasHit := false; // no sparks
1644 break;
1645 end;
1646 end;
1647 end;
1649 // need sparks?
1650 if wallWasHit then
1651 begin
1652 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1653 if (mptHit < 0) then
1654 begin
1655 e_WriteLog('OOPS: tree trace failed, but pixel trace found the wall!', MSG_WARNING);
1656 raise Exception.Create('map tree trace fucked');
1657 end
1658 else
1659 begin
1660 {$IF DEFINED(D2F_DEBUG)}
1661 //e_WriteLog(Format(' trace: (%d,%d)', [wallHitX, wallHitY]), MSG_NOTIFY);
1662 {$ENDIF}
1663 wallHitX := mptWX;
1664 wallHitY := mptWY;
1665 end;
1666 {$ENDIF}
1667 {$IF DEFINED(D2F_DEBUG)}
1668 stt := curTimeMicro()-stt;
1669 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1670 {$ENDIF}
1671 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1672 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1673 end
1674 else
1675 begin
1676 {$IF DEFINED(D2F_DEBUG)}
1677 stt := curTimeMicro()-stt;
1678 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1679 {$ENDIF}
1680 end;
1682 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1683 end;
1684 *)
1687 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1688 var
1689 hitray: Ray2D;
1690 xi, yi: Integer;
1691 wallDistSq: Single = 1.0e100;
1693 function doPlayerHit (idx: Integer): Boolean;
1694 begin
1695 result := false;
1696 if (idx < 0) or (idx > High(gPlayers)) then exit;
1697 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1698 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1699 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1700 {$IF DEFINED(D2F_DEBUG)}
1701 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1702 {$ENDIF}
1703 end;
1705 function doMonsterHit (mon: TMonster): Boolean;
1706 begin
1707 result := false;
1708 if (mon = nil) then exit;
1709 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1710 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1711 {$IF DEFINED(D2F_DEBUG)}
1712 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1713 {$ENDIF}
1714 end;
1716 // collect players along hitray
1717 // return `true` if instant hit was detected
1718 function playerPossibleHit (): Boolean;
1719 var
1720 i: Integer;
1721 aabb: AABB2D;
1722 tmin: Single;
1723 begin
1724 result := false;
1725 for i := 0 to High(gPlayers) do
1726 begin
1727 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1728 begin
1729 aabb := gPlayers[i].mapAABB;
1730 // inside?
1731 if aabb.contains(x, y) then
1732 begin
1733 if doPlayerHit(i) then begin result := true; exit; end;
1734 end
1735 else if (aabb.intersects(hitray, @tmin)) then
1736 begin
1737 // intersect
1738 if (tmin <= 0.0) then
1739 begin
1740 if doPlayerHit(i) then begin result := true; exit; end;
1741 end
1742 else
1743 begin
1744 if (tmin*tmin < wallDistSq) then appendHitTimePlr(tmin, i);
1745 end;
1746 end;
1747 end;
1748 end;
1749 end;
1751 function sqchecker (mon: TMonster; dist: Single): Boolean;
1752 begin
1753 result := false; // don't stop
1754 if (dist*dist < wallDistSq) then appendHitTimeMon(dist, mon);
1755 end;
1757 var
1758 a: Integer;
1759 x2, y2: Integer;
1760 dx, dy: Integer;
1761 xe, ye: Integer;
1762 s, c: Extended;
1763 i: Integer;
1764 wallHitIdx: Integer = -1;
1765 wallHitX: Integer = 0;
1766 wallHitY: Integer = 0;
1767 didHit: Boolean = false;
1768 {$IF DEFINED(D2F_DEBUG)}
1769 stt: UInt64;
1770 {$ENDIF}
1771 begin
1772 if not gwep_debug_fast_trace then
1773 begin
1774 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1775 exit;
1776 end;
1778 wgunMonHash.reset(); //FIXME: clear hash on level change
1779 wgunHitHeap.clear();
1780 wgunHitTimeUsed := 0;
1782 a := GetAngle(x, y, xd, yd)+180;
1784 SinCos(DegToRad(-a), s, c);
1786 if Abs(s) < 0.01 then s := 0;
1787 if Abs(c) < 0.01 then c := 0;
1789 x2 := x+Round(c*gMapInfo.Width);
1790 y2 := y+Round(s*gMapInfo.Width);
1792 dx := x2-x;
1793 dy := y2-y;
1795 if (xd = 0) and (yd = 0) then exit;
1797 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1798 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1800 {$IF DEFINED(D2F_DEBUG)}
1801 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1802 stt := curTimeMicro();
1803 {$ENDIF}
1805 wallHitIdx := g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY);
1806 if (wallHitIdx >= 0) then
1807 begin
1808 x2 := wallHitX;
1809 y2 := wallHitY;
1810 wallDistSq := (wallHitX-x)*(wallHitX-x)+(wallHitY-y)*(wallHitY-y);
1811 end
1812 else
1813 begin
1814 wallHitX := x2;
1815 wallHitY := y2;
1816 end;
1818 hitray := Ray2D.Create(x, y, x2, y2);
1820 if playerPossibleHit() then exit; // instant hit
1822 // collect monsters
1823 g_Mons_alongLine(x, y, x2, y2, sqchecker);
1825 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1826 // also, if `wallWasHit` >= 0, then `wallHitX` and `wallHitY` contains spark coords
1827 while (wgunHitHeap.count > 0) do
1828 begin
1829 // has some entities to check, do it
1830 i := wgunHitHeap.front;
1831 wgunHitHeap.popFront();
1832 hitray.atTime(wgunHitTime[i].time, xe, ye);
1833 // check if it is not behind the wall
1834 if (wgunHitTime[i].mon <> nil) then
1835 begin
1836 didHit := doMonsterHit(wgunHitTime[i].mon);
1837 end
1838 else
1839 begin
1840 didHit := doPlayerHit(wgunHitTime[i].plridx);
1841 end;
1842 if didHit then
1843 begin
1844 // need new coords for trigger
1845 wallHitX := xe;
1846 wallHitY := ye;
1847 wallHitIdx := -1; // no sparks
1848 break;
1849 end;
1850 end;
1852 // need sparks?
1853 if (wallHitIdx >= 0) then
1854 begin
1855 {$IF DEFINED(D2F_DEBUG)}
1856 stt := curTimeMicro()-stt;
1857 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1858 {$ENDIF}
1859 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1860 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1861 end
1862 else
1863 begin
1864 {$IF DEFINED(D2F_DEBUG)}
1865 stt := curTimeMicro()-stt;
1866 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1867 {$ENDIF}
1868 end;
1870 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1871 end;
1874 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1875 var
1876 obj: TObj;
1877 begin
1878 obj.X := X;
1879 obj.Y := Y;
1880 obj.rect.X := 0;
1881 obj.rect.Y := 0;
1882 obj.rect.Width := 39;
1883 obj.rect.Height := 52;
1884 obj.Vel.X := 0;
1885 obj.Vel.Y := 0;
1886 obj.Accel.X := 0;
1887 obj.Accel.Y := 0;
1889 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1890 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1891 else
1892 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1893 end;
1895 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1896 var
1897 obj: TObj;
1898 begin
1899 obj.X := X;
1900 obj.Y := Y;
1901 obj.rect.X := 0;
1902 obj.rect.Y := 0;
1903 obj.rect.Width := 32;
1904 obj.rect.Height := 52;
1905 obj.Vel.X := 0;
1906 obj.Vel.Y := 0;
1907 obj.Accel.X := 0;
1908 obj.Accel.Y := 0;
1910 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1911 end;
1913 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1914 Silent: Boolean = False);
1915 var
1916 find_id: DWORD;
1917 dx, dy: Integer;
1918 begin
1919 if WID < 0 then
1920 find_id := FindShot()
1921 else
1922 begin
1923 find_id := WID;
1924 if Integer(find_id) >= High(Shots) then
1925 SetLength(Shots, find_id + 64)
1926 end;
1928 with Shots[find_id] do
1929 begin
1930 g_Obj_Init(@Obj);
1932 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1933 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1935 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1936 dy := -(Obj.Rect.Height div 2);
1938 ShotType := WEAPON_ROCKETLAUNCHER;
1939 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1941 Animation := nil;
1942 triggers := nil;
1943 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1944 end;
1946 Shots[find_id].SpawnerUID := SpawnerUID;
1948 if not Silent then
1949 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1950 end;
1952 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1953 WID: Integer = -1; Silent: Boolean = False);
1954 var
1955 find_id, FramesID: DWORD;
1956 dx, dy: Integer;
1957 begin
1958 if WID < 0 then
1959 find_id := FindShot()
1960 else
1961 begin
1962 find_id := WID;
1963 if Integer(find_id) >= High(Shots) then
1964 SetLength(Shots, find_id + 64)
1965 end;
1967 with Shots[find_id] do
1968 begin
1969 g_Obj_Init(@Obj);
1971 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1972 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1974 dx := -(Obj.Rect.Width div 2);
1975 dy := -(Obj.Rect.Height div 2);
1977 ShotType := WEAPON_SKEL_FIRE;
1978 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1980 triggers := nil;
1981 target := TargetUID;
1982 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1983 Animation := TAnimation.Create(FramesID, True, 5);
1984 end;
1986 Shots[find_id].SpawnerUID := SpawnerUID;
1988 if not Silent then
1989 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1990 end;
1992 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1993 Silent: Boolean = False);
1994 var
1995 find_id, FramesID: DWORD;
1996 dx, dy: Integer;
1997 begin
1998 if WID < 0 then
1999 find_id := FindShot()
2000 else
2001 begin
2002 find_id := WID;
2003 if Integer(find_id) >= High(Shots) then
2004 SetLength(Shots, find_id + 64);
2005 end;
2007 with Shots[find_id] do
2008 begin
2009 g_Obj_Init(@Obj);
2011 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
2012 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
2014 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2015 dy := -(Obj.Rect.Height div 2);
2017 ShotType := WEAPON_PLASMA;
2018 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2020 triggers := nil;
2021 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
2022 Animation := TAnimation.Create(FramesID, True, 5);
2023 end;
2025 Shots[find_id].SpawnerUID := SpawnerUID;
2027 if not Silent then
2028 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
2029 end;
2031 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2032 Silent: Boolean = False);
2033 var
2034 find_id: DWORD;
2035 dx, dy: Integer;
2036 begin
2037 if WID < 0 then
2038 find_id := FindShot()
2039 else
2040 begin
2041 find_id := WID;
2042 if Integer(find_id) >= High(Shots) then
2043 SetLength(Shots, find_id + 64);
2044 end;
2046 with Shots[find_id] do
2047 begin
2048 g_Obj_Init(@Obj);
2050 Obj.Rect.Width := SHOT_FLAME_WIDTH;
2051 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
2053 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2054 dy := -(Obj.Rect.Height div 2);
2056 ShotType := WEAPON_FLAMETHROWER;
2057 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2059 triggers := nil;
2060 Animation := nil;
2061 TextureID := 0;
2062 g_Frames_Get(TextureID, 'FRAMES_FLAME');
2063 end;
2065 Shots[find_id].SpawnerUID := SpawnerUID;
2067 // if not Silent then
2068 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
2069 end;
2071 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2072 Silent: Boolean = False);
2073 var
2074 find_id, FramesID: DWORD;
2075 dx, dy: Integer;
2076 begin
2077 if WID < 0 then
2078 find_id := FindShot()
2079 else
2080 begin
2081 find_id := WID;
2082 if Integer(find_id) >= High(Shots) then
2083 SetLength(Shots, find_id + 64)
2084 end;
2086 with Shots[find_id] do
2087 begin
2088 g_Obj_Init(@Obj);
2090 Obj.Rect.Width := 16;
2091 Obj.Rect.Height := 16;
2093 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2094 dy := -(Obj.Rect.Height div 2);
2096 ShotType := WEAPON_IMP_FIRE;
2097 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2099 triggers := nil;
2100 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
2101 Animation := TAnimation.Create(FramesID, True, 4);
2102 end;
2104 Shots[find_id].SpawnerUID := SpawnerUID;
2106 if not Silent then
2107 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2108 end;
2110 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2111 Silent: Boolean = False);
2112 var
2113 find_id, FramesID: DWORD;
2114 dx, dy: Integer;
2115 begin
2116 if WID < 0 then
2117 find_id := FindShot()
2118 else
2119 begin
2120 find_id := WID;
2121 if Integer(find_id) >= High(Shots) then
2122 SetLength(Shots, find_id + 64)
2123 end;
2125 with Shots[find_id] do
2126 begin
2127 g_Obj_Init(@Obj);
2129 Obj.Rect.Width := 16;
2130 Obj.Rect.Height := 16;
2132 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2133 dy := -(Obj.Rect.Height div 2);
2135 ShotType := WEAPON_CACO_FIRE;
2136 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2138 triggers := nil;
2139 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
2140 Animation := TAnimation.Create(FramesID, True, 4);
2141 end;
2143 Shots[find_id].SpawnerUID := SpawnerUID;
2145 if not Silent then
2146 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2147 end;
2149 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2150 Silent: Boolean = False);
2151 var
2152 find_id, FramesID: DWORD;
2153 dx, dy: Integer;
2154 begin
2155 if WID < 0 then
2156 find_id := FindShot()
2157 else
2158 begin
2159 find_id := WID;
2160 if Integer(find_id) >= High(Shots) then
2161 SetLength(Shots, find_id + 64)
2162 end;
2164 with Shots[find_id] do
2165 begin
2166 g_Obj_Init(@Obj);
2168 Obj.Rect.Width := 32;
2169 Obj.Rect.Height := 16;
2171 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2172 dy := -(Obj.Rect.Height div 2);
2174 ShotType := WEAPON_BARON_FIRE;
2175 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2177 triggers := nil;
2178 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
2179 Animation := TAnimation.Create(FramesID, True, 4);
2180 end;
2182 Shots[find_id].SpawnerUID := SpawnerUID;
2184 if not Silent then
2185 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2186 end;
2188 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2189 Silent: Boolean = False);
2190 var
2191 find_id, FramesID: DWORD;
2192 dx, dy: Integer;
2193 begin
2194 if WID < 0 then
2195 find_id := FindShot()
2196 else
2197 begin
2198 find_id := WID;
2199 if Integer(find_id) >= High(Shots) then
2200 SetLength(Shots, find_id + 64)
2201 end;
2203 with Shots[find_id] do
2204 begin
2205 g_Obj_Init(@Obj);
2207 Obj.Rect.Width := 16;
2208 Obj.Rect.Height := 16;
2210 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2211 dy := -(Obj.Rect.Height div 2);
2213 ShotType := WEAPON_BSP_FIRE;
2214 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2216 triggers := nil;
2218 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
2219 Animation := TAnimation.Create(FramesID, True, 4);
2220 end;
2222 Shots[find_id].SpawnerUID := SpawnerUID;
2224 if not Silent then
2225 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
2226 end;
2228 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2229 Silent: Boolean = False);
2230 var
2231 find_id, FramesID: DWORD;
2232 dx, dy: Integer;
2233 begin
2234 if WID < 0 then
2235 find_id := FindShot()
2236 else
2237 begin
2238 find_id := WID;
2239 if Integer(find_id) >= High(Shots) then
2240 SetLength(Shots, find_id + 64)
2241 end;
2243 with Shots[find_id] do
2244 begin
2245 g_Obj_Init(@Obj);
2247 Obj.Rect.Width := 32;
2248 Obj.Rect.Height := 32;
2250 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2251 dy := -(Obj.Rect.Height div 2);
2253 ShotType := WEAPON_MANCUB_FIRE;
2254 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2256 triggers := nil;
2258 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
2259 Animation := TAnimation.Create(FramesID, True, 4);
2260 end;
2262 Shots[find_id].SpawnerUID := SpawnerUID;
2264 if not Silent then
2265 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2266 end;
2268 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2269 Silent: Boolean = False);
2270 var
2271 find_id, FramesID: DWORD;
2272 dx, dy: Integer;
2273 begin
2274 if WID < 0 then
2275 find_id := FindShot()
2276 else
2277 begin
2278 find_id := WID;
2279 if Integer(find_id) >= High(Shots) then
2280 SetLength(Shots, find_id + 64)
2281 end;
2283 with Shots[find_id] do
2284 begin
2285 g_Obj_Init(@Obj);
2287 Obj.Rect.Width := SHOT_BFG_WIDTH;
2288 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2290 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2291 dy := -(Obj.Rect.Height div 2);
2293 ShotType := WEAPON_BFG;
2294 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2296 triggers := nil;
2297 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2298 Animation := TAnimation.Create(FramesID, True, 6);
2299 end;
2301 Shots[find_id].SpawnerUID := SpawnerUID;
2303 if not Silent then
2304 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2305 end;
2307 procedure g_Weapon_bfghit(x, y: Integer);
2308 var
2309 ID: DWORD;
2310 Anim: TAnimation;
2311 begin
2312 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2313 begin
2314 Anim := TAnimation.Create(ID, False, 4);
2315 g_GFX_OnceAnim(x-32, y-32, Anim);
2316 Anim.Free();
2317 end;
2318 end;
2320 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2321 Silent: Boolean = False);
2322 begin
2323 if not Silent then
2324 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2326 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2327 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2328 begin
2329 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2330 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2331 end;
2332 end;
2334 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2335 Silent: Boolean = False);
2336 begin
2337 if not Silent then
2338 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2340 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2341 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2342 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2343 begin
2344 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2345 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2346 end;
2347 end;
2349 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2350 Silent: Boolean = False);
2351 var
2352 i, j: Integer;
2353 begin
2354 if not Silent then
2355 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2357 for i := 0 to 9 do
2358 begin
2359 j := Random(17)-8; // -8 .. 8
2360 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2361 end;
2362 end;
2364 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2365 Silent: Boolean = False);
2366 var
2367 a, i, j: Integer;
2368 begin
2369 if not Silent then
2370 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2372 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2373 for i := 0 to a do
2374 begin
2375 j := Random(41)-20; // -20 .. 20
2376 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2377 end;
2378 end;
2380 procedure g_Weapon_Update();
2381 var
2382 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2383 _id: DWORD;
2384 Anim: TAnimation;
2385 t: DWArray;
2386 st: Word;
2387 s: String;
2388 o: TObj;
2389 spl: Boolean;
2390 Loud: Boolean;
2391 tcx, tcy: Integer;
2392 begin
2393 if Shots = nil then
2394 Exit;
2396 for i := 0 to High(Shots) do
2397 begin
2398 if Shots[i].ShotType = 0 then
2399 Continue;
2401 Loud := True;
2403 with Shots[i] do
2404 begin
2405 Timeout := Timeout - 1;
2406 oldvx := Obj.Vel.X;
2407 oldvy := Obj.Vel.Y;
2408 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2409 if (Stopped = 0) and g_Game_IsServer then
2410 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2411 SpawnerUID, ACTIVATE_SHOT, triggers)
2412 else
2413 t := nil;
2415 if t <> nil then
2416 begin
2417 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2418 if triggers = nil then
2419 triggers := t
2420 else
2421 begin
2422 h := High(t);
2424 for a := 0 to h do
2425 if not InDWArray(t[a], triggers) then
2426 begin
2427 SetLength(triggers, Length(triggers)+1);
2428 triggers[High(triggers)] := t[a];
2429 end;
2430 end;
2431 end;
2433 // Àíèìàöèÿ ñíàðÿäà:
2434 if Animation <> nil then
2435 Animation.Update();
2437 // Äâèæåíèå:
2438 spl := (ShotType <> WEAPON_PLASMA) and
2439 (ShotType <> WEAPON_BFG) and
2440 (ShotType <> WEAPON_BSP_FIRE) and
2441 (ShotType <> WEAPON_FLAMETHROWER);
2443 if Stopped = 0 then
2444 begin
2445 st := g_Obj_Move(@Obj, False, spl);
2446 end
2447 else
2448 begin
2449 st := 0;
2450 end;
2451 positionChanged(); // this updates spatial accelerators
2453 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2454 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2455 begin
2456 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2457 ShotType := 0;
2458 Animation.Free();
2459 Continue;
2460 end;
2462 cx := Obj.X + (Obj.Rect.Width div 2);
2463 cy := Obj.Y + (Obj.Rect.Height div 2);
2465 case ShotType of
2466 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2467 begin
2468 // Âûëåòåëà èç âîäû:
2469 if WordBool(st and MOVE_HITAIR) then
2470 g_Obj_SetSpeed(@Obj, 12);
2472 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2473 if WordBool(st and MOVE_INWATER) then
2474 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2475 Obj.Y+(Obj.Rect.Height div 2),
2476 1+Random(3), 16, 16)
2477 else
2478 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2479 begin
2480 Anim := TAnimation.Create(_id, False, 3);
2481 Anim.Alpha := 150;
2482 g_GFX_OnceAnim(Obj.X-14+Random(9),
2483 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2484 Anim, ONCEANIM_SMOKE);
2485 Anim.Free();
2486 end;
2488 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2489 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2490 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2491 (Timeout < 1) then
2492 begin
2493 Obj.Vel.X := 0;
2494 Obj.Vel.Y := 0;
2496 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2498 if ShotType = WEAPON_SKEL_FIRE then
2499 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2500 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2501 begin
2502 Anim := TAnimation.Create(TextureID, False, 8);
2503 Anim.Blending := False;
2504 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2505 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2506 Anim.Free();
2507 end;
2508 end
2509 else
2510 begin // Âçðûâ Ðàêåòû
2511 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2512 begin
2513 Anim := TAnimation.Create(TextureID, False, 6);
2514 Anim.Blending := False;
2515 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2516 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2517 Anim.Free();
2518 end;
2519 end;
2521 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2523 ShotType := 0;
2524 end;
2526 if ShotType = WEAPON_SKEL_FIRE then
2527 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2528 if GetPos(target, @o) then
2529 throw(i, Obj.X, Obj.Y,
2530 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2531 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2532 12);
2533 end;
2534 end;
2536 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2537 begin
2538 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2539 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2540 begin
2541 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2542 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2543 ShotType := 0;
2544 Continue;
2545 end;
2547 // Âåëè÷èíà óðîíà:
2548 if (ShotType = WEAPON_PLASMA) and
2549 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2550 a := 10
2551 else
2552 a := 5;
2554 if ShotType = WEAPON_BSP_FIRE then
2555 a := 10;
2557 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2558 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2559 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2560 (Timeout < 1) then
2561 begin
2562 if ShotType = WEAPON_PLASMA then
2563 s := 'FRAMES_EXPLODE_PLASMA'
2564 else
2565 s := 'FRAMES_EXPLODE_BSPFIRE';
2567 // Âçðûâ Ïëàçìû:
2568 if g_Frames_Get(TextureID, s) then
2569 begin
2570 Anim := TAnimation.Create(TextureID, False, 3);
2571 Anim.Blending := False;
2572 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2573 Anim.Free();
2574 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2575 end;
2577 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2579 ShotType := 0;
2580 end;
2581 end;
2583 WEAPON_FLAMETHROWER: // Îãíåìåò
2584 begin
2585 // Ñî âðåìåíåì óìèðàåò
2586 if (Timeout < 1) then
2587 begin
2588 ShotType := 0;
2589 Continue;
2590 end;
2591 // Ïîä âîäîé òîæå
2592 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2593 begin
2594 if WordBool(st and MOVE_HITWATER) then
2595 begin
2596 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2597 begin
2598 Anim := TAnimation.Create(_id, False, 3);
2599 Anim.Alpha := 0;
2600 tcx := Random(8);
2601 tcy := Random(8);
2602 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2603 cy-4+tcy-(Anim.Height div 2),
2604 Anim, ONCEANIM_SMOKE);
2605 Anim.Free();
2606 end;
2607 end
2608 else
2609 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2610 ShotType := 0;
2611 Continue;
2612 end;
2614 // Ãðàâèòàöèÿ
2615 if Stopped = 0 then
2616 Obj.Accel.Y := Obj.Accel.Y + 1;
2617 // Ïîïàëè â ñòåíó èëè â âîäó:
2618 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2619 begin
2620 // Ïðèëèïàåì:
2621 Obj.Vel.X := 0;
2622 Obj.Vel.Y := 0;
2623 Obj.Accel.Y := 0;
2624 if WordBool(st and MOVE_HITWALL) then
2625 Stopped := MOVE_HITWALL
2626 else if WordBool(st and MOVE_HITLAND) then
2627 Stopped := MOVE_HITLAND
2628 else if WordBool(st and MOVE_HITCEIL) then
2629 Stopped := MOVE_HITCEIL;
2630 end;
2632 a := IfThen(Stopped = 0, 3, 1);
2633 // Åñëè â êîãî-òî ïîïàëè
2634 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2635 begin
2636 // HIT_FLAME ñàì ïîäîææåò
2637 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2638 if Stopped = 0 then
2639 ShotType := 0;
2640 end;
2642 if Stopped = 0 then
2643 tf := 2
2644 else
2645 tf := 3;
2647 if (gTime mod tf = 0) then
2648 begin
2649 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2650 Anim.Alpha := 0;
2651 case Stopped of
2652 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2653 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2654 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2655 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2656 end;
2657 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2658 Anim.Free();
2659 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2660 end;
2661 end;
2663 WEAPON_BFG: // BFG
2664 begin
2665 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2666 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2667 begin
2668 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2669 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2670 ShotType := 0;
2671 Continue;
2672 end;
2674 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2675 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2676 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2677 (Timeout < 1) then
2678 begin
2679 // Ëó÷è BFG:
2680 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2682 // Âçðûâ BFG:
2683 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2684 begin
2685 Anim := TAnimation.Create(TextureID, False, 6);
2686 Anim.Blending := False;
2687 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2688 Anim.Free();
2689 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2690 end;
2692 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2694 ShotType := 0;
2695 end;
2696 end;
2698 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2699 begin
2700 // Âûëåòåë èç âîäû:
2701 if WordBool(st and MOVE_HITAIR) then
2702 g_Obj_SetSpeed(@Obj, 16);
2704 // Âåëè÷èíà óðîíà:
2705 if ShotType = WEAPON_IMP_FIRE then
2706 a := 5
2707 else
2708 if ShotType = WEAPON_CACO_FIRE then
2709 a := 20
2710 else
2711 a := 40;
2713 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2714 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2715 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2716 (Timeout < 1) then
2717 begin
2718 if ShotType = WEAPON_IMP_FIRE then
2719 s := 'FRAMES_EXPLODE_IMPFIRE'
2720 else
2721 if ShotType = WEAPON_CACO_FIRE then
2722 s := 'FRAMES_EXPLODE_CACOFIRE'
2723 else
2724 s := 'FRAMES_EXPLODE_BARONFIRE';
2726 // Âçðûâ:
2727 if g_Frames_Get(TextureID, s) then
2728 begin
2729 Anim := TAnimation.Create(TextureID, False, 6);
2730 Anim.Blending := False;
2731 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2732 Anim.Free();
2733 end;
2735 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2737 ShotType := 0;
2738 end;
2739 end;
2741 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2742 begin
2743 // Âûëåòåë èç âîäû:
2744 if WordBool(st and MOVE_HITAIR) then
2745 g_Obj_SetSpeed(@Obj, 16);
2747 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2748 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2749 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2750 (Timeout < 1) then
2751 begin
2752 // Âçðûâ:
2753 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2754 begin
2755 Anim := TAnimation.Create(TextureID, False, 6);
2756 Anim.Blending := False;
2757 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2758 Anim.Free();
2759 end;
2761 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2763 ShotType := 0;
2764 end;
2765 end;
2766 end; // case ShotType of...
2768 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2769 if (ShotType = 0) then
2770 begin
2771 if gGameSettings.GameType = GT_SERVER then
2772 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2773 if Animation <> nil then
2774 begin
2775 Animation.Free();
2776 Animation := nil;
2777 end;
2778 end
2779 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2780 if gGameSettings.GameType = GT_SERVER then
2781 MH_SEND_UpdateShot(i);
2782 end;
2783 end;
2784 end;
2786 procedure g_Weapon_Draw();
2787 var
2788 i: Integer;
2789 a: SmallInt;
2790 p: TPoint;
2791 begin
2792 if Shots = nil then
2793 Exit;
2795 for i := 0 to High(Shots) do
2796 if Shots[i].ShotType <> 0 then
2797 with Shots[i] do
2798 begin
2799 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2800 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2801 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2802 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2803 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2804 else
2805 a := 0;
2807 p.X := Obj.Rect.Width div 2;
2808 p.Y := Obj.Rect.Height div 2;
2810 if Animation <> nil then
2811 begin
2812 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2813 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2814 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2815 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2816 else
2817 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2818 end
2819 else if TextureID <> 0 then
2820 begin
2821 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2822 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2823 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2824 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2825 end;
2827 if g_debug_Frames then
2828 begin
2829 e_DrawQuad(Obj.X+Obj.Rect.X,
2830 Obj.Y+Obj.Rect.Y,
2831 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2832 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2833 0, 255, 0);
2834 end;
2835 end;
2836 end;
2838 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2839 var
2840 a: Integer;
2841 begin
2842 Result := False;
2844 if Shots = nil then
2845 Exit;
2847 for a := 0 to High(Shots) do
2848 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2849 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2850 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2851 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2852 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2853 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2854 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2855 begin
2856 Result := True;
2857 Exit;
2858 end;
2859 end;
2861 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2862 var
2863 count, i, j: Integer;
2864 dw: DWORD;
2865 begin
2866 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2867 count := 0;
2868 if Shots <> nil then
2869 for i := 0 to High(Shots) do
2870 if Shots[i].ShotType <> 0 then
2871 count := count + 1;
2873 Mem := TBinMemoryWriter.Create((count+1) * 80);
2875 // Êîëè÷åñòâî ñíàðÿäîâ:
2876 Mem.WriteInt(count);
2878 if count = 0 then
2879 Exit;
2881 for i := 0 to High(Shots) do
2882 if Shots[i].ShotType <> 0 then
2883 begin
2884 // Ñèãíàòóðà ñíàðÿäà:
2885 dw := SHOT_SIGNATURE; // 'SHOT'
2886 Mem.WriteDWORD(dw);
2887 // Òèï ñíàðÿäà:
2888 Mem.WriteByte(Shots[i].ShotType);
2889 // Öåëü:
2890 Mem.WriteWord(Shots[i].Target);
2891 // UID ñòðåëÿâøåãî:
2892 Mem.WriteWord(Shots[i].SpawnerUID);
2893 // Ðàçìåð ïîëÿ Triggers:
2894 dw := Length(Shots[i].Triggers);
2895 Mem.WriteDWORD(dw);
2896 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2897 for j := 0 to Integer(dw)-1 do
2898 Mem.WriteDWORD(Shots[i].Triggers[j]);
2899 // Îáúåêò ñíàðÿäà:
2900 Obj_SaveState(@Shots[i].Obj, Mem);
2901 // Êîñòûëèíà åáàíàÿ:
2902 Mem.WriteByte(Shots[i].Stopped);
2903 end;
2904 end;
2906 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2907 var
2908 count, i, j: Integer;
2909 dw: DWORD;
2910 begin
2911 if Mem = nil then
2912 Exit;
2914 // Êîëè÷åñòâî ñíàðÿäîâ:
2915 Mem.ReadInt(count);
2917 SetLength(Shots, count);
2919 if count = 0 then
2920 Exit;
2922 for i := 0 to count-1 do
2923 begin
2924 // Ñèãíàòóðà ñíàðÿäà:
2925 Mem.ReadDWORD(dw);
2926 if dw <> SHOT_SIGNATURE then // 'SHOT'
2927 begin
2928 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2929 end;
2930 // Òèï ñíàðÿäà:
2931 Mem.ReadByte(Shots[i].ShotType);
2932 // Öåëü:
2933 Mem.ReadWord(Shots[i].Target);
2934 // UID ñòðåëÿâøåãî:
2935 Mem.ReadWord(Shots[i].SpawnerUID);
2936 // Ðàçìåð ïîëÿ Triggers:
2937 Mem.ReadDWORD(dw);
2938 SetLength(Shots[i].Triggers, dw);
2939 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2940 for j := 0 to Integer(dw)-1 do
2941 Mem.ReadDWORD(Shots[i].Triggers[j]);
2942 // Îáúåêò ïðåäìåòà:
2943 Obj_LoadState(@Shots[i].Obj, Mem);
2944 // Êîñòûëèíà åáàíàÿ:
2945 Mem.ReadByte(Shots[i].Stopped);
2947 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2948 Shots[i].TextureID := DWORD(-1);
2949 Shots[i].Animation := nil;
2951 case Shots[i].ShotType of
2952 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2953 begin
2954 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2955 end;
2956 WEAPON_PLASMA:
2957 begin
2958 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2959 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2960 end;
2961 WEAPON_BFG:
2962 begin
2963 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2964 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2965 end;
2966 WEAPON_IMP_FIRE:
2967 begin
2968 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2969 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2970 end;
2971 WEAPON_BSP_FIRE:
2972 begin
2973 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2974 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2975 end;
2976 WEAPON_CACO_FIRE:
2977 begin
2978 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2979 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2980 end;
2981 WEAPON_BARON_FIRE:
2982 begin
2983 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2984 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2985 end;
2986 WEAPON_MANCUB_FIRE:
2987 begin
2988 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2989 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2990 end;
2991 end;
2992 end;
2993 end;
2995 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2996 var
2997 cx, cy: Integer;
2998 Anim: TAnimation;
2999 s: string;
3000 begin
3001 if Shots = nil then
3002 Exit;
3003 if (I > High(Shots)) or (I < 0) then Exit;
3005 with Shots[I] do
3006 begin
3007 if ShotType = 0 then Exit;
3008 Obj.X := X;
3009 Obj.Y := Y;
3010 cx := Obj.X + (Obj.Rect.Width div 2);
3011 cy := Obj.Y + (Obj.Rect.Height div 2);
3013 case ShotType of
3014 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
3015 begin
3016 if Loud then
3017 begin
3018 if ShotType = WEAPON_SKEL_FIRE then
3019 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
3020 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
3021 begin
3022 Anim := TAnimation.Create(TextureID, False, 8);
3023 Anim.Blending := False;
3024 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
3025 Anim.Free();
3026 end;
3027 end
3028 else
3029 begin // Âçðûâ Ðàêåòû
3030 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
3031 begin
3032 Anim := TAnimation.Create(TextureID, False, 6);
3033 Anim.Blending := False;
3034 g_GFX_OnceAnim(cx-64, cy-64, Anim);
3035 Anim.Free();
3036 end;
3037 end;
3038 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
3039 end;
3040 end;
3042 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
3043 begin
3044 if ShotType = WEAPON_PLASMA then
3045 s := 'FRAMES_EXPLODE_PLASMA'
3046 else
3047 s := 'FRAMES_EXPLODE_BSPFIRE';
3049 if g_Frames_Get(TextureID, s) and loud then
3050 begin
3051 Anim := TAnimation.Create(TextureID, False, 3);
3052 Anim.Blending := False;
3053 g_GFX_OnceAnim(cx-16, cy-16, Anim);
3054 Anim.Free();
3056 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
3057 end;
3058 end;
3060 WEAPON_BFG: // BFG
3061 begin
3062 // Âçðûâ BFG:
3063 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
3064 begin
3065 Anim := TAnimation.Create(TextureID, False, 6);
3066 Anim.Blending := False;
3067 g_GFX_OnceAnim(cx-64, cy-64, Anim);
3068 Anim.Free();
3070 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
3071 end;
3072 end;
3074 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
3075 begin
3076 if ShotType = WEAPON_IMP_FIRE then
3077 s := 'FRAMES_EXPLODE_IMPFIRE'
3078 else
3079 if ShotType = WEAPON_CACO_FIRE then
3080 s := 'FRAMES_EXPLODE_CACOFIRE'
3081 else
3082 s := 'FRAMES_EXPLODE_BARONFIRE';
3084 if g_Frames_Get(TextureID, s) and Loud then
3085 begin
3086 Anim := TAnimation.Create(TextureID, False, 6);
3087 Anim.Blending := False;
3088 g_GFX_OnceAnim(cx-32, cy-32, Anim);
3089 Anim.Free();
3091 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
3092 end;
3093 end;
3095 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
3096 begin
3097 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
3098 begin
3099 Anim := TAnimation.Create(TextureID, False, 6);
3100 Anim.Blending := False;
3101 g_GFX_OnceAnim(cx-64, cy-64, Anim);
3102 Anim.Free();
3104 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
3105 end;
3106 end;
3107 end; // case ShotType of...
3109 ShotType := 0;
3110 Animation.Free();
3111 end;
3112 end;
3115 procedure g_Weapon_AddDynLights();
3116 var
3117 i: Integer;
3118 begin
3119 if Shots = nil then Exit;
3120 for i := 0 to High(Shots) do
3121 begin
3122 if Shots[i].ShotType = 0 then continue;
3123 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
3124 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
3125 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
3126 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
3127 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
3128 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
3129 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
3130 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
3131 (Shots[i].ShotType = WEAPON_PLASMA) or
3132 (Shots[i].ShotType = WEAPON_BFG) or
3133 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
3134 false then
3135 begin
3136 if (Shots[i].ShotType = WEAPON_PLASMA) then
3137 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)
3138 else if (Shots[i].ShotType = WEAPON_BFG) then
3139 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)
3140 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
3141 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)
3142 else
3143 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);
3144 end;
3145 end;
3146 end;
3149 procedure TShot.positionChanged (); begin end;
3152 end.