index 9f395fdde5dc474ab1ef27649f64d3b6ea88eaef..f94e8e8dc3160fb7e451647ce6513afb0db0d671 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.pas
uses
g_basic, e_graphics, g_phys, g_textures,
- g_saveload, BinEditor, g_panel;
+ g_saveload, BinEditor, g_panel, z_aabbtree, xprofiler;
const
MONSTATE_SLEEP = 0;
function findNewPrey(): Boolean;
procedure ActivateTriggers();
+ function getMapAABB (): AABB2D; inline;
+
public
FNoRespawn: Boolean;
FFireTime: Integer;
property GameAccelY: Integer read FObj.Accel.Y write FObj.Accel.Y;
property GameDirection: TDirection read FDirection write FDirection;
+ property mapAABB: AABB2D read getMapAABB;
+
property StartID: Integer read FStartID;
end;
type
- TEachMonsterCB = function (monidx: Integer; mon: TMonster): Boolean is nested; // return `true` to stop
-
-function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+ TEachMonsterCB = function (mon: TMonster): Boolean is nested; // return `true` to stop
// throws on invalid uid
function g_Mons_ByIdx (uid: Integer): TMonster; inline;
function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
+function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean;
+
function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
-function g_Mons_ForEachAtAlive (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
function g_Mons_getNewTrapFrameId (): DWord;
+type
+ TMonsAlongLineCB = function (mon: TMonster; dist: Single): Boolean is nested;
+
+function g_Mons_alongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB): TMonster;
+
+
var
gmon_debug_use_sqaccel: Boolean = true;
+//HACK!
+procedure g_Mons_ProfilersBegin ();
+procedure g_Mons_ProfilersEnd ();
+
+procedure g_Mons_LOS_Start (); inline;
+procedure g_Mons_LOS_End (); inline;
+
+var
+ profMonsLOS: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
+
+
implementation
uses
e_log, g_main, g_sound, g_gfx, g_player, g_game,
g_weapons, g_triggers, MAPDEF, g_items, g_options,
g_console, g_map, Math, SysUtils, g_menu, wadreader,
- g_language, g_netmsg, z_aabbtree;
+ g_language, g_netmsg;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure g_Mons_ProfilersBegin ();
+begin
+ if (profMonsLOS = nil) then profMonsLOS := TProfiler.Create('LOS CALC', g_profile_history_size);
+ profMonsLOS.mainBegin(g_profile_los);
+ if g_profile_los then
+ begin
+ profMonsLOS.sectionBegin('loscalc');
+ profMonsLOS.sectionEnd();
+ end;
+end;
+
+procedure g_Mons_ProfilersEnd ();
+begin
+ if (profMonsLOS <> nil) and (g_profile_los) then profMapCollision.mainEnd();
+end;
+
+procedure g_Mons_LOS_Start (); inline;
+begin
+ profMonsLOS.sectionBeginAccum('loscalc');
+end;
+
+procedure g_Mons_LOS_End (); inline;
+begin
+ profMonsLOS.sectionEnd();
+end;
// ////////////////////////////////////////////////////////////////////////// //
monCheckTrapLastFrameId: DWord;
+function TMonster.getMapAABB (): AABB2D; inline;
+begin
+ result := AABB2D.CreateWH(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
+end;
+
+
// ////////////////////////////////////////////////////////////////////////// //
type
TDynAABBTreeMonsBase = specialize TDynAABBTreeBase<TMonster>;
result := false;
if (flesh = nil) then raise Exception.Create('DynTree: trying to get dimensions of inexistant monsters');
if (flesh.Obj.Rect.Width < 1) or (flesh.Obj.Rect.Height < 1) then raise Exception.Create('DynTree: monster without size, wtf?!');
- aabb := AABB2D.CreateWH(flesh.Obj.X+flesh.Obj.Rect.X, flesh.Obj.Y+flesh.Obj.Rect.Y, flesh.Obj.Rect.Width, flesh.Obj.Rect.Height);
+ //aabb := AABB2D.CreateWH(flesh.Obj.X+flesh.Obj.Rect.X, flesh.Obj.Y+flesh.Obj.Rect.Y, flesh.Obj.Rect.Width, flesh.Obj.Rect.Height);
+ aabb := flesh.getMapAABB();
if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
result := true;
end;
monsTree: TDynAABBTreeMons = nil;
+function g_Mons_alongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB): TMonster;
+
+ function sqchecker (mon: TMonster; var ray: Ray2D): Single;
+ var
+ aabb: AABB2D;
+ tmin: Single;
+ begin
+ result := -666.0; // invalid
+ aabb := mon.mapAABB;
+ if not aabb.valid then exit;
+ if aabb.intersects(ray, @tmin) then
+ begin
+ if (tmin <= 0.0) then tmin := 0.0;
+ result := tmin;
+ if cb(mon, tmin) then result := 0.0; // instant stop
+ end;
+ end;
+
+var
+ qr: TDynAABBTreeMons.TSegmentQueryResult;
+begin
+ result := nil;
+ if not assigned(cb) then exit;
+ if monsTree.segmentQuery(qr, x0, y0, x1, y1, sqchecker) then
+ begin
+ if (qr.flesh <> nil) then result := qr.flesh;
+ end;
+end;
+
+
//WARNING! call this after monster position was changed, or coldet will not work right!
procedure TMonster.positionChanged ();
var
x, y: Integer;
begin
- {$IF DEFINED(D2F_DEBUG)}
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
//e_WriteLog(Format('monster #%d(%u): pos=(%d,%d); rpos=(%d,%d)', [arrIdx, UID, FObj.X, FObj.Y, FObj.Rect.X, FObj.Rect.Y]), MSG_NOTIFY);
{$ENDIF}
if (treeNode = -1) then
begin
treeNode := monsTree.insertObject(self, 0);
- {$IF DEFINED(D2F_DEBUG)}
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
monsTree.getNodeXY(treeNode, x, y);
e_WriteLog(Format('monster #%d(%u): inserted into the tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);
{$ENDIF}
begin
monsTree.getNodeXY(treeNode, x, y);
if (FObj.X+FObj.Rect.X = x) and (FObj.Y+FObj.Rect.Y = y) then exit; // nothing to do
- {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('monster #%d(%u): updating tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}e_WriteLog(Format('monster #%d(%u): updating tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
{$IFDEF TRUE}
monsTree.updateObject(treeNode);
treeNode := monsTree.insertObject(self);
{$ENDIF}
- {$IF DEFINED(D2F_DEBUG)}
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
monsTree.getNodeXY(treeNode, x, y);
e_WriteLog(Format('monster #%d(%u): updated tree; nodeid=%d; x=%d; y=%d', [arrIdx, UID, treeNode, x, y]), MSG_NOTIFY);
{$ENDIF}
Result := False;
end;
+
function BehaviourDamage(SpawnerUID: Word; BH, SelfType: Byte): Boolean;
var
m: TMonster;
end;
end;
+
function canShoot(m: Byte): Boolean;
begin
Result := False;
end;
end;
-function isCorpse(o: PObj; immediately: Boolean): Integer;
+
+function isCorpse (o: PObj; immediately: Boolean): Integer;
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
var
a: Integer;
begin
- for a := 0 to High(gMonsters) do gMonsters[a].Free();
monsTree.reset();
+ for a := 0 to High(gMonsters) do gMonsters[a].Free();
gMonsters := nil;
clearUidMap();
monCheckTrapLastFrameId := 0;
if (treeNode <> -1) then
begin
- {$IF DEFINED(D2F_DEBUG)}
- e_WriteLog(Format('monster #%d(%u): removed from tree; nodeid=%d', [arrIdx, UID, treeNode]), MSG_NOTIFY);
- {$ENDIF}
- monsTree.removeObject(treeNode);
+ if monsTree.isValidId(treeNode) then
+ begin
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+ e_WriteLog(Format('monster #%d(%u): removed from tree; nodeid=%d', [arrIdx, UID, treeNode]), MSG_NOTIFY);
+ {$ENDIF}
+ monsTree.removeObject(treeNode);
+ end;
end;
if (arrIdx <> -1) then
mon.SetState(STATE_GO);
mon.FNoRespawn := True;
Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+ if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
end;
mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
mon.SetState(STATE_GO);
mon.FNoRespawn := True;
Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+ if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
end;
mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
mon.SetState(STATE_GO);
mon.FNoRespawn := True;
Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+ if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
end;
- if g_Game_IsNet then MH_SEND_CoopStats(gMonsters[sx].UID);
+
+ if g_Game_IsNet then MH_SEND_CoopStats();
end;
// Ó ýòèõ ìîíñòðîâ íåò òðóïîâ:
mon.shoot(@o, True);
Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(gMonsters[sx].UID);
+ if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
end;
end;
end;
// ////////////////////////////////////////////////////////////////////////// //
+// throws on invalid uid
+function g_Mons_ByIdx (uid: Integer): TMonster; inline;
+begin
+ result := g_Mons_ByIdx_NC(uid);
+ if (result = nil) then raise Exception.Create('g_Mons_ByIdx: invalid monster id');
+end;
+
+
+// can return null
+function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
+begin
+ if (uid < 0) or (uid > High(gMonsters)) then begin result := nil; exit; end;
+ result := gMonsters[uid];
+end;
+
+
function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
var
idx: Integer;
+ mon: TMonster;
begin
result := false;
if (gMonsters = nil) or not assigned(cb) then exit;
for idx := 0 to High(gMonsters) do
begin
- if (gMonsters[idx] <> nil) then
+ mon := gMonsters[idx];
+ if (mon <> nil) then
begin
- result := cb(idx, gMonsters[idx]);
+ result := cb(mon);
if result then exit;
end;
end;
end;
-// throws on invalid uid
-function g_Mons_ByIdx (uid: Integer): TMonster; inline;
-begin
- result := g_Mons_ByIdx_NC(uid);
- if (result = nil) then raise Exception.Create('g_Mons_ByIdx: invalid monster id');
-end;
-
-
-// can return null
-function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
+function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean;
+var
+ idx: Integer;
+ mon: TMonster;
begin
- if (uid < 0) or (uid > High(gMonsters)) then begin result := nil; exit; end;
- result := gMonsters[uid];
+ result := false;
+ if (gMonsters = nil) or not assigned(cb) then exit;
+ for idx := 0 to High(gMonsters) do
+ begin
+ mon := gMonsters[idx];
+ if (mon <> nil) and mon.Live then
+ begin
+ result := cb(mon);
+ if result then exit;
+ end;
+ end;
end;
@@ -4456,10 +4571,10 @@ function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
var
idx: Integer;
+ mon: TMonster;
begin
result := false;
if (width < 1) or (height < 1) then exit;
-
if gmon_debug_use_sqaccel then
begin
result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
begin
for idx := 0 to High(gMonsters) do
begin
- if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ mon := gMonsters[idx];
+ if (mon <> nil) and mon.Live then
begin
- if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin
result := true;
exit;
@@ -4486,15 +4602,15 @@ function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonst
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
result := false;
- if g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon.arrIdx, mon);
+ if g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon);
end;
var
idx: Integer;
+ mon: TMonster;
begin
result := false;
if (width < 1) or (height < 1) then exit;
-
if gmon_debug_use_sqaccel then
begin
result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
begin
for idx := 0 to High(gMonsters) do
begin
- if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ mon := gMonsters[idx];
+ if (mon <> nil) and mon.Live then
begin
- if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin
- result := cb(idx, gMonsters[idx]);
+ result := cb(mon);
if result then exit;
end;
end;
end;
-function g_Mons_ForEachAtAlive (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
result := false;
- if mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon.arrIdx, mon);
+ if mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon);
end;
var
idx: Integer;
+ mon: TMonster;
begin
result := false;
if (width < 1) or (height < 1) then exit;
-
if gmon_debug_use_sqaccel then
begin
if (width = 1) and (height = 1) then
begin
for idx := 0 to High(gMonsters) do
begin
- if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ mon := gMonsters[idx];
+ if (mon <> nil) and mon.Live then
begin
- if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin
- result := cb(idx, gMonsters[idx]);
+ result := cb(mon);
if result then exit;
end;
end;