X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_weapons.pas;h=6e555a4d607d5ac83432f5ee9cc64809aae79125;hp=bcbf4c16352e7c35a3f173e0fddf781f51760538;hb=abda6900c041e39944de6a49aa088a60c170715e;hpb=5e6afeb0eca1550854b9d17269e707e984cb0f21 diff --git a/src/game/g_weapons.pas b/src/game/g_weapons.pas index bcbf4c1..6e555a4 100644 --- a/src/game/g_weapons.pas +++ b/src/game/g_weapons.pas @@ -1,9 +1,8 @@ -(* Copyright (C) DooM 2D:Forever Developers +(* Copyright (C) Doom 2D: Forever Developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,26 +12,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$MODE DELPHI} +{$INCLUDE ../shared/a_modes.inc} +{.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER} unit g_weapons; interface uses - g_textures, g_basic, e_graphics, g_phys, BinEditor; + SysUtils, Classes, mempool, + g_textures, g_basic, g_phys, xprofiler; -const - HIT_SOME = 0; - HIT_ROCKET = 1; - HIT_BFG = 2; - HIT_TRAP = 3; - HIT_FALL = 4; - HIT_WATER = 5; - HIT_ACID = 6; - HIT_ELECTRO = 7; - HIT_FLAME = 8; - HIT_SELF = 9; - HIT_DISCON = 10; type TShot = record @@ -41,11 +30,14 @@ type SpawnerUID: Word; Triggers: DWArray; Obj: TObj; - Animation: TAnimation; - TextureID: DWORD; + Animation: TAnimationState; Timeout: DWORD; + Stopped: Byte; + + procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right! end; + var Shots: array of TShot = nil; LastShotID: Integer = 0; @@ -58,11 +50,12 @@ function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorps function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean; function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord; -procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); +procedure g_Weapon_gun(const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word); function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer; procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False); +procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); @@ -78,13 +71,15 @@ procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boo function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean; procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); +procedure g_Weapon_PreUpdate(); procedure g_Weapon_Update(); -procedure g_Weapon_Draw(); function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean; procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True); -procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter); -procedure g_Weapon_LoadState(var Mem: TBinMemoryReader); +procedure g_Weapon_SaveState (st: TStream); +procedure g_Weapon_LoadState (st: TStream); + +procedure g_Weapon_AddDynLights(); const WEAPON_KASTET = 0; @@ -97,7 +92,7 @@ const WEAPON_PLASMA = 7; WEAPON_BFG = 8; WEAPON_SUPERPULEMET = 9; - WEAPON_MEGAKASTET = 10; + WEAPON_FLAMETHROWER = 10; WEAPON_ZOMBY_PISTOL = 20; WEAPON_IMP_FIRE = 21; WEAPON_BSP_FIRE = 22; @@ -106,13 +101,21 @@ const WEAPON_MANCUB_FIRE = 25; WEAPON_SKEL_FIRE = 26; + WP_FIRST = WEAPON_KASTET; + WP_LAST = WEAPON_FLAMETHROWER; + +var + gwep_debug_fast_trace: Boolean = true; + + implementation uses - Math, g_map, g_player, g_gfx, g_sound, g_main, - g_console, SysUtils, g_options, g_game, + Math, g_map, g_player, g_gfx, g_sound, g_panel, + g_console, g_options, g_game, r_gfx, g_triggers, MAPDEF, e_log, g_monsters, g_saveload, - g_language, g_netmsg; + g_language, g_netmsg, g_grid, + geom, binheap, hashtable, utils, xstreams; type TWaterPanel = record @@ -122,11 +125,11 @@ type end; const - SHOT_ROCKETLAUNCHER_WIDTH = 27; - SHOT_ROCKETLAUNCHER_HEIGHT = 12; + SHOT_ROCKETLAUNCHER_WIDTH = 14; + SHOT_ROCKETLAUNCHER_HEIGHT = 14; - SHOT_SKELFIRE_WIDTH = 32; - SHOT_SKELFIRE_HEIGHT = 16; + SHOT_SKELFIRE_WIDTH = 14; + SHOT_SKELFIRE_HEIGHT = 14; SHOT_PLASMA_WIDTH = 16; SHOT_PLASMA_HEIGHT = 16; @@ -136,10 +139,90 @@ const SHOT_BFG_DAMAGE = 100; SHOT_BFG_RADIUS = 256; + SHOT_FLAME_WIDTH = 4; + SHOT_FLAME_HEIGHT = 4; + SHOT_FLAME_LIFETIME = 180; + SHOT_SIGNATURE = $544F4853; // 'SHOT' +type + PHitTime = ^THitTime; + THitTime = record + distSq: Integer; + mon: TMonster; + plridx: Integer; // if mon=nil + x, y: Integer; + end; + + TBinHeapKeyHitTime = class + public + class function less (const a, b: Integer): Boolean; inline; + end; + + // indicies in `wgunHitTime` array + TBinaryHeapHitTimes = specialize TBinaryHeapBase; + var WaterMap: array of array of DWORD = nil; + //wgunMonHash: THashIntInt = nil; + wgunHitHeap: TBinaryHeapHitTimes = nil; + wgunHitTime: array of THitTime = nil; + wgunHitTimeUsed: Integer = 0; + + +class function TBinHeapKeyHitTime.less (const a, b: Integer): Boolean; +var + hta, htb: PHitTime; +begin + hta := @wgunHitTime[a]; + htb := @wgunHitTime[b]; + if (hta.distSq <> htb.distSq) then begin result := (hta.distSq < htb.distSq); exit; end; + if (hta.mon <> nil) then + begin + // a is monster + if (htb.mon = nil) then begin result := false; exit; end; // players first + result := (hta.mon.UID < htb.mon.UID); // why not? + end + else + begin + // a is player + if (htb.mon <> nil) then begin result := true; exit; end; // players first + result := (hta.plridx < htb.plridx); // why not? + end; +end; + + +procedure appendHitTimeMon (adistSq: Integer; amon: TMonster; ax, ay: Integer); +begin + if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128); + with wgunHitTime[wgunHitTimeUsed] do + begin + distSq := adistSq; + mon := amon; + plridx := -1; + x := ax; + y := ay; + end; + wgunHitHeap.insert(wgunHitTimeUsed); + Inc(wgunHitTimeUsed); +end; + + +procedure appendHitTimePlr (adistSq: Integer; aplridx: Integer; ax, ay: Integer); +begin + if (wgunHitTimeUsed = Length(wgunHitTime)) then SetLength(wgunHitTime, wgunHitTimeUsed+128); + with wgunHitTime[wgunHitTimeUsed] do + begin + distSq := adistSq; + mon := nil; + plridx := aplridx; + x := ax; + y := ay; + end; + wgunHitHeap.insert(wgunHitTimeUsed); + Inc(wgunHitTimeUsed); +end; + function FindShot(): DWORD; var @@ -229,68 +312,97 @@ begin WaterArray := nil; end; + +var + chkTrap_pl: array [0..256] of Integer; + chkTrap_mn: array [0..65535] of TMonster; + procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte); var - a, b, c, d, i1, i2: Integer; - pl, mn: WArray; + //a, b, c, d, i1, i2: Integer; + //chkTrap_pl, chkTrap_mn: WArray; + plaCount: Integer = 0; + mnaCount: Integer = 0; + frameId: DWord; + + { + function monsWaterCheck (mon: TMonster): Boolean; + begin + result := false; // don't stop + if mon.alive and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME + begin + i2 += 1; + chkTrap_mn[i2] := monidx; + end; + end; + } + + function monsWaterCheck (mon: TMonster): Boolean; + begin + result := false; // don't stop + if (mon.trapCheckFrameId <> frameId) then + begin + mon.trapCheckFrameId := frameId; + chkTrap_mn[mnaCount] := mon; + Inc(mnaCount); + end; + end; + +var + a, b, c, d, f: Integer; + pan: TPanel; begin if (gWater = nil) or (WaterMap = nil) then Exit; - i1 := -1; - i2 := -1; + frameId := g_Mons_getNewTrapFrameId(); - SetLength(pl, 1024); - SetLength(mn, 1024); - for d := 0 to 1023 do pl[d] := $FFFF; - for d := 0 to 1023 do mn[d] := $FFFF; + //i1 := -1; + //i2 := -1; + + //SetLength(chkTrap_pl, 1024); + //SetLength(chkTrap_mn, 1024); + //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF; + //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF; for a := 0 to High(WaterMap) do + begin for b := 0 to High(WaterMap[a]) do begin - if not g_Obj_Collide(gWater[WaterMap[a][b]].X, gWater[WaterMap[a][b]].Y, - gWater[WaterMap[a][b]].Width, gWater[WaterMap[a][b]].Height, - @Shots[ID].Obj) then Continue; + pan := gWater[WaterMap[a][b]]; + if not g_Obj_Collide(pan.X, pan.Y, pan.Width, pan.Height, @Shots[ID].Obj) then continue; for c := 0 to High(WaterMap[a]) do begin - if gPlayers <> nil then + pan := gWater[WaterMap[a][c]]; + for d := 0 to High(gPlayers) do begin - for d := 0 to High(gPlayers) do - if (gPlayers[d] <> nil) and (gPlayers[d].Live) then - if gPlayers[d].Collide(gWater[WaterMap[a][c]]) then - if not InWArray(d, pl) then - if i1 < 1023 then - begin - i1 := i1+1; - pl[i1] := d; - end; + if (gPlayers[d] <> nil) and (gPlayers[d].alive) then + begin + if gPlayers[d].Collide(pan) then + begin + f := 0; + while (f < plaCount) and (chkTrap_pl[f] <> d) do Inc(f); + if (f = plaCount) then + begin + chkTrap_pl[plaCount] := d; + Inc(plaCount); + if (plaCount = Length(chkTrap_pl)) then break; + end; + end; + end; end; - if gMonsters <> nil then - begin - for d := 0 to High(gMonsters) do - if (gMonsters[d] <> nil) and (gMonsters[d].Live) then - if gMonsters[d].Collide(gWater[WaterMap[a][c]]) then - if not InWArray(d, mn) then - if i2 < 1023 then - begin - i2 := i2+1; - mn[i2] := d; - end; - end; + //g_Mons_ForEach(monsWaterCheck); + g_Mons_ForEachAliveAt(pan.X, pan.Y, pan.Width, pan.Height, monsWaterCheck); end; - if i1 <> -1 then - for d := 0 to i1 do - gPlayers[pl[d]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t); - - if i2 <> -1 then - for d := 0 to i2 do - gMonsters[mn[d]].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t); + for f := 0 to plaCount-1 do gPlayers[chkTrap_pl[f]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t); + for f := 0 to mnaCount-1 do chkTrap_mn[f].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t); end; + end; - pl := nil; - mn := nil; + //chkTrap_pl := nil; + //chkTrap_mn := nil; end; function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean; @@ -303,9 +415,9 @@ begin tt := g_GetUIDType(SpawnerUID); if tt = UID_MONSTER then begin - mon := g_Monsters_Get(SpawnerUID); + mon := g_Monsters_ByUID(SpawnerUID); if mon <> nil then - mt := g_Monsters_Get(SpawnerUID).MonsterType + mt := g_Monsters_ByUID(SpawnerUID).MonsterType else mt := 0; end @@ -343,58 +455,56 @@ begin end; if g_Game_IsServer then - Result := m.Damage(d, vx, vy, SpawnerUID, t) + begin + if (t <> HIT_FLAME) or (m.FFireTime = 0) or (vx <> 0) or (vy <> 0) then + Result := m.Damage(d, vx, vy, SpawnerUID, t) + else + Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time + if t = HIT_FLAME then + m.CatchFire(SpawnerUID); + end else - Result := True; + Result := (gLMSRespawn = LMS_RESPAWN_NONE); // don't hit monsters when it's warmup time end; -function HitPlayer(p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean; + +function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean; begin - Result := False; + result := False; -// Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì: - if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then - Exit; + // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì + if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then exit; - if g_Game_IsServer then p.Damage(d, SpawnerUID, vx, vy, t); + if g_Game_IsServer then + begin + if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t); + if (t = HIT_FLAME) then p.CatchFire(SpawnerUID); + end; - Result := True; + result := true; end; -function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer; - SpawnerUID: Word; AllowPush: Boolean): Byte; -var - i, h: Integer; -begin - Result := 0; - - h := High(gPlayers); - - if h <> -1 then - for i := 0 to h do - if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then - if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then - begin - if AllowPush then gPlayers[i].Push(vx, vy); - Result := 1; - end; - - if Result <> 0 then Exit; - h := High(gMonsters); +procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); - if h <> -1 then - for i := 0 to h do - if (gMonsters[i] <> nil) and gMonsters[i].Live and gMonsters[i].Collide(X, Y) then - if HitMonster(gMonsters[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then + function monsCheck (mon: TMonster): Boolean; + begin + result := false; // don't stop + if (mon.alive) and (mon.UID <> SpawnerUID) then + begin + with mon do + begin + if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), + Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and + g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), + Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then begin - if AllowPush then gMonsters[i].Push(vx, vy); - Result := 2; - Exit; + if HitMonster(mon, 50, 0, 0, SpawnerUID, HIT_SOME) then mon.BFGHit(); end; -end; + end; + end; + end; -procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); var i, h: Integer; st: Byte; @@ -414,7 +524,7 @@ begin g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then begin - Damage(50, 0, 0); + Damage(50, SpawnerUID, 0, 0); g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)); end; @@ -428,7 +538,7 @@ begin if h <> -1 then for i := 0 to h do - if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then + if (gPlayers[i] <> nil) and (gPlayers[i].alive) and (gPlayers[i].UID <> SpawnerUID) then with gPlayers[i] do if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2), GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and @@ -443,22 +553,13 @@ begin gPlayers[i].BFGHit(); end; - h := High(gMonsters); - - if h <> -1 then - for i := 0 to h do - if (gMonsters[i] <> nil) and (gMonsters[i].Live) and (gMonsters[i].UID <> SpawnerUID) then - with gMonsters[i] do - if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), - Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and - g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), - Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then - if HitMonster(gMonsters[i], 50, 0, 0, SpawnerUID, HIT_SOME) then gMonsters[i].BFGHit(); + //FIXME + g_Mons_ForEachAlive(monsCheck); end; function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord; var - find_id, FramesID: DWORD; + find_id: DWord; begin if I < 0 then find_id := FindShot() @@ -482,7 +583,6 @@ begin Animation := nil; Triggers := nil; ShotType := WEAPON_ROCKETLAUNCHER; - g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID); end; end; @@ -497,8 +597,7 @@ begin Triggers := nil; ShotType := WEAPON_PLASMA; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA'); - Animation := TAnimation.Create(FramesID, True, 5); + Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table end; end; @@ -513,8 +612,22 @@ begin Triggers := nil; ShotType := WEAPON_BFG; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG'); - Animation := TAnimation.Create(FramesID, True, 6); + Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table + end; + end; + + WEAPON_FLAMETHROWER: + begin + with Shots[find_id] do + begin + g_Obj_Init(@Obj); + + Obj.Rect.Width := SHOT_FLAME_WIDTH; + Obj.Rect.Height := SHOT_FLAME_HEIGHT; + + Triggers := nil; + ShotType := WEAPON_FLAMETHROWER; + // Animation := TAnimationState.Create(True, 6, 0); // drawed as gfx end; end; @@ -529,8 +642,7 @@ begin Triggers := nil; ShotType := WEAPON_IMP_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; @@ -545,8 +657,7 @@ begin Triggers := nil; ShotType := WEAPON_CACO_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; @@ -561,8 +672,7 @@ begin Triggers := nil; ShotType := WEAPON_MANCUB_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; @@ -577,8 +687,7 @@ begin Triggers := nil; ShotType := WEAPON_BARON_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; @@ -593,8 +702,7 @@ begin Triggers := nil; ShotType := WEAPON_BSP_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; @@ -610,12 +718,13 @@ begin Triggers := nil; ShotType := WEAPON_SKEL_FIRE; target := TargetUID; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE'); - Animation := TAnimation.Create(FramesID, True, 5); + Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table end; end; end; + Shots[find_id].Obj.oldX := X; + Shots[find_id].Obj.oldY := Y; Shots[find_id].Obj.X := X; Shots[find_id].Obj.Y := Y; Shots[find_id].Obj.Vel.X := XV; @@ -623,6 +732,10 @@ begin Shots[find_id].Obj.Accel.X := 0; Shots[find_id].Obj.Accel.Y := 0; Shots[find_id].SpawnerUID := Spawner; + if (ShotType = WEAPON_FLAMETHROWER) and (XV = 0) and (YV = 0) then + Shots[find_id].Stopped := 255 + else + Shots[find_id].Stopped := 0; Result := find_id; end; @@ -637,16 +750,24 @@ begin if a = 0 then a := 1; + Shots[i].Obj.oldX := x; + Shots[i].Obj.oldY := y; Shots[i].Obj.X := x; Shots[i].Obj.Y := y; Shots[i].Obj.Vel.X := (xd*s) div a; Shots[i].Obj.Vel.Y := (yd*s) div a; Shots[i].Obj.Accel.X := 0; Shots[i].Obj.Accel.Y := 0; + Shots[i].Stopped := 0; if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then Shots[i].Timeout := 900 // ~25 sec else - Shots[i].Timeout := 550 // ~15 sec + begin + if Shots[i].ShotType = WEAPON_FLAMETHROWER then + Shots[i].Timeout := SHOT_FLAME_LIFETIME + else + Shots[i].Timeout := 550; // ~15 sec + end; end; function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte; @@ -664,7 +785,7 @@ var if h <> -1 then for i := 0 to h do - if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then + if (gPlayers[i] <> nil) and gPlayers[i].alive and g_Obj_Collide(obj, @gPlayers[i].Obj) then begin ChkTeam := True; if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then @@ -676,8 +797,9 @@ var if ChkTeam then if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then begin - gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, - (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); + if t <> HIT_FLAME then + gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, + (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); if t = HIT_BFG then g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID); Result := True; @@ -685,24 +807,47 @@ var end; end; end; - function MonsterHit(): Boolean; - var - i: Integer; - begin - Result := False; - h := High(gMonsters); - if h <> -1 then - for i := 0 to h do - if (gMonsters[i] <> nil) and gMonsters[i].Live and g_Obj_Collide(obj, @gMonsters[i].Obj) then - if HitMonster(gMonsters[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then - begin - gMonsters[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, + { + function monsCheckHit (monidx: Integer; mon: TMonster): Boolean; + begin + result := false; // don't stop + if mon.alive and g_Obj_Collide(obj, @mon.Obj) then + begin + if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then + begin + if (t <> HIT_FLAME) then + begin + mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); - Result := True; - break; - end; + end; + result := True; + end; + end; + end; + } + + function monsCheckHit (mon: TMonster): Boolean; + begin + result := false; // don't stop + if HitMonster(mon, d, obj.Vel.X, obj.Vel.Y, SpawnerUID, t) then + begin + if (t <> HIT_FLAME) then + begin + mon.Push((obj.Vel.X+obj.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, + (obj.Vel.Y+obj.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); + end; + result := true; + end; + end; + + function MonsterHit(): Boolean; + begin + //result := g_Mons_ForEach(monsCheckHit); + //FIXME: accelerate this! + result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit); end; + begin Result := 0; @@ -716,8 +861,8 @@ begin g_Obj_Collide(obj, @gCorpses[i].Obj) then begin // Ðàñïèëèâàåì òðóï: - gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4, - (obj^.Vel.Y+obj^.Accel.Y) div 4); + gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4, + (obj^.Vel.Y+obj^.Accel.Y) div 4); Result := 1; end; end; @@ -733,10 +878,16 @@ begin Exit; end; - if PlayerHit() then + // È â êîíöå èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî + // (èëè ñíàðÿä îò ìîíñòðà, èëè friendlyfire, èëè friendly_hit_projectile) + if (g_GetUIDType(SpawnerUID) <> UID_PLAYER) or + LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then begin - Result := 1; - Exit; + if PlayerHit() then + begin + Result := 1; + Exit; + end; end; end; @@ -774,11 +925,15 @@ begin Exit; end; - // È â êîíöå ñâîèõ èãðîêîâ - if PlayerHit(1) then + // È â êîíöå ñâîèõ èãðîêîâ, íî òîëüêî åñëè ïîëîæåíî + // (èëè friendlyfire, èëè friendly_hit_projectile) + if LongBool(gGameSettings.Options and (GAME_OPTION_TEAMDAMAGE or GAME_OPTION_TEAMHITPROJECTILE)) then begin - Result := 1; - Exit; + if PlayerHit(1) then + begin + Result := 1; + Exit; + end; end; end; @@ -791,17 +946,50 @@ begin case g_GetUIDType(UID) of UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t); - UID_MONSTER: Result := HitMonster(g_Monsters_Get(UID), d, 0, 0, SpawnerUID, t); + UID_MONSTER: Result := HitMonster(g_Monsters_ByUID(UID), d, 0, 0, SpawnerUID, t); else Exit; end; end; function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean; var - i, h, r, dx, dy, m, mm: Integer; + r: Integer; // squared radius + + function monsExCheck (mon: TMonster): Boolean; + var + dx, dy, mm: Integer; + begin + result := false; // don't stop + begin + dx := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-X; + dy := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-Y; + + if dx > 1000 then dx := 1000; + if dy > 1000 then dy := 1000; + + if (dx*dx+dy*dy < r) then + begin + //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height); + //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY); + + mm := Max(abs(dx), abs(dy)); + if mm = 0 then mm := 1; + + if mon.alive then + begin + HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET); + end; + + mon.Push((dx*7) div mm, (dy*7) div mm); + end; + end; + end; + +var + i, h, dx, dy, m, mm: Integer; _angle: SmallInt; begin - Result := False; + result := false; g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT); @@ -811,7 +999,7 @@ begin if h <> -1 then for i := 0 to h do - if (gPlayers[i] <> nil) and gPlayers[i].Live then + if (gPlayers[i] <> nil) and gPlayers[i].alive then with gPlayers[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; @@ -833,34 +1021,8 @@ begin end; end; - h := High(gMonsters); - - if h <> -1 then - for i := 0 to h do - if gMonsters[i] <> nil then - with gMonsters[i] do - begin - dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; - dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y; - - if dx > 1000 then dx := 1000; - if dy > 1000 then dy := 1000; - - if dx*dx+dy*dy < r then - begin - //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, - // Obj.Rect.Width, Obj.Rect.Height); - - mm := Max(abs(dx), abs(dy)); - if mm = 0 then mm := 1; - - if gMonsters[i].Live then - HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad, - 0, 0, SpawnerUID, HIT_ROCKET); - - gMonsters[i].Push((dx*7) div mm, (dy*7) div mm); - end; - end; + //g_Mons_ForEach(monsExCheck); + g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck); h := High(gCorpses); @@ -883,7 +1045,7 @@ begin mm := Max(abs(dx), abs(dy)); if mm = 0 then mm := 1; - Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm); + Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm); end; end; @@ -891,7 +1053,7 @@ begin if gAdvGibs and (h <> -1) then for i := 0 to h do - if gGibs[i].Live then + if gGibs[i].alive then with gGibs[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; @@ -908,6 +1070,7 @@ begin Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y); g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle); + positionChanged(); // this updates spatial accelerators end; end; end; @@ -935,7 +1098,7 @@ end; procedure g_Weapon_LoadData(); begin - e_WriteLog('Loading weapons data...', MSG_NOTIFY); + e_WriteLog('Loading weapons data...', TMsgType.Notify); g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH'); g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH'); @@ -953,6 +1116,7 @@ begin g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG'); g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE'); + g_Sound_CreateWADEx('SOUND_IGNITE', GameWAD+':SOUNDS\IGNITE'); g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG'); @@ -962,6 +1126,9 @@ begin g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV'); + g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEON', GameWAD+':SOUNDS\STARTFLM'); + g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEOFF', GameWAD+':SOUNDS\STOPFLM'); + g_Sound_CreateWADEx('SOUND_WEAPON_FLAMEWORK', GameWAD+':SOUNDS\WORKFLM'); g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK'); g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK'); g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK'); @@ -970,34 +1137,13 @@ begin g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1'); g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2'); - g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET'); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2); - g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3); - g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4); - g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3); - g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3); - g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False); - - g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET'); - g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL'); + //wgunMonHash := hashNewIntInt(); + wgunHitHeap := TBinaryHeapHitTimes.Create(); end; procedure g_Weapon_FreeData(); begin - e_WriteLog('Releasing weapons data...', MSG_NOTIFY); + e_WriteLog('Releasing weapons data...', TMsgType.Notify); g_Sound_Delete('SOUND_WEAPON_HITPUNCH'); g_Sound_Delete('SOUND_WEAPON_MISSPUNCH'); @@ -1015,6 +1161,7 @@ begin g_Sound_Delete('SOUND_WEAPON_FIRECGUN'); g_Sound_Delete('SOUND_WEAPON_FIREBFG'); g_Sound_Delete('SOUND_FIRE'); + g_Sound_Delete('SOUND_IGNITE'); g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG'); g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET'); g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG'); @@ -1024,6 +1171,9 @@ begin g_Sound_Delete('SOUND_WEAPON_FIREBALL'); g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL'); g_Sound_Delete('SOUND_WEAPON_FIREREV'); + g_Sound_Delete('SOUND_WEAPON_FLAMEON'); + g_Sound_Delete('SOUND_WEAPON_FLAMEOFF'); + g_Sound_Delete('SOUND_WEAPON_FLAMEWORK'); g_Sound_Delete('SOUND_PLAYER_JETFLY'); g_Sound_Delete('SOUND_PLAYER_JETON'); g_Sound_Delete('SOUND_PLAYER_JETOFF'); @@ -1031,28 +1181,49 @@ begin g_Sound_Delete('SOUND_PLAYER_CASING2'); g_Sound_Delete('SOUND_PLAYER_SHELL1'); g_Sound_Delete('SOUND_PLAYER_SHELL2'); +end; + + +function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean; +var + i: Integer; +begin + result := false; + for i := 0 to High(gPlayers) do + begin + if (gPlayers[i] <> nil) and gPlayers[i].alive and gPlayers[i].Collide(X, Y) then + begin + if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then + begin + if AllowPush then gPlayers[i].Push(vx, vy); + result := true; + end; + end; + end; +end; + + +function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte; + + function monsCheck (mon: TMonster): Boolean; + begin + result := false; // don't stop + if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then + begin + if AllowPush then mon.Push(vx, vy); + result := true; + end; + end; - g_Texture_Delete('TEXTURE_WEAPON_ROCKET'); - g_Frames_DeleteByName('FRAMES_WEAPON_BFG'); - g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA'); - g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE'); - g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE'); - g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE'); - g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE'); - g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET'); - g_Frames_DeleteByName('FRAMES_EXPLODE_BFG'); - g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE'); - g_Frames_DeleteByName('FRAMES_BFGHIT'); - g_Frames_DeleteByName('FRAMES_FIRE'); - g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA'); - g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE'); - g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE'); - g_Frames_DeleteByName('FRAMES_SMOKE'); - g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE'); - g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE'); +begin + result := 0; + if GunHitPlayer(X, Y, vx, vy, dmg, SpawnerUID, AllowPush) then result := 1 + else if g_Mons_ForEachAliveAt(X, Y, 1, 1, monsCheck) then result := 2; end; -procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); + +(* +procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); var a: Integer; x2, y2: Integer; @@ -1062,10 +1233,13 @@ var s, c: Extended; //vx, vy: Integer; xx, yy, d: Integer; - i: Integer; t1, _collide: Boolean; w, h: Word; + {$IF DEFINED(D2F_DEBUG)} + stt: UInt64; + showTime: Boolean = true; + {$ENDIF} begin a := GetAngle(x, y, xd, yd)+180; @@ -1101,6 +1275,10 @@ begin //vx := (dx*10 div d)*xi; //vy := (dy*10 div d)*yi; + {$IF DEFINED(D2F_DEBUG)} + stt := getTimeMicro(); + {$ENDIF} + xx := x; yy := y; @@ -1128,46 +1306,284 @@ begin if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then begin _collide := True; + {$IF DEFINED(D2F_DEBUG)} + stt := getTimeMicro()-stt; + e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY); + showTime := false; + {$ENDIF} g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0); if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK); end; if not _collide then + begin _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0; + end; + + if _collide then Break; + end; - if _collide then - Break; + {$IF DEFINED(D2F_DEBUG)} + if showTime then + begin + stt := getTimeMicro()-stt; + e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY); end; + {$ENDIF} if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT); end; +*) -procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word); + +//!!!FIXME!!! +procedure g_Weapon_gun (const x, y, xd, yd, v, indmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); var - obj: TObj; -begin - obj.X := X; - obj.Y := Y; - obj.rect.X := 0; - obj.rect.Y := 0; - obj.rect.Width := 39; - obj.rect.Height := 52; - obj.Vel.X := 0; - obj.Vel.Y := 0; - obj.Accel.X := 0; - obj.Accel.Y := 0; + x0, y0: Integer; + x2, y2: Integer; + xi, yi: Integer; + wallDistSq: Integer = $3fffffff; + spawnerPlr: TPlayer = nil; + dmg: Integer; - if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then - g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y) - else - g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y); -end; + function doPlayerHit (idx: Integer; hx, hy: Integer): Boolean; + begin + result := false; + if (idx < 0) or (idx > High(gPlayers)) then exit; + if (gPlayers[idx] = nil) or not gPlayers[idx].alive then exit; + if (spawnerPlr <> nil) then + begin + if ((gGameSettings.Options and (GAME_OPTION_TEAMHITTRACE or GAME_OPTION_TEAMDAMAGE)) = 0) and + (spawnerPlr.Team <> TEAM_NONE) and (spawnerPlr.Team = gPlayers[idx].Team) then + begin + if (spawnerPlr <> gPlayers[idx]) and ((gGameSettings.Options and GAME_OPTION_TEAMABSORBDAMAGE) = 0) then + dmg := Max(1, dmg div 2); + exit; + end; + end; + result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME); + if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v)); + {$IF DEFINED(D2F_DEBUG)} + //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY); + {$ENDIF} + end; -function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer; -var - obj: TObj; + function doMonsterHit (mon: TMonster; hx, hy: Integer): Boolean; + begin + result := false; + if (mon = nil) then exit; + result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME); + if result and (v <> 0) then mon.Push((xi*v), (yi*v)); + {$IF DEFINED(D2F_DEBUG)} + //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY); + {$ENDIF} + end; + + // collect players along hitray + // return `true` if instant hit was detected + function playerPossibleHit (): Boolean; + var + i: Integer; + px, py, pw, ph: Integer; + inx, iny: Integer; + distSq: Integer; + plr: TPlayer; + begin + result := false; + for i := 0 to High(gPlayers) do + begin + plr := gPlayers[i]; + if (plr <> nil) and plr.alive then + begin + plr.getMapBox(px, py, pw, ph); + if lineAABBIntersects(x, y, x2, y2, px, py, pw, ph, inx, iny) then + begin + distSq := distanceSq(x, y, inx, iny); + if (distSq = 0) then + begin + // contains + if doPlayerHit(i, x, y) then begin result := true; exit; end; + end + else if (distSq < wallDistSq) then + begin + appendHitTimePlr(distSq, i, inx, iny); + end; + end; + end; + end; + end; + + procedure sqchecker (mon: TMonster); + var + mx, my, mw, mh: Integer; + inx, iny: Integer; + distSq: Integer; + begin + mon.getMapBox(mx, my, mw, mh); + if lineAABBIntersects(x0, y0, x2, y2, mx, my, mw, mh, inx, iny) then + begin + distSq := distanceSq(x0, y0, inx, iny); + if (distSq < wallDistSq) then appendHitTimeMon(distSq, mon, inx, iny); + end; + end; + +var + a: Integer; + dx, dy: Integer; + xe, ye: Integer; + s, c: Extended; + i: Integer; + wallHitFlag: Boolean = false; + wallHitX: Integer = 0; + wallHitY: Integer = 0; + didHit: Boolean = false; + {$IF DEFINED(D2F_DEBUG)} + stt: UInt64; + {$ENDIF} + mit: PMonster; + it: TMonsterGrid.Iter; +begin + (* + if not gwep_debug_fast_trace then + begin + g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger); + exit; + end; + *) + + if (xd = 0) and (yd = 0) then exit; + + if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then + spawnerPlr := g_Player_Get(SpawnerUID); + + dmg := indmg; + + //wgunMonHash.reset(); //FIXME: clear hash on level change + wgunHitHeap.clear(); + wgunHitTimeUsed := 0; + + a := GetAngle(x, y, xd, yd)+180; + + SinCos(DegToRad(-a), s, c); + + if Abs(s) < 0.01 then s := 0; + if Abs(c) < 0.01 then c := 0; + + x0 := x; + y0 := y; + x2 := x+Round(c*gMapInfo.Width); + y2 := y+Round(s*gMapInfo.Width); + + dx := x2-x; + dy := y2-y; + + if (dx > 0) then xi := 1 else if (dx < 0) then xi := -1 else xi := 0; + if (dy > 0) then yi := 1 else if (dy < 0) then yi := -1 else yi := 0; + + {$IF DEFINED(D2F_DEBUG)} + e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), TMsgType.Notify); + stt := getTimeMicro(); + {$ENDIF} + + wallHitFlag := (g_Map_traceToNearestWall(x, y, x2, y2, @wallHitX, @wallHitY) <> nil); + if wallHitFlag then + begin + x2 := wallHitX; + y2 := wallHitY; + wallDistSq := distanceSq(x, y, wallHitX, wallHitY); + end + else + begin + wallHitX := x2; + wallHitY := y2; + end; + + if playerPossibleHit() then exit; // instant hit + + // collect monsters + //g_Mons_AlongLine(x, y, x2, y2, sqchecker); + + it := monsGrid.forEachAlongLine(x, y, x2, y2, -1); + for mit in it do sqchecker(mit^); + it.release(); + + // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime` + // also, if `wallWasHit` is `true`, then `wallHitX` and `wallHitY` contains spark coords + while (wgunHitHeap.count > 0) do + begin + // has some entities to check, do it + i := wgunHitHeap.front; + wgunHitHeap.popFront(); + // hitpoint + xe := wgunHitTime[i].x; + ye := wgunHitTime[i].y; + // check if it is not behind the wall + if (wgunHitTime[i].mon <> nil) then + begin + didHit := doMonsterHit(wgunHitTime[i].mon, xe, ye); + end + else + begin + didHit := doPlayerHit(wgunHitTime[i].plridx, xe, ye); + end; + if didHit then + begin + // need new coords for trigger + wallHitX := xe; + wallHitY := ye; + wallHitFlag := false; // no sparks + break; + end; + end; + + // need sparks? + if wallHitFlag then + begin + {$IF DEFINED(D2F_DEBUG)} + stt := getTimeMicro()-stt; + e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify); + {$ENDIF} + g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0); + if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK); + end + else + begin + {$IF DEFINED(D2F_DEBUG)} + stt := getTimeMicro()-stt; + e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), TMsgType.Notify); + {$ENDIF} + end; + + if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT); +end; + + +procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word); +var + obj: TObj; +begin + obj.X := X; + obj.Y := Y; + obj.rect.X := 0; + obj.rect.Y := 0; + obj.rect.Width := 39; + obj.rect.Height := 52; + obj.Vel.X := 0; + obj.Vel.Y := 0; + obj.Accel.X := 0; + obj.Accel.Y := 0; + + if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then + g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y) + else + g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y); +end; + +function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer; +var + obj: TObj; begin obj.X := X; obj.Y := Y; @@ -1205,14 +1621,14 @@ begin Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH; Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT; - dx := IfThen(xd>x, -Obj.Rect.Width, 0); + dx := IfThen(xd > x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_ROCKETLAUNCHER; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12); Animation := nil; triggers := nil; - ShotType := WEAPON_ROCKETLAUNCHER; - g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID); end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1224,7 +1640,7 @@ end; procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1243,15 +1659,15 @@ begin Obj.Rect.Width := SHOT_SKELFIRE_WIDTH; Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT; - dx := IfThen(xd>x, -Obj.Rect.Width, 0); + dx := -(Obj.Rect.Width div 2); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_SKEL_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12); triggers := nil; - ShotType := WEAPON_SKEL_FIRE; target := TargetUID; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE'); - Animation := TAnimation.Create(FramesID, True, 5); + Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1263,7 +1679,7 @@ end; procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1284,12 +1700,12 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_PLASMA; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_PLASMA; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA'); - Animation := TAnimation.Create(FramesID, True, 5); + Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1298,10 +1714,48 @@ begin g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y); end; +procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; + Silent: Boolean = False); +var + find_id: DWORD; + dx, dy: Integer; +begin + if WID < 0 then + find_id := FindShot() + else + begin + find_id := WID; + if Integer(find_id) >= High(Shots) then + SetLength(Shots, find_id + 64); + end; + + with Shots[find_id] do + begin + g_Obj_Init(@Obj); + + Obj.Rect.Width := SHOT_FLAME_WIDTH; + Obj.Rect.Height := SHOT_FLAME_HEIGHT; + + dx := IfThen(xd>x, -Obj.Rect.Width, 0); + dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_FLAMETHROWER; + throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); + + triggers := nil; + Animation := nil; + end; + + Shots[find_id].SpawnerUID := SpawnerUID; + + // if not Silent then + // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y); +end; + procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1322,12 +1776,12 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_IMP_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_IMP_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1339,7 +1793,7 @@ end; procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1360,12 +1814,12 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_CACO_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_CACO_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1377,7 +1831,7 @@ end; procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1398,12 +1852,12 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_BARON_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_BARON_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1415,7 +1869,7 @@ end; procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1436,12 +1890,13 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_BSP_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_BSP_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1453,7 +1908,7 @@ end; procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1474,12 +1929,13 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_MANCUB_FIRE; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_MANCUB_FIRE; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE'); - Animation := TAnimation.Create(FramesID, True, 4); + + Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1491,7 +1947,7 @@ end; procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var - find_id, FramesID: DWORD; + find_id: DWORD; dx, dy: Integer; begin if WID < 0 then @@ -1512,12 +1968,12 @@ begin dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); + + ShotType := WEAPON_BFG; throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - ShotType := WEAPON_BFG; - g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG'); - Animation := TAnimation.Create(FramesID, True, 6); + Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table end; Shots[find_id].SpawnerUID := SpawnerUID; @@ -1527,16 +1983,8 @@ begin end; procedure g_Weapon_bfghit(x, y: Integer); -var - ID: DWORD; - Anim: TAnimation; begin - if g_Frames_Get(ID, 'FRAMES_BFGHIT') then - begin - Anim := TAnimation.Create(ID, False, 4); - g_GFX_OnceAnim(x-32, y-32, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_BFG_HIT, x - 32, y - 32); end; procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; @@ -1599,17 +2047,28 @@ begin end; end; +procedure g_Weapon_PreUpdate(); +var + i: Integer; +begin + if Shots = nil then Exit; + for i := 0 to High(Shots) do + if Shots[i].ShotType <> 0 then + begin + Shots[i].Obj.oldX := Shots[i].Obj.X; + Shots[i].Obj.oldY := Shots[i].Obj.Y; + end; +end; + procedure g_Weapon_Update(); var - i, a, h, cx, cy, oldvx, oldvy: Integer; - _id: DWORD; - Anim: TAnimation; + i, a, h, cx, cy, oldvx, oldvy, tf: Integer; t: DWArray; st: Word; - s: String; o: TObj; spl: Boolean; Loud: Boolean; + tcx, tcy: Integer; begin if Shots = nil then Exit; @@ -1627,7 +2086,7 @@ begin oldvx := Obj.Vel.X; oldvy := Obj.Vel.Y; // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ): - if g_Game_IsServer then + if (Stopped = 0) and g_Game_IsServer then t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, SpawnerUID, ACTIVATE_SHOT, triggers) else @@ -1658,9 +2117,18 @@ begin // Äâèæåíèå: spl := (ShotType <> WEAPON_PLASMA) and (ShotType <> WEAPON_BFG) and - (ShotType <> WEAPON_BSP_FIRE); + (ShotType <> WEAPON_BSP_FIRE) and + (ShotType <> WEAPON_FLAMETHROWER); - st := g_Obj_Move(@Obj, False, spl); + if Stopped = 0 then + begin + st := g_Obj_Move_Projectile(@Obj, False, spl); + end + else + begin + st := 0; + end; + positionChanged(); // this updates spatial accelerators if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then @@ -1687,15 +2155,7 @@ begin Obj.Y+(Obj.Rect.Height div 2), 1+Random(3), 16, 16) else - if g_Frames_Get(_id, 'FRAMES_SMOKE') then - begin - Anim := TAnimation.Create(_id, False, 3); - Anim.Alpha := 150; - g_GFX_OnceAnim(Obj.X-8+Random(9), - Obj.Y+(Obj.Rect.Height div 2)-20+Random(9), - Anim, ONCEANIM_SMOKE); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_SMOKE_TRANS, Obj.X-14+Random(9), Obj.Y+(Obj.Rect.Height div 2)-20+Random(9)); // Ïîïàëè â êîãî-òî èëè â ñòåíó: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or @@ -1709,23 +2169,13 @@ begin if ShotType = WEAPON_SKEL_FIRE then begin // Âçðûâ ñíàðÿäà Ñêåëåòà - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then - begin - Anim := TAnimation.Create(TextureID, False, 8); - Anim.Blending := False; - g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_SKELFIRE, Obj.X + 32 - 58, Obj.Y + 8 - 36); + g_DynLightExplosion((Obj.X+32), (Obj.Y+8), 64, 1, 0, 0); end else begin // Âçðûâ Ðàêåòû - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64); + g_DynLightExplosion(cx, cy, 64, 1, 0, 0); end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y); @@ -1770,22 +2220,81 @@ begin (Timeout < 1) then begin if ShotType = WEAPON_PLASMA then - s := 'FRAMES_EXPLODE_PLASMA' + r_GFX_OnceAnim(R_GFX_EXPLODE_PLASMA, cx - 16, cy - 16) else - s := 'FRAMES_EXPLODE_BSPFIRE'; + r_GFX_OnceAnim(R_GFX_EXPLODE_BSPFIRE, cx - 16, cy - 16); + g_DynLightExplosion(cx, cy, 32, 0, 0.5, 0.5); + g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y); + ShotType := 0; + end; + end; - // Âçðûâ Ïëàçìû: - if g_Frames_Get(TextureID, s) then + WEAPON_FLAMETHROWER: // Îãíåìåò + begin + // Ñî âðåìåíåì óìèðàåò + if (Timeout < 1) then + begin + ShotType := 0; + Continue; + end; + // Ïîä âîäîé òîæå + if WordBool(st and (MOVE_HITWATER or MOVE_INWATER)) then + begin + if WordBool(st and MOVE_HITWATER) then begin - Anim := TAnimation.Create(TextureID, False, 3); - Anim.Blending := False; - g_GFX_OnceAnim(cx-16, cy-16, Anim); - Anim.Free(); - end; + tcx := Random(8); + tcy := Random(8); + r_GFX_OnceAnim(R_GFX_SMOKE, cx-4+tcx-(R_GFX_SMOKE_WIDTH div 2), cy-4+tcy-(R_GFX_SMOKE_HEIGHT div 2)); + end + else + g_GFX_Bubbles(cx, cy, 1+Random(3), 16, 16); + ShotType := 0; + Continue; + end; - g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y); + // Ãðàâèòàöèÿ + if Stopped = 0 then + Obj.Accel.Y := Obj.Accel.Y + 1; + // Ïîïàëè â ñòåíó èëè â âîäó: + if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL or MOVE_HITWATER)) then + begin + // Ïðèëèïàåì: + Obj.Vel.X := 0; + Obj.Vel.Y := 0; + Obj.Accel.Y := 0; + if WordBool(st and MOVE_HITWALL) then + Stopped := MOVE_HITWALL + else if WordBool(st and MOVE_HITLAND) then + Stopped := MOVE_HITLAND + else if WordBool(st and MOVE_HITCEIL) then + Stopped := MOVE_HITCEIL; + end; - ShotType := 0; + a := IfThen(Stopped = 0, 10, 1); + // Åñëè â êîãî-òî ïîïàëè + if g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_FLAME, False) <> 0 then + begin + // HIT_FLAME ñàì ïîäîææåò + // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì + if Stopped = 0 then + ShotType := 0; + end; + + if Stopped = 0 then + tf := 2 + else + tf := 3; + + if (gTime mod LongWord(tf) = 0) then + begin + case Stopped of + MOVE_HITWALL: begin tcx := cx-4+Random(8); tcy := cy-12+Random(24); end; + MOVE_HITLAND: begin tcx := cx-12+Random(24); tcy := cy-10+Random(8); end; + MOVE_HITCEIL: begin tcx := cx-12+Random(24); tcy := cy+6+Random(8); end; + else begin tcx := cx-4+Random(8); tcy := cy-4+Random(8); end; + end; + r_GFX_OnceAnim(R_GFX_FLAME_RAND, tcx - (R_GFX_FLAME_WIDTH div 2), tcy - (R_GFX_FLAME_HEIGHT div 2)); + //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3); end; end; @@ -1807,18 +2316,9 @@ begin begin // Ëó÷è BFG: if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID); - - // Âçðûâ BFG: - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - end; - + r_GFX_OnceAnim(R_GFX_EXPLODE_BFG, cx - 64, cy - 64); + g_DynLightExplosion(cx, cy, 96, 0, 1, 0); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y); - ShotType := 0; end; end; @@ -1843,25 +2343,12 @@ begin (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or (Timeout < 1) then begin - if ShotType = WEAPON_IMP_FIRE then - s := 'FRAMES_EXPLODE_IMPFIRE' - else - if ShotType = WEAPON_CACO_FIRE then - s := 'FRAMES_EXPLODE_CACOFIRE' - else - s := 'FRAMES_EXPLODE_BARONFIRE'; - - // Âçðûâ: - if g_Frames_Get(TextureID, s) then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-32, cy-32, Anim); - Anim.Free(); + case ShotType of + WEAPON_IMP_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_IMPFIRE, cx - 32, cy - 32); + WEAPON_CACO_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_CACOFIRE, cx - 32, cy - 32); + WEAPON_BARON_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_BARONFIRE, cx - 32, cy - 32); end; - g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); - ShotType := 0; end; end; @@ -1878,16 +2365,8 @@ begin (Timeout < 1) then begin // Âçðûâ: - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - end; - + r_GFX_OnceAnim(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); - ShotType := 0; end; end; @@ -1898,68 +2377,19 @@ begin begin if gGameSettings.GameType = GT_SERVER then MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud); - Animation.Free(); - Animation := nil; + if Animation <> nil then + begin + Animation.Free(); + Animation := nil; + end; end - else if (oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y) then + else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then if gGameSettings.GameType = GT_SERVER then MH_SEND_UpdateShot(i); end; end; end; -procedure g_Weapon_Draw(); -var - i: Integer; - a: SmallInt; - p: TPoint; -begin - if Shots = nil then - Exit; - - for i := 0 to High(Shots) do - if Shots[i].ShotType <> 0 then - with Shots[i] do - begin - if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or - (Shots[i].ShotType = WEAPON_BARON_FIRE) or - (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or - (Shots[i].ShotType = WEAPON_SKEL_FIRE) then - a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y) - else - a := 0; - - p.X := Obj.Rect.Width div 2; - p.Y := Obj.Rect.Height div 2; - - if Animation <> nil then - begin - if (Shots[i].ShotType = WEAPON_BARON_FIRE) or - (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or - (Shots[i].ShotType = WEAPON_SKEL_FIRE) then - Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a) - else - Animation.Draw(Obj.X, Obj.Y, M_NONE); - end - else - begin - if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then - e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE) - else - e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False); - end; - - if g_debug_Frames then - begin - e_DrawQuad(Obj.X+Obj.Rect.X, - Obj.Y+Obj.Rect.Y, - Obj.X+Obj.Rect.X+Obj.Rect.Width-1, - Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1, - 0, 255, 0); - end; - end; -end; - function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean; var a: Integer; @@ -1983,141 +2413,121 @@ begin end; end; -procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter); +procedure g_Weapon_SaveState (st: TStream); var count, i, j: Integer; - dw: DWORD; begin -// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ: + // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ count := 0; - if Shots <> nil then - for i := 0 to High(Shots) do - if Shots[i].ShotType <> 0 then - count := count + 1; - - Mem := TBinMemoryWriter.Create((count+1) * 80); + for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count); -// Êîëè÷åñòâî ñíàðÿäîâ: - Mem.WriteInt(count); + // Êîëè÷åñòâî ñíàðÿäîâ + utils.WriteInt(st, count); - if count = 0 then - Exit; + if (count = 0) then exit; for i := 0 to High(Shots) do + begin if Shots[i].ShotType <> 0 then begin - // Ñèãíàòóðà ñíàðÿäà: - dw := SHOT_SIGNATURE; // 'SHOT' - Mem.WriteDWORD(dw); - // Òèï ñíàðÿäà: - Mem.WriteByte(Shots[i].ShotType); - // Öåëü: - Mem.WriteWord(Shots[i].Target); - // UID ñòðåëÿâøåãî: - Mem.WriteWord(Shots[i].SpawnerUID); - // Ðàçìåð ïîëÿ Triggers: - dw := Length(Shots[i].Triggers); - Mem.WriteDWORD(dw); - // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì: - for j := 0 to Integer(dw)-1 do - Mem.WriteDWORD(Shots[i].Triggers[j]); - // Îáúåêò ñíàðÿäà: - Obj_SaveState(@Shots[i].Obj, Mem); + // Ñèãíàòóðà ñíàðÿäà + utils.writeSign(st, 'SHOT'); + utils.writeInt(st, Byte(0)); // version + // Òèï ñíàðÿäà + utils.writeInt(st, Byte(Shots[i].ShotType)); + // Öåëü + utils.writeInt(st, Word(Shots[i].Target)); + // UID ñòðåëÿâøåãî + utils.writeInt(st, Word(Shots[i].SpawnerUID)); + // Ðàçìåð ïîëÿ Triggers + utils.writeInt(st, Integer(Length(Shots[i].Triggers))); + // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì + for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j])); + // Îáúåêò ñíàðÿäà + Obj_SaveState(st, @Shots[i].Obj); + // Êîñòûëèíà åáàíàÿ + utils.writeInt(st, Byte(Shots[i].Stopped)); end; + end; end; -procedure g_Weapon_LoadState(var Mem: TBinMemoryReader); +procedure g_Weapon_LoadState (st: TStream); var - count, i, j: Integer; - dw: DWORD; + count, tc, i, j: Integer; begin - if Mem = nil then - Exit; + if (st = nil) then exit; -// Êîëè÷åñòâî ñíàðÿäîâ: - Mem.ReadInt(count); + // Êîëè÷åñòâî ñíàðÿäîâ + count := utils.readLongInt(st); + if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter'); SetLength(Shots, count); - if count = 0 then - Exit; + if (count = 0) then exit; for i := 0 to count-1 do begin - // Ñèãíàòóðà ñíàðÿäà: - Mem.ReadDWORD(dw); - if dw <> SHOT_SIGNATURE then // 'SHOT' - begin - raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature'); - end; - // Òèï ñíàðÿäà: - Mem.ReadByte(Shots[i].ShotType); - // Öåëü: - Mem.ReadWord(Shots[i].Target); - // UID ñòðåëÿâøåãî: - Mem.ReadWord(Shots[i].SpawnerUID); - // Ðàçìåð ïîëÿ Triggers: - Mem.ReadDWORD(dw); - SetLength(Shots[i].Triggers, dw); - // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì: - for j := 0 to Integer(dw)-1 do - Mem.ReadDWORD(Shots[i].Triggers[j]); - // Îáúåêò ïðåäìåòà: - Obj_LoadState(@Shots[i].Obj, Mem); - - // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè: - Shots[i].TextureID := DWORD(-1); + // Ñèãíàòóðà ñíàðÿäà + if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature'); + if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version'); + // Òèï ñíàðÿäà: + Shots[i].ShotType := utils.readByte(st); + // Öåëü + Shots[i].Target := utils.readWord(st); + // UID ñòðåëÿâøåãî + Shots[i].SpawnerUID := utils.readWord(st); + // Ðàçìåð ïîëÿ Triggers + tc := utils.readLongInt(st); + if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter'); + SetLength(Shots[i].Triggers, tc); + // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì + for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st); + // Îáúåêò ïðåäìåòà + Obj_LoadState(@Shots[i].Obj, st); + // Êîñòûëèíà åáàíàÿ + Shots[i].Stopped := utils.readByte(st); + + // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè Shots[i].Animation := nil; case Shots[i].ShotType of WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: begin - g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID); end; WEAPON_PLASMA: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA'); - Shots[i].Animation := TAnimation.Create(dw, True, 5); + Shots[i].Animation := TAnimationState.Create(True, 5, 2); // !!! put values into table end; WEAPON_BFG: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_BFG'); - Shots[i].Animation := TAnimation.Create(dw, True, 6); + Shots[i].Animation := TAnimationState.Create(True, 6, 2); // !!! put values into table end; WEAPON_IMP_FIRE: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE'); - Shots[i].Animation := TAnimation.Create(dw, True, 4); + Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; WEAPON_BSP_FIRE: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE'); - Shots[i].Animation := TAnimation.Create(dw, True, 4); + Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; WEAPON_CACO_FIRE: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE'); - Shots[i].Animation := TAnimation.Create(dw, True, 4); + Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; WEAPON_BARON_FIRE: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE'); - Shots[i].Animation := TAnimation.Create(dw, True, 4); + Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; WEAPON_MANCUB_FIRE: begin - g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE'); - Shots[i].Animation := TAnimation.Create(dw, True, 4); + Shots[i].Animation := TAnimationState.Create(True, 4, 2); // !!! put values into table end; end; end; end; procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True); -var - cx, cy: Integer; - Anim: TAnimation; - s: string; + var cx, cy: Integer; begin if Shots = nil then Exit; @@ -2137,93 +2547,48 @@ begin if Loud then begin if ShotType = WEAPON_SKEL_FIRE then - begin // Âçðûâ ñíàðÿäà Ñêåëåòà - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then - begin - Anim := TAnimation.Create(TextureID, False, 8); - Anim.Blending := False; - g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim); - Anim.Free(); - end; - end + r_GFX_OnceAnim(R_GFX_EXPLODE_SKELFIRE, (Obj.X + 32) - 32, (Obj.Y + 8) - 32) else - begin // Âçðûâ Ðàêåòû - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - end; - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y); end; end; WEAPON_PLASMA, WEAPON_BSP_FIRE: // Ïëàçìà, ïëàçìà Àðàõíàòðîíà begin - if ShotType = WEAPON_PLASMA then - s := 'FRAMES_EXPLODE_PLASMA' - else - s := 'FRAMES_EXPLODE_BSPFIRE'; - - if g_Frames_Get(TextureID, s) and loud then + if loud then begin - Anim := TAnimation.Create(TextureID, False, 3); - Anim.Blending := False; - g_GFX_OnceAnim(cx-16, cy-16, Anim); - Anim.Free(); - + if ShotType = WEAPON_PLASMA then + r_GFX_OnceAnim(R_GFX_EXPLODE_PLASMA, cx - 16, cy - 16) + else + r_GFX_OnceAnim(R_GFX_EXPLODE_BSPFIRE, cx - 16, cy - 16); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y); end; end; WEAPON_BFG: // BFG begin - // Âçðûâ BFG: - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - - g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_BFG, cx - 64, cy - 64); + g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y); end; WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà begin - if ShotType = WEAPON_IMP_FIRE then - s := 'FRAMES_EXPLODE_IMPFIRE' - else - if ShotType = WEAPON_CACO_FIRE then - s := 'FRAMES_EXPLODE_CACOFIRE' - else - s := 'FRAMES_EXPLODE_BARONFIRE'; - - if g_Frames_Get(TextureID, s) and Loud then + if loud then begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-32, cy-32, Anim); - Anim.Free(); - + case ShotType of + WEAPON_IMP_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_IMPFIRE, cx - 32, cy - 32); + WEAPON_CACO_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_CACOFIRE, cx - 32, cy - 32); + WEAPON_BARON_FIRE: r_GFX_OnceAnim(R_GFX_EXPLODE_BARONFIRE, cx - 32, cy - 32); + end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); end; end; WEAPON_MANCUB_FIRE: // Âûñòðåë Ìàíêóáóñà begin - if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then - begin - Anim := TAnimation.Create(TextureID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(cx-64, cy-64, Anim); - Anim.Free(); - - g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_ROCKET, cx - 64, cy - 64); + g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); end; end; // case ShotType of... @@ -2232,4 +2597,42 @@ begin end; end; + +procedure g_Weapon_AddDynLights(); +var + i: Integer; +begin + if Shots = nil then Exit; + for i := 0 to High(Shots) do + begin + if Shots[i].ShotType = 0 then continue; + if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or + (Shots[i].ShotType = WEAPON_BARON_FIRE) or + (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or + (Shots[i].ShotType = WEAPON_SKEL_FIRE) or + (Shots[i].ShotType = WEAPON_IMP_FIRE) or + (Shots[i].ShotType = WEAPON_CACO_FIRE) or + (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or + (Shots[i].ShotType = WEAPON_BSP_FIRE) or + (Shots[i].ShotType = WEAPON_PLASMA) or + (Shots[i].ShotType = WEAPON_BFG) or + (Shots[i].ShotType = WEAPON_FLAMETHROWER) or + false then + begin + if (Shots[i].ShotType = WEAPON_PLASMA) then + 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) + else if (Shots[i].ShotType = WEAPON_BFG) then + 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) + else if (Shots[i].ShotType = WEAPON_FLAMETHROWER) then + 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) + else + 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); + end; + end; +end; + + +procedure TShot.positionChanged (); begin end; + + end.