DEADSOFTWARE

g_weapons.g_Weapon_gun: faster traces (i hope)
[d2df-sdl.git] / src / game / g_weapons.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_weapons;
19 interface
21 uses
22 g_textures, g_basic, e_graphics, g_phys, BinEditor;
24 const
25 HIT_SOME = 0;
26 HIT_ROCKET = 1;
27 HIT_BFG = 2;
28 HIT_TRAP = 3;
29 HIT_FALL = 4;
30 HIT_WATER = 5;
31 HIT_ACID = 6;
32 HIT_ELECTRO = 7;
33 HIT_FLAME = 8;
34 HIT_SELF = 9;
35 HIT_DISCON = 10;
37 type
38 TShot = record
39 ShotType: Byte;
40 Target: Word;
41 SpawnerUID: Word;
42 Triggers: DWArray;
43 Obj: TObj;
44 Animation: TAnimation;
45 TextureID: DWORD;
46 Timeout: DWORD;
47 Stopped: Byte;
49 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
50 end;
53 var
54 Shots: array of TShot = nil;
55 LastShotID: Integer = 0;
57 procedure g_Weapon_LoadData();
58 procedure g_Weapon_FreeData();
59 procedure g_Weapon_Init();
60 procedure g_Weapon_Free();
61 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
62 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
63 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
65 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
66 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
67 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
68 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
69 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
70 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
71 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
74 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
75 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
76 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
77 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
78 procedure g_Weapon_bfghit(x, y: Integer);
79 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
80 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
81 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
82 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False);
84 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
85 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
86 procedure g_Weapon_Update();
87 procedure g_Weapon_Draw();
88 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
89 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
91 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
92 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
94 procedure g_Weapon_AddDynLights();
96 const
97 WEAPON_KASTET = 0;
98 WEAPON_SAW = 1;
99 WEAPON_PISTOL = 2;
100 WEAPON_SHOTGUN1 = 3;
101 WEAPON_SHOTGUN2 = 4;
102 WEAPON_CHAINGUN = 5;
103 WEAPON_ROCKETLAUNCHER = 6;
104 WEAPON_PLASMA = 7;
105 WEAPON_BFG = 8;
106 WEAPON_SUPERPULEMET = 9;
107 WEAPON_FLAMETHROWER = 10;
108 WEAPON_ZOMBY_PISTOL = 20;
109 WEAPON_IMP_FIRE = 21;
110 WEAPON_BSP_FIRE = 22;
111 WEAPON_CACO_FIRE = 23;
112 WEAPON_BARON_FIRE = 24;
113 WEAPON_MANCUB_FIRE = 25;
114 WEAPON_SKEL_FIRE = 26;
116 WP_FIRST = WEAPON_KASTET;
117 WP_LAST = WEAPON_FLAMETHROWER;
119 implementation
121 uses
122 Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
123 g_console, SysUtils, g_options, g_game,
124 g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
125 g_language, g_netmsg,
126 z_aabbtree, binheap, hashtable;
128 type
129 TWaterPanel = record
130 X, Y: Integer;
131 Width, Height: Word;
132 Active: Boolean;
133 end;
135 const
136 SHOT_ROCKETLAUNCHER_WIDTH = 14;
137 SHOT_ROCKETLAUNCHER_HEIGHT = 14;
139 SHOT_SKELFIRE_WIDTH = 14;
140 SHOT_SKELFIRE_HEIGHT = 14;
142 SHOT_PLASMA_WIDTH = 16;
143 SHOT_PLASMA_HEIGHT = 16;
145 SHOT_BFG_WIDTH = 32;
146 SHOT_BFG_HEIGHT = 32;
147 SHOT_BFG_DAMAGE = 100;
148 SHOT_BFG_RADIUS = 256;
150 SHOT_FLAME_WIDTH = 4;
151 SHOT_FLAME_HEIGHT = 4;
152 SHOT_FLAME_LIFETIME = 180;
154 SHOT_SIGNATURE = $544F4853; // 'SHOT'
156 type
157 PHitTime = ^THitTime;
158 THitTime = record
159 time: Single;
160 mon: TMonster;
161 plridx: Integer; // if mon=nil
162 end;
164 // indicies in `wgunHitTime` array
165 TBinaryHeapHitTimes = specialize TBinaryHeapBase<Integer>;
167 var
168 WaterMap: array of array of DWORD = nil;
169 wgunMonHash: THashIntInt = nil;
170 wgunHitHeap: TBinaryHeapHitTimes = nil;
171 wgunHitTime: array of THitTime = nil;
172 wgunHitTimeUsed: Integer = 0;
175 function hitTimeCompare (a, b: Integer): Boolean;
176 begin
177 if (wgunHitTime[a].time < wgunHitTime[b].time) then begin result := true; exit; end;
178 if (wgunHitTime[a].time > wgunHitTime[b].time) then begin result := false; exit; end;
179 if (wgunHitTime[a].mon <> nil) then
180 begin
181 // a is monster
182 if (wgunHitTime[b].mon = nil) then begin result := false; exit; end; // players first
183 result := (wgunHitTime[a].mon.UID < wgunHitTime[b].mon.UID); // why not?
184 end
185 else
186 begin
187 // a is player
188 if (wgunHitTime[b].mon <> nil) then begin result := true; exit; end; // players first
189 result := (wgunHitTime[a].plridx < wgunHitTime[b].plridx); // why not?
190 end;
191 end;
194 procedure appendHitTimeMon (time: Single; mon: TMonster);
195 begin
196 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
197 wgunHitTime[wgunHitTimeUsed].time := time;
198 wgunHitTime[wgunHitTimeUsed].mon := mon;
199 wgunHitTime[wgunHitTimeUsed].plridx := -1;
200 wgunHitHeap.insert(wgunHitTimeUsed);
201 Inc(wgunHitTimeUsed);
202 end;
205 procedure appendHitTimePlr (time: Single; plridx: Integer);
206 begin
207 if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128);
208 wgunHitTime[wgunHitTimeUsed].time := time;
209 wgunHitTime[wgunHitTimeUsed].mon := nil;
210 wgunHitTime[wgunHitTimeUsed].plridx := plridx;
211 wgunHitHeap.insert(wgunHitTimeUsed);
212 Inc(wgunHitTimeUsed);
213 end;
216 function FindShot(): DWORD;
217 var
218 i: Integer;
219 begin
220 if Shots <> nil then
221 for i := 0 to High(Shots) do
222 if Shots[i].ShotType = 0 then
223 begin
224 Result := i;
225 LastShotID := Result;
226 Exit;
227 end;
229 if Shots = nil then
230 begin
231 SetLength(Shots, 128);
232 Result := 0;
233 end
234 else
235 begin
236 Result := High(Shots) + 1;
237 SetLength(Shots, Length(Shots) + 128);
238 end;
239 LastShotID := Result;
240 end;
242 procedure CreateWaterMap();
243 var
244 WaterArray: Array of TWaterPanel;
245 a, b, c, m: Integer;
246 ok: Boolean;
247 begin
248 if gWater = nil then
249 Exit;
251 SetLength(WaterArray, Length(gWater));
253 for a := 0 to High(gWater) do
254 begin
255 WaterArray[a].X := gWater[a].X;
256 WaterArray[a].Y := gWater[a].Y;
257 WaterArray[a].Width := gWater[a].Width;
258 WaterArray[a].Height := gWater[a].Height;
259 WaterArray[a].Active := True;
260 end;
262 g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False);
264 for a := 0 to High(WaterArray) do
265 if WaterArray[a].Active then
266 begin
267 WaterArray[a].Active := False;
268 m := Length(WaterMap);
269 SetLength(WaterMap, m+1);
270 SetLength(WaterMap[m], 1);
271 WaterMap[m][0] := a;
272 ok := True;
274 while ok do
275 begin
276 ok := False;
277 for b := 0 to High(WaterArray) do
278 if WaterArray[b].Active then
279 for c := 0 to High(WaterMap[m]) do
280 if g_CollideAround(WaterArray[b].X,
281 WaterArray[b].Y,
282 WaterArray[b].Width,
283 WaterArray[b].Height,
284 WaterArray[WaterMap[m][c]].X,
285 WaterArray[WaterMap[m][c]].Y,
286 WaterArray[WaterMap[m][c]].Width,
287 WaterArray[WaterMap[m][c]].Height) then
288 begin
289 WaterArray[b].Active := False;
290 SetLength(WaterMap[m],
291 Length(WaterMap[m])+1);
292 WaterMap[m][High(WaterMap[m])] := b;
293 ok := True;
294 Break;
295 end;
296 end;
298 g_Game_StepLoading();
299 end;
301 WaterArray := nil;
302 end;
305 var
306 chkTrap_pl: array [0..256] of Integer;
307 chkTrap_mn: array [0..65535] of TMonster;
309 procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte);
310 var
311 //a, b, c, d, i1, i2: Integer;
312 //chkTrap_pl, chkTrap_mn: WArray;
313 plaCount: Integer = 0;
314 mnaCount: Integer = 0;
315 frameId: DWord;
318 function monsWaterCheck (mon: TMonster): Boolean;
319 begin
320 result := false; // don't stop
321 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
322 begin
323 i2 += 1;
324 chkTrap_mn[i2] := monidx;
325 end;
326 end;
329 function monsWaterCheck (mon: TMonster): Boolean;
330 begin
331 result := false; // don't stop
332 if (mon.trapCheckFrameId <> frameId) then
333 begin
334 mon.trapCheckFrameId := frameId;
335 chkTrap_mn[mnaCount] := mon;
336 Inc(mnaCount);
337 end;
338 end;
340 var
341 a, b, c, d, f: Integer;
342 pan: TPanel;
343 begin
344 if (gWater = nil) or (WaterMap = nil) then Exit;
346 frameId := g_Mons_getNewTrapFrameId();
348 //i1 := -1;
349 //i2 := -1;
351 //SetLength(chkTrap_pl, 1024);
352 //SetLength(chkTrap_mn, 1024);
353 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
354 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
356 for a := 0 to High(WaterMap) do
357 begin
358 for b := 0 to High(WaterMap[a]) do
359 begin
360 pan := gWater[WaterMap[a][b]];
361 if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue;
363 for c := 0 to High(WaterMap[a]) do
364 begin
365 pan := gWater[WaterMap[a][c]];
366 for d := 0 to High(gPlayers) do
367 begin
368 if (gPlayers[d] <> nil) and (gPlayers[d].Live) then
369 begin
370 if gPlayers[d].Collide(pan) then
371 begin
372 f := 0;
373 while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f);
374 if (f = plaCount) then
375 begin
376 chkTrap_pl[plaCount] := d;
377 Inc(plaCount);
378 if (plaCount = Length(chkTrap_pl)) then break;
379 end;
380 end;
381 end;
382 end;
384 //g_Mons_ForEach(monsWaterCheck);
385 g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck);
386 end;
388 for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t);
389 for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t);
390 end;
391 end;
393 //chkTrap_pl := nil;
394 //chkTrap_mn := nil;
395 end;
397 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
398 var
399 tt, mt: Byte;
400 mon: TMonster;
401 begin
402 Result := False;
404 tt := g_GetUIDType(SpawnerUID);
405 if tt = UID_MONSTER then
406 begin
407 mon := g_Monsters_ByUID(SpawnerUID);
408 if mon <> nil then
409 mt := g_Monsters_ByUID(SpawnerUID).MonsterType
410 else
411 mt := 0;
412 end
413 else
414 mt := 0;
416 if m = nil then Exit;
417 if m.UID = SpawnerUID then
418 begin
419 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
420 if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then
421 Exit;
422 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
423 if (m.MonsterType = MONSTER_CYBER) or
424 (m.MonsterType = MONSTER_BARREL) then
425 begin
426 Result := True;
427 Exit;
428 end;
429 end;
431 if tt = UID_MONSTER then
432 begin
433 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
434 if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then
435 Exit;
437 // Îáà ìîíñòðà îäíîãî âèäà:
438 if mt = m.MonsterType then
439 case mt of
440 MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO,
441 MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH:
442 Exit; // Ýòè íå áüþò ñâîèõ
443 end;
444 end;
446 if g_Game_IsServer then
447 begin
448 if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then
449 Result := m.Damage(d, vx, vy, SpawnerUID, t)
450 else
451 Result := True;
452 if t = HIT_FLAME then
453 m.CatchFire(SpawnerUID);
454 end
455 else
456 Result := True;
457 end;
460 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
461 begin
462 result := False;
464 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
465 if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit;
467 if g_Game_IsServer then
468 begin
469 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
470 if (t = HIT_FLAME) then p.CatchFire(SpawnerUID);
471 end;
473 result := true;
474 end;
477 procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word);
479 function monsCheck (mon: TMonster): Boolean;
480 begin
481 result := false; // don't stop
482 if (mon.Live) and (mon.UID <> SpawnerUID) then
483 begin
484 with mon do
485 begin
486 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
487 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
488 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
489 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
490 begin
491 if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit();
492 end;
493 end;
494 end;
495 end;
497 var
498 i, h: Integer;
499 st: Byte;
500 pl: TPlayer;
501 b: Boolean;
502 begin
503 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
505 h := High(gCorpses);
507 if gAdvCorpses and (h <> -1) then
508 for i := 0 to h do
509 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
510 with gCorpses[i] do
511 if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
512 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
513 g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
514 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
515 begin
516 Damage(50, 0, 0);
517 g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
518 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
519 end;
521 st := TEAM_NONE;
522 pl := g_Player_Get(SpawnerUID);
523 if pl <> nil then
524 st := pl.Team;
526 h := High(gPlayers);
528 if h <> -1 then
529 for i := 0 to h do
530 if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then
531 with gPlayers[i] do
532 if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
533 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and
534 g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2),
535 GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then
536 begin
537 if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then
538 b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME)
539 else
540 b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME);
541 if b then
542 gPlayers[i].BFGHit();
543 end;
545 //FIXME
546 g_Mons_ForEachAlive(monsCheck);
547 end;
549 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
550 var
551 find_id, FramesID: DWORD;
552 begin
553 if I < 0 then
554 find_id := FindShot()
555 else
556 begin
557 find_id := I;
558 if Integer(find_id) >= High(Shots) then
559 SetLength(Shots, find_id + 64)
560 end;
562 case ShotType of
563 WEAPON_ROCKETLAUNCHER:
564 begin
565 with Shots[find_id] do
566 begin
567 g_Obj_Init(@Obj);
569 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
570 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
572 Animation := nil;
573 Triggers := nil;
574 ShotType := WEAPON_ROCKETLAUNCHER;
575 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
576 end;
577 end;
579 WEAPON_PLASMA:
580 begin
581 with Shots[find_id] do
582 begin
583 g_Obj_Init(@Obj);
585 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
586 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
588 Triggers := nil;
589 ShotType := WEAPON_PLASMA;
590 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
591 Animation := TAnimation.Create(FramesID, True, 5);
592 end;
593 end;
595 WEAPON_BFG:
596 begin
597 with Shots[find_id] do
598 begin
599 g_Obj_Init(@Obj);
601 Obj.Rect.Width := SHOT_BFG_WIDTH;
602 Obj.Rect.Height := SHOT_BFG_HEIGHT;
604 Triggers := nil;
605 ShotType := WEAPON_BFG;
606 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
607 Animation := TAnimation.Create(FramesID, True, 6);
608 end;
609 end;
611 WEAPON_FLAMETHROWER:
612 begin
613 with Shots[find_id] do
614 begin
615 g_Obj_Init(@Obj);
617 Obj.Rect.Width := SHOT_FLAME_WIDTH;
618 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
620 Triggers := nil;
621 ShotType := WEAPON_FLAMETHROWER;
622 Animation := nil;
623 TextureID := 0;
624 g_Frames_Get(TextureID, 'FRAMES_FLAME');
625 end;
626 end;
628 WEAPON_IMP_FIRE:
629 begin
630 with Shots[find_id] do
631 begin
632 g_Obj_Init(@Obj);
634 Obj.Rect.Width := 16;
635 Obj.Rect.Height := 16;
637 Triggers := nil;
638 ShotType := WEAPON_IMP_FIRE;
639 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
640 Animation := TAnimation.Create(FramesID, True, 4);
641 end;
642 end;
644 WEAPON_CACO_FIRE:
645 begin
646 with Shots[find_id] do
647 begin
648 g_Obj_Init(@Obj);
650 Obj.Rect.Width := 16;
651 Obj.Rect.Height := 16;
653 Triggers := nil;
654 ShotType := WEAPON_CACO_FIRE;
655 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
656 Animation := TAnimation.Create(FramesID, True, 4);
657 end;
658 end;
660 WEAPON_MANCUB_FIRE:
661 begin
662 with Shots[find_id] do
663 begin
664 g_Obj_Init(@Obj);
666 Obj.Rect.Width := 32;
667 Obj.Rect.Height := 32;
669 Triggers := nil;
670 ShotType := WEAPON_MANCUB_FIRE;
671 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
672 Animation := TAnimation.Create(FramesID, True, 4);
673 end;
674 end;
676 WEAPON_BARON_FIRE:
677 begin
678 with Shots[find_id] do
679 begin
680 g_Obj_Init(@Obj);
682 Obj.Rect.Width := 32;
683 Obj.Rect.Height := 16;
685 Triggers := nil;
686 ShotType := WEAPON_BARON_FIRE;
687 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
688 Animation := TAnimation.Create(FramesID, True, 4);
689 end;
690 end;
692 WEAPON_BSP_FIRE:
693 begin
694 with Shots[find_id] do
695 begin
696 g_Obj_Init(@Obj);
698 Obj.Rect.Width := 16;
699 Obj.Rect.Height := 16;
701 Triggers := nil;
702 ShotType := WEAPON_BSP_FIRE;
703 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
704 Animation := TAnimation.Create(FramesID, True, 4);
705 end;
706 end;
708 WEAPON_SKEL_FIRE:
709 begin
710 with Shots[find_id] do
711 begin
712 g_Obj_Init(@Obj);
714 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
715 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
717 Triggers := nil;
718 ShotType := WEAPON_SKEL_FIRE;
719 target := TargetUID;
720 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
721 Animation := TAnimation.Create(FramesID, True, 5);
722 end;
723 end;
724 end;
726 Shots[find_id].Obj.X := X;
727 Shots[find_id].Obj.Y := Y;
728 Shots[find_id].Obj.Vel.X := XV;
729 Shots[find_id].Obj.Vel.Y := YV;
730 Shots[find_id].Obj.Accel.X := 0;
731 Shots[find_id].Obj.Accel.Y := 0;
732 Shots[find_id].SpawnerUID := Spawner;
733 if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then
734 Shots[find_id].Stopped := 255
735 else
736 Shots[find_id].Stopped := 0;
737 Result := find_id;
738 end;
740 procedure throw(i, x, y, xd, yd, s: Integer);
741 var
742 a: Integer;
743 begin
744 yd := yd - y;
745 xd := xd - x;
747 a := Max(Abs(xd), Abs(yd));
748 if a = 0 then
749 a := 1;
751 Shots[i].Obj.X := x;
752 Shots[i].Obj.Y := y;
753 Shots[i].Obj.Vel.X := (xd*s) div a;
754 Shots[i].Obj.Vel.Y := (yd*s) div a;
755 Shots[i].Obj.Accel.X := 0;
756 Shots[i].Obj.Accel.Y := 0;
757 Shots[i].Stopped := 0;
758 if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then
759 Shots[i].Timeout := 900 // ~25 sec
760 else
761 begin
762 if Shots[i].ShotType = WEAPON_FLAMETHROWER then
763 Shots[i].Timeout := SHOT_FLAME_LIFETIME
764 else
765 Shots[i].Timeout := 550; // ~15 sec
766 end;
767 end;
769 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
770 var
771 i, h: Integer;
773 function PlayerHit(Team: Byte = 0): Boolean;
774 var
775 i: Integer;
776 ChkTeam: Boolean;
777 p: TPlayer;
778 begin
779 Result := False;
780 h := High(gPlayers);
782 if h <> -1 then
783 for i := 0 to h do
784 if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then
785 begin
786 ChkTeam := True;
787 if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
788 begin
789 p := g_Player_Get(SpawnerUID);
790 if p <> nil then
791 ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2);
792 end;
793 if ChkTeam then
794 if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
795 begin
796 if t <> HIT_FLAME then
797 gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
798 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
799 if t = HIT_BFG then
800 g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID);
801 Result := True;
802 break;
803 end;
804 end;
805 end;
808 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
809 begin
810 result := false; // don't stop
811 if mon.Live and g_Obj_Collide(obj, @mon.Obj) then
812 begin
813 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
814 begin
815 if (t <> HIT_FLAME) then
816 begin
817 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
818 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
819 end;
820 result := True;
821 end;
822 end;
823 end;
826 function monsCheckHit (mon: TMonster): Boolean;
827 begin
828 result := false; // don't stop
829 if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then
830 begin
831 if (t <> HIT_FLAME) then
832 begin
833 mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
834 (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
835 end;
836 result := true;
837 end;
838 end;
840 function MonsterHit(): Boolean;
841 begin
842 //result := g_Mons_ForEach(monsCheckHit);
843 //FIXME: accelerate this!
844 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
845 end;
847 begin
848 Result := 0;
850 if HitCorpses then
851 begin
852 h := High(gCorpses);
854 if gAdvCorpses and (h <> -1) then
855 for i := 0 to h do
856 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
857 g_Obj_Collide(obj, @gCorpses[i].Obj) then
858 begin
859 // Ðàñïèëèâàåì òðóï:
860 gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4,
861 (obj^.Vel.Y+obj^.Accel.Y) div 4);
862 Result := 1;
863 end;
864 end;
866 case gGameSettings.GameMode of
867 // Êàìïàíèÿ:
868 GM_COOP, GM_SINGLE:
869 begin
870 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
871 if MonsterHit() then
872 begin
873 Result := 2;
874 Exit;
875 end;
877 if PlayerHit() then
878 begin
879 Result := 1;
880 Exit;
881 end;
882 end;
884 // Äåçìàò÷:
885 GM_DM:
886 begin
887 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
888 if PlayerHit() then
889 begin
890 Result := 1;
891 Exit;
892 end;
894 if MonsterHit() then
895 begin
896 Result := 2;
897 Exit;
898 end;
899 end;
901 // Êîìàíäíûå:
902 GM_TDM, GM_CTF:
903 begin
904 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
905 if PlayerHit(2) then
906 begin
907 Result := 1;
908 Exit;
909 end;
911 // Ïîòîì ìîíñòðîâ
912 if MonsterHit() then
913 begin
914 Result := 2;
915 Exit;
916 end;
918 // È â êîíöå ñâîèõ èãðîêîâ
919 if PlayerHit(1) then
920 begin
921 Result := 1;
922 Exit;
923 end;
924 end;
926 end;
927 end;
929 function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean;
930 begin
931 Result := False;
933 case g_GetUIDType(UID) of
934 UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t);
935 UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t);
936 else Exit;
937 end;
938 end;
940 function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean;
941 var
942 r: Integer; // squared radius
944 function monsExCheck (mon: TMonster): Boolean;
945 var
946 dx, dy, mm: Integer;
947 begin
948 result := false; // don't stop
949 begin
950 dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X;
951 dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y;
953 if dx > 1000 then dx := 1000;
954 if dy > 1000 then dy := 1000;
956 if (dx*dx+dy*dy < r) then
957 begin
958 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
959 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
961 mm := Max(abs(dx), abs(dy));
962 if mm = 0 then mm := 1;
964 if mon.Live then
965 begin
966 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
967 end;
969 mon.Push((dx*7) div mm, (dy*7) div mm);
970 end;
971 end;
972 end;
974 var
975 i, h, dx, dy, m, mm: Integer;
976 _angle: SmallInt;
977 begin
978 result := false;
980 g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT);
982 r := rad*rad;
984 h := High(gPlayers);
986 if h <> -1 then
987 for i := 0 to h do
988 if (gPlayers[i] <> nil) and gPlayers[i].Live then
989 with gPlayers[i] do
990 begin
991 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
992 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
994 if dx > 1000 then dx := 1000;
995 if dy > 1000 then dy := 1000;
997 if dx*dx+dy*dy < r then
998 begin
999 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1000 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1002 mm := Max(abs(dx), abs(dy));
1003 if mm = 0 then mm := 1;
1005 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1006 gPlayers[i].Push((dx*7) div mm, (dy*7) div mm);
1007 end;
1008 end;
1010 //g_Mons_ForEach(monsExCheck);
1011 g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
1013 h := High(gCorpses);
1015 if gAdvCorpses and (h <> -1) then
1016 for i := 0 to h do
1017 if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
1018 with gCorpses[i] do
1019 begin
1020 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1021 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1023 if dx > 1000 then dx := 1000;
1024 if dy > 1000 then dy := 1000;
1026 if dx*dx+dy*dy < r then
1027 begin
1028 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1029 Obj.Rect.Width, Obj.Rect.Height);
1031 mm := Max(abs(dx), abs(dy));
1032 if mm = 0 then mm := 1;
1034 Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm);
1035 end;
1036 end;
1038 h := High(gGibs);
1040 if gAdvGibs and (h <> -1) then
1041 for i := 0 to h do
1042 if gGibs[i].Live then
1043 with gGibs[i] do
1044 begin
1045 dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
1046 dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
1048 if dx > 1000 then dx := 1000;
1049 if dy > 1000 then dy := 1000;
1051 if dx*dx+dy*dy < r then
1052 begin
1053 m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
1054 Obj.Rect.Width, Obj.Rect.Height);
1055 _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
1056 Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y);
1058 g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle);
1059 positionChanged(); // this updates spatial accelerators
1060 end;
1061 end;
1062 end;
1064 procedure g_Weapon_Init();
1065 begin
1066 CreateWaterMap();
1067 end;
1069 procedure g_Weapon_Free();
1070 var
1071 i: Integer;
1072 begin
1073 if Shots <> nil then
1074 begin
1075 for i := 0 to High(Shots) do
1076 if Shots[i].ShotType <> 0 then
1077 Shots[i].Animation.Free();
1079 Shots := nil;
1080 end;
1082 WaterMap := nil;
1083 end;
1085 procedure g_Weapon_LoadData();
1086 begin
1087 e_WriteLog('Loading weapons data...', MSG_NOTIFY);
1089 g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH');
1090 g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH');
1091 g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK');
1092 g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK');
1093 g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW');
1094 g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW');
1095 g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW');
1096 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2');
1097 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN');
1098 g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW');
1099 g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET');
1100 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA');
1101 g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL');
1102 g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN');
1103 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG');
1104 g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE');
1105 g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG');
1106 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET');
1107 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG');
1108 g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER');
1109 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA');
1110 g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER');
1111 g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL');
1112 g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL');
1113 g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV');
1114 g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK');
1115 g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK');
1116 g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK');
1117 g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1');
1118 g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2');
1119 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1');
1120 g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2');
1122 g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET');
1123 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2);
1124 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2);
1125 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2);
1126 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2);
1127 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2);
1128 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2);
1129 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2);
1130 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1131 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6);
1132 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3);
1133 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6);
1134 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3);
1135 g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4);
1136 g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8);
1137 g_Frames_CreateWAD(nil, 'FRAMES_FLAME', GameWAD+':TEXTURES\FLAME', 32, 32, 11);
1138 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1139 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5);
1140 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3);
1141 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1142 g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False);
1144 g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET');
1145 g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL');
1147 wgunMonHash := hashNewIntInt();
1148 wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeCompare);
1149 end;
1151 procedure g_Weapon_FreeData();
1152 begin
1153 e_WriteLog('Releasing weapons data...', MSG_NOTIFY);
1155 g_Sound_Delete('SOUND_WEAPON_HITPUNCH');
1156 g_Sound_Delete('SOUND_WEAPON_MISSPUNCH');
1157 g_Sound_Delete('SOUND_WEAPON_HITBERSERK');
1158 g_Sound_Delete('SOUND_WEAPON_MISSBERSERK');
1159 g_Sound_Delete('SOUND_WEAPON_SELECTSAW');
1160 g_Sound_Delete('SOUND_WEAPON_IDLESAW');
1161 g_Sound_Delete('SOUND_WEAPON_HITSAW');
1162 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2');
1163 g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN');
1164 g_Sound_Delete('SOUND_WEAPON_FIRESAW');
1165 g_Sound_Delete('SOUND_WEAPON_FIREROCKET');
1166 g_Sound_Delete('SOUND_WEAPON_FIREPLASMA');
1167 g_Sound_Delete('SOUND_WEAPON_FIREPISTOL');
1168 g_Sound_Delete('SOUND_WEAPON_FIRECGUN');
1169 g_Sound_Delete('SOUND_WEAPON_FIREBFG');
1170 g_Sound_Delete('SOUND_FIRE');
1171 g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG');
1172 g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET');
1173 g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG');
1174 g_Sound_Delete('SOUND_WEAPON_BFGWATER');
1175 g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA');
1176 g_Sound_Delete('SOUND_WEAPON_PLASMAWATER');
1177 g_Sound_Delete('SOUND_WEAPON_FIREBALL');
1178 g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL');
1179 g_Sound_Delete('SOUND_WEAPON_FIREREV');
1180 g_Sound_Delete('SOUND_PLAYER_JETFLY');
1181 g_Sound_Delete('SOUND_PLAYER_JETON');
1182 g_Sound_Delete('SOUND_PLAYER_JETOFF');
1183 g_Sound_Delete('SOUND_PLAYER_CASING1');
1184 g_Sound_Delete('SOUND_PLAYER_CASING2');
1185 g_Sound_Delete('SOUND_PLAYER_SHELL1');
1186 g_Sound_Delete('SOUND_PLAYER_SHELL2');
1188 g_Texture_Delete('TEXTURE_WEAPON_ROCKET');
1189 g_Frames_DeleteByName('FRAMES_WEAPON_BFG');
1190 g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA');
1191 g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE');
1192 g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE');
1193 g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE');
1194 g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE');
1195 g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET');
1196 g_Frames_DeleteByName('FRAMES_EXPLODE_BFG');
1197 g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE');
1198 g_Frames_DeleteByName('FRAMES_BFGHIT');
1199 g_Frames_DeleteByName('FRAMES_FIRE');
1200 g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA');
1201 g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE');
1202 g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE');
1203 g_Frames_DeleteByName('FRAMES_SMOKE');
1204 g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE');
1205 g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE');
1206 end;
1209 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1210 var
1211 i: Integer;
1212 begin
1213 result := false;
1214 for i := 0 to High(gPlayers) do
1215 begin
1216 if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then
1217 begin
1218 if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1219 begin
1220 if AllowPush then gPlayers[i].Push(vx, vy);
1221 result := true;
1222 end;
1223 end;
1224 end;
1225 end;
1228 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1230 function monsCheck (mon: TMonster): Boolean;
1231 begin
1232 result := false; // don't stop
1233 if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then
1234 begin
1235 if AllowPush then mon.Push(vx, vy);
1236 result := true;
1237 end;
1238 end;
1240 begin
1241 result := 0;
1242 if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1
1243 else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2;
1244 end;
1247 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1248 var
1249 a: Integer;
1250 x2, y2: Integer;
1251 dx, dy: Integer;
1252 xe, ye: Integer;
1253 xi, yi: Integer;
1254 s, c: Extended;
1255 //vx, vy: Integer;
1256 xx, yy, d: Integer;
1258 i: Integer;
1259 t1, _collide: Boolean;
1260 w, h: Word;
1261 begin
1262 a := GetAngle(x, y, xd, yd)+180;
1264 SinCos(DegToRad(-a), s, c);
1266 if Abs(s) < 0.01 then s := 0;
1267 if Abs(c) < 0.01 then c := 0;
1269 x2 := x+Round(c*gMapInfo.Width);
1270 y2 := y+Round(s*gMapInfo.Width);
1272 t1 := gWalls <> nil;
1273 _collide := False;
1274 w := gMapInfo.Width;
1275 h := gMapInfo.Height;
1277 xe := 0;
1278 ye := 0;
1279 dx := x2-x;
1280 dy := y2-y;
1282 if (xd = 0) and (yd = 0) then Exit;
1284 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1285 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1287 dx := Abs(dx);
1288 dy := Abs(dy);
1290 if dx > dy then d := dx else d := dy;
1292 //blood vel, for Monster.Damage()
1293 //vx := (dx*10 div d)*xi;
1294 //vy := (dy*10 div d)*yi;
1296 xx := x;
1297 yy := y;
1299 for i := 1 to d do
1300 begin
1301 xe := xe+dx;
1302 ye := ye+dy;
1304 if xe > d then
1305 begin
1306 xe := xe-d;
1307 xx := xx+xi;
1308 end;
1310 if ye > d then
1311 begin
1312 ye := ye-d;
1313 yy := yy+yi;
1314 end;
1316 if (yy > h) or (yy < 0) then Break;
1317 if (xx > w) or (xx < 0) then Break;
1319 if t1 then
1320 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1321 begin
1322 _collide := True;
1323 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1324 if g_Game_IsServer and g_Game_IsNet then
1325 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1326 end;
1328 if not _collide then
1329 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1331 if _collide then
1332 Break;
1333 end;
1335 if CheckTrigger and g_Game_IsServer then
1336 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1337 end;
1340 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1341 const
1342 HHGridSize = 64;
1343 Nothing = -666666;
1345 var
1346 hitray: Ray2D;
1347 xi, yi: Integer;
1349 function doPlayerHit (idx: Integer): Boolean;
1350 begin
1351 result := false;
1352 if (idx < 0) or (idx > High(gPlayers)) then exit;
1353 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1354 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1355 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1356 end;
1358 function doMonsterHit (mon: TMonster): Boolean;
1359 begin
1360 result := false;
1361 if (mon = nil) then exit;
1362 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1363 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1364 end;
1366 // get nearest player along hitray
1367 // return `true` if instant hit was detected
1368 function playerPossibleHit (): Boolean;
1369 var
1370 i: Integer;
1371 aabb: AABB2D;
1372 tmin: Single;
1373 begin
1374 result := false;
1375 for i := 0 to High(gPlayers) do
1376 begin
1377 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1378 begin
1379 aabb := gPlayers[i].mapAABB;
1380 // inside?
1381 if aabb.contains(x, y) then
1382 begin
1383 if doPlayerHit(i) then begin result := true; exit; end;
1384 end
1385 else if (aabb.intersects(hitray, @tmin)) then
1386 begin
1387 // intersect
1388 if (tmin <= 0) then
1389 begin
1390 if doPlayerHit(i) then begin result := true; exit; end;
1391 end
1392 else
1393 begin
1394 appendHitTimePlr(tmin, i);
1395 end;
1396 end;
1397 end;
1398 end;
1399 end;
1401 function monsPossibleHitInstant (mon: TMonster): Boolean;
1402 var
1403 aabb: AABB2D;
1404 begin
1405 result := false; // don't stop
1406 aabb := mon.mapAABB;
1407 if aabb.contains(x, y) then
1408 begin
1409 result := doMonsterHit(mon);
1410 end;
1411 end;
1413 function monsPossibleHit (mon: TMonster): Boolean;
1414 var
1415 aabb: AABB2D;
1416 tmin: Single;
1417 begin
1418 result := false; // don't stop
1419 if not wgunMonHash.put(Integer(mon.UID), 1) then
1420 begin
1421 // new monster; calculate hitpoint
1422 aabb := mon.mapAABB;
1423 if (aabb.intersects(hitray, @tmin)) then
1424 begin
1425 if (tmin < 0) then tmin := 1.0;
1426 appendHitTimeMon(tmin, mon);
1427 end;
1428 end;
1429 end;
1431 var
1432 a: Integer;
1433 x2, y2: Integer;
1434 dx, dy: Integer;
1435 xe, ye: Integer;
1436 s, c: Extended;
1437 xx, yy, d: Integer;
1438 leftToNextMonsterQuery: Integer = 0;
1439 i: Integer;
1440 t1: Boolean;
1441 w, h: Word;
1442 wallWasHit: Boolean = false;
1443 wallHitX: Integer = 0;
1444 wallHitY: Integer = 0;
1445 didHit: Boolean = false;
1446 begin
1447 wgunMonHash.reset(); //FIXME: clear hash on level change
1448 wgunHitHeap.clear();
1449 wgunHitTimeUsed := 0;
1451 a := GetAngle(x, y, xd, yd)+180;
1453 SinCos(DegToRad(-a), s, c);
1455 if Abs(s) < 0.01 then s := 0;
1456 if Abs(c) < 0.01 then c := 0;
1458 x2 := x+Round(c*gMapInfo.Width);
1459 y2 := y+Round(s*gMapInfo.Width);
1461 hitray := Ray2D.Create(x, y, x2, y2);
1463 t1 := (gWalls <> nil);
1464 w := gMapInfo.Width;
1465 h := gMapInfo.Height;
1467 dx := x2-x;
1468 dy := y2-y;
1470 if (xd = 0) and (yd = 0) then Exit;
1472 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1473 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1475 // check instant hits
1476 xx := x;
1477 yy := y;
1478 if (dx < 0) then Dec(xx);
1479 if (dy < 0) then Dec(yy);
1481 dx := Abs(dx);
1482 dy := Abs(dy);
1484 if playerPossibleHit() then exit; // instant hit
1485 if g_Mons_ForEachAliveAt(xx, yy, 3, 3, monsPossibleHitInstant) then exit; // instant hit
1487 if dx > dy then d := dx else d := dy;
1489 //blood vel, for Monster.Damage()
1490 //vx := (dx*10 div d)*xi;
1491 //vy := (dy*10 div d)*yi;
1493 // find wall, collect monsters
1494 begin
1495 xe := 0;
1496 ye := 0;
1497 xx := x;
1498 yy := y;
1499 for i := 1 to d do
1500 begin
1501 xe += dx;
1502 ye += dy;
1503 if (xe > d) then begin xe -= d; xx += xi; end;
1504 if (ye > d) then begin ye -= d; yy += yi; end;
1506 // wtf?!
1507 //if (yy > h) or (yy < 0) then break;
1508 //if (xx > w) or (xx < 0) then break;
1510 if t1 and (xx >= 0) and (yy >= 0) and (xx < w) and (yy < h) then
1511 begin
1512 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1513 begin
1514 wallWasHit := true;
1515 wallHitX := xx-xi;
1516 wallHitY := yy-xi;
1517 end;
1518 end;
1520 if (leftToNextMonsterQuery <> 0) and not wallWasHit then
1521 begin
1522 Dec(leftToNextMonsterQuery);
1523 end
1524 else
1525 begin
1526 // check monsters
1527 g_Mons_ForEachAliveAt(xx-HHGridSize div 2, yy-HHGridSize div 2, HHGridSize+HHGridSize div 2, HHGridSize+HHGridSize div 2, monsPossibleHit);
1528 leftToNextMonsterQuery := HHGridSize; // again
1529 if wallWasHit then break;
1530 end;
1531 end;
1533 if not wallWasHit then
1534 begin
1535 wallHitX := xx;
1536 wallHitY := yy;
1537 end;
1538 end;
1540 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1541 // also, if `wallWasHit` is true, then `wallHitX` and `wallHitY` contains wall coords
1542 while (wgunHitHeap.count > 0) do
1543 begin
1544 // has some entities to check, do it
1545 i := wgunHitHeap.front;
1546 wgunHitHeap.popFront();
1547 hitray.atTime(wgunHitTime[i].time, xe, ye);
1548 // check if it is not behind the wall
1549 if ((xe-x)*(xe-x)+(ye-y)*(ye-y) < (wallHitX-x)*(wallHitX-x)+(wallHitY-y)*(wallHitY-y)) then
1550 begin
1551 if (wgunHitTime[i].mon <> nil) then
1552 begin
1553 didHit := doMonsterHit(wgunHitTime[i].mon);
1554 end
1555 else
1556 begin
1557 didHit := doPlayerHit(wgunHitTime[i].plridx);
1558 end;
1559 if didHit then
1560 begin
1561 // need new coords for trigger
1562 wallHitX := xe;
1563 wallHitY := ye;
1564 wallWasHit := false; // no sparks
1565 break;
1566 end;
1567 end;
1568 end;
1570 // need sparks?
1571 if wallWasHit then
1572 begin
1573 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1574 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1575 end;
1577 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1578 end;
1581 procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word);
1582 var
1583 obj: TObj;
1584 begin
1585 obj.X := X;
1586 obj.Y := Y;
1587 obj.rect.X := 0;
1588 obj.rect.Y := 0;
1589 obj.rect.Width := 39;
1590 obj.rect.Height := 52;
1591 obj.Vel.X := 0;
1592 obj.Vel.Y := 0;
1593 obj.Accel.X := 0;
1594 obj.Accel.Y := 0;
1596 if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then
1597 g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y)
1598 else
1599 g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y);
1600 end;
1602 function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer;
1603 var
1604 obj: TObj;
1605 begin
1606 obj.X := X;
1607 obj.Y := Y;
1608 obj.rect.X := 0;
1609 obj.rect.Y := 0;
1610 obj.rect.Width := 32;
1611 obj.rect.Height := 52;
1612 obj.Vel.X := 0;
1613 obj.Vel.Y := 0;
1614 obj.Accel.X := 0;
1615 obj.Accel.Y := 0;
1617 Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME);
1618 end;
1620 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1621 Silent: Boolean = False);
1622 var
1623 find_id: DWORD;
1624 dx, dy: Integer;
1625 begin
1626 if WID < 0 then
1627 find_id := FindShot()
1628 else
1629 begin
1630 find_id := WID;
1631 if Integer(find_id) >= High(Shots) then
1632 SetLength(Shots, find_id + 64)
1633 end;
1635 with Shots[find_id] do
1636 begin
1637 g_Obj_Init(@Obj);
1639 Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH;
1640 Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT;
1642 dx := IfThen(xd > x, -Obj.Rect.Width, 0);
1643 dy := -(Obj.Rect.Height div 2);
1645 ShotType := WEAPON_ROCKETLAUNCHER;
1646 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1648 Animation := nil;
1649 triggers := nil;
1650 g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID);
1651 end;
1653 Shots[find_id].SpawnerUID := SpawnerUID;
1655 if not Silent then
1656 g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y);
1657 end;
1659 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word;
1660 WID: Integer = -1; Silent: Boolean = False);
1661 var
1662 find_id, FramesID: DWORD;
1663 dx, dy: Integer;
1664 begin
1665 if WID < 0 then
1666 find_id := FindShot()
1667 else
1668 begin
1669 find_id := WID;
1670 if Integer(find_id) >= High(Shots) then
1671 SetLength(Shots, find_id + 64)
1672 end;
1674 with Shots[find_id] do
1675 begin
1676 g_Obj_Init(@Obj);
1678 Obj.Rect.Width := SHOT_SKELFIRE_WIDTH;
1679 Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT;
1681 dx := -(Obj.Rect.Width div 2);
1682 dy := -(Obj.Rect.Height div 2);
1684 ShotType := WEAPON_SKEL_FIRE;
1685 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12);
1687 triggers := nil;
1688 target := TargetUID;
1689 g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE');
1690 Animation := TAnimation.Create(FramesID, True, 5);
1691 end;
1693 Shots[find_id].SpawnerUID := SpawnerUID;
1695 if not Silent then
1696 g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y);
1697 end;
1699 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1700 Silent: Boolean = False);
1701 var
1702 find_id, FramesID: DWORD;
1703 dx, dy: Integer;
1704 begin
1705 if WID < 0 then
1706 find_id := FindShot()
1707 else
1708 begin
1709 find_id := WID;
1710 if Integer(find_id) >= High(Shots) then
1711 SetLength(Shots, find_id + 64);
1712 end;
1714 with Shots[find_id] do
1715 begin
1716 g_Obj_Init(@Obj);
1718 Obj.Rect.Width := SHOT_PLASMA_WIDTH;
1719 Obj.Rect.Height := SHOT_PLASMA_HEIGHT;
1721 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1722 dy := -(Obj.Rect.Height div 2);
1724 ShotType := WEAPON_PLASMA;
1725 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1727 triggers := nil;
1728 g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA');
1729 Animation := TAnimation.Create(FramesID, True, 5);
1730 end;
1732 Shots[find_id].SpawnerUID := SpawnerUID;
1734 if not Silent then
1735 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1736 end;
1738 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1739 Silent: Boolean = False);
1740 var
1741 find_id: DWORD;
1742 dx, dy: Integer;
1743 begin
1744 if WID < 0 then
1745 find_id := FindShot()
1746 else
1747 begin
1748 find_id := WID;
1749 if Integer(find_id) >= High(Shots) then
1750 SetLength(Shots, find_id + 64);
1751 end;
1753 with Shots[find_id] do
1754 begin
1755 g_Obj_Init(@Obj);
1757 Obj.Rect.Width := SHOT_FLAME_WIDTH;
1758 Obj.Rect.Height := SHOT_FLAME_HEIGHT;
1760 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1761 dy := -(Obj.Rect.Height div 2);
1763 ShotType := WEAPON_FLAMETHROWER;
1764 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1766 triggers := nil;
1767 Animation := nil;
1768 TextureID := 0;
1769 g_Frames_Get(TextureID, 'FRAMES_FLAME');
1770 end;
1772 Shots[find_id].SpawnerUID := SpawnerUID;
1774 // if not Silent then
1775 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1776 end;
1778 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1779 Silent: Boolean = False);
1780 var
1781 find_id, FramesID: DWORD;
1782 dx, dy: Integer;
1783 begin
1784 if WID < 0 then
1785 find_id := FindShot()
1786 else
1787 begin
1788 find_id := WID;
1789 if Integer(find_id) >= High(Shots) then
1790 SetLength(Shots, find_id + 64)
1791 end;
1793 with Shots[find_id] do
1794 begin
1795 g_Obj_Init(@Obj);
1797 Obj.Rect.Width := 16;
1798 Obj.Rect.Height := 16;
1800 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1801 dy := -(Obj.Rect.Height div 2);
1803 ShotType := WEAPON_IMP_FIRE;
1804 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1806 triggers := nil;
1807 g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE');
1808 Animation := TAnimation.Create(FramesID, True, 4);
1809 end;
1811 Shots[find_id].SpawnerUID := SpawnerUID;
1813 if not Silent then
1814 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1815 end;
1817 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1818 Silent: Boolean = False);
1819 var
1820 find_id, FramesID: DWORD;
1821 dx, dy: Integer;
1822 begin
1823 if WID < 0 then
1824 find_id := FindShot()
1825 else
1826 begin
1827 find_id := WID;
1828 if Integer(find_id) >= High(Shots) then
1829 SetLength(Shots, find_id + 64)
1830 end;
1832 with Shots[find_id] do
1833 begin
1834 g_Obj_Init(@Obj);
1836 Obj.Rect.Width := 16;
1837 Obj.Rect.Height := 16;
1839 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1840 dy := -(Obj.Rect.Height div 2);
1842 ShotType := WEAPON_CACO_FIRE;
1843 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1845 triggers := nil;
1846 g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE');
1847 Animation := TAnimation.Create(FramesID, True, 4);
1848 end;
1850 Shots[find_id].SpawnerUID := SpawnerUID;
1852 if not Silent then
1853 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1854 end;
1856 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1857 Silent: Boolean = False);
1858 var
1859 find_id, FramesID: DWORD;
1860 dx, dy: Integer;
1861 begin
1862 if WID < 0 then
1863 find_id := FindShot()
1864 else
1865 begin
1866 find_id := WID;
1867 if Integer(find_id) >= High(Shots) then
1868 SetLength(Shots, find_id + 64)
1869 end;
1871 with Shots[find_id] do
1872 begin
1873 g_Obj_Init(@Obj);
1875 Obj.Rect.Width := 32;
1876 Obj.Rect.Height := 16;
1878 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1879 dy := -(Obj.Rect.Height div 2);
1881 ShotType := WEAPON_BARON_FIRE;
1882 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1884 triggers := nil;
1885 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE');
1886 Animation := TAnimation.Create(FramesID, True, 4);
1887 end;
1889 Shots[find_id].SpawnerUID := SpawnerUID;
1891 if not Silent then
1892 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1893 end;
1895 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1896 Silent: Boolean = False);
1897 var
1898 find_id, FramesID: DWORD;
1899 dx, dy: Integer;
1900 begin
1901 if WID < 0 then
1902 find_id := FindShot()
1903 else
1904 begin
1905 find_id := WID;
1906 if Integer(find_id) >= High(Shots) then
1907 SetLength(Shots, find_id + 64)
1908 end;
1910 with Shots[find_id] do
1911 begin
1912 g_Obj_Init(@Obj);
1914 Obj.Rect.Width := 16;
1915 Obj.Rect.Height := 16;
1917 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1918 dy := -(Obj.Rect.Height div 2);
1920 ShotType := WEAPON_BSP_FIRE;
1921 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1923 triggers := nil;
1925 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE');
1926 Animation := TAnimation.Create(FramesID, True, 4);
1927 end;
1929 Shots[find_id].SpawnerUID := SpawnerUID;
1931 if not Silent then
1932 g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
1933 end;
1935 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1936 Silent: Boolean = False);
1937 var
1938 find_id, FramesID: DWORD;
1939 dx, dy: Integer;
1940 begin
1941 if WID < 0 then
1942 find_id := FindShot()
1943 else
1944 begin
1945 find_id := WID;
1946 if Integer(find_id) >= High(Shots) then
1947 SetLength(Shots, find_id + 64)
1948 end;
1950 with Shots[find_id] do
1951 begin
1952 g_Obj_Init(@Obj);
1954 Obj.Rect.Width := 32;
1955 Obj.Rect.Height := 32;
1957 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1958 dy := -(Obj.Rect.Height div 2);
1960 ShotType := WEAPON_MANCUB_FIRE;
1961 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
1963 triggers := nil;
1965 g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE');
1966 Animation := TAnimation.Create(FramesID, True, 4);
1967 end;
1969 Shots[find_id].SpawnerUID := SpawnerUID;
1971 if not Silent then
1972 g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y);
1973 end;
1975 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1;
1976 Silent: Boolean = False);
1977 var
1978 find_id, FramesID: DWORD;
1979 dx, dy: Integer;
1980 begin
1981 if WID < 0 then
1982 find_id := FindShot()
1983 else
1984 begin
1985 find_id := WID;
1986 if Integer(find_id) >= High(Shots) then
1987 SetLength(Shots, find_id + 64)
1988 end;
1990 with Shots[find_id] do
1991 begin
1992 g_Obj_Init(@Obj);
1994 Obj.Rect.Width := SHOT_BFG_WIDTH;
1995 Obj.Rect.Height := SHOT_BFG_HEIGHT;
1997 dx := IfThen(xd>x, -Obj.Rect.Width, 0);
1998 dy := -(Obj.Rect.Height div 2);
2000 ShotType := WEAPON_BFG;
2001 throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16);
2003 triggers := nil;
2004 g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG');
2005 Animation := TAnimation.Create(FramesID, True, 6);
2006 end;
2008 Shots[find_id].SpawnerUID := SpawnerUID;
2010 if not Silent then
2011 g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y);
2012 end;
2014 procedure g_Weapon_bfghit(x, y: Integer);
2015 var
2016 ID: DWORD;
2017 Anim: TAnimation;
2018 begin
2019 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
2020 begin
2021 Anim := TAnimation.Create(ID, False, 4);
2022 g_GFX_OnceAnim(x-32, y-32, Anim);
2023 Anim.Free();
2024 end;
2025 end;
2027 procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word;
2028 Silent: Boolean = False);
2029 begin
2030 if not Silent then
2031 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y);
2033 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2034 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then
2035 begin
2036 g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False);
2037 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2038 end;
2039 end;
2041 procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2042 Silent: Boolean = False);
2043 begin
2044 if not Silent then
2045 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y);
2047 g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True);
2048 if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and
2049 (g_GetUIDType(SpawnerUID) = UID_PLAYER) then
2050 begin
2051 g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False);
2052 g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False);
2053 end;
2054 end;
2056 procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2057 Silent: Boolean = False);
2058 var
2059 i, j: Integer;
2060 begin
2061 if not Silent then
2062 if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y);
2064 for i := 0 to 9 do
2065 begin
2066 j := Random(17)-8; // -8 .. 8
2067 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0);
2068 end;
2069 end;
2071 procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word;
2072 Silent: Boolean = False);
2073 var
2074 a, i, j: Integer;
2075 begin
2076 if not Silent then
2077 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y);
2079 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20;
2080 for i := 0 to a do
2081 begin
2082 j := Random(41)-20; // -20 .. 20
2083 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0);
2084 end;
2085 end;
2087 procedure g_Weapon_Update();
2088 var
2089 i, a, h, cx, cy, oldvx, oldvy, tf: Integer;
2090 _id: DWORD;
2091 Anim: TAnimation;
2092 t: DWArray;
2093 st: Word;
2094 s: String;
2095 o: TObj;
2096 spl: Boolean;
2097 Loud: Boolean;
2098 tcx, tcy: Integer;
2099 begin
2100 if Shots = nil then
2101 Exit;
2103 for i := 0 to High(Shots) do
2104 begin
2105 if Shots[i].ShotType = 0 then
2106 Continue;
2108 Loud := True;
2110 with Shots[i] do
2111 begin
2112 Timeout := Timeout - 1;
2113 oldvx := Obj.Vel.X;
2114 oldvy := Obj.Vel.Y;
2115 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2116 if (Stopped = 0) and g_Game_IsServer then
2117 t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
2118 SpawnerUID, ACTIVATE_SHOT, triggers)
2119 else
2120 t := nil;
2122 if t <> nil then
2123 begin
2124 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2125 if triggers = nil then
2126 triggers := t
2127 else
2128 begin
2129 h := High(t);
2131 for a := 0 to h do
2132 if not InDWArray(t[a], triggers) then
2133 begin
2134 SetLength(triggers, Length(triggers)+1);
2135 triggers[High(triggers)] := t[a];
2136 end;
2137 end;
2138 end;
2140 // Àíèìàöèÿ ñíàðÿäà:
2141 if Animation <> nil then
2142 Animation.Update();
2144 // Äâèæåíèå:
2145 spl := (ShotType <> WEAPON_PLASMA) and
2146 (ShotType <> WEAPON_BFG) and
2147 (ShotType <> WEAPON_BSP_FIRE) and
2148 (ShotType <> WEAPON_FLAMETHROWER);
2150 if Stopped = 0 then
2151 begin
2152 st := g_Obj_Move(@Obj, False, spl);
2153 end
2154 else
2155 begin
2156 st := 0;
2157 end;
2158 positionChanged(); // this updates spatial accelerators
2160 if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or
2161 (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then
2162 begin
2163 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2164 ShotType := 0;
2165 Animation.Free();
2166 Continue;
2167 end;
2169 cx := Obj.X + (Obj.Rect.Width div 2);
2170 cy := Obj.Y + (Obj.Rect.Height div 2);
2172 case ShotType of
2173 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2174 begin
2175 // Âûëåòåëà èç âîäû:
2176 if WordBool(st and MOVE_HITAIR) then
2177 g_Obj_SetSpeed(@Obj, 12);
2179 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2180 if WordBool(st and MOVE_INWATER) then
2181 g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2),
2182 Obj.Y+(Obj.Rect.Height div 2),
2183 1+Random(3), 16, 16)
2184 else
2185 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2186 begin
2187 Anim := TAnimation.Create(_id, False, 3);
2188 Anim.Alpha := 150;
2189 g_GFX_OnceAnim(Obj.X-14+Random(9),
2190 Obj.Y+(Obj.Rect.Height div 2)-20+Random(9),
2191 Anim, ONCEANIM_SMOKE);
2192 Anim.Free();
2193 end;
2195 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2196 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2197 (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or
2198 (Timeout < 1) then
2199 begin
2200 Obj.Vel.X := 0;
2201 Obj.Vel.Y := 0;
2203 g_Weapon_Explode(cx, cy, 60, SpawnerUID);
2205 if ShotType = WEAPON_SKEL_FIRE then
2206 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2207 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2208 begin
2209 Anim := TAnimation.Create(TextureID, False, 8);
2210 Anim.Blending := False;
2211 g_GFX_OnceAnim((Obj.X+32)-58, (Obj.Y+8)-36, Anim);
2212 g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0);
2213 Anim.Free();
2214 end;
2215 end
2216 else
2217 begin // Âçðûâ Ðàêåòû
2218 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2219 begin
2220 Anim := TAnimation.Create(TextureID, False, 6);
2221 Anim.Blending := False;
2222 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2223 g_DynLightExplosion(cx, cy, 64, 1, 0, 0);
2224 Anim.Free();
2225 end;
2226 end;
2228 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2230 ShotType := 0;
2231 end;
2233 if ShotType = WEAPON_SKEL_FIRE then
2234 begin // Ñàìîíàâîäêà ñíàðÿäà Ñêåëåòà:
2235 if GetPos(target, @o) then
2236 throw(i, Obj.X, Obj.Y,
2237 o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X,
2238 o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y,
2239 12);
2240 end;
2241 end;
2243 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2244 begin
2245 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2246 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2247 begin
2248 g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y);
2249 if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO);
2250 ShotType := 0;
2251 Continue;
2252 end;
2254 // Âåëè÷èíà óðîíà:
2255 if (ShotType = WEAPON_PLASMA) and
2256 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
2257 a := 10
2258 else
2259 a := 5;
2261 if ShotType = WEAPON_BSP_FIRE then
2262 a := 10;
2264 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2265 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2266 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or
2267 (Timeout < 1) then
2268 begin
2269 if ShotType = WEAPON_PLASMA then
2270 s := 'FRAMES_EXPLODE_PLASMA'
2271 else
2272 s := 'FRAMES_EXPLODE_BSPFIRE';
2274 // Âçðûâ Ïëàçìû:
2275 if g_Frames_Get(TextureID, s) then
2276 begin
2277 Anim := TAnimation.Create(TextureID, False, 3);
2278 Anim.Blending := False;
2279 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2280 Anim.Free();
2281 g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5);
2282 end;
2284 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2286 ShotType := 0;
2287 end;
2288 end;
2290 WEAPON_FLAMETHROWER: // Îãíåìåò
2291 begin
2292 // Ñî âðåìåíåì óìèðàåò
2293 if (Timeout < 1) then
2294 begin
2295 ShotType := 0;
2296 Continue;
2297 end;
2298 // Ïîä âîäîé òîæå
2299 if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then
2300 begin
2301 if WordBool(st and MOVE_HITWATER) then
2302 begin
2303 if g_Frames_Get(_id, 'FRAMES_SMOKE') then
2304 begin
2305 Anim := TAnimation.Create(_id, False, 3);
2306 Anim.Alpha := 0;
2307 tcx := Random(8);
2308 tcy := Random(8);
2309 g_GFX_OnceAnim(cx-4+tcx-(Anim.Width div 2),
2310 cy-4+tcy-(Anim.Height div 2),
2311 Anim, ONCEANIM_SMOKE);
2312 Anim.Free();
2313 end;
2314 end
2315 else
2316 g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16);
2317 ShotType := 0;
2318 Continue;
2319 end;
2321 // Ãðàâèòàöèÿ
2322 if Stopped = 0 then
2323 Obj.Accel.Y := Obj.Accel.Y + 1;
2324 // Ïîïàëè â ñòåíó èëè â âîäó:
2325 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then
2326 begin
2327 // Ïðèëèïàåì:
2328 Obj.Vel.X := 0;
2329 Obj.Vel.Y := 0;
2330 Obj.Accel.Y := 0;
2331 if WordBool(st and MOVE_HITWALL) then
2332 Stopped := MOVE_HITWALL
2333 else if WordBool(st and MOVE_HITLAND) then
2334 Stopped := MOVE_HITLAND
2335 else if WordBool(st and MOVE_HITCEIL) then
2336 Stopped := MOVE_HITCEIL;
2337 end;
2339 a := IfThen(Stopped = 0, 3, 1);
2340 // Åñëè â êîãî-òî ïîïàëè
2341 if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then
2342 begin
2343 // HIT_FLAME ñàì ïîäîææåò
2344 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2345 if Stopped = 0 then
2346 ShotType := 0;
2347 end;
2349 if Stopped = 0 then
2350 tf := 2
2351 else
2352 tf := 3;
2354 if (gTime mod tf = 0) then
2355 begin
2356 Anim := TAnimation.Create(TextureID, False, 2 + Random(2));
2357 Anim.Alpha := 0;
2358 case Stopped of
2359 MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end;
2360 MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end;
2361 MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end;
2362 else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end;
2363 end;
2364 g_GFX_OnceAnim(tcx-(Anim.Width div 2), tcy-(Anim.Height div 2), Anim, ONCEANIM_SMOKE);
2365 Anim.Free();
2366 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2367 end;
2368 end;
2370 WEAPON_BFG: // BFG
2371 begin
2372 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2373 if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then
2374 begin
2375 g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y);
2376 if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO);
2377 ShotType := 0;
2378 Continue;
2379 end;
2381 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2382 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2383 (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or
2384 (Timeout < 1) then
2385 begin
2386 // Ëó÷è BFG:
2387 if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID);
2389 // Âçðûâ BFG:
2390 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
2391 begin
2392 Anim := TAnimation.Create(TextureID, False, 6);
2393 Anim.Blending := False;
2394 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2395 Anim.Free();
2396 g_DynLightExplosion(cx, cy, 96, 0, 1, 0);
2397 end;
2399 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2401 ShotType := 0;
2402 end;
2403 end;
2405 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2406 begin
2407 // Âûëåòåë èç âîäû:
2408 if WordBool(st and MOVE_HITAIR) then
2409 g_Obj_SetSpeed(@Obj, 16);
2411 // Âåëè÷èíà óðîíà:
2412 if ShotType = WEAPON_IMP_FIRE then
2413 a := 5
2414 else
2415 if ShotType = WEAPON_CACO_FIRE then
2416 a := 20
2417 else
2418 a := 40;
2420 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2421 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2422 (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or
2423 (Timeout < 1) then
2424 begin
2425 if ShotType = WEAPON_IMP_FIRE then
2426 s := 'FRAMES_EXPLODE_IMPFIRE'
2427 else
2428 if ShotType = WEAPON_CACO_FIRE then
2429 s := 'FRAMES_EXPLODE_CACOFIRE'
2430 else
2431 s := 'FRAMES_EXPLODE_BARONFIRE';
2433 // Âçðûâ:
2434 if g_Frames_Get(TextureID, s) then
2435 begin
2436 Anim := TAnimation.Create(TextureID, False, 6);
2437 Anim.Blending := False;
2438 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2439 Anim.Free();
2440 end;
2442 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2444 ShotType := 0;
2445 end;
2446 end;
2448 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2449 begin
2450 // Âûëåòåë èç âîäû:
2451 if WordBool(st and MOVE_HITAIR) then
2452 g_Obj_SetSpeed(@Obj, 16);
2454 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2455 if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or
2456 (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or
2457 (Timeout < 1) then
2458 begin
2459 // Âçðûâ:
2460 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2461 begin
2462 Anim := TAnimation.Create(TextureID, False, 6);
2463 Anim.Blending := False;
2464 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2465 Anim.Free();
2466 end;
2468 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2470 ShotType := 0;
2471 end;
2472 end;
2473 end; // case ShotType of...
2475 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2476 if (ShotType = 0) then
2477 begin
2478 if gGameSettings.GameType = GT_SERVER then
2479 MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud);
2480 if Animation <> nil then
2481 begin
2482 Animation.Free();
2483 Animation := nil;
2484 end;
2485 end
2486 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2487 if gGameSettings.GameType = GT_SERVER then
2488 MH_SEND_UpdateShot(i);
2489 end;
2490 end;
2491 end;
2493 procedure g_Weapon_Draw();
2494 var
2495 i: Integer;
2496 a: SmallInt;
2497 p: TPoint;
2498 begin
2499 if Shots = nil then
2500 Exit;
2502 for i := 0 to High(Shots) do
2503 if Shots[i].ShotType <> 0 then
2504 with Shots[i] do
2505 begin
2506 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2507 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2508 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2509 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2510 a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y)
2511 else
2512 a := 0;
2514 p.X := Obj.Rect.Width div 2;
2515 p.Y := Obj.Rect.Height div 2;
2517 if Animation <> nil then
2518 begin
2519 if (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2520 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2521 (Shots[i].ShotType = WEAPON_SKEL_FIRE) then
2522 Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a)
2523 else
2524 Animation.Draw(Obj.X, Obj.Y, M_NONE);
2525 end
2526 else if TextureID <> 0 then
2527 begin
2528 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then
2529 e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE)
2530 else if (Shots[i].ShotType <> WEAPON_FLAMETHROWER) then
2531 e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False);
2532 end;
2534 if g_debug_Frames then
2535 begin
2536 e_DrawQuad(Obj.X+Obj.Rect.X,
2537 Obj.Y+Obj.Rect.Y,
2538 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2539 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2540 0, 255, 0);
2541 end;
2542 end;
2543 end;
2545 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
2546 var
2547 a: Integer;
2548 begin
2549 Result := False;
2551 if Shots = nil then
2552 Exit;
2554 for a := 0 to High(Shots) do
2555 if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then
2556 if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or
2557 (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then
2558 if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and
2559 g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y,
2560 Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and
2561 g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then
2562 begin
2563 Result := True;
2564 Exit;
2565 end;
2566 end;
2568 procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
2569 var
2570 count, i, j: Integer;
2571 dw: DWORD;
2572 begin
2573 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2574 count := 0;
2575 if Shots <> nil then
2576 for i := 0 to High(Shots) do
2577 if Shots[i].ShotType <> 0 then
2578 count := count + 1;
2580 Mem := TBinMemoryWriter.Create((count+1) * 80);
2582 // Êîëè÷åñòâî ñíàðÿäîâ:
2583 Mem.WriteInt(count);
2585 if count = 0 then
2586 Exit;
2588 for i := 0 to High(Shots) do
2589 if Shots[i].ShotType <> 0 then
2590 begin
2591 // Ñèãíàòóðà ñíàðÿäà:
2592 dw := SHOT_SIGNATURE; // 'SHOT'
2593 Mem.WriteDWORD(dw);
2594 // Òèï ñíàðÿäà:
2595 Mem.WriteByte(Shots[i].ShotType);
2596 // Öåëü:
2597 Mem.WriteWord(Shots[i].Target);
2598 // UID ñòðåëÿâøåãî:
2599 Mem.WriteWord(Shots[i].SpawnerUID);
2600 // Ðàçìåð ïîëÿ Triggers:
2601 dw := Length(Shots[i].Triggers);
2602 Mem.WriteDWORD(dw);
2603 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2604 for j := 0 to Integer(dw)-1 do
2605 Mem.WriteDWORD(Shots[i].Triggers[j]);
2606 // Îáúåêò ñíàðÿäà:
2607 Obj_SaveState(@Shots[i].Obj, Mem);
2608 // Êîñòûëèíà åáàíàÿ:
2609 Mem.WriteByte(Shots[i].Stopped);
2610 end;
2611 end;
2613 procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
2614 var
2615 count, i, j: Integer;
2616 dw: DWORD;
2617 begin
2618 if Mem = nil then
2619 Exit;
2621 // Êîëè÷åñòâî ñíàðÿäîâ:
2622 Mem.ReadInt(count);
2624 SetLength(Shots, count);
2626 if count = 0 then
2627 Exit;
2629 for i := 0 to count-1 do
2630 begin
2631 // Ñèãíàòóðà ñíàðÿäà:
2632 Mem.ReadDWORD(dw);
2633 if dw <> SHOT_SIGNATURE then // 'SHOT'
2634 begin
2635 raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
2636 end;
2637 // Òèï ñíàðÿäà:
2638 Mem.ReadByte(Shots[i].ShotType);
2639 // Öåëü:
2640 Mem.ReadWord(Shots[i].Target);
2641 // UID ñòðåëÿâøåãî:
2642 Mem.ReadWord(Shots[i].SpawnerUID);
2643 // Ðàçìåð ïîëÿ Triggers:
2644 Mem.ReadDWORD(dw);
2645 SetLength(Shots[i].Triggers, dw);
2646 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2647 for j := 0 to Integer(dw)-1 do
2648 Mem.ReadDWORD(Shots[i].Triggers[j]);
2649 // Îáúåêò ïðåäìåòà:
2650 Obj_LoadState(@Shots[i].Obj, Mem);
2651 // Êîñòûëèíà åáàíàÿ:
2652 Mem.ReadByte(Shots[i].Stopped);
2654 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2655 Shots[i].TextureID := DWORD(-1);
2656 Shots[i].Animation := nil;
2658 case Shots[i].ShotType of
2659 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE:
2660 begin
2661 g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID);
2662 end;
2663 WEAPON_PLASMA:
2664 begin
2665 g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA');
2666 Shots[i].Animation := TAnimation.Create(dw, True, 5);
2667 end;
2668 WEAPON_BFG:
2669 begin
2670 g_Frames_Get(dw, 'FRAMES_WEAPON_BFG');
2671 Shots[i].Animation := TAnimation.Create(dw, True, 6);
2672 end;
2673 WEAPON_IMP_FIRE:
2674 begin
2675 g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE');
2676 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2677 end;
2678 WEAPON_BSP_FIRE:
2679 begin
2680 g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE');
2681 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2682 end;
2683 WEAPON_CACO_FIRE:
2684 begin
2685 g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE');
2686 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2687 end;
2688 WEAPON_BARON_FIRE:
2689 begin
2690 g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE');
2691 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2692 end;
2693 WEAPON_MANCUB_FIRE:
2694 begin
2695 g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE');
2696 Shots[i].Animation := TAnimation.Create(dw, True, 4);
2697 end;
2698 end;
2699 end;
2700 end;
2702 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
2703 var
2704 cx, cy: Integer;
2705 Anim: TAnimation;
2706 s: string;
2707 begin
2708 if Shots = nil then
2709 Exit;
2710 if (I > High(Shots)) or (I < 0) then Exit;
2712 with Shots[I] do
2713 begin
2714 if ShotType = 0 then Exit;
2715 Obj.X := X;
2716 Obj.Y := Y;
2717 cx := Obj.X + (Obj.Rect.Width div 2);
2718 cy := Obj.Y + (Obj.Rect.Height div 2);
2720 case ShotType of
2721 WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ðàêåòû è ñíàðÿäû Ñêåëåòà
2722 begin
2723 if Loud then
2724 begin
2725 if ShotType = WEAPON_SKEL_FIRE then
2726 begin // Âçðûâ ñíàðÿäà Ñêåëåòà
2727 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then
2728 begin
2729 Anim := TAnimation.Create(TextureID, False, 8);
2730 Anim.Blending := False;
2731 g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim);
2732 Anim.Free();
2733 end;
2734 end
2735 else
2736 begin // Âçðûâ Ðàêåòû
2737 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
2738 begin
2739 Anim := TAnimation.Create(TextureID, False, 6);
2740 Anim.Blending := False;
2741 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2742 Anim.Free();
2743 end;
2744 end;
2745 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y);
2746 end;
2747 end;
2749 WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà
2750 begin
2751 if ShotType = WEAPON_PLASMA then
2752 s := 'FRAMES_EXPLODE_PLASMA'
2753 else
2754 s := 'FRAMES_EXPLODE_BSPFIRE';
2756 if g_Frames_Get(TextureID, s) and loud then
2757 begin
2758 Anim := TAnimation.Create(TextureID, False, 3);
2759 Anim.Blending := False;
2760 g_GFX_OnceAnim(cx-16, cy-16, Anim);
2761 Anim.Free();
2763 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y);
2764 end;
2765 end;
2767 WEAPON_BFG: // BFG
2768 begin
2769 // Âçðûâ BFG:
2770 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then
2771 begin
2772 Anim := TAnimation.Create(TextureID, False, 6);
2773 Anim.Blending := False;
2774 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2775 Anim.Free();
2777 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y);
2778 end;
2779 end;
2781 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2782 begin
2783 if ShotType = WEAPON_IMP_FIRE then
2784 s := 'FRAMES_EXPLODE_IMPFIRE'
2785 else
2786 if ShotType = WEAPON_CACO_FIRE then
2787 s := 'FRAMES_EXPLODE_CACOFIRE'
2788 else
2789 s := 'FRAMES_EXPLODE_BARONFIRE';
2791 if g_Frames_Get(TextureID, s) and Loud then
2792 begin
2793 Anim := TAnimation.Create(TextureID, False, 6);
2794 Anim.Blending := False;
2795 g_GFX_OnceAnim(cx-32, cy-32, Anim);
2796 Anim.Free();
2798 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2799 end;
2800 end;
2802 WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà
2803 begin
2804 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then
2805 begin
2806 Anim := TAnimation.Create(TextureID, False, 6);
2807 Anim.Blending := False;
2808 g_GFX_OnceAnim(cx-64, cy-64, Anim);
2809 Anim.Free();
2811 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y);
2812 end;
2813 end;
2814 end; // case ShotType of...
2816 ShotType := 0;
2817 Animation.Free();
2818 end;
2819 end;
2822 procedure g_Weapon_AddDynLights();
2823 var
2824 i: Integer;
2825 begin
2826 if Shots = nil then Exit;
2827 for i := 0 to High(Shots) do
2828 begin
2829 if Shots[i].ShotType = 0 then continue;
2830 if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or
2831 (Shots[i].ShotType = WEAPON_BARON_FIRE) or
2832 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2833 (Shots[i].ShotType = WEAPON_SKEL_FIRE) or
2834 (Shots[i].ShotType = WEAPON_IMP_FIRE) or
2835 (Shots[i].ShotType = WEAPON_CACO_FIRE) or
2836 (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or
2837 (Shots[i].ShotType = WEAPON_BSP_FIRE) or
2838 (Shots[i].ShotType = WEAPON_PLASMA) or
2839 (Shots[i].ShotType = WEAPON_BFG) or
2840 (Shots[i].ShotType = WEAPON_FLAMETHROWER) or
2841 false then
2842 begin
2843 if (Shots[i].ShotType = WEAPON_PLASMA) then
2844 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)
2845 else if (Shots[i].ShotType = WEAPON_BFG) then
2846 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)
2847 else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then
2848 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)
2849 else
2850 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);
2851 end;
2852 end;
2853 end;
2856 procedure TShot.positionChanged (); begin end;
2859 end.