DEADSOFTWARE

game: disable gfx for server
[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, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
17 unit g_weapons;
19 interface
21 uses
22 SysUtils, Classes, mempool,
23 g_textures, g_basic, g_phys, xprofiler;
26 type
27 TShot = record
28 ShotType: Byte;
29 Target: Word;
30 SpawnerUID: Word;
31 Triggers: DWArray;
32 Obj: TObj;
33 Animation: TAnimationState;
34 Timeout: DWORD;
35 Stopped: Byte;
37 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
38 end;
41 var
42 Shots: array of TShot = nil;
43 LastShotID: Integer = 0;
45 procedure g_Weapon_LoadData();
46 procedure g_Weapon_FreeData();
47 procedure g_Weapon_Init();
48 procedure g_Weapon_Free();
49 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
50 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
51 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
53 procedure g_Weapon_gun(const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
54 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
55 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
56 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
57 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
58 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
59 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
60 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
61 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
62 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
63 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
64 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
65 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
66 procedure g_Weapon_bfghit(x, y: Integer);
67 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
68 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
69 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
70 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
72 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
73 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
74 procedure g_Weapon_PreUpdate();
75 procedure g_Weapon_Update();
76 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
77 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
79 procedure g_Weapon_SaveState (st: TStream);
80 procedure g_Weapon_LoadState (st: TStream);
82 procedure g_Weapon_AddDynLights();
84 const
85 WEAPON_KASTET = 0;
86 WEAPON_SAW = 1;
87 WEAPON_PISTOL = 2;
88 WEAPON_SHOTGUN1 = 3;
89 WEAPON_SHOTGUN2 = 4;
90 WEAPON_CHAINGUN = 5;
91 WEAPON_ROCKETLAUNCHER = 6;
92 WEAPON_PLASMA = 7;
93 WEAPON_BFG = 8;
94 WEAPON_SUPERPULEMET = 9;
95 WEAPON_FLAMETHROWER = 10;
96 WEAPON_ZOMBY_PISTOL = 20;
97 WEAPON_IMP_FIRE = 21;
98 WEAPON_BSP_FIRE = 22;
99 WEAPON_CACO_FIRE = 23;
100 WEAPON_BARON_FIRE = 24;
101 WEAPON_MANCUB_FIRE = 25;
102 WEAPON_SKEL_FIRE = 26;
104 WP_FIRST = WEAPON_KASTET;
105 WP_LAST = WEAPON_FLAMETHROWER;
107 var
108 gwep_debug_fast_trace: Boolean = true;
111 implementation
113 uses
114 {$IFDEF ENABLE_GFX}
115 g_gfx,
116 {$ENDIF}
117 Math, g_map, g_player, g_sound, g_panel,
118 g_console, g_options, g_game,
119 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
120 g_language, g_netmsg, g_grid,
121 geom, binheap, hashtable, utils, xstreams
124 type
125 TWaterPanel = record
126 X, Y: Integer;
127 Width, Height: Word;
128 Active: Boolean;
129 end;
131 const
132 SHOT_ROCKETLAUNCHER_WIDTH = 14;
133 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
135 SHOT_SKELFIRE_WIDTH = 14;
136 SHOT_SKELFIRE_HEIGHT = 14;
138 SHOT_PLASMA_WIDTH = 16;
139 SHOT_PLASMA_HEIGHT = 16;
141 SHOT_BFG_WIDTH = 32;
142 SHOT_BFG_HEIGHT = 32;
143 SHOT_BFG_DAMAGE = 100;
144 SHOT_BFG_RADIUS = 256;
146 SHOT_FLAME_WIDTH = 4;
147 SHOT_FLAME_HEIGHT = 4;
148 SHOT_FLAME_LIFETIME = 180;
150 SHOT_SIGNATURE = $544F4853; // 'SHOT'
152 type
153 PHitTime = ^THitTime;
154 THitTime = record
155 distSq: Integer;
156 mon: TMonster;
157 plridx: Integer; // if mon=nil
158 x, y: Integer;
159 end;
161 TBinHeapKeyHitTime = class
162 public
163 class function less (const a, b: Integer): Boolean; inline;
164 end;
166 // indicies in `wgunHitTime` array
167 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer, TBinHeapKeyHitTime>;
169 var
170 WaterMap: array of array of DWORD = nil;
171 //wgunMonHash: THashIntInt = nil;
172 wgunHitHeap: TBinaryHeapHitTimes = nil;
173 wgunHitTime: array of THitTime = nil;
174 wgunHitTimeUsed: Integer = 0;
177 class function TBinHeapKeyHitTime.less (const a, b: Integer): Boolean;
178 var
179 hta, htb: PHitTime;
180 begin
181 hta := @wgunHitTime[a];
182 htb := @wgunHitTime[b];
183 if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end;
184 if (hta.mon <> nil) then
185 begin
186 // a is monster
187 if (htb.mon = nil) then begin result := false; exit; end; // players first
188 result := (hta.mon.UID < htb.mon.UID); // why not?
189 end
190 else
191 begin
192 // a is player
193 if (htb.mon <> nil) then begin result := true; exit; end; // players first
194 result := (hta.plridx < htb.plridx); // why not?
195 end;
196 end;
199 procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer);
200 begin
201 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
202 with wgunHitTime[wgunHitTimeUsed] do
203 begin
204 distSq := adistSq;
205 mon := amon;
206 plridx := -1;
207 x := ax;
208 y := ay;
209 end;
210 wgunHitHeap.insert(wgunHitTimeUsed);
211 Inc(wgunHitTimeUsed);
212 end;
215 procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer);
216 begin
217 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
218 with wgunHitTime[wgunHitTimeUsed] do
219 begin
220 distSq := adistSq;
221 mon := nil;
222 plridx := aplridx;
223 x := ax;
224 y := ay;
225 end;
226 wgunHitHeap.insert(wgunHitTimeUsed);
227 Inc(wgunHitTimeUsed);
228 end;
231 function FindShot(): DWORD;
232 var
233 i: Integer;
234 begin
235 if Shots <> nil then
236 for i := 0 to High(Shots) do
237 if Shots[i].ShotType = 0 then
238 begin
239 Result := i;
240 LastShotID := Result;
241 Exit;
242 end;
244 if Shots = nil then
245 begin
246 SetLength(Shots, 128);
247 Result := 0;
248 end
249 else
250 begin
251 Result := High(Shots) + 1;
252 SetLength(Shots, Length(Shots) + 128);
253 end;
254 LastShotID := Result;
255 end;
257 procedure CreateWaterMap();
258 var
259 WaterArray: Array of TWaterPanel;
260 a, b, c, m: Integer;
261 ok: Boolean;
262 begin
263 if gWater = nil then
264 Exit;
266 SetLength(WaterArray, Length(gWater));
268 for a := 0 to High(gWater) do
269 begin
270 WaterArray[a].X := gWater[a].X;
271 WaterArray[a].Y := gWater[a].Y;
272 WaterArray[a].Width := gWater[a].Width;
273 WaterArray[a].Height := gWater[a].Height;
274 WaterArray[a].Active := True;
275 end;
277 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
279 for a := 0 to High(WaterArray) do
280 if WaterArray[a].Active then
281 begin
282 WaterArray[a].Active := False;
283 m := Length(WaterMap);
284 SetLength(WaterMap, m+1);
285 SetLength(WaterMap[m], 1);
286 WaterMap[m][0] := a;
287 ok := True;
289 while ok do
290 begin
291 ok := False;
292 for b := 0 to High(WaterArray) do
293 if WaterArray[b].Active then
294 for c := 0 to High(WaterMap[m]) do
295 if g_CollideAround(WaterArray[b].X,
296 WaterArray[b].Y,
297 WaterArray[b].Width,
298 WaterArray[b].Height,
299 WaterArray[WaterMap[m][c]].X,
300 WaterArray[WaterMap[m][c]].Y,
301 WaterArray[WaterMap[m][c]].Width,
302 WaterArray[WaterMap[m][c]].Height) then
303 begin
304 WaterArray[b].Active := False;
305 SetLength(WaterMap[m],
306 Length(WaterMap[m])+1);
307 WaterMap[m][High(WaterMap[m])] := b;
308 ok := True;
309 Break;
310 end;
311 end;
313 g_Game_StepLoading();
314 end;
316 WaterArray := nil;
317 end;
320 var
321 chkTrap_pl: array [0..256] of Integer;
322 chkTrap_mn: array [0..65535] of TMonster;
324 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
325 var
326 //a, b, c, d, i1, i2: Integer;
327 //chkTrap_pl, chkTrap_mn: WArray;
328 plaCount: Integer = 0;
329 mnaCount: Integer = 0;
330 frameId: DWord;
333 function monsWaterCheck (mon: TMonster): Boolean;
334 begin
335 result := false; // don't stop
336 if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
337 begin
338 i2 += 1;
339 chkTrap_mn[i2] := monidx;
340 end;
341 end;
344 function monsWaterCheck (mon: TMonster): Boolean;
345 begin
346 result := false; // don't stop
347 if (mon.trapCheckFrameId <> frameId) then
348 begin
349 mon.trapCheckFrameId := frameId;
350 chkTrap_mn[mnaCount] := mon;
351 Inc(mnaCount);
352 end;
353 end;
355 var
356 a, b, c, d, f: Integer;
357 pan: TPanel;
358 begin
359 if (gWater = nil) or (WaterMap = nil) then Exit;
361 frameId := g_Mons_getNewTrapFrameId();
363 //i1 := -1;
364 //i2 := -1;
366 //SetLength(chkTrap_pl, 1024);
367 //SetLength(chkTrap_mn, 1024);
368 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
369 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
371 for a := 0 to High(WaterMap) do
372 begin
373 for b := 0 to High(WaterMap[a]) do
374 begin
375 pan := gWater[WaterMap[a][b]];
376 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
378 for c := 0 to High(WaterMap[a]) do
379 begin
380 pan := gWater[WaterMap[a][c]];
381 for d := 0 to High(gPlayers) do
382 begin
383 if (gPlayers[d] <> nil) and (gPlayers[d].alive) then
384 begin
385 if gPlayers[d].Collide(pan) then
386 begin
387 f := 0;
388 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
389 if (f = plaCount) then
390 begin
391 chkTrap_pl[plaCount] := d;
392 Inc(plaCount);
393 if (plaCount = Length(chkTrap_pl)) then break;
394 end;
395 end;
396 end;
397 end;
399 //g_Mons_ForEach(monsWaterCheck);
400 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
401 end;
403 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
404 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
405 end;
406 end;
408 //chkTrap_pl := nil;
409 //chkTrap_mn := nil;
410 end;
412 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
413 var
414 tt, mt: Byte;
415 mon: TMonster;
416 begin
417 Result := False;
419 tt := g_GetUIDType(SpawnerUID);
420 if tt = UID_MONSTER then
421 begin
422 mon := g_Monsters_ByUID(SpawnerUID);
423 if mon <> nil then
424 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
425 else
426 mt := 0;
427 end
428 else
429 mt := 0;
431 if m = nil then Exit;
432 if m.UID = SpawnerUID then
433 begin
434 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
435 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
436 Exit;
437 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
438 if (m.MonsterType = MONSTER_CYBER) or
439 (m.MonsterType = MONSTER_BARREL) then
440 begin
441 Result := True;
442 Exit;
443 end;
444 end;
446 if tt = UID_MONSTER then
447 begin
448 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
449 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
450 Exit;
452 // Îáà ìîíñòðà îäíîãî âèäà:
453 if mt = m.MonsterType then
454 case mt of
455 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
456 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
457 Exit; // Ýòè íå áüþò ñâîèõ
458 end;
459 end;
461 if g_Game_IsServer then
462 begin
463 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
464 Result := m.Damage(d, vx, vy, SpawnerUID, t)
465 else
466 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
467 if t = HIT_FLAME then
468 m.CatchFire(SpawnerUID);
469 end
470 else
471 Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time
472 end;
475 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
476 begin
477 result := False;
479 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
480 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
482 if g_Game_IsServer then
483 begin
484 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
485 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
486 end;
488 result := true;
489 end;
492 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
494 function monsCheck (mon: TMonster): Boolean;
495 begin
496 result := false; // don't stop
497 if (mon.alive) and (mon.UID <> SpawnerUID) then
498 begin
499 with mon do
500 begin
501 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
502 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
503 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
504 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
505 begin
506 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
507 end;
508 end;
509 end;
510 end;
512 var
513 i, h: Integer;
514 st: Byte;
515 pl: TPlayer;
516 b: Boolean;
517 begin
518 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
520 h := High(gCorpses);
522 if gAdvCorpses and (h <> -1) then
523 for i := 0 to h do
524 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
525 with gCorpses[i] do
526 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
527 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
528 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
529 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
530 begin
531 Damage(50, SpawnerUID, 0, 0);
532 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
533 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
534 end;
536 st := TEAM_NONE;
537 pl := g_Player_Get(SpawnerUID);
538 if pl <> nil then
539 st := pl.Team;
541 h := High(gPlayers);
543 if h <> -1 then
544 for i := 0 to h do
545 if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then
546 with gPlayers[i] do
547 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
548 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
549 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
550 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
551 begin
552 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
553 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
554 else
555 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
556 if b then
557 gPlayers[i].BFGHit();
558 end;
560 //FIXME
561 g_Mons_ForEachAlive(monsCheck);
562 end;
564 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
565 var
566 find_id: DWord;
567 begin
568 if I < 0 then
569 find_id := FindShot()
570 else
571 begin
572 find_id := I;
573 if Integer(find_id) >= High(Shots) then
574 SetLength(Shots, find_id + 64)
575 end;
577 case ShotType of
578 WEAPON_ROCKETLAUNCHER:
579 begin
580 with Shots[find_id] do
581 begin
582 g_Obj_Init(@Obj);
584 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
585 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
587 Animation := nil;
588 Triggers := nil;
589 ShotType := WEAPON_ROCKETLAUNCHER;
590 end;
591 end;
593 WEAPON_PLASMA:
594 begin
595 with Shots[find_id] do
596 begin
597 g_Obj_Init(@Obj);
599 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
600 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
602 Triggers := nil;
603 ShotType := WEAPON_PLASMA;
604 Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table
605 end;
606 end;
608 WEAPON_BFG:
609 begin
610 with Shots[find_id] do
611 begin
612 g_Obj_Init(@Obj);
614 Obj.Rect.Width := SHOT_BFG_WIDTH;
615 Obj.Rect.Height := SHOT_BFG_HEIGHT;
617 Triggers := nil;
618 ShotType := WEAPON_BFG;
619 Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table
620 end;
621 end;
623 WEAPON_FLAMETHROWER:
624 begin
625 with Shots[find_id] do
626 begin
627 g_Obj_Init(@Obj);
629 Obj.Rect.Width := SHOT_FLAME_WIDTH;
630 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
632 Triggers := nil;
633 ShotType := WEAPON_FLAMETHROWER;
634 // Animation := TAnimationState.Create(True, 6, 0); // drawed as gfx
635 end;
636 end;
638 WEAPON_IMP_FIRE:
639 begin
640 with Shots[find_id] do
641 begin
642 g_Obj_Init(@Obj);
644 Obj.Rect.Width := 16;
645 Obj.Rect.Height := 16;
647 Triggers := nil;
648 ShotType := WEAPON_IMP_FIRE;
649 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
650 end;
651 end;
653 WEAPON_CACO_FIRE:
654 begin
655 with Shots[find_id] do
656 begin
657 g_Obj_Init(@Obj);
659 Obj.Rect.Width := 16;
660 Obj.Rect.Height := 16;
662 Triggers := nil;
663 ShotType := WEAPON_CACO_FIRE;
664 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
665 end;
666 end;
668 WEAPON_MANCUB_FIRE:
669 begin
670 with Shots[find_id] do
671 begin
672 g_Obj_Init(@Obj);
674 Obj.Rect.Width := 32;
675 Obj.Rect.Height := 32;
677 Triggers := nil;
678 ShotType := WEAPON_MANCUB_FIRE;
679 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
680 end;
681 end;
683 WEAPON_BARON_FIRE:
684 begin
685 with Shots[find_id] do
686 begin
687 g_Obj_Init(@Obj);
689 Obj.Rect.Width := 32;
690 Obj.Rect.Height := 16;
692 Triggers := nil;
693 ShotType := WEAPON_BARON_FIRE;
694 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
695 end;
696 end;
698 WEAPON_BSP_FIRE:
699 begin
700 with Shots[find_id] do
701 begin
702 g_Obj_Init(@Obj);
704 Obj.Rect.Width := 16;
705 Obj.Rect.Height := 16;
707 Triggers := nil;
708 ShotType := WEAPON_BSP_FIRE;
709 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
710 end;
711 end;
713 WEAPON_SKEL_FIRE:
714 begin
715 with Shots[find_id] do
716 begin
717 g_Obj_Init(@Obj);
719 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
720 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
722 Triggers := nil;
723 ShotType := WEAPON_SKEL_FIRE;
724 target := TargetUID;
725 Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table
726 end;
727 end;
728 end;
730 Shots[find_id].Obj.oldX := X;
731 Shots[find_id].Obj.oldY := Y;
732 Shots[find_id].Obj.X := X;
733 Shots[find_id].Obj.Y := Y;
734 Shots[find_id].Obj.Vel.X := XV;
735 Shots[find_id].Obj.Vel.Y := YV;
736 Shots[find_id].Obj.Accel.X := 0;
737 Shots[find_id].Obj.Accel.Y := 0;
738 Shots[find_id].SpawnerUID := Spawner;
739 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
740 Shots[find_id].Stopped := 255
741 else
742 Shots[find_id].Stopped := 0;
743 Result := find_id;
744 end;
746 procedure throw(i, x, y, xd, yd, s: Integer);
747 var
748 a: Integer;
749 begin
750 yd := yd - y;
751 xd := xd - x;
753 a := Max(Abs(xd), Abs(yd));
754 if a = 0 then
755 a := 1;
757 Shots[i].Obj.oldX := x;
758 Shots[i].Obj.oldY := y;
759 Shots[i].Obj.X := x;
760 Shots[i].Obj.Y := y;
761 Shots[i].Obj.Vel.X := (xd*s) div a;
762 Shots[i].Obj.Vel.Y := (yd*s) div a;
763 Shots[i].Obj.Accel.X := 0;
764 Shots[i].Obj.Accel.Y := 0;
765 Shots[i].Stopped := 0;
766 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
767 Shots[i].Timeout := 900 // ~25 sec
768 else
769 begin
770 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
771 Shots[i].Timeout := SHOT_FLAME_LIFETIME
772 else
773 Shots[i].Timeout := 550; // ~15 sec
774 end;
775 end;
777 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
778 var
779 i, h: Integer;
781 function PlayerHit(Team: Byte = 0): Boolean;
782 var
783 i: Integer;
784 ChkTeam: Boolean;
785 p: TPlayer;
786 begin
787 Result := False;
788 h := High(gPlayers);
790 if h <> -1 then
791 for i := 0 to h do
792 if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then
793 begin
794 ChkTeam := True;
795 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
796 begin
797 p := g_Player_Get(SpawnerUID);
798 if p <> nil then
799 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
800 end;
801 if ChkTeam then
802 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
803 begin
804 if t <> HIT_FLAME then
805 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
806 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
807 if t = HIT_BFG then
808 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
809 Result := True;
810 break;
811 end;
812 end;
813 end;
816 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
817 begin
818 result := false; // don't stop
819 if mon.alive and g_Obj_Collide(obj, @mon.Obj) then
820 begin
821 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
822 begin
823 if (t <> HIT_FLAME) then
824 begin
825 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
826 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
827 end;
828 result := True;
829 end;
830 end;
831 end;
834 function monsCheckHit (mon: TMonster): Boolean;
835 begin
836 result := false; // don't stop
837 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
838 begin
839 if (t <> HIT_FLAME) then
840 begin
841 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
842 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
843 end;
844 result := true;
845 end;
846 end;
848 function MonsterHit(): Boolean;
849 begin
850 //result := g_Mons_ForEach(monsCheckHit);
851 //FIXME: accelerate this!
852 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
853 end;
855 begin
856 Result := 0;
858 if HitCorpses then
859 begin
860 h := High(gCorpses);
862 if gAdvCorpses and (h <> -1) then
863 for i := 0 to h do
864 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
865 g_Obj_Collide(obj, @gCorpses[i].Obj) then
866 begin
867 // Ðàñïèëèâàåì òðóï:
868 gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
869 (obj^.Vel.Y+obj^.Accel.Y) div 4);
870 Result := 1;
871 end;
872 end;
874 case gGameSettings.GameMode of
875 // Êàìïàíèÿ:
876 GM_COOP, GM_SINGLE:
877 begin
878 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
879 if MonsterHit() then
880 begin
881 Result := 2;
882 Exit;
883 end;
885 // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
886 // (èëè ñíàðÿä îò ìîíñòðà, èëè friendlyfire, èëè friendly_hit_projectile)
887 if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or
888 LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then
889 begin
890 if PlayerHit() then
891 begin
892 Result := 1;
893 Exit;
894 end;
895 end;
896 end;
898 // Äåçìàò÷:
899 GM_DM:
900 begin
901 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
902 if PlayerHit() then
903 begin
904 Result := 1;
905 Exit;
906 end;
908 if MonsterHit() then
909 begin
910 Result := 2;
911 Exit;
912 end;
913 end;
915 // Êîìàíäíûå:
916 GM_TDM, GM_CTF:
917 begin
918 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
919 if PlayerHit(2) then
920 begin
921 Result := 1;
922 Exit;
923 end;
925 // Ïîòîì ìîíñòðîâ
926 if MonsterHit() then
927 begin
928 Result := 2;
929 Exit;
930 end;
932 // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî
933 // (èëè friendlyfire, èëè friendly_hit_projectile)
934 if LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then
935 begin
936 if PlayerHit(1) then
937 begin
938 Result := 1;
939 Exit;
940 end;
941 end;
942 end;
944 end;
945 end;
947 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
948 begin
949 Result := False;
951 case g_GetUIDType(UID) of
952 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
953 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
954 else Exit;
955 end;
956 end;
958 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
959 var
960 r: Integer; // squared radius
962 function monsExCheck (mon: TMonster): Boolean;
963 var
964 dx, dy, mm: Integer;
965 begin
966 result := false; // don't stop
967 begin
968 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
969 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
971 if dx > 1000 then dx := 1000;
972 if dy > 1000 then dy := 1000;
974 if (dx*dx+dy*dy < r) then
975 begin
976 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
977 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
979 mm := Max(abs(dx), abs(dy));
980 if mm = 0 then mm := 1;
982 if mon.alive then
983 begin
984 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
985 end;
987 mon.Push((dx*7) div mm, (dy*7) div mm);
988 end;
989 end;
990 end;
992 var
993 i, h, dx, dy, m, mm: Integer;
994 _angle: SmallInt;
995 begin
996 result := false;
998 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
1000 r := rad*rad;
1002 h := High(gPlayers);
1004 if h <> -1 then
1005 for i := 0 to h do
1006 if (gPlayers[i] <> nil) and gPlayers[i].alive then
1007 with gPlayers[i] do
1008 begin
1009 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1010 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1012 if dx > 1000 then dx := 1000;
1013 if dy > 1000 then dy := 1000;
1015 if dx*dx+dy*dy < r then
1016 begin
1017 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1018 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1020 mm := Max(abs(dx), abs(dy));
1021 if mm = 0 then mm := 1;
1023 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1024 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1025 end;
1026 end;
1028 //g_Mons_ForEach(monsExCheck);
1029 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1031 h := High(gCorpses);
1033 if gAdvCorpses and (h <> -1) then
1034 for i := 0 to h do
1035 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1036 with gCorpses[i] do
1037 begin
1038 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1039 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1041 if dx > 1000 then dx := 1000;
1042 if dy > 1000 then dy := 1000;
1044 if dx*dx+dy*dy < r then
1045 begin
1046 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1047 Obj.Rect.Width, Obj.Rect.Height);
1049 mm := Max(abs(dx), abs(dy));
1050 if mm = 0 then mm := 1;
1052 Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
1053 end;
1054 end;
1056 h := High(gGibs);
1058 if gAdvGibs and (h <> -1) then
1059 for i := 0 to h do
1060 if gGibs[i].alive then
1061 with gGibs[i] do
1062 begin
1063 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1064 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1066 if dx > 1000 then dx := 1000;
1067 if dy > 1000 then dy := 1000;
1069 if dx*dx+dy*dy < r then
1070 begin
1071 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1072 Obj.Rect.Width, Obj.Rect.Height);
1073 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1074 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1076 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1077 positionChanged(); // this updates spatial accelerators
1078 end;
1079 end;
1080 end;
1082 procedure g_Weapon_Init();
1083 begin
1084 CreateWaterMap();
1085 end;
1087 procedure g_Weapon_Free();
1088 var
1089 i: Integer;
1090 begin
1091 if Shots <> nil then
1092 begin
1093 for i := 0 to High(Shots) do
1094 if Shots[i].ShotType <> 0 then
1095 Shots[i].Animation.Free();
1097 Shots := nil;
1098 end;
1100 WaterMap := nil;
1101 end;
1103 procedure g_Weapon_LoadData();
1104 begin
1105 e_WriteLog('Loading weapons data...', TMsgType.Notify);
1107 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1114 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1115 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1116 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1117 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1118 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1119 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1120 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1121 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1122 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1123 g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE');
1124 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1125 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1126 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1127 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1128 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1129 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1130 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1131 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1132 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1133 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM');
1134 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM');
1135 g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM');
1136 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1137 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1138 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1139 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1140 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1141 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1142 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1144 //wgunMonHash := hashNewIntInt();
1145 wgunHitHeap := TBinaryHeapHitTimes.Create();
1146 end;
1148 procedure g_Weapon_FreeData();
1149 begin
1150 e_WriteLog('Releasing weapons data...', TMsgType.Notify);
1152 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1153 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1154 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1155 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1156 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1157 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1158 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1159 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1160 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1161 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1162 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1163 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1164 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1165 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1166 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1167 g_Sound_Delete('SOUND_FIRE');
1168 g_Sound_Delete('SOUND_IGNITE');
1169 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1170 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1171 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1172 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1173 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1174 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1175 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1176 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1177 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1178 g_Sound_Delete('SOUND_WEAPON_FLAMEON');
1179 g_Sound_Delete('SOUND_WEAPON_FLAMEOFF');
1180 g_Sound_Delete('SOUND_WEAPON_FLAMEWORK');
1181 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1182 g_Sound_Delete('SOUND_PLAYER_JETON');
1183 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1184 g_Sound_Delete('SOUND_PLAYER_CASING1');
1185 g_Sound_Delete('SOUND_PLAYER_CASING2');
1186 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1187 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1188 end;
1191 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1192 var
1193 i: Integer;
1194 begin
1195 result := false;
1196 for i := 0 to High(gPlayers) do
1197 begin
1198 if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then
1199 begin
1200 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1201 begin
1202 if AllowPush then gPlayers[i].Push(vx, vy);
1203 result := true;
1204 end;
1205 end;
1206 end;
1207 end;
1210 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1212 function monsCheck (mon: TMonster): Boolean;
1213 begin
1214 result := false; // don't stop
1215 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1216 begin
1217 if AllowPush then mon.Push(vx, vy);
1218 result := true;
1219 end;
1220 end;
1222 begin
1223 result := 0;
1224 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1225 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1226 end;
1229 (*
1230 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1231 var
1232 a: Integer;
1233 x2, y2: Integer;
1234 dx, dy: Integer;
1235 xe, ye: Integer;
1236 xi, yi: Integer;
1237 s, c: Extended;
1238 //vx, vy: Integer;
1239 xx, yy, d: Integer;
1240 i: Integer;
1241 t1, _collide: Boolean;
1242 w, h: Word;
1243 {$IF DEFINED(D2F_DEBUG)}
1244 stt: UInt64;
1245 showTime: Boolean = true;
1246 {$ENDIF}
1247 begin
1248 a := GetAngle(x, y, xd, yd)+180;
1250 SinCos(DegToRad(-a), s, c);
1252 if Abs(s) < 0.01 then s := 0;
1253 if Abs(c) < 0.01 then c := 0;
1255 x2 := x+Round(c*gMapInfo.Width);
1256 y2 := y+Round(s*gMapInfo.Width);
1258 t1 := gWalls <> nil;
1259 _collide := False;
1260 w := gMapInfo.Width;
1261 h := gMapInfo.Height;
1263 xe := 0;
1264 ye := 0;
1265 dx := x2-x;
1266 dy := y2-y;
1268 if (xd = 0) and (yd = 0) then Exit;
1270 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1271 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1273 dx := Abs(dx);
1274 dy := Abs(dy);
1276 if dx > dy then d := dx else d := dy;
1278 //blood vel, for Monster.Damage()
1279 //vx := (dx*10 div d)*xi;
1280 //vy := (dy*10 div d)*yi;
1282 {$IF DEFINED(D2F_DEBUG)}
1283 stt := getTimeMicro();
1284 {$ENDIF}
1286 xx := x;
1287 yy := y;
1289 for i := 1 to d do
1290 begin
1291 xe := xe+dx;
1292 ye := ye+dy;
1294 if xe > d then
1295 begin
1296 xe := xe-d;
1297 xx := xx+xi;
1298 end;
1300 if ye > d then
1301 begin
1302 ye := ye-d;
1303 yy := yy+yi;
1304 end;
1306 if (yy > h) or (yy < 0) then Break;
1307 if (xx > w) or (xx < 0) then Break;
1309 if t1 then
1310 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1311 begin
1312 _collide := True;
1313 {$IF DEFINED(D2F_DEBUG)}
1314 stt := getTimeMicro()-stt;
1315 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1316 showTime := false;
1317 {$ENDIF}
1318 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1319 if g_Game_IsServer and g_Game_IsNet then
1320 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1321 end;
1323 if not _collide then
1324 begin
1325 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1326 end;
1328 if _collide then Break;
1329 end;
1331 {$IF DEFINED(D2F_DEBUG)}
1332 if showTime then
1333 begin
1334 stt := getTimeMicro()-stt;
1335 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1336 end;
1337 {$ENDIF}
1339 if CheckTrigger and g_Game_IsServer then
1340 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1341 end;
1342 *)
1345 //!!!FIXME!!!
1346 procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1347 var
1348 x0, y0: Integer;
1349 x2, y2: Integer;
1350 xi, yi: Integer;
1351 wallDistSq: Integer = $3fffffff;
1352 spawnerPlr: TPlayer = nil;
1353 dmg: Integer;
1355 function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean;
1356 begin
1357 result := false;
1358 if (idx < 0) or (idx > High(gPlayers)) then exit;
1359 if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit;
1360 if (spawnerPlr <> nil) then
1361 begin
1362 if ((gGameSettings.Options and (GAME_OPTION_TEAMHITTRACE or GAME_OPTION_TEAMDAMAGE)) = 0) and
1363 (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then
1364 begin
1365 if (spawnerPlr <> gPlayers[idx]) and ((gGameSettings.Options and GAME_OPTION_TEAMABSORBDAMAGE) = 0) then
1366 dmg := Max(1, dmg div 2);
1367 exit;
1368 end;
1369 end;
1370 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1371 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1372 {$IF DEFINED(D2F_DEBUG)}
1373 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1374 {$ENDIF}
1375 end;
1377 function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean;
1378 begin
1379 result := false;
1380 if (mon = nil) then exit;
1381 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1382 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1383 {$IF DEFINED(D2F_DEBUG)}
1384 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1385 {$ENDIF}
1386 end;
1388 // collect players along hitray
1389 // return `true` if instant hit was detected
1390 function playerPossibleHit (): Boolean;
1391 var
1392 i: Integer;
1393 px, py, pw, ph: Integer;
1394 inx, iny: Integer;
1395 distSq: Integer;
1396 plr: TPlayer;
1397 begin
1398 result := false;
1399 for i := 0 to High(gPlayers) do
1400 begin
1401 plr := gPlayers[i];
1402 if (plr <> nil) and plr.alive then
1403 begin
1404 plr.getMapBox(px, py, pw, ph);
1405 if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then
1406 begin
1407 distSq := distanceSq(x, y, inx, iny);
1408 if (distSq = 0) then
1409 begin
1410 // contains
1411 if doPlayerHit(i, x, y) then begin result := true; exit; end;
1412 end
1413 else if (distSq < wallDistSq) then
1414 begin
1415 appendHitTimePlr(distSq, i, inx, iny);
1416 end;
1417 end;
1418 end;
1419 end;
1420 end;
1422 procedure sqchecker (mon: TMonster);
1423 var
1424 mx, my, mw, mh: Integer;
1425 inx, iny: Integer;
1426 distSq: Integer;
1427 begin
1428 mon.getMapBox(mx, my, mw, mh);
1429 if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then
1430 begin
1431 distSq := distanceSq(x0, y0, inx, iny);
1432 if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny);
1433 end;
1434 end;
1436 var
1437 a: Integer;
1438 dx, dy: Integer;
1439 xe, ye: Integer;
1440 s, c: Extended;
1441 i: Integer;
1442 wallHitFlag: Boolean = false;
1443 wallHitX: Integer = 0;
1444 wallHitY: Integer = 0;
1445 didHit: Boolean = false;
1446 {$IF DEFINED(D2F_DEBUG)}
1447 stt: UInt64;
1448 {$ENDIF}
1449 mit: PMonster;
1450 it: TMonsterGrid.Iter;
1451 begin
1452 (*
1453 if not gwep_debug_fast_trace then
1454 begin
1455 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1456 exit;
1457 end;
1458 *)
1460 if (xd = 0) and (yd = 0) then exit;
1462 if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
1463 spawnerPlr := g_Player_Get(SpawnerUID);
1465 dmg := indmg;
1467 //wgunMonHash.reset(); //FIXME: clear hash on level change
1468 wgunHitHeap.clear();
1469 wgunHitTimeUsed := 0;
1471 a := GetAngle(x, y, xd, yd)+180;
1473 SinCos(DegToRad(-a), s, c);
1475 if Abs(s) < 0.01 then s := 0;
1476 if Abs(c) < 0.01 then c := 0;
1478 x0 := x;
1479 y0 := y;
1480 x2 := x+Round(c*gMapInfo.Width);
1481 y2 := y+Round(s*gMapInfo.Width);
1483 dx := x2-x;
1484 dy := y2-y;
1486 if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0;
1487 if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0;
1489 {$IF DEFINED(D2F_DEBUG)}
1490 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify);
1491 stt := getTimeMicro();
1492 {$ENDIF}
1494 wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil);
1495 if wallHitFlag then
1496 begin
1497 x2 := wallHitX;
1498 y2 := wallHitY;
1499 wallDistSq := distanceSq(x, y, wallHitX, wallHitY);
1500 end
1501 else
1502 begin
1503 wallHitX := x2;
1504 wallHitY := y2;
1505 end;
1507 if playerPossibleHit() then exit; // instant hit
1509 // collect monsters
1510 //g_Mons_AlongLine(x, y, x2, y2, sqchecker);
1512 it := monsGrid.forEachAlongLine(x, y, x2, y2, -1);
1513 for mit in it do sqchecker(mit^);
1514 it.release();
1516 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1517 // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords
1518 while (wgunHitHeap.count > 0) do
1519 begin
1520 // has some entities to check, do it
1521 i := wgunHitHeap.front;
1522 wgunHitHeap.popFront();
1523 // hitpoint
1524 xe := wgunHitTime[i].x;
1525 ye := wgunHitTime[i].y;
1526 // check if it is not behind the wall
1527 if (wgunHitTime[i].mon <> nil) then
1528 begin
1529 didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye);
1530 end
1531 else
1532 begin
1533 didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye);
1534 end;
1535 if didHit then
1536 begin
1537 // need new coords for trigger
1538 wallHitX := xe;
1539 wallHitY := ye;
1540 wallHitFlag := false; // no sparks
1541 break;
1542 end;
1543 end;
1545 // need sparks?
1546 if wallHitFlag then
1547 begin
1548 {$IF DEFINED(D2F_DEBUG)}
1549 stt := getTimeMicro()-stt;
1550 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1551 {$ENDIF}
1552 {$IFDEF ENABLE_GFX}
1553 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1554 {$ENDIF}
1555 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1556 end
1557 else
1558 begin
1559 {$IF DEFINED(D2F_DEBUG)}
1560 stt := getTimeMicro()-stt;
1561 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify);
1562 {$ENDIF}
1563 end;
1565 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1566 end;
1569 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1570 var
1571 obj: TObj;
1572 begin
1573 obj.X := X;
1574 obj.Y := Y;
1575 obj.rect.X := 0;
1576 obj.rect.Y := 0;
1577 obj.rect.Width := 39;
1578 obj.rect.Height := 52;
1579 obj.Vel.X := 0;
1580 obj.Vel.Y := 0;
1581 obj.Accel.X := 0;
1582 obj.Accel.Y := 0;
1584 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1585 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1586 else
1587 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1588 end;
1590 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1591 var
1592 obj: TObj;
1593 begin
1594 obj.X := X;
1595 obj.Y := Y;
1596 obj.rect.X := 0;
1597 obj.rect.Y := 0;
1598 obj.rect.Width := 32;
1599 obj.rect.Height := 52;
1600 obj.Vel.X := 0;
1601 obj.Vel.Y := 0;
1602 obj.Accel.X := 0;
1603 obj.Accel.Y := 0;
1605 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1606 end;
1608 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1609 Silent: Boolean = False);
1610 var
1611 find_id: DWORD;
1612 dx, dy: Integer;
1613 begin
1614 if WID < 0 then
1615 find_id := FindShot()
1616 else
1617 begin
1618 find_id := WID;
1619 if Integer(find_id) >= High(Shots) then
1620 SetLength(Shots, find_id + 64)
1621 end;
1623 with Shots[find_id] do
1624 begin
1625 g_Obj_Init(@Obj);
1627 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1628 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1630 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1631 dy := -(Obj.Rect.Height div 2);
1633 ShotType := WEAPON_ROCKETLAUNCHER;
1634 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1636 Animation := nil;
1637 triggers := nil;
1638 end;
1640 Shots[find_id].SpawnerUID := SpawnerUID;
1642 if not Silent then
1643 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1644 end;
1646 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1647 WID: Integer = -1; Silent: Boolean = False);
1648 var
1649 find_id: DWORD;
1650 dx, dy: Integer;
1651 begin
1652 if WID < 0 then
1653 find_id := FindShot()
1654 else
1655 begin
1656 find_id := WID;
1657 if Integer(find_id) >= High(Shots) then
1658 SetLength(Shots, find_id + 64)
1659 end;
1661 with Shots[find_id] do
1662 begin
1663 g_Obj_Init(@Obj);
1665 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1666 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1668 dx := -(Obj.Rect.Width div 2);
1669 dy := -(Obj.Rect.Height div 2);
1671 ShotType := WEAPON_SKEL_FIRE;
1672 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1674 triggers := nil;
1675 target := TargetUID;
1676 Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table
1677 end;
1679 Shots[find_id].SpawnerUID := SpawnerUID;
1681 if not Silent then
1682 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1683 end;
1685 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1686 Silent: Boolean = False);
1687 var
1688 find_id: DWORD;
1689 dx, dy: Integer;
1690 begin
1691 if WID < 0 then
1692 find_id := FindShot()
1693 else
1694 begin
1695 find_id := WID;
1696 if Integer(find_id) >= High(Shots) then
1697 SetLength(Shots, find_id + 64);
1698 end;
1700 with Shots[find_id] do
1701 begin
1702 g_Obj_Init(@Obj);
1704 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1705 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1707 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1708 dy := -(Obj.Rect.Height div 2);
1710 ShotType := WEAPON_PLASMA;
1711 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1713 triggers := nil;
1714 Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table
1715 end;
1717 Shots[find_id].SpawnerUID := SpawnerUID;
1719 if not Silent then
1720 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1721 end;
1723 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1724 Silent: Boolean = False);
1725 var
1726 find_id: DWORD;
1727 dx, dy: Integer;
1728 begin
1729 if WID < 0 then
1730 find_id := FindShot()
1731 else
1732 begin
1733 find_id := WID;
1734 if Integer(find_id) >= High(Shots) then
1735 SetLength(Shots, find_id + 64);
1736 end;
1738 with Shots[find_id] do
1739 begin
1740 g_Obj_Init(@Obj);
1742 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1743 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1745 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1746 dy := -(Obj.Rect.Height div 2);
1748 ShotType := WEAPON_FLAMETHROWER;
1749 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1751 triggers := nil;
1752 Animation := nil;
1753 end;
1755 Shots[find_id].SpawnerUID := SpawnerUID;
1757 // if not Silent then
1758 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1759 end;
1761 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1762 Silent: Boolean = False);
1763 var
1764 find_id: DWORD;
1765 dx, dy: Integer;
1766 begin
1767 if WID < 0 then
1768 find_id := FindShot()
1769 else
1770 begin
1771 find_id := WID;
1772 if Integer(find_id) >= High(Shots) then
1773 SetLength(Shots, find_id + 64)
1774 end;
1776 with Shots[find_id] do
1777 begin
1778 g_Obj_Init(@Obj);
1780 Obj.Rect.Width := 16;
1781 Obj.Rect.Height := 16;
1783 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1784 dy := -(Obj.Rect.Height div 2);
1786 ShotType := WEAPON_IMP_FIRE;
1787 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1789 triggers := nil;
1790 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
1791 end;
1793 Shots[find_id].SpawnerUID := SpawnerUID;
1795 if not Silent then
1796 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1797 end;
1799 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1800 Silent: Boolean = False);
1801 var
1802 find_id: DWORD;
1803 dx, dy: Integer;
1804 begin
1805 if WID < 0 then
1806 find_id := FindShot()
1807 else
1808 begin
1809 find_id := WID;
1810 if Integer(find_id) >= High(Shots) then
1811 SetLength(Shots, find_id + 64)
1812 end;
1814 with Shots[find_id] do
1815 begin
1816 g_Obj_Init(@Obj);
1818 Obj.Rect.Width := 16;
1819 Obj.Rect.Height := 16;
1821 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1822 dy := -(Obj.Rect.Height div 2);
1824 ShotType := WEAPON_CACO_FIRE;
1825 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1827 triggers := nil;
1828 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
1829 end;
1831 Shots[find_id].SpawnerUID := SpawnerUID;
1833 if not Silent then
1834 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1835 end;
1837 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1838 Silent: Boolean = False);
1839 var
1840 find_id: DWORD;
1841 dx, dy: Integer;
1842 begin
1843 if WID < 0 then
1844 find_id := FindShot()
1845 else
1846 begin
1847 find_id := WID;
1848 if Integer(find_id) >= High(Shots) then
1849 SetLength(Shots, find_id + 64)
1850 end;
1852 with Shots[find_id] do
1853 begin
1854 g_Obj_Init(@Obj);
1856 Obj.Rect.Width := 32;
1857 Obj.Rect.Height := 16;
1859 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1860 dy := -(Obj.Rect.Height div 2);
1862 ShotType := WEAPON_BARON_FIRE;
1863 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1865 triggers := nil;
1866 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
1867 end;
1869 Shots[find_id].SpawnerUID := SpawnerUID;
1871 if not Silent then
1872 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1873 end;
1875 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1876 Silent: Boolean = False);
1877 var
1878 find_id: DWORD;
1879 dx, dy: Integer;
1880 begin
1881 if WID < 0 then
1882 find_id := FindShot()
1883 else
1884 begin
1885 find_id := WID;
1886 if Integer(find_id) >= High(Shots) then
1887 SetLength(Shots, find_id + 64)
1888 end;
1890 with Shots[find_id] do
1891 begin
1892 g_Obj_Init(@Obj);
1894 Obj.Rect.Width := 16;
1895 Obj.Rect.Height := 16;
1897 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1898 dy := -(Obj.Rect.Height div 2);
1900 ShotType := WEAPON_BSP_FIRE;
1901 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1903 triggers := nil;
1905 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
1906 end;
1908 Shots[find_id].SpawnerUID := SpawnerUID;
1910 if not Silent then
1911 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1912 end;
1914 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1915 Silent: Boolean = False);
1916 var
1917 find_id: DWORD;
1918 dx, dy: Integer;
1919 begin
1920 if WID < 0 then
1921 find_id := FindShot()
1922 else
1923 begin
1924 find_id := WID;
1925 if Integer(find_id) >= High(Shots) then
1926 SetLength(Shots, find_id + 64)
1927 end;
1929 with Shots[find_id] do
1930 begin
1931 g_Obj_Init(@Obj);
1933 Obj.Rect.Width := 32;
1934 Obj.Rect.Height := 32;
1936 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1937 dy := -(Obj.Rect.Height div 2);
1939 ShotType := WEAPON_MANCUB_FIRE;
1940 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1942 triggers := nil;
1944 Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
1945 end;
1947 Shots[find_id].SpawnerUID := SpawnerUID;
1949 if not Silent then
1950 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1951 end;
1953 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1954 Silent: Boolean = False);
1955 var
1956 find_id: DWORD;
1957 dx, dy: Integer;
1958 begin
1959 if WID < 0 then
1960 find_id := FindShot()
1961 else
1962 begin
1963 find_id := WID;
1964 if Integer(find_id) >= High(Shots) then
1965 SetLength(Shots, find_id + 64)
1966 end;
1968 with Shots[find_id] do
1969 begin
1970 g_Obj_Init(@Obj);
1972 Obj.Rect.Width := SHOT_BFG_WIDTH;
1973 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1975 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1976 dy := -(Obj.Rect.Height div 2);
1978 ShotType := WEAPON_BFG;
1979 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1981 triggers := nil;
1982 Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table
1983 end;
1985 Shots[find_id].SpawnerUID := SpawnerUID;
1987 if not Silent then
1988 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
1989 end;
1991 procedure g_Weapon_bfghit(x, y: Integer);
1992 begin
1993 {$IFDEF ENABLE_GFX}
1994 g_GFX_QueueEffect(R_GFX_BFG_HIT, x - 32, y - 32);
1995 {$ENDIF}
1996 end;
1998 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
1999 Silent: Boolean = False);
2000 begin
2001 if not Silent then
2002 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2004 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2005 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2006 begin
2007 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2008 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2009 end;
2010 end;
2012 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2013 Silent: Boolean = False);
2014 begin
2015 if not Silent then
2016 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2018 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2019 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2020 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2021 begin
2022 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2023 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2024 end;
2025 end;
2027 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2028 Silent: Boolean = False);
2029 var
2030 i, j: Integer;
2031 begin
2032 if not Silent then
2033 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2035 for i := 0 to 9 do
2036 begin
2037 j := Random(17)-8; // -8 .. 8
2038 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2039 end;
2040 end;
2042 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2043 Silent: Boolean = False);
2044 var
2045 a, i, j: Integer;
2046 begin
2047 if not Silent then
2048 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2050 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2051 for i := 0 to a do
2052 begin
2053 j := Random(41)-20; // -20 .. 20
2054 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2055 end;
2056 end;
2058 procedure g_Weapon_PreUpdate();
2059 var
2060 i: Integer;
2061 begin
2062 if Shots = nil then Exit;
2063 for i := 0 to High(Shots) do
2064 if Shots[i].ShotType <> 0 then
2065 begin
2066 Shots[i].Obj.oldX := Shots[i].Obj.X;
2067 Shots[i].Obj.oldY := Shots[i].Obj.Y;
2068 end;
2069 end;
2071 procedure g_Weapon_Update();
2072 var
2073 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2074 t: DWArray;
2075 st: Word;
2076 o: TObj;
2077 spl: Boolean;
2078 Loud: Boolean;
2079 {$IFDEF ENABLE_GFX}
2080 var tcx, tcy: Integer;
2081 {$ENDIF}
2082 begin
2083 if Shots = nil then
2084 Exit;
2086 for i := 0 to High(Shots) do
2087 begin
2088 if Shots[i].ShotType = 0 then
2089 Continue;
2091 Loud := True;
2093 with Shots[i] do
2094 begin
2095 Timeout := Timeout - 1;
2096 oldvx := Obj.Vel.X;
2097 oldvy := Obj.Vel.Y;
2098 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2099 if (Stopped = 0) and g_Game_IsServer then
2100 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2101 SpawnerUID, ACTIVATE_SHOT, triggers)
2102 else
2103 t := nil;
2105 if t <> nil then
2106 begin
2107 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2108 if triggers = nil then
2109 triggers := t
2110 else
2111 begin
2112 h := High(t);
2114 for a := 0 to h do
2115 if not InDWArray(t[a], triggers) then
2116 begin
2117 SetLength(triggers, Length(triggers)+1);
2118 triggers[High(triggers)] := t[a];
2119 end;
2120 end;
2121 end;
2123 // Àíèìàöèÿ ñíàðÿäà:
2124 if Animation <> nil then
2125 Animation.Update();
2127 // Äâèæåíèå:
2128 spl := (ShotType <> WEAPON_PLASMA) and
2129 (ShotType <> WEAPON_BFG) and
2130 (ShotType <> WEAPON_BSP_FIRE) and
2131 (ShotType <> WEAPON_FLAMETHROWER);
2133 if Stopped = 0 then
2134 begin
2135 st := g_Obj_Move_Projectile(@Obj, False, spl);
2136 end
2137 else
2138 begin
2139 st := 0;
2140 end;
2141 positionChanged(); // this updates spatial accelerators
2143 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2144 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2145 begin
2146 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2147 ShotType := 0;
2148 Animation.Free();
2149 Continue;
2150 end;
2152 cx := Obj.X + (Obj.Rect.Width div 2);
2153 cy := Obj.Y + (Obj.Rect.Height div 2);
2155 case ShotType of
2156 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2157 begin
2158 // Âûëåòåëà èç âîäû:
2159 if WordBool(st and MOVE_HITAIR) then
2160 g_Obj_SetSpeed(@Obj, 12);
2162 {$IFDEF ENABLE_GFX}
2163 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2164 if WordBool(st and MOVE_INWATER) then
2165 begin
2166 g_GFX_Bubbles(Obj.X + (Obj.Rect.Width div 2), Obj.Y + (Obj.Rect.Height div 2), 1 + Random(3), 16, 16)
2167 end
2168 else
2169 begin
2170 g_GFX_QueueEffect(R_GFX_SMOKE_TRANS, Obj.X-14+Random(9), Obj.Y+(Obj.Rect.Height div 2)-20+Random(9));
2171 end;
2172 {$ENDIF}
2174 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2175 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2176 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2177 (Timeout < 1) then
2178 begin
2179 Obj.Vel.X := 0;
2180 Obj.Vel.Y := 0;
2182 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2184 if ShotType = WEAPON_SKEL_FIRE then
2185 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2186 {$IFDEF ENABLE_GFX}
2187 g_GFX_QueueEffect(R_GFX_EXPLODE_SKELFIRE, Obj.X + 32 - 58, Obj.Y + 8 - 36);
2188 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2189 {$ENDIF}
2190 end
2191 else
2192 begin // Âçðûâ Ðàêåòû
2193 {$IFDEF ENABLE_GFX}
2194 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64);
2195 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2196 {$ENDIF}
2197 end;
2199 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2201 ShotType := 0;
2202 end;
2204 if ShotType = WEAPON_SKEL_FIRE then
2205 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2206 if GetPos(target, @o) then
2207 throw(i, Obj.X, Obj.Y,
2208 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2209 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2210 12);
2211 end;
2212 end;
2214 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2215 begin
2216 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2217 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2218 begin
2219 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2220 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2221 ShotType := 0;
2222 Continue;
2223 end;
2225 // Âåëè÷èíà óðîíà:
2226 if (ShotType = WEAPON_PLASMA) and
2227 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2228 a := 10
2229 else
2230 a := 5;
2232 if ShotType = WEAPON_BSP_FIRE then
2233 a := 10;
2235 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2236 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2237 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2238 (Timeout < 1) then
2239 begin
2240 {$IFDEF ENABLE_GFX}
2241 if ShotType = WEAPON_PLASMA then
2242 g_GFX_QueueEffect(R_GFX_EXPLODE_PLASMA, cx - 16, cy - 16)
2243 else
2244 g_GFX_QueueEffect(R_GFX_EXPLODE_BSPFIRE, cx - 16, cy - 16);
2245 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2246 {$ENDIF}
2247 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2248 ShotType := 0;
2249 end;
2250 end;
2252 WEAPON_FLAMETHROWER: // Îãíåìåò
2253 begin
2254 // Ñî âðåìåíåì óìèðàåò
2255 if (Timeout < 1) then
2256 begin
2257 ShotType := 0;
2258 Continue;
2259 end;
2260 // Ïîä âîäîé òîæå
2261 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2262 begin
2263 {$IFDEF ENABLE_GFX}
2264 if WordBool(st and MOVE_HITWATER) then
2265 begin
2266 tcx := Random(8);
2267 tcy := Random(8);
2268 g_GFX_QueueEffect(R_GFX_SMOKE, cx-4+tcx-(R_GFX_SMOKE_WIDTH div 2), cy-4+tcy-(R_GFX_SMOKE_HEIGHT div 2));
2269 end
2270 else
2271 begin
2272 g_GFX_Bubbles(cx, cy, 1 + Random(3), 16, 16);
2273 end;
2274 {$ENDIF}
2275 ShotType := 0;
2276 Continue;
2277 end;
2279 // Ãðàâèòàöèÿ
2280 if Stopped = 0 then
2281 Obj.Accel.Y := Obj.Accel.Y + 1;
2282 // Ïîïàëè â ñòåíó èëè â âîäó:
2283 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2284 begin
2285 // Ïðèëèïàåì:
2286 Obj.Vel.X := 0;
2287 Obj.Vel.Y := 0;
2288 Obj.Accel.Y := 0;
2289 if WordBool(st and MOVE_HITWALL) then
2290 Stopped := MOVE_HITWALL
2291 else if WordBool(st and MOVE_HITLAND) then
2292 Stopped := MOVE_HITLAND
2293 else if WordBool(st and MOVE_HITCEIL) then
2294 Stopped := MOVE_HITCEIL;
2295 end;
2297 a := IfThen(Stopped = 0, 10, 1);
2298 // Åñëè â êîãî-òî ïîïàëè
2299 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2300 begin
2301 // HIT_FLAME ñàì ïîäîææåò
2302 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2303 if Stopped = 0 then
2304 ShotType := 0;
2305 end;
2307 if Stopped = 0 then
2308 tf := 2
2309 else
2310 tf := 3;
2312 if (gTime mod LongWord(tf) = 0) then
2313 begin
2314 {$IFDEF ENABLE_GFX}
2315 case Stopped of
2316 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2317 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2318 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2319 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2320 end;
2321 g_GFX_QueueEffect(R_GFX_FLAME_RAND, tcx - (R_GFX_FLAME_WIDTH div 2), tcy - (R_GFX_FLAME_HEIGHT div 2));
2322 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2323 {$ENDIF}
2324 end;
2325 end;
2327 WEAPON_BFG: // BFG
2328 begin
2329 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2330 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2331 begin
2332 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2333 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2334 ShotType := 0;
2335 Continue;
2336 end;
2338 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2339 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2340 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2341 (Timeout < 1) then
2342 begin
2343 // Ëó÷è BFG:
2344 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2345 {$IFDEF ENABLE_GFX}
2346 g_GFX_QueueEffect(R_GFX_EXPLODE_BFG, cx - 64, cy - 64);
2347 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2348 {$ENDIF}
2349 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2350 ShotType := 0;
2351 end;
2352 end;
2354 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2355 begin
2356 // Âûëåòåë èç âîäû:
2357 if WordBool(st and MOVE_HITAIR) then
2358 g_Obj_SetSpeed(@Obj, 16);
2360 // Âåëè÷èíà óðîíà:
2361 if ShotType = WEAPON_IMP_FIRE then
2362 a := 5
2363 else
2364 if ShotType = WEAPON_CACO_FIRE then
2365 a := 20
2366 else
2367 a := 40;
2369 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2370 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2371 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2372 (Timeout < 1) then
2373 begin
2374 {$IFDEF ENABLE_GFX}
2375 case ShotType of
2376 WEAPON_IMP_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_IMPFIRE, cx - 32, cy - 32);
2377 WEAPON_CACO_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_CACOFIRE, cx - 32, cy - 32);
2378 WEAPON_BARON_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_BARONFIRE, cx - 32, cy - 32);
2379 end;
2380 {$ENDIF}
2381 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2382 ShotType := 0;
2383 end;
2384 end;
2386 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2387 begin
2388 // Âûëåòåë èç âîäû:
2389 if WordBool(st and MOVE_HITAIR) then
2390 g_Obj_SetSpeed(@Obj, 16);
2392 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2393 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2394 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2395 (Timeout < 1) then
2396 begin
2397 // Âçðûâ:
2398 {$IFDEF ENABLE_GFX}
2399 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64);
2400 {$ENDIF}
2401 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2402 ShotType := 0;
2403 end;
2404 end;
2405 end; // case ShotType of...
2407 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2408 if (ShotType = 0) then
2409 begin
2410 if gGameSettings.GameType = GT_SERVER then
2411 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2412 if Animation <> nil then
2413 begin
2414 Animation.Free();
2415 Animation := nil;
2416 end;
2417 end
2418 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2419 if gGameSettings.GameType = GT_SERVER then
2420 MH_SEND_UpdateShot(i);
2421 end;
2422 end;
2423 end;
2425 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2426 var
2427 a: Integer;
2428 begin
2429 Result := False;
2431 if Shots = nil then
2432 Exit;
2434 for a := 0 to High(Shots) do
2435 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2436 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2437 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2438 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2439 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2440 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2441 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2442 begin
2443 Result := True;
2444 Exit;
2445 end;
2446 end;
2448 procedure g_Weapon_SaveState (st: TStream);
2449 var
2450 count, i, j: Integer;
2451 begin
2452 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
2453 count := 0;
2454 for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count);
2456 // Êîëè÷åñòâî ñíàðÿäîâ
2457 utils.WriteInt(st, count);
2459 if (count = 0) then exit;
2461 for i := 0 to High(Shots) do
2462 begin
2463 if Shots[i].ShotType <> 0 then
2464 begin
2465 // Ñèãíàòóðà ñíàðÿäà
2466 utils.writeSign(st, 'SHOT');
2467 utils.writeInt(st, Byte(0)); // version
2468 // Òèï ñíàðÿäà
2469 utils.writeInt(st, Byte(Shots[i].ShotType));
2470 // Öåëü
2471 utils.writeInt(st, Word(Shots[i].Target));
2472 // UID ñòðåëÿâøåãî
2473 utils.writeInt(st, Word(Shots[i].SpawnerUID));
2474 // Ðàçìåð ïîëÿ Triggers
2475 utils.writeInt(st, Integer(Length(Shots[i].Triggers)));
2476 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2477 for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j]));
2478 // Îáúåêò ñíàðÿäà
2479 Obj_SaveState(st, @Shots[i].Obj);
2480 // Êîñòûëèíà åáàíàÿ
2481 utils.writeInt(st, Byte(Shots[i].Stopped));
2482 end;
2483 end;
2484 end;
2486 procedure g_Weapon_LoadState (st: TStream);
2487 var
2488 count, tc, i, j: Integer;
2489 begin
2490 if (st = nil) then exit;
2492 // Êîëè÷åñòâî ñíàðÿäîâ
2493 count := utils.readLongInt(st);
2494 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter');
2496 SetLength(Shots, count);
2498 if (count = 0) then exit;
2500 for i := 0 to count-1 do
2501 begin
2502 // Ñèãíàòóðà ñíàðÿäà
2503 if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature');
2504 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version');
2505 // Òèï ñíàðÿäà:
2506 Shots[i].ShotType := utils.readByte(st);
2507 // Öåëü
2508 Shots[i].Target := utils.readWord(st);
2509 // UID ñòðåëÿâøåãî
2510 Shots[i].SpawnerUID := utils.readWord(st);
2511 // Ðàçìåð ïîëÿ Triggers
2512 tc := utils.readLongInt(st);
2513 if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter');
2514 SetLength(Shots[i].Triggers, tc);
2515 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
2516 for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st);
2517 // Îáúåêò ïðåäìåòà
2518 Obj_LoadState(@Shots[i].Obj, st);
2519 // Êîñòûëèíà åáàíàÿ
2520 Shots[i].Stopped := utils.readByte(st);
2522 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
2523 Shots[i].Animation := nil;
2525 case Shots[i].ShotType of
2526 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2527 begin
2528 end;
2529 WEAPON_PLASMA:
2530 begin
2531 Shots[i].Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table
2532 end;
2533 WEAPON_BFG:
2534 begin
2535 Shots[i].Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table
2536 end;
2537 WEAPON_IMP_FIRE:
2538 begin
2539 Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
2540 end;
2541 WEAPON_BSP_FIRE:
2542 begin
2543 Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
2544 end;
2545 WEAPON_CACO_FIRE:
2546 begin
2547 Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
2548 end;
2549 WEAPON_BARON_FIRE:
2550 begin
2551 Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
2552 end;
2553 WEAPON_MANCUB_FIRE:
2554 begin
2555 Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table
2556 end;
2557 end;
2558 end;
2559 end;
2561 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2562 {$IFDEF ENABLE_GFX}
2563 var cx, cy: Integer;
2564 {$ENDIF}
2565 begin
2566 if Shots = nil then
2567 Exit;
2568 if (I > High(Shots)) or (I < 0) then Exit;
2570 with Shots[I] do
2571 begin
2572 if ShotType = 0 then Exit;
2573 Obj.X := X;
2574 Obj.Y := Y;
2575 {$IFDEF ENABLE_GFX}
2576 cx := Obj.X + (Obj.Rect.Width div 2);
2577 cy := Obj.Y + (Obj.Rect.Height div 2);
2578 {$ENDIF}
2580 case ShotType of
2581 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2582 begin
2583 if Loud then
2584 begin
2585 {$IFDEF ENABLE_GFX}
2586 if ShotType = WEAPON_SKEL_FIRE then
2587 g_GFX_QueueEffect(R_GFX_EXPLODE_SKELFIRE, (Obj.X + 32) - 32, (Obj.Y + 8) - 32)
2588 else
2589 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64);
2590 {$ENDIF}
2591 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2592 end;
2593 end;
2595 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2596 begin
2597 if loud then
2598 begin
2599 {$IFDEF ENABLE_GFX}
2600 if ShotType = WEAPON_PLASMA then
2601 g_GFX_QueueEffect(R_GFX_EXPLODE_PLASMA, cx - 16, cy - 16)
2602 else
2603 g_GFX_QueueEffect(R_GFX_EXPLODE_BSPFIRE, cx - 16, cy - 16);
2604 {$ENDIF}
2605 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2606 end;
2607 end;
2609 WEAPON_BFG: // BFG
2610 begin
2611 {$IFDEF ENABLE_GFX}
2612 g_GFX_QueueEffect(R_GFX_EXPLODE_BFG, cx - 64, cy - 64);
2613 {$ENDIF}
2614 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2615 end;
2617 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2618 begin
2619 if loud then
2620 begin
2621 {$IFDEF ENABLE_GFX}
2622 case ShotType of
2623 WEAPON_IMP_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_IMPFIRE, cx - 32, cy - 32);
2624 WEAPON_CACO_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_CACOFIRE, cx - 32, cy - 32);
2625 WEAPON_BARON_FIRE: g_GFX_QueueEffect(R_GFX_EXPLODE_BARONFIRE, cx - 32, cy - 32);
2626 end;
2627 {$ENDIF}
2628 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2629 end;
2630 end;
2632 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2633 begin
2634 {$IFDEF ENABLE_GFX}
2635 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64);
2636 {$ENDIF}
2637 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2638 end;
2639 end; // case ShotType of...
2641 ShotType := 0;
2642 Animation.Free();
2643 end;
2644 end;
2647 procedure g_Weapon_AddDynLights();
2648 var
2649 i: Integer;
2650 begin
2651 if Shots = nil then Exit;
2652 for i := 0 to High(Shots) do
2653 begin
2654 if Shots[i].ShotType = 0 then continue;
2655 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2656 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2657 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2658 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2659 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2660 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2661 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2662 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2663 (Shots[i].ShotType = WEAPON_PLASMA) or
2664 (Shots[i].ShotType = WEAPON_BFG) or
2665 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2666 false then
2667 begin
2668 if (Shots[i].ShotType = WEAPON_PLASMA) then
2669 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 0.3, 1, 0.4)
2670 else if (Shots[i].ShotType = WEAPON_BFG) then
2671 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
2672 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2673 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
2674 else
2675 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);
2676 end;
2677 end;
2678 end;
2681 procedure TShot.positionChanged (); begin end;
2684 end.