DEADSOFTWARE

Add flamethrower weapon, item and ammo
[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_FLAMETHROWER = 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 WP_FIRST = WEAPON_KASTET;
110 WP_LAST = WEAPON_FLAMETHROWER;
112 implementation
114 uses
115 Math, g_map, g_player, g_gfx, g_sound, g_main,
116 g_console, SysUtils, g_options, g_game,
117 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
118 g_language, g_netmsg;
120 type
121 TWaterPanel = record
122 X, Y: Integer;
123 Width, Height: Word;
124 Active: Boolean;
125 end;
127 const
128 SHOT_ROCKETLAUNCHER_WIDTH = 14;
129 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
131 SHOT_SKELFIRE_WIDTH = 14;
132 SHOT_SKELFIRE_HEIGHT = 14;
134 SHOT_PLASMA_WIDTH = 16;
135 SHOT_PLASMA_HEIGHT = 16;
137 SHOT_BFG_WIDTH = 32;
138 SHOT_BFG_HEIGHT = 32;
139 SHOT_BFG_DAMAGE = 100;
140 SHOT_BFG_RADIUS = 256;
142 SHOT_SIGNATURE = $544F4853; // 'SHOT'
144 var
145 WaterMap: array of array of DWORD = nil;
147 function FindShot(): DWORD;
148 var
149 i: Integer;
150 begin
151 if Shots <> nil then
152 for i := 0 to High(Shots) do
153 if Shots[i].ShotType = 0 then
154 begin
155 Result := i;
156 LastShotID := Result;
157 Exit;
158 end;
160 if Shots = nil then
161 begin
162 SetLength(Shots, 128);
163 Result := 0;
164 end
165 else
166 begin
167 Result := High(Shots) + 1;
168 SetLength(Shots, Length(Shots) + 128);
169 end;
170 LastShotID := Result;
171 end;
173 procedure CreateWaterMap();
174 var
175 WaterArray: Array of TWaterPanel;
176 a, b, c, m: Integer;
177 ok: Boolean;
178 begin
179 if gWater = nil then
180 Exit;
182 SetLength(WaterArray, Length(gWater));
184 for a := 0 to High(gWater) do
185 begin
186 WaterArray[a].X := gWater[a].X;
187 WaterArray[a].Y := gWater[a].Y;
188 WaterArray[a].Width := gWater[a].Width;
189 WaterArray[a].Height := gWater[a].Height;
190 WaterArray[a].Active := True;
191 end;
193 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
195 for a := 0 to High(WaterArray) do
196 if WaterArray[a].Active then
197 begin
198 WaterArray[a].Active := False;
199 m := Length(WaterMap);
200 SetLength(WaterMap, m+1);
201 SetLength(WaterMap[m], 1);
202 WaterMap[m][0] := a;
203 ok := True;
205 while ok do
206 begin
207 ok := False;
208 for b := 0 to High(WaterArray) do
209 if WaterArray[b].Active then
210 for c := 0 to High(WaterMap[m]) do
211 if g_CollideAround(WaterArray[b].X,
212 WaterArray[b].Y,
213 WaterArray[b].Width,
214 WaterArray[b].Height,
215 WaterArray[WaterMap[m][c]].X,
216 WaterArray[WaterMap[m][c]].Y,
217 WaterArray[WaterMap[m][c]].Width,
218 WaterArray[WaterMap[m][c]].Height) then
219 begin
220 WaterArray[b].Active := False;
221 SetLength(WaterMap[m],
222 Length(WaterMap[m])+1);
223 WaterMap[m][High(WaterMap[m])] := b;
224 ok := True;
225 Break;
226 end;
227 end;
229 g_Game_StepLoading();
230 end;
232 WaterArray := nil;
233 end;
235 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
236 var
237 a, b, c, d, i1, i2: Integer;
238 pl, mn: WArray;
239 begin
240 if (gWater = nil) or (WaterMap = nil) then Exit;
242 i1 := -1;
243 i2 := -1;
245 SetLength(pl, 1024);
246 SetLength(mn, 1024);
247 for d := 0 to 1023 do pl[d] := $FFFF;
248 for d := 0 to 1023 do mn[d] := $FFFF;
250 for a := 0 to High(WaterMap) do
251 for b := 0 to High(WaterMap[a]) do
252 begin
253 if not g_Obj_Collide(gWater[WaterMap[a][b]].X, gWater[WaterMap[a][b]].Y,
254 gWater[WaterMap[a][b]].Width, gWater[WaterMap[a][b]].Height,
255 @Shots[ID].Obj) then Continue;
257 for c := 0 to High(WaterMap[a]) do
258 begin
259 if gPlayers <> nil then
260 begin
261 for d := 0 to High(gPlayers) do
262 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
263 if gPlayers[d].Collide(gWater[WaterMap[a][c]]) then
264 if not InWArray(d, pl) then
265 if i1 < 1023 then
266 begin
267 i1 := i1+1;
268 pl[i1] := d;
269 end;
270 end;
272 if gMonsters <> nil then
273 begin
274 for d := 0 to High(gMonsters) do
275 if (gMonsters[d] <> nil) and (gMonsters[d].Live) then
276 if gMonsters[d].Collide(gWater[WaterMap[a][c]]) then
277 if not InWArray(d, mn) then
278 if i2 < 1023 then
279 begin
280 i2 := i2+1;
281 mn[i2] := d;
282 end;
283 end;
284 end;
286 if i1 <> -1 then
287 for d := 0 to i1 do
288 gPlayers[pl[d]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
290 if i2 <> -1 then
291 for d := 0 to i2 do
292 gMonsters[mn[d]].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
293 end;
295 pl := nil;
296 mn := nil;
297 end;
299 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
300 var
301 tt, mt: Byte;
302 mon: TMonster;
303 begin
304 Result := False;
306 tt := g_GetUIDType(SpawnerUID);
307 if tt = UID_MONSTER then
308 begin
309 mon := g_Monsters_Get(SpawnerUID);
310 if mon <> nil then
311 mt := g_Monsters_Get(SpawnerUID).MonsterType
312 else
313 mt := 0;
314 end
315 else
316 mt := 0;
318 if m = nil then Exit;
319 if m.UID = SpawnerUID then
320 begin
321 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
322 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
323 Exit;
324 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
325 if (m.MonsterType = MONSTER_CYBER) or
326 (m.MonsterType = MONSTER_BARREL) then
327 begin
328 Result := True;
329 Exit;
330 end;
331 end;
333 if tt = UID_MONSTER then
334 begin
335 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
336 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
337 Exit;
339 // Îáà ìîíñòðà îäíîãî âèäà:
340 if mt = m.MonsterType then
341 case mt of
342 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
343 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
344 Exit; // Ýòè íå áüþò ñâîèõ
345 end;
346 end;
348 if g_Game_IsServer then
349 Result := m.Damage(d, vx, vy, SpawnerUID, t)
350 else
351 Result := True;
352 end;
354 function HitPlayer(p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
355 begin
356 Result := False;
358 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
359 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
360 Exit;
362 if g_Game_IsServer then p.Damage(d, SpawnerUID, vx, vy, t);
364 Result := True;
365 end;
367 function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer;
368 SpawnerUID: Word; AllowPush: Boolean): Byte;
369 var
370 i, h: Integer;
371 begin
372 Result := 0;
374 h := High(gPlayers);
376 if h <> -1 then
377 for i := 0 to h do
378 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
379 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
380 begin
381 if AllowPush then gPlayers[i].Push(vx, vy);
382 Result := 1;
383 end;
385 if Result <> 0 then Exit;
387 h := High(gMonsters);
389 if h <> -1 then
390 for i := 0 to h do
391 if (gMonsters[i] <> nil) and gMonsters[i].Live and gMonsters[i].Collide(X, Y) then
392 if HitMonster(gMonsters[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
393 begin
394 if AllowPush then gMonsters[i].Push(vx, vy);
395 Result := 2;
396 Exit;
397 end;
398 end;
400 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
401 var
402 i, h: Integer;
403 st: Byte;
404 pl: TPlayer;
405 b: Boolean;
406 begin
407 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
409 h := High(gCorpses);
411 if gAdvCorpses and (h <> -1) then
412 for i := 0 to h do
413 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
414 with gCorpses[i] do
415 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
416 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
417 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
418 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
419 begin
420 Damage(50, 0, 0);
421 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
422 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
423 end;
425 st := TEAM_NONE;
426 pl := g_Player_Get(SpawnerUID);
427 if pl <> nil then
428 st := pl.Team;
430 h := High(gPlayers);
432 if h <> -1 then
433 for i := 0 to h do
434 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
435 with gPlayers[i] do
436 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
437 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
438 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
439 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
440 begin
441 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
442 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
443 else
444 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
445 if b then
446 gPlayers[i].BFGHit();
447 end;
449 h := High(gMonsters);
451 if h <> -1 then
452 for i := 0 to h do
453 if (gMonsters[i] <> nil) and (gMonsters[i].Live) and (gMonsters[i].UID <> SpawnerUID) then
454 with gMonsters[i] do
455 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
456 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
457 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
458 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
459 if HitMonster(gMonsters[i], 50, 0, 0, SpawnerUID, HIT_SOME) then gMonsters[i].BFGHit();
460 end;
462 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
463 var
464 find_id, FramesID: DWORD;
465 begin
466 if I < 0 then
467 find_id := FindShot()
468 else
469 begin
470 find_id := I;
471 if Integer(find_id) >= High(Shots) then
472 SetLength(Shots, find_id + 64)
473 end;
475 case ShotType of
476 WEAPON_ROCKETLAUNCHER:
477 begin
478 with Shots[find_id] do
479 begin
480 g_Obj_Init(@Obj);
482 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
483 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
485 Animation := nil;
486 Triggers := nil;
487 ShotType := WEAPON_ROCKETLAUNCHER;
488 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
489 end;
490 end;
492 WEAPON_PLASMA:
493 begin
494 with Shots[find_id] do
495 begin
496 g_Obj_Init(@Obj);
498 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
499 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
501 Triggers := nil;
502 ShotType := WEAPON_PLASMA;
503 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
504 Animation := TAnimation.Create(FramesID, True, 5);
505 end;
506 end;
508 WEAPON_BFG:
509 begin
510 with Shots[find_id] do
511 begin
512 g_Obj_Init(@Obj);
514 Obj.Rect.Width := SHOT_BFG_WIDTH;
515 Obj.Rect.Height := SHOT_BFG_HEIGHT;
517 Triggers := nil;
518 ShotType := WEAPON_BFG;
519 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
520 Animation := TAnimation.Create(FramesID, True, 6);
521 end;
522 end;
524 WEAPON_IMP_FIRE:
525 begin
526 with Shots[find_id] do
527 begin
528 g_Obj_Init(@Obj);
530 Obj.Rect.Width := 16;
531 Obj.Rect.Height := 16;
533 Triggers := nil;
534 ShotType := WEAPON_IMP_FIRE;
535 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
536 Animation := TAnimation.Create(FramesID, True, 4);
537 end;
538 end;
540 WEAPON_CACO_FIRE:
541 begin
542 with Shots[find_id] do
543 begin
544 g_Obj_Init(@Obj);
546 Obj.Rect.Width := 16;
547 Obj.Rect.Height := 16;
549 Triggers := nil;
550 ShotType := WEAPON_CACO_FIRE;
551 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
552 Animation := TAnimation.Create(FramesID, True, 4);
553 end;
554 end;
556 WEAPON_MANCUB_FIRE:
557 begin
558 with Shots[find_id] do
559 begin
560 g_Obj_Init(@Obj);
562 Obj.Rect.Width := 32;
563 Obj.Rect.Height := 32;
565 Triggers := nil;
566 ShotType := WEAPON_MANCUB_FIRE;
567 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
568 Animation := TAnimation.Create(FramesID, True, 4);
569 end;
570 end;
572 WEAPON_BARON_FIRE:
573 begin
574 with Shots[find_id] do
575 begin
576 g_Obj_Init(@Obj);
578 Obj.Rect.Width := 32;
579 Obj.Rect.Height := 16;
581 Triggers := nil;
582 ShotType := WEAPON_BARON_FIRE;
583 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
584 Animation := TAnimation.Create(FramesID, True, 4);
585 end;
586 end;
588 WEAPON_BSP_FIRE:
589 begin
590 with Shots[find_id] do
591 begin
592 g_Obj_Init(@Obj);
594 Obj.Rect.Width := 16;
595 Obj.Rect.Height := 16;
597 Triggers := nil;
598 ShotType := WEAPON_BSP_FIRE;
599 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
600 Animation := TAnimation.Create(FramesID, True, 4);
601 end;
602 end;
604 WEAPON_SKEL_FIRE:
605 begin
606 with Shots[find_id] do
607 begin
608 g_Obj_Init(@Obj);
610 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
611 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
613 Triggers := nil;
614 ShotType := WEAPON_SKEL_FIRE;
615 target := TargetUID;
616 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
617 Animation := TAnimation.Create(FramesID, True, 5);
618 end;
619 end;
620 end;
622 Shots[find_id].Obj.X := X;
623 Shots[find_id].Obj.Y := Y;
624 Shots[find_id].Obj.Vel.X := XV;
625 Shots[find_id].Obj.Vel.Y := YV;
626 Shots[find_id].Obj.Accel.X := 0;
627 Shots[find_id].Obj.Accel.Y := 0;
628 Shots[find_id].SpawnerUID := Spawner;
629 Result := find_id;
630 end;
632 procedure throw(i, x, y, xd, yd, s: Integer);
633 var
634 a: Integer;
635 begin
636 yd := yd - y;
637 xd := xd - x;
639 a := Max(Abs(xd), Abs(yd));
640 if a = 0 then
641 a := 1;
643 Shots[i].Obj.X := x;
644 Shots[i].Obj.Y := y;
645 Shots[i].Obj.Vel.X := (xd*s) div a;
646 Shots[i].Obj.Vel.Y := (yd*s) div a;
647 Shots[i].Obj.Accel.X := 0;
648 Shots[i].Obj.Accel.Y := 0;
649 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
650 Shots[i].Timeout := 900 // ~25 sec
651 else
652 Shots[i].Timeout := 550 // ~15 sec
653 end;
655 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
656 var
657 i, h: Integer;
659 function PlayerHit(Team: Byte = 0): Boolean;
660 var
661 i: Integer;
662 ChkTeam: Boolean;
663 p: TPlayer;
664 begin
665 Result := False;
666 h := High(gPlayers);
668 if h <> -1 then
669 for i := 0 to h do
670 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
671 begin
672 ChkTeam := True;
673 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
674 begin
675 p := g_Player_Get(SpawnerUID);
676 if p <> nil then
677 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
678 end;
679 if ChkTeam then
680 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
681 begin
682 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
683 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
684 if t = HIT_BFG then
685 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
686 Result := True;
687 break;
688 end;
689 end;
690 end;
691 function MonsterHit(): Boolean;
692 var
693 i: Integer;
694 begin
695 Result := False;
696 h := High(gMonsters);
698 if h <> -1 then
699 for i := 0 to h do
700 if (gMonsters[i] <> nil) and gMonsters[i].Live and g_Obj_Collide(obj, @gMonsters[i].Obj) then
701 if HitMonster(gMonsters[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
702 begin
703 gMonsters[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
704 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
705 Result := True;
706 break;
707 end;
708 end;
709 begin
710 Result := 0;
712 if HitCorpses then
713 begin
714 h := High(gCorpses);
716 if gAdvCorpses and (h <> -1) then
717 for i := 0 to h do
718 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
719 g_Obj_Collide(obj, @gCorpses[i].Obj) then
720 begin
721 // Ðàñïèëèâàåì òðóï:
722 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
723 (obj^.Vel.Y+obj^.Accel.Y) div 4);
724 Result := 1;
725 end;
726 end;
728 case gGameSettings.GameMode of
729 // Êàìïàíèÿ:
730 GM_COOP, GM_SINGLE:
731 begin
732 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
733 if MonsterHit() then
734 begin
735 Result := 2;
736 Exit;
737 end;
739 if PlayerHit() then
740 begin
741 Result := 1;
742 Exit;
743 end;
744 end;
746 // Äåçìàò÷:
747 GM_DM:
748 begin
749 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
750 if PlayerHit() then
751 begin
752 Result := 1;
753 Exit;
754 end;
756 if MonsterHit() then
757 begin
758 Result := 2;
759 Exit;
760 end;
761 end;
763 // Êîìàíäíûå:
764 GM_TDM, GM_CTF:
765 begin
766 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
767 if PlayerHit(2) then
768 begin
769 Result := 1;
770 Exit;
771 end;
773 // Ïîòîì ìîíñòðîâ
774 if MonsterHit() then
775 begin
776 Result := 2;
777 Exit;
778 end;
780 // È â êîíöå ñâîèõ èãðîêîâ
781 if PlayerHit(1) then
782 begin
783 Result := 1;
784 Exit;
785 end;
786 end;
788 end;
789 end;
791 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
792 begin
793 Result := False;
795 case g_GetUIDType(UID) of
796 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
797 UID_MONSTER: Result := HitMonster(g_Monsters_Get(UID), d, 0, 0, SpawnerUID, t);
798 else Exit;
799 end;
800 end;
802 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
803 var
804 i, h, r, dx, dy, m, mm: Integer;
805 _angle: SmallInt;
806 begin
807 Result := False;
809 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
811 r := rad*rad;
813 h := High(gPlayers);
815 if h <> -1 then
816 for i := 0 to h do
817 if (gPlayers[i] <> nil) and gPlayers[i].Live then
818 with gPlayers[i] do
819 begin
820 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
821 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
823 if dx > 1000 then dx := 1000;
824 if dy > 1000 then dy := 1000;
826 if dx*dx+dy*dy < r then
827 begin
828 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
829 // PLAYER_RECT.Width, PLAYER_RECT.Height);
831 mm := Max(abs(dx), abs(dy));
832 if mm = 0 then mm := 1;
834 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
835 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
836 end;
837 end;
839 h := High(gMonsters);
841 if h <> -1 then
842 for i := 0 to h do
843 if gMonsters[i] <> nil then
844 with gMonsters[i] do
845 begin
846 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
847 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
849 if dx > 1000 then dx := 1000;
850 if dy > 1000 then dy := 1000;
852 if dx*dx+dy*dy < r then
853 begin
854 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
855 // Obj.Rect.Width, Obj.Rect.Height);
857 mm := Max(abs(dx), abs(dy));
858 if mm = 0 then mm := 1;
860 if gMonsters[i].Live then
861 HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad,
862 0, 0, SpawnerUID, HIT_ROCKET);
864 gMonsters[i].Push((dx*7) div mm, (dy*7) div mm);
865 end;
866 end;
868 h := High(gCorpses);
870 if gAdvCorpses and (h <> -1) then
871 for i := 0 to h do
872 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
873 with gCorpses[i] do
874 begin
875 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
876 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
878 if dx > 1000 then dx := 1000;
879 if dy > 1000 then dy := 1000;
881 if dx*dx+dy*dy < r then
882 begin
883 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
884 Obj.Rect.Width, Obj.Rect.Height);
886 mm := Max(abs(dx), abs(dy));
887 if mm = 0 then mm := 1;
889 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
890 end;
891 end;
893 h := High(gGibs);
895 if gAdvGibs and (h <> -1) then
896 for i := 0 to h do
897 if gGibs[i].Live then
898 with gGibs[i] do
899 begin
900 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
901 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
903 if dx > 1000 then dx := 1000;
904 if dy > 1000 then dy := 1000;
906 if dx*dx+dy*dy < r then
907 begin
908 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
909 Obj.Rect.Width, Obj.Rect.Height);
910 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
911 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
913 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
914 end;
915 end;
916 end;
918 procedure g_Weapon_Init();
919 begin
920 CreateWaterMap();
921 end;
923 procedure g_Weapon_Free();
924 var
925 i: Integer;
926 begin
927 if Shots <> nil then
928 begin
929 for i := 0 to High(Shots) do
930 if Shots[i].ShotType <> 0 then
931 Shots[i].Animation.Free();
933 Shots := nil;
934 end;
936 WaterMap := nil;
937 end;
939 procedure g_Weapon_LoadData();
940 begin
941 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
943 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
944 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
945 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
946 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
947 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
948 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
949 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
950 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
951 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
952 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
953 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
954 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
955 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
956 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
957 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
958 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
959 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
960 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
961 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
962 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
963 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
964 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
965 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
966 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
967 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
968 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
969 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
970 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
971 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
972 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
973 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
974 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
976 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
977 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
978 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
979 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
980 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
981 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
982 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
983 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
984 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
985 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
986 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
987 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
988 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
989 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
990 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
991 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
992 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
993 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
994 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
995 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
997 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
998 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
999 end;
1001 procedure g_Weapon_FreeData();
1002 begin
1003 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1005 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1006 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1007 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1008 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1009 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1010 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1011 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1012 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1013 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1014 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1015 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1016 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1017 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1018 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1019 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1020 g_Sound_Delete('SOUND_FIRE');
1021 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1022 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1023 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1024 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1025 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1026 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1027 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1028 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1029 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1030 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1031 g_Sound_Delete('SOUND_PLAYER_JETON');
1032 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1033 g_Sound_Delete('SOUND_PLAYER_CASING1');
1034 g_Sound_Delete('SOUND_PLAYER_CASING2');
1035 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1036 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1038 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1039 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1040 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1041 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1042 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1043 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1044 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1045 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1046 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1047 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1048 g_Frames_DeleteByName('FRAMES_BFGHIT');
1049 g_Frames_DeleteByName('FRAMES_FIRE');
1050 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1051 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1052 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1053 g_Frames_DeleteByName('FRAMES_SMOKE');
1054 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1055 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1056 end;
1058 procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1059 var
1060 a: Integer;
1061 x2, y2: Integer;
1062 dx, dy: Integer;
1063 xe, ye: Integer;
1064 xi, yi: Integer;
1065 s, c: Extended;
1066 //vx, vy: Integer;
1067 xx, yy, d: Integer;
1069 i: Integer;
1070 t1, _collide: Boolean;
1071 w, h: Word;
1072 begin
1073 a := GetAngle(x, y, xd, yd)+180;
1075 SinCos(DegToRad(-a), s, c);
1077 if Abs(s) < 0.01 then s := 0;
1078 if Abs(c) < 0.01 then c := 0;
1080 x2 := x+Round(c*gMapInfo.Width);
1081 y2 := y+Round(s*gMapInfo.Width);
1083 t1 := gWalls <> nil;
1084 _collide := False;
1085 w := gMapInfo.Width;
1086 h := gMapInfo.Height;
1088 xe := 0;
1089 ye := 0;
1090 dx := x2-x;
1091 dy := y2-y;
1093 if (xd = 0) and (yd = 0) then Exit;
1095 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1096 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1098 dx := Abs(dx);
1099 dy := Abs(dy);
1101 if dx > dy then d := dx else d := dy;
1103 //blood vel, for Monster.Damage()
1104 //vx := (dx*10 div d)*xi;
1105 //vy := (dy*10 div d)*yi;
1107 xx := x;
1108 yy := y;
1110 for i := 1 to d do
1111 begin
1112 xe := xe+dx;
1113 ye := ye+dy;
1115 if xe > d then
1116 begin
1117 xe := xe-d;
1118 xx := xx+xi;
1119 end;
1121 if ye > d then
1122 begin
1123 ye := ye-d;
1124 yy := yy+yi;
1125 end;
1127 if (yy > h) or (yy < 0) then Break;
1128 if (xx > w) or (xx < 0) then Break;
1130 if t1 then
1131 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1132 begin
1133 _collide := True;
1134 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1135 if g_Game_IsServer and g_Game_IsNet then
1136 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1137 end;
1139 if not _collide then
1140 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1142 if _collide then
1143 Break;
1144 end;
1146 if CheckTrigger and g_Game_IsServer then
1147 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1148 end;
1150 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1151 var
1152 obj: TObj;
1153 begin
1154 obj.X := X;
1155 obj.Y := Y;
1156 obj.rect.X := 0;
1157 obj.rect.Y := 0;
1158 obj.rect.Width := 39;
1159 obj.rect.Height := 52;
1160 obj.Vel.X := 0;
1161 obj.Vel.Y := 0;
1162 obj.Accel.X := 0;
1163 obj.Accel.Y := 0;
1165 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1166 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1167 else
1168 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1169 end;
1171 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1172 var
1173 obj: TObj;
1174 begin
1175 obj.X := X;
1176 obj.Y := Y;
1177 obj.rect.X := 0;
1178 obj.rect.Y := 0;
1179 obj.rect.Width := 32;
1180 obj.rect.Height := 52;
1181 obj.Vel.X := 0;
1182 obj.Vel.Y := 0;
1183 obj.Accel.X := 0;
1184 obj.Accel.Y := 0;
1186 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1187 end;
1189 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1190 Silent: Boolean = False);
1191 var
1192 find_id: DWORD;
1193 dx, dy: Integer;
1194 begin
1195 if WID < 0 then
1196 find_id := FindShot()
1197 else
1198 begin
1199 find_id := WID;
1200 if Integer(find_id) >= High(Shots) then
1201 SetLength(Shots, find_id + 64)
1202 end;
1204 with Shots[find_id] do
1205 begin
1206 g_Obj_Init(@Obj);
1208 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1209 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1211 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1212 dy := -(Obj.Rect.Height div 2);
1213 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1215 Animation := nil;
1216 triggers := nil;
1217 ShotType := WEAPON_ROCKETLAUNCHER;
1218 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1219 end;
1221 Shots[find_id].SpawnerUID := SpawnerUID;
1223 if not Silent then
1224 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1225 end;
1227 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1228 WID: Integer = -1; Silent: Boolean = False);
1229 var
1230 find_id, FramesID: DWORD;
1231 dx, dy: Integer;
1232 begin
1233 if WID < 0 then
1234 find_id := FindShot()
1235 else
1236 begin
1237 find_id := WID;
1238 if Integer(find_id) >= High(Shots) then
1239 SetLength(Shots, find_id + 64)
1240 end;
1242 with Shots[find_id] do
1243 begin
1244 g_Obj_Init(@Obj);
1246 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1247 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1249 dx := -(Obj.Rect.Width div 2);
1250 dy := -(Obj.Rect.Height div 2);
1251 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1253 triggers := nil;
1254 ShotType := WEAPON_SKEL_FIRE;
1255 target := TargetUID;
1256 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1257 Animation := TAnimation.Create(FramesID, True, 5);
1258 end;
1260 Shots[find_id].SpawnerUID := SpawnerUID;
1262 if not Silent then
1263 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1264 end;
1266 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1267 Silent: Boolean = False);
1268 var
1269 find_id, FramesID: DWORD;
1270 dx, dy: Integer;
1271 begin
1272 if WID < 0 then
1273 find_id := FindShot()
1274 else
1275 begin
1276 find_id := WID;
1277 if Integer(find_id) >= High(Shots) then
1278 SetLength(Shots, find_id + 64);
1279 end;
1281 with Shots[find_id] do
1282 begin
1283 g_Obj_Init(@Obj);
1285 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1286 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1288 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1289 dy := -(Obj.Rect.Height div 2);
1290 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1292 triggers := nil;
1293 ShotType := WEAPON_PLASMA;
1294 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1295 Animation := TAnimation.Create(FramesID, True, 5);
1296 end;
1298 Shots[find_id].SpawnerUID := SpawnerUID;
1300 if not Silent then
1301 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1302 end;
1304 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1305 Silent: Boolean = False);
1306 var
1307 find_id, FramesID: DWORD;
1308 dx, dy: Integer;
1309 begin
1310 if WID < 0 then
1311 find_id := FindShot()
1312 else
1313 begin
1314 find_id := WID;
1315 if Integer(find_id) >= High(Shots) then
1316 SetLength(Shots, find_id + 64)
1317 end;
1319 with Shots[find_id] do
1320 begin
1321 g_Obj_Init(@Obj);
1323 Obj.Rect.Width := 16;
1324 Obj.Rect.Height := 16;
1326 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1327 dy := -(Obj.Rect.Height div 2);
1328 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1330 triggers := nil;
1331 ShotType := WEAPON_IMP_FIRE;
1332 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1333 Animation := TAnimation.Create(FramesID, True, 4);
1334 end;
1336 Shots[find_id].SpawnerUID := SpawnerUID;
1338 if not Silent then
1339 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1340 end;
1342 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1343 Silent: Boolean = False);
1344 var
1345 find_id, FramesID: DWORD;
1346 dx, dy: Integer;
1347 begin
1348 if WID < 0 then
1349 find_id := FindShot()
1350 else
1351 begin
1352 find_id := WID;
1353 if Integer(find_id) >= High(Shots) then
1354 SetLength(Shots, find_id + 64)
1355 end;
1357 with Shots[find_id] do
1358 begin
1359 g_Obj_Init(@Obj);
1361 Obj.Rect.Width := 16;
1362 Obj.Rect.Height := 16;
1364 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1365 dy := -(Obj.Rect.Height div 2);
1366 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1368 triggers := nil;
1369 ShotType := WEAPON_CACO_FIRE;
1370 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1371 Animation := TAnimation.Create(FramesID, True, 4);
1372 end;
1374 Shots[find_id].SpawnerUID := SpawnerUID;
1376 if not Silent then
1377 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1378 end;
1380 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1381 Silent: Boolean = False);
1382 var
1383 find_id, FramesID: DWORD;
1384 dx, dy: Integer;
1385 begin
1386 if WID < 0 then
1387 find_id := FindShot()
1388 else
1389 begin
1390 find_id := WID;
1391 if Integer(find_id) >= High(Shots) then
1392 SetLength(Shots, find_id + 64)
1393 end;
1395 with Shots[find_id] do
1396 begin
1397 g_Obj_Init(@Obj);
1399 Obj.Rect.Width := 32;
1400 Obj.Rect.Height := 16;
1402 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1403 dy := -(Obj.Rect.Height div 2);
1404 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1406 triggers := nil;
1407 ShotType := WEAPON_BARON_FIRE;
1408 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1409 Animation := TAnimation.Create(FramesID, True, 4);
1410 end;
1412 Shots[find_id].SpawnerUID := SpawnerUID;
1414 if not Silent then
1415 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1416 end;
1418 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1419 Silent: Boolean = False);
1420 var
1421 find_id, FramesID: DWORD;
1422 dx, dy: Integer;
1423 begin
1424 if WID < 0 then
1425 find_id := FindShot()
1426 else
1427 begin
1428 find_id := WID;
1429 if Integer(find_id) >= High(Shots) then
1430 SetLength(Shots, find_id + 64)
1431 end;
1433 with Shots[find_id] do
1434 begin
1435 g_Obj_Init(@Obj);
1437 Obj.Rect.Width := 16;
1438 Obj.Rect.Height := 16;
1440 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1441 dy := -(Obj.Rect.Height div 2);
1442 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1444 triggers := nil;
1445 ShotType := WEAPON_BSP_FIRE;
1446 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1447 Animation := TAnimation.Create(FramesID, True, 4);
1448 end;
1450 Shots[find_id].SpawnerUID := SpawnerUID;
1452 if not Silent then
1453 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1454 end;
1456 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1457 Silent: Boolean = False);
1458 var
1459 find_id, FramesID: DWORD;
1460 dx, dy: Integer;
1461 begin
1462 if WID < 0 then
1463 find_id := FindShot()
1464 else
1465 begin
1466 find_id := WID;
1467 if Integer(find_id) >= High(Shots) then
1468 SetLength(Shots, find_id + 64)
1469 end;
1471 with Shots[find_id] do
1472 begin
1473 g_Obj_Init(@Obj);
1475 Obj.Rect.Width := 32;
1476 Obj.Rect.Height := 32;
1478 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1479 dy := -(Obj.Rect.Height div 2);
1480 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1482 triggers := nil;
1483 ShotType := WEAPON_MANCUB_FIRE;
1484 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1485 Animation := TAnimation.Create(FramesID, True, 4);
1486 end;
1488 Shots[find_id].SpawnerUID := SpawnerUID;
1490 if not Silent then
1491 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1492 end;
1494 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1495 Silent: Boolean = False);
1496 var
1497 find_id, FramesID: DWORD;
1498 dx, dy: Integer;
1499 begin
1500 if WID < 0 then
1501 find_id := FindShot()
1502 else
1503 begin
1504 find_id := WID;
1505 if Integer(find_id) >= High(Shots) then
1506 SetLength(Shots, find_id + 64)
1507 end;
1509 with Shots[find_id] do
1510 begin
1511 g_Obj_Init(@Obj);
1513 Obj.Rect.Width := SHOT_BFG_WIDTH;
1514 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1516 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1517 dy := -(Obj.Rect.Height div 2);
1518 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1520 triggers := nil;
1521 ShotType := WEAPON_BFG;
1522 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
1523 Animation := TAnimation.Create(FramesID, True, 6);
1524 end;
1526 Shots[find_id].SpawnerUID := SpawnerUID;
1528 if not Silent then
1529 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
1530 end;
1532 procedure g_Weapon_bfghit(x, y: Integer);
1533 var
1534 ID: DWORD;
1535 Anim: TAnimation;
1536 begin
1537 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1538 begin
1539 Anim := TAnimation.Create(ID, False, 4);
1540 g_GFX_OnceAnim(x-32, y-32, Anim);
1541 Anim.Free();
1542 end;
1543 end;
1545 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
1546 Silent: Boolean = False);
1547 begin
1548 if not Silent then
1549 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
1551 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1552 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
1553 begin
1554 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
1555 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1556 end;
1557 end;
1559 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1560 Silent: Boolean = False);
1561 begin
1562 if not Silent then
1563 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
1565 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
1566 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
1567 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1568 begin
1569 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
1570 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
1571 end;
1572 end;
1574 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1575 Silent: Boolean = False);
1576 var
1577 i, j: Integer;
1578 begin
1579 if not Silent then
1580 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
1582 for i := 0 to 9 do
1583 begin
1584 j := Random(17)-8; // -8 .. 8
1585 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
1586 end;
1587 end;
1589 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
1590 Silent: Boolean = False);
1591 var
1592 a, i, j: Integer;
1593 begin
1594 if not Silent then
1595 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
1597 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
1598 for i := 0 to a do
1599 begin
1600 j := Random(41)-20; // -20 .. 20
1601 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
1602 end;
1603 end;
1605 procedure g_Weapon_Update();
1606 var
1607 i, a, h, cx, cy, oldvx, oldvy: Integer;
1608 _id: DWORD;
1609 Anim: TAnimation;
1610 t: DWArray;
1611 st: Word;
1612 s: String;
1613 o: TObj;
1614 spl: Boolean;
1615 Loud: Boolean;
1616 begin
1617 if Shots = nil then
1618 Exit;
1620 for i := 0 to High(Shots) do
1621 begin
1622 if Shots[i].ShotType = 0 then
1623 Continue;
1625 Loud := True;
1627 with Shots[i] do
1628 begin
1629 Timeout := Timeout - 1;
1630 oldvx := Obj.Vel.X;
1631 oldvy := Obj.Vel.Y;
1632 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
1633 if g_Game_IsServer then
1634 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
1635 SpawnerUID, ACTIVATE_SHOT, triggers)
1636 else
1637 t := nil;
1639 if t <> nil then
1640 begin
1641 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
1642 if triggers = nil then
1643 triggers := t
1644 else
1645 begin
1646 h := High(t);
1648 for a := 0 to h do
1649 if not InDWArray(t[a], triggers) then
1650 begin
1651 SetLength(triggers, Length(triggers)+1);
1652 triggers[High(triggers)] := t[a];
1653 end;
1654 end;
1655 end;
1657 // Àíèìàöèÿ ñíàðÿäà:
1658 if Animation <> nil then
1659 Animation.Update();
1661 // Äâèæåíèå:
1662 spl := (ShotType <> WEAPON_PLASMA) and
1663 (ShotType <> WEAPON_BFG) and
1664 (ShotType <> WEAPON_BSP_FIRE);
1666 st := g_Obj_Move(@Obj, False, spl);
1668 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
1669 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
1670 begin
1671 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
1672 ShotType := 0;
1673 Animation.Free();
1674 Continue;
1675 end;
1677 cx := Obj.X + (Obj.Rect.Width div 2);
1678 cy := Obj.Y + (Obj.Rect.Height div 2);
1680 case ShotType of
1681 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
1682 begin
1683 // Âûëåòåëà èç âîäû:
1684 if WordBool(st and MOVE_HITAIR) then
1685 g_Obj_SetSpeed(@Obj, 12);
1687 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
1688 if WordBool(st and MOVE_INWATER) then
1689 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
1690 Obj.Y+(Obj.Rect.Height div 2),
1691 1+Random(3), 16, 16)
1692 else
1693 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
1694 begin
1695 Anim := TAnimation.Create(_id, False, 3);
1696 Anim.Alpha := 150;
1697 g_GFX_OnceAnim(Obj.X-14+Random(9),
1698 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
1699 Anim, ONCEANIM_SMOKE);
1700 Anim.Free();
1701 end;
1703 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1704 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1705 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
1706 (Timeout < 1) then
1707 begin
1708 Obj.Vel.X := 0;
1709 Obj.Vel.Y := 0;
1711 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
1713 if ShotType = WEAPON_SKEL_FIRE then
1714 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
1715 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
1716 begin
1717 Anim := TAnimation.Create(TextureID, False, 8);
1718 Anim.Blending := False;
1719 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
1720 Anim.Free();
1721 end;
1722 end
1723 else
1724 begin // Âçðûâ Ðàêåòû
1725 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1726 begin
1727 Anim := TAnimation.Create(TextureID, False, 6);
1728 Anim.Blending := False;
1729 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1730 Anim.Free();
1731 end;
1732 end;
1734 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
1736 ShotType := 0;
1737 end;
1739 if ShotType = WEAPON_SKEL_FIRE then
1740 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
1741 if GetPos(target, @o) then
1742 throw(i, Obj.X, Obj.Y,
1743 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
1744 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
1745 12);
1746 end;
1747 end;
1749 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
1750 begin
1751 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1752 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1753 begin
1754 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
1755 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
1756 ShotType := 0;
1757 Continue;
1758 end;
1760 // Âåëè÷èíà óðîíà:
1761 if (ShotType = WEAPON_PLASMA) and
1762 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1763 a := 10
1764 else
1765 a := 5;
1767 if ShotType = WEAPON_BSP_FIRE then
1768 a := 10;
1770 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1771 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1772 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
1773 (Timeout < 1) then
1774 begin
1775 if ShotType = WEAPON_PLASMA then
1776 s := 'FRAMES_EXPLODE_PLASMA'
1777 else
1778 s := 'FRAMES_EXPLODE_BSPFIRE';
1780 // Âçðûâ Ïëàçìû:
1781 if g_Frames_Get(TextureID, s) then
1782 begin
1783 Anim := TAnimation.Create(TextureID, False, 3);
1784 Anim.Blending := False;
1785 g_GFX_OnceAnim(cx-16, cy-16, Anim);
1786 Anim.Free();
1787 end;
1789 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
1791 ShotType := 0;
1792 end;
1793 end;
1795 WEAPON_BFG: // BFG
1796 begin
1797 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
1798 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
1799 begin
1800 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
1801 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
1802 ShotType := 0;
1803 Continue;
1804 end;
1806 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1807 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1808 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
1809 (Timeout < 1) then
1810 begin
1811 // Ëó÷è BFG:
1812 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
1814 // Âçðûâ BFG:
1815 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
1816 begin
1817 Anim := TAnimation.Create(TextureID, False, 6);
1818 Anim.Blending := False;
1819 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1820 Anim.Free();
1821 end;
1823 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
1825 ShotType := 0;
1826 end;
1827 end;
1829 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
1830 begin
1831 // Âûëåòåë èç âîäû:
1832 if WordBool(st and MOVE_HITAIR) then
1833 g_Obj_SetSpeed(@Obj, 16);
1835 // Âåëè÷èíà óðîíà:
1836 if ShotType = WEAPON_IMP_FIRE then
1837 a := 5
1838 else
1839 if ShotType = WEAPON_CACO_FIRE then
1840 a := 20
1841 else
1842 a := 40;
1844 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1845 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1846 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
1847 (Timeout < 1) then
1848 begin
1849 if ShotType = WEAPON_IMP_FIRE then
1850 s := 'FRAMES_EXPLODE_IMPFIRE'
1851 else
1852 if ShotType = WEAPON_CACO_FIRE then
1853 s := 'FRAMES_EXPLODE_CACOFIRE'
1854 else
1855 s := 'FRAMES_EXPLODE_BARONFIRE';
1857 // Âçðûâ:
1858 if g_Frames_Get(TextureID, s) then
1859 begin
1860 Anim := TAnimation.Create(TextureID, False, 6);
1861 Anim.Blending := False;
1862 g_GFX_OnceAnim(cx-32, cy-32, Anim);
1863 Anim.Free();
1864 end;
1866 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1868 ShotType := 0;
1869 end;
1870 end;
1872 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
1873 begin
1874 // Âûëåòåë èç âîäû:
1875 if WordBool(st and MOVE_HITAIR) then
1876 g_Obj_SetSpeed(@Obj, 16);
1878 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
1879 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
1880 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
1881 (Timeout < 1) then
1882 begin
1883 // Âçðûâ:
1884 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
1885 begin
1886 Anim := TAnimation.Create(TextureID, False, 6);
1887 Anim.Blending := False;
1888 g_GFX_OnceAnim(cx-64, cy-64, Anim);
1889 Anim.Free();
1890 end;
1892 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
1894 ShotType := 0;
1895 end;
1896 end;
1897 end; // case ShotType of...
1899 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
1900 if (ShotType = 0) then
1901 begin
1902 if gGameSettings.GameType = GT_SERVER then
1903 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
1904 Animation.Free();
1905 Animation := nil;
1906 end
1907 else if (oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y) then
1908 if gGameSettings.GameType = GT_SERVER then
1909 MH_SEND_UpdateShot(i);
1910 end;
1911 end;
1912 end;
1914 procedure g_Weapon_Draw();
1915 var
1916 i: Integer;
1917 a: SmallInt;
1918 p: TPoint;
1919 begin
1920 if Shots = nil then
1921 Exit;
1923 for i := 0 to High(Shots) do
1924 if Shots[i].ShotType <> 0 then
1925 with Shots[i] do
1926 begin
1927 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
1928 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1929 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1930 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1931 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
1932 else
1933 a := 0;
1935 p.X := Obj.Rect.Width div 2;
1936 p.Y := Obj.Rect.Height div 2;
1938 if Animation <> nil then
1939 begin
1940 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
1941 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
1942 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
1943 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
1944 else
1945 Animation.Draw(Obj.X, Obj.Y, M_NONE);
1946 end
1947 else
1948 begin
1949 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
1950 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
1951 else
1952 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
1953 end;
1955 if g_debug_Frames then
1956 begin
1957 e_DrawQuad(Obj.X+Obj.Rect.X,
1958 Obj.Y+Obj.Rect.Y,
1959 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
1960 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
1961 0, 255, 0);
1962 end;
1963 end;
1964 end;
1966 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
1967 var
1968 a: Integer;
1969 begin
1970 Result := False;
1972 if Shots = nil then
1973 Exit;
1975 for a := 0 to High(Shots) do
1976 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
1977 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
1978 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
1979 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
1980 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
1981 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
1982 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
1983 begin
1984 Result := True;
1985 Exit;
1986 end;
1987 end;
1989 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
1990 var
1991 count, i, j: Integer;
1992 dw: DWORD;
1993 begin
1994 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
1995 count := 0;
1996 if Shots <> nil then
1997 for i := 0 to High(Shots) do
1998 if Shots[i].ShotType <> 0 then
1999 count := count + 1;
2001 Mem := TBinMemoryWriter.Create((count+1) * 80);
2003 // Êîëè÷åñòâî ñíàðÿäîâ:
2004 Mem.WriteInt(count);
2006 if count = 0 then
2007 Exit;
2009 for i := 0 to High(Shots) do
2010 if Shots[i].ShotType <> 0 then
2011 begin
2012 // Ñèãíàòóðà ñíàðÿäà:
2013 dw := SHOT_SIGNATURE; // 'SHOT'
2014 Mem.WriteDWORD(dw);
2015 // Òèï ñíàðÿäà:
2016 Mem.WriteByte(Shots[i].ShotType);
2017 // Öåëü:
2018 Mem.WriteWord(Shots[i].Target);
2019 // UID ñòðåëÿâøåãî:
2020 Mem.WriteWord(Shots[i].SpawnerUID);
2021 // Ðàçìåð ïîëÿ Triggers:
2022 dw := Length(Shots[i].Triggers);
2023 Mem.WriteDWORD(dw);
2024 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2025 for j := 0 to Integer(dw)-1 do
2026 Mem.WriteDWORD(Shots[i].Triggers[j]);
2027 // Îáúåêò ñíàðÿäà:
2028 Obj_SaveState(@Shots[i].Obj, Mem);
2029 end;
2030 end;
2032 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2033 var
2034 count, i, j: Integer;
2035 dw: DWORD;
2036 begin
2037 if Mem = nil then
2038 Exit;
2040 // Êîëè÷åñòâî ñíàðÿäîâ:
2041 Mem.ReadInt(count);
2043 SetLength(Shots, count);
2045 if count = 0 then
2046 Exit;
2048 for i := 0 to count-1 do
2049 begin
2050 // Ñèãíàòóðà ñíàðÿäà:
2051 Mem.ReadDWORD(dw);
2052 if dw <> SHOT_SIGNATURE then // 'SHOT'
2053 begin
2054 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2055 end;
2056 // Òèï ñíàðÿäà:
2057 Mem.ReadByte(Shots[i].ShotType);
2058 // Öåëü:
2059 Mem.ReadWord(Shots[i].Target);
2060 // UID ñòðåëÿâøåãî:
2061 Mem.ReadWord(Shots[i].SpawnerUID);
2062 // Ðàçìåð ïîëÿ Triggers:
2063 Mem.ReadDWORD(dw);
2064 SetLength(Shots[i].Triggers, dw);
2065 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2066 for j := 0 to Integer(dw)-1 do
2067 Mem.ReadDWORD(Shots[i].Triggers[j]);
2068 // Îáúåêò ïðåäìåòà:
2069 Obj_LoadState(@Shots[i].Obj, Mem);
2071 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2072 Shots[i].TextureID := DWORD(-1);
2073 Shots[i].Animation := nil;
2075 case Shots[i].ShotType of
2076 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2077 begin
2078 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2079 end;
2080 WEAPON_PLASMA:
2081 begin
2082 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2083 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2084 end;
2085 WEAPON_BFG:
2086 begin
2087 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2088 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2089 end;
2090 WEAPON_IMP_FIRE:
2091 begin
2092 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2093 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2094 end;
2095 WEAPON_BSP_FIRE:
2096 begin
2097 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2098 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2099 end;
2100 WEAPON_CACO_FIRE:
2101 begin
2102 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2103 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2104 end;
2105 WEAPON_BARON_FIRE:
2106 begin
2107 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2108 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2109 end;
2110 WEAPON_MANCUB_FIRE:
2111 begin
2112 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2113 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2114 end;
2115 end;
2116 end;
2117 end;
2119 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2120 var
2121 cx, cy: Integer;
2122 Anim: TAnimation;
2123 s: string;
2124 begin
2125 if Shots = nil then
2126 Exit;
2127 if (I > High(Shots)) or (I < 0) then Exit;
2129 with Shots[I] do
2130 begin
2131 if ShotType = 0 then Exit;
2132 Obj.X := X;
2133 Obj.Y := Y;
2134 cx := Obj.X + (Obj.Rect.Width div 2);
2135 cy := Obj.Y + (Obj.Rect.Height div 2);
2137 case ShotType of
2138 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2139 begin
2140 if Loud then
2141 begin
2142 if ShotType = WEAPON_SKEL_FIRE then
2143 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2144 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2145 begin
2146 Anim := TAnimation.Create(TextureID, False, 8);
2147 Anim.Blending := False;
2148 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2149 Anim.Free();
2150 end;
2151 end
2152 else
2153 begin // Âçðûâ Ðàêåòû
2154 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2155 begin
2156 Anim := TAnimation.Create(TextureID, False, 6);
2157 Anim.Blending := False;
2158 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2159 Anim.Free();
2160 end;
2161 end;
2162 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2163 end;
2164 end;
2166 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2167 begin
2168 if ShotType = WEAPON_PLASMA then
2169 s := 'FRAMES_EXPLODE_PLASMA'
2170 else
2171 s := 'FRAMES_EXPLODE_BSPFIRE';
2173 if g_Frames_Get(TextureID, s) and loud then
2174 begin
2175 Anim := TAnimation.Create(TextureID, False, 3);
2176 Anim.Blending := False;
2177 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2178 Anim.Free();
2180 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2181 end;
2182 end;
2184 WEAPON_BFG: // BFG
2185 begin
2186 // Âçðûâ BFG:
2187 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2188 begin
2189 Anim := TAnimation.Create(TextureID, False, 6);
2190 Anim.Blending := False;
2191 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2192 Anim.Free();
2194 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2195 end;
2196 end;
2198 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2199 begin
2200 if ShotType = WEAPON_IMP_FIRE then
2201 s := 'FRAMES_EXPLODE_IMPFIRE'
2202 else
2203 if ShotType = WEAPON_CACO_FIRE then
2204 s := 'FRAMES_EXPLODE_CACOFIRE'
2205 else
2206 s := 'FRAMES_EXPLODE_BARONFIRE';
2208 if g_Frames_Get(TextureID, s) and Loud then
2209 begin
2210 Anim := TAnimation.Create(TextureID, False, 6);
2211 Anim.Blending := False;
2212 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2213 Anim.Free();
2215 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2216 end;
2217 end;
2219 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2220 begin
2221 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2222 begin
2223 Anim := TAnimation.Create(TextureID, False, 6);
2224 Anim.Blending := False;
2225 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2226 Anim.Free();
2228 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2229 end;
2230 end;
2231 end; // case ShotType of...
2233 ShotType := 0;
2234 Animation.Free();
2235 end;
2236 end;
2238 end.