DEADSOFTWARE

43a02a25ead779c9c08bdbac902b6683811abaa5
[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): 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 = 14;
126 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
128 SHOT_SKELFIRE_WIDTH = 14;
129 SHOT_SKELFIRE_HEIGHT = 14;
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): Boolean;
800 var
801 i, h, r, dx, dy, m, mm: Integer;
802 _angle: SmallInt;
803 begin
804 Result := False;
806 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
808 r := rad*rad;
810 h := High(gPlayers);
812 if h <> -1 then
813 for i := 0 to h do
814 if (gPlayers[i] <> nil) and gPlayers[i].Live then
815 with gPlayers[i] do
816 begin
817 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
818 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
820 if dx > 1000 then dx := 1000;
821 if dy > 1000 then dy := 1000;
823 if dx*dx+dy*dy < r then
824 begin
825 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
826 // PLAYER_RECT.Width, PLAYER_RECT.Height);
828 mm := Max(abs(dx), abs(dy));
829 if mm = 0 then mm := 1;
831 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
832 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
833 end;
834 end;
836 h := High(gMonsters);
838 if h <> -1 then
839 for i := 0 to h do
840 if gMonsters[i] <> nil then
841 with gMonsters[i] do
842 begin
843 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
844 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
846 if dx > 1000 then dx := 1000;
847 if dy > 1000 then dy := 1000;
849 if dx*dx+dy*dy < r then
850 begin
851 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
852 // Obj.Rect.Width, Obj.Rect.Height);
854 mm := Max(abs(dx), abs(dy));
855 if mm = 0 then mm := 1;
857 if gMonsters[i].Live then
858 HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad,
859 0, 0, SpawnerUID, HIT_ROCKET);
861 gMonsters[i].Push((dx*7) div mm, (dy*7) div mm);
862 end;
863 end;
865 h := High(gCorpses);
867 if gAdvCorpses and (h <> -1) then
868 for i := 0 to h do
869 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
870 with gCorpses[i] do
871 begin
872 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
873 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
875 if dx > 1000 then dx := 1000;
876 if dy > 1000 then dy := 1000;
878 if dx*dx+dy*dy < r then
879 begin
880 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
881 Obj.Rect.Width, Obj.Rect.Height);
883 mm := Max(abs(dx), abs(dy));
884 if mm = 0 then mm := 1;
886 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
887 end;
888 end;
890 h := High(gGibs);
892 if gAdvGibs and (h <> -1) then
893 for i := 0 to h do
894 if gGibs[i].Live then
895 with gGibs[i] do
896 begin
897 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
898 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
900 if dx > 1000 then dx := 1000;
901 if dy > 1000 then dy := 1000;
903 if dx*dx+dy*dy < r then
904 begin
905 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
906 Obj.Rect.Width, Obj.Rect.Height);
907 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
908 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
910 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
911 end;
912 end;
913 end;
915 procedure g_Weapon_Init();
916 begin
917 CreateWaterMap();
918 end;
920 procedure g_Weapon_Free();
921 var
922 i: Integer;
923 begin
924 if Shots <> nil then
925 begin
926 for i := 0 to High(Shots) do
927 if Shots[i].ShotType <> 0 then
928 Shots[i].Animation.Free();
930 Shots := nil;
931 end;
933 WaterMap := nil;
934 end;
936 procedure g_Weapon_LoadData();
937 begin
938 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
940 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
941 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
942 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
943 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
944 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
945 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
946 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
947 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
948 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
949 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
950 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
951 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
952 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
953 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
954 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
955 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
956 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
957 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
958 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
959 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
960 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
961 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
962 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
963 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
964 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
965 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
966 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
967 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
968 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
969 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
970 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
971 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
973 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
974 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
975 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
976 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
977 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
978 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
979 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
980 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
981 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
982 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
983 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
984 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
985 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
986 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
987 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
988 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
989 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
990 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
991 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
992 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
994 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
995 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
996 end;
998 procedure g_Weapon_FreeData();
999 begin
1000 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1002 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1003 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1004 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1005 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1006 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1007 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1008 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1009 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1010 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1011 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1012 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1013 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1014 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1015 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1016 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1017 g_Sound_Delete('SOUND_FIRE');
1018 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1019 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1020 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1021 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1022 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1023 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1024 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1025 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1026 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1027 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1028 g_Sound_Delete('SOUND_PLAYER_JETON');
1029 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1030 g_Sound_Delete('SOUND_PLAYER_CASING1');
1031 g_Sound_Delete('SOUND_PLAYER_CASING2');
1032 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1033 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1035 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1036 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1037 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1038 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1039 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1040 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1041 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1042 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1043 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1044 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1045 g_Frames_DeleteByName('FRAMES_BFGHIT');
1046 g_Frames_DeleteByName('FRAMES_FIRE');
1047 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1048 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1049 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1050 g_Frames_DeleteByName('FRAMES_SMOKE');
1051 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1052 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1053 end;
1055 procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1056 var
1057 a: Integer;
1058 x2, y2: Integer;
1059 dx, dy: Integer;
1060 xe, ye: Integer;
1061 xi, yi: Integer;
1062 s, c: Extended;
1063 //vx, vy: Integer;
1064 xx, yy, d: Integer;
1066 i: Integer;
1067 t1, _collide: Boolean;
1068 w, h: Word;
1069 begin
1070 a := GetAngle(x, y, xd, yd)+180;
1072 SinCos(DegToRad(-a), s, c);
1074 if Abs(s) < 0.01 then s := 0;
1075 if Abs(c) < 0.01 then c := 0;
1077 x2 := x+Round(c*gMapInfo.Width);
1078 y2 := y+Round(s*gMapInfo.Width);
1080 t1 := gWalls <> nil;
1081 _collide := False;
1082 w := gMapInfo.Width;
1083 h := gMapInfo.Height;
1085 xe := 0;
1086 ye := 0;
1087 dx := x2-x;
1088 dy := y2-y;
1090 if (xd = 0) and (yd = 0) then Exit;
1092 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1093 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1095 dx := Abs(dx);
1096 dy := Abs(dy);
1098 if dx > dy then d := dx else d := dy;
1100 //blood vel, for Monster.Damage()
1101 //vx := (dx*10 div d)*xi;
1102 //vy := (dy*10 div d)*yi;
1104 xx := x;
1105 yy := y;
1107 for i := 1 to d do
1108 begin
1109 xe := xe+dx;
1110 ye := ye+dy;
1112 if xe > d then
1113 begin
1114 xe := xe-d;
1115 xx := xx+xi;
1116 end;
1118 if ye > d then
1119 begin
1120 ye := ye-d;
1121 yy := yy+yi;
1122 end;
1124 if (yy > h) or (yy < 0) then Break;
1125 if (xx > w) or (xx < 0) then Break;
1127 if t1 then
1128 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1129 begin
1130 _collide := True;
1131 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1132 if g_Game_IsServer and g_Game_IsNet then
1133 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1134 end;
1136 if not _collide then
1137 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1139 if _collide then
1140 Break;
1141 end;
1143 if CheckTrigger and g_Game_IsServer then
1144 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1145 end;
1147 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1148 var
1149 obj: TObj;
1150 begin
1151 obj.X := X;
1152 obj.Y := Y;
1153 obj.rect.X := 0;
1154 obj.rect.Y := 0;
1155 obj.rect.Width := 39;
1156 obj.rect.Height := 52;
1157 obj.Vel.X := 0;
1158 obj.Vel.Y := 0;
1159 obj.Accel.X := 0;
1160 obj.Accel.Y := 0;
1162 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1163 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1164 else
1165 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1166 end;
1168 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1169 var
1170 obj: TObj;
1171 begin
1172 obj.X := X;
1173 obj.Y := Y;
1174 obj.rect.X := 0;
1175 obj.rect.Y := 0;
1176 obj.rect.Width := 32;
1177 obj.rect.Height := 52;
1178 obj.Vel.X := 0;
1179 obj.Vel.Y := 0;
1180 obj.Accel.X := 0;
1181 obj.Accel.Y := 0;
1183 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1184 end;
1186 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1187 Silent: Boolean = False);
1188 var
1189 find_id: DWORD;
1190 dx, dy: Integer;
1191 begin
1192 if WID < 0 then
1193 find_id := FindShot()
1194 else
1195 begin
1196 find_id := WID;
1197 if Integer(find_id) >= High(Shots) then
1198 SetLength(Shots, find_id + 64)
1199 end;
1201 with Shots[find_id] do
1202 begin
1203 g_Obj_Init(@Obj);
1205 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1206 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1208 dx := -(Obj.Rect.Width div 2);
1209 dy := -(Obj.Rect.Height div 2);
1210 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1212 Animation := nil;
1213 triggers := nil;
1214 ShotType := WEAPON_ROCKETLAUNCHER;
1215 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1216 end;
1218 Shots[find_id].SpawnerUID := SpawnerUID;
1220 if not Silent then
1221 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1222 end;
1224 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1225 WID: Integer = -1; Silent: Boolean = False);
1226 var
1227 find_id, FramesID: DWORD;
1228 dx, dy: Integer;
1229 begin
1230 if WID < 0 then
1231 find_id := FindShot()
1232 else
1233 begin
1234 find_id := WID;
1235 if Integer(find_id) >= High(Shots) then
1236 SetLength(Shots, find_id + 64)
1237 end;
1239 with Shots[find_id] do
1240 begin
1241 g_Obj_Init(@Obj);
1243 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1244 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1246 dx := -(Obj.Rect.Width div 2);
1247 dy := -(Obj.Rect.Height div 2);
1248 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1250 triggers := nil;
1251 ShotType := WEAPON_SKEL_FIRE;
1252 target := TargetUID;
1253 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1254 Animation := TAnimation.Create(FramesID, True, 5);
1255 end;
1257 Shots[find_id].SpawnerUID := SpawnerUID;
1259 if not Silent then
1260 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1261 end;
1263 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1264 Silent: Boolean = False);
1265 var
1266 find_id, FramesID: DWORD;
1267 dx, dy: Integer;
1268 begin
1269 if WID < 0 then
1270 find_id := FindShot()
1271 else
1272 begin
1273 find_id := WID;
1274 if Integer(find_id) >= High(Shots) then
1275 SetLength(Shots, find_id + 64);
1276 end;
1278 with Shots[find_id] do
1279 begin
1280 g_Obj_Init(@Obj);
1282 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1283 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1285 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1286 dy := -(Obj.Rect.Height div 2);
1287 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1289 triggers := nil;
1290 ShotType := WEAPON_PLASMA;
1291 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1292 Animation := TAnimation.Create(FramesID, True, 5);
1293 end;
1295 Shots[find_id].SpawnerUID := SpawnerUID;
1297 if not Silent then
1298 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1299 end;
1301 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1302 Silent: Boolean = False);
1303 var
1304 find_id, FramesID: DWORD;
1305 dx, dy: Integer;
1306 begin
1307 if WID < 0 then
1308 find_id := FindShot()
1309 else
1310 begin
1311 find_id := WID;
1312 if Integer(find_id) >= High(Shots) then
1313 SetLength(Shots, find_id + 64)
1314 end;
1316 with Shots[find_id] do
1317 begin
1318 g_Obj_Init(@Obj);
1320 Obj.Rect.Width := 16;
1321 Obj.Rect.Height := 16;
1323 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1324 dy := -(Obj.Rect.Height div 2);
1325 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1327 triggers := nil;
1328 ShotType := WEAPON_IMP_FIRE;
1329 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1330 Animation := TAnimation.Create(FramesID, True, 4);
1331 end;
1333 Shots[find_id].SpawnerUID := SpawnerUID;
1335 if not Silent then
1336 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1337 end;
1339 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1340 Silent: Boolean = False);
1341 var
1342 find_id, FramesID: DWORD;
1343 dx, dy: Integer;
1344 begin
1345 if WID < 0 then
1346 find_id := FindShot()
1347 else
1348 begin
1349 find_id := WID;
1350 if Integer(find_id) >= High(Shots) then
1351 SetLength(Shots, find_id + 64)
1352 end;
1354 with Shots[find_id] do
1355 begin
1356 g_Obj_Init(@Obj);
1358 Obj.Rect.Width := 16;
1359 Obj.Rect.Height := 16;
1361 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1362 dy := -(Obj.Rect.Height div 2);
1363 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1365 triggers := nil;
1366 ShotType := WEAPON_CACO_FIRE;
1367 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1368 Animation := TAnimation.Create(FramesID, True, 4);
1369 end;
1371 Shots[find_id].SpawnerUID := SpawnerUID;
1373 if not Silent then
1374 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1375 end;
1377 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1378 Silent: Boolean = False);
1379 var
1380 find_id, FramesID: DWORD;
1381 dx, dy: Integer;
1382 begin
1383 if WID < 0 then
1384 find_id := FindShot()
1385 else
1386 begin
1387 find_id := WID;
1388 if Integer(find_id) >= High(Shots) then
1389 SetLength(Shots, find_id + 64)
1390 end;
1392 with Shots[find_id] do
1393 begin
1394 g_Obj_Init(@Obj);
1396 Obj.Rect.Width := 32;
1397 Obj.Rect.Height := 16;
1399 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1400 dy := -(Obj.Rect.Height div 2);
1401 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1403 triggers := nil;
1404 ShotType := WEAPON_BARON_FIRE;
1405 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1406 Animation := TAnimation.Create(FramesID, True, 4);
1407 end;
1409 Shots[find_id].SpawnerUID := SpawnerUID;
1411 if not Silent then
1412 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1413 end;
1415 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1416 Silent: Boolean = False);
1417 var
1418 find_id, FramesID: DWORD;
1419 dx, dy: Integer;
1420 begin
1421 if WID < 0 then
1422 find_id := FindShot()
1423 else
1424 begin
1425 find_id := WID;
1426 if Integer(find_id) >= High(Shots) then
1427 SetLength(Shots, find_id + 64)
1428 end;
1430 with Shots[find_id] do
1431 begin
1432 g_Obj_Init(@Obj);
1434 Obj.Rect.Width := 16;
1435 Obj.Rect.Height := 16;
1437 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1438 dy := -(Obj.Rect.Height div 2);
1439 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1441 triggers := nil;
1442 ShotType := WEAPON_BSP_FIRE;
1443 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1444 Animation := TAnimation.Create(FramesID, True, 4);
1445 end;
1447 Shots[find_id].SpawnerUID := SpawnerUID;
1449 if not Silent then
1450 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1451 end;
1453 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1454 Silent: Boolean = False);
1455 var
1456 find_id, FramesID: DWORD;
1457 dx, dy: Integer;
1458 begin
1459 if WID < 0 then
1460 find_id := FindShot()
1461 else
1462 begin
1463 find_id := WID;
1464 if Integer(find_id) >= High(Shots) then
1465 SetLength(Shots, find_id + 64)
1466 end;
1468 with Shots[find_id] do
1469 begin
1470 g_Obj_Init(@Obj);
1472 Obj.Rect.Width := 32;
1473 Obj.Rect.Height := 32;
1475 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1476 dy := -(Obj.Rect.Height div 2);
1477 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1479 triggers := nil;
1480 ShotType := WEAPON_MANCUB_FIRE;
1481 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1482 Animation := TAnimation.Create(FramesID, True, 4);
1483 end;
1485 Shots[find_id].SpawnerUID := SpawnerUID;
1487 if not Silent then
1488 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1489 end;
1491 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1492 Silent: Boolean = False);
1493 var
1494 find_id, FramesID: DWORD;
1495 dx, dy: Integer;
1496 begin
1497 if WID < 0 then
1498 find_id := FindShot()
1499 else
1500 begin
1501 find_id := WID;
1502 if Integer(find_id) >= High(Shots) then
1503 SetLength(Shots, find_id + 64)
1504 end;
1506 with Shots[find_id] do
1507 begin
1508 g_Obj_Init(@Obj);
1510 Obj.Rect.Width := SHOT_BFG_WIDTH;
1511 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1513 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1514 dy := -(Obj.Rect.Height div 2);
1515 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1517 triggers := nil;
1518 ShotType := WEAPON_BFG;
1519 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
1520 Animation := TAnimation.Create(FramesID, True, 6);
1521 end;
1523 Shots[find_id].SpawnerUID := SpawnerUID;
1525 if not Silent then
1526 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
1527 end;
1529 procedure g_Weapon_bfghit(x, y: Integer);
1530 var
1531 ID: DWORD;
1532 Anim: TAnimation;
1533 begin
1534 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1535 begin
1536 Anim := TAnimation.Create(ID, False, 4);
1537 g_GFX_OnceAnim(x-32, y-32, Anim);
1538 Anim.Free();
1539 end;
1540 end;
1542 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
1543 Silent: Boolean = False);
1544 begin
1545 if not Silent then
1546 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
1548 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1549 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
1550 begin
1551 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
1552 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1553 end;
1554 end;
1556 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1557 Silent: Boolean = False);
1558 begin
1559 if not Silent then
1560 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
1562 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1563 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
1564 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1565 begin
1566 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
1567 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1568 end;
1569 end;
1571 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1572 Silent: Boolean = False);
1573 var
1574 i, j: Integer;
1575 begin
1576 if not Silent then
1577 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
1579 for i := 0 to 9 do
1580 begin
1581 j := Random(17)-8; // -8 .. 8
1582 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
1583 end;
1584 end;
1586 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1587 Silent: Boolean = False);
1588 var
1589 a, i, j: Integer;
1590 begin
1591 if not Silent then
1592 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
1594 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
1595 for i := 0 to a do
1596 begin
1597 j := Random(41)-20; // -20 .. 20
1598 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
1599 end;
1600 end;
1602 procedure g_Weapon_Update();
1603 var
1604 i, a, h, cx, cy, oldvx, oldvy: Integer;
1605 _id: DWORD;
1606 Anim: TAnimation;
1607 t: DWArray;
1608 st: Word;
1609 s: String;
1610 o: TObj;
1611 spl: Boolean;
1612 Loud: Boolean;
1613 begin
1614 if Shots = nil then
1615 Exit;
1617 for i := 0 to High(Shots) do
1618 begin
1619 if Shots[i].ShotType = 0 then
1620 Continue;
1622 Loud := True;
1624 with Shots[i] do
1625 begin
1626 Timeout := Timeout - 1;
1627 oldvx := Obj.Vel.X;
1628 oldvy := Obj.Vel.Y;
1629 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
1630 if g_Game_IsServer then
1631 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
1632 SpawnerUID, ACTIVATE_SHOT, triggers)
1633 else
1634 t := nil;
1636 if t <> nil then
1637 begin
1638 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
1639 if triggers = nil then
1640 triggers := t
1641 else
1642 begin
1643 h := High(t);
1645 for a := 0 to h do
1646 if not InDWArray(t[a], triggers) then
1647 begin
1648 SetLength(triggers, Length(triggers)+1);
1649 triggers[High(triggers)] := t[a];
1650 end;
1651 end;
1652 end;
1654 // Àíèìàöèÿ ñíàðÿäà:
1655 if Animation <> nil then
1656 Animation.Update();
1658 // Äâèæåíèå:
1659 spl := (ShotType <> WEAPON_PLASMA) and
1660 (ShotType <> WEAPON_BFG) and
1661 (ShotType <> WEAPON_BSP_FIRE);
1663 st := g_Obj_Move(@Obj, False, spl);
1665 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
1666 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
1667 begin
1668 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
1669 ShotType := 0;
1670 Animation.Free();
1671 Continue;
1672 end;
1674 cx := Obj.X + (Obj.Rect.Width div 2);
1675 cy := Obj.Y + (Obj.Rect.Height div 2);
1677 case ShotType of
1678 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
1679 begin
1680 // Âûëåòåëà èç âîäû:
1681 if WordBool(st and MOVE_HITAIR) then
1682 g_Obj_SetSpeed(@Obj, 12);
1684 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
1685 if WordBool(st and MOVE_INWATER) then
1686 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
1687 Obj.Y+(Obj.Rect.Height div 2),
1688 1+Random(3), 16, 16)
1689 else
1690 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
1691 begin
1692 Anim := TAnimation.Create(_id, False, 3);
1693 Anim.Alpha := 150;
1694 g_GFX_OnceAnim(Obj.X-14+Random(9),
1695 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
1696 Anim, ONCEANIM_SMOKE);
1697 Anim.Free();
1698 end;
1700 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1701 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1702 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
1703 (Timeout < 1) then
1704 begin
1705 Obj.Vel.X := 0;
1706 Obj.Vel.Y := 0;
1708 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
1710 if ShotType = WEAPON_SKEL_FIRE then
1711 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
1712 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
1713 begin
1714 Anim := TAnimation.Create(TextureID, False, 8);
1715 Anim.Blending := False;
1716 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
1717 Anim.Free();
1718 end;
1719 end
1720 else
1721 begin // Âçðûâ Ðàêåòû
1722 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1723 begin
1724 Anim := TAnimation.Create(TextureID, False, 6);
1725 Anim.Blending := False;
1726 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1727 Anim.Free();
1728 end;
1729 end;
1731 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
1733 ShotType := 0;
1734 end;
1736 if ShotType = WEAPON_SKEL_FIRE then
1737 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
1738 if GetPos(target, @o) then
1739 throw(i, Obj.X, Obj.Y,
1740 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
1741 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
1742 12);
1743 end;
1744 end;
1746 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
1747 begin
1748 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1749 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1750 begin
1751 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
1752 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
1753 ShotType := 0;
1754 Continue;
1755 end;
1757 // Âåëè÷èíà óðîíà:
1758 if (ShotType = WEAPON_PLASMA) and
1759 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1760 a := 10
1761 else
1762 a := 5;
1764 if ShotType = WEAPON_BSP_FIRE then
1765 a := 10;
1767 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1768 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1769 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
1770 (Timeout < 1) then
1771 begin
1772 if ShotType = WEAPON_PLASMA then
1773 s := 'FRAMES_EXPLODE_PLASMA'
1774 else
1775 s := 'FRAMES_EXPLODE_BSPFIRE';
1777 // Âçðûâ Ïëàçìû:
1778 if g_Frames_Get(TextureID, s) then
1779 begin
1780 Anim := TAnimation.Create(TextureID, False, 3);
1781 Anim.Blending := False;
1782 g_GFX_OnceAnim(cx-16, cy-16, Anim);
1783 Anim.Free();
1784 end;
1786 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
1788 ShotType := 0;
1789 end;
1790 end;
1792 WEAPON_BFG: // BFG
1793 begin
1794 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1795 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1796 begin
1797 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
1798 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
1799 ShotType := 0;
1800 Continue;
1801 end;
1803 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1804 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1805 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
1806 (Timeout < 1) then
1807 begin
1808 // Ëó÷è BFG:
1809 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
1811 // Âçðûâ BFG:
1812 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
1813 begin
1814 Anim := TAnimation.Create(TextureID, False, 6);
1815 Anim.Blending := False;
1816 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1817 Anim.Free();
1818 end;
1820 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
1822 ShotType := 0;
1823 end;
1824 end;
1826 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
1827 begin
1828 // Âûëåòåë èç âîäû:
1829 if WordBool(st and MOVE_HITAIR) then
1830 g_Obj_SetSpeed(@Obj, 16);
1832 // Âåëè÷èíà óðîíà:
1833 if ShotType = WEAPON_IMP_FIRE then
1834 a := 5
1835 else
1836 if ShotType = WEAPON_CACO_FIRE then
1837 a := 20
1838 else
1839 a := 40;
1841 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1842 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1843 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
1844 (Timeout < 1) then
1845 begin
1846 if ShotType = WEAPON_IMP_FIRE then
1847 s := 'FRAMES_EXPLODE_IMPFIRE'
1848 else
1849 if ShotType = WEAPON_CACO_FIRE then
1850 s := 'FRAMES_EXPLODE_CACOFIRE'
1851 else
1852 s := 'FRAMES_EXPLODE_BARONFIRE';
1854 // Âçðûâ:
1855 if g_Frames_Get(TextureID, s) then
1856 begin
1857 Anim := TAnimation.Create(TextureID, False, 6);
1858 Anim.Blending := False;
1859 g_GFX_OnceAnim(cx-32, cy-32, Anim);
1860 Anim.Free();
1861 end;
1863 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1865 ShotType := 0;
1866 end;
1867 end;
1869 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
1870 begin
1871 // Âûëåòåë èç âîäû:
1872 if WordBool(st and MOVE_HITAIR) then
1873 g_Obj_SetSpeed(@Obj, 16);
1875 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1876 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1877 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
1878 (Timeout < 1) then
1879 begin
1880 // Âçðûâ:
1881 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1882 begin
1883 Anim := TAnimation.Create(TextureID, False, 6);
1884 Anim.Blending := False;
1885 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1886 Anim.Free();
1887 end;
1889 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1891 ShotType := 0;
1892 end;
1893 end;
1894 end; // case ShotType of...
1896 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
1897 if (ShotType = 0) then
1898 begin
1899 if gGameSettings.GameType = GT_SERVER then
1900 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
1901 Animation.Free();
1902 Animation := nil;
1903 end
1904 else if (oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y) then
1905 if gGameSettings.GameType = GT_SERVER then
1906 MH_SEND_UpdateShot(i);
1907 end;
1908 end;
1909 end;
1911 procedure g_Weapon_Draw();
1912 var
1913 i: Integer;
1914 a: SmallInt;
1915 p: TPoint;
1916 begin
1917 if Shots = nil then
1918 Exit;
1920 for i := 0 to High(Shots) do
1921 if Shots[i].ShotType <> 0 then
1922 with Shots[i] do
1923 begin
1924 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
1925 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1926 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1927 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1928 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
1929 else
1930 a := 0;
1932 p.X := Obj.Rect.Width div 2;
1933 p.Y := Obj.Rect.Height div 2;
1935 if Animation <> nil then
1936 begin
1937 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1938 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1939 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1940 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
1941 else
1942 Animation.Draw(Obj.X, Obj.Y, M_NONE);
1943 end
1944 else
1945 begin
1946 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
1947 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
1948 else
1949 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
1950 end;
1952 if g_debug_Frames then
1953 begin
1954 e_DrawQuad(Obj.X+Obj.Rect.X,
1955 Obj.Y+Obj.Rect.Y,
1956 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
1957 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
1958 0, 255, 0);
1959 end;
1960 end;
1961 end;
1963 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
1964 var
1965 a: Integer;
1966 begin
1967 Result := False;
1969 if Shots = nil then
1970 Exit;
1972 for a := 0 to High(Shots) do
1973 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
1974 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
1975 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
1976 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
1977 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
1978 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
1979 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
1980 begin
1981 Result := True;
1982 Exit;
1983 end;
1984 end;
1986 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
1987 var
1988 count, i, j: Integer;
1989 dw: DWORD;
1990 begin
1991 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
1992 count := 0;
1993 if Shots <> nil then
1994 for i := 0 to High(Shots) do
1995 if Shots[i].ShotType <> 0 then
1996 count := count + 1;
1998 Mem := TBinMemoryWriter.Create((count+1) * 80);
2000 // Êîëè÷åñòâî ñíàðÿäîâ:
2001 Mem.WriteInt(count);
2003 if count = 0 then
2004 Exit;
2006 for i := 0 to High(Shots) do
2007 if Shots[i].ShotType <> 0 then
2008 begin
2009 // Ñèãíàòóðà ñíàðÿäà:
2010 dw := SHOT_SIGNATURE; // 'SHOT'
2011 Mem.WriteDWORD(dw);
2012 // Òèï ñíàðÿäà:
2013 Mem.WriteByte(Shots[i].ShotType);
2014 // Öåëü:
2015 Mem.WriteWord(Shots[i].Target);
2016 // UID ñòðåëÿâøåãî:
2017 Mem.WriteWord(Shots[i].SpawnerUID);
2018 // Ðàçìåð ïîëÿ Triggers:
2019 dw := Length(Shots[i].Triggers);
2020 Mem.WriteDWORD(dw);
2021 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2022 for j := 0 to Integer(dw)-1 do
2023 Mem.WriteDWORD(Shots[i].Triggers[j]);
2024 // Îáúåêò ñíàðÿäà:
2025 Obj_SaveState(@Shots[i].Obj, Mem);
2026 end;
2027 end;
2029 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2030 var
2031 count, i, j: Integer;
2032 dw: DWORD;
2033 begin
2034 if Mem = nil then
2035 Exit;
2037 // Êîëè÷åñòâî ñíàðÿäîâ:
2038 Mem.ReadInt(count);
2040 SetLength(Shots, count);
2042 if count = 0 then
2043 Exit;
2045 for i := 0 to count-1 do
2046 begin
2047 // Ñèãíàòóðà ñíàðÿäà:
2048 Mem.ReadDWORD(dw);
2049 if dw <> SHOT_SIGNATURE then // 'SHOT'
2050 begin
2051 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2052 end;
2053 // Òèï ñíàðÿäà:
2054 Mem.ReadByte(Shots[i].ShotType);
2055 // Öåëü:
2056 Mem.ReadWord(Shots[i].Target);
2057 // UID ñòðåëÿâøåãî:
2058 Mem.ReadWord(Shots[i].SpawnerUID);
2059 // Ðàçìåð ïîëÿ Triggers:
2060 Mem.ReadDWORD(dw);
2061 SetLength(Shots[i].Triggers, dw);
2062 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2063 for j := 0 to Integer(dw)-1 do
2064 Mem.ReadDWORD(Shots[i].Triggers[j]);
2065 // Îáúåêò ïðåäìåòà:
2066 Obj_LoadState(@Shots[i].Obj, Mem);
2068 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2069 Shots[i].TextureID := DWORD(-1);
2070 Shots[i].Animation := nil;
2072 case Shots[i].ShotType of
2073 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2074 begin
2075 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2076 end;
2077 WEAPON_PLASMA:
2078 begin
2079 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2080 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2081 end;
2082 WEAPON_BFG:
2083 begin
2084 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2085 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2086 end;
2087 WEAPON_IMP_FIRE:
2088 begin
2089 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2090 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2091 end;
2092 WEAPON_BSP_FIRE:
2093 begin
2094 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2095 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2096 end;
2097 WEAPON_CACO_FIRE:
2098 begin
2099 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2100 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2101 end;
2102 WEAPON_BARON_FIRE:
2103 begin
2104 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2105 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2106 end;
2107 WEAPON_MANCUB_FIRE:
2108 begin
2109 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2110 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2111 end;
2112 end;
2113 end;
2114 end;
2116 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2117 var
2118 cx, cy: Integer;
2119 Anim: TAnimation;
2120 s: string;
2121 begin
2122 if Shots = nil then
2123 Exit;
2124 if (I > High(Shots)) or (I < 0) then Exit;
2126 with Shots[I] do
2127 begin
2128 if ShotType = 0 then Exit;
2129 Obj.X := X;
2130 Obj.Y := Y;
2131 cx := Obj.X + (Obj.Rect.Width div 2);
2132 cy := Obj.Y + (Obj.Rect.Height div 2);
2134 case ShotType of
2135 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2136 begin
2137 if Loud then
2138 begin
2139 if ShotType = WEAPON_SKEL_FIRE then
2140 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2141 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2142 begin
2143 Anim := TAnimation.Create(TextureID, False, 8);
2144 Anim.Blending := False;
2145 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2146 Anim.Free();
2147 end;
2148 end
2149 else
2150 begin // Âçðûâ Ðàêåòû
2151 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2152 begin
2153 Anim := TAnimation.Create(TextureID, False, 6);
2154 Anim.Blending := False;
2155 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2156 Anim.Free();
2157 end;
2158 end;
2159 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2160 end;
2161 end;
2163 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2164 begin
2165 if ShotType = WEAPON_PLASMA then
2166 s := 'FRAMES_EXPLODE_PLASMA'
2167 else
2168 s := 'FRAMES_EXPLODE_BSPFIRE';
2170 if g_Frames_Get(TextureID, s) and loud then
2171 begin
2172 Anim := TAnimation.Create(TextureID, False, 3);
2173 Anim.Blending := False;
2174 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2175 Anim.Free();
2177 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2178 end;
2179 end;
2181 WEAPON_BFG: // BFG
2182 begin
2183 // Âçðûâ BFG:
2184 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2185 begin
2186 Anim := TAnimation.Create(TextureID, False, 6);
2187 Anim.Blending := False;
2188 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2189 Anim.Free();
2191 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2192 end;
2193 end;
2195 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2196 begin
2197 if ShotType = WEAPON_IMP_FIRE then
2198 s := 'FRAMES_EXPLODE_IMPFIRE'
2199 else
2200 if ShotType = WEAPON_CACO_FIRE then
2201 s := 'FRAMES_EXPLODE_CACOFIRE'
2202 else
2203 s := 'FRAMES_EXPLODE_BARONFIRE';
2205 if g_Frames_Get(TextureID, s) and Loud then
2206 begin
2207 Anim := TAnimation.Create(TextureID, False, 6);
2208 Anim.Blending := False;
2209 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2210 Anim.Free();
2212 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2213 end;
2214 end;
2216 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2217 begin
2218 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2219 begin
2220 Anim := TAnimation.Create(TextureID, False, 6);
2221 Anim.Blending := False;
2222 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2223 Anim.Free();
2225 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2226 end;
2227 end;
2228 end; // case ShotType of...
2230 ShotType := 0;
2231 Animation.Free();
2232 end;
2233 end;
2235 end.