X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_weapons.pas;h=aa78ebd94e7ddae5fa7f224eada8b21f0cf3f79e;hb=c185246bfc49a2ae31fc79ef7c73dc6bcc1073e2;hp=3585ef2b4d136f148003fcc717d9a172bc28f4e6;hpb=0fdf4d69047801a6a8d789df6def4a47015e9ae4;p=d2df-sdl.git diff --git a/src/game/g_weapons.pas b/src/game/g_weapons.pas index 3585ef2..aa78ebd 100644 --- a/src/game/g_weapons.pas +++ b/src/game/g_weapons.pas @@ -14,12 +14,13 @@ * along with this program. If not, see . *) {$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; + g_textures, g_basic, e_graphics, g_phys, BinEditor, xprofiler; const HIT_SOME = 0; @@ -62,7 +63,7 @@ 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, dmg: 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); @@ -116,13 +117,19 @@ const 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, + Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel, g_console, SysUtils, g_options, g_game, g_triggers, MAPDEF, e_log, g_monsters, g_saveload, - g_language, g_netmsg; + g_language, g_netmsg, g_grid, + binheap, hashtable; type TWaterPanel = record @@ -152,8 +159,79 @@ const SHOT_SIGNATURE = $544F4853; // 'SHOT' +type + PHitTime = ^THitTime; + THitTime = record + distSq: Integer; + mon: TMonster; + plridx: Integer; // if mon=nil + x, y: Integer; + 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; + + +function hitTimeLess (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 @@ -243,68 +321,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.Live 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(); + + //i1 := -1; + //i2 := -1; - SetLength(pl, 1024); - SetLength(mn, 1024); - for d := 0 to 1023 do pl[d] := $FFFF; - for d := 0 to 1023 do mn[d] := $FFFF; + //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].Live) 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; @@ -317,9 +424,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 @@ -369,59 +476,44 @@ begin Result := True; 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 + 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); + 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.Live) 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; @@ -470,22 +562,14 @@ 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; + FramesID: DWORD = 0; begin if I < 0 then find_id := FindShot() @@ -694,7 +778,7 @@ begin Shots[i].Stopped := 0; if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then Shots[i].Timeout := 900 // ~25 sec - else + else begin if Shots[i].ShotType = WEAPON_FLAMETHROWER then Shots[i].Timeout := SHOT_FLAME_LIFETIME @@ -740,25 +824,47 @@ var end; end; end; - function MonsterHit(): Boolean; - var - i: Integer; + + { + function monsCheckHit (monidx: Integer; mon: TMonster): Boolean; begin - Result := False; - h := High(gMonsters); + result := false; // don't stop + if mon.Live 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); + end; + result := True; + end; + end; + end; + } - 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 - if t <> HIT_FLAME then - gMonsters[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); - Result := True; - break; - 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; @@ -847,17 +953,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.Live 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); @@ -889,34 +1028,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); @@ -1051,6 +1164,9 @@ begin g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET'); g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL'); + + //wgunMonHash := hashNewIntInt(); + wgunHitHeap := TBinaryHeapHitTimes.Create(hitTimeLess); end; procedure g_Weapon_FreeData(); @@ -1110,7 +1226,47 @@ begin g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE'); end; -procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); + +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].Live 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; + +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_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); var a: Integer; x2, y2: Integer; @@ -1120,10 +1276,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; @@ -1159,6 +1318,10 @@ begin //vx := (dx*10 div d)*xi; //vy := (dy*10 div d)*yi; + {$IF DEFINED(D2F_DEBUG)} + stt := curTimeMicro(); + {$ENDIF} + xx := x; yy := y; @@ -1186,21 +1349,237 @@ begin if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then begin _collide := True; + {$IF DEFINED(D2F_DEBUG)} + stt := curTimeMicro()-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 := curTimeMicro()-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; +*) + + +//!!!FIXME!!! +procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); +var + x0, y0: Integer; + x2, y2: Integer; + xi, yi: Integer; + wallDistSq: Integer = $3fffffff; + + 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].Live then exit; + 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 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.Live 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; + + function sqchecker (mon: TMonster; tag: Integer): Boolean; + var + mx, my, mw, mh: Integer; + inx, iny: Integer; + distSq: Integer; + begin + result := false; // don't stop + 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} +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; + + //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]), MSG_NOTIFY); + stt := curTimeMicro(); + {$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); + + // 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 := curTimeMicro()-stt; + e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_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 := curTimeMicro()-stt; + e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_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 @@ -1545,7 +1924,7 @@ begin throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; - + g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; @@ -1975,7 +2354,7 @@ begin else tf := 3; - if (gTime mod tf = 0) then + if (gTime mod LongWord(tf) = 0) then begin Anim := TAnimation.Create(TextureID, False, 2 + Random(2)); Anim.Alpha := 0;