DEADSOFTWARE

Cosmetic: DooM 2D:Forever -> Doom 2D: Forever
[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 SysUtils, Classes,
24 g_textures, g_basic, e_graphics, g_phys, xprofiler;
27 type
28 TShot = record
29 ShotType: Byte;
30 Target: Word;
31 SpawnerUID: Word;
32 Triggers: DWArray;
33 Obj: TObj;
34 Animation: TAnimation;
35 TextureID: DWORD;
36 Timeout: DWORD;
37 Stopped: Byte;
39 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
40 end;
43 var
44 Shots: array of TShot = nil;
45 LastShotID: Integer = 0;
47 procedure g_Weapon_LoadData();
48 procedure g_Weapon_FreeData();
49 procedure g_Weapon_Init();
50 procedure g_Weapon_Free();
51 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
52 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
53 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
55 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
56 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
57 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
58 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
59 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
60 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
61 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
62 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
63 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
64 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
65 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
66 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
67 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
68 procedure g_Weapon_bfghit(x, y: Integer);
69 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
70 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
71 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
72 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
74 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
75 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
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_KASTET = 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_SUPERPULEMET = 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_KASTET;
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 := True;
466 if t = HIT_FLAME then
467 m.CatchFire(SpawnerUID);
468 end
469 else
470 Result := True;
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, 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 := 32;
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.X := X;
742 Shots[find_id].Obj.Y := Y;
743 Shots[find_id].Obj.Vel.X := XV;
744 Shots[find_id].Obj.Vel.Y := YV;
745 Shots[find_id].Obj.Accel.X := 0;
746 Shots[find_id].Obj.Accel.Y := 0;
747 Shots[find_id].SpawnerUID := Spawner;
748 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
749 Shots[find_id].Stopped := 255
750 else
751 Shots[find_id].Stopped := 0;
752 Result := find_id;
753 end;
755 procedure throw(i, x, y, xd, yd, s: Integer);
756 var
757 a: Integer;
758 begin
759 yd := yd - y;
760 xd := xd - x;
762 a := Max(Abs(xd), Abs(yd));
763 if a = 0 then
764 a := 1;
766 Shots[i].Obj.X := x;
767 Shots[i].Obj.Y := y;
768 Shots[i].Obj.Vel.X := (xd*s) div a;
769 Shots[i].Obj.Vel.Y := (yd*s) div a;
770 Shots[i].Obj.Accel.X := 0;
771 Shots[i].Obj.Accel.Y := 0;
772 Shots[i].Stopped := 0;
773 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
774 Shots[i].Timeout := 900 // ~25 sec
775 else
776 begin
777 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
778 Shots[i].Timeout := SHOT_FLAME_LIFETIME
779 else
780 Shots[i].Timeout := 550; // ~15 sec
781 end;
782 end;
784 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
785 var
786 i, h: Integer;
788 function PlayerHit(Team: Byte = 0): Boolean;
789 var
790 i: Integer;
791 ChkTeam: Boolean;
792 p: TPlayer;
793 begin
794 Result := False;
795 h := High(gPlayers);
797 if h <> -1 then
798 for i := 0 to h do
799 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
800 begin
801 ChkTeam := True;
802 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
803 begin
804 p := g_Player_Get(SpawnerUID);
805 if p <> nil then
806 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
807 end;
808 if ChkTeam then
809 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
810 begin
811 if t <> HIT_FLAME then
812 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
813 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
814 if t = HIT_BFG then
815 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
816 Result := True;
817 break;
818 end;
819 end;
820 end;
823 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
824 begin
825 result := false; // don't stop
826 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
827 begin
828 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
829 begin
830 if (t <> HIT_FLAME) then
831 begin
832 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
833 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
834 end;
835 result := True;
836 end;
837 end;
838 end;
841 function monsCheckHit (mon: TMonster): Boolean;
842 begin
843 result := false; // don't stop
844 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
845 begin
846 if (t <> HIT_FLAME) then
847 begin
848 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
849 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
850 end;
851 result := true;
852 end;
853 end;
855 function MonsterHit(): Boolean;
856 begin
857 //result := g_Mons_ForEach(monsCheckHit);
858 //FIXME: accelerate this!
859 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
860 end;
862 begin
863 Result := 0;
865 if HitCorpses then
866 begin
867 h := High(gCorpses);
869 if gAdvCorpses and (h <> -1) then
870 for i := 0 to h do
871 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
872 g_Obj_Collide(obj, @gCorpses[i].Obj) then
873 begin
874 // Ðàñïèëèâàåì òðóï:
875 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
876 (obj^.Vel.Y+obj^.Accel.Y) div 4);
877 Result := 1;
878 end;
879 end;
881 case gGameSettings.GameMode of
882 // Êàìïàíèÿ:
883 GM_COOP, GM_SINGLE:
884 begin
885 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
886 if MonsterHit() then
887 begin
888 Result := 2;
889 Exit;
890 end;
892 if PlayerHit() then
893 begin
894 Result := 1;
895 Exit;
896 end;
897 end;
899 // Äåçìàò÷:
900 GM_DM:
901 begin
902 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
903 if PlayerHit() then
904 begin
905 Result := 1;
906 Exit;
907 end;
909 if MonsterHit() then
910 begin
911 Result := 2;
912 Exit;
913 end;
914 end;
916 // Êîìàíäíûå:
917 GM_TDM, GM_CTF:
918 begin
919 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
920 if PlayerHit(2) then
921 begin
922 Result := 1;
923 Exit;
924 end;
926 // Ïîòîì ìîíñòðîâ
927 if MonsterHit() then
928 begin
929 Result := 2;
930 Exit;
931 end;
933 // È â êîíöå ñâîèõ èãðîêîâ
934 if PlayerHit(1) then
935 begin
936 Result := 1;
937 Exit;
938 end;
939 end;
941 end;
942 end;
944 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
945 begin
946 Result := False;
948 case g_GetUIDType(UID) of
949 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
950 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
951 else Exit;
952 end;
953 end;
955 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
956 var
957 r: Integer; // squared radius
959 function monsExCheck (mon: TMonster): Boolean;
960 var
961 dx, dy, mm: Integer;
962 begin
963 result := false; // don't stop
964 begin
965 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
966 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
968 if dx > 1000 then dx := 1000;
969 if dy > 1000 then dy := 1000;
971 if (dx*dx+dy*dy < r) then
972 begin
973 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
974 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
976 mm := Max(abs(dx), abs(dy));
977 if mm = 0 then mm := 1;
979 if mon.alive then
980 begin
981 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
982 end;
984 mon.Push((dx*7) div mm, (dy*7) div mm);
985 end;
986 end;
987 end;
989 var
990 i, h, dx, dy, m, mm: Integer;
991 _angle: SmallInt;
992 begin
993 result := false;
995 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
997 r := rad*rad;
999 h := High(gPlayers);
1001 if h <> -1 then
1002 for i := 0 to h do
1003 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1004 with gPlayers[i] do
1005 begin
1006 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1007 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1009 if dx > 1000 then dx := 1000;
1010 if dy > 1000 then dy := 1000;
1012 if dx*dx+dy*dy < r then
1013 begin
1014 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1015 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1017 mm := Max(abs(dx), abs(dy));
1018 if mm = 0 then mm := 1;
1020 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1021 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1022 end;
1023 end;
1025 //g_Mons_ForEach(monsExCheck);
1026 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1028 h := High(gCorpses);
1030 if gAdvCorpses and (h <> -1) then
1031 for i := 0 to h do
1032 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1033 with gCorpses[i] do
1034 begin
1035 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1036 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1038 if dx > 1000 then dx := 1000;
1039 if dy > 1000 then dy := 1000;
1041 if dx*dx+dy*dy < r then
1042 begin
1043 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1044 Obj.Rect.Width, Obj.Rect.Height);
1046 mm := Max(abs(dx), abs(dy));
1047 if mm = 0 then mm := 1;
1049 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1050 end;
1051 end;
1053 h := High(gGibs);
1055 if gAdvGibs and (h <> -1) then
1056 for i := 0 to h do
1057 if gGibs[i].alive then
1058 with gGibs[i] do
1059 begin
1060 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1061 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1063 if dx > 1000 then dx := 1000;
1064 if dy > 1000 then dy := 1000;
1066 if dx*dx+dy*dy < r then
1067 begin
1068 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1069 Obj.Rect.Width, Obj.Rect.Height);
1070 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1071 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1073 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1074 positionChanged(); // this updates spatial accelerators
1075 end;
1076 end;
1077 end;
1079 procedure g_Weapon_Init();
1080 begin
1081 CreateWaterMap();
1082 end;
1084 procedure g_Weapon_Free();
1085 var
1086 i: Integer;
1087 begin
1088 if Shots <> nil then
1089 begin
1090 for i := 0 to High(Shots) do
1091 if Shots[i].ShotType <> 0 then
1092 Shots[i].Animation.Free();
1094 Shots := nil;
1095 end;
1097 WaterMap := nil;
1098 end;
1100 procedure g_Weapon_LoadData();
1101 begin
1102 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1104 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1105 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1106 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1107 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1119 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1129 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1130 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1131 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1132 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1133 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1134 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1135 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1137 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1138 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1139 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1140 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1141 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1142 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1143 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1144 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1145 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1146 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1147 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1148 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1149 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1150 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1151 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1152 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1153 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1154 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1155 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1156 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1157 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1159 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1160 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1162 //wgunMonHash := hashNewIntInt();
1163 wgunHitHeap := TBinaryHeapHitTimes.Create();
1164 end;
1166 procedure g_Weapon_FreeData();
1167 begin
1168 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1170 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1171 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1172 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1173 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1174 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1175 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1176 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1177 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1178 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1179 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1180 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1181 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1182 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1183 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1184 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1185 g_Sound_Delete('SOUND_FIRE');
1186 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1187 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1188 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1189 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1190 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1191 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1192 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1193 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1194 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1195 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1196 g_Sound_Delete('SOUND_PLAYER_JETON');
1197 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1198 g_Sound_Delete('SOUND_PLAYER_CASING1');
1199 g_Sound_Delete('SOUND_PLAYER_CASING2');
1200 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1201 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1203 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1204 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1205 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1206 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1207 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1208 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1209 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1210 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1211 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1212 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1213 g_Frames_DeleteByName('FRAMES_BFGHIT');
1214 g_Frames_DeleteByName('FRAMES_FIRE');
1215 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1216 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1217 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1218 g_Frames_DeleteByName('FRAMES_SMOKE');
1219 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1220 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1221 end;
1224 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1225 var
1226 i: Integer;
1227 begin
1228 result := false;
1229 for i := 0 to High(gPlayers) do
1230 begin
1231 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1232 begin
1233 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1234 begin
1235 if AllowPush then gPlayers[i].Push(vx, vy);
1236 result := true;
1237 end;
1238 end;
1239 end;
1240 end;
1243 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1245 function monsCheck (mon: TMonster): Boolean;
1246 begin
1247 result := false; // don't stop
1248 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1249 begin
1250 if AllowPush then mon.Push(vx, vy);
1251 result := true;
1252 end;
1253 end;
1255 begin
1256 result := 0;
1257 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1258 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1259 end;
1262 (*
1263 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1264 var
1265 a: Integer;
1266 x2, y2: Integer;
1267 dx, dy: Integer;
1268 xe, ye: Integer;
1269 xi, yi: Integer;
1270 s, c: Extended;
1271 //vx, vy: Integer;
1272 xx, yy, d: Integer;
1273 i: Integer;
1274 t1, _collide: Boolean;
1275 w, h: Word;
1276 {$IF DEFINED(D2F_DEBUG)}
1277 stt: UInt64;
1278 showTime: Boolean = true;
1279 {$ENDIF}
1280 begin
1281 a := GetAngle(x, y, xd, yd)+180;
1283 SinCos(DegToRad(-a), s, c);
1285 if Abs(s) < 0.01 then s := 0;
1286 if Abs(c) < 0.01 then c := 0;
1288 x2 := x+Round(c*gMapInfo.Width);
1289 y2 := y+Round(s*gMapInfo.Width);
1291 t1 := gWalls <> nil;
1292 _collide := False;
1293 w := gMapInfo.Width;
1294 h := gMapInfo.Height;
1296 xe := 0;
1297 ye := 0;
1298 dx := x2-x;
1299 dy := y2-y;
1301 if (xd = 0) and (yd = 0) then Exit;
1303 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1304 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1306 dx := Abs(dx);
1307 dy := Abs(dy);
1309 if dx > dy then d := dx else d := dy;
1311 //blood vel, for Monster.Damage()
1312 //vx := (dx*10 div d)*xi;
1313 //vy := (dy*10 div d)*yi;
1315 {$IF DEFINED(D2F_DEBUG)}
1316 stt := getTimeMicro();
1317 {$ENDIF}
1319 xx := x;
1320 yy := y;
1322 for i := 1 to d do
1323 begin
1324 xe := xe+dx;
1325 ye := ye+dy;
1327 if xe > d then
1328 begin
1329 xe := xe-d;
1330 xx := xx+xi;
1331 end;
1333 if ye > d then
1334 begin
1335 ye := ye-d;
1336 yy := yy+yi;
1337 end;
1339 if (yy > h) or (yy < 0) then Break;
1340 if (xx > w) or (xx < 0) then Break;
1342 if t1 then
1343 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1344 begin
1345 _collide := True;
1346 {$IF DEFINED(D2F_DEBUG)}
1347 stt := getTimeMicro()-stt;
1348 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1349 showTime := false;
1350 {$ENDIF}
1351 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1352 if g_Game_IsServer and g_Game_IsNet then
1353 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1354 end;
1356 if not _collide then
1357 begin
1358 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1359 end;
1361 if _collide then Break;
1362 end;
1364 {$IF DEFINED(D2F_DEBUG)}
1365 if showTime then
1366 begin
1367 stt := getTimeMicro()-stt;
1368 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1369 end;
1370 {$ENDIF}
1372 if CheckTrigger and g_Game_IsServer then
1373 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1374 end;
1375 *)
1378 //!!!FIXME!!!
1379 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1380 var
1381 x0, y0: Integer;
1382 x2, y2: Integer;
1383 xi, yi: Integer;
1384 wallDistSq: Integer = $3fffffff;
1386 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1387 begin
1388 result := false;
1389 if (idx < 0) or (idx > High(gPlayers)) then exit;
1390 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1391 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1392 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1393 {$IF DEFINED(D2F_DEBUG)}
1394 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1395 {$ENDIF}
1396 end;
1398 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1399 begin
1400 result := false;
1401 if (mon = nil) then exit;
1402 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1403 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1404 {$IF DEFINED(D2F_DEBUG)}
1405 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1406 {$ENDIF}
1407 end;
1409 // collect players along hitray
1410 // return `true` if instant hit was detected
1411 function playerPossibleHit (): Boolean;
1412 var
1413 i: Integer;
1414 px, py, pw, ph: Integer;
1415 inx, iny: Integer;
1416 distSq: Integer;
1417 plr: TPlayer;
1418 begin
1419 result := false;
1420 for i := 0 to High(gPlayers) do
1421 begin
1422 plr := gPlayers[i];
1423 if (plr <> nil) and plr.alive then
1424 begin
1425 plr.getMapBox(px, py, pw, ph);
1426 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1427 begin
1428 distSq := distanceSq(x, y, inx, iny);
1429 if (distSq = 0) then
1430 begin
1431 // contains
1432 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1433 end
1434 else if (distSq < wallDistSq) then
1435 begin
1436 appendHitTimePlr(distSq, i, inx, iny);
1437 end;
1438 end;
1439 end;
1440 end;
1441 end;
1443 function sqchecker (mon: TMonster; tag: Integer): Boolean;
1444 var
1445 mx, my, mw, mh: Integer;
1446 inx, iny: Integer;
1447 distSq: Integer;
1448 begin
1449 result := false; // don't stop
1450 mon.getMapBox(mx, my, mw, mh);
1451 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1452 begin
1453 distSq := distanceSq(x0, y0, inx, iny);
1454 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1455 end;
1456 end;
1458 var
1459 a: Integer;
1460 dx, dy: Integer;
1461 xe, ye: Integer;
1462 s, c: Extended;
1463 i: Integer;
1464 wallHitFlag: Boolean = false;
1465 wallHitX: Integer = 0;
1466 wallHitY: Integer = 0;
1467 didHit: Boolean = false;
1468 {$IF DEFINED(D2F_DEBUG)}
1469 stt: UInt64;
1470 {$ENDIF}
1471 begin
1472 (*
1473 if not gwep_debug_fast_trace then
1474 begin
1475 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1476 exit;
1477 end;
1478 *)
1480 if (xd = 0) and (yd = 0) then exit;
1482 //wgunMonHash.reset(); //FIXME: clear hash on level change
1483 wgunHitHeap.clear();
1484 wgunHitTimeUsed := 0;
1486 a := GetAngle(x, y, xd, yd)+180;
1488 SinCos(DegToRad(-a), s, c);
1490 if Abs(s) < 0.01 then s := 0;
1491 if Abs(c) < 0.01 then c := 0;
1493 x0 := x;
1494 y0 := y;
1495 x2 := x+Round(c*gMapInfo.Width);
1496 y2 := y+Round(s*gMapInfo.Width);
1498 dx := x2-x;
1499 dy := y2-y;
1501 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1502 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1504 {$IF DEFINED(D2F_DEBUG)}
1505 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1506 stt := getTimeMicro();
1507 {$ENDIF}
1509 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1510 if wallHitFlag then
1511 begin
1512 x2 := wallHitX;
1513 y2 := wallHitY;
1514 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1515 end
1516 else
1517 begin
1518 wallHitX := x2;
1519 wallHitY := y2;
1520 end;
1522 if playerPossibleHit() then exit; // instant hit
1524 // collect monsters
1525 g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1527 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1528 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1529 while (wgunHitHeap.count > 0) do
1530 begin
1531 // has some entities to check, do it
1532 i := wgunHitHeap.front;
1533 wgunHitHeap.popFront();
1534 // hitpoint
1535 xe := wgunHitTime[i].x;
1536 ye := wgunHitTime[i].y;
1537 // check if it is not behind the wall
1538 if (wgunHitTime[i].mon <> nil) then
1539 begin
1540 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1541 end
1542 else
1543 begin
1544 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1545 end;
1546 if didHit then
1547 begin
1548 // need new coords for trigger
1549 wallHitX := xe;
1550 wallHitY := ye;
1551 wallHitFlag := false; // no sparks
1552 break;
1553 end;
1554 end;
1556 // need sparks?
1557 if wallHitFlag then
1558 begin
1559 {$IF DEFINED(D2F_DEBUG)}
1560 stt := getTimeMicro()-stt;
1561 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1562 {$ENDIF}
1563 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1564 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1565 end
1566 else
1567 begin
1568 {$IF DEFINED(D2F_DEBUG)}
1569 stt := getTimeMicro()-stt;
1570 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1571 {$ENDIF}
1572 end;
1574 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1575 end;
1578 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1579 var
1580 obj: TObj;
1581 begin
1582 obj.X := X;
1583 obj.Y := Y;
1584 obj.rect.X := 0;
1585 obj.rect.Y := 0;
1586 obj.rect.Width := 39;
1587 obj.rect.Height := 52;
1588 obj.Vel.X := 0;
1589 obj.Vel.Y := 0;
1590 obj.Accel.X := 0;
1591 obj.Accel.Y := 0;
1593 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1594 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1595 else
1596 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1597 end;
1599 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1600 var
1601 obj: TObj;
1602 begin
1603 obj.X := X;
1604 obj.Y := Y;
1605 obj.rect.X := 0;
1606 obj.rect.Y := 0;
1607 obj.rect.Width := 32;
1608 obj.rect.Height := 52;
1609 obj.Vel.X := 0;
1610 obj.Vel.Y := 0;
1611 obj.Accel.X := 0;
1612 obj.Accel.Y := 0;
1614 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1615 end;
1617 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1618 Silent: Boolean = False);
1619 var
1620 find_id: DWORD;
1621 dx, dy: Integer;
1622 begin
1623 if WID < 0 then
1624 find_id := FindShot()
1625 else
1626 begin
1627 find_id := WID;
1628 if Integer(find_id) >= High(Shots) then
1629 SetLength(Shots, find_id + 64)
1630 end;
1632 with Shots[find_id] do
1633 begin
1634 g_Obj_Init(@Obj);
1636 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1637 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1639 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1640 dy := -(Obj.Rect.Height div 2);
1642 ShotType := WEAPON_ROCKETLAUNCHER;
1643 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1645 Animation := nil;
1646 triggers := nil;
1647 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1648 end;
1650 Shots[find_id].SpawnerUID := SpawnerUID;
1652 if not Silent then
1653 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1654 end;
1656 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1657 WID: Integer = -1; Silent: Boolean = False);
1658 var
1659 find_id, FramesID: DWORD;
1660 dx, dy: Integer;
1661 begin
1662 if WID < 0 then
1663 find_id := FindShot()
1664 else
1665 begin
1666 find_id := WID;
1667 if Integer(find_id) >= High(Shots) then
1668 SetLength(Shots, find_id + 64)
1669 end;
1671 with Shots[find_id] do
1672 begin
1673 g_Obj_Init(@Obj);
1675 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1676 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1678 dx := -(Obj.Rect.Width div 2);
1679 dy := -(Obj.Rect.Height div 2);
1681 ShotType := WEAPON_SKEL_FIRE;
1682 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1684 triggers := nil;
1685 target := TargetUID;
1686 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1687 Animation := TAnimation.Create(FramesID, True, 5);
1688 end;
1690 Shots[find_id].SpawnerUID := SpawnerUID;
1692 if not Silent then
1693 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1694 end;
1696 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1697 Silent: Boolean = False);
1698 var
1699 find_id, FramesID: DWORD;
1700 dx, dy: Integer;
1701 begin
1702 if WID < 0 then
1703 find_id := FindShot()
1704 else
1705 begin
1706 find_id := WID;
1707 if Integer(find_id) >= High(Shots) then
1708 SetLength(Shots, find_id + 64);
1709 end;
1711 with Shots[find_id] do
1712 begin
1713 g_Obj_Init(@Obj);
1715 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1716 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1718 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1719 dy := -(Obj.Rect.Height div 2);
1721 ShotType := WEAPON_PLASMA;
1722 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1724 triggers := nil;
1725 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1726 Animation := TAnimation.Create(FramesID, True, 5);
1727 end;
1729 Shots[find_id].SpawnerUID := SpawnerUID;
1731 if not Silent then
1732 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1733 end;
1735 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1736 Silent: Boolean = False);
1737 var
1738 find_id: DWORD;
1739 dx, dy: Integer;
1740 begin
1741 if WID < 0 then
1742 find_id := FindShot()
1743 else
1744 begin
1745 find_id := WID;
1746 if Integer(find_id) >= High(Shots) then
1747 SetLength(Shots, find_id + 64);
1748 end;
1750 with Shots[find_id] do
1751 begin
1752 g_Obj_Init(@Obj);
1754 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1755 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1757 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1758 dy := -(Obj.Rect.Height div 2);
1760 ShotType := WEAPON_FLAMETHROWER;
1761 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1763 triggers := nil;
1764 Animation := nil;
1765 TextureID := 0;
1766 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1767 end;
1769 Shots[find_id].SpawnerUID := SpawnerUID;
1771 // if not Silent then
1772 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1773 end;
1775 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1776 Silent: Boolean = False);
1777 var
1778 find_id, FramesID: DWORD;
1779 dx, dy: Integer;
1780 begin
1781 if WID < 0 then
1782 find_id := FindShot()
1783 else
1784 begin
1785 find_id := WID;
1786 if Integer(find_id) >= High(Shots) then
1787 SetLength(Shots, find_id + 64)
1788 end;
1790 with Shots[find_id] do
1791 begin
1792 g_Obj_Init(@Obj);
1794 Obj.Rect.Width := 16;
1795 Obj.Rect.Height := 16;
1797 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1798 dy := -(Obj.Rect.Height div 2);
1800 ShotType := WEAPON_IMP_FIRE;
1801 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1803 triggers := nil;
1804 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1805 Animation := TAnimation.Create(FramesID, True, 4);
1806 end;
1808 Shots[find_id].SpawnerUID := SpawnerUID;
1810 if not Silent then
1811 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1812 end;
1814 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1815 Silent: Boolean = False);
1816 var
1817 find_id, FramesID: DWORD;
1818 dx, dy: Integer;
1819 begin
1820 if WID < 0 then
1821 find_id := FindShot()
1822 else
1823 begin
1824 find_id := WID;
1825 if Integer(find_id) >= High(Shots) then
1826 SetLength(Shots, find_id + 64)
1827 end;
1829 with Shots[find_id] do
1830 begin
1831 g_Obj_Init(@Obj);
1833 Obj.Rect.Width := 16;
1834 Obj.Rect.Height := 16;
1836 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1837 dy := -(Obj.Rect.Height div 2);
1839 ShotType := WEAPON_CACO_FIRE;
1840 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1842 triggers := nil;
1843 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1844 Animation := TAnimation.Create(FramesID, True, 4);
1845 end;
1847 Shots[find_id].SpawnerUID := SpawnerUID;
1849 if not Silent then
1850 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1851 end;
1853 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1854 Silent: Boolean = False);
1855 var
1856 find_id, FramesID: DWORD;
1857 dx, dy: Integer;
1858 begin
1859 if WID < 0 then
1860 find_id := FindShot()
1861 else
1862 begin
1863 find_id := WID;
1864 if Integer(find_id) >= High(Shots) then
1865 SetLength(Shots, find_id + 64)
1866 end;
1868 with Shots[find_id] do
1869 begin
1870 g_Obj_Init(@Obj);
1872 Obj.Rect.Width := 32;
1873 Obj.Rect.Height := 16;
1875 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1876 dy := -(Obj.Rect.Height div 2);
1878 ShotType := WEAPON_BARON_FIRE;
1879 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1881 triggers := nil;
1882 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1883 Animation := TAnimation.Create(FramesID, True, 4);
1884 end;
1886 Shots[find_id].SpawnerUID := SpawnerUID;
1888 if not Silent then
1889 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1890 end;
1892 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1893 Silent: Boolean = False);
1894 var
1895 find_id, FramesID: DWORD;
1896 dx, dy: Integer;
1897 begin
1898 if WID < 0 then
1899 find_id := FindShot()
1900 else
1901 begin
1902 find_id := WID;
1903 if Integer(find_id) >= High(Shots) then
1904 SetLength(Shots, find_id + 64)
1905 end;
1907 with Shots[find_id] do
1908 begin
1909 g_Obj_Init(@Obj);
1911 Obj.Rect.Width := 16;
1912 Obj.Rect.Height := 16;
1914 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1915 dy := -(Obj.Rect.Height div 2);
1917 ShotType := WEAPON_BSP_FIRE;
1918 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1920 triggers := nil;
1922 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1923 Animation := TAnimation.Create(FramesID, True, 4);
1924 end;
1926 Shots[find_id].SpawnerUID := SpawnerUID;
1928 if not Silent then
1929 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1930 end;
1932 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1933 Silent: Boolean = False);
1934 var
1935 find_id, FramesID: DWORD;
1936 dx, dy: Integer;
1937 begin
1938 if WID < 0 then
1939 find_id := FindShot()
1940 else
1941 begin
1942 find_id := WID;
1943 if Integer(find_id) >= High(Shots) then
1944 SetLength(Shots, find_id + 64)
1945 end;
1947 with Shots[find_id] do
1948 begin
1949 g_Obj_Init(@Obj);
1951 Obj.Rect.Width := 32;
1952 Obj.Rect.Height := 32;
1954 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1955 dy := -(Obj.Rect.Height div 2);
1957 ShotType := WEAPON_MANCUB_FIRE;
1958 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1960 triggers := nil;
1962 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1963 Animation := TAnimation.Create(FramesID, True, 4);
1964 end;
1966 Shots[find_id].SpawnerUID := SpawnerUID;
1968 if not Silent then
1969 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1970 end;
1972 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1973 Silent: Boolean = False);
1974 var
1975 find_id, FramesID: DWORD;
1976 dx, dy: Integer;
1977 begin
1978 if WID < 0 then
1979 find_id := FindShot()
1980 else
1981 begin
1982 find_id := WID;
1983 if Integer(find_id) >= High(Shots) then
1984 SetLength(Shots, find_id + 64)
1985 end;
1987 with Shots[find_id] do
1988 begin
1989 g_Obj_Init(@Obj);
1991 Obj.Rect.Width := SHOT_BFG_WIDTH;
1992 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1994 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1995 dy := -(Obj.Rect.Height div 2);
1997 ShotType := WEAPON_BFG;
1998 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2000 triggers := nil;
2001 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2002 Animation := TAnimation.Create(FramesID, True, 6);
2003 end;
2005 Shots[find_id].SpawnerUID := SpawnerUID;
2007 if not Silent then
2008 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2009 end;
2011 procedure g_Weapon_bfghit(x, y: Integer);
2012 var
2013 ID: DWORD;
2014 Anim: TAnimation;
2015 begin
2016 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2017 begin
2018 Anim := TAnimation.Create(ID, False, 4);
2019 g_GFX_OnceAnim(x-32, y-32, Anim);
2020 Anim.Free();
2021 end;
2022 end;
2024 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2025 Silent: Boolean = False);
2026 begin
2027 if not Silent then
2028 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2030 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2031 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2032 begin
2033 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2034 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2035 end;
2036 end;
2038 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2039 Silent: Boolean = False);
2040 begin
2041 if not Silent then
2042 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2044 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2045 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2046 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2047 begin
2048 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2049 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2050 end;
2051 end;
2053 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2054 Silent: Boolean = False);
2055 var
2056 i, j: Integer;
2057 begin
2058 if not Silent then
2059 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2061 for i := 0 to 9 do
2062 begin
2063 j := Random(17)-8; // -8 .. 8
2064 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2065 end;
2066 end;
2068 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2069 Silent: Boolean = False);
2070 var
2071 a, i, j: Integer;
2072 begin
2073 if not Silent then
2074 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2076 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2077 for i := 0 to a do
2078 begin
2079 j := Random(41)-20; // -20 .. 20
2080 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2081 end;
2082 end;
2084 procedure g_Weapon_Update();
2085 var
2086 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2087 _id: DWORD;
2088 Anim: TAnimation;
2089 t: DWArray;
2090 st: Word;
2091 s: String;
2092 o: TObj;
2093 spl: Boolean;
2094 Loud: Boolean;
2095 tcx, tcy: Integer;
2096 begin
2097 if Shots = nil then
2098 Exit;
2100 for i := 0 to High(Shots) do
2101 begin
2102 if Shots[i].ShotType = 0 then
2103 Continue;
2105 Loud := True;
2107 with Shots[i] do
2108 begin
2109 Timeout := Timeout - 1;
2110 oldvx := Obj.Vel.X;
2111 oldvy := Obj.Vel.Y;
2112 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2113 if (Stopped = 0) and g_Game_IsServer then
2114 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2115 SpawnerUID, ACTIVATE_SHOT, triggers)
2116 else
2117 t := nil;
2119 if t <> nil then
2120 begin
2121 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2122 if triggers = nil then
2123 triggers := t
2124 else
2125 begin
2126 h := High(t);
2128 for a := 0 to h do
2129 if not InDWArray(t[a], triggers) then
2130 begin
2131 SetLength(triggers, Length(triggers)+1);
2132 triggers[High(triggers)] := t[a];
2133 end;
2134 end;
2135 end;
2137 // Àíèìàöèÿ ñíàðÿäà:
2138 if Animation <> nil then
2139 Animation.Update();
2141 // Äâèæåíèå:
2142 spl := (ShotType <> WEAPON_PLASMA) and
2143 (ShotType <> WEAPON_BFG) and
2144 (ShotType <> WEAPON_BSP_FIRE) and
2145 (ShotType <> WEAPON_FLAMETHROWER);
2147 if Stopped = 0 then
2148 begin
2149 st := g_Obj_Move(@Obj, False, spl);
2150 end
2151 else
2152 begin
2153 st := 0;
2154 end;
2155 positionChanged(); // this updates spatial accelerators
2157 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2158 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2159 begin
2160 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2161 ShotType := 0;
2162 Animation.Free();
2163 Continue;
2164 end;
2166 cx := Obj.X + (Obj.Rect.Width div 2);
2167 cy := Obj.Y + (Obj.Rect.Height div 2);
2169 case ShotType of
2170 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2171 begin
2172 // Âûëåòåëà èç âîäû:
2173 if WordBool(st and MOVE_HITAIR) then
2174 g_Obj_SetSpeed(@Obj, 12);
2176 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2177 if WordBool(st and MOVE_INWATER) then
2178 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2179 Obj.Y+(Obj.Rect.Height div 2),
2180 1+Random(3), 16, 16)
2181 else
2182 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2183 begin
2184 Anim := TAnimation.Create(_id, False, 3);
2185 Anim.Alpha := 150;
2186 g_GFX_OnceAnim(Obj.X-14+Random(9),
2187 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2188 Anim, ONCEANIM_SMOKE);
2189 Anim.Free();
2190 end;
2192 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2193 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2194 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2195 (Timeout < 1) then
2196 begin
2197 Obj.Vel.X := 0;
2198 Obj.Vel.Y := 0;
2200 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2202 if ShotType = WEAPON_SKEL_FIRE then
2203 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2204 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2205 begin
2206 Anim := TAnimation.Create(TextureID, False, 8);
2207 Anim.Blending := False;
2208 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2209 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2210 Anim.Free();
2211 end;
2212 end
2213 else
2214 begin // Âçðûâ Ðàêåòû
2215 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2216 begin
2217 Anim := TAnimation.Create(TextureID, False, 6);
2218 Anim.Blending := False;
2219 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2220 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2221 Anim.Free();
2222 end;
2223 end;
2225 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2227 ShotType := 0;
2228 end;
2230 if ShotType = WEAPON_SKEL_FIRE then
2231 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2232 if GetPos(target, @o) then
2233 throw(i, Obj.X, Obj.Y,
2234 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2235 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2236 12);
2237 end;
2238 end;
2240 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2241 begin
2242 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2243 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2244 begin
2245 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2246 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2247 ShotType := 0;
2248 Continue;
2249 end;
2251 // Âåëè÷èíà óðîíà:
2252 if (ShotType = WEAPON_PLASMA) and
2253 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2254 a := 10
2255 else
2256 a := 5;
2258 if ShotType = WEAPON_BSP_FIRE then
2259 a := 10;
2261 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2262 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2263 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2264 (Timeout < 1) then
2265 begin
2266 if ShotType = WEAPON_PLASMA then
2267 s := 'FRAMES_EXPLODE_PLASMA'
2268 else
2269 s := 'FRAMES_EXPLODE_BSPFIRE';
2271 // Âçðûâ Ïëàçìû:
2272 if g_Frames_Get(TextureID, s) then
2273 begin
2274 Anim := TAnimation.Create(TextureID, False, 3);
2275 Anim.Blending := False;
2276 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2277 Anim.Free();
2278 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2279 end;
2281 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2283 ShotType := 0;
2284 end;
2285 end;
2287 WEAPON_FLAMETHROWER: // Îãíåìåò
2288 begin
2289 // Ñî âðåìåíåì óìèðàåò
2290 if (Timeout < 1) then
2291 begin
2292 ShotType := 0;
2293 Continue;
2294 end;
2295 // Ïîä âîäîé òîæå
2296 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2297 begin
2298 if WordBool(st and MOVE_HITWATER) then
2299 begin
2300 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2301 begin
2302 Anim := TAnimation.Create(_id, False, 3);
2303 Anim.Alpha := 0;
2304 tcx := Random(8);
2305 tcy := Random(8);
2306 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2307 cy-4+tcy-(Anim.Height div 2),
2308 Anim, ONCEANIM_SMOKE);
2309 Anim.Free();
2310 end;
2311 end
2312 else
2313 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2314 ShotType := 0;
2315 Continue;
2316 end;
2318 // Ãðàâèòàöèÿ
2319 if Stopped = 0 then
2320 Obj.Accel.Y := Obj.Accel.Y + 1;
2321 // Ïîïàëè â ñòåíó èëè â âîäó:
2322 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2323 begin
2324 // Ïðèëèïàåì:
2325 Obj.Vel.X := 0;
2326 Obj.Vel.Y := 0;
2327 Obj.Accel.Y := 0;
2328 if WordBool(st and MOVE_HITWALL) then
2329 Stopped := MOVE_HITWALL
2330 else if WordBool(st and MOVE_HITLAND) then
2331 Stopped := MOVE_HITLAND
2332 else if WordBool(st and MOVE_HITCEIL) then
2333 Stopped := MOVE_HITCEIL;
2334 end;
2336 a := IfThen(Stopped = 0, 3, 1);
2337 // Åñëè â êîãî-òî ïîïàëè
2338 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2339 begin
2340 // HIT_FLAME ñàì ïîäîææåò
2341 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2342 if Stopped = 0 then
2343 ShotType := 0;
2344 end;
2346 if Stopped = 0 then
2347 tf := 2
2348 else
2349 tf := 3;
2351 if (gTime mod LongWord(tf) = 0) then
2352 begin
2353 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2354 Anim.Alpha := 0;
2355 case Stopped of
2356 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2357 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2358 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2359 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2360 end;
2361 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2362 Anim.Free();
2363 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2364 end;
2365 end;
2367 WEAPON_BFG: // BFG
2368 begin
2369 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2370 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2371 begin
2372 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2373 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2374 ShotType := 0;
2375 Continue;
2376 end;
2378 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2379 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2380 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2381 (Timeout < 1) then
2382 begin
2383 // Ëó÷è BFG:
2384 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2386 // Âçðûâ BFG:
2387 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2388 begin
2389 Anim := TAnimation.Create(TextureID, False, 6);
2390 Anim.Blending := False;
2391 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2392 Anim.Free();
2393 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2394 end;
2396 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2398 ShotType := 0;
2399 end;
2400 end;
2402 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2403 begin
2404 // Âûëåòåë èç âîäû:
2405 if WordBool(st and MOVE_HITAIR) then
2406 g_Obj_SetSpeed(@Obj, 16);
2408 // Âåëè÷èíà óðîíà:
2409 if ShotType = WEAPON_IMP_FIRE then
2410 a := 5
2411 else
2412 if ShotType = WEAPON_CACO_FIRE then
2413 a := 20
2414 else
2415 a := 40;
2417 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2418 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2419 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2420 (Timeout < 1) then
2421 begin
2422 if ShotType = WEAPON_IMP_FIRE then
2423 s := 'FRAMES_EXPLODE_IMPFIRE'
2424 else
2425 if ShotType = WEAPON_CACO_FIRE then
2426 s := 'FRAMES_EXPLODE_CACOFIRE'
2427 else
2428 s := 'FRAMES_EXPLODE_BARONFIRE';
2430 // Âçðûâ:
2431 if g_Frames_Get(TextureID, s) then
2432 begin
2433 Anim := TAnimation.Create(TextureID, False, 6);
2434 Anim.Blending := False;
2435 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2436 Anim.Free();
2437 end;
2439 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2441 ShotType := 0;
2442 end;
2443 end;
2445 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2446 begin
2447 // Âûëåòåë èç âîäû:
2448 if WordBool(st and MOVE_HITAIR) then
2449 g_Obj_SetSpeed(@Obj, 16);
2451 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2452 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2453 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2454 (Timeout < 1) then
2455 begin
2456 // Âçðûâ:
2457 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2458 begin
2459 Anim := TAnimation.Create(TextureID, False, 6);
2460 Anim.Blending := False;
2461 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2462 Anim.Free();
2463 end;
2465 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2467 ShotType := 0;
2468 end;
2469 end;
2470 end; // case ShotType of...
2472 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2473 if (ShotType = 0) then
2474 begin
2475 if gGameSettings.GameType = GT_SERVER then
2476 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2477 if Animation <> nil then
2478 begin
2479 Animation.Free();
2480 Animation := nil;
2481 end;
2482 end
2483 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2484 if gGameSettings.GameType = GT_SERVER then
2485 MH_SEND_UpdateShot(i);
2486 end;
2487 end;
2488 end;
2490 procedure g_Weapon_Draw();
2491 var
2492 i: Integer;
2493 a: SmallInt;
2494 p: TDFPoint;
2495 begin
2496 if Shots = nil then
2497 Exit;
2499 for i := 0 to High(Shots) do
2500 if Shots[i].ShotType <> 0 then
2501 with Shots[i] do
2502 begin
2503 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2504 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2505 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2506 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2507 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2508 else
2509 a := 0;
2511 p.X := Obj.Rect.Width div 2;
2512 p.Y := Obj.Rect.Height div 2;
2514 if Animation <> nil then
2515 begin
2516 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2517 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2518 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2519 Animation.DrawEx(Obj.X, Obj.Y, TMirrorType.None, p, a)
2520 else
2521 Animation.Draw(Obj.X, Obj.Y, TMirrorType.None);
2522 end
2523 else if TextureID <> 0 then
2524 begin
2525 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2526 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, TMirrorType.None)
2527 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2528 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2529 end;
2531 if g_debug_Frames then
2532 begin
2533 e_DrawQuad(Obj.X+Obj.Rect.X,
2534 Obj.Y+Obj.Rect.Y,
2535 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2536 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2537 0, 255, 0);
2538 end;
2539 end;
2540 end;
2542 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2543 var
2544 a: Integer;
2545 begin
2546 Result := False;
2548 if Shots = nil then
2549 Exit;
2551 for a := 0 to High(Shots) do
2552 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2553 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2554 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2555 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2556 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2557 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2558 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2559 begin
2560 Result := True;
2561 Exit;
2562 end;
2563 end;
2565 procedure g_Weapon_SaveState (st: TStream);
2566 var
2567 count, i, j: Integer;
2568 begin
2569 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2570 count := 0;
2571 for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count);
2573 // Êîëè÷åñòâî ñíàðÿäîâ
2574 utils.WriteInt(st, count);
2576 if (count = 0) then exit;
2578 for i := 0 to High(Shots) do
2579 begin
2580 if Shots[i].ShotType <> 0 then
2581 begin
2582 // Ñèãíàòóðà ñíàðÿäà
2583 utils.writeSign(st, 'SHOT');
2584 utils.writeInt(st, Byte(0)); // version
2585 // Òèï ñíàðÿäà
2586 utils.writeInt(st, Byte(Shots[i].ShotType));
2587 // Öåëü
2588 utils.writeInt(st, Word(Shots[i].Target));
2589 // UID ñòðåëÿâøåãî
2590 utils.writeInt(st, Word(Shots[i].SpawnerUID));
2591 // Ðàçìåð ïîëÿ Triggers
2592 utils.writeInt(st, Integer(Length(Shots[i].Triggers)));
2593 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2594 for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j]));
2595 // Îáúåêò ñíàðÿäà
2596 Obj_SaveState(st, @Shots[i].Obj);
2597 // Êîñòûëèíà åáàíàÿ
2598 utils.writeInt(st, Byte(Shots[i].Stopped));
2599 end;
2600 end;
2601 end;
2603 procedure g_Weapon_LoadState (st: TStream);
2604 var
2605 count, tc, i, j: Integer;
2606 dw: LongWord;
2607 begin
2608 if (st = nil) then exit;
2610 // Êîëè÷åñòâî ñíàðÿäîâ
2611 count := utils.readLongInt(st);
2612 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter');
2614 SetLength(Shots, count);
2616 if (count = 0) then exit;
2618 for i := 0 to count-1 do
2619 begin
2620 // Ñèãíàòóðà ñíàðÿäà
2621 if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature');
2622 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version');
2623 // Òèï ñíàðÿäà:
2624 Shots[i].ShotType := utils.readByte(st);
2625 // Öåëü
2626 Shots[i].Target := utils.readWord(st);
2627 // UID ñòðåëÿâøåãî
2628 Shots[i].SpawnerUID := utils.readWord(st);
2629 // Ðàçìåð ïîëÿ Triggers
2630 tc := utils.readLongInt(st);
2631 if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter');
2632 SetLength(Shots[i].Triggers, tc);
2633 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2634 for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st);
2635 // Îáúåêò ïðåäìåòà
2636 Obj_LoadState(@Shots[i].Obj, st);
2637 // Êîñòûëèíà åáàíàÿ
2638 Shots[i].Stopped := utils.readByte(st);
2640 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2641 Shots[i].TextureID := DWORD(-1);
2642 Shots[i].Animation := nil;
2644 case Shots[i].ShotType of
2645 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2646 begin
2647 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2648 end;
2649 WEAPON_PLASMA:
2650 begin
2651 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2652 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2653 end;
2654 WEAPON_BFG:
2655 begin
2656 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2657 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2658 end;
2659 WEAPON_IMP_FIRE:
2660 begin
2661 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2662 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2663 end;
2664 WEAPON_BSP_FIRE:
2665 begin
2666 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2667 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2668 end;
2669 WEAPON_CACO_FIRE:
2670 begin
2671 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2672 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2673 end;
2674 WEAPON_BARON_FIRE:
2675 begin
2676 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2677 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2678 end;
2679 WEAPON_MANCUB_FIRE:
2680 begin
2681 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2682 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2683 end;
2684 end;
2685 end;
2686 end;
2688 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2689 var
2690 cx, cy: Integer;
2691 Anim: TAnimation;
2692 s: string;
2693 begin
2694 if Shots = nil then
2695 Exit;
2696 if (I > High(Shots)) or (I < 0) then Exit;
2698 with Shots[I] do
2699 begin
2700 if ShotType = 0 then Exit;
2701 Obj.X := X;
2702 Obj.Y := Y;
2703 cx := Obj.X + (Obj.Rect.Width div 2);
2704 cy := Obj.Y + (Obj.Rect.Height div 2);
2706 case ShotType of
2707 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2708 begin
2709 if Loud then
2710 begin
2711 if ShotType = WEAPON_SKEL_FIRE then
2712 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2713 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2714 begin
2715 Anim := TAnimation.Create(TextureID, False, 8);
2716 Anim.Blending := False;
2717 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2718 Anim.Free();
2719 end;
2720 end
2721 else
2722 begin // Âçðûâ Ðàêåòû
2723 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2724 begin
2725 Anim := TAnimation.Create(TextureID, False, 6);
2726 Anim.Blending := False;
2727 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2728 Anim.Free();
2729 end;
2730 end;
2731 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2732 end;
2733 end;
2735 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2736 begin
2737 if ShotType = WEAPON_PLASMA then
2738 s := 'FRAMES_EXPLODE_PLASMA'
2739 else
2740 s := 'FRAMES_EXPLODE_BSPFIRE';
2742 if g_Frames_Get(TextureID, s) and loud then
2743 begin
2744 Anim := TAnimation.Create(TextureID, False, 3);
2745 Anim.Blending := False;
2746 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2747 Anim.Free();
2749 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2750 end;
2751 end;
2753 WEAPON_BFG: // BFG
2754 begin
2755 // Âçðûâ BFG:
2756 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2757 begin
2758 Anim := TAnimation.Create(TextureID, False, 6);
2759 Anim.Blending := False;
2760 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2761 Anim.Free();
2763 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2764 end;
2765 end;
2767 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2768 begin
2769 if ShotType = WEAPON_IMP_FIRE then
2770 s := 'FRAMES_EXPLODE_IMPFIRE'
2771 else
2772 if ShotType = WEAPON_CACO_FIRE then
2773 s := 'FRAMES_EXPLODE_CACOFIRE'
2774 else
2775 s := 'FRAMES_EXPLODE_BARONFIRE';
2777 if g_Frames_Get(TextureID, s) and Loud then
2778 begin
2779 Anim := TAnimation.Create(TextureID, False, 6);
2780 Anim.Blending := False;
2781 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2782 Anim.Free();
2784 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2785 end;
2786 end;
2788 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2789 begin
2790 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2791 begin
2792 Anim := TAnimation.Create(TextureID, False, 6);
2793 Anim.Blending := False;
2794 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2795 Anim.Free();
2797 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2798 end;
2799 end;
2800 end; // case ShotType of...
2802 ShotType := 0;
2803 Animation.Free();
2804 end;
2805 end;
2808 procedure g_Weapon_AddDynLights();
2809 var
2810 i: Integer;
2811 begin
2812 if Shots = nil then Exit;
2813 for i := 0 to High(Shots) do
2814 begin
2815 if Shots[i].ShotType = 0 then continue;
2816 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2817 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2818 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2819 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2820 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2821 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2822 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2823 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2824 (Shots[i].ShotType = WEAPON_PLASMA) or
2825 (Shots[i].ShotType = WEAPON_BFG) or
2826 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2827 false then
2828 begin
2829 if (Shots[i].ShotType = WEAPON_PLASMA) then
2830 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)
2831 else if (Shots[i].ShotType = WEAPON_BFG) then
2832 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)
2833 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2834 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)
2835 else
2836 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);
2837 end;
2838 end;
2839 end;
2842 procedure TShot.positionChanged (); begin end;
2845 end.