DEADSOFTWARE

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