DEADSOFTWARE

no more tree traces (i hope); still not working right
[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, g_grid,
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 distSq: Integer;
166 mon: TMonster;
167 plridx: Integer; // if mon=nil
168 x, y: Integer;
169 end;
171 // indicies in `wgunHitTime` array
172 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer>;
174 var
175 WaterMap: array of array of DWORD = nil;
176 //wgunMonHash: THashIntInt = nil;
177 wgunHitHeap: TBinaryHeapHitTimes = nil;
178 wgunHitTime: array of THitTime = nil;
179 wgunHitTimeUsed: Integer = 0;
182 function hitTimeCompare (a, b: Integer): Boolean;
183 begin
184 if (wgunHitTime[a].distSq < wgunHitTime[b].distSq) then begin result := true; exit; end;
185 if (wgunHitTime[a].distSq > wgunHitTime[b].distSq) then begin result := false; exit; end;
186 if (wgunHitTime[a].mon <> nil) then
187 begin
188 // a is monster
189 if (wgunHitTime[b].mon = nil) then begin result := false; exit; end; // players first
190 result := (wgunHitTime[a].mon.UID < wgunHitTime[b].mon.UID); // why not?
191 end
192 else
193 begin
194 // a is player
195 if (wgunHitTime[b].mon <> nil) then begin result := true; exit; end; // players first
196 result := (wgunHitTime[a].plridx < wgunHitTime[b].plridx); // why not?
197 end;
198 end;
201 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
202 begin
203 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
204 with wgunHitTime[wgunHitTimeUsed] do
205 begin
206 distSq := adistSq;
207 mon := amon;
208 plridx := -1;
209 x := ax;
210 y := ay;
211 end;
212 wgunHitHeap.insert(wgunHitTimeUsed);
213 Inc(wgunHitTimeUsed);
214 end;
217 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
218 begin
219 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
220 with wgunHitTime[wgunHitTimeUsed] do
221 begin
222 distSq := adistSq;
223 mon := nil;
224 plridx := aplridx;
225 x := ax;
226 y := ay;
227 end;
228 wgunHitHeap.insert(wgunHitTimeUsed);
229 Inc(wgunHitTimeUsed);
230 end;
233 function FindShot(): DWORD;
234 var
235 i: Integer;
236 begin
237 if Shots <> nil then
238 for i := 0 to High(Shots) do
239 if Shots[i].ShotType = 0 then
240 begin
241 Result := i;
242 LastShotID := Result;
243 Exit;
244 end;
246 if Shots = nil then
247 begin
248 SetLength(Shots, 128);
249 Result := 0;
250 end
251 else
252 begin
253 Result := High(Shots) + 1;
254 SetLength(Shots, Length(Shots) + 128);
255 end;
256 LastShotID := Result;
257 end;
259 procedure CreateWaterMap();
260 var
261 WaterArray: Array of TWaterPanel;
262 a, b, c, m: Integer;
263 ok: Boolean;
264 begin
265 if gWater = nil then
266 Exit;
268 SetLength(WaterArray, Length(gWater));
270 for a := 0 to High(gWater) do
271 begin
272 WaterArray[a].X := gWater[a].X;
273 WaterArray[a].Y := gWater[a].Y;
274 WaterArray[a].Width := gWater[a].Width;
275 WaterArray[a].Height := gWater[a].Height;
276 WaterArray[a].Active := True;
277 end;
279 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
281 for a := 0 to High(WaterArray) do
282 if WaterArray[a].Active then
283 begin
284 WaterArray[a].Active := False;
285 m := Length(WaterMap);
286 SetLength(WaterMap, m+1);
287 SetLength(WaterMap[m], 1);
288 WaterMap[m][0] := a;
289 ok := True;
291 while ok do
292 begin
293 ok := False;
294 for b := 0 to High(WaterArray) do
295 if WaterArray[b].Active then
296 for c := 0 to High(WaterMap[m]) do
297 if g_CollideAround(WaterArray[b].X,
298 WaterArray[b].Y,
299 WaterArray[b].Width,
300 WaterArray[b].Height,
301 WaterArray[WaterMap[m][c]].X,
302 WaterArray[WaterMap[m][c]].Y,
303 WaterArray[WaterMap[m][c]].Width,
304 WaterArray[WaterMap[m][c]].Height) then
305 begin
306 WaterArray[b].Active := False;
307 SetLength(WaterMap[m],
308 Length(WaterMap[m])+1);
309 WaterMap[m][High(WaterMap[m])] := b;
310 ok := True;
311 Break;
312 end;
313 end;
315 g_Game_StepLoading();
316 end;
318 WaterArray := nil;
319 end;
322 var
323 chkTrap_pl: array [0..256] of Integer;
324 chkTrap_mn: array [0..65535] of TMonster;
326 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
327 var
328 //a, b, c, d, i1, i2: Integer;
329 //chkTrap_pl, chkTrap_mn: WArray;
330 plaCount: Integer = 0;
331 mnaCount: Integer = 0;
332 frameId: DWord;
335 function monsWaterCheck (mon: TMonster): Boolean;
336 begin
337 result := false; // don't stop
338 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
339 begin
340 i2 += 1;
341 chkTrap_mn[i2] := monidx;
342 end;
343 end;
346 function monsWaterCheck (mon: TMonster): Boolean;
347 begin
348 result := false; // don't stop
349 if (mon.trapCheckFrameId <> frameId) then
350 begin
351 mon.trapCheckFrameId := frameId;
352 chkTrap_mn[mnaCount] := mon;
353 Inc(mnaCount);
354 end;
355 end;
357 var
358 a, b, c, d, f: Integer;
359 pan: TPanel;
360 begin
361 if (gWater = nil) or (WaterMap = nil) then Exit;
363 frameId := g_Mons_getNewTrapFrameId();
365 //i1 := -1;
366 //i2 := -1;
368 //SetLength(chkTrap_pl, 1024);
369 //SetLength(chkTrap_mn, 1024);
370 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
371 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
373 for a := 0 to High(WaterMap) do
374 begin
375 for b := 0 to High(WaterMap[a]) do
376 begin
377 pan := gWater[WaterMap[a][b]];
378 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
380 for c := 0 to High(WaterMap[a]) do
381 begin
382 pan := gWater[WaterMap[a][c]];
383 for d := 0 to High(gPlayers) do
384 begin
385 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
386 begin
387 if gPlayers[d].Collide(pan) then
388 begin
389 f := 0;
390 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
391 if (f = plaCount) then
392 begin
393 chkTrap_pl[plaCount] := d;
394 Inc(plaCount);
395 if (plaCount = Length(chkTrap_pl)) then break;
396 end;
397 end;
398 end;
399 end;
401 //g_Mons_ForEach(monsWaterCheck);
402 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
403 end;
405 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
406 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
407 end;
408 end;
410 //chkTrap_pl := nil;
411 //chkTrap_mn := nil;
412 end;
414 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
415 var
416 tt, mt: Byte;
417 mon: TMonster;
418 begin
419 Result := False;
421 tt := g_GetUIDType(SpawnerUID);
422 if tt = UID_MONSTER then
423 begin
424 mon := g_Monsters_ByUID(SpawnerUID);
425 if mon <> nil then
426 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
427 else
428 mt := 0;
429 end
430 else
431 mt := 0;
433 if m = nil then Exit;
434 if m.UID = SpawnerUID then
435 begin
436 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
437 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
438 Exit;
439 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
440 if (m.MonsterType = MONSTER_CYBER) or
441 (m.MonsterType = MONSTER_BARREL) then
442 begin
443 Result := True;
444 Exit;
445 end;
446 end;
448 if tt = UID_MONSTER then
449 begin
450 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
451 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
452 Exit;
454 // Îáà ìîíñòðà îäíîãî âèäà:
455 if mt = m.MonsterType then
456 case mt of
457 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
458 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
459 Exit; // Ýòè íå áüþò ñâîèõ
460 end;
461 end;
463 if g_Game_IsServer then
464 begin
465 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
466 Result := m.Damage(d, vx, vy, SpawnerUID, t)
467 else
468 Result := True;
469 if t = HIT_FLAME then
470 m.CatchFire(SpawnerUID);
471 end
472 else
473 Result := True;
474 end;
477 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
478 begin
479 result := False;
481 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
482 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
484 if g_Game_IsServer then
485 begin
486 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
487 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
488 end;
490 result := true;
491 end;
494 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
496 function monsCheck (mon: TMonster): Boolean;
497 begin
498 result := false; // don't stop
499 if (mon.Live) and (mon.UID <> SpawnerUID) then
500 begin
501 with mon do
502 begin
503 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
504 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
505 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
506 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
507 begin
508 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
509 end;
510 end;
511 end;
512 end;
514 var
515 i, h: Integer;
516 st: Byte;
517 pl: TPlayer;
518 b: Boolean;
519 begin
520 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
522 h := High(gCorpses);
524 if gAdvCorpses and (h <> -1) then
525 for i := 0 to h do
526 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
527 with gCorpses[i] do
528 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
529 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
530 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
531 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
532 begin
533 Damage(50, 0, 0);
534 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
535 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
536 end;
538 st := TEAM_NONE;
539 pl := g_Player_Get(SpawnerUID);
540 if pl <> nil then
541 st := pl.Team;
543 h := High(gPlayers);
545 if h <> -1 then
546 for i := 0 to h do
547 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
548 with gPlayers[i] do
549 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
550 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
551 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
552 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
553 begin
554 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
555 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
556 else
557 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
558 if b then
559 gPlayers[i].BFGHit();
560 end;
562 //FIXME
563 g_Mons_ForEachAlive(monsCheck);
564 end;
566 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
567 var
568 find_id: DWord;
569 FramesID: DWORD = 0;
570 begin
571 if I < 0 then
572 find_id := FindShot()
573 else
574 begin
575 find_id := I;
576 if Integer(find_id) >= High(Shots) then
577 SetLength(Shots, find_id + 64)
578 end;
580 case ShotType of
581 WEAPON_ROCKETLAUNCHER:
582 begin
583 with Shots[find_id] do
584 begin
585 g_Obj_Init(@Obj);
587 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
588 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
590 Animation := nil;
591 Triggers := nil;
592 ShotType := WEAPON_ROCKETLAUNCHER;
593 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
594 end;
595 end;
597 WEAPON_PLASMA:
598 begin
599 with Shots[find_id] do
600 begin
601 g_Obj_Init(@Obj);
603 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
604 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
606 Triggers := nil;
607 ShotType := WEAPON_PLASMA;
608 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
609 Animation := TAnimation.Create(FramesID, True, 5);
610 end;
611 end;
613 WEAPON_BFG:
614 begin
615 with Shots[find_id] do
616 begin
617 g_Obj_Init(@Obj);
619 Obj.Rect.Width := SHOT_BFG_WIDTH;
620 Obj.Rect.Height := SHOT_BFG_HEIGHT;
622 Triggers := nil;
623 ShotType := WEAPON_BFG;
624 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
625 Animation := TAnimation.Create(FramesID, True, 6);
626 end;
627 end;
629 WEAPON_FLAMETHROWER:
630 begin
631 with Shots[find_id] do
632 begin
633 g_Obj_Init(@Obj);
635 Obj.Rect.Width := SHOT_FLAME_WIDTH;
636 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
638 Triggers := nil;
639 ShotType := WEAPON_FLAMETHROWER;
640 Animation := nil;
641 TextureID := 0;
642 g_Frames_Get(TextureID, 'FRAMES_FLAME');
643 end;
644 end;
646 WEAPON_IMP_FIRE:
647 begin
648 with Shots[find_id] do
649 begin
650 g_Obj_Init(@Obj);
652 Obj.Rect.Width := 16;
653 Obj.Rect.Height := 16;
655 Triggers := nil;
656 ShotType := WEAPON_IMP_FIRE;
657 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
658 Animation := TAnimation.Create(FramesID, True, 4);
659 end;
660 end;
662 WEAPON_CACO_FIRE:
663 begin
664 with Shots[find_id] do
665 begin
666 g_Obj_Init(@Obj);
668 Obj.Rect.Width := 16;
669 Obj.Rect.Height := 16;
671 Triggers := nil;
672 ShotType := WEAPON_CACO_FIRE;
673 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
674 Animation := TAnimation.Create(FramesID, True, 4);
675 end;
676 end;
678 WEAPON_MANCUB_FIRE:
679 begin
680 with Shots[find_id] do
681 begin
682 g_Obj_Init(@Obj);
684 Obj.Rect.Width := 32;
685 Obj.Rect.Height := 32;
687 Triggers := nil;
688 ShotType := WEAPON_MANCUB_FIRE;
689 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
690 Animation := TAnimation.Create(FramesID, True, 4);
691 end;
692 end;
694 WEAPON_BARON_FIRE:
695 begin
696 with Shots[find_id] do
697 begin
698 g_Obj_Init(@Obj);
700 Obj.Rect.Width := 32;
701 Obj.Rect.Height := 16;
703 Triggers := nil;
704 ShotType := WEAPON_BARON_FIRE;
705 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
706 Animation := TAnimation.Create(FramesID, True, 4);
707 end;
708 end;
710 WEAPON_BSP_FIRE:
711 begin
712 with Shots[find_id] do
713 begin
714 g_Obj_Init(@Obj);
716 Obj.Rect.Width := 16;
717 Obj.Rect.Height := 16;
719 Triggers := nil;
720 ShotType := WEAPON_BSP_FIRE;
721 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
722 Animation := TAnimation.Create(FramesID, True, 4);
723 end;
724 end;
726 WEAPON_SKEL_FIRE:
727 begin
728 with Shots[find_id] do
729 begin
730 g_Obj_Init(@Obj);
732 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
733 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
735 Triggers := nil;
736 ShotType := WEAPON_SKEL_FIRE;
737 target := TargetUID;
738 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
739 Animation := TAnimation.Create(FramesID, True, 5);
740 end;
741 end;
742 end;
744 Shots[find_id].Obj.X := X;
745 Shots[find_id].Obj.Y := Y;
746 Shots[find_id].Obj.Vel.X := XV;
747 Shots[find_id].Obj.Vel.Y := YV;
748 Shots[find_id].Obj.Accel.X := 0;
749 Shots[find_id].Obj.Accel.Y := 0;
750 Shots[find_id].SpawnerUID := Spawner;
751 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
752 Shots[find_id].Stopped := 255
753 else
754 Shots[find_id].Stopped := 0;
755 Result := find_id;
756 end;
758 procedure throw(i, x, y, xd, yd, s: Integer);
759 var
760 a: Integer;
761 begin
762 yd := yd - y;
763 xd := xd - x;
765 a := Max(Abs(xd), Abs(yd));
766 if a = 0 then
767 a := 1;
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].Live 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.Live 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, (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 if PlayerHit() then
896 begin
897 Result := 1;
898 Exit;
899 end;
900 end;
902 // Äåçìàò÷:
903 GM_DM:
904 begin
905 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
906 if PlayerHit() then
907 begin
908 Result := 1;
909 Exit;
910 end;
912 if MonsterHit() then
913 begin
914 Result := 2;
915 Exit;
916 end;
917 end;
919 // Êîìàíäíûå:
920 GM_TDM, GM_CTF:
921 begin
922 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
923 if PlayerHit(2) then
924 begin
925 Result := 1;
926 Exit;
927 end;
929 // Ïîòîì ìîíñòðîâ
930 if MonsterHit() then
931 begin
932 Result := 2;
933 Exit;
934 end;
936 // È â êîíöå ñâîèõ èãðîêîâ
937 if PlayerHit(1) then
938 begin
939 Result := 1;
940 Exit;
941 end;
942 end;
944 end;
945 end;
947 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
948 begin
949 Result := False;
951 case g_GetUIDType(UID) of
952 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
953 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
954 else Exit;
955 end;
956 end;
958 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
959 var
960 r: Integer; // squared radius
962 function monsExCheck (mon: TMonster): Boolean;
963 var
964 dx, dy, mm: Integer;
965 begin
966 result := false; // don't stop
967 begin
968 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
969 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
971 if dx > 1000 then dx := 1000;
972 if dy > 1000 then dy := 1000;
974 if (dx*dx+dy*dy < r) then
975 begin
976 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
977 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
979 mm := Max(abs(dx), abs(dy));
980 if mm = 0 then mm := 1;
982 if mon.Live then
983 begin
984 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
985 end;
987 mon.Push((dx*7) div mm, (dy*7) div mm);
988 end;
989 end;
990 end;
992 var
993 i, h, dx, dy, m, mm: Integer;
994 _angle: SmallInt;
995 begin
996 result := false;
998 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1000 r := rad*rad;
1002 h := High(gPlayers);
1004 if h <> -1 then
1005 for i := 0 to h do
1006 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1007 with gPlayers[i] do
1008 begin
1009 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1010 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1012 if dx > 1000 then dx := 1000;
1013 if dy > 1000 then dy := 1000;
1015 if dx*dx+dy*dy < r then
1016 begin
1017 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1018 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1020 mm := Max(abs(dx), abs(dy));
1021 if mm = 0 then mm := 1;
1023 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1024 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1025 end;
1026 end;
1028 //g_Mons_ForEach(monsExCheck);
1029 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1031 h := High(gCorpses);
1033 if gAdvCorpses and (h <> -1) then
1034 for i := 0 to h do
1035 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1036 with gCorpses[i] do
1037 begin
1038 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1039 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1041 if dx > 1000 then dx := 1000;
1042 if dy > 1000 then dy := 1000;
1044 if dx*dx+dy*dy < r then
1045 begin
1046 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1047 Obj.Rect.Width, Obj.Rect.Height);
1049 mm := Max(abs(dx), abs(dy));
1050 if mm = 0 then mm := 1;
1052 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1053 end;
1054 end;
1056 h := High(gGibs);
1058 if gAdvGibs and (h <> -1) then
1059 for i := 0 to h do
1060 if gGibs[i].Live then
1061 with gGibs[i] do
1062 begin
1063 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1064 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1066 if dx > 1000 then dx := 1000;
1067 if dy > 1000 then dy := 1000;
1069 if dx*dx+dy*dy < r then
1070 begin
1071 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1072 Obj.Rect.Width, Obj.Rect.Height);
1073 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1074 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1076 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1077 positionChanged(); // this updates spatial accelerators
1078 end;
1079 end;
1080 end;
1082 procedure g_Weapon_Init();
1083 begin
1084 CreateWaterMap();
1085 end;
1087 procedure g_Weapon_Free();
1088 var
1089 i: Integer;
1090 begin
1091 if Shots <> nil then
1092 begin
1093 for i := 0 to High(Shots) do
1094 if Shots[i].ShotType <> 0 then
1095 Shots[i].Animation.Free();
1097 Shots := nil;
1098 end;
1100 WaterMap := nil;
1101 end;
1103 procedure g_Weapon_LoadData();
1104 begin
1105 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1107 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1122 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1132 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1133 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1134 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1135 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1136 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1137 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1138 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1140 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1141 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1142 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1143 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1144 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1145 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1146 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1147 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1148 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1149 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1150 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1151 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1152 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1153 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1154 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1155 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1156 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1157 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1158 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1159 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1160 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1162 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1163 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1165 //wgunMonHash := hashNewIntInt();
1166 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeCompare);
1167 end;
1169 procedure g_Weapon_FreeData();
1170 begin
1171 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1173 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1174 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1175 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1176 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1177 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1178 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1179 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1180 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1181 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1182 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1183 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1184 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1185 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1186 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1187 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1188 g_Sound_Delete('SOUND_FIRE');
1189 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1190 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1191 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1192 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1193 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1194 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1195 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1196 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1197 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1198 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1199 g_Sound_Delete('SOUND_PLAYER_JETON');
1200 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1201 g_Sound_Delete('SOUND_PLAYER_CASING1');
1202 g_Sound_Delete('SOUND_PLAYER_CASING2');
1203 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1204 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1206 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1207 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1208 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1209 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1210 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1211 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1212 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1213 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1214 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1215 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1216 g_Frames_DeleteByName('FRAMES_BFGHIT');
1217 g_Frames_DeleteByName('FRAMES_FIRE');
1218 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1219 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1220 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1221 g_Frames_DeleteByName('FRAMES_SMOKE');
1222 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1223 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1224 end;
1227 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1228 var
1229 i: Integer;
1230 begin
1231 result := false;
1232 for i := 0 to High(gPlayers) do
1233 begin
1234 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
1235 begin
1236 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1237 begin
1238 if AllowPush then gPlayers[i].Push(vx, vy);
1239 result := true;
1240 end;
1241 end;
1242 end;
1243 end;
1246 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1248 function monsCheck (mon: TMonster): Boolean;
1249 begin
1250 result := false; // don't stop
1251 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1252 begin
1253 if AllowPush then mon.Push(vx, vy);
1254 result := true;
1255 end;
1256 end;
1258 begin
1259 result := 0;
1260 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1261 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1262 end;
1265 (*
1266 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1267 var
1268 a: Integer;
1269 x2, y2: Integer;
1270 dx, dy: Integer;
1271 xe, ye: Integer;
1272 xi, yi: Integer;
1273 s, c: Extended;
1274 //vx, vy: Integer;
1275 xx, yy, d: Integer;
1276 i: Integer;
1277 t1, _collide: Boolean;
1278 w, h: Word;
1279 {$IF DEFINED(D2F_DEBUG)}
1280 stt: UInt64;
1281 showTime: Boolean = true;
1282 {$ENDIF}
1283 begin
1284 a := GetAngle(x, y, xd, yd)+180;
1286 SinCos(DegToRad(-a), s, c);
1288 if Abs(s) < 0.01 then s := 0;
1289 if Abs(c) < 0.01 then c := 0;
1291 x2 := x+Round(c*gMapInfo.Width);
1292 y2 := y+Round(s*gMapInfo.Width);
1294 t1 := gWalls <> nil;
1295 _collide := False;
1296 w := gMapInfo.Width;
1297 h := gMapInfo.Height;
1299 xe := 0;
1300 ye := 0;
1301 dx := x2-x;
1302 dy := y2-y;
1304 if (xd = 0) and (yd = 0) then Exit;
1306 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1307 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1309 dx := Abs(dx);
1310 dy := Abs(dy);
1312 if dx > dy then d := dx else d := dy;
1314 //blood vel, for Monster.Damage()
1315 //vx := (dx*10 div d)*xi;
1316 //vy := (dy*10 div d)*yi;
1318 {$IF DEFINED(D2F_DEBUG)}
1319 stt := curTimeMicro();
1320 {$ENDIF}
1322 xx := x;
1323 yy := y;
1325 for i := 1 to d do
1326 begin
1327 xe := xe+dx;
1328 ye := ye+dy;
1330 if xe > d then
1331 begin
1332 xe := xe-d;
1333 xx := xx+xi;
1334 end;
1336 if ye > d then
1337 begin
1338 ye := ye-d;
1339 yy := yy+yi;
1340 end;
1342 if (yy > h) or (yy < 0) then Break;
1343 if (xx > w) or (xx < 0) then Break;
1345 if t1 then
1346 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1347 begin
1348 _collide := True;
1349 {$IF DEFINED(D2F_DEBUG)}
1350 stt := curTimeMicro()-stt;
1351 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1352 showTime := false;
1353 {$ENDIF}
1354 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1355 if g_Game_IsServer and g_Game_IsNet then
1356 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1357 end;
1359 if not _collide then
1360 begin
1361 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1362 end;
1364 if _collide then Break;
1365 end;
1367 {$IF DEFINED(D2F_DEBUG)}
1368 if showTime then
1369 begin
1370 stt := curTimeMicro()-stt;
1371 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1372 end;
1373 {$ENDIF}
1375 if CheckTrigger and g_Game_IsServer then
1376 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1377 end;
1378 *)
1381 //!!!FIXME!!!
1382 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1383 var
1384 x2, y2: Integer;
1385 xi, yi: Integer;
1386 wallDistSq: Integer = $3fffffff;
1388 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1389 begin
1390 result := false;
1391 if (idx < 0) or (idx > High(gPlayers)) then exit;
1392 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1393 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1394 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1395 {$IF DEFINED(D2F_DEBUG)}
1396 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1397 {$ENDIF}
1398 end;
1400 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1401 begin
1402 result := false;
1403 if (mon = nil) then exit;
1404 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1405 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1406 {$IF DEFINED(D2F_DEBUG)}
1407 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1408 {$ENDIF}
1409 end;
1411 // collect players along hitray
1412 // return `true` if instant hit was detected
1413 function playerPossibleHit (): Boolean;
1414 var
1415 i: Integer;
1416 px, py, pw, ph: Integer;
1417 inx, iny: Integer;
1418 distSq: Integer;
1419 plr: TPlayer;
1420 begin
1421 result := false;
1422 for i := 0 to High(gPlayers) do
1423 begin
1424 plr := gPlayers[i];
1425 if (plr <> nil) and plr.Live then
1426 begin
1427 plr.getMapBox(px, py, pw, ph);
1428 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1429 begin
1430 distSq := distanceSq(x, y, inx, iny);
1431 if (distSq = 0) then
1432 begin
1433 // contains
1434 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1435 end
1436 else if (distSq < wallDistSq) then
1437 begin
1438 appendHitTimePlr(distSq, i, inx, iny);
1439 end;
1440 end;
1441 end;
1442 end;
1443 end;
1445 function sqchecker (mon: TMonster; tag: Integer): Boolean;
1446 var
1447 mx, my, mw, mh: Integer;
1448 inx, iny: Integer;
1449 distSq: Integer;
1450 begin
1451 result := false; // don't stop
1452 mon.getMapBox(mx, my, mw, mh);
1453 if lineAABBIntersects(x, y, x2, y2, mx, my, mw, mh, inx, iny) then
1454 begin
1455 distSq := distanceSq(x, y, inx, iny);
1456 if (distSq < wallDistSq) then appendHitTimeMon(distanceSq(x, y, inx, iny), mon, inx, iny);
1457 end;
1458 end;
1460 var
1461 a: Integer;
1462 dx, dy: Integer;
1463 xe, ye: Integer;
1464 s, c: Extended;
1465 i: Integer;
1466 wallHitFlag: Boolean = false;
1467 wallHitX: Integer = 0;
1468 wallHitY: Integer = 0;
1469 didHit: Boolean = false;
1470 {$IF DEFINED(D2F_DEBUG)}
1471 stt: UInt64;
1472 {$ENDIF}
1473 begin
1474 (*
1475 if not gwep_debug_fast_trace then
1476 begin
1477 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1478 exit;
1479 end;
1480 *)
1482 //wgunMonHash.reset(); //FIXME: clear hash on level change
1483 wgunHitHeap.clear();
1484 wgunHitTimeUsed := 0;
1486 a := GetAngle(x, y, xd, yd)+180;
1488 SinCos(DegToRad(-a), s, c);
1490 if Abs(s) < 0.01 then s := 0;
1491 if Abs(c) < 0.01 then c := 0;
1493 x2 := x+Round(c*gMapInfo.Width);
1494 y2 := y+Round(s*gMapInfo.Width);
1496 dx := x2-x;
1497 dy := y2-y;
1499 if (xd = 0) and (yd = 0) then exit;
1501 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1502 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1504 {$IF DEFINED(D2F_DEBUG)}
1505 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1506 stt := curTimeMicro();
1507 {$ENDIF}
1509 wallHitFlag := g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY);
1510 if wallHitFlag then
1511 begin
1512 x2 := wallHitX;
1513 y2 := wallHitY;
1514 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1515 end
1516 else
1517 begin
1518 wallHitX := x2;
1519 wallHitY := y2;
1520 end;
1522 if playerPossibleHit() then exit; // instant hit
1524 // collect monsters
1525 g_Mons_alongLine(x, y, x2, y2, sqchecker);
1527 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1528 // also, if `wallWasHit` >= 0, then `wallHitX` and `wallHitY` contains spark coords
1529 while (wgunHitHeap.count > 0) do
1530 begin
1531 // has some entities to check, do it
1532 i := wgunHitHeap.front;
1533 wgunHitHeap.popFront();
1534 xe := wgunHitTime[i].x;
1535 ye := wgunHitTime[i].y;
1536 // check if it is not behind the wall
1537 if (wgunHitTime[i].mon <> nil) then
1538 begin
1539 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1540 end
1541 else
1542 begin
1543 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1544 end;
1545 if didHit then
1546 begin
1547 // need new coords for trigger
1548 wallHitX := xe;
1549 wallHitY := ye;
1550 wallHitFlag := false; // no sparks
1551 break;
1552 end;
1553 end;
1555 // need sparks?
1556 if wallHitFlag then
1557 begin
1558 {$IF DEFINED(D2F_DEBUG)}
1559 stt := curTimeMicro()-stt;
1560 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1561 {$ENDIF}
1562 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1563 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1564 end
1565 else
1566 begin
1567 {$IF DEFINED(D2F_DEBUG)}
1568 stt := curTimeMicro()-stt;
1569 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1570 {$ENDIF}
1571 end;
1573 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1574 end;
1577 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1578 var
1579 obj: TObj;
1580 begin
1581 obj.X := X;
1582 obj.Y := Y;
1583 obj.rect.X := 0;
1584 obj.rect.Y := 0;
1585 obj.rect.Width := 39;
1586 obj.rect.Height := 52;
1587 obj.Vel.X := 0;
1588 obj.Vel.Y := 0;
1589 obj.Accel.X := 0;
1590 obj.Accel.Y := 0;
1592 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1593 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1594 else
1595 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1596 end;
1598 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1599 var
1600 obj: TObj;
1601 begin
1602 obj.X := X;
1603 obj.Y := Y;
1604 obj.rect.X := 0;
1605 obj.rect.Y := 0;
1606 obj.rect.Width := 32;
1607 obj.rect.Height := 52;
1608 obj.Vel.X := 0;
1609 obj.Vel.Y := 0;
1610 obj.Accel.X := 0;
1611 obj.Accel.Y := 0;
1613 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1614 end;
1616 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1617 Silent: Boolean = False);
1618 var
1619 find_id: DWORD;
1620 dx, dy: Integer;
1621 begin
1622 if WID < 0 then
1623 find_id := FindShot()
1624 else
1625 begin
1626 find_id := WID;
1627 if Integer(find_id) >= High(Shots) then
1628 SetLength(Shots, find_id + 64)
1629 end;
1631 with Shots[find_id] do
1632 begin
1633 g_Obj_Init(@Obj);
1635 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1636 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1638 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1639 dy := -(Obj.Rect.Height div 2);
1641 ShotType := WEAPON_ROCKETLAUNCHER;
1642 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1644 Animation := nil;
1645 triggers := nil;
1646 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1647 end;
1649 Shots[find_id].SpawnerUID := SpawnerUID;
1651 if not Silent then
1652 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1653 end;
1655 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1656 WID: Integer = -1; Silent: Boolean = False);
1657 var
1658 find_id, FramesID: DWORD;
1659 dx, dy: Integer;
1660 begin
1661 if WID < 0 then
1662 find_id := FindShot()
1663 else
1664 begin
1665 find_id := WID;
1666 if Integer(find_id) >= High(Shots) then
1667 SetLength(Shots, find_id + 64)
1668 end;
1670 with Shots[find_id] do
1671 begin
1672 g_Obj_Init(@Obj);
1674 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1675 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1677 dx := -(Obj.Rect.Width div 2);
1678 dy := -(Obj.Rect.Height div 2);
1680 ShotType := WEAPON_SKEL_FIRE;
1681 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1683 triggers := nil;
1684 target := TargetUID;
1685 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1686 Animation := TAnimation.Create(FramesID, True, 5);
1687 end;
1689 Shots[find_id].SpawnerUID := SpawnerUID;
1691 if not Silent then
1692 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1693 end;
1695 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1696 Silent: Boolean = False);
1697 var
1698 find_id, FramesID: DWORD;
1699 dx, dy: Integer;
1700 begin
1701 if WID < 0 then
1702 find_id := FindShot()
1703 else
1704 begin
1705 find_id := WID;
1706 if Integer(find_id) >= High(Shots) then
1707 SetLength(Shots, find_id + 64);
1708 end;
1710 with Shots[find_id] do
1711 begin
1712 g_Obj_Init(@Obj);
1714 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1715 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1717 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1718 dy := -(Obj.Rect.Height div 2);
1720 ShotType := WEAPON_PLASMA;
1721 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1723 triggers := nil;
1724 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1725 Animation := TAnimation.Create(FramesID, True, 5);
1726 end;
1728 Shots[find_id].SpawnerUID := SpawnerUID;
1730 if not Silent then
1731 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1732 end;
1734 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1735 Silent: Boolean = False);
1736 var
1737 find_id: DWORD;
1738 dx, dy: Integer;
1739 begin
1740 if WID < 0 then
1741 find_id := FindShot()
1742 else
1743 begin
1744 find_id := WID;
1745 if Integer(find_id) >= High(Shots) then
1746 SetLength(Shots, find_id + 64);
1747 end;
1749 with Shots[find_id] do
1750 begin
1751 g_Obj_Init(@Obj);
1753 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1754 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1756 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1757 dy := -(Obj.Rect.Height div 2);
1759 ShotType := WEAPON_FLAMETHROWER;
1760 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1762 triggers := nil;
1763 Animation := nil;
1764 TextureID := 0;
1765 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1766 end;
1768 Shots[find_id].SpawnerUID := SpawnerUID;
1770 // if not Silent then
1771 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1772 end;
1774 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1775 Silent: Boolean = False);
1776 var
1777 find_id, FramesID: DWORD;
1778 dx, dy: Integer;
1779 begin
1780 if WID < 0 then
1781 find_id := FindShot()
1782 else
1783 begin
1784 find_id := WID;
1785 if Integer(find_id) >= High(Shots) then
1786 SetLength(Shots, find_id + 64)
1787 end;
1789 with Shots[find_id] do
1790 begin
1791 g_Obj_Init(@Obj);
1793 Obj.Rect.Width := 16;
1794 Obj.Rect.Height := 16;
1796 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1797 dy := -(Obj.Rect.Height div 2);
1799 ShotType := WEAPON_IMP_FIRE;
1800 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1802 triggers := nil;
1803 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1804 Animation := TAnimation.Create(FramesID, True, 4);
1805 end;
1807 Shots[find_id].SpawnerUID := SpawnerUID;
1809 if not Silent then
1810 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1811 end;
1813 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1814 Silent: Boolean = False);
1815 var
1816 find_id, FramesID: DWORD;
1817 dx, dy: Integer;
1818 begin
1819 if WID < 0 then
1820 find_id := FindShot()
1821 else
1822 begin
1823 find_id := WID;
1824 if Integer(find_id) >= High(Shots) then
1825 SetLength(Shots, find_id + 64)
1826 end;
1828 with Shots[find_id] do
1829 begin
1830 g_Obj_Init(@Obj);
1832 Obj.Rect.Width := 16;
1833 Obj.Rect.Height := 16;
1835 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1836 dy := -(Obj.Rect.Height div 2);
1838 ShotType := WEAPON_CACO_FIRE;
1839 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1841 triggers := nil;
1842 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1843 Animation := TAnimation.Create(FramesID, True, 4);
1844 end;
1846 Shots[find_id].SpawnerUID := SpawnerUID;
1848 if not Silent then
1849 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1850 end;
1852 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1853 Silent: Boolean = False);
1854 var
1855 find_id, FramesID: DWORD;
1856 dx, dy: Integer;
1857 begin
1858 if WID < 0 then
1859 find_id := FindShot()
1860 else
1861 begin
1862 find_id := WID;
1863 if Integer(find_id) >= High(Shots) then
1864 SetLength(Shots, find_id + 64)
1865 end;
1867 with Shots[find_id] do
1868 begin
1869 g_Obj_Init(@Obj);
1871 Obj.Rect.Width := 32;
1872 Obj.Rect.Height := 16;
1874 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1875 dy := -(Obj.Rect.Height div 2);
1877 ShotType := WEAPON_BARON_FIRE;
1878 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1880 triggers := nil;
1881 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1882 Animation := TAnimation.Create(FramesID, True, 4);
1883 end;
1885 Shots[find_id].SpawnerUID := SpawnerUID;
1887 if not Silent then
1888 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1889 end;
1891 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1892 Silent: Boolean = False);
1893 var
1894 find_id, FramesID: DWORD;
1895 dx, dy: Integer;
1896 begin
1897 if WID < 0 then
1898 find_id := FindShot()
1899 else
1900 begin
1901 find_id := WID;
1902 if Integer(find_id) >= High(Shots) then
1903 SetLength(Shots, find_id + 64)
1904 end;
1906 with Shots[find_id] do
1907 begin
1908 g_Obj_Init(@Obj);
1910 Obj.Rect.Width := 16;
1911 Obj.Rect.Height := 16;
1913 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1914 dy := -(Obj.Rect.Height div 2);
1916 ShotType := WEAPON_BSP_FIRE;
1917 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1919 triggers := nil;
1921 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1922 Animation := TAnimation.Create(FramesID, True, 4);
1923 end;
1925 Shots[find_id].SpawnerUID := SpawnerUID;
1927 if not Silent then
1928 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1929 end;
1931 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1932 Silent: Boolean = False);
1933 var
1934 find_id, FramesID: DWORD;
1935 dx, dy: Integer;
1936 begin
1937 if WID < 0 then
1938 find_id := FindShot()
1939 else
1940 begin
1941 find_id := WID;
1942 if Integer(find_id) >= High(Shots) then
1943 SetLength(Shots, find_id + 64)
1944 end;
1946 with Shots[find_id] do
1947 begin
1948 g_Obj_Init(@Obj);
1950 Obj.Rect.Width := 32;
1951 Obj.Rect.Height := 32;
1953 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1954 dy := -(Obj.Rect.Height div 2);
1956 ShotType := WEAPON_MANCUB_FIRE;
1957 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1959 triggers := nil;
1961 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1962 Animation := TAnimation.Create(FramesID, True, 4);
1963 end;
1965 Shots[find_id].SpawnerUID := SpawnerUID;
1967 if not Silent then
1968 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1969 end;
1971 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1972 Silent: Boolean = False);
1973 var
1974 find_id, FramesID: DWORD;
1975 dx, dy: Integer;
1976 begin
1977 if WID < 0 then
1978 find_id := FindShot()
1979 else
1980 begin
1981 find_id := WID;
1982 if Integer(find_id) >= High(Shots) then
1983 SetLength(Shots, find_id + 64)
1984 end;
1986 with Shots[find_id] do
1987 begin
1988 g_Obj_Init(@Obj);
1990 Obj.Rect.Width := SHOT_BFG_WIDTH;
1991 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1993 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1994 dy := -(Obj.Rect.Height div 2);
1996 ShotType := WEAPON_BFG;
1997 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1999 triggers := nil;
2000 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2001 Animation := TAnimation.Create(FramesID, True, 6);
2002 end;
2004 Shots[find_id].SpawnerUID := SpawnerUID;
2006 if not Silent then
2007 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2008 end;
2010 procedure g_Weapon_bfghit(x, y: Integer);
2011 var
2012 ID: DWORD;
2013 Anim: TAnimation;
2014 begin
2015 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2016 begin
2017 Anim := TAnimation.Create(ID, False, 4);
2018 g_GFX_OnceAnim(x-32, y-32, Anim);
2019 Anim.Free();
2020 end;
2021 end;
2023 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2024 Silent: Boolean = False);
2025 begin
2026 if not Silent then
2027 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2029 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2030 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2031 begin
2032 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2033 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2034 end;
2035 end;
2037 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2038 Silent: Boolean = False);
2039 begin
2040 if not Silent then
2041 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2043 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2044 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2045 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2046 begin
2047 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2048 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2049 end;
2050 end;
2052 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2053 Silent: Boolean = False);
2054 var
2055 i, j: Integer;
2056 begin
2057 if not Silent then
2058 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2060 for i := 0 to 9 do
2061 begin
2062 j := Random(17)-8; // -8 .. 8
2063 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2064 end;
2065 end;
2067 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2068 Silent: Boolean = False);
2069 var
2070 a, i, j: Integer;
2071 begin
2072 if not Silent then
2073 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2075 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2076 for i := 0 to a do
2077 begin
2078 j := Random(41)-20; // -20 .. 20
2079 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2080 end;
2081 end;
2083 procedure g_Weapon_Update();
2084 var
2085 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2086 _id: DWORD;
2087 Anim: TAnimation;
2088 t: DWArray;
2089 st: Word;
2090 s: String;
2091 o: TObj;
2092 spl: Boolean;
2093 Loud: Boolean;
2094 tcx, tcy: Integer;
2095 begin
2096 if Shots = nil then
2097 Exit;
2099 for i := 0 to High(Shots) do
2100 begin
2101 if Shots[i].ShotType = 0 then
2102 Continue;
2104 Loud := True;
2106 with Shots[i] do
2107 begin
2108 Timeout := Timeout - 1;
2109 oldvx := Obj.Vel.X;
2110 oldvy := Obj.Vel.Y;
2111 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2112 if (Stopped = 0) and g_Game_IsServer then
2113 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2114 SpawnerUID, ACTIVATE_SHOT, triggers)
2115 else
2116 t := nil;
2118 if t <> nil then
2119 begin
2120 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2121 if triggers = nil then
2122 triggers := t
2123 else
2124 begin
2125 h := High(t);
2127 for a := 0 to h do
2128 if not InDWArray(t[a], triggers) then
2129 begin
2130 SetLength(triggers, Length(triggers)+1);
2131 triggers[High(triggers)] := t[a];
2132 end;
2133 end;
2134 end;
2136 // Àíèìàöèÿ ñíàðÿäà:
2137 if Animation <> nil then
2138 Animation.Update();
2140 // Äâèæåíèå:
2141 spl := (ShotType <> WEAPON_PLASMA) and
2142 (ShotType <> WEAPON_BFG) and
2143 (ShotType <> WEAPON_BSP_FIRE) and
2144 (ShotType <> WEAPON_FLAMETHROWER);
2146 if Stopped = 0 then
2147 begin
2148 st := g_Obj_Move(@Obj, False, spl);
2149 end
2150 else
2151 begin
2152 st := 0;
2153 end;
2154 positionChanged(); // this updates spatial accelerators
2156 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2157 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2158 begin
2159 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2160 ShotType := 0;
2161 Animation.Free();
2162 Continue;
2163 end;
2165 cx := Obj.X + (Obj.Rect.Width div 2);
2166 cy := Obj.Y + (Obj.Rect.Height div 2);
2168 case ShotType of
2169 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2170 begin
2171 // Âûëåòåëà èç âîäû:
2172 if WordBool(st and MOVE_HITAIR) then
2173 g_Obj_SetSpeed(@Obj, 12);
2175 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2176 if WordBool(st and MOVE_INWATER) then
2177 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2178 Obj.Y+(Obj.Rect.Height div 2),
2179 1+Random(3), 16, 16)
2180 else
2181 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2182 begin
2183 Anim := TAnimation.Create(_id, False, 3);
2184 Anim.Alpha := 150;
2185 g_GFX_OnceAnim(Obj.X-14+Random(9),
2186 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2187 Anim, ONCEANIM_SMOKE);
2188 Anim.Free();
2189 end;
2191 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2192 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2193 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2194 (Timeout < 1) then
2195 begin
2196 Obj.Vel.X := 0;
2197 Obj.Vel.Y := 0;
2199 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2201 if ShotType = WEAPON_SKEL_FIRE then
2202 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2203 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2204 begin
2205 Anim := TAnimation.Create(TextureID, False, 8);
2206 Anim.Blending := False;
2207 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2208 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2209 Anim.Free();
2210 end;
2211 end
2212 else
2213 begin // Âçðûâ Ðàêåòû
2214 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2215 begin
2216 Anim := TAnimation.Create(TextureID, False, 6);
2217 Anim.Blending := False;
2218 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2219 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2220 Anim.Free();
2221 end;
2222 end;
2224 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2226 ShotType := 0;
2227 end;
2229 if ShotType = WEAPON_SKEL_FIRE then
2230 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2231 if GetPos(target, @o) then
2232 throw(i, Obj.X, Obj.Y,
2233 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2234 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2235 12);
2236 end;
2237 end;
2239 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2240 begin
2241 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2242 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2243 begin
2244 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2245 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2246 ShotType := 0;
2247 Continue;
2248 end;
2250 // Âåëè÷èíà óðîíà:
2251 if (ShotType = WEAPON_PLASMA) and
2252 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2253 a := 10
2254 else
2255 a := 5;
2257 if ShotType = WEAPON_BSP_FIRE then
2258 a := 10;
2260 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2261 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2262 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2263 (Timeout < 1) then
2264 begin
2265 if ShotType = WEAPON_PLASMA then
2266 s := 'FRAMES_EXPLODE_PLASMA'
2267 else
2268 s := 'FRAMES_EXPLODE_BSPFIRE';
2270 // Âçðûâ Ïëàçìû:
2271 if g_Frames_Get(TextureID, s) then
2272 begin
2273 Anim := TAnimation.Create(TextureID, False, 3);
2274 Anim.Blending := False;
2275 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2276 Anim.Free();
2277 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2278 end;
2280 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2282 ShotType := 0;
2283 end;
2284 end;
2286 WEAPON_FLAMETHROWER: // Îãíåìåò
2287 begin
2288 // Ñî âðåìåíåì óìèðàåò
2289 if (Timeout < 1) then
2290 begin
2291 ShotType := 0;
2292 Continue;
2293 end;
2294 // Ïîä âîäîé òîæå
2295 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2296 begin
2297 if WordBool(st and MOVE_HITWATER) then
2298 begin
2299 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2300 begin
2301 Anim := TAnimation.Create(_id, False, 3);
2302 Anim.Alpha := 0;
2303 tcx := Random(8);
2304 tcy := Random(8);
2305 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2306 cy-4+tcy-(Anim.Height div 2),
2307 Anim, ONCEANIM_SMOKE);
2308 Anim.Free();
2309 end;
2310 end
2311 else
2312 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2313 ShotType := 0;
2314 Continue;
2315 end;
2317 // Ãðàâèòàöèÿ
2318 if Stopped = 0 then
2319 Obj.Accel.Y := Obj.Accel.Y + 1;
2320 // Ïîïàëè â ñòåíó èëè â âîäó:
2321 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2322 begin
2323 // Ïðèëèïàåì:
2324 Obj.Vel.X := 0;
2325 Obj.Vel.Y := 0;
2326 Obj.Accel.Y := 0;
2327 if WordBool(st and MOVE_HITWALL) then
2328 Stopped := MOVE_HITWALL
2329 else if WordBool(st and MOVE_HITLAND) then
2330 Stopped := MOVE_HITLAND
2331 else if WordBool(st and MOVE_HITCEIL) then
2332 Stopped := MOVE_HITCEIL;
2333 end;
2335 a := IfThen(Stopped = 0, 3, 1);
2336 // Åñëè â êîãî-òî ïîïàëè
2337 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2338 begin
2339 // HIT_FLAME ñàì ïîäîææåò
2340 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2341 if Stopped = 0 then
2342 ShotType := 0;
2343 end;
2345 if Stopped = 0 then
2346 tf := 2
2347 else
2348 tf := 3;
2350 if (gTime mod LongWord(tf) = 0) then
2351 begin
2352 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2353 Anim.Alpha := 0;
2354 case Stopped of
2355 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2356 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2357 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2358 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2359 end;
2360 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2361 Anim.Free();
2362 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2363 end;
2364 end;
2366 WEAPON_BFG: // BFG
2367 begin
2368 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2369 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2370 begin
2371 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2372 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2373 ShotType := 0;
2374 Continue;
2375 end;
2377 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2378 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2379 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2380 (Timeout < 1) then
2381 begin
2382 // Ëó÷è BFG:
2383 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2385 // Âçðûâ BFG:
2386 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2387 begin
2388 Anim := TAnimation.Create(TextureID, False, 6);
2389 Anim.Blending := False;
2390 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2391 Anim.Free();
2392 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2393 end;
2395 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2397 ShotType := 0;
2398 end;
2399 end;
2401 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2402 begin
2403 // Âûëåòåë èç âîäû:
2404 if WordBool(st and MOVE_HITAIR) then
2405 g_Obj_SetSpeed(@Obj, 16);
2407 // Âåëè÷èíà óðîíà:
2408 if ShotType = WEAPON_IMP_FIRE then
2409 a := 5
2410 else
2411 if ShotType = WEAPON_CACO_FIRE then
2412 a := 20
2413 else
2414 a := 40;
2416 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2417 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2418 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2419 (Timeout < 1) then
2420 begin
2421 if ShotType = WEAPON_IMP_FIRE then
2422 s := 'FRAMES_EXPLODE_IMPFIRE'
2423 else
2424 if ShotType = WEAPON_CACO_FIRE then
2425 s := 'FRAMES_EXPLODE_CACOFIRE'
2426 else
2427 s := 'FRAMES_EXPLODE_BARONFIRE';
2429 // Âçðûâ:
2430 if g_Frames_Get(TextureID, s) then
2431 begin
2432 Anim := TAnimation.Create(TextureID, False, 6);
2433 Anim.Blending := False;
2434 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2435 Anim.Free();
2436 end;
2438 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2440 ShotType := 0;
2441 end;
2442 end;
2444 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2445 begin
2446 // Âûëåòåë èç âîäû:
2447 if WordBool(st and MOVE_HITAIR) then
2448 g_Obj_SetSpeed(@Obj, 16);
2450 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2451 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2452 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2453 (Timeout < 1) then
2454 begin
2455 // Âçðûâ:
2456 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2457 begin
2458 Anim := TAnimation.Create(TextureID, False, 6);
2459 Anim.Blending := False;
2460 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2461 Anim.Free();
2462 end;
2464 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2466 ShotType := 0;
2467 end;
2468 end;
2469 end; // case ShotType of...
2471 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2472 if (ShotType = 0) then
2473 begin
2474 if gGameSettings.GameType = GT_SERVER then
2475 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2476 if Animation <> nil then
2477 begin
2478 Animation.Free();
2479 Animation := nil;
2480 end;
2481 end
2482 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2483 if gGameSettings.GameType = GT_SERVER then
2484 MH_SEND_UpdateShot(i);
2485 end;
2486 end;
2487 end;
2489 procedure g_Weapon_Draw();
2490 var
2491 i: Integer;
2492 a: SmallInt;
2493 p: TPoint;
2494 begin
2495 if Shots = nil then
2496 Exit;
2498 for i := 0 to High(Shots) do
2499 if Shots[i].ShotType <> 0 then
2500 with Shots[i] do
2501 begin
2502 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2503 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2504 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2505 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2506 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2507 else
2508 a := 0;
2510 p.X := Obj.Rect.Width div 2;
2511 p.Y := Obj.Rect.Height div 2;
2513 if Animation <> nil then
2514 begin
2515 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2516 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2517 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2518 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2519 else
2520 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2521 end
2522 else if TextureID <> 0 then
2523 begin
2524 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2525 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2526 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2527 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2528 end;
2530 if g_debug_Frames then
2531 begin
2532 e_DrawQuad(Obj.X+Obj.Rect.X,
2533 Obj.Y+Obj.Rect.Y,
2534 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2535 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2536 0, 255, 0);
2537 end;
2538 end;
2539 end;
2541 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2542 var
2543 a: Integer;
2544 begin
2545 Result := False;
2547 if Shots = nil then
2548 Exit;
2550 for a := 0 to High(Shots) do
2551 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2552 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2553 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2554 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2555 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2556 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2557 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2558 begin
2559 Result := True;
2560 Exit;
2561 end;
2562 end;
2564 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2565 var
2566 count, i, j: Integer;
2567 dw: DWORD;
2568 begin
2569 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2570 count := 0;
2571 if Shots <> nil then
2572 for i := 0 to High(Shots) do
2573 if Shots[i].ShotType <> 0 then
2574 count := count + 1;
2576 Mem := TBinMemoryWriter.Create((count+1) * 80);
2578 // Êîëè÷åñòâî ñíàðÿäîâ:
2579 Mem.WriteInt(count);
2581 if count = 0 then
2582 Exit;
2584 for i := 0 to High(Shots) do
2585 if Shots[i].ShotType <> 0 then
2586 begin
2587 // Ñèãíàòóðà ñíàðÿäà:
2588 dw := SHOT_SIGNATURE; // 'SHOT'
2589 Mem.WriteDWORD(dw);
2590 // Òèï ñíàðÿäà:
2591 Mem.WriteByte(Shots[i].ShotType);
2592 // Öåëü:
2593 Mem.WriteWord(Shots[i].Target);
2594 // UID ñòðåëÿâøåãî:
2595 Mem.WriteWord(Shots[i].SpawnerUID);
2596 // Ðàçìåð ïîëÿ Triggers:
2597 dw := Length(Shots[i].Triggers);
2598 Mem.WriteDWORD(dw);
2599 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2600 for j := 0 to Integer(dw)-1 do
2601 Mem.WriteDWORD(Shots[i].Triggers[j]);
2602 // Îáúåêò ñíàðÿäà:
2603 Obj_SaveState(@Shots[i].Obj, Mem);
2604 // Êîñòûëèíà åáàíàÿ:
2605 Mem.WriteByte(Shots[i].Stopped);
2606 end;
2607 end;
2609 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2610 var
2611 count, i, j: Integer;
2612 dw: DWORD;
2613 begin
2614 if Mem = nil then
2615 Exit;
2617 // Êîëè÷åñòâî ñíàðÿäîâ:
2618 Mem.ReadInt(count);
2620 SetLength(Shots, count);
2622 if count = 0 then
2623 Exit;
2625 for i := 0 to count-1 do
2626 begin
2627 // Ñèãíàòóðà ñíàðÿäà:
2628 Mem.ReadDWORD(dw);
2629 if dw <> SHOT_SIGNATURE then // 'SHOT'
2630 begin
2631 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2632 end;
2633 // Òèï ñíàðÿäà:
2634 Mem.ReadByte(Shots[i].ShotType);
2635 // Öåëü:
2636 Mem.ReadWord(Shots[i].Target);
2637 // UID ñòðåëÿâøåãî:
2638 Mem.ReadWord(Shots[i].SpawnerUID);
2639 // Ðàçìåð ïîëÿ Triggers:
2640 Mem.ReadDWORD(dw);
2641 SetLength(Shots[i].Triggers, dw);
2642 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2643 for j := 0 to Integer(dw)-1 do
2644 Mem.ReadDWORD(Shots[i].Triggers[j]);
2645 // Îáúåêò ïðåäìåòà:
2646 Obj_LoadState(@Shots[i].Obj, Mem);
2647 // Êîñòûëèíà åáàíàÿ:
2648 Mem.ReadByte(Shots[i].Stopped);
2650 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2651 Shots[i].TextureID := DWORD(-1);
2652 Shots[i].Animation := nil;
2654 case Shots[i].ShotType of
2655 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2656 begin
2657 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2658 end;
2659 WEAPON_PLASMA:
2660 begin
2661 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2662 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2663 end;
2664 WEAPON_BFG:
2665 begin
2666 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2667 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2668 end;
2669 WEAPON_IMP_FIRE:
2670 begin
2671 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2672 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2673 end;
2674 WEAPON_BSP_FIRE:
2675 begin
2676 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2677 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2678 end;
2679 WEAPON_CACO_FIRE:
2680 begin
2681 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2682 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2683 end;
2684 WEAPON_BARON_FIRE:
2685 begin
2686 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2687 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2688 end;
2689 WEAPON_MANCUB_FIRE:
2690 begin
2691 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2692 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2693 end;
2694 end;
2695 end;
2696 end;
2698 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2699 var
2700 cx, cy: Integer;
2701 Anim: TAnimation;
2702 s: string;
2703 begin
2704 if Shots = nil then
2705 Exit;
2706 if (I > High(Shots)) or (I < 0) then Exit;
2708 with Shots[I] do
2709 begin
2710 if ShotType = 0 then Exit;
2711 Obj.X := X;
2712 Obj.Y := Y;
2713 cx := Obj.X + (Obj.Rect.Width div 2);
2714 cy := Obj.Y + (Obj.Rect.Height div 2);
2716 case ShotType of
2717 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2718 begin
2719 if Loud then
2720 begin
2721 if ShotType = WEAPON_SKEL_FIRE then
2722 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2723 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2724 begin
2725 Anim := TAnimation.Create(TextureID, False, 8);
2726 Anim.Blending := False;
2727 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2728 Anim.Free();
2729 end;
2730 end
2731 else
2732 begin // Âçðûâ Ðàêåòû
2733 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2734 begin
2735 Anim := TAnimation.Create(TextureID, False, 6);
2736 Anim.Blending := False;
2737 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2738 Anim.Free();
2739 end;
2740 end;
2741 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2742 end;
2743 end;
2745 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2746 begin
2747 if ShotType = WEAPON_PLASMA then
2748 s := 'FRAMES_EXPLODE_PLASMA'
2749 else
2750 s := 'FRAMES_EXPLODE_BSPFIRE';
2752 if g_Frames_Get(TextureID, s) and loud then
2753 begin
2754 Anim := TAnimation.Create(TextureID, False, 3);
2755 Anim.Blending := False;
2756 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2757 Anim.Free();
2759 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2760 end;
2761 end;
2763 WEAPON_BFG: // BFG
2764 begin
2765 // Âçðûâ BFG:
2766 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2767 begin
2768 Anim := TAnimation.Create(TextureID, False, 6);
2769 Anim.Blending := False;
2770 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2771 Anim.Free();
2773 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2774 end;
2775 end;
2777 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2778 begin
2779 if ShotType = WEAPON_IMP_FIRE then
2780 s := 'FRAMES_EXPLODE_IMPFIRE'
2781 else
2782 if ShotType = WEAPON_CACO_FIRE then
2783 s := 'FRAMES_EXPLODE_CACOFIRE'
2784 else
2785 s := 'FRAMES_EXPLODE_BARONFIRE';
2787 if g_Frames_Get(TextureID, s) and Loud then
2788 begin
2789 Anim := TAnimation.Create(TextureID, False, 6);
2790 Anim.Blending := False;
2791 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2792 Anim.Free();
2794 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2795 end;
2796 end;
2798 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2799 begin
2800 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2801 begin
2802 Anim := TAnimation.Create(TextureID, False, 6);
2803 Anim.Blending := False;
2804 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2805 Anim.Free();
2807 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2808 end;
2809 end;
2810 end; // case ShotType of...
2812 ShotType := 0;
2813 Animation.Free();
2814 end;
2815 end;
2818 procedure g_Weapon_AddDynLights();
2819 var
2820 i: Integer;
2821 begin
2822 if Shots = nil then Exit;
2823 for i := 0 to High(Shots) do
2824 begin
2825 if Shots[i].ShotType = 0 then continue;
2826 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2827 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2828 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2829 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2830 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2831 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2832 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2833 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2834 (Shots[i].ShotType = WEAPON_PLASMA) or
2835 (Shots[i].ShotType = WEAPON_BFG) or
2836 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2837 false then
2838 begin
2839 if (Shots[i].ShotType = WEAPON_PLASMA) then
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, 0, 0.3, 1, 0.4)
2841 else if (Shots[i].ShotType = WEAPON_BFG) then
2842 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)
2843 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2844 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)
2845 else
2846 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);
2847 end;
2848 end;
2849 end;
2852 procedure TShot.positionChanged (); begin end;
2855 end.