DEADSOFTWARE

removed all mentions of dynaabb tree from the sources; WARNING! EVERYTHING IS BROKEN!
[d2df-sdl.git] / src / game / g_weapons.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
18 unit g_weapons;
20 interface
22 uses
23 g_textures, g_basic, e_graphics, g_phys, BinEditor, xprofiler;
25 const
26 HIT_SOME = 0;
27 HIT_ROCKET = 1;
28 HIT_BFG = 2;
29 HIT_TRAP = 3;
30 HIT_FALL = 4;
31 HIT_WATER = 5;
32 HIT_ACID = 6;
33 HIT_ELECTRO = 7;
34 HIT_FLAME = 8;
35 HIT_SELF = 9;
36 HIT_DISCON = 10;
38 type
39 TShot = record
40 ShotType: Byte;
41 Target: Word;
42 SpawnerUID: Word;
43 Triggers: DWArray;
44 Obj: TObj;
45 Animation: TAnimation;
46 TextureID: DWORD;
47 Timeout: DWORD;
48 Stopped: Byte;
50 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
51 end;
54 var
55 Shots: array of TShot = nil;
56 LastShotID: Integer = 0;
58 procedure g_Weapon_LoadData();
59 procedure g_Weapon_FreeData();
60 procedure g_Weapon_Init();
61 procedure g_Weapon_Free();
62 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
63 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
64 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
66 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
67 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
68 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
69 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
70 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
71 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
74 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
75 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
76 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
77 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
78 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
79 procedure g_Weapon_bfghit(x, y: Integer);
80 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
81 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
82 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
83 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
85 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
86 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
87 procedure g_Weapon_Update();
88 procedure g_Weapon_Draw();
89 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
90 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
92 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
93 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
95 procedure g_Weapon_AddDynLights();
97 const
98 WEAPON_KASTET = 0;
99 WEAPON_SAW = 1;
100 WEAPON_PISTOL = 2;
101 WEAPON_SHOTGUN1 = 3;
102 WEAPON_SHOTGUN2 = 4;
103 WEAPON_CHAINGUN = 5;
104 WEAPON_ROCKETLAUNCHER = 6;
105 WEAPON_PLASMA = 7;
106 WEAPON_BFG = 8;
107 WEAPON_SUPERPULEMET = 9;
108 WEAPON_FLAMETHROWER = 10;
109 WEAPON_ZOMBY_PISTOL = 20;
110 WEAPON_IMP_FIRE = 21;
111 WEAPON_BSP_FIRE = 22;
112 WEAPON_CACO_FIRE = 23;
113 WEAPON_BARON_FIRE = 24;
114 WEAPON_MANCUB_FIRE = 25;
115 WEAPON_SKEL_FIRE = 26;
117 WP_FIRST = WEAPON_KASTET;
118 WP_LAST = WEAPON_FLAMETHROWER;
121 var
122 gwep_debug_fast_trace: Boolean = true;
125 implementation
127 uses
128 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
129 g_console, SysUtils, g_options, g_game,
130 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
131 g_language, g_netmsg,
132 binheap, hashtable;
134 type
135 TWaterPanel = record
136 X, Y: Integer;
137 Width, Height: Word;
138 Active: Boolean;
139 end;
141 const
142 SHOT_ROCKETLAUNCHER_WIDTH = 14;
143 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
145 SHOT_SKELFIRE_WIDTH = 14;
146 SHOT_SKELFIRE_HEIGHT = 14;
148 SHOT_PLASMA_WIDTH = 16;
149 SHOT_PLASMA_HEIGHT = 16;
151 SHOT_BFG_WIDTH = 32;
152 SHOT_BFG_HEIGHT = 32;
153 SHOT_BFG_DAMAGE = 100;
154 SHOT_BFG_RADIUS = 256;
156 SHOT_FLAME_WIDTH = 4;
157 SHOT_FLAME_HEIGHT = 4;
158 SHOT_FLAME_LIFETIME = 180;
160 SHOT_SIGNATURE = $544F4853; // 'SHOT'
162 type
163 PHitTime = ^THitTime;
164 THitTime = record
165 time: Single;
166 mon: TMonster;
167 plridx: Integer; // if mon=nil
168 end;
170 // indicies in `wgunHitTime` array
171 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer>;
173 var
174 WaterMap: array of array of DWORD = nil;
175 wgunMonHash: THashIntInt = nil;
176 wgunHitHeap: TBinaryHeapHitTimes = nil;
177 wgunHitTime: array of THitTime = nil;
178 wgunHitTimeUsed: Integer = 0;
181 function hitTimeCompare (a, b: Integer): Boolean;
182 begin
183 if (wgunHitTime[a].time < wgunHitTime[b].time) then begin result := true; exit; end;
184 if (wgunHitTime[a].time > wgunHitTime[b].time) then begin result := false; exit; end;
185 if (wgunHitTime[a].mon <> nil) then
186 begin
187 // a is monster
188 if (wgunHitTime[b].mon = nil) then begin result := false; exit; end; // players first
189 result := (wgunHitTime[a].mon.UID < wgunHitTime[b].mon.UID); // why not?
190 end
191 else
192 begin
193 // a is player
194 if (wgunHitTime[b].mon <> nil) then begin result := true; exit; end; // players first
195 result := (wgunHitTime[a].plridx < wgunHitTime[b].plridx); // why not?
196 end;
197 end;
200 procedure appendHitTimeMon (time: Single; mon: TMonster);
201 begin
202 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
203 wgunHitTime[wgunHitTimeUsed].time := time;
204 wgunHitTime[wgunHitTimeUsed].mon := mon;
205 wgunHitTime[wgunHitTimeUsed].plridx := -1;
206 wgunHitHeap.insert(wgunHitTimeUsed);
207 Inc(wgunHitTimeUsed);
208 end;
211 procedure appendHitTimePlr (time: Single; plridx: Integer);
212 begin
213 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
214 wgunHitTime[wgunHitTimeUsed].time := time;
215 wgunHitTime[wgunHitTimeUsed].mon := nil;
216 wgunHitTime[wgunHitTimeUsed].plridx := plridx;
217 wgunHitHeap.insert(wgunHitTimeUsed);
218 Inc(wgunHitTimeUsed);
219 end;
222 function FindShot(): DWORD;
223 var
224 i: Integer;
225 begin
226 if Shots <> nil then
227 for i := 0 to High(Shots) do
228 if Shots[i].ShotType = 0 then
229 begin
230 Result := i;
231 LastShotID := Result;
232 Exit;
233 end;
235 if Shots = nil then
236 begin
237 SetLength(Shots, 128);
238 Result := 0;
239 end
240 else
241 begin
242 Result := High(Shots) + 1;
243 SetLength(Shots, Length(Shots) + 128);
244 end;
245 LastShotID := Result;
246 end;
248 procedure CreateWaterMap();
249 var
250 WaterArray: Array of TWaterPanel;
251 a, b, c, m: Integer;
252 ok: Boolean;
253 begin
254 if gWater = nil then
255 Exit;
257 SetLength(WaterArray, Length(gWater));
259 for a := 0 to High(gWater) do
260 begin
261 WaterArray[a].X := gWater[a].X;
262 WaterArray[a].Y := gWater[a].Y;
263 WaterArray[a].Width := gWater[a].Width;
264 WaterArray[a].Height := gWater[a].Height;
265 WaterArray[a].Active := True;
266 end;
268 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
270 for a := 0 to High(WaterArray) do
271 if WaterArray[a].Active then
272 begin
273 WaterArray[a].Active := False;
274 m := Length(WaterMap);
275 SetLength(WaterMap, m+1);
276 SetLength(WaterMap[m], 1);
277 WaterMap[m][0] := a;
278 ok := True;
280 while ok do
281 begin
282 ok := False;
283 for b := 0 to High(WaterArray) do
284 if WaterArray[b].Active then
285 for c := 0 to High(WaterMap[m]) do
286 if g_CollideAround(WaterArray[b].X,
287 WaterArray[b].Y,
288 WaterArray[b].Width,
289 WaterArray[b].Height,
290 WaterArray[WaterMap[m][c]].X,
291 WaterArray[WaterMap[m][c]].Y,
292 WaterArray[WaterMap[m][c]].Width,
293 WaterArray[WaterMap[m][c]].Height) then
294 begin
295 WaterArray[b].Active := False;
296 SetLength(WaterMap[m],
297 Length(WaterMap[m])+1);
298 WaterMap[m][High(WaterMap[m])] := b;
299 ok := True;
300 Break;
301 end;
302 end;
304 g_Game_StepLoading();
305 end;
307 WaterArray := nil;
308 end;
311 var
312 chkTrap_pl: array [0..256] of Integer;
313 chkTrap_mn: array [0..65535] of TMonster;
315 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
316 var
317 //a, b, c, d, i1, i2: Integer;
318 //chkTrap_pl, chkTrap_mn: WArray;
319 plaCount: Integer = 0;
320 mnaCount: Integer = 0;
321 frameId: DWord;
324 function monsWaterCheck (mon: TMonster): Boolean;
325 begin
326 result := false; // don't stop
327 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
328 begin
329 i2 += 1;
330 chkTrap_mn[i2] := monidx;
331 end;
332 end;
335 function monsWaterCheck (mon: TMonster): Boolean;
336 begin
337 result := false; // don't stop
338 if (mon.trapCheckFrameId <> frameId) then
339 begin
340 mon.trapCheckFrameId := frameId;
341 chkTrap_mn[mnaCount] := mon;
342 Inc(mnaCount);
343 end;
344 end;
346 var
347 a, b, c, d, f: Integer;
348 pan: TPanel;
349 begin
350 if (gWater = nil) or (WaterMap = nil) then Exit;
352 frameId := g_Mons_getNewTrapFrameId();
354 //i1 := -1;
355 //i2 := -1;
357 //SetLength(chkTrap_pl, 1024);
358 //SetLength(chkTrap_mn, 1024);
359 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
360 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
362 for a := 0 to High(WaterMap) do
363 begin
364 for b := 0 to High(WaterMap[a]) do
365 begin
366 pan := gWater[WaterMap[a][b]];
367 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
369 for c := 0 to High(WaterMap[a]) do
370 begin
371 pan := gWater[WaterMap[a][c]];
372 for d := 0 to High(gPlayers) do
373 begin
374 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
375 begin
376 if gPlayers[d].Collide(pan) then
377 begin
378 f := 0;
379 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
380 if (f = plaCount) then
381 begin
382 chkTrap_pl[plaCount] := d;
383 Inc(plaCount);
384 if (plaCount = Length(chkTrap_pl)) then break;
385 end;
386 end;
387 end;
388 end;
390 //g_Mons_ForEach(monsWaterCheck);
391 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
392 end;
394 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
395 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
396 end;
397 end;
399 //chkTrap_pl := nil;
400 //chkTrap_mn := nil;
401 end;
403 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
404 var
405 tt, mt: Byte;
406 mon: TMonster;
407 begin
408 Result := False;
410 tt := g_GetUIDType(SpawnerUID);
411 if tt = UID_MONSTER then
412 begin
413 mon := g_Monsters_ByUID(SpawnerUID);
414 if mon <> nil then
415 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
416 else
417 mt := 0;
418 end
419 else
420 mt := 0;
422 if m = nil then Exit;
423 if m.UID = SpawnerUID then
424 begin
425 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
426 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
427 Exit;
428 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
429 if (m.MonsterType = MONSTER_CYBER) or
430 (m.MonsterType = MONSTER_BARREL) then
431 begin
432 Result := True;
433 Exit;
434 end;
435 end;
437 if tt = UID_MONSTER then
438 begin
439 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
440 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
441 Exit;
443 // Îáà ìîíñòðà îäíîãî âèäà:
444 if mt = m.MonsterType then
445 case mt of
446 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
447 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
448 Exit; // Ýòè íå áüþò ñâîèõ
449 end;
450 end;
452 if g_Game_IsServer then
453 begin
454 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
455 Result := m.Damage(d, vx, vy, SpawnerUID, t)
456 else
457 Result := True;
458 if t = HIT_FLAME then
459 m.CatchFire(SpawnerUID);
460 end
461 else
462 Result := True;
463 end;
466 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
467 begin
468 result := False;
470 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
471 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
473 if g_Game_IsServer then
474 begin
475 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
476 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
477 end;
479 result := true;
480 end;
483 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
485 function monsCheck (mon: TMonster): Boolean;
486 begin
487 result := false; // don't stop
488 if (mon.Live) and (mon.UID <> SpawnerUID) then
489 begin
490 with mon do
491 begin
492 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
493 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
494 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
495 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
496 begin
497 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
498 end;
499 end;
500 end;
501 end;
503 var
504 i, h: Integer;
505 st: Byte;
506 pl: TPlayer;
507 b: Boolean;
508 begin
509 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
511 h := High(gCorpses);
513 if gAdvCorpses and (h <> -1) then
514 for i := 0 to h do
515 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
516 with gCorpses[i] do
517 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
518 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
519 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
520 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
521 begin
522 Damage(50, 0, 0);
523 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
524 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
525 end;
527 st := TEAM_NONE;
528 pl := g_Player_Get(SpawnerUID);
529 if pl <> nil then
530 st := pl.Team;
532 h := High(gPlayers);
534 if h <> -1 then
535 for i := 0 to h do
536 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
537 with gPlayers[i] do
538 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
539 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
540 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
541 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
542 begin
543 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
544 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
545 else
546 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
547 if b then
548 gPlayers[i].BFGHit();
549 end;
551 //FIXME
552 g_Mons_ForEachAlive(monsCheck);
553 end;
555 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
556 var
557 find_id: DWord;
558 FramesID: DWORD = 0;
559 begin
560 if I < 0 then
561 find_id := FindShot()
562 else
563 begin
564 find_id := I;
565 if Integer(find_id) >= High(Shots) then
566 SetLength(Shots, find_id + 64)
567 end;
569 case ShotType of
570 WEAPON_ROCKETLAUNCHER:
571 begin
572 with Shots[find_id] do
573 begin
574 g_Obj_Init(@Obj);
576 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
577 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
579 Animation := nil;
580 Triggers := nil;
581 ShotType := WEAPON_ROCKETLAUNCHER;
582 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
583 end;
584 end;
586 WEAPON_PLASMA:
587 begin
588 with Shots[find_id] do
589 begin
590 g_Obj_Init(@Obj);
592 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
593 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
595 Triggers := nil;
596 ShotType := WEAPON_PLASMA;
597 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
598 Animation := TAnimation.Create(FramesID, True, 5);
599 end;
600 end;
602 WEAPON_BFG:
603 begin
604 with Shots[find_id] do
605 begin
606 g_Obj_Init(@Obj);
608 Obj.Rect.Width := SHOT_BFG_WIDTH;
609 Obj.Rect.Height := SHOT_BFG_HEIGHT;
611 Triggers := nil;
612 ShotType := WEAPON_BFG;
613 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
614 Animation := TAnimation.Create(FramesID, True, 6);
615 end;
616 end;
618 WEAPON_FLAMETHROWER:
619 begin
620 with Shots[find_id] do
621 begin
622 g_Obj_Init(@Obj);
624 Obj.Rect.Width := SHOT_FLAME_WIDTH;
625 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
627 Triggers := nil;
628 ShotType := WEAPON_FLAMETHROWER;
629 Animation := nil;
630 TextureID := 0;
631 g_Frames_Get(TextureID, 'FRAMES_FLAME');
632 end;
633 end;
635 WEAPON_IMP_FIRE:
636 begin
637 with Shots[find_id] do
638 begin
639 g_Obj_Init(@Obj);
641 Obj.Rect.Width := 16;
642 Obj.Rect.Height := 16;
644 Triggers := nil;
645 ShotType := WEAPON_IMP_FIRE;
646 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
647 Animation := TAnimation.Create(FramesID, True, 4);
648 end;
649 end;
651 WEAPON_CACO_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_CACO_FIRE;
662 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
663 Animation := TAnimation.Create(FramesID, True, 4);
664 end;
665 end;
667 WEAPON_MANCUB_FIRE:
668 begin
669 with Shots[find_id] do
670 begin
671 g_Obj_Init(@Obj);
673 Obj.Rect.Width := 32;
674 Obj.Rect.Height := 32;
676 Triggers := nil;
677 ShotType := WEAPON_MANCUB_FIRE;
678 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
679 Animation := TAnimation.Create(FramesID, True, 4);
680 end;
681 end;
683 WEAPON_BARON_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 := 16;
692 Triggers := nil;
693 ShotType := WEAPON_BARON_FIRE;
694 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
695 Animation := TAnimation.Create(FramesID, True, 4);
696 end;
697 end;
699 WEAPON_BSP_FIRE:
700 begin
701 with Shots[find_id] do
702 begin
703 g_Obj_Init(@Obj);
705 Obj.Rect.Width := 16;
706 Obj.Rect.Height := 16;
708 Triggers := nil;
709 ShotType := WEAPON_BSP_FIRE;
710 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
711 Animation := TAnimation.Create(FramesID, True, 4);
712 end;
713 end;
715 WEAPON_SKEL_FIRE:
716 begin
717 with Shots[find_id] do
718 begin
719 g_Obj_Init(@Obj);
721 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
722 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
724 Triggers := nil;
725 ShotType := WEAPON_SKEL_FIRE;
726 target := TargetUID;
727 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
728 Animation := TAnimation.Create(FramesID, True, 5);
729 end;
730 end;
731 end;
733 Shots[find_id].Obj.X := X;
734 Shots[find_id].Obj.Y := Y;
735 Shots[find_id].Obj.Vel.X := XV;
736 Shots[find_id].Obj.Vel.Y := YV;
737 Shots[find_id].Obj.Accel.X := 0;
738 Shots[find_id].Obj.Accel.Y := 0;
739 Shots[find_id].SpawnerUID := Spawner;
740 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
741 Shots[find_id].Stopped := 255
742 else
743 Shots[find_id].Stopped := 0;
744 Result := find_id;
745 end;
747 procedure throw(i, x, y, xd, yd, s: Integer);
748 var
749 a: Integer;
750 begin
751 yd := yd - y;
752 xd := xd - x;
754 a := Max(Abs(xd), Abs(yd));
755 if a = 0 then
756 a := 1;
758 Shots[i].Obj.X := x;
759 Shots[i].Obj.Y := y;
760 Shots[i].Obj.Vel.X := (xd*s) div a;
761 Shots[i].Obj.Vel.Y := (yd*s) div a;
762 Shots[i].Obj.Accel.X := 0;
763 Shots[i].Obj.Accel.Y := 0;
764 Shots[i].Stopped := 0;
765 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
766 Shots[i].Timeout := 900 // ~25 sec
767 else
768 begin
769 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
770 Shots[i].Timeout := SHOT_FLAME_LIFETIME
771 else
772 Shots[i].Timeout := 550; // ~15 sec
773 end;
774 end;
776 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
777 var
778 i, h: Integer;
780 function PlayerHit(Team: Byte = 0): Boolean;
781 var
782 i: Integer;
783 ChkTeam: Boolean;
784 p: TPlayer;
785 begin
786 Result := False;
787 h := High(gPlayers);
789 if h <> -1 then
790 for i := 0 to h do
791 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
792 begin
793 ChkTeam := True;
794 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
795 begin
796 p := g_Player_Get(SpawnerUID);
797 if p <> nil then
798 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
799 end;
800 if ChkTeam then
801 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
802 begin
803 if t <> HIT_FLAME then
804 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
805 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
806 if t = HIT_BFG then
807 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
808 Result := True;
809 break;
810 end;
811 end;
812 end;
815 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
816 begin
817 result := false; // don't stop
818 if mon.Live and g_Obj_Collide(obj, @mon.Obj) then
819 begin
820 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
821 begin
822 if (t <> HIT_FLAME) then
823 begin
824 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
825 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
826 end;
827 result := True;
828 end;
829 end;
830 end;
833 function monsCheckHit (mon: TMonster): Boolean;
834 begin
835 result := false; // don't stop
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;
847 function MonsterHit(): Boolean;
848 begin
849 //result := g_Mons_ForEach(monsCheckHit);
850 //FIXME: accelerate this!
851 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
852 end;
854 begin
855 Result := 0;
857 if HitCorpses then
858 begin
859 h := High(gCorpses);
861 if gAdvCorpses and (h <> -1) then
862 for i := 0 to h do
863 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
864 g_Obj_Collide(obj, @gCorpses[i].Obj) then
865 begin
866 // Ðàñïèëèâàåì òðóï:
867 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
868 (obj^.Vel.Y+obj^.Accel.Y) div 4);
869 Result := 1;
870 end;
871 end;
873 case gGameSettings.GameMode of
874 // Êàìïàíèÿ:
875 GM_COOP, GM_SINGLE:
876 begin
877 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
878 if MonsterHit() then
879 begin
880 Result := 2;
881 Exit;
882 end;
884 if PlayerHit() then
885 begin
886 Result := 1;
887 Exit;
888 end;
889 end;
891 // Äåçìàò÷:
892 GM_DM:
893 begin
894 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
895 if PlayerHit() then
896 begin
897 Result := 1;
898 Exit;
899 end;
901 if MonsterHit() then
902 begin
903 Result := 2;
904 Exit;
905 end;
906 end;
908 // Êîìàíäíûå:
909 GM_TDM, GM_CTF:
910 begin
911 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
912 if PlayerHit(2) then
913 begin
914 Result := 1;
915 Exit;
916 end;
918 // Ïîòîì ìîíñòðîâ
919 if MonsterHit() then
920 begin
921 Result := 2;
922 Exit;
923 end;
925 // È â êîíöå ñâîèõ èãðîêîâ
926 if PlayerHit(1) then
927 begin
928 Result := 1;
929 Exit;
930 end;
931 end;
933 end;
934 end;
936 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
937 begin
938 Result := False;
940 case g_GetUIDType(UID) of
941 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
942 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
943 else Exit;
944 end;
945 end;
947 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
948 var
949 r: Integer; // squared radius
951 function monsExCheck (mon: TMonster): Boolean;
952 var
953 dx, dy, mm: Integer;
954 begin
955 result := false; // don't stop
956 begin
957 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
958 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
960 if dx > 1000 then dx := 1000;
961 if dy > 1000 then dy := 1000;
963 if (dx*dx+dy*dy < r) then
964 begin
965 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
966 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
968 mm := Max(abs(dx), abs(dy));
969 if mm = 0 then mm := 1;
971 if mon.Live then
972 begin
973 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
974 end;
976 mon.Push((dx*7) div mm, (dy*7) div mm);
977 end;
978 end;
979 end;
981 var
982 i, h, dx, dy, m, mm: Integer;
983 _angle: SmallInt;
984 begin
985 result := false;
987 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
989 r := rad*rad;
991 h := High(gPlayers);
993 if h <> -1 then
994 for i := 0 to h do
995 if (gPlayers[i] <> nil) and gPlayers[i].Live then
996 with gPlayers[i] do
997 begin
998 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
999 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1001 if dx > 1000 then dx := 1000;
1002 if dy > 1000 then dy := 1000;
1004 if dx*dx+dy*dy < r then
1005 begin
1006 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1007 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1009 mm := Max(abs(dx), abs(dy));
1010 if mm = 0 then mm := 1;
1012 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1013 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1014 end;
1015 end;
1017 //g_Mons_ForEach(monsExCheck);
1018 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1020 h := High(gCorpses);
1022 if gAdvCorpses and (h <> -1) then
1023 for i := 0 to h do
1024 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1025 with gCorpses[i] do
1026 begin
1027 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1028 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1030 if dx > 1000 then dx := 1000;
1031 if dy > 1000 then dy := 1000;
1033 if dx*dx+dy*dy < r then
1034 begin
1035 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1036 Obj.Rect.Width, Obj.Rect.Height);
1038 mm := Max(abs(dx), abs(dy));
1039 if mm = 0 then mm := 1;
1041 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1042 end;
1043 end;
1045 h := High(gGibs);
1047 if gAdvGibs and (h <> -1) then
1048 for i := 0 to h do
1049 if gGibs[i].Live then
1050 with gGibs[i] do
1051 begin
1052 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1053 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1055 if dx > 1000 then dx := 1000;
1056 if dy > 1000 then dy := 1000;
1058 if dx*dx+dy*dy < r then
1059 begin
1060 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1061 Obj.Rect.Width, Obj.Rect.Height);
1062 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1063 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1065 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1066 positionChanged(); // this updates spatial accelerators
1067 end;
1068 end;
1069 end;
1071 procedure g_Weapon_Init();
1072 begin
1073 CreateWaterMap();
1074 end;
1076 procedure g_Weapon_Free();
1077 var
1078 i: Integer;
1079 begin
1080 if Shots <> nil then
1081 begin
1082 for i := 0 to High(Shots) do
1083 if Shots[i].ShotType <> 0 then
1084 Shots[i].Animation.Free();
1086 Shots := nil;
1087 end;
1089 WaterMap := nil;
1090 end;
1092 procedure g_Weapon_LoadData();
1093 begin
1094 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1096 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1097 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1098 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1099 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1100 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1101 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1102 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1103 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1104 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1105 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1106 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1107 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1111 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1121 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1122 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1123 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1124 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1125 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1126 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1127 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1129 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1130 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1131 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1132 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1133 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1134 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1135 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1136 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1137 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1138 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1139 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1140 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1141 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1142 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1143 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1144 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1145 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1146 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1147 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1148 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1149 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1151 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1152 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1154 wgunMonHash := hashNewIntInt();
1155 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeCompare);
1156 end;
1158 procedure g_Weapon_FreeData();
1159 begin
1160 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1162 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1163 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1164 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1165 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1166 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1167 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1168 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1169 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1170 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1171 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1172 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1173 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1174 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1175 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1176 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1177 g_Sound_Delete('SOUND_FIRE');
1178 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1179 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1180 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1181 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1182 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1183 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1184 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1185 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1186 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1187 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1188 g_Sound_Delete('SOUND_PLAYER_JETON');
1189 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1190 g_Sound_Delete('SOUND_PLAYER_CASING1');
1191 g_Sound_Delete('SOUND_PLAYER_CASING2');
1192 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1193 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1195 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1196 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1197 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1198 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1199 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1200 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1201 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1202 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1203 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1204 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1205 g_Frames_DeleteByName('FRAMES_BFGHIT');
1206 g_Frames_DeleteByName('FRAMES_FIRE');
1207 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1208 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1209 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1210 g_Frames_DeleteByName('FRAMES_SMOKE');
1211 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1212 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1213 end;
1216 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1217 var
1218 i: Integer;
1219 begin
1220 result := false;
1221 for i := 0 to High(gPlayers) do
1222 begin
1223 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
1224 begin
1225 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1226 begin
1227 if AllowPush then gPlayers[i].Push(vx, vy);
1228 result := true;
1229 end;
1230 end;
1231 end;
1232 end;
1235 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1237 function monsCheck (mon: TMonster): Boolean;
1238 begin
1239 result := false; // don't stop
1240 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1241 begin
1242 if AllowPush then mon.Push(vx, vy);
1243 result := true;
1244 end;
1245 end;
1247 begin
1248 result := 0;
1249 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1250 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1251 end;
1254 (*
1255 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1256 var
1257 a: Integer;
1258 x2, y2: Integer;
1259 dx, dy: Integer;
1260 xe, ye: Integer;
1261 xi, yi: Integer;
1262 s, c: Extended;
1263 //vx, vy: Integer;
1264 xx, yy, d: Integer;
1265 i: Integer;
1266 t1, _collide: Boolean;
1267 w, h: Word;
1268 {$IF DEFINED(D2F_DEBUG)}
1269 stt: UInt64;
1270 showTime: Boolean = true;
1271 {$ENDIF}
1272 begin
1273 a := GetAngle(x, y, xd, yd)+180;
1275 SinCos(DegToRad(-a), s, c);
1277 if Abs(s) < 0.01 then s := 0;
1278 if Abs(c) < 0.01 then c := 0;
1280 x2 := x+Round(c*gMapInfo.Width);
1281 y2 := y+Round(s*gMapInfo.Width);
1283 t1 := gWalls <> nil;
1284 _collide := False;
1285 w := gMapInfo.Width;
1286 h := gMapInfo.Height;
1288 xe := 0;
1289 ye := 0;
1290 dx := x2-x;
1291 dy := y2-y;
1293 if (xd = 0) and (yd = 0) then Exit;
1295 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1296 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1298 dx := Abs(dx);
1299 dy := Abs(dy);
1301 if dx > dy then d := dx else d := dy;
1303 //blood vel, for Monster.Damage()
1304 //vx := (dx*10 div d)*xi;
1305 //vy := (dy*10 div d)*yi;
1307 {$IF DEFINED(D2F_DEBUG)}
1308 stt := curTimeMicro();
1309 {$ENDIF}
1311 xx := x;
1312 yy := y;
1314 for i := 1 to d do
1315 begin
1316 xe := xe+dx;
1317 ye := ye+dy;
1319 if xe > d then
1320 begin
1321 xe := xe-d;
1322 xx := xx+xi;
1323 end;
1325 if ye > d then
1326 begin
1327 ye := ye-d;
1328 yy := yy+yi;
1329 end;
1331 if (yy > h) or (yy < 0) then Break;
1332 if (xx > w) or (xx < 0) then Break;
1334 if t1 then
1335 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1336 begin
1337 _collide := True;
1338 {$IF DEFINED(D2F_DEBUG)}
1339 stt := curTimeMicro()-stt;
1340 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1341 showTime := false;
1342 {$ENDIF}
1343 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1344 if g_Game_IsServer and g_Game_IsNet then
1345 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1346 end;
1348 if not _collide then
1349 begin
1350 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1351 end;
1353 if _collide then Break;
1354 end;
1356 {$IF DEFINED(D2F_DEBUG)}
1357 if showTime then
1358 begin
1359 stt := curTimeMicro()-stt;
1360 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1361 end;
1362 {$ENDIF}
1364 if CheckTrigger and g_Game_IsServer then
1365 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1366 end;
1367 *)
1370 //!!!FIXME!!!
1371 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1372 (*
1373 var
1374 hitray: Ray2D;
1375 xi, yi: Integer;
1376 wallDistSq: Single = 1.0e100;
1378 function doPlayerHit (idx: Integer): Boolean;
1379 begin
1380 result := false;
1381 if (idx < 0) or (idx > High(gPlayers)) then exit;
1382 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1383 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1384 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1385 {$IF DEFINED(D2F_DEBUG)}
1386 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1387 {$ENDIF}
1388 end;
1390 function doMonsterHit (mon: TMonster): Boolean;
1391 begin
1392 result := false;
1393 if (mon = nil) then exit;
1394 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1395 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1396 {$IF DEFINED(D2F_DEBUG)}
1397 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1398 {$ENDIF}
1399 end;
1401 // collect players along hitray
1402 // return `true` if instant hit was detected
1403 function playerPossibleHit (): Boolean;
1404 var
1405 i: Integer;
1406 aabb: AABB2D;
1407 tmin: Single;
1408 begin
1409 result := false;
1410 for i := 0 to High(gPlayers) do
1411 begin
1412 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1413 begin
1414 aabb := gPlayers[i].mapAABB;
1415 // inside?
1416 if aabb.contains(x, y) then
1417 begin
1418 if doPlayerHit(i) then begin result := true; exit; end;
1419 end
1420 else if (aabb.intersects(hitray, @tmin)) then
1421 begin
1422 // intersect
1423 if (tmin <= 0.0) then
1424 begin
1425 if doPlayerHit(i) then begin result := true; exit; end;
1426 end
1427 else
1428 begin
1429 if (tmin*tmin < wallDistSq) then appendHitTimePlr(tmin, i);
1430 end;
1431 end;
1432 end;
1433 end;
1434 end;
1436 function sqchecker (mon: TMonster; dist: Single): Boolean;
1437 begin
1438 result := false; // don't stop
1439 if (dist*dist < wallDistSq) then appendHitTimeMon(dist, mon);
1440 end;
1441 *)
1443 (*
1444 var
1445 a: Integer;
1446 x2, y2: Integer;
1447 dx, dy: Integer;
1448 xe, ye: Integer;
1449 s, c: Extended;
1450 i: Integer;
1451 wallHitFlag: Boolean = false;
1452 wallHitX: Integer = 0;
1453 wallHitY: Integer = 0;
1454 didHit: Boolean = false;
1455 {$IF DEFINED(D2F_DEBUG)}
1456 stt: UInt64;
1457 {$ENDIF}
1458 *)
1459 begin
1460 (*
1461 if not gwep_debug_fast_trace then
1462 begin
1463 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1464 exit;
1465 end;
1466 *)
1468 (*
1469 wgunMonHash.reset(); //FIXME: clear hash on level change
1470 wgunHitHeap.clear();
1471 wgunHitTimeUsed := 0;
1473 a := GetAngle(x, y, xd, yd)+180;
1475 SinCos(DegToRad(-a), s, c);
1477 if Abs(s) < 0.01 then s := 0;
1478 if Abs(c) < 0.01 then c := 0;
1480 x2 := x+Round(c*gMapInfo.Width);
1481 y2 := y+Round(s*gMapInfo.Width);
1483 dx := x2-x;
1484 dy := y2-y;
1486 if (xd = 0) and (yd = 0) then exit;
1488 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1489 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1491 {$IF DEFINED(D2F_DEBUG)}
1492 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1493 stt := curTimeMicro();
1494 {$ENDIF}
1496 wallHitFlag := g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY);
1497 if wallHitFlag then
1498 begin
1499 x2 := wallHitX;
1500 y2 := wallHitY;
1501 wallDistSq := (wallHitX-x)*(wallHitX-x)+(wallHitY-y)*(wallHitY-y);
1502 end
1503 else
1504 begin
1505 wallHitX := x2;
1506 wallHitY := y2;
1507 end;
1509 hitray := Ray2D.Create(x, y, x2, y2);
1511 if playerPossibleHit() then exit; // instant hit
1513 // collect monsters
1514 g_Mons_alongLine(x, y, x2, y2, sqchecker);
1516 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1517 // also, if `wallWasHit` >= 0, then `wallHitX` and `wallHitY` contains spark coords
1518 while (wgunHitHeap.count > 0) do
1519 begin
1520 // has some entities to check, do it
1521 i := wgunHitHeap.front;
1522 wgunHitHeap.popFront();
1523 hitray.atTime(wgunHitTime[i].time, xe, ye);
1524 // check if it is not behind the wall
1525 if (wgunHitTime[i].mon <> nil) then
1526 begin
1527 didHit := doMonsterHit(wgunHitTime[i].mon);
1528 end
1529 else
1530 begin
1531 didHit := doPlayerHit(wgunHitTime[i].plridx);
1532 end;
1533 if didHit then
1534 begin
1535 // need new coords for trigger
1536 wallHitX := xe;
1537 wallHitY := ye;
1538 wallHitFlag := false; // no sparks
1539 break;
1540 end;
1541 end;
1543 // need sparks?
1544 if wallHitFlag then
1545 begin
1546 {$IF DEFINED(D2F_DEBUG)}
1547 stt := curTimeMicro()-stt;
1548 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1549 {$ENDIF}
1550 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1551 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1552 end
1553 else
1554 begin
1555 {$IF DEFINED(D2F_DEBUG)}
1556 stt := curTimeMicro()-stt;
1557 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1558 {$ENDIF}
1559 end;
1561 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1562 *)
1563 end;
1566 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1567 var
1568 obj: TObj;
1569 begin
1570 obj.X := X;
1571 obj.Y := Y;
1572 obj.rect.X := 0;
1573 obj.rect.Y := 0;
1574 obj.rect.Width := 39;
1575 obj.rect.Height := 52;
1576 obj.Vel.X := 0;
1577 obj.Vel.Y := 0;
1578 obj.Accel.X := 0;
1579 obj.Accel.Y := 0;
1581 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1582 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1583 else
1584 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1585 end;
1587 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1588 var
1589 obj: TObj;
1590 begin
1591 obj.X := X;
1592 obj.Y := Y;
1593 obj.rect.X := 0;
1594 obj.rect.Y := 0;
1595 obj.rect.Width := 32;
1596 obj.rect.Height := 52;
1597 obj.Vel.X := 0;
1598 obj.Vel.Y := 0;
1599 obj.Accel.X := 0;
1600 obj.Accel.Y := 0;
1602 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1603 end;
1605 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1606 Silent: Boolean = False);
1607 var
1608 find_id: DWORD;
1609 dx, dy: Integer;
1610 begin
1611 if WID < 0 then
1612 find_id := FindShot()
1613 else
1614 begin
1615 find_id := WID;
1616 if Integer(find_id) >= High(Shots) then
1617 SetLength(Shots, find_id + 64)
1618 end;
1620 with Shots[find_id] do
1621 begin
1622 g_Obj_Init(@Obj);
1624 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1625 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1627 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1628 dy := -(Obj.Rect.Height div 2);
1630 ShotType := WEAPON_ROCKETLAUNCHER;
1631 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1633 Animation := nil;
1634 triggers := nil;
1635 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1636 end;
1638 Shots[find_id].SpawnerUID := SpawnerUID;
1640 if not Silent then
1641 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1642 end;
1644 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1645 WID: Integer = -1; Silent: Boolean = False);
1646 var
1647 find_id, FramesID: DWORD;
1648 dx, dy: Integer;
1649 begin
1650 if WID < 0 then
1651 find_id := FindShot()
1652 else
1653 begin
1654 find_id := WID;
1655 if Integer(find_id) >= High(Shots) then
1656 SetLength(Shots, find_id + 64)
1657 end;
1659 with Shots[find_id] do
1660 begin
1661 g_Obj_Init(@Obj);
1663 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1664 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1666 dx := -(Obj.Rect.Width div 2);
1667 dy := -(Obj.Rect.Height div 2);
1669 ShotType := WEAPON_SKEL_FIRE;
1670 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1672 triggers := nil;
1673 target := TargetUID;
1674 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1675 Animation := TAnimation.Create(FramesID, True, 5);
1676 end;
1678 Shots[find_id].SpawnerUID := SpawnerUID;
1680 if not Silent then
1681 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1682 end;
1684 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1685 Silent: Boolean = False);
1686 var
1687 find_id, FramesID: DWORD;
1688 dx, dy: Integer;
1689 begin
1690 if WID < 0 then
1691 find_id := FindShot()
1692 else
1693 begin
1694 find_id := WID;
1695 if Integer(find_id) >= High(Shots) then
1696 SetLength(Shots, find_id + 64);
1697 end;
1699 with Shots[find_id] do
1700 begin
1701 g_Obj_Init(@Obj);
1703 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1704 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1706 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1707 dy := -(Obj.Rect.Height div 2);
1709 ShotType := WEAPON_PLASMA;
1710 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1712 triggers := nil;
1713 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1714 Animation := TAnimation.Create(FramesID, True, 5);
1715 end;
1717 Shots[find_id].SpawnerUID := SpawnerUID;
1719 if not Silent then
1720 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1721 end;
1723 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1724 Silent: Boolean = False);
1725 var
1726 find_id: DWORD;
1727 dx, dy: Integer;
1728 begin
1729 if WID < 0 then
1730 find_id := FindShot()
1731 else
1732 begin
1733 find_id := WID;
1734 if Integer(find_id) >= High(Shots) then
1735 SetLength(Shots, find_id + 64);
1736 end;
1738 with Shots[find_id] do
1739 begin
1740 g_Obj_Init(@Obj);
1742 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1743 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1745 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1746 dy := -(Obj.Rect.Height div 2);
1748 ShotType := WEAPON_FLAMETHROWER;
1749 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1751 triggers := nil;
1752 Animation := nil;
1753 TextureID := 0;
1754 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1755 end;
1757 Shots[find_id].SpawnerUID := SpawnerUID;
1759 // if not Silent then
1760 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1761 end;
1763 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1764 Silent: Boolean = False);
1765 var
1766 find_id, FramesID: DWORD;
1767 dx, dy: Integer;
1768 begin
1769 if WID < 0 then
1770 find_id := FindShot()
1771 else
1772 begin
1773 find_id := WID;
1774 if Integer(find_id) >= High(Shots) then
1775 SetLength(Shots, find_id + 64)
1776 end;
1778 with Shots[find_id] do
1779 begin
1780 g_Obj_Init(@Obj);
1782 Obj.Rect.Width := 16;
1783 Obj.Rect.Height := 16;
1785 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1786 dy := -(Obj.Rect.Height div 2);
1788 ShotType := WEAPON_IMP_FIRE;
1789 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1791 triggers := nil;
1792 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1793 Animation := TAnimation.Create(FramesID, True, 4);
1794 end;
1796 Shots[find_id].SpawnerUID := SpawnerUID;
1798 if not Silent then
1799 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1800 end;
1802 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1803 Silent: Boolean = False);
1804 var
1805 find_id, FramesID: DWORD;
1806 dx, dy: Integer;
1807 begin
1808 if WID < 0 then
1809 find_id := FindShot()
1810 else
1811 begin
1812 find_id := WID;
1813 if Integer(find_id) >= High(Shots) then
1814 SetLength(Shots, find_id + 64)
1815 end;
1817 with Shots[find_id] do
1818 begin
1819 g_Obj_Init(@Obj);
1821 Obj.Rect.Width := 16;
1822 Obj.Rect.Height := 16;
1824 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1825 dy := -(Obj.Rect.Height div 2);
1827 ShotType := WEAPON_CACO_FIRE;
1828 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1830 triggers := nil;
1831 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1832 Animation := TAnimation.Create(FramesID, True, 4);
1833 end;
1835 Shots[find_id].SpawnerUID := SpawnerUID;
1837 if not Silent then
1838 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1839 end;
1841 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1842 Silent: Boolean = False);
1843 var
1844 find_id, FramesID: DWORD;
1845 dx, dy: Integer;
1846 begin
1847 if WID < 0 then
1848 find_id := FindShot()
1849 else
1850 begin
1851 find_id := WID;
1852 if Integer(find_id) >= High(Shots) then
1853 SetLength(Shots, find_id + 64)
1854 end;
1856 with Shots[find_id] do
1857 begin
1858 g_Obj_Init(@Obj);
1860 Obj.Rect.Width := 32;
1861 Obj.Rect.Height := 16;
1863 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1864 dy := -(Obj.Rect.Height div 2);
1866 ShotType := WEAPON_BARON_FIRE;
1867 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1869 triggers := nil;
1870 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1871 Animation := TAnimation.Create(FramesID, True, 4);
1872 end;
1874 Shots[find_id].SpawnerUID := SpawnerUID;
1876 if not Silent then
1877 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1878 end;
1880 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1881 Silent: Boolean = False);
1882 var
1883 find_id, FramesID: DWORD;
1884 dx, dy: Integer;
1885 begin
1886 if WID < 0 then
1887 find_id := FindShot()
1888 else
1889 begin
1890 find_id := WID;
1891 if Integer(find_id) >= High(Shots) then
1892 SetLength(Shots, find_id + 64)
1893 end;
1895 with Shots[find_id] do
1896 begin
1897 g_Obj_Init(@Obj);
1899 Obj.Rect.Width := 16;
1900 Obj.Rect.Height := 16;
1902 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1903 dy := -(Obj.Rect.Height div 2);
1905 ShotType := WEAPON_BSP_FIRE;
1906 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1908 triggers := nil;
1910 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1911 Animation := TAnimation.Create(FramesID, True, 4);
1912 end;
1914 Shots[find_id].SpawnerUID := SpawnerUID;
1916 if not Silent then
1917 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1918 end;
1920 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1921 Silent: Boolean = False);
1922 var
1923 find_id, FramesID: DWORD;
1924 dx, dy: Integer;
1925 begin
1926 if WID < 0 then
1927 find_id := FindShot()
1928 else
1929 begin
1930 find_id := WID;
1931 if Integer(find_id) >= High(Shots) then
1932 SetLength(Shots, find_id + 64)
1933 end;
1935 with Shots[find_id] do
1936 begin
1937 g_Obj_Init(@Obj);
1939 Obj.Rect.Width := 32;
1940 Obj.Rect.Height := 32;
1942 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1943 dy := -(Obj.Rect.Height div 2);
1945 ShotType := WEAPON_MANCUB_FIRE;
1946 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1948 triggers := nil;
1950 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1951 Animation := TAnimation.Create(FramesID, True, 4);
1952 end;
1954 Shots[find_id].SpawnerUID := SpawnerUID;
1956 if not Silent then
1957 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1958 end;
1960 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1961 Silent: Boolean = False);
1962 var
1963 find_id, FramesID: DWORD;
1964 dx, dy: Integer;
1965 begin
1966 if WID < 0 then
1967 find_id := FindShot()
1968 else
1969 begin
1970 find_id := WID;
1971 if Integer(find_id) >= High(Shots) then
1972 SetLength(Shots, find_id + 64)
1973 end;
1975 with Shots[find_id] do
1976 begin
1977 g_Obj_Init(@Obj);
1979 Obj.Rect.Width := SHOT_BFG_WIDTH;
1980 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1982 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1983 dy := -(Obj.Rect.Height div 2);
1985 ShotType := WEAPON_BFG;
1986 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1988 triggers := nil;
1989 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
1990 Animation := TAnimation.Create(FramesID, True, 6);
1991 end;
1993 Shots[find_id].SpawnerUID := SpawnerUID;
1995 if not Silent then
1996 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
1997 end;
1999 procedure g_Weapon_bfghit(x, y: Integer);
2000 var
2001 ID: DWORD;
2002 Anim: TAnimation;
2003 begin
2004 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2005 begin
2006 Anim := TAnimation.Create(ID, False, 4);
2007 g_GFX_OnceAnim(x-32, y-32, Anim);
2008 Anim.Free();
2009 end;
2010 end;
2012 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2013 Silent: Boolean = False);
2014 begin
2015 if not Silent then
2016 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2018 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2019 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2020 begin
2021 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2022 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2023 end;
2024 end;
2026 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2027 Silent: Boolean = False);
2028 begin
2029 if not Silent then
2030 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2032 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2033 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2034 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2035 begin
2036 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2037 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2038 end;
2039 end;
2041 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2042 Silent: Boolean = False);
2043 var
2044 i, j: Integer;
2045 begin
2046 if not Silent then
2047 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2049 for i := 0 to 9 do
2050 begin
2051 j := Random(17)-8; // -8 .. 8
2052 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2053 end;
2054 end;
2056 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2057 Silent: Boolean = False);
2058 var
2059 a, i, j: Integer;
2060 begin
2061 if not Silent then
2062 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2064 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2065 for i := 0 to a do
2066 begin
2067 j := Random(41)-20; // -20 .. 20
2068 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2069 end;
2070 end;
2072 procedure g_Weapon_Update();
2073 var
2074 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2075 _id: DWORD;
2076 Anim: TAnimation;
2077 t: DWArray;
2078 st: Word;
2079 s: String;
2080 o: TObj;
2081 spl: Boolean;
2082 Loud: Boolean;
2083 tcx, tcy: Integer;
2084 begin
2085 if Shots = nil then
2086 Exit;
2088 for i := 0 to High(Shots) do
2089 begin
2090 if Shots[i].ShotType = 0 then
2091 Continue;
2093 Loud := True;
2095 with Shots[i] do
2096 begin
2097 Timeout := Timeout - 1;
2098 oldvx := Obj.Vel.X;
2099 oldvy := Obj.Vel.Y;
2100 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2101 if (Stopped = 0) and g_Game_IsServer then
2102 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2103 SpawnerUID, ACTIVATE_SHOT, triggers)
2104 else
2105 t := nil;
2107 if t <> nil then
2108 begin
2109 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2110 if triggers = nil then
2111 triggers := t
2112 else
2113 begin
2114 h := High(t);
2116 for a := 0 to h do
2117 if not InDWArray(t[a], triggers) then
2118 begin
2119 SetLength(triggers, Length(triggers)+1);
2120 triggers[High(triggers)] := t[a];
2121 end;
2122 end;
2123 end;
2125 // Àíèìàöèÿ ñíàðÿäà:
2126 if Animation <> nil then
2127 Animation.Update();
2129 // Äâèæåíèå:
2130 spl := (ShotType <> WEAPON_PLASMA) and
2131 (ShotType <> WEAPON_BFG) and
2132 (ShotType <> WEAPON_BSP_FIRE) and
2133 (ShotType <> WEAPON_FLAMETHROWER);
2135 if Stopped = 0 then
2136 begin
2137 st := g_Obj_Move(@Obj, False, spl);
2138 end
2139 else
2140 begin
2141 st := 0;
2142 end;
2143 positionChanged(); // this updates spatial accelerators
2145 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2146 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2147 begin
2148 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2149 ShotType := 0;
2150 Animation.Free();
2151 Continue;
2152 end;
2154 cx := Obj.X + (Obj.Rect.Width div 2);
2155 cy := Obj.Y + (Obj.Rect.Height div 2);
2157 case ShotType of
2158 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2159 begin
2160 // Âûëåòåëà èç âîäû:
2161 if WordBool(st and MOVE_HITAIR) then
2162 g_Obj_SetSpeed(@Obj, 12);
2164 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2165 if WordBool(st and MOVE_INWATER) then
2166 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2167 Obj.Y+(Obj.Rect.Height div 2),
2168 1+Random(3), 16, 16)
2169 else
2170 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2171 begin
2172 Anim := TAnimation.Create(_id, False, 3);
2173 Anim.Alpha := 150;
2174 g_GFX_OnceAnim(Obj.X-14+Random(9),
2175 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2176 Anim, ONCEANIM_SMOKE);
2177 Anim.Free();
2178 end;
2180 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2181 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2182 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2183 (Timeout < 1) then
2184 begin
2185 Obj.Vel.X := 0;
2186 Obj.Vel.Y := 0;
2188 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2190 if ShotType = WEAPON_SKEL_FIRE then
2191 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2192 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2193 begin
2194 Anim := TAnimation.Create(TextureID, False, 8);
2195 Anim.Blending := False;
2196 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2197 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2198 Anim.Free();
2199 end;
2200 end
2201 else
2202 begin // Âçðûâ Ðàêåòû
2203 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2204 begin
2205 Anim := TAnimation.Create(TextureID, False, 6);
2206 Anim.Blending := False;
2207 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2208 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2209 Anim.Free();
2210 end;
2211 end;
2213 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2215 ShotType := 0;
2216 end;
2218 if ShotType = WEAPON_SKEL_FIRE then
2219 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2220 if GetPos(target, @o) then
2221 throw(i, Obj.X, Obj.Y,
2222 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2223 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2224 12);
2225 end;
2226 end;
2228 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2229 begin
2230 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2231 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2232 begin
2233 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2234 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2235 ShotType := 0;
2236 Continue;
2237 end;
2239 // Âåëè÷èíà óðîíà:
2240 if (ShotType = WEAPON_PLASMA) and
2241 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2242 a := 10
2243 else
2244 a := 5;
2246 if ShotType = WEAPON_BSP_FIRE then
2247 a := 10;
2249 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2250 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2251 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2252 (Timeout < 1) then
2253 begin
2254 if ShotType = WEAPON_PLASMA then
2255 s := 'FRAMES_EXPLODE_PLASMA'
2256 else
2257 s := 'FRAMES_EXPLODE_BSPFIRE';
2259 // Âçðûâ Ïëàçìû:
2260 if g_Frames_Get(TextureID, s) then
2261 begin
2262 Anim := TAnimation.Create(TextureID, False, 3);
2263 Anim.Blending := False;
2264 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2265 Anim.Free();
2266 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2267 end;
2269 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2271 ShotType := 0;
2272 end;
2273 end;
2275 WEAPON_FLAMETHROWER: // Îãíåìåò
2276 begin
2277 // Ñî âðåìåíåì óìèðàåò
2278 if (Timeout < 1) then
2279 begin
2280 ShotType := 0;
2281 Continue;
2282 end;
2283 // Ïîä âîäîé òîæå
2284 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2285 begin
2286 if WordBool(st and MOVE_HITWATER) then
2287 begin
2288 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2289 begin
2290 Anim := TAnimation.Create(_id, False, 3);
2291 Anim.Alpha := 0;
2292 tcx := Random(8);
2293 tcy := Random(8);
2294 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2295 cy-4+tcy-(Anim.Height div 2),
2296 Anim, ONCEANIM_SMOKE);
2297 Anim.Free();
2298 end;
2299 end
2300 else
2301 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2302 ShotType := 0;
2303 Continue;
2304 end;
2306 // Ãðàâèòàöèÿ
2307 if Stopped = 0 then
2308 Obj.Accel.Y := Obj.Accel.Y + 1;
2309 // Ïîïàëè â ñòåíó èëè â âîäó:
2310 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2311 begin
2312 // Ïðèëèïàåì:
2313 Obj.Vel.X := 0;
2314 Obj.Vel.Y := 0;
2315 Obj.Accel.Y := 0;
2316 if WordBool(st and MOVE_HITWALL) then
2317 Stopped := MOVE_HITWALL
2318 else if WordBool(st and MOVE_HITLAND) then
2319 Stopped := MOVE_HITLAND
2320 else if WordBool(st and MOVE_HITCEIL) then
2321 Stopped := MOVE_HITCEIL;
2322 end;
2324 a := IfThen(Stopped = 0, 3, 1);
2325 // Åñëè â êîãî-òî ïîïàëè
2326 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2327 begin
2328 // HIT_FLAME ñàì ïîäîææåò
2329 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2330 if Stopped = 0 then
2331 ShotType := 0;
2332 end;
2334 if Stopped = 0 then
2335 tf := 2
2336 else
2337 tf := 3;
2339 if (gTime mod LongWord(tf) = 0) then
2340 begin
2341 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2342 Anim.Alpha := 0;
2343 case Stopped of
2344 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2345 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2346 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2347 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2348 end;
2349 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2350 Anim.Free();
2351 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2352 end;
2353 end;
2355 WEAPON_BFG: // BFG
2356 begin
2357 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2358 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2359 begin
2360 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2361 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2362 ShotType := 0;
2363 Continue;
2364 end;
2366 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2367 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2368 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2369 (Timeout < 1) then
2370 begin
2371 // Ëó÷è BFG:
2372 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2374 // Âçðûâ BFG:
2375 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2376 begin
2377 Anim := TAnimation.Create(TextureID, False, 6);
2378 Anim.Blending := False;
2379 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2380 Anim.Free();
2381 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2382 end;
2384 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2386 ShotType := 0;
2387 end;
2388 end;
2390 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2391 begin
2392 // Âûëåòåë èç âîäû:
2393 if WordBool(st and MOVE_HITAIR) then
2394 g_Obj_SetSpeed(@Obj, 16);
2396 // Âåëè÷èíà óðîíà:
2397 if ShotType = WEAPON_IMP_FIRE then
2398 a := 5
2399 else
2400 if ShotType = WEAPON_CACO_FIRE then
2401 a := 20
2402 else
2403 a := 40;
2405 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2406 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2407 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2408 (Timeout < 1) then
2409 begin
2410 if ShotType = WEAPON_IMP_FIRE then
2411 s := 'FRAMES_EXPLODE_IMPFIRE'
2412 else
2413 if ShotType = WEAPON_CACO_FIRE then
2414 s := 'FRAMES_EXPLODE_CACOFIRE'
2415 else
2416 s := 'FRAMES_EXPLODE_BARONFIRE';
2418 // Âçðûâ:
2419 if g_Frames_Get(TextureID, s) then
2420 begin
2421 Anim := TAnimation.Create(TextureID, False, 6);
2422 Anim.Blending := False;
2423 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2424 Anim.Free();
2425 end;
2427 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2429 ShotType := 0;
2430 end;
2431 end;
2433 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2434 begin
2435 // Âûëåòåë èç âîäû:
2436 if WordBool(st and MOVE_HITAIR) then
2437 g_Obj_SetSpeed(@Obj, 16);
2439 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2440 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2441 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2442 (Timeout < 1) then
2443 begin
2444 // Âçðûâ:
2445 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2446 begin
2447 Anim := TAnimation.Create(TextureID, False, 6);
2448 Anim.Blending := False;
2449 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2450 Anim.Free();
2451 end;
2453 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2455 ShotType := 0;
2456 end;
2457 end;
2458 end; // case ShotType of...
2460 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2461 if (ShotType = 0) then
2462 begin
2463 if gGameSettings.GameType = GT_SERVER then
2464 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2465 if Animation <> nil then
2466 begin
2467 Animation.Free();
2468 Animation := nil;
2469 end;
2470 end
2471 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2472 if gGameSettings.GameType = GT_SERVER then
2473 MH_SEND_UpdateShot(i);
2474 end;
2475 end;
2476 end;
2478 procedure g_Weapon_Draw();
2479 var
2480 i: Integer;
2481 a: SmallInt;
2482 p: TPoint;
2483 begin
2484 if Shots = nil then
2485 Exit;
2487 for i := 0 to High(Shots) do
2488 if Shots[i].ShotType <> 0 then
2489 with Shots[i] do
2490 begin
2491 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2492 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2493 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2494 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2495 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2496 else
2497 a := 0;
2499 p.X := Obj.Rect.Width div 2;
2500 p.Y := Obj.Rect.Height div 2;
2502 if Animation <> nil then
2503 begin
2504 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2505 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2506 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2507 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2508 else
2509 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2510 end
2511 else if TextureID <> 0 then
2512 begin
2513 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2514 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2515 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2516 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2517 end;
2519 if g_debug_Frames then
2520 begin
2521 e_DrawQuad(Obj.X+Obj.Rect.X,
2522 Obj.Y+Obj.Rect.Y,
2523 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2524 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2525 0, 255, 0);
2526 end;
2527 end;
2528 end;
2530 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2531 var
2532 a: Integer;
2533 begin
2534 Result := False;
2536 if Shots = nil then
2537 Exit;
2539 for a := 0 to High(Shots) do
2540 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2541 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2542 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2543 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2544 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2545 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2546 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2547 begin
2548 Result := True;
2549 Exit;
2550 end;
2551 end;
2553 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2554 var
2555 count, i, j: Integer;
2556 dw: DWORD;
2557 begin
2558 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2559 count := 0;
2560 if Shots <> nil then
2561 for i := 0 to High(Shots) do
2562 if Shots[i].ShotType <> 0 then
2563 count := count + 1;
2565 Mem := TBinMemoryWriter.Create((count+1) * 80);
2567 // Êîëè÷åñòâî ñíàðÿäîâ:
2568 Mem.WriteInt(count);
2570 if count = 0 then
2571 Exit;
2573 for i := 0 to High(Shots) do
2574 if Shots[i].ShotType <> 0 then
2575 begin
2576 // Ñèãíàòóðà ñíàðÿäà:
2577 dw := SHOT_SIGNATURE; // 'SHOT'
2578 Mem.WriteDWORD(dw);
2579 // Òèï ñíàðÿäà:
2580 Mem.WriteByte(Shots[i].ShotType);
2581 // Öåëü:
2582 Mem.WriteWord(Shots[i].Target);
2583 // UID ñòðåëÿâøåãî:
2584 Mem.WriteWord(Shots[i].SpawnerUID);
2585 // Ðàçìåð ïîëÿ Triggers:
2586 dw := Length(Shots[i].Triggers);
2587 Mem.WriteDWORD(dw);
2588 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2589 for j := 0 to Integer(dw)-1 do
2590 Mem.WriteDWORD(Shots[i].Triggers[j]);
2591 // Îáúåêò ñíàðÿäà:
2592 Obj_SaveState(@Shots[i].Obj, Mem);
2593 // Êîñòûëèíà åáàíàÿ:
2594 Mem.WriteByte(Shots[i].Stopped);
2595 end;
2596 end;
2598 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2599 var
2600 count, i, j: Integer;
2601 dw: DWORD;
2602 begin
2603 if Mem = nil then
2604 Exit;
2606 // Êîëè÷åñòâî ñíàðÿäîâ:
2607 Mem.ReadInt(count);
2609 SetLength(Shots, count);
2611 if count = 0 then
2612 Exit;
2614 for i := 0 to count-1 do
2615 begin
2616 // Ñèãíàòóðà ñíàðÿäà:
2617 Mem.ReadDWORD(dw);
2618 if dw <> SHOT_SIGNATURE then // 'SHOT'
2619 begin
2620 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2621 end;
2622 // Òèï ñíàðÿäà:
2623 Mem.ReadByte(Shots[i].ShotType);
2624 // Öåëü:
2625 Mem.ReadWord(Shots[i].Target);
2626 // UID ñòðåëÿâøåãî:
2627 Mem.ReadWord(Shots[i].SpawnerUID);
2628 // Ðàçìåð ïîëÿ Triggers:
2629 Mem.ReadDWORD(dw);
2630 SetLength(Shots[i].Triggers, dw);
2631 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2632 for j := 0 to Integer(dw)-1 do
2633 Mem.ReadDWORD(Shots[i].Triggers[j]);
2634 // Îáúåêò ïðåäìåòà:
2635 Obj_LoadState(@Shots[i].Obj, Mem);
2636 // Êîñòûëèíà åáàíàÿ:
2637 Mem.ReadByte(Shots[i].Stopped);
2639 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2640 Shots[i].TextureID := DWORD(-1);
2641 Shots[i].Animation := nil;
2643 case Shots[i].ShotType of
2644 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2645 begin
2646 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2647 end;
2648 WEAPON_PLASMA:
2649 begin
2650 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2651 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2652 end;
2653 WEAPON_BFG:
2654 begin
2655 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2656 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2657 end;
2658 WEAPON_IMP_FIRE:
2659 begin
2660 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2661 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2662 end;
2663 WEAPON_BSP_FIRE:
2664 begin
2665 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2666 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2667 end;
2668 WEAPON_CACO_FIRE:
2669 begin
2670 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2671 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2672 end;
2673 WEAPON_BARON_FIRE:
2674 begin
2675 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2676 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2677 end;
2678 WEAPON_MANCUB_FIRE:
2679 begin
2680 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2681 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2682 end;
2683 end;
2684 end;
2685 end;
2687 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2688 var
2689 cx, cy: Integer;
2690 Anim: TAnimation;
2691 s: string;
2692 begin
2693 if Shots = nil then
2694 Exit;
2695 if (I > High(Shots)) or (I < 0) then Exit;
2697 with Shots[I] do
2698 begin
2699 if ShotType = 0 then Exit;
2700 Obj.X := X;
2701 Obj.Y := Y;
2702 cx := Obj.X + (Obj.Rect.Width div 2);
2703 cy := Obj.Y + (Obj.Rect.Height div 2);
2705 case ShotType of
2706 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2707 begin
2708 if Loud then
2709 begin
2710 if ShotType = WEAPON_SKEL_FIRE then
2711 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2712 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2713 begin
2714 Anim := TAnimation.Create(TextureID, False, 8);
2715 Anim.Blending := False;
2716 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2717 Anim.Free();
2718 end;
2719 end
2720 else
2721 begin // Âçðûâ Ðàêåòû
2722 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2723 begin
2724 Anim := TAnimation.Create(TextureID, False, 6);
2725 Anim.Blending := False;
2726 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2727 Anim.Free();
2728 end;
2729 end;
2730 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2731 end;
2732 end;
2734 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2735 begin
2736 if ShotType = WEAPON_PLASMA then
2737 s := 'FRAMES_EXPLODE_PLASMA'
2738 else
2739 s := 'FRAMES_EXPLODE_BSPFIRE';
2741 if g_Frames_Get(TextureID, s) and loud then
2742 begin
2743 Anim := TAnimation.Create(TextureID, False, 3);
2744 Anim.Blending := False;
2745 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2746 Anim.Free();
2748 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2749 end;
2750 end;
2752 WEAPON_BFG: // BFG
2753 begin
2754 // Âçðûâ BFG:
2755 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2756 begin
2757 Anim := TAnimation.Create(TextureID, False, 6);
2758 Anim.Blending := False;
2759 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2760 Anim.Free();
2762 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2763 end;
2764 end;
2766 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2767 begin
2768 if ShotType = WEAPON_IMP_FIRE then
2769 s := 'FRAMES_EXPLODE_IMPFIRE'
2770 else
2771 if ShotType = WEAPON_CACO_FIRE then
2772 s := 'FRAMES_EXPLODE_CACOFIRE'
2773 else
2774 s := 'FRAMES_EXPLODE_BARONFIRE';
2776 if g_Frames_Get(TextureID, s) and Loud then
2777 begin
2778 Anim := TAnimation.Create(TextureID, False, 6);
2779 Anim.Blending := False;
2780 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2781 Anim.Free();
2783 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2784 end;
2785 end;
2787 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2788 begin
2789 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2790 begin
2791 Anim := TAnimation.Create(TextureID, False, 6);
2792 Anim.Blending := False;
2793 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2794 Anim.Free();
2796 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2797 end;
2798 end;
2799 end; // case ShotType of...
2801 ShotType := 0;
2802 Animation.Free();
2803 end;
2804 end;
2807 procedure g_Weapon_AddDynLights();
2808 var
2809 i: Integer;
2810 begin
2811 if Shots = nil then Exit;
2812 for i := 0 to High(Shots) do
2813 begin
2814 if Shots[i].ShotType = 0 then continue;
2815 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2816 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2817 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2818 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2819 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2820 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2821 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2822 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2823 (Shots[i].ShotType = WEAPON_PLASMA) or
2824 (Shots[i].ShotType = WEAPON_BFG) or
2825 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2826 false then
2827 begin
2828 if (Shots[i].ShotType = WEAPON_PLASMA) then
2829 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)
2830 else if (Shots[i].ShotType = WEAPON_BFG) then
2831 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)
2832 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2833 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)
2834 else
2835 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);
2836 end;
2837 end;
2838 end;
2841 procedure TShot.positionChanged (); begin end;
2844 end.