DEADSOFTWARE

typo in mapcvt: microseconds -> milliseconds
[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 hitTimeLess (a, b: Integer): Boolean;
183 var
184 hta, htb: PHitTime;
185 begin
186 hta := @wgunHitTime[a];
187 htb := @wgunHitTime[b];
188 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
189 if (hta.mon <> nil) then
190 begin
191 // a is monster
192 if (htb.mon = nil) then begin result := false; exit; end; // players first
193 result := (hta.mon.UID < htb.mon.UID); // why not?
194 end
195 else
196 begin
197 // a is player
198 if (htb.mon <> nil) then begin result := true; exit; end; // players first
199 result := (hta.plridx < htb.plridx); // why not?
200 end;
201 end;
204 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
205 begin
206 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
207 with wgunHitTime[wgunHitTimeUsed] do
208 begin
209 distSq := adistSq;
210 mon := amon;
211 plridx := -1;
212 x := ax;
213 y := ay;
214 end;
215 wgunHitHeap.insert(wgunHitTimeUsed);
216 Inc(wgunHitTimeUsed);
217 end;
220 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
221 begin
222 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
223 with wgunHitTime[wgunHitTimeUsed] do
224 begin
225 distSq := adistSq;
226 mon := nil;
227 plridx := aplridx;
228 x := ax;
229 y := ay;
230 end;
231 wgunHitHeap.insert(wgunHitTimeUsed);
232 Inc(wgunHitTimeUsed);
233 end;
236 function FindShot(): DWORD;
237 var
238 i: Integer;
239 begin
240 if Shots <> nil then
241 for i := 0 to High(Shots) do
242 if Shots[i].ShotType = 0 then
243 begin
244 Result := i;
245 LastShotID := Result;
246 Exit;
247 end;
249 if Shots = nil then
250 begin
251 SetLength(Shots, 128);
252 Result := 0;
253 end
254 else
255 begin
256 Result := High(Shots) + 1;
257 SetLength(Shots, Length(Shots) + 128);
258 end;
259 LastShotID := Result;
260 end;
262 procedure CreateWaterMap();
263 var
264 WaterArray: Array of TWaterPanel;
265 a, b, c, m: Integer;
266 ok: Boolean;
267 begin
268 if gWater = nil then
269 Exit;
271 SetLength(WaterArray, Length(gWater));
273 for a := 0 to High(gWater) do
274 begin
275 WaterArray[a].X := gWater[a].X;
276 WaterArray[a].Y := gWater[a].Y;
277 WaterArray[a].Width := gWater[a].Width;
278 WaterArray[a].Height := gWater[a].Height;
279 WaterArray[a].Active := True;
280 end;
282 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
284 for a := 0 to High(WaterArray) do
285 if WaterArray[a].Active then
286 begin
287 WaterArray[a].Active := False;
288 m := Length(WaterMap);
289 SetLength(WaterMap, m+1);
290 SetLength(WaterMap[m], 1);
291 WaterMap[m][0] := a;
292 ok := True;
294 while ok do
295 begin
296 ok := False;
297 for b := 0 to High(WaterArray) do
298 if WaterArray[b].Active then
299 for c := 0 to High(WaterMap[m]) do
300 if g_CollideAround(WaterArray[b].X,
301 WaterArray[b].Y,
302 WaterArray[b].Width,
303 WaterArray[b].Height,
304 WaterArray[WaterMap[m][c]].X,
305 WaterArray[WaterMap[m][c]].Y,
306 WaterArray[WaterMap[m][c]].Width,
307 WaterArray[WaterMap[m][c]].Height) then
308 begin
309 WaterArray[b].Active := False;
310 SetLength(WaterMap[m],
311 Length(WaterMap[m])+1);
312 WaterMap[m][High(WaterMap[m])] := b;
313 ok := True;
314 Break;
315 end;
316 end;
318 g_Game_StepLoading();
319 end;
321 WaterArray := nil;
322 end;
325 var
326 chkTrap_pl: array [0..256] of Integer;
327 chkTrap_mn: array [0..65535] of TMonster;
329 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
330 var
331 //a, b, c, d, i1, i2: Integer;
332 //chkTrap_pl, chkTrap_mn: WArray;
333 plaCount: Integer = 0;
334 mnaCount: Integer = 0;
335 frameId: DWord;
338 function monsWaterCheck (mon: TMonster): Boolean;
339 begin
340 result := false; // don't stop
341 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
342 begin
343 i2 += 1;
344 chkTrap_mn[i2] := monidx;
345 end;
346 end;
349 function monsWaterCheck (mon: TMonster): Boolean;
350 begin
351 result := false; // don't stop
352 if (mon.trapCheckFrameId <> frameId) then
353 begin
354 mon.trapCheckFrameId := frameId;
355 chkTrap_mn[mnaCount] := mon;
356 Inc(mnaCount);
357 end;
358 end;
360 var
361 a, b, c, d, f: Integer;
362 pan: TPanel;
363 begin
364 if (gWater = nil) or (WaterMap = nil) then Exit;
366 frameId := g_Mons_getNewTrapFrameId();
368 //i1 := -1;
369 //i2 := -1;
371 //SetLength(chkTrap_pl, 1024);
372 //SetLength(chkTrap_mn, 1024);
373 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
374 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
376 for a := 0 to High(WaterMap) do
377 begin
378 for b := 0 to High(WaterMap[a]) do
379 begin
380 pan := gWater[WaterMap[a][b]];
381 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
383 for c := 0 to High(WaterMap[a]) do
384 begin
385 pan := gWater[WaterMap[a][c]];
386 for d := 0 to High(gPlayers) do
387 begin
388 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
389 begin
390 if gPlayers[d].Collide(pan) then
391 begin
392 f := 0;
393 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
394 if (f = plaCount) then
395 begin
396 chkTrap_pl[plaCount] := d;
397 Inc(plaCount);
398 if (plaCount = Length(chkTrap_pl)) then break;
399 end;
400 end;
401 end;
402 end;
404 //g_Mons_ForEach(monsWaterCheck);
405 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
406 end;
408 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
409 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
410 end;
411 end;
413 //chkTrap_pl := nil;
414 //chkTrap_mn := nil;
415 end;
417 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
418 var
419 tt, mt: Byte;
420 mon: TMonster;
421 begin
422 Result := False;
424 tt := g_GetUIDType(SpawnerUID);
425 if tt = UID_MONSTER then
426 begin
427 mon := g_Monsters_ByUID(SpawnerUID);
428 if mon <> nil then
429 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
430 else
431 mt := 0;
432 end
433 else
434 mt := 0;
436 if m = nil then Exit;
437 if m.UID = SpawnerUID then
438 begin
439 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
440 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
441 Exit;
442 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
443 if (m.MonsterType = MONSTER_CYBER) or
444 (m.MonsterType = MONSTER_BARREL) then
445 begin
446 Result := True;
447 Exit;
448 end;
449 end;
451 if tt = UID_MONSTER then
452 begin
453 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
454 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
455 Exit;
457 // Îáà ìîíñòðà îäíîãî âèäà:
458 if mt = m.MonsterType then
459 case mt of
460 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
461 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
462 Exit; // Ýòè íå áüþò ñâîèõ
463 end;
464 end;
466 if g_Game_IsServer then
467 begin
468 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
469 Result := m.Damage(d, vx, vy, SpawnerUID, t)
470 else
471 Result := True;
472 if t = HIT_FLAME then
473 m.CatchFire(SpawnerUID);
474 end
475 else
476 Result := True;
477 end;
480 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
481 begin
482 result := False;
484 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
485 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
487 if g_Game_IsServer then
488 begin
489 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
490 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
491 end;
493 result := true;
494 end;
497 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
499 function monsCheck (mon: TMonster): Boolean;
500 begin
501 result := false; // don't stop
502 if (mon.Live) and (mon.UID <> SpawnerUID) then
503 begin
504 with mon do
505 begin
506 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
507 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
508 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
509 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
510 begin
511 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
512 end;
513 end;
514 end;
515 end;
517 var
518 i, h: Integer;
519 st: Byte;
520 pl: TPlayer;
521 b: Boolean;
522 begin
523 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
525 h := High(gCorpses);
527 if gAdvCorpses and (h <> -1) then
528 for i := 0 to h do
529 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
530 with gCorpses[i] do
531 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
532 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
533 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
534 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
535 begin
536 Damage(50, 0, 0);
537 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
538 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
539 end;
541 st := TEAM_NONE;
542 pl := g_Player_Get(SpawnerUID);
543 if pl <> nil then
544 st := pl.Team;
546 h := High(gPlayers);
548 if h <> -1 then
549 for i := 0 to h do
550 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
551 with gPlayers[i] do
552 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
553 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
554 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
555 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
556 begin
557 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
558 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
559 else
560 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
561 if b then
562 gPlayers[i].BFGHit();
563 end;
565 //FIXME
566 g_Mons_ForEachAlive(monsCheck);
567 end;
569 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
570 var
571 find_id: DWord;
572 FramesID: DWORD = 0;
573 begin
574 if I < 0 then
575 find_id := FindShot()
576 else
577 begin
578 find_id := I;
579 if Integer(find_id) >= High(Shots) then
580 SetLength(Shots, find_id + 64)
581 end;
583 case ShotType of
584 WEAPON_ROCKETLAUNCHER:
585 begin
586 with Shots[find_id] do
587 begin
588 g_Obj_Init(@Obj);
590 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
591 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
593 Animation := nil;
594 Triggers := nil;
595 ShotType := WEAPON_ROCKETLAUNCHER;
596 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
597 end;
598 end;
600 WEAPON_PLASMA:
601 begin
602 with Shots[find_id] do
603 begin
604 g_Obj_Init(@Obj);
606 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
607 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
609 Triggers := nil;
610 ShotType := WEAPON_PLASMA;
611 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
612 Animation := TAnimation.Create(FramesID, True, 5);
613 end;
614 end;
616 WEAPON_BFG:
617 begin
618 with Shots[find_id] do
619 begin
620 g_Obj_Init(@Obj);
622 Obj.Rect.Width := SHOT_BFG_WIDTH;
623 Obj.Rect.Height := SHOT_BFG_HEIGHT;
625 Triggers := nil;
626 ShotType := WEAPON_BFG;
627 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
628 Animation := TAnimation.Create(FramesID, True, 6);
629 end;
630 end;
632 WEAPON_FLAMETHROWER:
633 begin
634 with Shots[find_id] do
635 begin
636 g_Obj_Init(@Obj);
638 Obj.Rect.Width := SHOT_FLAME_WIDTH;
639 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
641 Triggers := nil;
642 ShotType := WEAPON_FLAMETHROWER;
643 Animation := nil;
644 TextureID := 0;
645 g_Frames_Get(TextureID, 'FRAMES_FLAME');
646 end;
647 end;
649 WEAPON_IMP_FIRE:
650 begin
651 with Shots[find_id] do
652 begin
653 g_Obj_Init(@Obj);
655 Obj.Rect.Width := 16;
656 Obj.Rect.Height := 16;
658 Triggers := nil;
659 ShotType := WEAPON_IMP_FIRE;
660 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
661 Animation := TAnimation.Create(FramesID, True, 4);
662 end;
663 end;
665 WEAPON_CACO_FIRE:
666 begin
667 with Shots[find_id] do
668 begin
669 g_Obj_Init(@Obj);
671 Obj.Rect.Width := 16;
672 Obj.Rect.Height := 16;
674 Triggers := nil;
675 ShotType := WEAPON_CACO_FIRE;
676 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
677 Animation := TAnimation.Create(FramesID, True, 4);
678 end;
679 end;
681 WEAPON_MANCUB_FIRE:
682 begin
683 with Shots[find_id] do
684 begin
685 g_Obj_Init(@Obj);
687 Obj.Rect.Width := 32;
688 Obj.Rect.Height := 32;
690 Triggers := nil;
691 ShotType := WEAPON_MANCUB_FIRE;
692 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
693 Animation := TAnimation.Create(FramesID, True, 4);
694 end;
695 end;
697 WEAPON_BARON_FIRE:
698 begin
699 with Shots[find_id] do
700 begin
701 g_Obj_Init(@Obj);
703 Obj.Rect.Width := 32;
704 Obj.Rect.Height := 16;
706 Triggers := nil;
707 ShotType := WEAPON_BARON_FIRE;
708 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
709 Animation := TAnimation.Create(FramesID, True, 4);
710 end;
711 end;
713 WEAPON_BSP_FIRE:
714 begin
715 with Shots[find_id] do
716 begin
717 g_Obj_Init(@Obj);
719 Obj.Rect.Width := 16;
720 Obj.Rect.Height := 16;
722 Triggers := nil;
723 ShotType := WEAPON_BSP_FIRE;
724 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
725 Animation := TAnimation.Create(FramesID, True, 4);
726 end;
727 end;
729 WEAPON_SKEL_FIRE:
730 begin
731 with Shots[find_id] do
732 begin
733 g_Obj_Init(@Obj);
735 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
736 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
738 Triggers := nil;
739 ShotType := WEAPON_SKEL_FIRE;
740 target := TargetUID;
741 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
742 Animation := TAnimation.Create(FramesID, True, 5);
743 end;
744 end;
745 end;
747 Shots[find_id].Obj.X := X;
748 Shots[find_id].Obj.Y := Y;
749 Shots[find_id].Obj.Vel.X := XV;
750 Shots[find_id].Obj.Vel.Y := YV;
751 Shots[find_id].Obj.Accel.X := 0;
752 Shots[find_id].Obj.Accel.Y := 0;
753 Shots[find_id].SpawnerUID := Spawner;
754 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
755 Shots[find_id].Stopped := 255
756 else
757 Shots[find_id].Stopped := 0;
758 Result := find_id;
759 end;
761 procedure throw(i, x, y, xd, yd, s: Integer);
762 var
763 a: Integer;
764 begin
765 yd := yd - y;
766 xd := xd - x;
768 a := Max(Abs(xd), Abs(yd));
769 if a = 0 then
770 a := 1;
772 Shots[i].Obj.X := x;
773 Shots[i].Obj.Y := y;
774 Shots[i].Obj.Vel.X := (xd*s) div a;
775 Shots[i].Obj.Vel.Y := (yd*s) div a;
776 Shots[i].Obj.Accel.X := 0;
777 Shots[i].Obj.Accel.Y := 0;
778 Shots[i].Stopped := 0;
779 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
780 Shots[i].Timeout := 900 // ~25 sec
781 else
782 begin
783 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
784 Shots[i].Timeout := SHOT_FLAME_LIFETIME
785 else
786 Shots[i].Timeout := 550; // ~15 sec
787 end;
788 end;
790 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
791 var
792 i, h: Integer;
794 function PlayerHit(Team: Byte = 0): Boolean;
795 var
796 i: Integer;
797 ChkTeam: Boolean;
798 p: TPlayer;
799 begin
800 Result := False;
801 h := High(gPlayers);
803 if h <> -1 then
804 for i := 0 to h do
805 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
806 begin
807 ChkTeam := True;
808 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
809 begin
810 p := g_Player_Get(SpawnerUID);
811 if p <> nil then
812 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
813 end;
814 if ChkTeam then
815 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
816 begin
817 if t <> HIT_FLAME then
818 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
819 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
820 if t = HIT_BFG then
821 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
822 Result := True;
823 break;
824 end;
825 end;
826 end;
829 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
830 begin
831 result := false; // don't stop
832 if mon.Live and g_Obj_Collide(obj, @mon.Obj) then
833 begin
834 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
835 begin
836 if (t <> HIT_FLAME) then
837 begin
838 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
839 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
840 end;
841 result := True;
842 end;
843 end;
844 end;
847 function monsCheckHit (mon: TMonster): Boolean;
848 begin
849 result := false; // don't stop
850 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
851 begin
852 if (t <> HIT_FLAME) then
853 begin
854 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
855 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
856 end;
857 result := true;
858 end;
859 end;
861 function MonsterHit(): Boolean;
862 begin
863 //result := g_Mons_ForEach(monsCheckHit);
864 //FIXME: accelerate this!
865 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
866 end;
868 begin
869 Result := 0;
871 if HitCorpses then
872 begin
873 h := High(gCorpses);
875 if gAdvCorpses and (h <> -1) then
876 for i := 0 to h do
877 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
878 g_Obj_Collide(obj, @gCorpses[i].Obj) then
879 begin
880 // Ðàñïèëèâàåì òðóï:
881 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
882 (obj^.Vel.Y+obj^.Accel.Y) div 4);
883 Result := 1;
884 end;
885 end;
887 case gGameSettings.GameMode of
888 // Êàìïàíèÿ:
889 GM_COOP, GM_SINGLE:
890 begin
891 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
892 if MonsterHit() then
893 begin
894 Result := 2;
895 Exit;
896 end;
898 if PlayerHit() then
899 begin
900 Result := 1;
901 Exit;
902 end;
903 end;
905 // Äåçìàò÷:
906 GM_DM:
907 begin
908 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
909 if PlayerHit() then
910 begin
911 Result := 1;
912 Exit;
913 end;
915 if MonsterHit() then
916 begin
917 Result := 2;
918 Exit;
919 end;
920 end;
922 // Êîìàíäíûå:
923 GM_TDM, GM_CTF:
924 begin
925 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
926 if PlayerHit(2) then
927 begin
928 Result := 1;
929 Exit;
930 end;
932 // Ïîòîì ìîíñòðîâ
933 if MonsterHit() then
934 begin
935 Result := 2;
936 Exit;
937 end;
939 // È â êîíöå ñâîèõ èãðîêîâ
940 if PlayerHit(1) then
941 begin
942 Result := 1;
943 Exit;
944 end;
945 end;
947 end;
948 end;
950 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
951 begin
952 Result := False;
954 case g_GetUIDType(UID) of
955 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
956 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
957 else Exit;
958 end;
959 end;
961 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
962 var
963 r: Integer; // squared radius
965 function monsExCheck (mon: TMonster): Boolean;
966 var
967 dx, dy, mm: Integer;
968 begin
969 result := false; // don't stop
970 begin
971 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
972 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
974 if dx > 1000 then dx := 1000;
975 if dy > 1000 then dy := 1000;
977 if (dx*dx+dy*dy < r) then
978 begin
979 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
980 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
982 mm := Max(abs(dx), abs(dy));
983 if mm = 0 then mm := 1;
985 if mon.Live then
986 begin
987 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
988 end;
990 mon.Push((dx*7) div mm, (dy*7) div mm);
991 end;
992 end;
993 end;
995 var
996 i, h, dx, dy, m, mm: Integer;
997 _angle: SmallInt;
998 begin
999 result := false;
1001 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1003 r := rad*rad;
1005 h := High(gPlayers);
1007 if h <> -1 then
1008 for i := 0 to h do
1009 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1010 with gPlayers[i] do
1011 begin
1012 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1013 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1015 if dx > 1000 then dx := 1000;
1016 if dy > 1000 then dy := 1000;
1018 if dx*dx+dy*dy < r then
1019 begin
1020 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1021 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1023 mm := Max(abs(dx), abs(dy));
1024 if mm = 0 then mm := 1;
1026 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1027 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1028 end;
1029 end;
1031 //g_Mons_ForEach(monsExCheck);
1032 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1034 h := High(gCorpses);
1036 if gAdvCorpses and (h <> -1) then
1037 for i := 0 to h do
1038 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1039 with gCorpses[i] do
1040 begin
1041 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1042 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1044 if dx > 1000 then dx := 1000;
1045 if dy > 1000 then dy := 1000;
1047 if dx*dx+dy*dy < r then
1048 begin
1049 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1050 Obj.Rect.Width, Obj.Rect.Height);
1052 mm := Max(abs(dx), abs(dy));
1053 if mm = 0 then mm := 1;
1055 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1056 end;
1057 end;
1059 h := High(gGibs);
1061 if gAdvGibs and (h <> -1) then
1062 for i := 0 to h do
1063 if gGibs[i].Live then
1064 with gGibs[i] do
1065 begin
1066 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1067 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1069 if dx > 1000 then dx := 1000;
1070 if dy > 1000 then dy := 1000;
1072 if dx*dx+dy*dy < r then
1073 begin
1074 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1075 Obj.Rect.Width, Obj.Rect.Height);
1076 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1077 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1079 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1080 positionChanged(); // this updates spatial accelerators
1081 end;
1082 end;
1083 end;
1085 procedure g_Weapon_Init();
1086 begin
1087 CreateWaterMap();
1088 end;
1090 procedure g_Weapon_Free();
1091 var
1092 i: Integer;
1093 begin
1094 if Shots <> nil then
1095 begin
1096 for i := 0 to High(Shots) do
1097 if Shots[i].ShotType <> 0 then
1098 Shots[i].Animation.Free();
1100 Shots := nil;
1101 end;
1103 WaterMap := nil;
1104 end;
1106 procedure g_Weapon_LoadData();
1107 begin
1108 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1110 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1122 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1123 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1125 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1132 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1133 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1134 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1135 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1136 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1137 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1138 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1139 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1140 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1141 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1143 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1144 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1145 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1146 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1147 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1148 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1149 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1150 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1151 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1152 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1153 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1154 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1155 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1156 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1157 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1158 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1159 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1160 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1161 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1162 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1163 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1165 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1166 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1168 //wgunMonHash := hashNewIntInt();
1169 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeLess);
1170 end;
1172 procedure g_Weapon_FreeData();
1173 begin
1174 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1176 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1177 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1178 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1179 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1180 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1181 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1182 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1183 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1184 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1185 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1186 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1187 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1188 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1189 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1190 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1191 g_Sound_Delete('SOUND_FIRE');
1192 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1193 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1194 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1195 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1196 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1197 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1198 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1199 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1200 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1201 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1202 g_Sound_Delete('SOUND_PLAYER_JETON');
1203 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1204 g_Sound_Delete('SOUND_PLAYER_CASING1');
1205 g_Sound_Delete('SOUND_PLAYER_CASING2');
1206 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1207 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1209 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1210 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1211 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1212 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1213 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1214 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1215 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1216 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1217 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1218 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1219 g_Frames_DeleteByName('FRAMES_BFGHIT');
1220 g_Frames_DeleteByName('FRAMES_FIRE');
1221 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1222 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1223 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1224 g_Frames_DeleteByName('FRAMES_SMOKE');
1225 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1226 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1227 end;
1230 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1231 var
1232 i: Integer;
1233 begin
1234 result := false;
1235 for i := 0 to High(gPlayers) do
1236 begin
1237 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
1238 begin
1239 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1240 begin
1241 if AllowPush then gPlayers[i].Push(vx, vy);
1242 result := true;
1243 end;
1244 end;
1245 end;
1246 end;
1249 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1251 function monsCheck (mon: TMonster): Boolean;
1252 begin
1253 result := false; // don't stop
1254 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1255 begin
1256 if AllowPush then mon.Push(vx, vy);
1257 result := true;
1258 end;
1259 end;
1261 begin
1262 result := 0;
1263 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1264 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1265 end;
1268 (*
1269 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1270 var
1271 a: Integer;
1272 x2, y2: Integer;
1273 dx, dy: Integer;
1274 xe, ye: Integer;
1275 xi, yi: Integer;
1276 s, c: Extended;
1277 //vx, vy: Integer;
1278 xx, yy, d: Integer;
1279 i: Integer;
1280 t1, _collide: Boolean;
1281 w, h: Word;
1282 {$IF DEFINED(D2F_DEBUG)}
1283 stt: UInt64;
1284 showTime: Boolean = true;
1285 {$ENDIF}
1286 begin
1287 a := GetAngle(x, y, xd, yd)+180;
1289 SinCos(DegToRad(-a), s, c);
1291 if Abs(s) < 0.01 then s := 0;
1292 if Abs(c) < 0.01 then c := 0;
1294 x2 := x+Round(c*gMapInfo.Width);
1295 y2 := y+Round(s*gMapInfo.Width);
1297 t1 := gWalls <> nil;
1298 _collide := False;
1299 w := gMapInfo.Width;
1300 h := gMapInfo.Height;
1302 xe := 0;
1303 ye := 0;
1304 dx := x2-x;
1305 dy := y2-y;
1307 if (xd = 0) and (yd = 0) then Exit;
1309 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1310 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1312 dx := Abs(dx);
1313 dy := Abs(dy);
1315 if dx > dy then d := dx else d := dy;
1317 //blood vel, for Monster.Damage()
1318 //vx := (dx*10 div d)*xi;
1319 //vy := (dy*10 div d)*yi;
1321 {$IF DEFINED(D2F_DEBUG)}
1322 stt := curTimeMicro();
1323 {$ENDIF}
1325 xx := x;
1326 yy := y;
1328 for i := 1 to d do
1329 begin
1330 xe := xe+dx;
1331 ye := ye+dy;
1333 if xe > d then
1334 begin
1335 xe := xe-d;
1336 xx := xx+xi;
1337 end;
1339 if ye > d then
1340 begin
1341 ye := ye-d;
1342 yy := yy+yi;
1343 end;
1345 if (yy > h) or (yy < 0) then Break;
1346 if (xx > w) or (xx < 0) then Break;
1348 if t1 then
1349 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1350 begin
1351 _collide := True;
1352 {$IF DEFINED(D2F_DEBUG)}
1353 stt := curTimeMicro()-stt;
1354 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1355 showTime := false;
1356 {$ENDIF}
1357 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1358 if g_Game_IsServer and g_Game_IsNet then
1359 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1360 end;
1362 if not _collide then
1363 begin
1364 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1365 end;
1367 if _collide then Break;
1368 end;
1370 {$IF DEFINED(D2F_DEBUG)}
1371 if showTime then
1372 begin
1373 stt := curTimeMicro()-stt;
1374 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1375 end;
1376 {$ENDIF}
1378 if CheckTrigger and g_Game_IsServer then
1379 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1380 end;
1381 *)
1384 //!!!FIXME!!!
1385 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1386 var
1387 x0, y0: Integer;
1388 x2, y2: Integer;
1389 xi, yi: Integer;
1390 wallDistSq: Integer = $3fffffff;
1392 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1393 begin
1394 result := false;
1395 if (idx < 0) or (idx > High(gPlayers)) then exit;
1396 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1397 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1398 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1399 {$IF DEFINED(D2F_DEBUG)}
1400 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1401 {$ENDIF}
1402 end;
1404 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1405 begin
1406 result := false;
1407 if (mon = nil) then exit;
1408 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1409 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1410 {$IF DEFINED(D2F_DEBUG)}
1411 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1412 {$ENDIF}
1413 end;
1415 // collect players along hitray
1416 // return `true` if instant hit was detected
1417 function playerPossibleHit (): Boolean;
1418 var
1419 i: Integer;
1420 px, py, pw, ph: Integer;
1421 inx, iny: Integer;
1422 distSq: Integer;
1423 plr: TPlayer;
1424 begin
1425 result := false;
1426 for i := 0 to High(gPlayers) do
1427 begin
1428 plr := gPlayers[i];
1429 if (plr <> nil) and plr.Live then
1430 begin
1431 plr.getMapBox(px, py, pw, ph);
1432 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1433 begin
1434 distSq := distanceSq(x, y, inx, iny);
1435 if (distSq = 0) then
1436 begin
1437 // contains
1438 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1439 end
1440 else if (distSq < wallDistSq) then
1441 begin
1442 appendHitTimePlr(distSq, i, inx, iny);
1443 end;
1444 end;
1445 end;
1446 end;
1447 end;
1449 function sqchecker (mon: TMonster; tag: Integer): Boolean;
1450 var
1451 mx, my, mw, mh: Integer;
1452 inx, iny: Integer;
1453 distSq: Integer;
1454 begin
1455 result := false; // don't stop
1456 mon.getMapBox(mx, my, mw, mh);
1457 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1458 begin
1459 distSq := distanceSq(x0, y0, inx, iny);
1460 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1461 end;
1462 end;
1464 var
1465 a: Integer;
1466 dx, dy: Integer;
1467 xe, ye: Integer;
1468 s, c: Extended;
1469 i: Integer;
1470 wallHitFlag: Boolean = false;
1471 wallHitX: Integer = 0;
1472 wallHitY: Integer = 0;
1473 didHit: Boolean = false;
1474 {$IF DEFINED(D2F_DEBUG)}
1475 stt: UInt64;
1476 {$ENDIF}
1477 begin
1478 (*
1479 if not gwep_debug_fast_trace then
1480 begin
1481 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1482 exit;
1483 end;
1484 *)
1486 if (xd = 0) and (yd = 0) then exit;
1488 //wgunMonHash.reset(); //FIXME: clear hash on level change
1489 wgunHitHeap.clear();
1490 wgunHitTimeUsed := 0;
1492 a := GetAngle(x, y, xd, yd)+180;
1494 SinCos(DegToRad(-a), s, c);
1496 if Abs(s) < 0.01 then s := 0;
1497 if Abs(c) < 0.01 then c := 0;
1499 x0 := x;
1500 y0 := y;
1501 x2 := x+Round(c*gMapInfo.Width);
1502 y2 := y+Round(s*gMapInfo.Width);
1504 dx := x2-x;
1505 dy := y2-y;
1507 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1508 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1510 {$IF DEFINED(D2F_DEBUG)}
1511 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1512 stt := curTimeMicro();
1513 {$ENDIF}
1515 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1516 if wallHitFlag then
1517 begin
1518 x2 := wallHitX;
1519 y2 := wallHitY;
1520 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1521 end
1522 else
1523 begin
1524 wallHitX := x2;
1525 wallHitY := y2;
1526 end;
1528 if playerPossibleHit() then exit; // instant hit
1530 // collect monsters
1531 g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1533 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1534 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1535 while (wgunHitHeap.count > 0) do
1536 begin
1537 // has some entities to check, do it
1538 i := wgunHitHeap.front;
1539 wgunHitHeap.popFront();
1540 // hitpoint
1541 xe := wgunHitTime[i].x;
1542 ye := wgunHitTime[i].y;
1543 // check if it is not behind the wall
1544 if (wgunHitTime[i].mon <> nil) then
1545 begin
1546 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1547 end
1548 else
1549 begin
1550 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1551 end;
1552 if didHit then
1553 begin
1554 // need new coords for trigger
1555 wallHitX := xe;
1556 wallHitY := ye;
1557 wallHitFlag := false; // no sparks
1558 break;
1559 end;
1560 end;
1562 // need sparks?
1563 if wallHitFlag then
1564 begin
1565 {$IF DEFINED(D2F_DEBUG)}
1566 stt := curTimeMicro()-stt;
1567 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1568 {$ENDIF}
1569 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1570 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1571 end
1572 else
1573 begin
1574 {$IF DEFINED(D2F_DEBUG)}
1575 stt := curTimeMicro()-stt;
1576 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1577 {$ENDIF}
1578 end;
1580 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1581 end;
1584 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1585 var
1586 obj: TObj;
1587 begin
1588 obj.X := X;
1589 obj.Y := Y;
1590 obj.rect.X := 0;
1591 obj.rect.Y := 0;
1592 obj.rect.Width := 39;
1593 obj.rect.Height := 52;
1594 obj.Vel.X := 0;
1595 obj.Vel.Y := 0;
1596 obj.Accel.X := 0;
1597 obj.Accel.Y := 0;
1599 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1600 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1601 else
1602 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1603 end;
1605 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1606 var
1607 obj: TObj;
1608 begin
1609 obj.X := X;
1610 obj.Y := Y;
1611 obj.rect.X := 0;
1612 obj.rect.Y := 0;
1613 obj.rect.Width := 32;
1614 obj.rect.Height := 52;
1615 obj.Vel.X := 0;
1616 obj.Vel.Y := 0;
1617 obj.Accel.X := 0;
1618 obj.Accel.Y := 0;
1620 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1621 end;
1623 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1624 Silent: Boolean = False);
1625 var
1626 find_id: DWORD;
1627 dx, dy: Integer;
1628 begin
1629 if WID < 0 then
1630 find_id := FindShot()
1631 else
1632 begin
1633 find_id := WID;
1634 if Integer(find_id) >= High(Shots) then
1635 SetLength(Shots, find_id + 64)
1636 end;
1638 with Shots[find_id] do
1639 begin
1640 g_Obj_Init(@Obj);
1642 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1643 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1645 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1646 dy := -(Obj.Rect.Height div 2);
1648 ShotType := WEAPON_ROCKETLAUNCHER;
1649 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1651 Animation := nil;
1652 triggers := nil;
1653 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1654 end;
1656 Shots[find_id].SpawnerUID := SpawnerUID;
1658 if not Silent then
1659 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1660 end;
1662 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1663 WID: Integer = -1; Silent: Boolean = False);
1664 var
1665 find_id, FramesID: DWORD;
1666 dx, dy: Integer;
1667 begin
1668 if WID < 0 then
1669 find_id := FindShot()
1670 else
1671 begin
1672 find_id := WID;
1673 if Integer(find_id) >= High(Shots) then
1674 SetLength(Shots, find_id + 64)
1675 end;
1677 with Shots[find_id] do
1678 begin
1679 g_Obj_Init(@Obj);
1681 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1682 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1684 dx := -(Obj.Rect.Width div 2);
1685 dy := -(Obj.Rect.Height div 2);
1687 ShotType := WEAPON_SKEL_FIRE;
1688 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1690 triggers := nil;
1691 target := TargetUID;
1692 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1693 Animation := TAnimation.Create(FramesID, True, 5);
1694 end;
1696 Shots[find_id].SpawnerUID := SpawnerUID;
1698 if not Silent then
1699 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1700 end;
1702 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1703 Silent: Boolean = False);
1704 var
1705 find_id, FramesID: DWORD;
1706 dx, dy: Integer;
1707 begin
1708 if WID < 0 then
1709 find_id := FindShot()
1710 else
1711 begin
1712 find_id := WID;
1713 if Integer(find_id) >= High(Shots) then
1714 SetLength(Shots, find_id + 64);
1715 end;
1717 with Shots[find_id] do
1718 begin
1719 g_Obj_Init(@Obj);
1721 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1722 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1724 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1725 dy := -(Obj.Rect.Height div 2);
1727 ShotType := WEAPON_PLASMA;
1728 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1730 triggers := nil;
1731 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1732 Animation := TAnimation.Create(FramesID, True, 5);
1733 end;
1735 Shots[find_id].SpawnerUID := SpawnerUID;
1737 if not Silent then
1738 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1739 end;
1741 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1742 Silent: Boolean = False);
1743 var
1744 find_id: DWORD;
1745 dx, dy: Integer;
1746 begin
1747 if WID < 0 then
1748 find_id := FindShot()
1749 else
1750 begin
1751 find_id := WID;
1752 if Integer(find_id) >= High(Shots) then
1753 SetLength(Shots, find_id + 64);
1754 end;
1756 with Shots[find_id] do
1757 begin
1758 g_Obj_Init(@Obj);
1760 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1761 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1763 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1764 dy := -(Obj.Rect.Height div 2);
1766 ShotType := WEAPON_FLAMETHROWER;
1767 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1769 triggers := nil;
1770 Animation := nil;
1771 TextureID := 0;
1772 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1773 end;
1775 Shots[find_id].SpawnerUID := SpawnerUID;
1777 // if not Silent then
1778 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1779 end;
1781 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1782 Silent: Boolean = False);
1783 var
1784 find_id, FramesID: DWORD;
1785 dx, dy: Integer;
1786 begin
1787 if WID < 0 then
1788 find_id := FindShot()
1789 else
1790 begin
1791 find_id := WID;
1792 if Integer(find_id) >= High(Shots) then
1793 SetLength(Shots, find_id + 64)
1794 end;
1796 with Shots[find_id] do
1797 begin
1798 g_Obj_Init(@Obj);
1800 Obj.Rect.Width := 16;
1801 Obj.Rect.Height := 16;
1803 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1804 dy := -(Obj.Rect.Height div 2);
1806 ShotType := WEAPON_IMP_FIRE;
1807 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1809 triggers := nil;
1810 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1811 Animation := TAnimation.Create(FramesID, True, 4);
1812 end;
1814 Shots[find_id].SpawnerUID := SpawnerUID;
1816 if not Silent then
1817 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1818 end;
1820 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1821 Silent: Boolean = False);
1822 var
1823 find_id, FramesID: DWORD;
1824 dx, dy: Integer;
1825 begin
1826 if WID < 0 then
1827 find_id := FindShot()
1828 else
1829 begin
1830 find_id := WID;
1831 if Integer(find_id) >= High(Shots) then
1832 SetLength(Shots, find_id + 64)
1833 end;
1835 with Shots[find_id] do
1836 begin
1837 g_Obj_Init(@Obj);
1839 Obj.Rect.Width := 16;
1840 Obj.Rect.Height := 16;
1842 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1843 dy := -(Obj.Rect.Height div 2);
1845 ShotType := WEAPON_CACO_FIRE;
1846 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1848 triggers := nil;
1849 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1850 Animation := TAnimation.Create(FramesID, True, 4);
1851 end;
1853 Shots[find_id].SpawnerUID := SpawnerUID;
1855 if not Silent then
1856 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1857 end;
1859 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1860 Silent: Boolean = False);
1861 var
1862 find_id, FramesID: DWORD;
1863 dx, dy: Integer;
1864 begin
1865 if WID < 0 then
1866 find_id := FindShot()
1867 else
1868 begin
1869 find_id := WID;
1870 if Integer(find_id) >= High(Shots) then
1871 SetLength(Shots, find_id + 64)
1872 end;
1874 with Shots[find_id] do
1875 begin
1876 g_Obj_Init(@Obj);
1878 Obj.Rect.Width := 32;
1879 Obj.Rect.Height := 16;
1881 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1882 dy := -(Obj.Rect.Height div 2);
1884 ShotType := WEAPON_BARON_FIRE;
1885 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1887 triggers := nil;
1888 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1889 Animation := TAnimation.Create(FramesID, True, 4);
1890 end;
1892 Shots[find_id].SpawnerUID := SpawnerUID;
1894 if not Silent then
1895 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1896 end;
1898 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1899 Silent: Boolean = False);
1900 var
1901 find_id, FramesID: DWORD;
1902 dx, dy: Integer;
1903 begin
1904 if WID < 0 then
1905 find_id := FindShot()
1906 else
1907 begin
1908 find_id := WID;
1909 if Integer(find_id) >= High(Shots) then
1910 SetLength(Shots, find_id + 64)
1911 end;
1913 with Shots[find_id] do
1914 begin
1915 g_Obj_Init(@Obj);
1917 Obj.Rect.Width := 16;
1918 Obj.Rect.Height := 16;
1920 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1921 dy := -(Obj.Rect.Height div 2);
1923 ShotType := WEAPON_BSP_FIRE;
1924 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1926 triggers := nil;
1928 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1929 Animation := TAnimation.Create(FramesID, True, 4);
1930 end;
1932 Shots[find_id].SpawnerUID := SpawnerUID;
1934 if not Silent then
1935 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1936 end;
1938 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1939 Silent: Boolean = False);
1940 var
1941 find_id, FramesID: DWORD;
1942 dx, dy: Integer;
1943 begin
1944 if WID < 0 then
1945 find_id := FindShot()
1946 else
1947 begin
1948 find_id := WID;
1949 if Integer(find_id) >= High(Shots) then
1950 SetLength(Shots, find_id + 64)
1951 end;
1953 with Shots[find_id] do
1954 begin
1955 g_Obj_Init(@Obj);
1957 Obj.Rect.Width := 32;
1958 Obj.Rect.Height := 32;
1960 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1961 dy := -(Obj.Rect.Height div 2);
1963 ShotType := WEAPON_MANCUB_FIRE;
1964 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1966 triggers := nil;
1968 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1969 Animation := TAnimation.Create(FramesID, True, 4);
1970 end;
1972 Shots[find_id].SpawnerUID := SpawnerUID;
1974 if not Silent then
1975 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1976 end;
1978 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1979 Silent: Boolean = False);
1980 var
1981 find_id, FramesID: DWORD;
1982 dx, dy: Integer;
1983 begin
1984 if WID < 0 then
1985 find_id := FindShot()
1986 else
1987 begin
1988 find_id := WID;
1989 if Integer(find_id) >= High(Shots) then
1990 SetLength(Shots, find_id + 64)
1991 end;
1993 with Shots[find_id] do
1994 begin
1995 g_Obj_Init(@Obj);
1997 Obj.Rect.Width := SHOT_BFG_WIDTH;
1998 Obj.Rect.Height := SHOT_BFG_HEIGHT;
2000 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
2001 dy := -(Obj.Rect.Height div 2);
2003 ShotType := WEAPON_BFG;
2004 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2006 triggers := nil;
2007 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2008 Animation := TAnimation.Create(FramesID, True, 6);
2009 end;
2011 Shots[find_id].SpawnerUID := SpawnerUID;
2013 if not Silent then
2014 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2015 end;
2017 procedure g_Weapon_bfghit(x, y: Integer);
2018 var
2019 ID: DWORD;
2020 Anim: TAnimation;
2021 begin
2022 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2023 begin
2024 Anim := TAnimation.Create(ID, False, 4);
2025 g_GFX_OnceAnim(x-32, y-32, Anim);
2026 Anim.Free();
2027 end;
2028 end;
2030 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2031 Silent: Boolean = False);
2032 begin
2033 if not Silent then
2034 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2036 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2037 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2038 begin
2039 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2040 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2041 end;
2042 end;
2044 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2045 Silent: Boolean = False);
2046 begin
2047 if not Silent then
2048 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2050 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2051 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2052 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2053 begin
2054 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2055 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2056 end;
2057 end;
2059 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2060 Silent: Boolean = False);
2061 var
2062 i, j: Integer;
2063 begin
2064 if not Silent then
2065 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2067 for i := 0 to 9 do
2068 begin
2069 j := Random(17)-8; // -8 .. 8
2070 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2071 end;
2072 end;
2074 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2075 Silent: Boolean = False);
2076 var
2077 a, i, j: Integer;
2078 begin
2079 if not Silent then
2080 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2082 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2083 for i := 0 to a do
2084 begin
2085 j := Random(41)-20; // -20 .. 20
2086 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2087 end;
2088 end;
2090 procedure g_Weapon_Update();
2091 var
2092 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2093 _id: DWORD;
2094 Anim: TAnimation;
2095 t: DWArray;
2096 st: Word;
2097 s: String;
2098 o: TObj;
2099 spl: Boolean;
2100 Loud: Boolean;
2101 tcx, tcy: Integer;
2102 begin
2103 if Shots = nil then
2104 Exit;
2106 for i := 0 to High(Shots) do
2107 begin
2108 if Shots[i].ShotType = 0 then
2109 Continue;
2111 Loud := True;
2113 with Shots[i] do
2114 begin
2115 Timeout := Timeout - 1;
2116 oldvx := Obj.Vel.X;
2117 oldvy := Obj.Vel.Y;
2118 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2119 if (Stopped = 0) and g_Game_IsServer then
2120 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2121 SpawnerUID, ACTIVATE_SHOT, triggers)
2122 else
2123 t := nil;
2125 if t <> nil then
2126 begin
2127 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2128 if triggers = nil then
2129 triggers := t
2130 else
2131 begin
2132 h := High(t);
2134 for a := 0 to h do
2135 if not InDWArray(t[a], triggers) then
2136 begin
2137 SetLength(triggers, Length(triggers)+1);
2138 triggers[High(triggers)] := t[a];
2139 end;
2140 end;
2141 end;
2143 // Àíèìàöèÿ ñíàðÿäà:
2144 if Animation <> nil then
2145 Animation.Update();
2147 // Äâèæåíèå:
2148 spl := (ShotType <> WEAPON_PLASMA) and
2149 (ShotType <> WEAPON_BFG) and
2150 (ShotType <> WEAPON_BSP_FIRE) and
2151 (ShotType <> WEAPON_FLAMETHROWER);
2153 if Stopped = 0 then
2154 begin
2155 st := g_Obj_Move(@Obj, False, spl);
2156 end
2157 else
2158 begin
2159 st := 0;
2160 end;
2161 positionChanged(); // this updates spatial accelerators
2163 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2164 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2165 begin
2166 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2167 ShotType := 0;
2168 Animation.Free();
2169 Continue;
2170 end;
2172 cx := Obj.X + (Obj.Rect.Width div 2);
2173 cy := Obj.Y + (Obj.Rect.Height div 2);
2175 case ShotType of
2176 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2177 begin
2178 // Âûëåòåëà èç âîäû:
2179 if WordBool(st and MOVE_HITAIR) then
2180 g_Obj_SetSpeed(@Obj, 12);
2182 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2183 if WordBool(st and MOVE_INWATER) then
2184 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2185 Obj.Y+(Obj.Rect.Height div 2),
2186 1+Random(3), 16, 16)
2187 else
2188 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2189 begin
2190 Anim := TAnimation.Create(_id, False, 3);
2191 Anim.Alpha := 150;
2192 g_GFX_OnceAnim(Obj.X-14+Random(9),
2193 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2194 Anim, ONCEANIM_SMOKE);
2195 Anim.Free();
2196 end;
2198 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2199 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2200 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2201 (Timeout < 1) then
2202 begin
2203 Obj.Vel.X := 0;
2204 Obj.Vel.Y := 0;
2206 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2208 if ShotType = WEAPON_SKEL_FIRE then
2209 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2210 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2211 begin
2212 Anim := TAnimation.Create(TextureID, False, 8);
2213 Anim.Blending := False;
2214 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2215 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2216 Anim.Free();
2217 end;
2218 end
2219 else
2220 begin // Âçðûâ Ðàêåòû
2221 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2222 begin
2223 Anim := TAnimation.Create(TextureID, False, 6);
2224 Anim.Blending := False;
2225 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2226 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2227 Anim.Free();
2228 end;
2229 end;
2231 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2233 ShotType := 0;
2234 end;
2236 if ShotType = WEAPON_SKEL_FIRE then
2237 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2238 if GetPos(target, @o) then
2239 throw(i, Obj.X, Obj.Y,
2240 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2241 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2242 12);
2243 end;
2244 end;
2246 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2247 begin
2248 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2249 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2250 begin
2251 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2252 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2253 ShotType := 0;
2254 Continue;
2255 end;
2257 // Âåëè÷èíà óðîíà:
2258 if (ShotType = WEAPON_PLASMA) and
2259 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2260 a := 10
2261 else
2262 a := 5;
2264 if ShotType = WEAPON_BSP_FIRE then
2265 a := 10;
2267 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2268 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2269 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2270 (Timeout < 1) then
2271 begin
2272 if ShotType = WEAPON_PLASMA then
2273 s := 'FRAMES_EXPLODE_PLASMA'
2274 else
2275 s := 'FRAMES_EXPLODE_BSPFIRE';
2277 // Âçðûâ Ïëàçìû:
2278 if g_Frames_Get(TextureID, s) then
2279 begin
2280 Anim := TAnimation.Create(TextureID, False, 3);
2281 Anim.Blending := False;
2282 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2283 Anim.Free();
2284 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2285 end;
2287 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2289 ShotType := 0;
2290 end;
2291 end;
2293 WEAPON_FLAMETHROWER: // Îãíåìåò
2294 begin
2295 // Ñî âðåìåíåì óìèðàåò
2296 if (Timeout < 1) then
2297 begin
2298 ShotType := 0;
2299 Continue;
2300 end;
2301 // Ïîä âîäîé òîæå
2302 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2303 begin
2304 if WordBool(st and MOVE_HITWATER) then
2305 begin
2306 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2307 begin
2308 Anim := TAnimation.Create(_id, False, 3);
2309 Anim.Alpha := 0;
2310 tcx := Random(8);
2311 tcy := Random(8);
2312 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2313 cy-4+tcy-(Anim.Height div 2),
2314 Anim, ONCEANIM_SMOKE);
2315 Anim.Free();
2316 end;
2317 end
2318 else
2319 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2320 ShotType := 0;
2321 Continue;
2322 end;
2324 // Ãðàâèòàöèÿ
2325 if Stopped = 0 then
2326 Obj.Accel.Y := Obj.Accel.Y + 1;
2327 // Ïîïàëè â ñòåíó èëè â âîäó:
2328 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2329 begin
2330 // Ïðèëèïàåì:
2331 Obj.Vel.X := 0;
2332 Obj.Vel.Y := 0;
2333 Obj.Accel.Y := 0;
2334 if WordBool(st and MOVE_HITWALL) then
2335 Stopped := MOVE_HITWALL
2336 else if WordBool(st and MOVE_HITLAND) then
2337 Stopped := MOVE_HITLAND
2338 else if WordBool(st and MOVE_HITCEIL) then
2339 Stopped := MOVE_HITCEIL;
2340 end;
2342 a := IfThen(Stopped = 0, 3, 1);
2343 // Åñëè â êîãî-òî ïîïàëè
2344 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2345 begin
2346 // HIT_FLAME ñàì ïîäîææåò
2347 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2348 if Stopped = 0 then
2349 ShotType := 0;
2350 end;
2352 if Stopped = 0 then
2353 tf := 2
2354 else
2355 tf := 3;
2357 if (gTime mod LongWord(tf) = 0) then
2358 begin
2359 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2360 Anim.Alpha := 0;
2361 case Stopped of
2362 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2363 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2364 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2365 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2366 end;
2367 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2368 Anim.Free();
2369 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2370 end;
2371 end;
2373 WEAPON_BFG: // BFG
2374 begin
2375 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2376 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2377 begin
2378 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2379 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2380 ShotType := 0;
2381 Continue;
2382 end;
2384 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2385 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2386 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2387 (Timeout < 1) then
2388 begin
2389 // Ëó÷è BFG:
2390 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2392 // Âçðûâ BFG:
2393 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2394 begin
2395 Anim := TAnimation.Create(TextureID, False, 6);
2396 Anim.Blending := False;
2397 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2398 Anim.Free();
2399 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2400 end;
2402 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2404 ShotType := 0;
2405 end;
2406 end;
2408 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2409 begin
2410 // Âûëåòåë èç âîäû:
2411 if WordBool(st and MOVE_HITAIR) then
2412 g_Obj_SetSpeed(@Obj, 16);
2414 // Âåëè÷èíà óðîíà:
2415 if ShotType = WEAPON_IMP_FIRE then
2416 a := 5
2417 else
2418 if ShotType = WEAPON_CACO_FIRE then
2419 a := 20
2420 else
2421 a := 40;
2423 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2424 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2425 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2426 (Timeout < 1) then
2427 begin
2428 if ShotType = WEAPON_IMP_FIRE then
2429 s := 'FRAMES_EXPLODE_IMPFIRE'
2430 else
2431 if ShotType = WEAPON_CACO_FIRE then
2432 s := 'FRAMES_EXPLODE_CACOFIRE'
2433 else
2434 s := 'FRAMES_EXPLODE_BARONFIRE';
2436 // Âçðûâ:
2437 if g_Frames_Get(TextureID, s) then
2438 begin
2439 Anim := TAnimation.Create(TextureID, False, 6);
2440 Anim.Blending := False;
2441 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2442 Anim.Free();
2443 end;
2445 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2447 ShotType := 0;
2448 end;
2449 end;
2451 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2452 begin
2453 // Âûëåòåë èç âîäû:
2454 if WordBool(st and MOVE_HITAIR) then
2455 g_Obj_SetSpeed(@Obj, 16);
2457 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2458 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2459 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2460 (Timeout < 1) then
2461 begin
2462 // Âçðûâ:
2463 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2464 begin
2465 Anim := TAnimation.Create(TextureID, False, 6);
2466 Anim.Blending := False;
2467 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2468 Anim.Free();
2469 end;
2471 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2473 ShotType := 0;
2474 end;
2475 end;
2476 end; // case ShotType of...
2478 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2479 if (ShotType = 0) then
2480 begin
2481 if gGameSettings.GameType = GT_SERVER then
2482 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2483 if Animation <> nil then
2484 begin
2485 Animation.Free();
2486 Animation := nil;
2487 end;
2488 end
2489 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2490 if gGameSettings.GameType = GT_SERVER then
2491 MH_SEND_UpdateShot(i);
2492 end;
2493 end;
2494 end;
2496 procedure g_Weapon_Draw();
2497 var
2498 i: Integer;
2499 a: SmallInt;
2500 p: TDFPoint;
2501 begin
2502 if Shots = nil then
2503 Exit;
2505 for i := 0 to High(Shots) do
2506 if Shots[i].ShotType <> 0 then
2507 with Shots[i] do
2508 begin
2509 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2510 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2511 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2512 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2513 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2514 else
2515 a := 0;
2517 p.X := Obj.Rect.Width div 2;
2518 p.Y := Obj.Rect.Height div 2;
2520 if Animation <> nil then
2521 begin
2522 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2523 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2524 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2525 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2526 else
2527 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2528 end
2529 else if TextureID <> 0 then
2530 begin
2531 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2532 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2533 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2534 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2535 end;
2537 if g_debug_Frames then
2538 begin
2539 e_DrawQuad(Obj.X+Obj.Rect.X,
2540 Obj.Y+Obj.Rect.Y,
2541 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2542 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2543 0, 255, 0);
2544 end;
2545 end;
2546 end;
2548 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2549 var
2550 a: Integer;
2551 begin
2552 Result := False;
2554 if Shots = nil then
2555 Exit;
2557 for a := 0 to High(Shots) do
2558 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2559 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2560 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2561 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2562 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2563 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2564 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2565 begin
2566 Result := True;
2567 Exit;
2568 end;
2569 end;
2571 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2572 var
2573 count, i, j: Integer;
2574 dw: DWORD;
2575 begin
2576 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2577 count := 0;
2578 if Shots <> nil then
2579 for i := 0 to High(Shots) do
2580 if Shots[i].ShotType <> 0 then
2581 count := count + 1;
2583 Mem := TBinMemoryWriter.Create((count+1) * 80);
2585 // Êîëè÷åñòâî ñíàðÿäîâ:
2586 Mem.WriteInt(count);
2588 if count = 0 then
2589 Exit;
2591 for i := 0 to High(Shots) do
2592 if Shots[i].ShotType <> 0 then
2593 begin
2594 // Ñèãíàòóðà ñíàðÿäà:
2595 dw := SHOT_SIGNATURE; // 'SHOT'
2596 Mem.WriteDWORD(dw);
2597 // Òèï ñíàðÿäà:
2598 Mem.WriteByte(Shots[i].ShotType);
2599 // Öåëü:
2600 Mem.WriteWord(Shots[i].Target);
2601 // UID ñòðåëÿâøåãî:
2602 Mem.WriteWord(Shots[i].SpawnerUID);
2603 // Ðàçìåð ïîëÿ Triggers:
2604 dw := Length(Shots[i].Triggers);
2605 Mem.WriteDWORD(dw);
2606 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2607 for j := 0 to Integer(dw)-1 do
2608 Mem.WriteDWORD(Shots[i].Triggers[j]);
2609 // Îáúåêò ñíàðÿäà:
2610 Obj_SaveState(@Shots[i].Obj, Mem);
2611 // Êîñòûëèíà åáàíàÿ:
2612 Mem.WriteByte(Shots[i].Stopped);
2613 end;
2614 end;
2616 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2617 var
2618 count, i, j: Integer;
2619 dw: DWORD;
2620 begin
2621 if Mem = nil then
2622 Exit;
2624 // Êîëè÷åñòâî ñíàðÿäîâ:
2625 Mem.ReadInt(count);
2627 SetLength(Shots, count);
2629 if count = 0 then
2630 Exit;
2632 for i := 0 to count-1 do
2633 begin
2634 // Ñèãíàòóðà ñíàðÿäà:
2635 Mem.ReadDWORD(dw);
2636 if dw <> SHOT_SIGNATURE then // 'SHOT'
2637 begin
2638 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2639 end;
2640 // Òèï ñíàðÿäà:
2641 Mem.ReadByte(Shots[i].ShotType);
2642 // Öåëü:
2643 Mem.ReadWord(Shots[i].Target);
2644 // UID ñòðåëÿâøåãî:
2645 Mem.ReadWord(Shots[i].SpawnerUID);
2646 // Ðàçìåð ïîëÿ Triggers:
2647 Mem.ReadDWORD(dw);
2648 SetLength(Shots[i].Triggers, dw);
2649 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2650 for j := 0 to Integer(dw)-1 do
2651 Mem.ReadDWORD(Shots[i].Triggers[j]);
2652 // Îáúåêò ïðåäìåòà:
2653 Obj_LoadState(@Shots[i].Obj, Mem);
2654 // Êîñòûëèíà åáàíàÿ:
2655 Mem.ReadByte(Shots[i].Stopped);
2657 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2658 Shots[i].TextureID := DWORD(-1);
2659 Shots[i].Animation := nil;
2661 case Shots[i].ShotType of
2662 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2663 begin
2664 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2665 end;
2666 WEAPON_PLASMA:
2667 begin
2668 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2669 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2670 end;
2671 WEAPON_BFG:
2672 begin
2673 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2674 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2675 end;
2676 WEAPON_IMP_FIRE:
2677 begin
2678 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2679 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2680 end;
2681 WEAPON_BSP_FIRE:
2682 begin
2683 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2684 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2685 end;
2686 WEAPON_CACO_FIRE:
2687 begin
2688 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2689 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2690 end;
2691 WEAPON_BARON_FIRE:
2692 begin
2693 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2694 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2695 end;
2696 WEAPON_MANCUB_FIRE:
2697 begin
2698 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2699 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2700 end;
2701 end;
2702 end;
2703 end;
2705 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2706 var
2707 cx, cy: Integer;
2708 Anim: TAnimation;
2709 s: string;
2710 begin
2711 if Shots = nil then
2712 Exit;
2713 if (I > High(Shots)) or (I < 0) then Exit;
2715 with Shots[I] do
2716 begin
2717 if ShotType = 0 then Exit;
2718 Obj.X := X;
2719 Obj.Y := Y;
2720 cx := Obj.X + (Obj.Rect.Width div 2);
2721 cy := Obj.Y + (Obj.Rect.Height div 2);
2723 case ShotType of
2724 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2725 begin
2726 if Loud then
2727 begin
2728 if ShotType = WEAPON_SKEL_FIRE then
2729 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2730 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2731 begin
2732 Anim := TAnimation.Create(TextureID, False, 8);
2733 Anim.Blending := False;
2734 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2735 Anim.Free();
2736 end;
2737 end
2738 else
2739 begin // Âçðûâ Ðàêåòû
2740 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2741 begin
2742 Anim := TAnimation.Create(TextureID, False, 6);
2743 Anim.Blending := False;
2744 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2745 Anim.Free();
2746 end;
2747 end;
2748 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2749 end;
2750 end;
2752 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2753 begin
2754 if ShotType = WEAPON_PLASMA then
2755 s := 'FRAMES_EXPLODE_PLASMA'
2756 else
2757 s := 'FRAMES_EXPLODE_BSPFIRE';
2759 if g_Frames_Get(TextureID, s) and loud then
2760 begin
2761 Anim := TAnimation.Create(TextureID, False, 3);
2762 Anim.Blending := False;
2763 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2764 Anim.Free();
2766 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2767 end;
2768 end;
2770 WEAPON_BFG: // BFG
2771 begin
2772 // Âçðûâ BFG:
2773 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2774 begin
2775 Anim := TAnimation.Create(TextureID, False, 6);
2776 Anim.Blending := False;
2777 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2778 Anim.Free();
2780 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2781 end;
2782 end;
2784 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2785 begin
2786 if ShotType = WEAPON_IMP_FIRE then
2787 s := 'FRAMES_EXPLODE_IMPFIRE'
2788 else
2789 if ShotType = WEAPON_CACO_FIRE then
2790 s := 'FRAMES_EXPLODE_CACOFIRE'
2791 else
2792 s := 'FRAMES_EXPLODE_BARONFIRE';
2794 if g_Frames_Get(TextureID, s) and Loud then
2795 begin
2796 Anim := TAnimation.Create(TextureID, False, 6);
2797 Anim.Blending := False;
2798 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2799 Anim.Free();
2801 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2802 end;
2803 end;
2805 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2806 begin
2807 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2808 begin
2809 Anim := TAnimation.Create(TextureID, False, 6);
2810 Anim.Blending := False;
2811 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2812 Anim.Free();
2814 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2815 end;
2816 end;
2817 end; // case ShotType of...
2819 ShotType := 0;
2820 Animation.Free();
2821 end;
2822 end;
2825 procedure g_Weapon_AddDynLights();
2826 var
2827 i: Integer;
2828 begin
2829 if Shots = nil then Exit;
2830 for i := 0 to High(Shots) do
2831 begin
2832 if Shots[i].ShotType = 0 then continue;
2833 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2834 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2835 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2836 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2837 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2838 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2839 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2840 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2841 (Shots[i].ShotType = WEAPON_PLASMA) or
2842 (Shots[i].ShotType = WEAPON_BFG) or
2843 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2844 false then
2845 begin
2846 if (Shots[i].ShotType = WEAPON_PLASMA) then
2847 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)
2848 else if (Shots[i].ShotType = WEAPON_BFG) then
2849 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
2850 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2851 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
2852 else
2853 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);
2854 end;
2855 end;
2856 end;
2859 procedure TShot.positionChanged (); begin end;
2862 end.