DEADSOFTWARE

cosmetix: `curTime*` -> `getTime*`
[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 {
26 const
27 HIT_SOME = 0;
28 HIT_ROCKET = 1;
29 HIT_BFG = 2;
30 HIT_TRAP = 3;
31 HIT_FALL = 4;
32 HIT_WATER = 5;
33 HIT_ACID = 6;
34 HIT_ELECTRO = 7;
35 HIT_FLAME = 8;
36 HIT_SELF = 9;
37 HIT_DISCON = 10;
38 }
40 type
41 TShot = record
42 ShotType: Byte;
43 Target: Word;
44 SpawnerUID: Word;
45 Triggers: DWArray;
46 Obj: TObj;
47 Animation: TAnimation;
48 TextureID: DWORD;
49 Timeout: DWORD;
50 Stopped: Byte;
52 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
53 end;
56 var
57 Shots: array of TShot = nil;
58 LastShotID: Integer = 0;
60 procedure g_Weapon_LoadData();
61 procedure g_Weapon_FreeData();
62 procedure g_Weapon_Init();
63 procedure g_Weapon_Free();
64 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
65 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
66 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
68 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
69 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
70 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
71 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
74 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
75 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
76 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
77 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
78 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
79 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
80 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
81 procedure g_Weapon_bfghit(x, y: Integer);
82 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
83 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
84 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
85 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
87 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
88 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
89 procedure g_Weapon_Update();
90 procedure g_Weapon_Draw();
91 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
92 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
94 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
95 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
97 procedure g_Weapon_AddDynLights();
99 const
100 WEAPON_KASTET = 0;
101 WEAPON_SAW = 1;
102 WEAPON_PISTOL = 2;
103 WEAPON_SHOTGUN1 = 3;
104 WEAPON_SHOTGUN2 = 4;
105 WEAPON_CHAINGUN = 5;
106 WEAPON_ROCKETLAUNCHER = 6;
107 WEAPON_PLASMA = 7;
108 WEAPON_BFG = 8;
109 WEAPON_SUPERPULEMET = 9;
110 WEAPON_FLAMETHROWER = 10;
111 WEAPON_ZOMBY_PISTOL = 20;
112 WEAPON_IMP_FIRE = 21;
113 WEAPON_BSP_FIRE = 22;
114 WEAPON_CACO_FIRE = 23;
115 WEAPON_BARON_FIRE = 24;
116 WEAPON_MANCUB_FIRE = 25;
117 WEAPON_SKEL_FIRE = 26;
119 WP_FIRST = WEAPON_KASTET;
120 WP_LAST = WEAPON_FLAMETHROWER;
123 var
124 gwep_debug_fast_trace: Boolean = true;
127 implementation
129 uses
130 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
131 g_console, SysUtils, g_options, g_game,
132 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
133 g_language, g_netmsg, g_grid,
134 binheap, hashtable;
136 type
137 TWaterPanel = record
138 X, Y: Integer;
139 Width, Height: Word;
140 Active: Boolean;
141 end;
143 const
144 SHOT_ROCKETLAUNCHER_WIDTH = 14;
145 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
147 SHOT_SKELFIRE_WIDTH = 14;
148 SHOT_SKELFIRE_HEIGHT = 14;
150 SHOT_PLASMA_WIDTH = 16;
151 SHOT_PLASMA_HEIGHT = 16;
153 SHOT_BFG_WIDTH = 32;
154 SHOT_BFG_HEIGHT = 32;
155 SHOT_BFG_DAMAGE = 100;
156 SHOT_BFG_RADIUS = 256;
158 SHOT_FLAME_WIDTH = 4;
159 SHOT_FLAME_HEIGHT = 4;
160 SHOT_FLAME_LIFETIME = 180;
162 SHOT_SIGNATURE = $544F4853; // 'SHOT'
164 type
165 PHitTime = ^THitTime;
166 THitTime = record
167 distSq: Integer;
168 mon: TMonster;
169 plridx: Integer; // if mon=nil
170 x, y: Integer;
171 end;
173 // indicies in `wgunHitTime` array
174 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer>;
176 var
177 WaterMap: array of array of DWORD = nil;
178 //wgunMonHash: THashIntInt = nil;
179 wgunHitHeap: TBinaryHeapHitTimes = nil;
180 wgunHitTime: array of THitTime = nil;
181 wgunHitTimeUsed: Integer = 0;
184 function hitTimeLess (a, b: Integer): Boolean;
185 var
186 hta, htb: PHitTime;
187 begin
188 hta := @wgunHitTime[a];
189 htb := @wgunHitTime[b];
190 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
191 if (hta.mon <> nil) then
192 begin
193 // a is monster
194 if (htb.mon = nil) then begin result := false; exit; end; // players first
195 result := (hta.mon.UID < htb.mon.UID); // why not?
196 end
197 else
198 begin
199 // a is player
200 if (htb.mon <> nil) then begin result := true; exit; end; // players first
201 result := (hta.plridx < htb.plridx); // why not?
202 end;
203 end;
206 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
207 begin
208 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
209 with wgunHitTime[wgunHitTimeUsed] do
210 begin
211 distSq := adistSq;
212 mon := amon;
213 plridx := -1;
214 x := ax;
215 y := ay;
216 end;
217 wgunHitHeap.insert(wgunHitTimeUsed);
218 Inc(wgunHitTimeUsed);
219 end;
222 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
223 begin
224 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
225 with wgunHitTime[wgunHitTimeUsed] do
226 begin
227 distSq := adistSq;
228 mon := nil;
229 plridx := aplridx;
230 x := ax;
231 y := ay;
232 end;
233 wgunHitHeap.insert(wgunHitTimeUsed);
234 Inc(wgunHitTimeUsed);
235 end;
238 function FindShot(): DWORD;
239 var
240 i: Integer;
241 begin
242 if Shots <> nil then
243 for i := 0 to High(Shots) do
244 if Shots[i].ShotType = 0 then
245 begin
246 Result := i;
247 LastShotID := Result;
248 Exit;
249 end;
251 if Shots = nil then
252 begin
253 SetLength(Shots, 128);
254 Result := 0;
255 end
256 else
257 begin
258 Result := High(Shots) + 1;
259 SetLength(Shots, Length(Shots) + 128);
260 end;
261 LastShotID := Result;
262 end;
264 procedure CreateWaterMap();
265 var
266 WaterArray: Array of TWaterPanel;
267 a, b, c, m: Integer;
268 ok: Boolean;
269 begin
270 if gWater = nil then
271 Exit;
273 SetLength(WaterArray, Length(gWater));
275 for a := 0 to High(gWater) do
276 begin
277 WaterArray[a].X := gWater[a].X;
278 WaterArray[a].Y := gWater[a].Y;
279 WaterArray[a].Width := gWater[a].Width;
280 WaterArray[a].Height := gWater[a].Height;
281 WaterArray[a].Active := True;
282 end;
284 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
286 for a := 0 to High(WaterArray) do
287 if WaterArray[a].Active then
288 begin
289 WaterArray[a].Active := False;
290 m := Length(WaterMap);
291 SetLength(WaterMap, m+1);
292 SetLength(WaterMap[m], 1);
293 WaterMap[m][0] := a;
294 ok := True;
296 while ok do
297 begin
298 ok := False;
299 for b := 0 to High(WaterArray) do
300 if WaterArray[b].Active then
301 for c := 0 to High(WaterMap[m]) do
302 if g_CollideAround(WaterArray[b].X,
303 WaterArray[b].Y,
304 WaterArray[b].Width,
305 WaterArray[b].Height,
306 WaterArray[WaterMap[m][c]].X,
307 WaterArray[WaterMap[m][c]].Y,
308 WaterArray[WaterMap[m][c]].Width,
309 WaterArray[WaterMap[m][c]].Height) then
310 begin
311 WaterArray[b].Active := False;
312 SetLength(WaterMap[m],
313 Length(WaterMap[m])+1);
314 WaterMap[m][High(WaterMap[m])] := b;
315 ok := True;
316 Break;
317 end;
318 end;
320 g_Game_StepLoading();
321 end;
323 WaterArray := nil;
324 end;
327 var
328 chkTrap_pl: array [0..256] of Integer;
329 chkTrap_mn: array [0..65535] of TMonster;
331 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
332 var
333 //a, b, c, d, i1, i2: Integer;
334 //chkTrap_pl, chkTrap_mn: WArray;
335 plaCount: Integer = 0;
336 mnaCount: Integer = 0;
337 frameId: DWord;
340 function monsWaterCheck (mon: TMonster): Boolean;
341 begin
342 result := false; // don't stop
343 if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
344 begin
345 i2 += 1;
346 chkTrap_mn[i2] := monidx;
347 end;
348 end;
351 function monsWaterCheck (mon: TMonster): Boolean;
352 begin
353 result := false; // don't stop
354 if (mon.trapCheckFrameId <> frameId) then
355 begin
356 mon.trapCheckFrameId := frameId;
357 chkTrap_mn[mnaCount] := mon;
358 Inc(mnaCount);
359 end;
360 end;
362 var
363 a, b, c, d, f: Integer;
364 pan: TPanel;
365 begin
366 if (gWater = nil) or (WaterMap = nil) then Exit;
368 frameId := g_Mons_getNewTrapFrameId();
370 //i1 := -1;
371 //i2 := -1;
373 //SetLength(chkTrap_pl, 1024);
374 //SetLength(chkTrap_mn, 1024);
375 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
376 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
378 for a := 0 to High(WaterMap) do
379 begin
380 for b := 0 to High(WaterMap[a]) do
381 begin
382 pan := gWater[WaterMap[a][b]];
383 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
385 for c := 0 to High(WaterMap[a]) do
386 begin
387 pan := gWater[WaterMap[a][c]];
388 for d := 0 to High(gPlayers) do
389 begin
390 if (gPlayers[d] <> nil) and (gPlayers[d].alive) then
391 begin
392 if gPlayers[d].Collide(pan) then
393 begin
394 f := 0;
395 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
396 if (f = plaCount) then
397 begin
398 chkTrap_pl[plaCount] := d;
399 Inc(plaCount);
400 if (plaCount = Length(chkTrap_pl)) then break;
401 end;
402 end;
403 end;
404 end;
406 //g_Mons_ForEach(monsWaterCheck);
407 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
408 end;
410 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
411 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
412 end;
413 end;
415 //chkTrap_pl := nil;
416 //chkTrap_mn := nil;
417 end;
419 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
420 var
421 tt, mt: Byte;
422 mon: TMonster;
423 begin
424 Result := False;
426 tt := g_GetUIDType(SpawnerUID);
427 if tt = UID_MONSTER then
428 begin
429 mon := g_Monsters_ByUID(SpawnerUID);
430 if mon <> nil then
431 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
432 else
433 mt := 0;
434 end
435 else
436 mt := 0;
438 if m = nil then Exit;
439 if m.UID = SpawnerUID then
440 begin
441 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
442 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
443 Exit;
444 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
445 if (m.MonsterType = MONSTER_CYBER) or
446 (m.MonsterType = MONSTER_BARREL) then
447 begin
448 Result := True;
449 Exit;
450 end;
451 end;
453 if tt = UID_MONSTER then
454 begin
455 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
456 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
457 Exit;
459 // Îáà ìîíñòðà îäíîãî âèäà:
460 if mt = m.MonsterType then
461 case mt of
462 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
463 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
464 Exit; // Ýòè íå áüþò ñâîèõ
465 end;
466 end;
468 if g_Game_IsServer then
469 begin
470 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
471 Result := m.Damage(d, vx, vy, SpawnerUID, t)
472 else
473 Result := True;
474 if t = HIT_FLAME then
475 m.CatchFire(SpawnerUID);
476 end
477 else
478 Result := True;
479 end;
482 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
483 begin
484 result := False;
486 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
487 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
489 if g_Game_IsServer then
490 begin
491 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
492 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
493 end;
495 result := true;
496 end;
499 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
501 function monsCheck (mon: TMonster): Boolean;
502 begin
503 result := false; // don't stop
504 if (mon.alive) and (mon.UID <> SpawnerUID) then
505 begin
506 with mon do
507 begin
508 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
509 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
510 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
511 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
512 begin
513 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
514 end;
515 end;
516 end;
517 end;
519 var
520 i, h: Integer;
521 st: Byte;
522 pl: TPlayer;
523 b: Boolean;
524 begin
525 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
527 h := High(gCorpses);
529 if gAdvCorpses and (h <> -1) then
530 for i := 0 to h do
531 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
532 with gCorpses[i] do
533 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
534 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
535 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
536 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
537 begin
538 Damage(50, 0, 0);
539 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
540 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
541 end;
543 st := TEAM_NONE;
544 pl := g_Player_Get(SpawnerUID);
545 if pl <> nil then
546 st := pl.Team;
548 h := High(gPlayers);
550 if h <> -1 then
551 for i := 0 to h do
552 if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then
553 with gPlayers[i] do
554 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
555 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
556 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
557 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
558 begin
559 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
560 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
561 else
562 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
563 if b then
564 gPlayers[i].BFGHit();
565 end;
567 //FIXME
568 g_Mons_ForEachAlive(monsCheck);
569 end;
571 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
572 var
573 find_id: DWord;
574 FramesID: DWORD = 0;
575 begin
576 if I < 0 then
577 find_id := FindShot()
578 else
579 begin
580 find_id := I;
581 if Integer(find_id) >= High(Shots) then
582 SetLength(Shots, find_id + 64)
583 end;
585 case ShotType of
586 WEAPON_ROCKETLAUNCHER:
587 begin
588 with Shots[find_id] do
589 begin
590 g_Obj_Init(@Obj);
592 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
593 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
595 Animation := nil;
596 Triggers := nil;
597 ShotType := WEAPON_ROCKETLAUNCHER;
598 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
599 end;
600 end;
602 WEAPON_PLASMA:
603 begin
604 with Shots[find_id] do
605 begin
606 g_Obj_Init(@Obj);
608 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
609 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
611 Triggers := nil;
612 ShotType := WEAPON_PLASMA;
613 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
614 Animation := TAnimation.Create(FramesID, True, 5);
615 end;
616 end;
618 WEAPON_BFG:
619 begin
620 with Shots[find_id] do
621 begin
622 g_Obj_Init(@Obj);
624 Obj.Rect.Width := SHOT_BFG_WIDTH;
625 Obj.Rect.Height := SHOT_BFG_HEIGHT;
627 Triggers := nil;
628 ShotType := WEAPON_BFG;
629 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
630 Animation := TAnimation.Create(FramesID, True, 6);
631 end;
632 end;
634 WEAPON_FLAMETHROWER:
635 begin
636 with Shots[find_id] do
637 begin
638 g_Obj_Init(@Obj);
640 Obj.Rect.Width := SHOT_FLAME_WIDTH;
641 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
643 Triggers := nil;
644 ShotType := WEAPON_FLAMETHROWER;
645 Animation := nil;
646 TextureID := 0;
647 g_Frames_Get(TextureID, 'FRAMES_FLAME');
648 end;
649 end;
651 WEAPON_IMP_FIRE:
652 begin
653 with Shots[find_id] do
654 begin
655 g_Obj_Init(@Obj);
657 Obj.Rect.Width := 16;
658 Obj.Rect.Height := 16;
660 Triggers := nil;
661 ShotType := WEAPON_IMP_FIRE;
662 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
663 Animation := TAnimation.Create(FramesID, True, 4);
664 end;
665 end;
667 WEAPON_CACO_FIRE:
668 begin
669 with Shots[find_id] do
670 begin
671 g_Obj_Init(@Obj);
673 Obj.Rect.Width := 16;
674 Obj.Rect.Height := 16;
676 Triggers := nil;
677 ShotType := WEAPON_CACO_FIRE;
678 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
679 Animation := TAnimation.Create(FramesID, True, 4);
680 end;
681 end;
683 WEAPON_MANCUB_FIRE:
684 begin
685 with Shots[find_id] do
686 begin
687 g_Obj_Init(@Obj);
689 Obj.Rect.Width := 32;
690 Obj.Rect.Height := 32;
692 Triggers := nil;
693 ShotType := WEAPON_MANCUB_FIRE;
694 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
695 Animation := TAnimation.Create(FramesID, True, 4);
696 end;
697 end;
699 WEAPON_BARON_FIRE:
700 begin
701 with Shots[find_id] do
702 begin
703 g_Obj_Init(@Obj);
705 Obj.Rect.Width := 32;
706 Obj.Rect.Height := 16;
708 Triggers := nil;
709 ShotType := WEAPON_BARON_FIRE;
710 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
711 Animation := TAnimation.Create(FramesID, True, 4);
712 end;
713 end;
715 WEAPON_BSP_FIRE:
716 begin
717 with Shots[find_id] do
718 begin
719 g_Obj_Init(@Obj);
721 Obj.Rect.Width := 16;
722 Obj.Rect.Height := 16;
724 Triggers := nil;
725 ShotType := WEAPON_BSP_FIRE;
726 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
727 Animation := TAnimation.Create(FramesID, True, 4);
728 end;
729 end;
731 WEAPON_SKEL_FIRE:
732 begin
733 with Shots[find_id] do
734 begin
735 g_Obj_Init(@Obj);
737 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
738 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
740 Triggers := nil;
741 ShotType := WEAPON_SKEL_FIRE;
742 target := TargetUID;
743 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
744 Animation := TAnimation.Create(FramesID, True, 5);
745 end;
746 end;
747 end;
749 Shots[find_id].Obj.X := X;
750 Shots[find_id].Obj.Y := Y;
751 Shots[find_id].Obj.Vel.X := XV;
752 Shots[find_id].Obj.Vel.Y := YV;
753 Shots[find_id].Obj.Accel.X := 0;
754 Shots[find_id].Obj.Accel.Y := 0;
755 Shots[find_id].SpawnerUID := Spawner;
756 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
757 Shots[find_id].Stopped := 255
758 else
759 Shots[find_id].Stopped := 0;
760 Result := find_id;
761 end;
763 procedure throw(i, x, y, xd, yd, s: Integer);
764 var
765 a: Integer;
766 begin
767 yd := yd - y;
768 xd := xd - x;
770 a := Max(Abs(xd), Abs(yd));
771 if a = 0 then
772 a := 1;
774 Shots[i].Obj.X := x;
775 Shots[i].Obj.Y := y;
776 Shots[i].Obj.Vel.X := (xd*s) div a;
777 Shots[i].Obj.Vel.Y := (yd*s) div a;
778 Shots[i].Obj.Accel.X := 0;
779 Shots[i].Obj.Accel.Y := 0;
780 Shots[i].Stopped := 0;
781 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
782 Shots[i].Timeout := 900 // ~25 sec
783 else
784 begin
785 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
786 Shots[i].Timeout := SHOT_FLAME_LIFETIME
787 else
788 Shots[i].Timeout := 550; // ~15 sec
789 end;
790 end;
792 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
793 var
794 i, h: Integer;
796 function PlayerHit(Team: Byte = 0): Boolean;
797 var
798 i: Integer;
799 ChkTeam: Boolean;
800 p: TPlayer;
801 begin
802 Result := False;
803 h := High(gPlayers);
805 if h <> -1 then
806 for i := 0 to h do
807 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
808 begin
809 ChkTeam := True;
810 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
811 begin
812 p := g_Player_Get(SpawnerUID);
813 if p <> nil then
814 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
815 end;
816 if ChkTeam then
817 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
818 begin
819 if t <> HIT_FLAME then
820 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
821 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
822 if t = HIT_BFG then
823 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
824 Result := True;
825 break;
826 end;
827 end;
828 end;
831 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
832 begin
833 result := false; // don't stop
834 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
835 begin
836 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
837 begin
838 if (t <> HIT_FLAME) then
839 begin
840 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
841 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
842 end;
843 result := True;
844 end;
845 end;
846 end;
849 function monsCheckHit (mon: TMonster): Boolean;
850 begin
851 result := false; // don't stop
852 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
853 begin
854 if (t <> HIT_FLAME) then
855 begin
856 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
857 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
858 end;
859 result := true;
860 end;
861 end;
863 function MonsterHit(): Boolean;
864 begin
865 //result := g_Mons_ForEach(monsCheckHit);
866 //FIXME: accelerate this!
867 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
868 end;
870 begin
871 Result := 0;
873 if HitCorpses then
874 begin
875 h := High(gCorpses);
877 if gAdvCorpses and (h <> -1) then
878 for i := 0 to h do
879 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
880 g_Obj_Collide(obj, @gCorpses[i].Obj) then
881 begin
882 // Ðàñïèëèâàåì òðóï:
883 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
884 (obj^.Vel.Y+obj^.Accel.Y) div 4);
885 Result := 1;
886 end;
887 end;
889 case gGameSettings.GameMode of
890 // Êàìïàíèÿ:
891 GM_COOP, GM_SINGLE:
892 begin
893 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
894 if MonsterHit() then
895 begin
896 Result := 2;
897 Exit;
898 end;
900 if PlayerHit() then
901 begin
902 Result := 1;
903 Exit;
904 end;
905 end;
907 // Äåçìàò÷:
908 GM_DM:
909 begin
910 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
911 if PlayerHit() then
912 begin
913 Result := 1;
914 Exit;
915 end;
917 if MonsterHit() then
918 begin
919 Result := 2;
920 Exit;
921 end;
922 end;
924 // Êîìàíäíûå:
925 GM_TDM, GM_CTF:
926 begin
927 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
928 if PlayerHit(2) then
929 begin
930 Result := 1;
931 Exit;
932 end;
934 // Ïîòîì ìîíñòðîâ
935 if MonsterHit() then
936 begin
937 Result := 2;
938 Exit;
939 end;
941 // È â êîíöå ñâîèõ èãðîêîâ
942 if PlayerHit(1) then
943 begin
944 Result := 1;
945 Exit;
946 end;
947 end;
949 end;
950 end;
952 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
953 begin
954 Result := False;
956 case g_GetUIDType(UID) of
957 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
958 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
959 else Exit;
960 end;
961 end;
963 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
964 var
965 r: Integer; // squared radius
967 function monsExCheck (mon: TMonster): Boolean;
968 var
969 dx, dy, mm: Integer;
970 begin
971 result := false; // don't stop
972 begin
973 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
974 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
976 if dx > 1000 then dx := 1000;
977 if dy > 1000 then dy := 1000;
979 if (dx*dx+dy*dy < r) then
980 begin
981 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
982 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
984 mm := Max(abs(dx), abs(dy));
985 if mm = 0 then mm := 1;
987 if mon.alive then
988 begin
989 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
990 end;
992 mon.Push((dx*7) div mm, (dy*7) div mm);
993 end;
994 end;
995 end;
997 var
998 i, h, dx, dy, m, mm: Integer;
999 _angle: SmallInt;
1000 begin
1001 result := false;
1003 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1005 r := rad*rad;
1007 h := High(gPlayers);
1009 if h <> -1 then
1010 for i := 0 to h do
1011 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1012 with gPlayers[i] do
1013 begin
1014 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1015 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1017 if dx > 1000 then dx := 1000;
1018 if dy > 1000 then dy := 1000;
1020 if dx*dx+dy*dy < r then
1021 begin
1022 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1023 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1025 mm := Max(abs(dx), abs(dy));
1026 if mm = 0 then mm := 1;
1028 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1029 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1030 end;
1031 end;
1033 //g_Mons_ForEach(monsExCheck);
1034 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1036 h := High(gCorpses);
1038 if gAdvCorpses and (h <> -1) then
1039 for i := 0 to h do
1040 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1041 with gCorpses[i] do
1042 begin
1043 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1044 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1046 if dx > 1000 then dx := 1000;
1047 if dy > 1000 then dy := 1000;
1049 if dx*dx+dy*dy < r then
1050 begin
1051 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1052 Obj.Rect.Width, Obj.Rect.Height);
1054 mm := Max(abs(dx), abs(dy));
1055 if mm = 0 then mm := 1;
1057 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1058 end;
1059 end;
1061 h := High(gGibs);
1063 if gAdvGibs and (h <> -1) then
1064 for i := 0 to h do
1065 if gGibs[i].alive then
1066 with gGibs[i] do
1067 begin
1068 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1069 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1071 if dx > 1000 then dx := 1000;
1072 if dy > 1000 then dy := 1000;
1074 if dx*dx+dy*dy < r then
1075 begin
1076 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1077 Obj.Rect.Width, Obj.Rect.Height);
1078 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1079 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1081 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1082 positionChanged(); // this updates spatial accelerators
1083 end;
1084 end;
1085 end;
1087 procedure g_Weapon_Init();
1088 begin
1089 CreateWaterMap();
1090 end;
1092 procedure g_Weapon_Free();
1093 var
1094 i: Integer;
1095 begin
1096 if Shots <> nil then
1097 begin
1098 for i := 0 to High(Shots) do
1099 if Shots[i].ShotType <> 0 then
1100 Shots[i].Animation.Free();
1102 Shots := nil;
1103 end;
1105 WaterMap := nil;
1106 end;
1108 procedure g_Weapon_LoadData();
1109 begin
1110 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1112 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1127 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1132 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1133 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1134 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1135 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1136 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1137 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1138 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1139 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1140 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1141 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1142 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1143 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1145 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1146 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1147 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1148 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1149 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1150 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1151 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1152 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1153 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1154 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1155 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1156 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1157 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1158 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1159 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1160 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1161 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1162 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1163 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1164 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1165 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1167 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1168 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1170 //wgunMonHash := hashNewIntInt();
1171 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeLess);
1172 end;
1174 procedure g_Weapon_FreeData();
1175 begin
1176 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1178 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1179 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1180 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1181 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1182 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1183 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1184 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1185 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1186 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1187 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1188 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1189 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1190 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1191 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1192 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1193 g_Sound_Delete('SOUND_FIRE');
1194 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1195 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1196 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1197 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1198 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1199 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1200 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1201 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1202 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1203 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1204 g_Sound_Delete('SOUND_PLAYER_JETON');
1205 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1206 g_Sound_Delete('SOUND_PLAYER_CASING1');
1207 g_Sound_Delete('SOUND_PLAYER_CASING2');
1208 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1209 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1211 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1212 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1213 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1214 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1215 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1216 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1217 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1218 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1219 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1220 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1221 g_Frames_DeleteByName('FRAMES_BFGHIT');
1222 g_Frames_DeleteByName('FRAMES_FIRE');
1223 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1224 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1225 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1226 g_Frames_DeleteByName('FRAMES_SMOKE');
1227 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1228 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1229 end;
1232 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1233 var
1234 i: Integer;
1235 begin
1236 result := false;
1237 for i := 0 to High(gPlayers) do
1238 begin
1239 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1240 begin
1241 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1242 begin
1243 if AllowPush then gPlayers[i].Push(vx, vy);
1244 result := true;
1245 end;
1246 end;
1247 end;
1248 end;
1251 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1253 function monsCheck (mon: TMonster): Boolean;
1254 begin
1255 result := false; // don't stop
1256 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1257 begin
1258 if AllowPush then mon.Push(vx, vy);
1259 result := true;
1260 end;
1261 end;
1263 begin
1264 result := 0;
1265 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1266 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1267 end;
1270 (*
1271 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1272 var
1273 a: Integer;
1274 x2, y2: Integer;
1275 dx, dy: Integer;
1276 xe, ye: Integer;
1277 xi, yi: Integer;
1278 s, c: Extended;
1279 //vx, vy: Integer;
1280 xx, yy, d: Integer;
1281 i: Integer;
1282 t1, _collide: Boolean;
1283 w, h: Word;
1284 {$IF DEFINED(D2F_DEBUG)}
1285 stt: UInt64;
1286 showTime: Boolean = true;
1287 {$ENDIF}
1288 begin
1289 a := GetAngle(x, y, xd, yd)+180;
1291 SinCos(DegToRad(-a), s, c);
1293 if Abs(s) < 0.01 then s := 0;
1294 if Abs(c) < 0.01 then c := 0;
1296 x2 := x+Round(c*gMapInfo.Width);
1297 y2 := y+Round(s*gMapInfo.Width);
1299 t1 := gWalls <> nil;
1300 _collide := False;
1301 w := gMapInfo.Width;
1302 h := gMapInfo.Height;
1304 xe := 0;
1305 ye := 0;
1306 dx := x2-x;
1307 dy := y2-y;
1309 if (xd = 0) and (yd = 0) then Exit;
1311 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1312 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1314 dx := Abs(dx);
1315 dy := Abs(dy);
1317 if dx > dy then d := dx else d := dy;
1319 //blood vel, for Monster.Damage()
1320 //vx := (dx*10 div d)*xi;
1321 //vy := (dy*10 div d)*yi;
1323 {$IF DEFINED(D2F_DEBUG)}
1324 stt := getTimeMicro();
1325 {$ENDIF}
1327 xx := x;
1328 yy := y;
1330 for i := 1 to d do
1331 begin
1332 xe := xe+dx;
1333 ye := ye+dy;
1335 if xe > d then
1336 begin
1337 xe := xe-d;
1338 xx := xx+xi;
1339 end;
1341 if ye > d then
1342 begin
1343 ye := ye-d;
1344 yy := yy+yi;
1345 end;
1347 if (yy > h) or (yy < 0) then Break;
1348 if (xx > w) or (xx < 0) then Break;
1350 if t1 then
1351 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1352 begin
1353 _collide := True;
1354 {$IF DEFINED(D2F_DEBUG)}
1355 stt := getTimeMicro()-stt;
1356 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1357 showTime := false;
1358 {$ENDIF}
1359 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1360 if g_Game_IsServer and g_Game_IsNet then
1361 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1362 end;
1364 if not _collide then
1365 begin
1366 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1367 end;
1369 if _collide then Break;
1370 end;
1372 {$IF DEFINED(D2F_DEBUG)}
1373 if showTime then
1374 begin
1375 stt := getTimeMicro()-stt;
1376 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1377 end;
1378 {$ENDIF}
1380 if CheckTrigger and g_Game_IsServer then
1381 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1382 end;
1383 *)
1386 //!!!FIXME!!!
1387 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1388 var
1389 x0, y0: Integer;
1390 x2, y2: Integer;
1391 xi, yi: Integer;
1392 wallDistSq: Integer = $3fffffff;
1394 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1395 begin
1396 result := false;
1397 if (idx < 0) or (idx > High(gPlayers)) then exit;
1398 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1399 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1400 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1401 {$IF DEFINED(D2F_DEBUG)}
1402 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1403 {$ENDIF}
1404 end;
1406 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1407 begin
1408 result := false;
1409 if (mon = nil) then exit;
1410 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1411 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1412 {$IF DEFINED(D2F_DEBUG)}
1413 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1414 {$ENDIF}
1415 end;
1417 // collect players along hitray
1418 // return `true` if instant hit was detected
1419 function playerPossibleHit (): Boolean;
1420 var
1421 i: Integer;
1422 px, py, pw, ph: Integer;
1423 inx, iny: Integer;
1424 distSq: Integer;
1425 plr: TPlayer;
1426 begin
1427 result := false;
1428 for i := 0 to High(gPlayers) do
1429 begin
1430 plr := gPlayers[i];
1431 if (plr <> nil) and plr.alive then
1432 begin
1433 plr.getMapBox(px, py, pw, ph);
1434 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1435 begin
1436 distSq := distanceSq(x, y, inx, iny);
1437 if (distSq = 0) then
1438 begin
1439 // contains
1440 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1441 end
1442 else if (distSq < wallDistSq) then
1443 begin
1444 appendHitTimePlr(distSq, i, inx, iny);
1445 end;
1446 end;
1447 end;
1448 end;
1449 end;
1451 function sqchecker (mon: TMonster; tag: Integer): Boolean;
1452 var
1453 mx, my, mw, mh: Integer;
1454 inx, iny: Integer;
1455 distSq: Integer;
1456 begin
1457 result := false; // don't stop
1458 mon.getMapBox(mx, my, mw, mh);
1459 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1460 begin
1461 distSq := distanceSq(x0, y0, inx, iny);
1462 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1463 end;
1464 end;
1466 var
1467 a: Integer;
1468 dx, dy: Integer;
1469 xe, ye: Integer;
1470 s, c: Extended;
1471 i: Integer;
1472 wallHitFlag: Boolean = false;
1473 wallHitX: Integer = 0;
1474 wallHitY: Integer = 0;
1475 didHit: Boolean = false;
1476 {$IF DEFINED(D2F_DEBUG)}
1477 stt: UInt64;
1478 {$ENDIF}
1479 begin
1480 (*
1481 if not gwep_debug_fast_trace then
1482 begin
1483 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1484 exit;
1485 end;
1486 *)
1488 if (xd = 0) and (yd = 0) then exit;
1490 //wgunMonHash.reset(); //FIXME: clear hash on level change
1491 wgunHitHeap.clear();
1492 wgunHitTimeUsed := 0;
1494 a := GetAngle(x, y, xd, yd)+180;
1496 SinCos(DegToRad(-a), s, c);
1498 if Abs(s) < 0.01 then s := 0;
1499 if Abs(c) < 0.01 then c := 0;
1501 x0 := x;
1502 y0 := y;
1503 x2 := x+Round(c*gMapInfo.Width);
1504 y2 := y+Round(s*gMapInfo.Width);
1506 dx := x2-x;
1507 dy := y2-y;
1509 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1510 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1512 {$IF DEFINED(D2F_DEBUG)}
1513 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1514 stt := getTimeMicro();
1515 {$ENDIF}
1517 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1518 if wallHitFlag then
1519 begin
1520 x2 := wallHitX;
1521 y2 := wallHitY;
1522 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1523 end
1524 else
1525 begin
1526 wallHitX := x2;
1527 wallHitY := y2;
1528 end;
1530 if playerPossibleHit() then exit; // instant hit
1532 // collect monsters
1533 g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1535 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1536 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1537 while (wgunHitHeap.count > 0) do
1538 begin
1539 // has some entities to check, do it
1540 i := wgunHitHeap.front;
1541 wgunHitHeap.popFront();
1542 // hitpoint
1543 xe := wgunHitTime[i].x;
1544 ye := wgunHitTime[i].y;
1545 // check if it is not behind the wall
1546 if (wgunHitTime[i].mon <> nil) then
1547 begin
1548 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1549 end
1550 else
1551 begin
1552 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1553 end;
1554 if didHit then
1555 begin
1556 // need new coords for trigger
1557 wallHitX := xe;
1558 wallHitY := ye;
1559 wallHitFlag := false; // no sparks
1560 break;
1561 end;
1562 end;
1564 // need sparks?
1565 if wallHitFlag then
1566 begin
1567 {$IF DEFINED(D2F_DEBUG)}
1568 stt := getTimeMicro()-stt;
1569 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1570 {$ENDIF}
1571 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1572 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1573 end
1574 else
1575 begin
1576 {$IF DEFINED(D2F_DEBUG)}
1577 stt := getTimeMicro()-stt;
1578 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1579 {$ENDIF}
1580 end;
1582 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1583 end;
1586 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1587 var
1588 obj: TObj;
1589 begin
1590 obj.X := X;
1591 obj.Y := Y;
1592 obj.rect.X := 0;
1593 obj.rect.Y := 0;
1594 obj.rect.Width := 39;
1595 obj.rect.Height := 52;
1596 obj.Vel.X := 0;
1597 obj.Vel.Y := 0;
1598 obj.Accel.X := 0;
1599 obj.Accel.Y := 0;
1601 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1602 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1603 else
1604 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1605 end;
1607 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1608 var
1609 obj: TObj;
1610 begin
1611 obj.X := X;
1612 obj.Y := Y;
1613 obj.rect.X := 0;
1614 obj.rect.Y := 0;
1615 obj.rect.Width := 32;
1616 obj.rect.Height := 52;
1617 obj.Vel.X := 0;
1618 obj.Vel.Y := 0;
1619 obj.Accel.X := 0;
1620 obj.Accel.Y := 0;
1622 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1623 end;
1625 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1626 Silent: Boolean = False);
1627 var
1628 find_id: DWORD;
1629 dx, dy: Integer;
1630 begin
1631 if WID < 0 then
1632 find_id := FindShot()
1633 else
1634 begin
1635 find_id := WID;
1636 if Integer(find_id) >= High(Shots) then
1637 SetLength(Shots, find_id + 64)
1638 end;
1640 with Shots[find_id] do
1641 begin
1642 g_Obj_Init(@Obj);
1644 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1645 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1647 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1648 dy := -(Obj.Rect.Height div 2);
1650 ShotType := WEAPON_ROCKETLAUNCHER;
1651 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1653 Animation := nil;
1654 triggers := nil;
1655 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1656 end;
1658 Shots[find_id].SpawnerUID := SpawnerUID;
1660 if not Silent then
1661 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1662 end;
1664 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1665 WID: Integer = -1; Silent: Boolean = False);
1666 var
1667 find_id, FramesID: DWORD;
1668 dx, dy: Integer;
1669 begin
1670 if WID < 0 then
1671 find_id := FindShot()
1672 else
1673 begin
1674 find_id := WID;
1675 if Integer(find_id) >= High(Shots) then
1676 SetLength(Shots, find_id + 64)
1677 end;
1679 with Shots[find_id] do
1680 begin
1681 g_Obj_Init(@Obj);
1683 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1684 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1686 dx := -(Obj.Rect.Width div 2);
1687 dy := -(Obj.Rect.Height div 2);
1689 ShotType := WEAPON_SKEL_FIRE;
1690 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1692 triggers := nil;
1693 target := TargetUID;
1694 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1695 Animation := TAnimation.Create(FramesID, True, 5);
1696 end;
1698 Shots[find_id].SpawnerUID := SpawnerUID;
1700 if not Silent then
1701 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1702 end;
1704 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1705 Silent: Boolean = False);
1706 var
1707 find_id, FramesID: DWORD;
1708 dx, dy: Integer;
1709 begin
1710 if WID < 0 then
1711 find_id := FindShot()
1712 else
1713 begin
1714 find_id := WID;
1715 if Integer(find_id) >= High(Shots) then
1716 SetLength(Shots, find_id + 64);
1717 end;
1719 with Shots[find_id] do
1720 begin
1721 g_Obj_Init(@Obj);
1723 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1724 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1726 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1727 dy := -(Obj.Rect.Height div 2);
1729 ShotType := WEAPON_PLASMA;
1730 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1732 triggers := nil;
1733 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1734 Animation := TAnimation.Create(FramesID, True, 5);
1735 end;
1737 Shots[find_id].SpawnerUID := SpawnerUID;
1739 if not Silent then
1740 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1741 end;
1743 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1744 Silent: Boolean = False);
1745 var
1746 find_id: DWORD;
1747 dx, dy: Integer;
1748 begin
1749 if WID < 0 then
1750 find_id := FindShot()
1751 else
1752 begin
1753 find_id := WID;
1754 if Integer(find_id) >= High(Shots) then
1755 SetLength(Shots, find_id + 64);
1756 end;
1758 with Shots[find_id] do
1759 begin
1760 g_Obj_Init(@Obj);
1762 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1763 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1765 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1766 dy := -(Obj.Rect.Height div 2);
1768 ShotType := WEAPON_FLAMETHROWER;
1769 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1771 triggers := nil;
1772 Animation := nil;
1773 TextureID := 0;
1774 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1775 end;
1777 Shots[find_id].SpawnerUID := SpawnerUID;
1779 // if not Silent then
1780 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1781 end;
1783 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1784 Silent: Boolean = False);
1785 var
1786 find_id, FramesID: DWORD;
1787 dx, dy: Integer;
1788 begin
1789 if WID < 0 then
1790 find_id := FindShot()
1791 else
1792 begin
1793 find_id := WID;
1794 if Integer(find_id) >= High(Shots) then
1795 SetLength(Shots, find_id + 64)
1796 end;
1798 with Shots[find_id] do
1799 begin
1800 g_Obj_Init(@Obj);
1802 Obj.Rect.Width := 16;
1803 Obj.Rect.Height := 16;
1805 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1806 dy := -(Obj.Rect.Height div 2);
1808 ShotType := WEAPON_IMP_FIRE;
1809 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1811 triggers := nil;
1812 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1813 Animation := TAnimation.Create(FramesID, True, 4);
1814 end;
1816 Shots[find_id].SpawnerUID := SpawnerUID;
1818 if not Silent then
1819 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1820 end;
1822 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1823 Silent: Boolean = False);
1824 var
1825 find_id, FramesID: DWORD;
1826 dx, dy: Integer;
1827 begin
1828 if WID < 0 then
1829 find_id := FindShot()
1830 else
1831 begin
1832 find_id := WID;
1833 if Integer(find_id) >= High(Shots) then
1834 SetLength(Shots, find_id + 64)
1835 end;
1837 with Shots[find_id] do
1838 begin
1839 g_Obj_Init(@Obj);
1841 Obj.Rect.Width := 16;
1842 Obj.Rect.Height := 16;
1844 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1845 dy := -(Obj.Rect.Height div 2);
1847 ShotType := WEAPON_CACO_FIRE;
1848 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1850 triggers := nil;
1851 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1852 Animation := TAnimation.Create(FramesID, True, 4);
1853 end;
1855 Shots[find_id].SpawnerUID := SpawnerUID;
1857 if not Silent then
1858 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1859 end;
1861 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1862 Silent: Boolean = False);
1863 var
1864 find_id, FramesID: DWORD;
1865 dx, dy: Integer;
1866 begin
1867 if WID < 0 then
1868 find_id := FindShot()
1869 else
1870 begin
1871 find_id := WID;
1872 if Integer(find_id) >= High(Shots) then
1873 SetLength(Shots, find_id + 64)
1874 end;
1876 with Shots[find_id] do
1877 begin
1878 g_Obj_Init(@Obj);
1880 Obj.Rect.Width := 32;
1881 Obj.Rect.Height := 16;
1883 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1884 dy := -(Obj.Rect.Height div 2);
1886 ShotType := WEAPON_BARON_FIRE;
1887 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1889 triggers := nil;
1890 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1891 Animation := TAnimation.Create(FramesID, True, 4);
1892 end;
1894 Shots[find_id].SpawnerUID := SpawnerUID;
1896 if not Silent then
1897 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1898 end;
1900 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1901 Silent: Boolean = False);
1902 var
1903 find_id, FramesID: DWORD;
1904 dx, dy: Integer;
1905 begin
1906 if WID < 0 then
1907 find_id := FindShot()
1908 else
1909 begin
1910 find_id := WID;
1911 if Integer(find_id) >= High(Shots) then
1912 SetLength(Shots, find_id + 64)
1913 end;
1915 with Shots[find_id] do
1916 begin
1917 g_Obj_Init(@Obj);
1919 Obj.Rect.Width := 16;
1920 Obj.Rect.Height := 16;
1922 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1923 dy := -(Obj.Rect.Height div 2);
1925 ShotType := WEAPON_BSP_FIRE;
1926 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1928 triggers := nil;
1930 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1931 Animation := TAnimation.Create(FramesID, True, 4);
1932 end;
1934 Shots[find_id].SpawnerUID := SpawnerUID;
1936 if not Silent then
1937 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1938 end;
1940 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1941 Silent: Boolean = False);
1942 var
1943 find_id, FramesID: DWORD;
1944 dx, dy: Integer;
1945 begin
1946 if WID < 0 then
1947 find_id := FindShot()
1948 else
1949 begin
1950 find_id := WID;
1951 if Integer(find_id) >= High(Shots) then
1952 SetLength(Shots, find_id + 64)
1953 end;
1955 with Shots[find_id] do
1956 begin
1957 g_Obj_Init(@Obj);
1959 Obj.Rect.Width := 32;
1960 Obj.Rect.Height := 32;
1962 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1963 dy := -(Obj.Rect.Height div 2);
1965 ShotType := WEAPON_MANCUB_FIRE;
1966 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1968 triggers := nil;
1970 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1971 Animation := TAnimation.Create(FramesID, True, 4);
1972 end;
1974 Shots[find_id].SpawnerUID := SpawnerUID;
1976 if not Silent then
1977 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1978 end;
1980 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1981 Silent: Boolean = False);
1982 var
1983 find_id, FramesID: DWORD;
1984 dx, dy: Integer;
1985 begin
1986 if WID < 0 then
1987 find_id := FindShot()
1988 else
1989 begin
1990 find_id := WID;
1991 if Integer(find_id) >= High(Shots) then
1992 SetLength(Shots, find_id + 64)
1993 end;
1995 with Shots[find_id] do
1996 begin
1997 g_Obj_Init(@Obj);
1999 Obj.Rect.Width := SHOT_BFG_WIDTH;
2000 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2002 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2003 dy := -(Obj.Rect.Height div 2);
2005 ShotType := WEAPON_BFG;
2006 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2008 triggers := nil;
2009 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2010 Animation := TAnimation.Create(FramesID, True, 6);
2011 end;
2013 Shots[find_id].SpawnerUID := SpawnerUID;
2015 if not Silent then
2016 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2017 end;
2019 procedure g_Weapon_bfghit(x, y: Integer);
2020 var
2021 ID: DWORD;
2022 Anim: TAnimation;
2023 begin
2024 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2025 begin
2026 Anim := TAnimation.Create(ID, False, 4);
2027 g_GFX_OnceAnim(x-32, y-32, Anim);
2028 Anim.Free();
2029 end;
2030 end;
2032 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2033 Silent: Boolean = False);
2034 begin
2035 if not Silent then
2036 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2038 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2039 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2040 begin
2041 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2042 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2043 end;
2044 end;
2046 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2047 Silent: Boolean = False);
2048 begin
2049 if not Silent then
2050 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2052 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2053 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2054 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2055 begin
2056 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2057 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2058 end;
2059 end;
2061 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2062 Silent: Boolean = False);
2063 var
2064 i, j: Integer;
2065 begin
2066 if not Silent then
2067 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2069 for i := 0 to 9 do
2070 begin
2071 j := Random(17)-8; // -8 .. 8
2072 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2073 end;
2074 end;
2076 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2077 Silent: Boolean = False);
2078 var
2079 a, i, j: Integer;
2080 begin
2081 if not Silent then
2082 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2084 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2085 for i := 0 to a do
2086 begin
2087 j := Random(41)-20; // -20 .. 20
2088 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2089 end;
2090 end;
2092 procedure g_Weapon_Update();
2093 var
2094 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2095 _id: DWORD;
2096 Anim: TAnimation;
2097 t: DWArray;
2098 st: Word;
2099 s: String;
2100 o: TObj;
2101 spl: Boolean;
2102 Loud: Boolean;
2103 tcx, tcy: Integer;
2104 begin
2105 if Shots = nil then
2106 Exit;
2108 for i := 0 to High(Shots) do
2109 begin
2110 if Shots[i].ShotType = 0 then
2111 Continue;
2113 Loud := True;
2115 with Shots[i] do
2116 begin
2117 Timeout := Timeout - 1;
2118 oldvx := Obj.Vel.X;
2119 oldvy := Obj.Vel.Y;
2120 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2121 if (Stopped = 0) and g_Game_IsServer then
2122 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2123 SpawnerUID, ACTIVATE_SHOT, triggers)
2124 else
2125 t := nil;
2127 if t <> nil then
2128 begin
2129 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2130 if triggers = nil then
2131 triggers := t
2132 else
2133 begin
2134 h := High(t);
2136 for a := 0 to h do
2137 if not InDWArray(t[a], triggers) then
2138 begin
2139 SetLength(triggers, Length(triggers)+1);
2140 triggers[High(triggers)] := t[a];
2141 end;
2142 end;
2143 end;
2145 // Àíèìàöèÿ ñíàðÿäà:
2146 if Animation <> nil then
2147 Animation.Update();
2149 // Äâèæåíèå:
2150 spl := (ShotType <> WEAPON_PLASMA) and
2151 (ShotType <> WEAPON_BFG) and
2152 (ShotType <> WEAPON_BSP_FIRE) and
2153 (ShotType <> WEAPON_FLAMETHROWER);
2155 if Stopped = 0 then
2156 begin
2157 st := g_Obj_Move(@Obj, False, spl);
2158 end
2159 else
2160 begin
2161 st := 0;
2162 end;
2163 positionChanged(); // this updates spatial accelerators
2165 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2166 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2167 begin
2168 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2169 ShotType := 0;
2170 Animation.Free();
2171 Continue;
2172 end;
2174 cx := Obj.X + (Obj.Rect.Width div 2);
2175 cy := Obj.Y + (Obj.Rect.Height div 2);
2177 case ShotType of
2178 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2179 begin
2180 // Âûëåòåëà èç âîäû:
2181 if WordBool(st and MOVE_HITAIR) then
2182 g_Obj_SetSpeed(@Obj, 12);
2184 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2185 if WordBool(st and MOVE_INWATER) then
2186 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2187 Obj.Y+(Obj.Rect.Height div 2),
2188 1+Random(3), 16, 16)
2189 else
2190 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2191 begin
2192 Anim := TAnimation.Create(_id, False, 3);
2193 Anim.Alpha := 150;
2194 g_GFX_OnceAnim(Obj.X-14+Random(9),
2195 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2196 Anim, ONCEANIM_SMOKE);
2197 Anim.Free();
2198 end;
2200 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2201 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2202 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2203 (Timeout < 1) then
2204 begin
2205 Obj.Vel.X := 0;
2206 Obj.Vel.Y := 0;
2208 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2210 if ShotType = WEAPON_SKEL_FIRE then
2211 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2212 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2213 begin
2214 Anim := TAnimation.Create(TextureID, False, 8);
2215 Anim.Blending := False;
2216 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2217 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2218 Anim.Free();
2219 end;
2220 end
2221 else
2222 begin // Âçðûâ Ðàêåòû
2223 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2224 begin
2225 Anim := TAnimation.Create(TextureID, False, 6);
2226 Anim.Blending := False;
2227 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2228 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2229 Anim.Free();
2230 end;
2231 end;
2233 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2235 ShotType := 0;
2236 end;
2238 if ShotType = WEAPON_SKEL_FIRE then
2239 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2240 if GetPos(target, @o) then
2241 throw(i, Obj.X, Obj.Y,
2242 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2243 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2244 12);
2245 end;
2246 end;
2248 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2249 begin
2250 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2251 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2252 begin
2253 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2254 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2255 ShotType := 0;
2256 Continue;
2257 end;
2259 // Âåëè÷èíà óðîíà:
2260 if (ShotType = WEAPON_PLASMA) and
2261 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2262 a := 10
2263 else
2264 a := 5;
2266 if ShotType = WEAPON_BSP_FIRE then
2267 a := 10;
2269 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2270 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2271 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2272 (Timeout < 1) then
2273 begin
2274 if ShotType = WEAPON_PLASMA then
2275 s := 'FRAMES_EXPLODE_PLASMA'
2276 else
2277 s := 'FRAMES_EXPLODE_BSPFIRE';
2279 // Âçðûâ Ïëàçìû:
2280 if g_Frames_Get(TextureID, s) then
2281 begin
2282 Anim := TAnimation.Create(TextureID, False, 3);
2283 Anim.Blending := False;
2284 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2285 Anim.Free();
2286 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2287 end;
2289 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2291 ShotType := 0;
2292 end;
2293 end;
2295 WEAPON_FLAMETHROWER: // Îãíåìåò
2296 begin
2297 // Ñî âðåìåíåì óìèðàåò
2298 if (Timeout < 1) then
2299 begin
2300 ShotType := 0;
2301 Continue;
2302 end;
2303 // Ïîä âîäîé òîæå
2304 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2305 begin
2306 if WordBool(st and MOVE_HITWATER) then
2307 begin
2308 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2309 begin
2310 Anim := TAnimation.Create(_id, False, 3);
2311 Anim.Alpha := 0;
2312 tcx := Random(8);
2313 tcy := Random(8);
2314 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2315 cy-4+tcy-(Anim.Height div 2),
2316 Anim, ONCEANIM_SMOKE);
2317 Anim.Free();
2318 end;
2319 end
2320 else
2321 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2322 ShotType := 0;
2323 Continue;
2324 end;
2326 // Ãðàâèòàöèÿ
2327 if Stopped = 0 then
2328 Obj.Accel.Y := Obj.Accel.Y + 1;
2329 // Ïîïàëè â ñòåíó èëè â âîäó:
2330 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2331 begin
2332 // Ïðèëèïàåì:
2333 Obj.Vel.X := 0;
2334 Obj.Vel.Y := 0;
2335 Obj.Accel.Y := 0;
2336 if WordBool(st and MOVE_HITWALL) then
2337 Stopped := MOVE_HITWALL
2338 else if WordBool(st and MOVE_HITLAND) then
2339 Stopped := MOVE_HITLAND
2340 else if WordBool(st and MOVE_HITCEIL) then
2341 Stopped := MOVE_HITCEIL;
2342 end;
2344 a := IfThen(Stopped = 0, 3, 1);
2345 // Åñëè â êîãî-òî ïîïàëè
2346 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2347 begin
2348 // HIT_FLAME ñàì ïîäîææåò
2349 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2350 if Stopped = 0 then
2351 ShotType := 0;
2352 end;
2354 if Stopped = 0 then
2355 tf := 2
2356 else
2357 tf := 3;
2359 if (gTime mod LongWord(tf) = 0) then
2360 begin
2361 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2362 Anim.Alpha := 0;
2363 case Stopped of
2364 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2365 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2366 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2367 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2368 end;
2369 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2370 Anim.Free();
2371 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2372 end;
2373 end;
2375 WEAPON_BFG: // BFG
2376 begin
2377 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2378 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2379 begin
2380 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2381 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2382 ShotType := 0;
2383 Continue;
2384 end;
2386 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2387 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2388 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2389 (Timeout < 1) then
2390 begin
2391 // Ëó÷è BFG:
2392 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2394 // Âçðûâ BFG:
2395 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2396 begin
2397 Anim := TAnimation.Create(TextureID, False, 6);
2398 Anim.Blending := False;
2399 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2400 Anim.Free();
2401 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2402 end;
2404 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2406 ShotType := 0;
2407 end;
2408 end;
2410 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2411 begin
2412 // Âûëåòåë èç âîäû:
2413 if WordBool(st and MOVE_HITAIR) then
2414 g_Obj_SetSpeed(@Obj, 16);
2416 // Âåëè÷èíà óðîíà:
2417 if ShotType = WEAPON_IMP_FIRE then
2418 a := 5
2419 else
2420 if ShotType = WEAPON_CACO_FIRE then
2421 a := 20
2422 else
2423 a := 40;
2425 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2426 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2427 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2428 (Timeout < 1) then
2429 begin
2430 if ShotType = WEAPON_IMP_FIRE then
2431 s := 'FRAMES_EXPLODE_IMPFIRE'
2432 else
2433 if ShotType = WEAPON_CACO_FIRE then
2434 s := 'FRAMES_EXPLODE_CACOFIRE'
2435 else
2436 s := 'FRAMES_EXPLODE_BARONFIRE';
2438 // Âçðûâ:
2439 if g_Frames_Get(TextureID, s) then
2440 begin
2441 Anim := TAnimation.Create(TextureID, False, 6);
2442 Anim.Blending := False;
2443 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2444 Anim.Free();
2445 end;
2447 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2449 ShotType := 0;
2450 end;
2451 end;
2453 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2454 begin
2455 // Âûëåòåë èç âîäû:
2456 if WordBool(st and MOVE_HITAIR) then
2457 g_Obj_SetSpeed(@Obj, 16);
2459 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2460 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2461 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2462 (Timeout < 1) then
2463 begin
2464 // Âçðûâ:
2465 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2466 begin
2467 Anim := TAnimation.Create(TextureID, False, 6);
2468 Anim.Blending := False;
2469 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2470 Anim.Free();
2471 end;
2473 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2475 ShotType := 0;
2476 end;
2477 end;
2478 end; // case ShotType of...
2480 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2481 if (ShotType = 0) then
2482 begin
2483 if gGameSettings.GameType = GT_SERVER then
2484 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2485 if Animation <> nil then
2486 begin
2487 Animation.Free();
2488 Animation := nil;
2489 end;
2490 end
2491 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2492 if gGameSettings.GameType = GT_SERVER then
2493 MH_SEND_UpdateShot(i);
2494 end;
2495 end;
2496 end;
2498 procedure g_Weapon_Draw();
2499 var
2500 i: Integer;
2501 a: SmallInt;
2502 p: TDFPoint;
2503 begin
2504 if Shots = nil then
2505 Exit;
2507 for i := 0 to High(Shots) do
2508 if Shots[i].ShotType <> 0 then
2509 with Shots[i] do
2510 begin
2511 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2512 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2513 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2514 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2515 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2516 else
2517 a := 0;
2519 p.X := Obj.Rect.Width div 2;
2520 p.Y := Obj.Rect.Height div 2;
2522 if Animation <> nil then
2523 begin
2524 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2525 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2526 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2527 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2528 else
2529 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2530 end
2531 else if TextureID <> 0 then
2532 begin
2533 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2534 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2535 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2536 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2537 end;
2539 if g_debug_Frames then
2540 begin
2541 e_DrawQuad(Obj.X+Obj.Rect.X,
2542 Obj.Y+Obj.Rect.Y,
2543 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2544 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2545 0, 255, 0);
2546 end;
2547 end;
2548 end;
2550 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2551 var
2552 a: Integer;
2553 begin
2554 Result := False;
2556 if Shots = nil then
2557 Exit;
2559 for a := 0 to High(Shots) do
2560 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2561 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2562 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2563 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2564 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2565 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2566 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2567 begin
2568 Result := True;
2569 Exit;
2570 end;
2571 end;
2573 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2574 var
2575 count, i, j: Integer;
2576 dw: DWORD;
2577 begin
2578 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2579 count := 0;
2580 if Shots <> nil then
2581 for i := 0 to High(Shots) do
2582 if Shots[i].ShotType <> 0 then
2583 count := count + 1;
2585 Mem := TBinMemoryWriter.Create((count+1) * 80);
2587 // Êîëè÷åñòâî ñíàðÿäîâ:
2588 Mem.WriteInt(count);
2590 if count = 0 then
2591 Exit;
2593 for i := 0 to High(Shots) do
2594 if Shots[i].ShotType <> 0 then
2595 begin
2596 // Ñèãíàòóðà ñíàðÿäà:
2597 dw := SHOT_SIGNATURE; // 'SHOT'
2598 Mem.WriteDWORD(dw);
2599 // Òèï ñíàðÿäà:
2600 Mem.WriteByte(Shots[i].ShotType);
2601 // Öåëü:
2602 Mem.WriteWord(Shots[i].Target);
2603 // UID ñòðåëÿâøåãî:
2604 Mem.WriteWord(Shots[i].SpawnerUID);
2605 // Ðàçìåð ïîëÿ Triggers:
2606 dw := Length(Shots[i].Triggers);
2607 Mem.WriteDWORD(dw);
2608 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2609 for j := 0 to Integer(dw)-1 do
2610 Mem.WriteDWORD(Shots[i].Triggers[j]);
2611 // Îáúåêò ñíàðÿäà:
2612 Obj_SaveState(@Shots[i].Obj, Mem);
2613 // Êîñòûëèíà åáàíàÿ:
2614 Mem.WriteByte(Shots[i].Stopped);
2615 end;
2616 end;
2618 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2619 var
2620 count, i, j: Integer;
2621 dw: DWORD;
2622 begin
2623 if Mem = nil then
2624 Exit;
2626 // Êîëè÷åñòâî ñíàðÿäîâ:
2627 Mem.ReadInt(count);
2629 SetLength(Shots, count);
2631 if count = 0 then
2632 Exit;
2634 for i := 0 to count-1 do
2635 begin
2636 // Ñèãíàòóðà ñíàðÿäà:
2637 Mem.ReadDWORD(dw);
2638 if dw <> SHOT_SIGNATURE then // 'SHOT'
2639 begin
2640 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2641 end;
2642 // Òèï ñíàðÿäà:
2643 Mem.ReadByte(Shots[i].ShotType);
2644 // Öåëü:
2645 Mem.ReadWord(Shots[i].Target);
2646 // UID ñòðåëÿâøåãî:
2647 Mem.ReadWord(Shots[i].SpawnerUID);
2648 // Ðàçìåð ïîëÿ Triggers:
2649 Mem.ReadDWORD(dw);
2650 SetLength(Shots[i].Triggers, dw);
2651 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2652 for j := 0 to Integer(dw)-1 do
2653 Mem.ReadDWORD(Shots[i].Triggers[j]);
2654 // Îáúåêò ïðåäìåòà:
2655 Obj_LoadState(@Shots[i].Obj, Mem);
2656 // Êîñòûëèíà åáàíàÿ:
2657 Mem.ReadByte(Shots[i].Stopped);
2659 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2660 Shots[i].TextureID := DWORD(-1);
2661 Shots[i].Animation := nil;
2663 case Shots[i].ShotType of
2664 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2665 begin
2666 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2667 end;
2668 WEAPON_PLASMA:
2669 begin
2670 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2671 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2672 end;
2673 WEAPON_BFG:
2674 begin
2675 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2676 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2677 end;
2678 WEAPON_IMP_FIRE:
2679 begin
2680 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2681 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2682 end;
2683 WEAPON_BSP_FIRE:
2684 begin
2685 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2686 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2687 end;
2688 WEAPON_CACO_FIRE:
2689 begin
2690 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2691 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2692 end;
2693 WEAPON_BARON_FIRE:
2694 begin
2695 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2696 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2697 end;
2698 WEAPON_MANCUB_FIRE:
2699 begin
2700 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2701 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2702 end;
2703 end;
2704 end;
2705 end;
2707 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2708 var
2709 cx, cy: Integer;
2710 Anim: TAnimation;
2711 s: string;
2712 begin
2713 if Shots = nil then
2714 Exit;
2715 if (I > High(Shots)) or (I < 0) then Exit;
2717 with Shots[I] do
2718 begin
2719 if ShotType = 0 then Exit;
2720 Obj.X := X;
2721 Obj.Y := Y;
2722 cx := Obj.X + (Obj.Rect.Width div 2);
2723 cy := Obj.Y + (Obj.Rect.Height div 2);
2725 case ShotType of
2726 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2727 begin
2728 if Loud then
2729 begin
2730 if ShotType = WEAPON_SKEL_FIRE then
2731 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2732 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2733 begin
2734 Anim := TAnimation.Create(TextureID, False, 8);
2735 Anim.Blending := False;
2736 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2737 Anim.Free();
2738 end;
2739 end
2740 else
2741 begin // Âçðûâ Ðàêåòû
2742 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2743 begin
2744 Anim := TAnimation.Create(TextureID, False, 6);
2745 Anim.Blending := False;
2746 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2747 Anim.Free();
2748 end;
2749 end;
2750 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2751 end;
2752 end;
2754 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2755 begin
2756 if ShotType = WEAPON_PLASMA then
2757 s := 'FRAMES_EXPLODE_PLASMA'
2758 else
2759 s := 'FRAMES_EXPLODE_BSPFIRE';
2761 if g_Frames_Get(TextureID, s) and loud then
2762 begin
2763 Anim := TAnimation.Create(TextureID, False, 3);
2764 Anim.Blending := False;
2765 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2766 Anim.Free();
2768 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2769 end;
2770 end;
2772 WEAPON_BFG: // BFG
2773 begin
2774 // Âçðûâ BFG:
2775 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2776 begin
2777 Anim := TAnimation.Create(TextureID, False, 6);
2778 Anim.Blending := False;
2779 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2780 Anim.Free();
2782 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2783 end;
2784 end;
2786 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2787 begin
2788 if ShotType = WEAPON_IMP_FIRE then
2789 s := 'FRAMES_EXPLODE_IMPFIRE'
2790 else
2791 if ShotType = WEAPON_CACO_FIRE then
2792 s := 'FRAMES_EXPLODE_CACOFIRE'
2793 else
2794 s := 'FRAMES_EXPLODE_BARONFIRE';
2796 if g_Frames_Get(TextureID, s) and Loud then
2797 begin
2798 Anim := TAnimation.Create(TextureID, False, 6);
2799 Anim.Blending := False;
2800 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2801 Anim.Free();
2803 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2804 end;
2805 end;
2807 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2808 begin
2809 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2810 begin
2811 Anim := TAnimation.Create(TextureID, False, 6);
2812 Anim.Blending := False;
2813 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2814 Anim.Free();
2816 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2817 end;
2818 end;
2819 end; // case ShotType of...
2821 ShotType := 0;
2822 Animation.Free();
2823 end;
2824 end;
2827 procedure g_Weapon_AddDynLights();
2828 var
2829 i: Integer;
2830 begin
2831 if Shots = nil then Exit;
2832 for i := 0 to High(Shots) do
2833 begin
2834 if Shots[i].ShotType = 0 then continue;
2835 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2836 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2837 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2838 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2839 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2840 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2841 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2842 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2843 (Shots[i].ShotType = WEAPON_PLASMA) or
2844 (Shots[i].ShotType = WEAPON_BFG) or
2845 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2846 false then
2847 begin
2848 if (Shots[i].ShotType = WEAPON_PLASMA) then
2849 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)
2850 else if (Shots[i].ShotType = WEAPON_BFG) then
2851 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)
2852 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2853 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)
2854 else
2855 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);
2856 end;
2857 end;
2858 end;
2861 procedure TShot.positionChanged (); begin end;
2864 end.