DEADSOFTWARE

21c580375a533aa95e6f2dc6b8e9e0791bb52c27
[d2df-sdl.git] / src / game / g_weapons.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
17 unit g_weapons;
19 interface
21 uses
22 SysUtils, Classes, mempool,
23 g_textures, g_basic, g_phys, xprofiler;
26 type
27 TShot = record
28 ShotType: Byte;
29 Target: Word;
30 SpawnerUID: Word;
31 Triggers: DWArray;
32 Obj: TObj;
33 Animation: TAnimation;
34 TextureID: DWORD;
35 Timeout: DWORD;
36 Stopped: Byte;
38 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
39 end;
42 var
43 Shots: array of TShot = nil;
44 LastShotID: Integer = 0;
46 procedure g_Weapon_LoadData();
47 procedure g_Weapon_FreeData();
48 procedure g_Weapon_Init();
49 procedure g_Weapon_Free();
50 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
51 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
52 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
54 procedure g_Weapon_gun(const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
55 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
56 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
57 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
58 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
59 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
60 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
61 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
62 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
63 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
64 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
65 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
66 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
67 procedure g_Weapon_bfghit(x, y: Integer);
68 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
69 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
70 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
71 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
73 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
74 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
75 procedure g_Weapon_PreUpdate();
76 procedure g_Weapon_Update();
77 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
78 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
80 procedure g_Weapon_SaveState (st: TStream);
81 procedure g_Weapon_LoadState (st: TStream);
83 procedure g_Weapon_AddDynLights();
85 const
86 WEAPON_KASTET = 0;
87 WEAPON_SAW = 1;
88 WEAPON_PISTOL = 2;
89 WEAPON_SHOTGUN1 = 3;
90 WEAPON_SHOTGUN2 = 4;
91 WEAPON_CHAINGUN = 5;
92 WEAPON_ROCKETLAUNCHER = 6;
93 WEAPON_PLASMA = 7;
94 WEAPON_BFG = 8;
95 WEAPON_SUPERPULEMET = 9;
96 WEAPON_FLAMETHROWER = 10;
97 WEAPON_ZOMBY_PISTOL = 20;
98 WEAPON_IMP_FIRE = 21;
99 WEAPON_BSP_FIRE = 22;
100 WEAPON_CACO_FIRE = 23;
101 WEAPON_BARON_FIRE = 24;
102 WEAPON_MANCUB_FIRE = 25;
103 WEAPON_SKEL_FIRE = 26;
105 WP_FIRST = WEAPON_KASTET;
106 WP_LAST = WEAPON_FLAMETHROWER;
109 var
110 gwep_debug_fast_trace: Boolean = true;
113 implementation
115 uses
116 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
117 g_console, g_options, g_game,
118 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
119 g_language, g_netmsg, g_grid,
120 geom, binheap, hashtable, utils, xstreams;
122 type
123 TWaterPanel = record
124 X, Y: Integer;
125 Width, Height: Word;
126 Active: Boolean;
127 end;
129 const
130 SHOT_ROCKETLAUNCHER_WIDTH = 14;
131 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
133 SHOT_SKELFIRE_WIDTH = 14;
134 SHOT_SKELFIRE_HEIGHT = 14;
136 SHOT_PLASMA_WIDTH = 16;
137 SHOT_PLASMA_HEIGHT = 16;
139 SHOT_BFG_WIDTH = 32;
140 SHOT_BFG_HEIGHT = 32;
141 SHOT_BFG_DAMAGE = 100;
142 SHOT_BFG_RADIUS = 256;
144 SHOT_FLAME_WIDTH = 4;
145 SHOT_FLAME_HEIGHT = 4;
146 SHOT_FLAME_LIFETIME = 180;
148 SHOT_SIGNATURE = $544F4853; // 'SHOT'
150 type
151 PHitTime = ^THitTime;
152 THitTime = record
153 distSq: Integer;
154 mon: TMonster;
155 plridx: Integer; // if mon=nil
156 x, y: Integer;
157 end;
159 TBinHeapKeyHitTime = class
160 public
161 class function less (const a, b: Integer): Boolean; inline;
162 end;
164 // indicies in `wgunHitTime` array
165 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer, TBinHeapKeyHitTime>;
167 var
168 WaterMap: array of array of DWORD = nil;
169 //wgunMonHash: THashIntInt = nil;
170 wgunHitHeap: TBinaryHeapHitTimes = nil;
171 wgunHitTime: array of THitTime = nil;
172 wgunHitTimeUsed: Integer = 0;
175 class function TBinHeapKeyHitTime.less (const a, b: Integer): Boolean;
176 var
177 hta, htb: PHitTime;
178 begin
179 hta := @wgunHitTime[a];
180 htb := @wgunHitTime[b];
181 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
182 if (hta.mon <> nil) then
183 begin
184 // a is monster
185 if (htb.mon = nil) then begin result := false; exit; end; // players first
186 result := (hta.mon.UID < htb.mon.UID); // why not?
187 end
188 else
189 begin
190 // a is player
191 if (htb.mon <> nil) then begin result := true; exit; end; // players first
192 result := (hta.plridx < htb.plridx); // why not?
193 end;
194 end;
197 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
198 begin
199 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
200 with wgunHitTime[wgunHitTimeUsed] do
201 begin
202 distSq := adistSq;
203 mon := amon;
204 plridx := -1;
205 x := ax;
206 y := ay;
207 end;
208 wgunHitHeap.insert(wgunHitTimeUsed);
209 Inc(wgunHitTimeUsed);
210 end;
213 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
214 begin
215 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
216 with wgunHitTime[wgunHitTimeUsed] do
217 begin
218 distSq := adistSq;
219 mon := nil;
220 plridx := aplridx;
221 x := ax;
222 y := ay;
223 end;
224 wgunHitHeap.insert(wgunHitTimeUsed);
225 Inc(wgunHitTimeUsed);
226 end;
229 function FindShot(): DWORD;
230 var
231 i: Integer;
232 begin
233 if Shots <> nil then
234 for i := 0 to High(Shots) do
235 if Shots[i].ShotType = 0 then
236 begin
237 Result := i;
238 LastShotID := Result;
239 Exit;
240 end;
242 if Shots = nil then
243 begin
244 SetLength(Shots, 128);
245 Result := 0;
246 end
247 else
248 begin
249 Result := High(Shots) + 1;
250 SetLength(Shots, Length(Shots) + 128);
251 end;
252 LastShotID := Result;
253 end;
255 procedure CreateWaterMap();
256 var
257 WaterArray: Array of TWaterPanel;
258 a, b, c, m: Integer;
259 ok: Boolean;
260 begin
261 if gWater = nil then
262 Exit;
264 SetLength(WaterArray, Length(gWater));
266 for a := 0 to High(gWater) do
267 begin
268 WaterArray[a].X := gWater[a].X;
269 WaterArray[a].Y := gWater[a].Y;
270 WaterArray[a].Width := gWater[a].Width;
271 WaterArray[a].Height := gWater[a].Height;
272 WaterArray[a].Active := True;
273 end;
275 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
277 for a := 0 to High(WaterArray) do
278 if WaterArray[a].Active then
279 begin
280 WaterArray[a].Active := False;
281 m := Length(WaterMap);
282 SetLength(WaterMap, m+1);
283 SetLength(WaterMap[m], 1);
284 WaterMap[m][0] := a;
285 ok := True;
287 while ok do
288 begin
289 ok := False;
290 for b := 0 to High(WaterArray) do
291 if WaterArray[b].Active then
292 for c := 0 to High(WaterMap[m]) do
293 if g_CollideAround(WaterArray[b].X,
294 WaterArray[b].Y,
295 WaterArray[b].Width,
296 WaterArray[b].Height,
297 WaterArray[WaterMap[m][c]].X,
298 WaterArray[WaterMap[m][c]].Y,
299 WaterArray[WaterMap[m][c]].Width,
300 WaterArray[WaterMap[m][c]].Height) then
301 begin
302 WaterArray[b].Active := False;
303 SetLength(WaterMap[m],
304 Length(WaterMap[m])+1);
305 WaterMap[m][High(WaterMap[m])] := b;
306 ok := True;
307 Break;
308 end;
309 end;
311 g_Game_StepLoading();
312 end;
314 WaterArray := nil;
315 end;
318 var
319 chkTrap_pl: array [0..256] of Integer;
320 chkTrap_mn: array [0..65535] of TMonster;
322 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
323 var
324 //a, b, c, d, i1, i2: Integer;
325 //chkTrap_pl, chkTrap_mn: WArray;
326 plaCount: Integer = 0;
327 mnaCount: Integer = 0;
328 frameId: DWord;
331 function monsWaterCheck (mon: TMonster): Boolean;
332 begin
333 result := false; // don't stop
334 if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
335 begin
336 i2 += 1;
337 chkTrap_mn[i2] := monidx;
338 end;
339 end;
342 function monsWaterCheck (mon: TMonster): Boolean;
343 begin
344 result := false; // don't stop
345 if (mon.trapCheckFrameId <> frameId) then
346 begin
347 mon.trapCheckFrameId := frameId;
348 chkTrap_mn[mnaCount] := mon;
349 Inc(mnaCount);
350 end;
351 end;
353 var
354 a, b, c, d, f: Integer;
355 pan: TPanel;
356 begin
357 if (gWater = nil) or (WaterMap = nil) then Exit;
359 frameId := g_Mons_getNewTrapFrameId();
361 //i1 := -1;
362 //i2 := -1;
364 //SetLength(chkTrap_pl, 1024);
365 //SetLength(chkTrap_mn, 1024);
366 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
367 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
369 for a := 0 to High(WaterMap) do
370 begin
371 for b := 0 to High(WaterMap[a]) do
372 begin
373 pan := gWater[WaterMap[a][b]];
374 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
376 for c := 0 to High(WaterMap[a]) do
377 begin
378 pan := gWater[WaterMap[a][c]];
379 for d := 0 to High(gPlayers) do
380 begin
381 if (gPlayers[d] <> nil) and (gPlayers[d].alive) then
382 begin
383 if gPlayers[d].Collide(pan) then
384 begin
385 f := 0;
386 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
387 if (f = plaCount) then
388 begin
389 chkTrap_pl[plaCount] := d;
390 Inc(plaCount);
391 if (plaCount = Length(chkTrap_pl)) then break;
392 end;
393 end;
394 end;
395 end;
397 //g_Mons_ForEach(monsWaterCheck);
398 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
399 end;
401 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
402 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
403 end;
404 end;
406 //chkTrap_pl := nil;
407 //chkTrap_mn := nil;
408 end;
410 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
411 var
412 tt, mt: Byte;
413 mon: TMonster;
414 begin
415 Result := False;
417 tt := g_GetUIDType(SpawnerUID);
418 if tt = UID_MONSTER then
419 begin
420 mon := g_Monsters_ByUID(SpawnerUID);
421 if mon <> nil then
422 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
423 else
424 mt := 0;
425 end
426 else
427 mt := 0;
429 if m = nil then Exit;
430 if m.UID = SpawnerUID then
431 begin
432 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
433 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
434 Exit;
435 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
436 if (m.MonsterType = MONSTER_CYBER) or
437 (m.MonsterType = MONSTER_BARREL) then
438 begin
439 Result := True;
440 Exit;
441 end;
442 end;
444 if tt = UID_MONSTER then
445 begin
446 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
447 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
448 Exit;
450 // Îáà ìîíñòðà îäíîãî âèäà:
451 if mt = m.MonsterType then
452 case mt of
453 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
454 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
455 Exit; // Ýòè íå áüþò ñâîèõ
456 end;
457 end;
459 if g_Game_IsServer then
460 begin
461 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
462 Result := m.Damage(d, vx, vy, SpawnerUID, t)
463 else
464 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
465 if t = HIT_FLAME then
466 m.CatchFire(SpawnerUID);
467 end
468 else
469 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
470 end;
473 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
474 begin
475 result := False;
477 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
478 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
480 if g_Game_IsServer then
481 begin
482 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
483 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
484 end;
486 result := true;
487 end;
490 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
492 function monsCheck (mon: TMonster): Boolean;
493 begin
494 result := false; // don't stop
495 if (mon.alive) and (mon.UID <> SpawnerUID) then
496 begin
497 with mon do
498 begin
499 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
500 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
501 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
502 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
503 begin
504 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
505 end;
506 end;
507 end;
508 end;
510 var
511 i, h: Integer;
512 st: Byte;
513 pl: TPlayer;
514 b: Boolean;
515 begin
516 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
518 h := High(gCorpses);
520 if gAdvCorpses and (h <> -1) then
521 for i := 0 to h do
522 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
523 with gCorpses[i] do
524 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
525 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
526 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
527 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
528 begin
529 Damage(50, SpawnerUID, 0, 0);
530 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
531 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
532 end;
534 st := TEAM_NONE;
535 pl := g_Player_Get(SpawnerUID);
536 if pl <> nil then
537 st := pl.Team;
539 h := High(gPlayers);
541 if h <> -1 then
542 for i := 0 to h do
543 if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then
544 with gPlayers[i] do
545 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
546 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
547 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
548 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
549 begin
550 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
551 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
552 else
553 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
554 if b then
555 gPlayers[i].BFGHit();
556 end;
558 //FIXME
559 g_Mons_ForEachAlive(monsCheck);
560 end;
562 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
563 var
564 find_id: DWord;
565 FramesID: DWORD = 0;
566 begin
567 if I < 0 then
568 find_id := FindShot()
569 else
570 begin
571 find_id := I;
572 if Integer(find_id) >= High(Shots) then
573 SetLength(Shots, find_id + 64)
574 end;
576 case ShotType of
577 WEAPON_ROCKETLAUNCHER:
578 begin
579 with Shots[find_id] do
580 begin
581 g_Obj_Init(@Obj);
583 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
584 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
586 Animation := nil;
587 Triggers := nil;
588 ShotType := WEAPON_ROCKETLAUNCHER;
589 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
590 end;
591 end;
593 WEAPON_PLASMA:
594 begin
595 with Shots[find_id] do
596 begin
597 g_Obj_Init(@Obj);
599 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
600 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
602 Triggers := nil;
603 ShotType := WEAPON_PLASMA;
604 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
605 Animation := TAnimation.Create(FramesID, True, 5);
606 end;
607 end;
609 WEAPON_BFG:
610 begin
611 with Shots[find_id] do
612 begin
613 g_Obj_Init(@Obj);
615 Obj.Rect.Width := SHOT_BFG_WIDTH;
616 Obj.Rect.Height := SHOT_BFG_HEIGHT;
618 Triggers := nil;
619 ShotType := WEAPON_BFG;
620 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
621 Animation := TAnimation.Create(FramesID, True, 6);
622 end;
623 end;
625 WEAPON_FLAMETHROWER:
626 begin
627 with Shots[find_id] do
628 begin
629 g_Obj_Init(@Obj);
631 Obj.Rect.Width := SHOT_FLAME_WIDTH;
632 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
634 Triggers := nil;
635 ShotType := WEAPON_FLAMETHROWER;
636 Animation := nil;
637 TextureID := 0;
638 g_Frames_Get(TextureID, 'FRAMES_FLAME');
639 end;
640 end;
642 WEAPON_IMP_FIRE:
643 begin
644 with Shots[find_id] do
645 begin
646 g_Obj_Init(@Obj);
648 Obj.Rect.Width := 16;
649 Obj.Rect.Height := 16;
651 Triggers := nil;
652 ShotType := WEAPON_IMP_FIRE;
653 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
654 Animation := TAnimation.Create(FramesID, True, 4);
655 end;
656 end;
658 WEAPON_CACO_FIRE:
659 begin
660 with Shots[find_id] do
661 begin
662 g_Obj_Init(@Obj);
664 Obj.Rect.Width := 16;
665 Obj.Rect.Height := 16;
667 Triggers := nil;
668 ShotType := WEAPON_CACO_FIRE;
669 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
670 Animation := TAnimation.Create(FramesID, True, 4);
671 end;
672 end;
674 WEAPON_MANCUB_FIRE:
675 begin
676 with Shots[find_id] do
677 begin
678 g_Obj_Init(@Obj);
680 Obj.Rect.Width := 32;
681 Obj.Rect.Height := 32;
683 Triggers := nil;
684 ShotType := WEAPON_MANCUB_FIRE;
685 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
686 Animation := TAnimation.Create(FramesID, True, 4);
687 end;
688 end;
690 WEAPON_BARON_FIRE:
691 begin
692 with Shots[find_id] do
693 begin
694 g_Obj_Init(@Obj);
696 Obj.Rect.Width := 32;
697 Obj.Rect.Height := 16;
699 Triggers := nil;
700 ShotType := WEAPON_BARON_FIRE;
701 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
702 Animation := TAnimation.Create(FramesID, True, 4);
703 end;
704 end;
706 WEAPON_BSP_FIRE:
707 begin
708 with Shots[find_id] do
709 begin
710 g_Obj_Init(@Obj);
712 Obj.Rect.Width := 16;
713 Obj.Rect.Height := 16;
715 Triggers := nil;
716 ShotType := WEAPON_BSP_FIRE;
717 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
718 Animation := TAnimation.Create(FramesID, True, 4);
719 end;
720 end;
722 WEAPON_SKEL_FIRE:
723 begin
724 with Shots[find_id] do
725 begin
726 g_Obj_Init(@Obj);
728 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
729 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
731 Triggers := nil;
732 ShotType := WEAPON_SKEL_FIRE;
733 target := TargetUID;
734 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
735 Animation := TAnimation.Create(FramesID, True, 5);
736 end;
737 end;
738 end;
740 Shots[find_id].Obj.oldX := X;
741 Shots[find_id].Obj.oldY := Y;
742 Shots[find_id].Obj.X := X;
743 Shots[find_id].Obj.Y := Y;
744 Shots[find_id].Obj.Vel.X := XV;
745 Shots[find_id].Obj.Vel.Y := YV;
746 Shots[find_id].Obj.Accel.X := 0;
747 Shots[find_id].Obj.Accel.Y := 0;
748 Shots[find_id].SpawnerUID := Spawner;
749 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
750 Shots[find_id].Stopped := 255
751 else
752 Shots[find_id].Stopped := 0;
753 Result := find_id;
754 end;
756 procedure throw(i, x, y, xd, yd, s: Integer);
757 var
758 a: Integer;
759 begin
760 yd := yd - y;
761 xd := xd - x;
763 a := Max(Abs(xd), Abs(yd));
764 if a = 0 then
765 a := 1;
767 Shots[i].Obj.oldX := x;
768 Shots[i].Obj.oldY := y;
769 Shots[i].Obj.X := x;
770 Shots[i].Obj.Y := y;
771 Shots[i].Obj.Vel.X := (xd*s) div a;
772 Shots[i].Obj.Vel.Y := (yd*s) div a;
773 Shots[i].Obj.Accel.X := 0;
774 Shots[i].Obj.Accel.Y := 0;
775 Shots[i].Stopped := 0;
776 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
777 Shots[i].Timeout := 900 // ~25 sec
778 else
779 begin
780 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
781 Shots[i].Timeout := SHOT_FLAME_LIFETIME
782 else
783 Shots[i].Timeout := 550; // ~15 sec
784 end;
785 end;
787 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
788 var
789 i, h: Integer;
791 function PlayerHit(Team: Byte = 0): Boolean;
792 var
793 i: Integer;
794 ChkTeam: Boolean;
795 p: TPlayer;
796 begin
797 Result := False;
798 h := High(gPlayers);
800 if h <> -1 then
801 for i := 0 to h do
802 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
803 begin
804 ChkTeam := True;
805 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
806 begin
807 p := g_Player_Get(SpawnerUID);
808 if p <> nil then
809 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
810 end;
811 if ChkTeam then
812 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
813 begin
814 if t <> HIT_FLAME then
815 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
816 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
817 if t = HIT_BFG then
818 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
819 Result := True;
820 break;
821 end;
822 end;
823 end;
826 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
827 begin
828 result := false; // don't stop
829 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
830 begin
831 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
832 begin
833 if (t <> HIT_FLAME) then
834 begin
835 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
836 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
837 end;
838 result := True;
839 end;
840 end;
841 end;
844 function monsCheckHit (mon: TMonster): Boolean;
845 begin
846 result := false; // don't stop
847 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
848 begin
849 if (t <> HIT_FLAME) then
850 begin
851 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
852 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
853 end;
854 result := true;
855 end;
856 end;
858 function MonsterHit(): Boolean;
859 begin
860 //result := g_Mons_ForEach(monsCheckHit);
861 //FIXME: accelerate this!
862 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
863 end;
865 begin
866 Result := 0;
868 if HitCorpses then
869 begin
870 h := High(gCorpses);
872 if gAdvCorpses and (h <> -1) then
873 for i := 0 to h do
874 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
875 g_Obj_Collide(obj, @gCorpses[i].Obj) then
876 begin
877 // Ðàñïèëèâàåì òðóï:
878 gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
879 (obj^.Vel.Y+obj^.Accel.Y) div 4);
880 Result := 1;
881 end;
882 end;
884 case gGameSettings.GameMode of
885 // Êàìïàíèÿ:
886 GM_COOP, GM_SINGLE:
887 begin
888 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
889 if MonsterHit() then
890 begin
891 Result := 2;
892 Exit;
893 end;
895 // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
896 // (èëè ñíàðÿä îò ìîíñòðà, èëè friendlyfire, èëè friendly_hit_projectile)
897 if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or
898 LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then
899 begin
900 if PlayerHit() then
901 begin
902 Result := 1;
903 Exit;
904 end;
905 end;
906 end;
908 // Äåçìàò÷:
909 GM_DM:
910 begin
911 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
912 if PlayerHit() then
913 begin
914 Result := 1;
915 Exit;
916 end;
918 if MonsterHit() then
919 begin
920 Result := 2;
921 Exit;
922 end;
923 end;
925 // Êîìàíäíûå:
926 GM_TDM, GM_CTF:
927 begin
928 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
929 if PlayerHit(2) then
930 begin
931 Result := 1;
932 Exit;
933 end;
935 // Ïîòîì ìîíñòðîâ
936 if MonsterHit() then
937 begin
938 Result := 2;
939 Exit;
940 end;
942 // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
943 // (èëè friendlyfire, èëè friendly_hit_projectile)
944 if LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then
945 begin
946 if PlayerHit(1) then
947 begin
948 Result := 1;
949 Exit;
950 end;
951 end;
952 end;
954 end;
955 end;
957 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
958 begin
959 Result := False;
961 case g_GetUIDType(UID) of
962 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
963 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
964 else Exit;
965 end;
966 end;
968 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
969 var
970 r: Integer; // squared radius
972 function monsExCheck (mon: TMonster): Boolean;
973 var
974 dx, dy, mm: Integer;
975 begin
976 result := false; // don't stop
977 begin
978 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
979 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
981 if dx > 1000 then dx := 1000;
982 if dy > 1000 then dy := 1000;
984 if (dx*dx+dy*dy < r) then
985 begin
986 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
987 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
989 mm := Max(abs(dx), abs(dy));
990 if mm = 0 then mm := 1;
992 if mon.alive then
993 begin
994 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
995 end;
997 mon.Push((dx*7) div mm, (dy*7) div mm);
998 end;
999 end;
1000 end;
1002 var
1003 i, h, dx, dy, m, mm: Integer;
1004 _angle: SmallInt;
1005 begin
1006 result := false;
1008 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1010 r := rad*rad;
1012 h := High(gPlayers);
1014 if h <> -1 then
1015 for i := 0 to h do
1016 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1017 with gPlayers[i] do
1018 begin
1019 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1020 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1022 if dx > 1000 then dx := 1000;
1023 if dy > 1000 then dy := 1000;
1025 if dx*dx+dy*dy < r then
1026 begin
1027 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1028 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1030 mm := Max(abs(dx), abs(dy));
1031 if mm = 0 then mm := 1;
1033 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1034 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1035 end;
1036 end;
1038 //g_Mons_ForEach(monsExCheck);
1039 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1041 h := High(gCorpses);
1043 if gAdvCorpses and (h <> -1) then
1044 for i := 0 to h do
1045 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1046 with gCorpses[i] do
1047 begin
1048 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1049 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1051 if dx > 1000 then dx := 1000;
1052 if dy > 1000 then dy := 1000;
1054 if dx*dx+dy*dy < r then
1055 begin
1056 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1057 Obj.Rect.Width, Obj.Rect.Height);
1059 mm := Max(abs(dx), abs(dy));
1060 if mm = 0 then mm := 1;
1062 Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
1063 end;
1064 end;
1066 h := High(gGibs);
1068 if gAdvGibs and (h <> -1) then
1069 for i := 0 to h do
1070 if gGibs[i].alive then
1071 with gGibs[i] do
1072 begin
1073 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1074 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1076 if dx > 1000 then dx := 1000;
1077 if dy > 1000 then dy := 1000;
1079 if dx*dx+dy*dy < r then
1080 begin
1081 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1082 Obj.Rect.Width, Obj.Rect.Height);
1083 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1084 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1086 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1087 positionChanged(); // this updates spatial accelerators
1088 end;
1089 end;
1090 end;
1092 procedure g_Weapon_Init();
1093 begin
1094 CreateWaterMap();
1095 end;
1097 procedure g_Weapon_Free();
1098 var
1099 i: Integer;
1100 begin
1101 if Shots <> nil then
1102 begin
1103 for i := 0 to High(Shots) do
1104 if Shots[i].ShotType <> 0 then
1105 Shots[i].Animation.Free();
1107 Shots := nil;
1108 end;
1110 WaterMap := nil;
1111 end;
1113 procedure g_Weapon_LoadData();
1114 begin
1115 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1117 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1132 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1133 g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE');
1134 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1135 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1136 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1137 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1138 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1139 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1140 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1141 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1142 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1143 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM');
1144 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM');
1145 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM');
1146 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1147 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1148 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1149 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1150 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1151 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1152 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1154 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1155 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1156 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1157 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1158 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1159 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1160 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1161 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1162 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1163 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1164 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1165 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1166 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1167 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1168 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1169 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1170 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1171 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1172 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1173 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1174 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1176 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1177 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1179 //wgunMonHash := hashNewIntInt();
1180 wgunHitHeap := TBinaryHeapHitTimes.Create();
1181 end;
1183 procedure g_Weapon_FreeData();
1184 begin
1185 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1187 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1188 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1189 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1190 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1191 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1192 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1193 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1194 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1195 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1196 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1197 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1198 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1199 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1200 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1201 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1202 g_Sound_Delete('SOUND_FIRE');
1203 g_Sound_Delete('SOUND_IGNITE');
1204 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1205 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1206 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1207 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1208 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1209 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1210 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1211 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1212 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1213 g_Sound_Delete('SOUND_WEAPON_FLAMEON');
1214 g_Sound_Delete('SOUND_WEAPON_FLAMEOFF');
1215 g_Sound_Delete('SOUND_WEAPON_FLAMEWORK');
1216 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1217 g_Sound_Delete('SOUND_PLAYER_JETON');
1218 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1219 g_Sound_Delete('SOUND_PLAYER_CASING1');
1220 g_Sound_Delete('SOUND_PLAYER_CASING2');
1221 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1222 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1224 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1225 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1226 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1227 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1228 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1229 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1230 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1231 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1232 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1233 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1234 g_Frames_DeleteByName('FRAMES_BFGHIT');
1235 g_Frames_DeleteByName('FRAMES_FIRE');
1236 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1237 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1238 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1239 g_Frames_DeleteByName('FRAMES_SMOKE');
1240 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1241 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1242 end;
1245 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1246 var
1247 i: Integer;
1248 begin
1249 result := false;
1250 for i := 0 to High(gPlayers) do
1251 begin
1252 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1253 begin
1254 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1255 begin
1256 if AllowPush then gPlayers[i].Push(vx, vy);
1257 result := true;
1258 end;
1259 end;
1260 end;
1261 end;
1264 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1266 function monsCheck (mon: TMonster): Boolean;
1267 begin
1268 result := false; // don't stop
1269 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1270 begin
1271 if AllowPush then mon.Push(vx, vy);
1272 result := true;
1273 end;
1274 end;
1276 begin
1277 result := 0;
1278 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1279 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1280 end;
1283 (*
1284 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1285 var
1286 a: Integer;
1287 x2, y2: Integer;
1288 dx, dy: Integer;
1289 xe, ye: Integer;
1290 xi, yi: Integer;
1291 s, c: Extended;
1292 //vx, vy: Integer;
1293 xx, yy, d: Integer;
1294 i: Integer;
1295 t1, _collide: Boolean;
1296 w, h: Word;
1297 {$IF DEFINED(D2F_DEBUG)}
1298 stt: UInt64;
1299 showTime: Boolean = true;
1300 {$ENDIF}
1301 begin
1302 a := GetAngle(x, y, xd, yd)+180;
1304 SinCos(DegToRad(-a), s, c);
1306 if Abs(s) < 0.01 then s := 0;
1307 if Abs(c) < 0.01 then c := 0;
1309 x2 := x+Round(c*gMapInfo.Width);
1310 y2 := y+Round(s*gMapInfo.Width);
1312 t1 := gWalls <> nil;
1313 _collide := False;
1314 w := gMapInfo.Width;
1315 h := gMapInfo.Height;
1317 xe := 0;
1318 ye := 0;
1319 dx := x2-x;
1320 dy := y2-y;
1322 if (xd = 0) and (yd = 0) then Exit;
1324 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1325 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1327 dx := Abs(dx);
1328 dy := Abs(dy);
1330 if dx > dy then d := dx else d := dy;
1332 //blood vel, for Monster.Damage()
1333 //vx := (dx*10 div d)*xi;
1334 //vy := (dy*10 div d)*yi;
1336 {$IF DEFINED(D2F_DEBUG)}
1337 stt := getTimeMicro();
1338 {$ENDIF}
1340 xx := x;
1341 yy := y;
1343 for i := 1 to d do
1344 begin
1345 xe := xe+dx;
1346 ye := ye+dy;
1348 if xe > d then
1349 begin
1350 xe := xe-d;
1351 xx := xx+xi;
1352 end;
1354 if ye > d then
1355 begin
1356 ye := ye-d;
1357 yy := yy+yi;
1358 end;
1360 if (yy > h) or (yy < 0) then Break;
1361 if (xx > w) or (xx < 0) then Break;
1363 if t1 then
1364 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1365 begin
1366 _collide := True;
1367 {$IF DEFINED(D2F_DEBUG)}
1368 stt := getTimeMicro()-stt;
1369 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1370 showTime := false;
1371 {$ENDIF}
1372 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1373 if g_Game_IsServer and g_Game_IsNet then
1374 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1375 end;
1377 if not _collide then
1378 begin
1379 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1380 end;
1382 if _collide then Break;
1383 end;
1385 {$IF DEFINED(D2F_DEBUG)}
1386 if showTime then
1387 begin
1388 stt := getTimeMicro()-stt;
1389 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1390 end;
1391 {$ENDIF}
1393 if CheckTrigger and g_Game_IsServer then
1394 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1395 end;
1396 *)
1399 //!!!FIXME!!!
1400 procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1401 var
1402 x0, y0: Integer;
1403 x2, y2: Integer;
1404 xi, yi: Integer;
1405 wallDistSq: Integer = $3fffffff;
1406 spawnerPlr: TPlayer = nil;
1407 dmg: Integer;
1409 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1410 begin
1411 result := false;
1412 if (idx < 0) or (idx > High(gPlayers)) then exit;
1413 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1414 if (spawnerPlr <> nil) then
1415 begin
1416 if ((gGameSettings.Options and (GAME_OPTION_TEAMHITTRACE or GAME_OPTION_TEAMDAMAGE)) = 0) and
1417 (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then
1418 begin
1419 if (spawnerPlr <> gPlayers[idx]) and ((gGameSettings.Options and GAME_OPTION_TEAMABSORBDAMAGE) = 0) then
1420 dmg := Max(1, dmg div 2);
1421 exit;
1422 end;
1423 end;
1424 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1425 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1426 {$IF DEFINED(D2F_DEBUG)}
1427 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1428 {$ENDIF}
1429 end;
1431 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1432 begin
1433 result := false;
1434 if (mon = nil) then exit;
1435 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1436 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1437 {$IF DEFINED(D2F_DEBUG)}
1438 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1439 {$ENDIF}
1440 end;
1442 // collect players along hitray
1443 // return `true` if instant hit was detected
1444 function playerPossibleHit (): Boolean;
1445 var
1446 i: Integer;
1447 px, py, pw, ph: Integer;
1448 inx, iny: Integer;
1449 distSq: Integer;
1450 plr: TPlayer;
1451 begin
1452 result := false;
1453 for i := 0 to High(gPlayers) do
1454 begin
1455 plr := gPlayers[i];
1456 if (plr <> nil) and plr.alive then
1457 begin
1458 plr.getMapBox(px, py, pw, ph);
1459 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1460 begin
1461 distSq := distanceSq(x, y, inx, iny);
1462 if (distSq = 0) then
1463 begin
1464 // contains
1465 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1466 end
1467 else if (distSq < wallDistSq) then
1468 begin
1469 appendHitTimePlr(distSq, i, inx, iny);
1470 end;
1471 end;
1472 end;
1473 end;
1474 end;
1476 procedure sqchecker (mon: TMonster);
1477 var
1478 mx, my, mw, mh: Integer;
1479 inx, iny: Integer;
1480 distSq: Integer;
1481 begin
1482 mon.getMapBox(mx, my, mw, mh);
1483 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1484 begin
1485 distSq := distanceSq(x0, y0, inx, iny);
1486 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1487 end;
1488 end;
1490 var
1491 a: Integer;
1492 dx, dy: Integer;
1493 xe, ye: Integer;
1494 s, c: Extended;
1495 i: Integer;
1496 wallHitFlag: Boolean = false;
1497 wallHitX: Integer = 0;
1498 wallHitY: Integer = 0;
1499 didHit: Boolean = false;
1500 {$IF DEFINED(D2F_DEBUG)}
1501 stt: UInt64;
1502 {$ENDIF}
1503 mit: PMonster;
1504 it: TMonsterGrid.Iter;
1505 begin
1506 (*
1507 if not gwep_debug_fast_trace then
1508 begin
1509 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1510 exit;
1511 end;
1512 *)
1514 if (xd = 0) and (yd = 0) then exit;
1516 if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1517 spawnerPlr := g_Player_Get(SpawnerUID);
1519 dmg := indmg;
1521 //wgunMonHash.reset(); //FIXME: clear hash on level change
1522 wgunHitHeap.clear();
1523 wgunHitTimeUsed := 0;
1525 a := GetAngle(x, y, xd, yd)+180;
1527 SinCos(DegToRad(-a), s, c);
1529 if Abs(s) < 0.01 then s := 0;
1530 if Abs(c) < 0.01 then c := 0;
1532 x0 := x;
1533 y0 := y;
1534 x2 := x+Round(c*gMapInfo.Width);
1535 y2 := y+Round(s*gMapInfo.Width);
1537 dx := x2-x;
1538 dy := y2-y;
1540 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1541 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1543 {$IF DEFINED(D2F_DEBUG)}
1544 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1545 stt := getTimeMicro();
1546 {$ENDIF}
1548 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1549 if wallHitFlag then
1550 begin
1551 x2 := wallHitX;
1552 y2 := wallHitY;
1553 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1554 end
1555 else
1556 begin
1557 wallHitX := x2;
1558 wallHitY := y2;
1559 end;
1561 if playerPossibleHit() then exit; // instant hit
1563 // collect monsters
1564 //g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1566 it := monsGrid.forEachAlongLine(x, y, x2, y2, -1);
1567 for mit in it do sqchecker(mit^);
1568 it.release();
1570 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1571 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1572 while (wgunHitHeap.count > 0) do
1573 begin
1574 // has some entities to check, do it
1575 i := wgunHitHeap.front;
1576 wgunHitHeap.popFront();
1577 // hitpoint
1578 xe := wgunHitTime[i].x;
1579 ye := wgunHitTime[i].y;
1580 // check if it is not behind the wall
1581 if (wgunHitTime[i].mon <> nil) then
1582 begin
1583 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1584 end
1585 else
1586 begin
1587 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1588 end;
1589 if didHit then
1590 begin
1591 // need new coords for trigger
1592 wallHitX := xe;
1593 wallHitY := ye;
1594 wallHitFlag := false; // no sparks
1595 break;
1596 end;
1597 end;
1599 // need sparks?
1600 if wallHitFlag then
1601 begin
1602 {$IF DEFINED(D2F_DEBUG)}
1603 stt := getTimeMicro()-stt;
1604 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1605 {$ENDIF}
1606 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1607 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1608 end
1609 else
1610 begin
1611 {$IF DEFINED(D2F_DEBUG)}
1612 stt := getTimeMicro()-stt;
1613 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1614 {$ENDIF}
1615 end;
1617 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1618 end;
1621 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1622 var
1623 obj: TObj;
1624 begin
1625 obj.X := X;
1626 obj.Y := Y;
1627 obj.rect.X := 0;
1628 obj.rect.Y := 0;
1629 obj.rect.Width := 39;
1630 obj.rect.Height := 52;
1631 obj.Vel.X := 0;
1632 obj.Vel.Y := 0;
1633 obj.Accel.X := 0;
1634 obj.Accel.Y := 0;
1636 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1637 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1638 else
1639 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1640 end;
1642 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1643 var
1644 obj: TObj;
1645 begin
1646 obj.X := X;
1647 obj.Y := Y;
1648 obj.rect.X := 0;
1649 obj.rect.Y := 0;
1650 obj.rect.Width := 32;
1651 obj.rect.Height := 52;
1652 obj.Vel.X := 0;
1653 obj.Vel.Y := 0;
1654 obj.Accel.X := 0;
1655 obj.Accel.Y := 0;
1657 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1658 end;
1660 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1661 Silent: Boolean = False);
1662 var
1663 find_id: DWORD;
1664 dx, dy: Integer;
1665 begin
1666 if WID < 0 then
1667 find_id := FindShot()
1668 else
1669 begin
1670 find_id := WID;
1671 if Integer(find_id) >= High(Shots) then
1672 SetLength(Shots, find_id + 64)
1673 end;
1675 with Shots[find_id] do
1676 begin
1677 g_Obj_Init(@Obj);
1679 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1680 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1682 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1683 dy := -(Obj.Rect.Height div 2);
1685 ShotType := WEAPON_ROCKETLAUNCHER;
1686 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1688 Animation := nil;
1689 triggers := nil;
1690 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1691 end;
1693 Shots[find_id].SpawnerUID := SpawnerUID;
1695 if not Silent then
1696 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1697 end;
1699 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1700 WID: Integer = -1; Silent: Boolean = False);
1701 var
1702 find_id, FramesID: DWORD;
1703 dx, dy: Integer;
1704 begin
1705 if WID < 0 then
1706 find_id := FindShot()
1707 else
1708 begin
1709 find_id := WID;
1710 if Integer(find_id) >= High(Shots) then
1711 SetLength(Shots, find_id + 64)
1712 end;
1714 with Shots[find_id] do
1715 begin
1716 g_Obj_Init(@Obj);
1718 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1719 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1721 dx := -(Obj.Rect.Width div 2);
1722 dy := -(Obj.Rect.Height div 2);
1724 ShotType := WEAPON_SKEL_FIRE;
1725 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1727 triggers := nil;
1728 target := TargetUID;
1729 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1730 Animation := TAnimation.Create(FramesID, True, 5);
1731 end;
1733 Shots[find_id].SpawnerUID := SpawnerUID;
1735 if not Silent then
1736 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1737 end;
1739 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1740 Silent: Boolean = False);
1741 var
1742 find_id, FramesID: DWORD;
1743 dx, dy: Integer;
1744 begin
1745 if WID < 0 then
1746 find_id := FindShot()
1747 else
1748 begin
1749 find_id := WID;
1750 if Integer(find_id) >= High(Shots) then
1751 SetLength(Shots, find_id + 64);
1752 end;
1754 with Shots[find_id] do
1755 begin
1756 g_Obj_Init(@Obj);
1758 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1759 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1761 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1762 dy := -(Obj.Rect.Height div 2);
1764 ShotType := WEAPON_PLASMA;
1765 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1767 triggers := nil;
1768 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1769 Animation := TAnimation.Create(FramesID, True, 5);
1770 end;
1772 Shots[find_id].SpawnerUID := SpawnerUID;
1774 if not Silent then
1775 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1776 end;
1778 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1779 Silent: Boolean = False);
1780 var
1781 find_id: DWORD;
1782 dx, dy: Integer;
1783 begin
1784 if WID < 0 then
1785 find_id := FindShot()
1786 else
1787 begin
1788 find_id := WID;
1789 if Integer(find_id) >= High(Shots) then
1790 SetLength(Shots, find_id + 64);
1791 end;
1793 with Shots[find_id] do
1794 begin
1795 g_Obj_Init(@Obj);
1797 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1798 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1800 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1801 dy := -(Obj.Rect.Height div 2);
1803 ShotType := WEAPON_FLAMETHROWER;
1804 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1806 triggers := nil;
1807 Animation := nil;
1808 TextureID := 0;
1809 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1810 end;
1812 Shots[find_id].SpawnerUID := SpawnerUID;
1814 // if not Silent then
1815 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1816 end;
1818 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1819 Silent: Boolean = False);
1820 var
1821 find_id, FramesID: DWORD;
1822 dx, dy: Integer;
1823 begin
1824 if WID < 0 then
1825 find_id := FindShot()
1826 else
1827 begin
1828 find_id := WID;
1829 if Integer(find_id) >= High(Shots) then
1830 SetLength(Shots, find_id + 64)
1831 end;
1833 with Shots[find_id] do
1834 begin
1835 g_Obj_Init(@Obj);
1837 Obj.Rect.Width := 16;
1838 Obj.Rect.Height := 16;
1840 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1841 dy := -(Obj.Rect.Height div 2);
1843 ShotType := WEAPON_IMP_FIRE;
1844 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1846 triggers := nil;
1847 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1848 Animation := TAnimation.Create(FramesID, True, 4);
1849 end;
1851 Shots[find_id].SpawnerUID := SpawnerUID;
1853 if not Silent then
1854 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1855 end;
1857 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1858 Silent: Boolean = False);
1859 var
1860 find_id, FramesID: DWORD;
1861 dx, dy: Integer;
1862 begin
1863 if WID < 0 then
1864 find_id := FindShot()
1865 else
1866 begin
1867 find_id := WID;
1868 if Integer(find_id) >= High(Shots) then
1869 SetLength(Shots, find_id + 64)
1870 end;
1872 with Shots[find_id] do
1873 begin
1874 g_Obj_Init(@Obj);
1876 Obj.Rect.Width := 16;
1877 Obj.Rect.Height := 16;
1879 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1880 dy := -(Obj.Rect.Height div 2);
1882 ShotType := WEAPON_CACO_FIRE;
1883 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1885 triggers := nil;
1886 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1887 Animation := TAnimation.Create(FramesID, True, 4);
1888 end;
1890 Shots[find_id].SpawnerUID := SpawnerUID;
1892 if not Silent then
1893 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1894 end;
1896 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1897 Silent: Boolean = False);
1898 var
1899 find_id, FramesID: DWORD;
1900 dx, dy: Integer;
1901 begin
1902 if WID < 0 then
1903 find_id := FindShot()
1904 else
1905 begin
1906 find_id := WID;
1907 if Integer(find_id) >= High(Shots) then
1908 SetLength(Shots, find_id + 64)
1909 end;
1911 with Shots[find_id] do
1912 begin
1913 g_Obj_Init(@Obj);
1915 Obj.Rect.Width := 32;
1916 Obj.Rect.Height := 16;
1918 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1919 dy := -(Obj.Rect.Height div 2);
1921 ShotType := WEAPON_BARON_FIRE;
1922 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1924 triggers := nil;
1925 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1926 Animation := TAnimation.Create(FramesID, True, 4);
1927 end;
1929 Shots[find_id].SpawnerUID := SpawnerUID;
1931 if not Silent then
1932 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1933 end;
1935 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1936 Silent: Boolean = False);
1937 var
1938 find_id, FramesID: DWORD;
1939 dx, dy: Integer;
1940 begin
1941 if WID < 0 then
1942 find_id := FindShot()
1943 else
1944 begin
1945 find_id := WID;
1946 if Integer(find_id) >= High(Shots) then
1947 SetLength(Shots, find_id + 64)
1948 end;
1950 with Shots[find_id] do
1951 begin
1952 g_Obj_Init(@Obj);
1954 Obj.Rect.Width := 16;
1955 Obj.Rect.Height := 16;
1957 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1958 dy := -(Obj.Rect.Height div 2);
1960 ShotType := WEAPON_BSP_FIRE;
1961 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1963 triggers := nil;
1965 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1966 Animation := TAnimation.Create(FramesID, True, 4);
1967 end;
1969 Shots[find_id].SpawnerUID := SpawnerUID;
1971 if not Silent then
1972 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1973 end;
1975 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1976 Silent: Boolean = False);
1977 var
1978 find_id, FramesID: DWORD;
1979 dx, dy: Integer;
1980 begin
1981 if WID < 0 then
1982 find_id := FindShot()
1983 else
1984 begin
1985 find_id := WID;
1986 if Integer(find_id) >= High(Shots) then
1987 SetLength(Shots, find_id + 64)
1988 end;
1990 with Shots[find_id] do
1991 begin
1992 g_Obj_Init(@Obj);
1994 Obj.Rect.Width := 32;
1995 Obj.Rect.Height := 32;
1997 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1998 dy := -(Obj.Rect.Height div 2);
2000 ShotType := WEAPON_MANCUB_FIRE;
2001 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2003 triggers := nil;
2005 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
2006 Animation := TAnimation.Create(FramesID, True, 4);
2007 end;
2009 Shots[find_id].SpawnerUID := SpawnerUID;
2011 if not Silent then
2012 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
2013 end;
2015 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
2016 Silent: Boolean = False);
2017 var
2018 find_id, FramesID: DWORD;
2019 dx, dy: Integer;
2020 begin
2021 if WID < 0 then
2022 find_id := FindShot()
2023 else
2024 begin
2025 find_id := WID;
2026 if Integer(find_id) >= High(Shots) then
2027 SetLength(Shots, find_id + 64)
2028 end;
2030 with Shots[find_id] do
2031 begin
2032 g_Obj_Init(@Obj);
2034 Obj.Rect.Width := SHOT_BFG_WIDTH;
2035 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2037 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2038 dy := -(Obj.Rect.Height div 2);
2040 ShotType := WEAPON_BFG;
2041 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2043 triggers := nil;
2044 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2045 Animation := TAnimation.Create(FramesID, True, 6);
2046 end;
2048 Shots[find_id].SpawnerUID := SpawnerUID;
2050 if not Silent then
2051 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2052 end;
2054 procedure g_Weapon_bfghit(x, y: Integer);
2055 var
2056 ID: DWORD;
2057 Anim: TAnimation;
2058 begin
2059 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2060 begin
2061 Anim := TAnimation.Create(ID, False, 4);
2062 g_GFX_OnceAnim(x-32, y-32, Anim);
2063 Anim.Free();
2064 end;
2065 end;
2067 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2068 Silent: Boolean = False);
2069 begin
2070 if not Silent then
2071 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2073 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2074 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2075 begin
2076 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2077 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2078 end;
2079 end;
2081 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2082 Silent: Boolean = False);
2083 begin
2084 if not Silent then
2085 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2087 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2088 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2089 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2090 begin
2091 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2092 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2093 end;
2094 end;
2096 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2097 Silent: Boolean = False);
2098 var
2099 i, j: Integer;
2100 begin
2101 if not Silent then
2102 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2104 for i := 0 to 9 do
2105 begin
2106 j := Random(17)-8; // -8 .. 8
2107 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2108 end;
2109 end;
2111 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2112 Silent: Boolean = False);
2113 var
2114 a, i, j: Integer;
2115 begin
2116 if not Silent then
2117 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2119 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2120 for i := 0 to a do
2121 begin
2122 j := Random(41)-20; // -20 .. 20
2123 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2124 end;
2125 end;
2127 procedure g_Weapon_PreUpdate();
2128 var
2129 i: Integer;
2130 begin
2131 if Shots = nil then Exit;
2132 for i := 0 to High(Shots) do
2133 if Shots[i].ShotType <> 0 then
2134 begin
2135 Shots[i].Obj.oldX := Shots[i].Obj.X;
2136 Shots[i].Obj.oldY := Shots[i].Obj.Y;
2137 end;
2138 end;
2140 procedure g_Weapon_Update();
2141 var
2142 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2143 _id: DWORD;
2144 Anim: TAnimation;
2145 t: DWArray;
2146 st: Word;
2147 s: String;
2148 o: TObj;
2149 spl: Boolean;
2150 Loud: Boolean;
2151 tcx, tcy: Integer;
2152 begin
2153 if Shots = nil then
2154 Exit;
2156 for i := 0 to High(Shots) do
2157 begin
2158 if Shots[i].ShotType = 0 then
2159 Continue;
2161 Loud := True;
2163 with Shots[i] do
2164 begin
2165 Timeout := Timeout - 1;
2166 oldvx := Obj.Vel.X;
2167 oldvy := Obj.Vel.Y;
2168 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2169 if (Stopped = 0) and g_Game_IsServer then
2170 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2171 SpawnerUID, ACTIVATE_SHOT, triggers)
2172 else
2173 t := nil;
2175 if t <> nil then
2176 begin
2177 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2178 if triggers = nil then
2179 triggers := t
2180 else
2181 begin
2182 h := High(t);
2184 for a := 0 to h do
2185 if not InDWArray(t[a], triggers) then
2186 begin
2187 SetLength(triggers, Length(triggers)+1);
2188 triggers[High(triggers)] := t[a];
2189 end;
2190 end;
2191 end;
2193 // Àíèìàöèÿ ñíàðÿäà:
2194 if Animation <> nil then
2195 Animation.Update();
2197 // Äâèæåíèå:
2198 spl := (ShotType <> WEAPON_PLASMA) and
2199 (ShotType <> WEAPON_BFG) and
2200 (ShotType <> WEAPON_BSP_FIRE) and
2201 (ShotType <> WEAPON_FLAMETHROWER);
2203 if Stopped = 0 then
2204 begin
2205 st := g_Obj_Move_Projectile(@Obj, False, spl);
2206 end
2207 else
2208 begin
2209 st := 0;
2210 end;
2211 positionChanged(); // this updates spatial accelerators
2213 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2214 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2215 begin
2216 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2217 ShotType := 0;
2218 Animation.Free();
2219 Continue;
2220 end;
2222 cx := Obj.X + (Obj.Rect.Width div 2);
2223 cy := Obj.Y + (Obj.Rect.Height div 2);
2225 case ShotType of
2226 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2227 begin
2228 // Âûëåòåëà èç âîäû:
2229 if WordBool(st and MOVE_HITAIR) then
2230 g_Obj_SetSpeed(@Obj, 12);
2232 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2233 if WordBool(st and MOVE_INWATER) then
2234 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2235 Obj.Y+(Obj.Rect.Height div 2),
2236 1+Random(3), 16, 16)
2237 else
2238 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2239 begin
2240 Anim := TAnimation.Create(_id, False, 3);
2241 Anim.Alpha := 150;
2242 g_GFX_OnceAnim(Obj.X-14+Random(9),
2243 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2244 Anim, ONCEANIM_SMOKE);
2245 Anim.Free();
2246 end;
2248 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2249 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2250 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2251 (Timeout < 1) then
2252 begin
2253 Obj.Vel.X := 0;
2254 Obj.Vel.Y := 0;
2256 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2258 if ShotType = WEAPON_SKEL_FIRE then
2259 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2260 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2261 begin
2262 Anim := TAnimation.Create(TextureID, False, 8);
2263 Anim.Blending := False;
2264 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2265 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2266 Anim.Free();
2267 end;
2268 end
2269 else
2270 begin // Âçðûâ Ðàêåòû
2271 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2272 begin
2273 Anim := TAnimation.Create(TextureID, False, 6);
2274 Anim.Blending := False;
2275 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2276 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2277 Anim.Free();
2278 end;
2279 end;
2281 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2283 ShotType := 0;
2284 end;
2286 if ShotType = WEAPON_SKEL_FIRE then
2287 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2288 if GetPos(target, @o) then
2289 throw(i, Obj.X, Obj.Y,
2290 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2291 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2292 12);
2293 end;
2294 end;
2296 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2297 begin
2298 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2299 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2300 begin
2301 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2302 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2303 ShotType := 0;
2304 Continue;
2305 end;
2307 // Âåëè÷èíà óðîíà:
2308 if (ShotType = WEAPON_PLASMA) and
2309 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2310 a := 10
2311 else
2312 a := 5;
2314 if ShotType = WEAPON_BSP_FIRE then
2315 a := 10;
2317 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2318 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2319 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2320 (Timeout < 1) then
2321 begin
2322 if ShotType = WEAPON_PLASMA then
2323 s := 'FRAMES_EXPLODE_PLASMA'
2324 else
2325 s := 'FRAMES_EXPLODE_BSPFIRE';
2327 // Âçðûâ Ïëàçìû:
2328 if g_Frames_Get(TextureID, s) then
2329 begin
2330 Anim := TAnimation.Create(TextureID, False, 3);
2331 Anim.Blending := False;
2332 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2333 Anim.Free();
2334 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2335 end;
2337 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2339 ShotType := 0;
2340 end;
2341 end;
2343 WEAPON_FLAMETHROWER: // Îãíåìåò
2344 begin
2345 // Ñî âðåìåíåì óìèðàåò
2346 if (Timeout < 1) then
2347 begin
2348 ShotType := 0;
2349 Continue;
2350 end;
2351 // Ïîä âîäîé òîæå
2352 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2353 begin
2354 if WordBool(st and MOVE_HITWATER) then
2355 begin
2356 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2357 begin
2358 Anim := TAnimation.Create(_id, False, 3);
2359 Anim.Alpha := 0;
2360 tcx := Random(8);
2361 tcy := Random(8);
2362 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2363 cy-4+tcy-(Anim.Height div 2),
2364 Anim, ONCEANIM_SMOKE);
2365 Anim.Free();
2366 end;
2367 end
2368 else
2369 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2370 ShotType := 0;
2371 Continue;
2372 end;
2374 // Ãðàâèòàöèÿ
2375 if Stopped = 0 then
2376 Obj.Accel.Y := Obj.Accel.Y + 1;
2377 // Ïîïàëè â ñòåíó èëè â âîäó:
2378 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2379 begin
2380 // Ïðèëèïàåì:
2381 Obj.Vel.X := 0;
2382 Obj.Vel.Y := 0;
2383 Obj.Accel.Y := 0;
2384 if WordBool(st and MOVE_HITWALL) then
2385 Stopped := MOVE_HITWALL
2386 else if WordBool(st and MOVE_HITLAND) then
2387 Stopped := MOVE_HITLAND
2388 else if WordBool(st and MOVE_HITCEIL) then
2389 Stopped := MOVE_HITCEIL;
2390 end;
2392 a := IfThen(Stopped = 0, 10, 1);
2393 // Åñëè â êîãî-òî ïîïàëè
2394 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2395 begin
2396 // HIT_FLAME ñàì ïîäîææåò
2397 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2398 if Stopped = 0 then
2399 ShotType := 0;
2400 end;
2402 if Stopped = 0 then
2403 tf := 2
2404 else
2405 tf := 3;
2407 if (gTime mod LongWord(tf) = 0) then
2408 begin
2409 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2410 Anim.Alpha := 0;
2411 case Stopped of
2412 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2413 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2414 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2415 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2416 end;
2417 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2418 Anim.Free();
2419 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2420 end;
2421 end;
2423 WEAPON_BFG: // BFG
2424 begin
2425 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2426 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2427 begin
2428 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2429 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2430 ShotType := 0;
2431 Continue;
2432 end;
2434 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2435 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2436 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2437 (Timeout < 1) then
2438 begin
2439 // Ëó÷è BFG:
2440 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2442 // Âçðûâ BFG:
2443 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2444 begin
2445 Anim := TAnimation.Create(TextureID, False, 6);
2446 Anim.Blending := False;
2447 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2448 Anim.Free();
2449 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2450 end;
2452 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2454 ShotType := 0;
2455 end;
2456 end;
2458 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2459 begin
2460 // Âûëåòåë èç âîäû:
2461 if WordBool(st and MOVE_HITAIR) then
2462 g_Obj_SetSpeed(@Obj, 16);
2464 // Âåëè÷èíà óðîíà:
2465 if ShotType = WEAPON_IMP_FIRE then
2466 a := 5
2467 else
2468 if ShotType = WEAPON_CACO_FIRE then
2469 a := 20
2470 else
2471 a := 40;
2473 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2474 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2475 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2476 (Timeout < 1) then
2477 begin
2478 if ShotType = WEAPON_IMP_FIRE then
2479 s := 'FRAMES_EXPLODE_IMPFIRE'
2480 else
2481 if ShotType = WEAPON_CACO_FIRE then
2482 s := 'FRAMES_EXPLODE_CACOFIRE'
2483 else
2484 s := 'FRAMES_EXPLODE_BARONFIRE';
2486 // Âçðûâ:
2487 if g_Frames_Get(TextureID, s) then
2488 begin
2489 Anim := TAnimation.Create(TextureID, False, 6);
2490 Anim.Blending := False;
2491 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2492 Anim.Free();
2493 end;
2495 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2497 ShotType := 0;
2498 end;
2499 end;
2501 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2502 begin
2503 // Âûëåòåë èç âîäû:
2504 if WordBool(st and MOVE_HITAIR) then
2505 g_Obj_SetSpeed(@Obj, 16);
2507 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2508 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2509 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2510 (Timeout < 1) then
2511 begin
2512 // Âçðûâ:
2513 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2514 begin
2515 Anim := TAnimation.Create(TextureID, False, 6);
2516 Anim.Blending := False;
2517 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2518 Anim.Free();
2519 end;
2521 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2523 ShotType := 0;
2524 end;
2525 end;
2526 end; // case ShotType of...
2528 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2529 if (ShotType = 0) then
2530 begin
2531 if gGameSettings.GameType = GT_SERVER then
2532 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2533 if Animation <> nil then
2534 begin
2535 Animation.Free();
2536 Animation := nil;
2537 end;
2538 end
2539 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2540 if gGameSettings.GameType = GT_SERVER then
2541 MH_SEND_UpdateShot(i);
2542 end;
2543 end;
2544 end;
2546 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2547 var
2548 a: Integer;
2549 begin
2550 Result := False;
2552 if Shots = nil then
2553 Exit;
2555 for a := 0 to High(Shots) do
2556 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2557 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2558 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2559 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2560 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2561 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2562 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2563 begin
2564 Result := True;
2565 Exit;
2566 end;
2567 end;
2569 procedure g_Weapon_SaveState (st: TStream);
2570 var
2571 count, i, j: Integer;
2572 begin
2573 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2574 count := 0;
2575 for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count);
2577 // Êîëè÷åñòâî ñíàðÿäîâ
2578 utils.WriteInt(st, count);
2580 if (count = 0) then exit;
2582 for i := 0 to High(Shots) do
2583 begin
2584 if Shots[i].ShotType <> 0 then
2585 begin
2586 // Ñèãíàòóðà ñíàðÿäà
2587 utils.writeSign(st, 'SHOT');
2588 utils.writeInt(st, Byte(0)); // version
2589 // Òèï ñíàðÿäà
2590 utils.writeInt(st, Byte(Shots[i].ShotType));
2591 // Öåëü
2592 utils.writeInt(st, Word(Shots[i].Target));
2593 // UID ñòðåëÿâøåãî
2594 utils.writeInt(st, Word(Shots[i].SpawnerUID));
2595 // Ðàçìåð ïîëÿ Triggers
2596 utils.writeInt(st, Integer(Length(Shots[i].Triggers)));
2597 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2598 for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j]));
2599 // Îáúåêò ñíàðÿäà
2600 Obj_SaveState(st, @Shots[i].Obj);
2601 // Êîñòûëèíà åáàíàÿ
2602 utils.writeInt(st, Byte(Shots[i].Stopped));
2603 end;
2604 end;
2605 end;
2607 procedure g_Weapon_LoadState (st: TStream);
2608 var
2609 count, tc, i, j: Integer;
2610 dw: LongWord;
2611 begin
2612 if (st = nil) then exit;
2614 // Êîëè÷åñòâî ñíàðÿäîâ
2615 count := utils.readLongInt(st);
2616 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter');
2618 SetLength(Shots, count);
2620 if (count = 0) then exit;
2622 for i := 0 to count-1 do
2623 begin
2624 // Ñèãíàòóðà ñíàðÿäà
2625 if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature');
2626 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version');
2627 // Òèï ñíàðÿäà:
2628 Shots[i].ShotType := utils.readByte(st);
2629 // Öåëü
2630 Shots[i].Target := utils.readWord(st);
2631 // UID ñòðåëÿâøåãî
2632 Shots[i].SpawnerUID := utils.readWord(st);
2633 // Ðàçìåð ïîëÿ Triggers
2634 tc := utils.readLongInt(st);
2635 if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter');
2636 SetLength(Shots[i].Triggers, tc);
2637 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2638 for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st);
2639 // Îáúåêò ïðåäìåòà
2640 Obj_LoadState(@Shots[i].Obj, st);
2641 // Êîñòûëèíà åáàíàÿ
2642 Shots[i].Stopped := utils.readByte(st);
2644 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2645 Shots[i].TextureID := DWORD(-1);
2646 Shots[i].Animation := nil;
2648 case Shots[i].ShotType of
2649 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2650 begin
2651 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2652 end;
2653 WEAPON_PLASMA:
2654 begin
2655 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2656 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2657 end;
2658 WEAPON_BFG:
2659 begin
2660 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2661 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2662 end;
2663 WEAPON_IMP_FIRE:
2664 begin
2665 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2666 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2667 end;
2668 WEAPON_BSP_FIRE:
2669 begin
2670 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2671 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2672 end;
2673 WEAPON_CACO_FIRE:
2674 begin
2675 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2676 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2677 end;
2678 WEAPON_BARON_FIRE:
2679 begin
2680 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2681 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2682 end;
2683 WEAPON_MANCUB_FIRE:
2684 begin
2685 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2686 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2687 end;
2688 end;
2689 end;
2690 end;
2692 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2693 var
2694 cx, cy: Integer;
2695 Anim: TAnimation;
2696 s: string;
2697 begin
2698 if Shots = nil then
2699 Exit;
2700 if (I > High(Shots)) or (I < 0) then Exit;
2702 with Shots[I] do
2703 begin
2704 if ShotType = 0 then Exit;
2705 Obj.X := X;
2706 Obj.Y := Y;
2707 cx := Obj.X + (Obj.Rect.Width div 2);
2708 cy := Obj.Y + (Obj.Rect.Height div 2);
2710 case ShotType of
2711 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2712 begin
2713 if Loud then
2714 begin
2715 if ShotType = WEAPON_SKEL_FIRE then
2716 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2717 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2718 begin
2719 Anim := TAnimation.Create(TextureID, False, 8);
2720 Anim.Blending := False;
2721 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2722 Anim.Free();
2723 end;
2724 end
2725 else
2726 begin // Âçðûâ Ðàêåòû
2727 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2728 begin
2729 Anim := TAnimation.Create(TextureID, False, 6);
2730 Anim.Blending := False;
2731 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2732 Anim.Free();
2733 end;
2734 end;
2735 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2736 end;
2737 end;
2739 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2740 begin
2741 if ShotType = WEAPON_PLASMA then
2742 s := 'FRAMES_EXPLODE_PLASMA'
2743 else
2744 s := 'FRAMES_EXPLODE_BSPFIRE';
2746 if g_Frames_Get(TextureID, s) and loud then
2747 begin
2748 Anim := TAnimation.Create(TextureID, False, 3);
2749 Anim.Blending := False;
2750 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2751 Anim.Free();
2753 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2754 end;
2755 end;
2757 WEAPON_BFG: // BFG
2758 begin
2759 // Âçðûâ BFG:
2760 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2761 begin
2762 Anim := TAnimation.Create(TextureID, False, 6);
2763 Anim.Blending := False;
2764 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2765 Anim.Free();
2767 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2768 end;
2769 end;
2771 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2772 begin
2773 if ShotType = WEAPON_IMP_FIRE then
2774 s := 'FRAMES_EXPLODE_IMPFIRE'
2775 else
2776 if ShotType = WEAPON_CACO_FIRE then
2777 s := 'FRAMES_EXPLODE_CACOFIRE'
2778 else
2779 s := 'FRAMES_EXPLODE_BARONFIRE';
2781 if g_Frames_Get(TextureID, s) and Loud then
2782 begin
2783 Anim := TAnimation.Create(TextureID, False, 6);
2784 Anim.Blending := False;
2785 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2786 Anim.Free();
2788 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2789 end;
2790 end;
2792 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2793 begin
2794 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2795 begin
2796 Anim := TAnimation.Create(TextureID, False, 6);
2797 Anim.Blending := False;
2798 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2799 Anim.Free();
2801 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2802 end;
2803 end;
2804 end; // case ShotType of...
2806 ShotType := 0;
2807 Animation.Free();
2808 end;
2809 end;
2812 procedure g_Weapon_AddDynLights();
2813 var
2814 i: Integer;
2815 begin
2816 if Shots = nil then Exit;
2817 for i := 0 to High(Shots) do
2818 begin
2819 if Shots[i].ShotType = 0 then continue;
2820 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2821 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2822 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2823 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2824 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2825 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2826 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2827 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2828 (Shots[i].ShotType = WEAPON_PLASMA) or
2829 (Shots[i].ShotType = WEAPON_BFG) or
2830 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2831 false then
2832 begin
2833 if (Shots[i].ShotType = WEAPON_PLASMA) 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), 128, 0, 0.3, 1, 0.4)
2835 else if (Shots[i].ShotType = WEAPON_BFG) then
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, 0, 1, 0, 0.5)
2837 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2838 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)
2839 else
2840 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);
2841 end;
2842 end;
2843 end;
2846 procedure TShot.positionChanged (); begin end;
2849 end.