DEADSOFTWARE

make turret explosions ignore the turret trigger
[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 {$MODE DELPHI}
17 unit g_weapons;
19 interface
21 uses
22 g_textures, g_basic, e_graphics, g_phys, BinEditor;
24 const
25 HIT_SOME = 0;
26 HIT_ROCKET = 1;
27 HIT_BFG = 2;
28 HIT_TRAP = 3;
29 HIT_FALL = 4;
30 HIT_WATER = 5;
31 HIT_ACID = 6;
32 HIT_ELECTRO = 7;
33 HIT_FLAME = 8;
34 HIT_SELF = 9;
35 HIT_DISCON = 10;
37 type
38 TShot = record
39 ShotType: Byte;
40 Target: Word;
41 SpawnerUID: Word;
42 Triggers: DWArray;
43 Obj: TObj;
44 Animation: TAnimation;
45 TextureID: DWORD;
46 Timeout: DWORD;
47 end;
49 var
50 Shots: array of TShot = nil;
51 LastShotID: Integer = 0;
53 procedure g_Weapon_LoadData();
54 procedure g_Weapon_FreeData();
55 procedure g_Weapon_Init();
56 procedure g_Weapon_Free();
57 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
58 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
59 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
61 procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
62 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
63 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
64 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
65 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
66 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
67 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
68 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
69 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
70 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
71 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_bfghit(x, y: Integer);
74 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
75 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
76 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
77 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
79 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word; IgnoreTrigger: Integer = -1): Boolean;
80 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
81 procedure g_Weapon_Update();
82 procedure g_Weapon_Draw();
83 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
84 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
86 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
87 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
89 const
90 WEAPON_KASTET = 0;
91 WEAPON_SAW = 1;
92 WEAPON_PISTOL = 2;
93 WEAPON_SHOTGUN1 = 3;
94 WEAPON_SHOTGUN2 = 4;
95 WEAPON_CHAINGUN = 5;
96 WEAPON_ROCKETLAUNCHER = 6;
97 WEAPON_PLASMA = 7;
98 WEAPON_BFG = 8;
99 WEAPON_SUPERPULEMET = 9;
100 WEAPON_MEGAKASTET = 10;
101 WEAPON_ZOMBY_PISTOL = 20;
102 WEAPON_IMP_FIRE = 21;
103 WEAPON_BSP_FIRE = 22;
104 WEAPON_CACO_FIRE = 23;
105 WEAPON_BARON_FIRE = 24;
106 WEAPON_MANCUB_FIRE = 25;
107 WEAPON_SKEL_FIRE = 26;
109 implementation
111 uses
112 Math, g_map, g_player, g_gfx, g_sound, g_main,
113 g_console, SysUtils, g_options, g_game,
114 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
115 g_language, g_netmsg;
117 type
118 TWaterPanel = record
119 X, Y: Integer;
120 Width, Height: Word;
121 Active: Boolean;
122 end;
124 const
125 SHOT_ROCKETLAUNCHER_WIDTH = 27;
126 SHOT_ROCKETLAUNCHER_HEIGHT = 12;
128 SHOT_SKELFIRE_WIDTH = 32;
129 SHOT_SKELFIRE_HEIGHT = 16;
131 SHOT_PLASMA_WIDTH = 16;
132 SHOT_PLASMA_HEIGHT = 16;
134 SHOT_BFG_WIDTH = 32;
135 SHOT_BFG_HEIGHT = 32;
136 SHOT_BFG_DAMAGE = 100;
137 SHOT_BFG_RADIUS = 256;
139 SHOT_SIGNATURE = $544F4853; // 'SHOT'
141 var
142 WaterMap: array of array of DWORD = nil;
144 function FindShot(): DWORD;
145 var
146 i: Integer;
147 begin
148 if Shots <> nil then
149 for i := 0 to High(Shots) do
150 if Shots[i].ShotType = 0 then
151 begin
152 Result := i;
153 LastShotID := Result;
154 Exit;
155 end;
157 if Shots = nil then
158 begin
159 SetLength(Shots, 128);
160 Result := 0;
161 end
162 else
163 begin
164 Result := High(Shots) + 1;
165 SetLength(Shots, Length(Shots) + 128);
166 end;
167 LastShotID := Result;
168 end;
170 procedure CreateWaterMap();
171 var
172 WaterArray: Array of TWaterPanel;
173 a, b, c, m: Integer;
174 ok: Boolean;
175 begin
176 if gWater = nil then
177 Exit;
179 SetLength(WaterArray, Length(gWater));
181 for a := 0 to High(gWater) do
182 begin
183 WaterArray[a].X := gWater[a].X;
184 WaterArray[a].Y := gWater[a].Y;
185 WaterArray[a].Width := gWater[a].Width;
186 WaterArray[a].Height := gWater[a].Height;
187 WaterArray[a].Active := True;
188 end;
190 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
192 for a := 0 to High(WaterArray) do
193 if WaterArray[a].Active then
194 begin
195 WaterArray[a].Active := False;
196 m := Length(WaterMap);
197 SetLength(WaterMap, m+1);
198 SetLength(WaterMap[m], 1);
199 WaterMap[m][0] := a;
200 ok := True;
202 while ok do
203 begin
204 ok := False;
205 for b := 0 to High(WaterArray) do
206 if WaterArray[b].Active then
207 for c := 0 to High(WaterMap[m]) do
208 if g_CollideAround(WaterArray[b].X,
209 WaterArray[b].Y,
210 WaterArray[b].Width,
211 WaterArray[b].Height,
212 WaterArray[WaterMap[m][c]].X,
213 WaterArray[WaterMap[m][c]].Y,
214 WaterArray[WaterMap[m][c]].Width,
215 WaterArray[WaterMap[m][c]].Height) then
216 begin
217 WaterArray[b].Active := False;
218 SetLength(WaterMap[m],
219 Length(WaterMap[m])+1);
220 WaterMap[m][High(WaterMap[m])] := b;
221 ok := True;
222 Break;
223 end;
224 end;
226 g_Game_StepLoading();
227 end;
229 WaterArray := nil;
230 end;
232 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
233 var
234 a, b, c, d, i1, i2: Integer;
235 pl, mn: WArray;
236 begin
237 if (gWater = nil) or (WaterMap = nil) then Exit;
239 i1 := -1;
240 i2 := -1;
242 SetLength(pl, 1024);
243 SetLength(mn, 1024);
244 for d := 0 to 1023 do pl[d] := $FFFF;
245 for d := 0 to 1023 do mn[d] := $FFFF;
247 for a := 0 to High(WaterMap) do
248 for b := 0 to High(WaterMap[a]) do
249 begin
250 if not g_Obj_Collide(gWater[WaterMap[a][b]].X, gWater[WaterMap[a][b]].Y,
251 gWater[WaterMap[a][b]].Width, gWater[WaterMap[a][b]].Height,
252 @Shots[ID].Obj) then Continue;
254 for c := 0 to High(WaterMap[a]) do
255 begin
256 if gPlayers <> nil then
257 begin
258 for d := 0 to High(gPlayers) do
259 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
260 if gPlayers[d].Collide(gWater[WaterMap[a][c]]) then
261 if not InWArray(d, pl) then
262 if i1 < 1023 then
263 begin
264 i1 := i1+1;
265 pl[i1] := d;
266 end;
267 end;
269 if gMonsters <> nil then
270 begin
271 for d := 0 to High(gMonsters) do
272 if (gMonsters[d] <> nil) and (gMonsters[d].Live) then
273 if gMonsters[d].Collide(gWater[WaterMap[a][c]]) then
274 if not InWArray(d, mn) then
275 if i2 < 1023 then
276 begin
277 i2 := i2+1;
278 mn[i2] := d;
279 end;
280 end;
281 end;
283 if i1 <> -1 then
284 for d := 0 to i1 do
285 gPlayers[pl[d]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
287 if i2 <> -1 then
288 for d := 0 to i2 do
289 gMonsters[mn[d]].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
290 end;
292 pl := nil;
293 mn := nil;
294 end;
296 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
297 var
298 tt, mt: Byte;
299 mon: TMonster;
300 begin
301 Result := False;
303 tt := g_GetUIDType(SpawnerUID);
304 if tt = UID_MONSTER then
305 begin
306 mon := g_Monsters_Get(SpawnerUID);
307 if mon <> nil then
308 mt := g_Monsters_Get(SpawnerUID).MonsterType
309 else
310 mt := 0;
311 end
312 else
313 mt := 0;
315 if m = nil then Exit;
316 if m.UID = SpawnerUID then
317 begin
318 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
319 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
320 Exit;
321 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
322 if (m.MonsterType = MONSTER_CYBER) or
323 (m.MonsterType = MONSTER_BARREL) then
324 begin
325 Result := True;
326 Exit;
327 end;
328 end;
330 if tt = UID_MONSTER then
331 begin
332 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
333 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
334 Exit;
336 // Îáà ìîíñòðà îäíîãî âèäà:
337 if mt = m.MonsterType then
338 case mt of
339 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
340 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
341 Exit; // Ýòè íå áüþò ñâîèõ
342 end;
343 end;
345 if g_Game_IsServer then
346 Result := m.Damage(d, vx, vy, SpawnerUID, t)
347 else
348 Result := True;
349 end;
351 function HitPlayer(p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
352 begin
353 Result := False;
355 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
356 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
357 Exit;
359 if g_Game_IsServer then p.Damage(d, SpawnerUID, vx, vy, t);
361 Result := True;
362 end;
364 function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer;
365 SpawnerUID: Word; AllowPush: Boolean): Byte;
366 var
367 i, h: Integer;
368 begin
369 Result := 0;
371 h := High(gPlayers);
373 if h <> -1 then
374 for i := 0 to h do
375 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
376 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
377 begin
378 if AllowPush then gPlayers[i].Push(vx, vy);
379 Result := 1;
380 end;
382 if Result <> 0 then Exit;
384 h := High(gMonsters);
386 if h <> -1 then
387 for i := 0 to h do
388 if (gMonsters[i] <> nil) and gMonsters[i].Live and gMonsters[i].Collide(X, Y) then
389 if HitMonster(gMonsters[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
390 begin
391 if AllowPush then gMonsters[i].Push(vx, vy);
392 Result := 2;
393 Exit;
394 end;
395 end;
397 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
398 var
399 i, h: Integer;
400 st: Byte;
401 pl: TPlayer;
402 b: Boolean;
403 begin
404 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
406 h := High(gCorpses);
408 if gAdvCorpses and (h <> -1) then
409 for i := 0 to h do
410 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
411 with gCorpses[i] do
412 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
413 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
414 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
415 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
416 begin
417 Damage(50, 0, 0);
418 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
419 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
420 end;
422 st := TEAM_NONE;
423 pl := g_Player_Get(SpawnerUID);
424 if pl <> nil then
425 st := pl.Team;
427 h := High(gPlayers);
429 if h <> -1 then
430 for i := 0 to h do
431 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
432 with gPlayers[i] do
433 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
434 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
435 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
436 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
437 begin
438 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
439 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
440 else
441 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
442 if b then
443 gPlayers[i].BFGHit();
444 end;
446 h := High(gMonsters);
448 if h <> -1 then
449 for i := 0 to h do
450 if (gMonsters[i] <> nil) and (gMonsters[i].Live) and (gMonsters[i].UID <> SpawnerUID) then
451 with gMonsters[i] do
452 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
453 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
454 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
455 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
456 if HitMonster(gMonsters[i], 50, 0, 0, SpawnerUID, HIT_SOME) then gMonsters[i].BFGHit();
457 end;
459 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
460 var
461 find_id, FramesID: DWORD;
462 begin
463 if I < 0 then
464 find_id := FindShot()
465 else
466 begin
467 find_id := I;
468 if Integer(find_id) >= High(Shots) then
469 SetLength(Shots, find_id + 64)
470 end;
472 case ShotType of
473 WEAPON_ROCKETLAUNCHER:
474 begin
475 with Shots[find_id] do
476 begin
477 g_Obj_Init(@Obj);
479 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
480 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
482 Animation := nil;
483 Triggers := nil;
484 ShotType := WEAPON_ROCKETLAUNCHER;
485 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
486 end;
487 end;
489 WEAPON_PLASMA:
490 begin
491 with Shots[find_id] do
492 begin
493 g_Obj_Init(@Obj);
495 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
496 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
498 Triggers := nil;
499 ShotType := WEAPON_PLASMA;
500 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
501 Animation := TAnimation.Create(FramesID, True, 5);
502 end;
503 end;
505 WEAPON_BFG:
506 begin
507 with Shots[find_id] do
508 begin
509 g_Obj_Init(@Obj);
511 Obj.Rect.Width := SHOT_BFG_WIDTH;
512 Obj.Rect.Height := SHOT_BFG_HEIGHT;
514 Triggers := nil;
515 ShotType := WEAPON_BFG;
516 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
517 Animation := TAnimation.Create(FramesID, True, 6);
518 end;
519 end;
521 WEAPON_IMP_FIRE:
522 begin
523 with Shots[find_id] do
524 begin
525 g_Obj_Init(@Obj);
527 Obj.Rect.Width := 16;
528 Obj.Rect.Height := 16;
530 Triggers := nil;
531 ShotType := WEAPON_IMP_FIRE;
532 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
533 Animation := TAnimation.Create(FramesID, True, 4);
534 end;
535 end;
537 WEAPON_CACO_FIRE:
538 begin
539 with Shots[find_id] do
540 begin
541 g_Obj_Init(@Obj);
543 Obj.Rect.Width := 16;
544 Obj.Rect.Height := 16;
546 Triggers := nil;
547 ShotType := WEAPON_CACO_FIRE;
548 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
549 Animation := TAnimation.Create(FramesID, True, 4);
550 end;
551 end;
553 WEAPON_MANCUB_FIRE:
554 begin
555 with Shots[find_id] do
556 begin
557 g_Obj_Init(@Obj);
559 Obj.Rect.Width := 32;
560 Obj.Rect.Height := 32;
562 Triggers := nil;
563 ShotType := WEAPON_MANCUB_FIRE;
564 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
565 Animation := TAnimation.Create(FramesID, True, 4);
566 end;
567 end;
569 WEAPON_BARON_FIRE:
570 begin
571 with Shots[find_id] do
572 begin
573 g_Obj_Init(@Obj);
575 Obj.Rect.Width := 32;
576 Obj.Rect.Height := 16;
578 Triggers := nil;
579 ShotType := WEAPON_BARON_FIRE;
580 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
581 Animation := TAnimation.Create(FramesID, True, 4);
582 end;
583 end;
585 WEAPON_BSP_FIRE:
586 begin
587 with Shots[find_id] do
588 begin
589 g_Obj_Init(@Obj);
591 Obj.Rect.Width := 16;
592 Obj.Rect.Height := 16;
594 Triggers := nil;
595 ShotType := WEAPON_BSP_FIRE;
596 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
597 Animation := TAnimation.Create(FramesID, True, 4);
598 end;
599 end;
601 WEAPON_SKEL_FIRE:
602 begin
603 with Shots[find_id] do
604 begin
605 g_Obj_Init(@Obj);
607 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
608 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
610 Triggers := nil;
611 ShotType := WEAPON_SKEL_FIRE;
612 target := TargetUID;
613 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
614 Animation := TAnimation.Create(FramesID, True, 5);
615 end;
616 end;
617 end;
619 Shots[find_id].Obj.X := X;
620 Shots[find_id].Obj.Y := Y;
621 Shots[find_id].Obj.Vel.X := XV;
622 Shots[find_id].Obj.Vel.Y := YV;
623 Shots[find_id].Obj.Accel.X := 0;
624 Shots[find_id].Obj.Accel.Y := 0;
625 Shots[find_id].SpawnerUID := Spawner;
626 Result := find_id;
627 end;
629 procedure throw(i, x, y, xd, yd, s: Integer);
630 var
631 a: Integer;
632 begin
633 yd := yd - y;
634 xd := xd - x;
636 a := Max(Abs(xd), Abs(yd));
637 if a = 0 then
638 a := 1;
640 Shots[i].Obj.X := x;
641 Shots[i].Obj.Y := y;
642 Shots[i].Obj.Vel.X := (xd*s) div a;
643 Shots[i].Obj.Vel.Y := (yd*s) div a;
644 Shots[i].Obj.Accel.X := 0;
645 Shots[i].Obj.Accel.Y := 0;
646 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
647 Shots[i].Timeout := 900 // ~25 sec
648 else
649 Shots[i].Timeout := 550 // ~15 sec
650 end;
652 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
653 var
654 i, h: Integer;
656 function PlayerHit(Team: Byte = 0): Boolean;
657 var
658 i: Integer;
659 ChkTeam: Boolean;
660 p: TPlayer;
661 begin
662 Result := False;
663 h := High(gPlayers);
665 if h <> -1 then
666 for i := 0 to h do
667 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
668 begin
669 ChkTeam := True;
670 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
671 begin
672 p := g_Player_Get(SpawnerUID);
673 if p <> nil then
674 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
675 end;
676 if ChkTeam then
677 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
678 begin
679 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
680 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
681 if t = HIT_BFG then
682 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
683 Result := True;
684 break;
685 end;
686 end;
687 end;
688 function MonsterHit(): Boolean;
689 var
690 i: Integer;
691 begin
692 Result := False;
693 h := High(gMonsters);
695 if h <> -1 then
696 for i := 0 to h do
697 if (gMonsters[i] <> nil) and gMonsters[i].Live and g_Obj_Collide(obj, @gMonsters[i].Obj) then
698 if HitMonster(gMonsters[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
699 begin
700 gMonsters[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
701 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
702 Result := True;
703 break;
704 end;
705 end;
706 begin
707 Result := 0;
709 if HitCorpses then
710 begin
711 h := High(gCorpses);
713 if gAdvCorpses and (h <> -1) then
714 for i := 0 to h do
715 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
716 g_Obj_Collide(obj, @gCorpses[i].Obj) then
717 begin
718 // Ðàñïèëèâàåì òðóï:
719 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
720 (obj^.Vel.Y+obj^.Accel.Y) div 4);
721 Result := 1;
722 end;
723 end;
725 case gGameSettings.GameMode of
726 // Êàìïàíèÿ:
727 GM_COOP, GM_SINGLE:
728 begin
729 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
730 if MonsterHit() then
731 begin
732 Result := 2;
733 Exit;
734 end;
736 if PlayerHit() then
737 begin
738 Result := 1;
739 Exit;
740 end;
741 end;
743 // Äåçìàò÷:
744 GM_DM:
745 begin
746 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
747 if PlayerHit() then
748 begin
749 Result := 1;
750 Exit;
751 end;
753 if MonsterHit() then
754 begin
755 Result := 2;
756 Exit;
757 end;
758 end;
760 // Êîìàíäíûå:
761 GM_TDM, GM_CTF:
762 begin
763 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
764 if PlayerHit(2) then
765 begin
766 Result := 1;
767 Exit;
768 end;
770 // Ïîòîì ìîíñòðîâ
771 if MonsterHit() then
772 begin
773 Result := 2;
774 Exit;
775 end;
777 // È â êîíöå ñâîèõ èãðîêîâ
778 if PlayerHit(1) then
779 begin
780 Result := 1;
781 Exit;
782 end;
783 end;
785 end;
786 end;
788 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
789 begin
790 Result := False;
792 case g_GetUIDType(UID) of
793 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
794 UID_MONSTER: Result := HitMonster(g_Monsters_Get(UID), d, 0, 0, SpawnerUID, t);
795 else Exit;
796 end;
797 end;
799 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word; IgnoreTrigger: Integer = -1): Boolean;
800 var
801 i, h, r, dx, dy, m, mm: Integer;
802 _angle: SmallInt;
803 begin
804 Result := False;
806 // don't let turret explosions use the turret
807 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT, IgnoreTrigger);
809 r := rad*rad;
811 h := High(gPlayers);
813 if h <> -1 then
814 for i := 0 to h do
815 if (gPlayers[i] <> nil) and gPlayers[i].Live then
816 with gPlayers[i] do
817 begin
818 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
819 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
821 if dx > 1000 then dx := 1000;
822 if dy > 1000 then dy := 1000;
824 if dx*dx+dy*dy < r then
825 begin
826 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
827 // PLAYER_RECT.Width, PLAYER_RECT.Height);
829 mm := Max(abs(dx), abs(dy));
830 if mm = 0 then mm := 1;
832 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
833 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
834 end;
835 end;
837 h := High(gMonsters);
839 if h <> -1 then
840 for i := 0 to h do
841 if gMonsters[i] <> nil then
842 with gMonsters[i] do
843 begin
844 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
845 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
847 if dx > 1000 then dx := 1000;
848 if dy > 1000 then dy := 1000;
850 if dx*dx+dy*dy < r then
851 begin
852 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
853 // Obj.Rect.Width, Obj.Rect.Height);
855 mm := Max(abs(dx), abs(dy));
856 if mm = 0 then mm := 1;
858 if gMonsters[i].Live then
859 HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad,
860 0, 0, SpawnerUID, HIT_ROCKET);
862 gMonsters[i].Push((dx*7) div mm, (dy*7) div mm);
863 end;
864 end;
866 h := High(gCorpses);
868 if gAdvCorpses and (h <> -1) then
869 for i := 0 to h do
870 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
871 with gCorpses[i] do
872 begin
873 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
874 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
876 if dx > 1000 then dx := 1000;
877 if dy > 1000 then dy := 1000;
879 if dx*dx+dy*dy < r then
880 begin
881 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
882 Obj.Rect.Width, Obj.Rect.Height);
884 mm := Max(abs(dx), abs(dy));
885 if mm = 0 then mm := 1;
887 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
888 end;
889 end;
891 h := High(gGibs);
893 if gAdvGibs and (h <> -1) then
894 for i := 0 to h do
895 if gGibs[i].Live then
896 with gGibs[i] do
897 begin
898 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
899 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
901 if dx > 1000 then dx := 1000;
902 if dy > 1000 then dy := 1000;
904 if dx*dx+dy*dy < r then
905 begin
906 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
907 Obj.Rect.Width, Obj.Rect.Height);
908 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
909 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
911 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
912 end;
913 end;
914 end;
916 procedure g_Weapon_Init();
917 begin
918 CreateWaterMap();
919 end;
921 procedure g_Weapon_Free();
922 var
923 i: Integer;
924 begin
925 if Shots <> nil then
926 begin
927 for i := 0 to High(Shots) do
928 if Shots[i].ShotType <> 0 then
929 Shots[i].Animation.Free();
931 Shots := nil;
932 end;
934 WaterMap := nil;
935 end;
937 procedure g_Weapon_LoadData();
938 begin
939 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
941 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
942 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
943 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
944 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
945 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
946 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
947 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
948 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
949 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
950 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
951 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
952 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
953 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
954 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
955 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
956 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
957 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
958 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
959 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
960 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
961 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
962 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
963 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
964 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
965 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
966 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
967 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
968 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
969 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
970 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
971 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
972 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
974 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
975 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
976 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
977 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
978 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
979 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
980 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
981 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
982 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
983 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
984 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
985 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
986 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
987 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
988 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
989 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
990 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
991 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
992 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
993 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
995 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
996 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
997 end;
999 procedure g_Weapon_FreeData();
1000 begin
1001 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1003 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1004 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1005 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1006 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1007 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1008 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1009 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1010 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1011 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1012 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1013 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1014 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1015 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1016 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1017 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1018 g_Sound_Delete('SOUND_FIRE');
1019 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1020 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1021 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1022 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1023 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1024 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1025 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1026 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1027 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1028 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1029 g_Sound_Delete('SOUND_PLAYER_JETON');
1030 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1031 g_Sound_Delete('SOUND_PLAYER_CASING1');
1032 g_Sound_Delete('SOUND_PLAYER_CASING2');
1033 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1034 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1036 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1037 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1038 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1039 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1040 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1041 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1042 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1043 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1044 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1045 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1046 g_Frames_DeleteByName('FRAMES_BFGHIT');
1047 g_Frames_DeleteByName('FRAMES_FIRE');
1048 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1049 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1050 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1051 g_Frames_DeleteByName('FRAMES_SMOKE');
1052 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1053 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1054 end;
1056 procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1057 var
1058 a: Integer;
1059 x2, y2: Integer;
1060 dx, dy: Integer;
1061 xe, ye: Integer;
1062 xi, yi: Integer;
1063 s, c: Extended;
1064 //vx, vy: Integer;
1065 xx, yy, d: Integer;
1067 i: Integer;
1068 t1, _collide: Boolean;
1069 w, h: Word;
1070 begin
1071 a := GetAngle(x, y, xd, yd)+180;
1073 SinCos(DegToRad(-a), s, c);
1075 if Abs(s) < 0.01 then s := 0;
1076 if Abs(c) < 0.01 then c := 0;
1078 x2 := x+Round(c*gMapInfo.Width);
1079 y2 := y+Round(s*gMapInfo.Width);
1081 t1 := gWalls <> nil;
1082 _collide := False;
1083 w := gMapInfo.Width;
1084 h := gMapInfo.Height;
1086 xe := 0;
1087 ye := 0;
1088 dx := x2-x;
1089 dy := y2-y;
1091 if (xd = 0) and (yd = 0) then Exit;
1093 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1094 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1096 dx := Abs(dx);
1097 dy := Abs(dy);
1099 if dx > dy then d := dx else d := dy;
1101 //blood vel, for Monster.Damage()
1102 //vx := (dx*10 div d)*xi;
1103 //vy := (dy*10 div d)*yi;
1105 xx := x;
1106 yy := y;
1108 for i := 1 to d do
1109 begin
1110 xe := xe+dx;
1111 ye := ye+dy;
1113 if xe > d then
1114 begin
1115 xe := xe-d;
1116 xx := xx+xi;
1117 end;
1119 if ye > d then
1120 begin
1121 ye := ye-d;
1122 yy := yy+yi;
1123 end;
1125 if (yy > h) or (yy < 0) then Break;
1126 if (xx > w) or (xx < 0) then Break;
1128 if t1 then
1129 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1130 begin
1131 _collide := True;
1132 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1133 if g_Game_IsServer and g_Game_IsNet then
1134 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1135 end;
1137 if not _collide then
1138 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1140 if _collide then
1141 Break;
1142 end;
1144 if CheckTrigger and g_Game_IsServer then
1145 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1146 end;
1148 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1149 var
1150 obj: TObj;
1151 begin
1152 obj.X := X;
1153 obj.Y := Y;
1154 obj.rect.X := 0;
1155 obj.rect.Y := 0;
1156 obj.rect.Width := 39;
1157 obj.rect.Height := 52;
1158 obj.Vel.X := 0;
1159 obj.Vel.Y := 0;
1160 obj.Accel.X := 0;
1161 obj.Accel.Y := 0;
1163 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1164 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1165 else
1166 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1167 end;
1169 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1170 var
1171 obj: TObj;
1172 begin
1173 obj.X := X;
1174 obj.Y := Y;
1175 obj.rect.X := 0;
1176 obj.rect.Y := 0;
1177 obj.rect.Width := 32;
1178 obj.rect.Height := 52;
1179 obj.Vel.X := 0;
1180 obj.Vel.Y := 0;
1181 obj.Accel.X := 0;
1182 obj.Accel.Y := 0;
1184 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1185 end;
1187 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1188 Silent: Boolean = False);
1189 var
1190 find_id: DWORD;
1191 dx, dy: Integer;
1192 begin
1193 if WID < 0 then
1194 find_id := FindShot()
1195 else
1196 begin
1197 find_id := WID;
1198 if Integer(find_id) >= High(Shots) then
1199 SetLength(Shots, find_id + 64)
1200 end;
1202 with Shots[find_id] do
1203 begin
1204 g_Obj_Init(@Obj);
1206 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1207 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1209 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1210 dy := -(Obj.Rect.Height div 2);
1211 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1213 Animation := nil;
1214 triggers := nil;
1215 ShotType := WEAPON_ROCKETLAUNCHER;
1216 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1217 end;
1219 Shots[find_id].SpawnerUID := SpawnerUID;
1221 if not Silent then
1222 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1223 end;
1225 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1226 WID: Integer = -1; Silent: Boolean = False);
1227 var
1228 find_id, FramesID: DWORD;
1229 dx, dy: Integer;
1230 begin
1231 if WID < 0 then
1232 find_id := FindShot()
1233 else
1234 begin
1235 find_id := WID;
1236 if Integer(find_id) >= High(Shots) then
1237 SetLength(Shots, find_id + 64)
1238 end;
1240 with Shots[find_id] do
1241 begin
1242 g_Obj_Init(@Obj);
1244 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1245 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1247 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1248 dy := -(Obj.Rect.Height div 2);
1249 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1251 triggers := nil;
1252 ShotType := WEAPON_SKEL_FIRE;
1253 target := TargetUID;
1254 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1255 Animation := TAnimation.Create(FramesID, True, 5);
1256 end;
1258 Shots[find_id].SpawnerUID := SpawnerUID;
1260 if not Silent then
1261 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1262 end;
1264 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1265 Silent: Boolean = False);
1266 var
1267 find_id, FramesID: DWORD;
1268 dx, dy: Integer;
1269 begin
1270 if WID < 0 then
1271 find_id := FindShot()
1272 else
1273 begin
1274 find_id := WID;
1275 if Integer(find_id) >= High(Shots) then
1276 SetLength(Shots, find_id + 64);
1277 end;
1279 with Shots[find_id] do
1280 begin
1281 g_Obj_Init(@Obj);
1283 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1284 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1286 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1287 dy := -(Obj.Rect.Height div 2);
1288 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1290 triggers := nil;
1291 ShotType := WEAPON_PLASMA;
1292 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1293 Animation := TAnimation.Create(FramesID, True, 5);
1294 end;
1296 Shots[find_id].SpawnerUID := SpawnerUID;
1298 if not Silent then
1299 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1300 end;
1302 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1303 Silent: Boolean = False);
1304 var
1305 find_id, FramesID: DWORD;
1306 dx, dy: Integer;
1307 begin
1308 if WID < 0 then
1309 find_id := FindShot()
1310 else
1311 begin
1312 find_id := WID;
1313 if Integer(find_id) >= High(Shots) then
1314 SetLength(Shots, find_id + 64)
1315 end;
1317 with Shots[find_id] do
1318 begin
1319 g_Obj_Init(@Obj);
1321 Obj.Rect.Width := 16;
1322 Obj.Rect.Height := 16;
1324 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1325 dy := -(Obj.Rect.Height div 2);
1326 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1328 triggers := nil;
1329 ShotType := WEAPON_IMP_FIRE;
1330 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1331 Animation := TAnimation.Create(FramesID, True, 4);
1332 end;
1334 Shots[find_id].SpawnerUID := SpawnerUID;
1336 if not Silent then
1337 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1338 end;
1340 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1341 Silent: Boolean = False);
1342 var
1343 find_id, FramesID: DWORD;
1344 dx, dy: Integer;
1345 begin
1346 if WID < 0 then
1347 find_id := FindShot()
1348 else
1349 begin
1350 find_id := WID;
1351 if Integer(find_id) >= High(Shots) then
1352 SetLength(Shots, find_id + 64)
1353 end;
1355 with Shots[find_id] do
1356 begin
1357 g_Obj_Init(@Obj);
1359 Obj.Rect.Width := 16;
1360 Obj.Rect.Height := 16;
1362 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1363 dy := -(Obj.Rect.Height div 2);
1364 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1366 triggers := nil;
1367 ShotType := WEAPON_CACO_FIRE;
1368 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1369 Animation := TAnimation.Create(FramesID, True, 4);
1370 end;
1372 Shots[find_id].SpawnerUID := SpawnerUID;
1374 if not Silent then
1375 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1376 end;
1378 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1379 Silent: Boolean = False);
1380 var
1381 find_id, FramesID: DWORD;
1382 dx, dy: Integer;
1383 begin
1384 if WID < 0 then
1385 find_id := FindShot()
1386 else
1387 begin
1388 find_id := WID;
1389 if Integer(find_id) >= High(Shots) then
1390 SetLength(Shots, find_id + 64)
1391 end;
1393 with Shots[find_id] do
1394 begin
1395 g_Obj_Init(@Obj);
1397 Obj.Rect.Width := 32;
1398 Obj.Rect.Height := 16;
1400 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1401 dy := -(Obj.Rect.Height div 2);
1402 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1404 triggers := nil;
1405 ShotType := WEAPON_BARON_FIRE;
1406 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1407 Animation := TAnimation.Create(FramesID, True, 4);
1408 end;
1410 Shots[find_id].SpawnerUID := SpawnerUID;
1412 if not Silent then
1413 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1414 end;
1416 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1417 Silent: Boolean = False);
1418 var
1419 find_id, FramesID: DWORD;
1420 dx, dy: Integer;
1421 begin
1422 if WID < 0 then
1423 find_id := FindShot()
1424 else
1425 begin
1426 find_id := WID;
1427 if Integer(find_id) >= High(Shots) then
1428 SetLength(Shots, find_id + 64)
1429 end;
1431 with Shots[find_id] do
1432 begin
1433 g_Obj_Init(@Obj);
1435 Obj.Rect.Width := 16;
1436 Obj.Rect.Height := 16;
1438 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1439 dy := -(Obj.Rect.Height div 2);
1440 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1442 triggers := nil;
1443 ShotType := WEAPON_BSP_FIRE;
1444 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1445 Animation := TAnimation.Create(FramesID, True, 4);
1446 end;
1448 Shots[find_id].SpawnerUID := SpawnerUID;
1450 if not Silent then
1451 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1452 end;
1454 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1455 Silent: Boolean = False);
1456 var
1457 find_id, FramesID: DWORD;
1458 dx, dy: Integer;
1459 begin
1460 if WID < 0 then
1461 find_id := FindShot()
1462 else
1463 begin
1464 find_id := WID;
1465 if Integer(find_id) >= High(Shots) then
1466 SetLength(Shots, find_id + 64)
1467 end;
1469 with Shots[find_id] do
1470 begin
1471 g_Obj_Init(@Obj);
1473 Obj.Rect.Width := 32;
1474 Obj.Rect.Height := 32;
1476 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1477 dy := -(Obj.Rect.Height div 2);
1478 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1480 triggers := nil;
1481 ShotType := WEAPON_MANCUB_FIRE;
1482 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1483 Animation := TAnimation.Create(FramesID, True, 4);
1484 end;
1486 Shots[find_id].SpawnerUID := SpawnerUID;
1488 if not Silent then
1489 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1490 end;
1492 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1493 Silent: Boolean = False);
1494 var
1495 find_id, FramesID: DWORD;
1496 dx, dy: Integer;
1497 begin
1498 if WID < 0 then
1499 find_id := FindShot()
1500 else
1501 begin
1502 find_id := WID;
1503 if Integer(find_id) >= High(Shots) then
1504 SetLength(Shots, find_id + 64)
1505 end;
1507 with Shots[find_id] do
1508 begin
1509 g_Obj_Init(@Obj);
1511 Obj.Rect.Width := SHOT_BFG_WIDTH;
1512 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1514 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1515 dy := -(Obj.Rect.Height div 2);
1516 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1518 triggers := nil;
1519 ShotType := WEAPON_BFG;
1520 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
1521 Animation := TAnimation.Create(FramesID, True, 6);
1522 end;
1524 Shots[find_id].SpawnerUID := SpawnerUID;
1526 if not Silent then
1527 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
1528 end;
1530 procedure g_Weapon_bfghit(x, y: Integer);
1531 var
1532 ID: DWORD;
1533 Anim: TAnimation;
1534 begin
1535 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1536 begin
1537 Anim := TAnimation.Create(ID, False, 4);
1538 g_GFX_OnceAnim(x-32, y-32, Anim);
1539 Anim.Free();
1540 end;
1541 end;
1543 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
1544 Silent: Boolean = False);
1545 begin
1546 if not Silent then
1547 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
1549 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1550 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
1551 begin
1552 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
1553 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1554 end;
1555 end;
1557 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1558 Silent: Boolean = False);
1559 begin
1560 if not Silent then
1561 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
1563 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1564 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
1565 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1566 begin
1567 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
1568 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1569 end;
1570 end;
1572 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1573 Silent: Boolean = False);
1574 var
1575 i, j: Integer;
1576 begin
1577 if not Silent then
1578 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
1580 for i := 0 to 9 do
1581 begin
1582 j := Random(17)-8; // -8 .. 8
1583 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
1584 end;
1585 end;
1587 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1588 Silent: Boolean = False);
1589 var
1590 a, i, j: Integer;
1591 begin
1592 if not Silent then
1593 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
1595 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
1596 for i := 0 to a do
1597 begin
1598 j := Random(41)-20; // -20 .. 20
1599 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
1600 end;
1601 end;
1603 procedure g_Weapon_Update();
1604 var
1605 i, a, h, cx, cy, oldvx, oldvy: Integer;
1606 _id: DWORD;
1607 Anim: TAnimation;
1608 t: DWArray;
1609 st: Word;
1610 s: String;
1611 o: TObj;
1612 spl: Boolean;
1613 Loud: Boolean;
1614 begin
1615 if Shots = nil then
1616 Exit;
1618 for i := 0 to High(Shots) do
1619 begin
1620 if Shots[i].ShotType = 0 then
1621 Continue;
1623 Loud := True;
1625 with Shots[i] do
1626 begin
1627 Timeout := Timeout - 1;
1628 oldvx := Obj.Vel.X;
1629 oldvy := Obj.Vel.Y;
1630 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
1631 if g_Game_IsServer then
1632 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
1633 SpawnerUID, ACTIVATE_SHOT, triggers)
1634 else
1635 t := nil;
1637 if t <> nil then
1638 begin
1639 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
1640 if triggers = nil then
1641 triggers := t
1642 else
1643 begin
1644 h := High(t);
1646 for a := 0 to h do
1647 if not InDWArray(t[a], triggers) then
1648 begin
1649 SetLength(triggers, Length(triggers)+1);
1650 triggers[High(triggers)] := t[a];
1651 end;
1652 end;
1653 end;
1655 // Àíèìàöèÿ ñíàðÿäà:
1656 if Animation <> nil then
1657 Animation.Update();
1659 // Äâèæåíèå:
1660 spl := (ShotType <> WEAPON_PLASMA) and
1661 (ShotType <> WEAPON_BFG) and
1662 (ShotType <> WEAPON_BSP_FIRE);
1664 st := g_Obj_Move(@Obj, False, spl);
1666 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
1667 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
1668 begin
1669 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
1670 ShotType := 0;
1671 Animation.Free();
1672 Continue;
1673 end;
1675 cx := Obj.X + (Obj.Rect.Width div 2);
1676 cy := Obj.Y + (Obj.Rect.Height div 2);
1678 case ShotType of
1679 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
1680 begin
1681 // Âûëåòåëà èç âîäû:
1682 if WordBool(st and MOVE_HITAIR) then
1683 g_Obj_SetSpeed(@Obj, 12);
1685 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
1686 if WordBool(st and MOVE_INWATER) then
1687 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
1688 Obj.Y+(Obj.Rect.Height div 2),
1689 1+Random(3), 16, 16)
1690 else
1691 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
1692 begin
1693 Anim := TAnimation.Create(_id, False, 3);
1694 Anim.Alpha := 150;
1695 g_GFX_OnceAnim(Obj.X-8+Random(9),
1696 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
1697 Anim, ONCEANIM_SMOKE);
1698 Anim.Free();
1699 end;
1701 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1702 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1703 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
1704 (Timeout < 1) then
1705 begin
1706 Obj.Vel.X := 0;
1707 Obj.Vel.Y := 0;
1709 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
1711 if ShotType = WEAPON_SKEL_FIRE then
1712 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
1713 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
1714 begin
1715 Anim := TAnimation.Create(TextureID, False, 8);
1716 Anim.Blending := False;
1717 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
1718 Anim.Free();
1719 end;
1720 end
1721 else
1722 begin // Âçðûâ Ðàêåòû
1723 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1724 begin
1725 Anim := TAnimation.Create(TextureID, False, 6);
1726 Anim.Blending := False;
1727 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1728 Anim.Free();
1729 end;
1730 end;
1732 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
1734 ShotType := 0;
1735 end;
1737 if ShotType = WEAPON_SKEL_FIRE then
1738 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
1739 if GetPos(target, @o) then
1740 throw(i, Obj.X, Obj.Y,
1741 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
1742 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
1743 12);
1744 end;
1745 end;
1747 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
1748 begin
1749 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1750 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1751 begin
1752 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
1753 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
1754 ShotType := 0;
1755 Continue;
1756 end;
1758 // Âåëè÷èíà óðîíà:
1759 if (ShotType = WEAPON_PLASMA) and
1760 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1761 a := 10
1762 else
1763 a := 5;
1765 if ShotType = WEAPON_BSP_FIRE then
1766 a := 10;
1768 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1769 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1770 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
1771 (Timeout < 1) then
1772 begin
1773 if ShotType = WEAPON_PLASMA then
1774 s := 'FRAMES_EXPLODE_PLASMA'
1775 else
1776 s := 'FRAMES_EXPLODE_BSPFIRE';
1778 // Âçðûâ Ïëàçìû:
1779 if g_Frames_Get(TextureID, s) then
1780 begin
1781 Anim := TAnimation.Create(TextureID, False, 3);
1782 Anim.Blending := False;
1783 g_GFX_OnceAnim(cx-16, cy-16, Anim);
1784 Anim.Free();
1785 end;
1787 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
1789 ShotType := 0;
1790 end;
1791 end;
1793 WEAPON_BFG: // BFG
1794 begin
1795 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1796 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1797 begin
1798 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
1799 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
1800 ShotType := 0;
1801 Continue;
1802 end;
1804 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1805 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1806 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
1807 (Timeout < 1) then
1808 begin
1809 // Ëó÷è BFG:
1810 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
1812 // Âçðûâ BFG:
1813 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
1814 begin
1815 Anim := TAnimation.Create(TextureID, False, 6);
1816 Anim.Blending := False;
1817 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1818 Anim.Free();
1819 end;
1821 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
1823 ShotType := 0;
1824 end;
1825 end;
1827 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
1828 begin
1829 // Âûëåòåë èç âîäû:
1830 if WordBool(st and MOVE_HITAIR) then
1831 g_Obj_SetSpeed(@Obj, 16);
1833 // Âåëè÷èíà óðîíà:
1834 if ShotType = WEAPON_IMP_FIRE then
1835 a := 5
1836 else
1837 if ShotType = WEAPON_CACO_FIRE then
1838 a := 20
1839 else
1840 a := 40;
1842 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1843 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1844 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
1845 (Timeout < 1) then
1846 begin
1847 if ShotType = WEAPON_IMP_FIRE then
1848 s := 'FRAMES_EXPLODE_IMPFIRE'
1849 else
1850 if ShotType = WEAPON_CACO_FIRE then
1851 s := 'FRAMES_EXPLODE_CACOFIRE'
1852 else
1853 s := 'FRAMES_EXPLODE_BARONFIRE';
1855 // Âçðûâ:
1856 if g_Frames_Get(TextureID, s) then
1857 begin
1858 Anim := TAnimation.Create(TextureID, False, 6);
1859 Anim.Blending := False;
1860 g_GFX_OnceAnim(cx-32, cy-32, Anim);
1861 Anim.Free();
1862 end;
1864 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1866 ShotType := 0;
1867 end;
1868 end;
1870 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
1871 begin
1872 // Âûëåòåë èç âîäû:
1873 if WordBool(st and MOVE_HITAIR) then
1874 g_Obj_SetSpeed(@Obj, 16);
1876 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1877 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1878 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
1879 (Timeout < 1) then
1880 begin
1881 // Âçðûâ:
1882 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1883 begin
1884 Anim := TAnimation.Create(TextureID, False, 6);
1885 Anim.Blending := False;
1886 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1887 Anim.Free();
1888 end;
1890 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1892 ShotType := 0;
1893 end;
1894 end;
1895 end; // case ShotType of...
1897 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
1898 if (ShotType = 0) then
1899 begin
1900 if gGameSettings.GameType = GT_SERVER then
1901 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
1902 Animation.Free();
1903 Animation := nil;
1904 end
1905 else if (oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y) then
1906 if gGameSettings.GameType = GT_SERVER then
1907 MH_SEND_UpdateShot(i);
1908 end;
1909 end;
1910 end;
1912 procedure g_Weapon_Draw();
1913 var
1914 i: Integer;
1915 a: SmallInt;
1916 p: TPoint;
1917 begin
1918 if Shots = nil then
1919 Exit;
1921 for i := 0 to High(Shots) do
1922 if Shots[i].ShotType <> 0 then
1923 with Shots[i] do
1924 begin
1925 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
1926 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1927 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1928 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1929 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
1930 else
1931 a := 0;
1933 p.X := Obj.Rect.Width div 2;
1934 p.Y := Obj.Rect.Height div 2;
1936 if Animation <> nil then
1937 begin
1938 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1939 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1940 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1941 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
1942 else
1943 Animation.Draw(Obj.X, Obj.Y, M_NONE);
1944 end
1945 else
1946 begin
1947 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
1948 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
1949 else
1950 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
1951 end;
1953 if g_debug_Frames then
1954 begin
1955 e_DrawQuad(Obj.X+Obj.Rect.X,
1956 Obj.Y+Obj.Rect.Y,
1957 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
1958 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
1959 0, 255, 0);
1960 end;
1961 end;
1962 end;
1964 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
1965 var
1966 a: Integer;
1967 begin
1968 Result := False;
1970 if Shots = nil then
1971 Exit;
1973 for a := 0 to High(Shots) do
1974 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
1975 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
1976 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
1977 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
1978 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
1979 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
1980 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
1981 begin
1982 Result := True;
1983 Exit;
1984 end;
1985 end;
1987 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
1988 var
1989 count, i, j: Integer;
1990 dw: DWORD;
1991 begin
1992 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
1993 count := 0;
1994 if Shots <> nil then
1995 for i := 0 to High(Shots) do
1996 if Shots[i].ShotType <> 0 then
1997 count := count + 1;
1999 Mem := TBinMemoryWriter.Create((count+1) * 80);
2001 // Êîëè÷åñòâî ñíàðÿäîâ:
2002 Mem.WriteInt(count);
2004 if count = 0 then
2005 Exit;
2007 for i := 0 to High(Shots) do
2008 if Shots[i].ShotType <> 0 then
2009 begin
2010 // Ñèãíàòóðà ñíàðÿäà:
2011 dw := SHOT_SIGNATURE; // 'SHOT'
2012 Mem.WriteDWORD(dw);
2013 // Òèï ñíàðÿäà:
2014 Mem.WriteByte(Shots[i].ShotType);
2015 // Öåëü:
2016 Mem.WriteWord(Shots[i].Target);
2017 // UID ñòðåëÿâøåãî:
2018 Mem.WriteWord(Shots[i].SpawnerUID);
2019 // Ðàçìåð ïîëÿ Triggers:
2020 dw := Length(Shots[i].Triggers);
2021 Mem.WriteDWORD(dw);
2022 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2023 for j := 0 to Integer(dw)-1 do
2024 Mem.WriteDWORD(Shots[i].Triggers[j]);
2025 // Îáúåêò ñíàðÿäà:
2026 Obj_SaveState(@Shots[i].Obj, Mem);
2027 end;
2028 end;
2030 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2031 var
2032 count, i, j: Integer;
2033 dw: DWORD;
2034 begin
2035 if Mem = nil then
2036 Exit;
2038 // Êîëè÷åñòâî ñíàðÿäîâ:
2039 Mem.ReadInt(count);
2041 SetLength(Shots, count);
2043 if count = 0 then
2044 Exit;
2046 for i := 0 to count-1 do
2047 begin
2048 // Ñèãíàòóðà ñíàðÿäà:
2049 Mem.ReadDWORD(dw);
2050 if dw <> SHOT_SIGNATURE then // 'SHOT'
2051 begin
2052 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2053 end;
2054 // Òèï ñíàðÿäà:
2055 Mem.ReadByte(Shots[i].ShotType);
2056 // Öåëü:
2057 Mem.ReadWord(Shots[i].Target);
2058 // UID ñòðåëÿâøåãî:
2059 Mem.ReadWord(Shots[i].SpawnerUID);
2060 // Ðàçìåð ïîëÿ Triggers:
2061 Mem.ReadDWORD(dw);
2062 SetLength(Shots[i].Triggers, dw);
2063 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2064 for j := 0 to Integer(dw)-1 do
2065 Mem.ReadDWORD(Shots[i].Triggers[j]);
2066 // Îáúåêò ïðåäìåòà:
2067 Obj_LoadState(@Shots[i].Obj, Mem);
2069 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2070 Shots[i].TextureID := DWORD(-1);
2071 Shots[i].Animation := nil;
2073 case Shots[i].ShotType of
2074 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2075 begin
2076 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2077 end;
2078 WEAPON_PLASMA:
2079 begin
2080 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2081 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2082 end;
2083 WEAPON_BFG:
2084 begin
2085 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2086 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2087 end;
2088 WEAPON_IMP_FIRE:
2089 begin
2090 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2091 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2092 end;
2093 WEAPON_BSP_FIRE:
2094 begin
2095 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2096 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2097 end;
2098 WEAPON_CACO_FIRE:
2099 begin
2100 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2101 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2102 end;
2103 WEAPON_BARON_FIRE:
2104 begin
2105 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2106 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2107 end;
2108 WEAPON_MANCUB_FIRE:
2109 begin
2110 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2111 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2112 end;
2113 end;
2114 end;
2115 end;
2117 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2118 var
2119 cx, cy: Integer;
2120 Anim: TAnimation;
2121 s: string;
2122 begin
2123 if Shots = nil then
2124 Exit;
2125 if (I > High(Shots)) or (I < 0) then Exit;
2127 with Shots[I] do
2128 begin
2129 if ShotType = 0 then Exit;
2130 Obj.X := X;
2131 Obj.Y := Y;
2132 cx := Obj.X + (Obj.Rect.Width div 2);
2133 cy := Obj.Y + (Obj.Rect.Height div 2);
2135 case ShotType of
2136 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2137 begin
2138 if Loud then
2139 begin
2140 if ShotType = WEAPON_SKEL_FIRE then
2141 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2142 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2143 begin
2144 Anim := TAnimation.Create(TextureID, False, 8);
2145 Anim.Blending := False;
2146 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2147 Anim.Free();
2148 end;
2149 end
2150 else
2151 begin // Âçðûâ Ðàêåòû
2152 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2153 begin
2154 Anim := TAnimation.Create(TextureID, False, 6);
2155 Anim.Blending := False;
2156 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2157 Anim.Free();
2158 end;
2159 end;
2160 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2161 end;
2162 end;
2164 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2165 begin
2166 if ShotType = WEAPON_PLASMA then
2167 s := 'FRAMES_EXPLODE_PLASMA'
2168 else
2169 s := 'FRAMES_EXPLODE_BSPFIRE';
2171 if g_Frames_Get(TextureID, s) and loud then
2172 begin
2173 Anim := TAnimation.Create(TextureID, False, 3);
2174 Anim.Blending := False;
2175 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2176 Anim.Free();
2178 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2179 end;
2180 end;
2182 WEAPON_BFG: // BFG
2183 begin
2184 // Âçðûâ BFG:
2185 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2186 begin
2187 Anim := TAnimation.Create(TextureID, False, 6);
2188 Anim.Blending := False;
2189 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2190 Anim.Free();
2192 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2193 end;
2194 end;
2196 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2197 begin
2198 if ShotType = WEAPON_IMP_FIRE then
2199 s := 'FRAMES_EXPLODE_IMPFIRE'
2200 else
2201 if ShotType = WEAPON_CACO_FIRE then
2202 s := 'FRAMES_EXPLODE_CACOFIRE'
2203 else
2204 s := 'FRAMES_EXPLODE_BARONFIRE';
2206 if g_Frames_Get(TextureID, s) and Loud then
2207 begin
2208 Anim := TAnimation.Create(TextureID, False, 6);
2209 Anim.Blending := False;
2210 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2211 Anim.Free();
2213 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2214 end;
2215 end;
2217 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2218 begin
2219 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2220 begin
2221 Anim := TAnimation.Create(TextureID, False, 6);
2222 Anim.Blending := False;
2223 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2224 Anim.Free();
2226 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2227 end;
2228 end;
2229 end; // case ShotType of...
2231 ShotType := 0;
2232 Animation.Free();
2233 end;
2234 end;
2236 end.