index af98abae75f8936317a489592ebc0656dbb6e000..59b5506a5ee732d02bde51f54f49820dedc7a1c2 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.pas
FFireAttacker: Word;
vilefire: TAnimation;
+ treeNode: Integer; // node in dyntree or -1
+ arrIdx: Integer; // in gMonsters
+
FDieTriggers: Array of Integer;
FSpawnTrigger: Integer;
// can return null
function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline;
-function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean;
+function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): 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;
+
+
+var
+ gmon_debug_use_sqaccel: Boolean = true;
implementation
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;
+ g_language, g_netmsg, z_aabbtree;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+ TDynAABBTreeMonsBase = specialize TDynAABBTreeBase<TMonster>;
+
+ TDynAABBTreeMons = class(TDynAABBTreeMonsBase)
+ function getFleshAABB (out aabb: AABB2D; flesh: TMonster; tag: Integer): Boolean; override;
+ end;
+
+function TDynAABBTreeMons.getFleshAABB (out aabb: AABB2D; flesh: TMonster; tag: Integer): Boolean;
+begin
+ 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);
+ if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
+ result := true;
+end;
+
+
+var
+ monsTree: TDynAABBTreeMons = nil;
+
+
+//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)}
+ //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)}
+ 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}
+ end
+ else
+ 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}
+
+ {$IFDEF TRUE}
+ monsTree.updateObject(treeNode);
+ {$ELSE}
+ monsTree.removeObject(treeNode);
+ treeNode := monsTree.insertObject(self);
+ {$ENDIF}
+
+ {$IF DEFINED(D2F_DEBUG)}
+ 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}
+ end;
+end;
+
+// ////////////////////////////////////////////////////////////////////////// //
const
ANIM_SLEEP = 0;
ANIM_GO = 1;
MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè
MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ
+
var
gMonsters: array of TMonster;
pt_ys: Integer = 1;
soulcount: Integer = 0;
-function FindMonster(): DWORD;
+
+function allocMonster(): DWORD;
var
- i: Integer;
+ i, olen: Integer;
begin
- if gMonsters <> nil then
- for i := 0 to High(gMonsters) do
- if gMonsters[i] = nil then
- begin
- Result := i;
- Exit;
- end;
-
- if gMonsters = nil then
- begin
- SetLength(gMonsters, 32);
- Result := 0;
- end
- else
+ for i := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[i] = nil) then
begin
- Result := High(gMonsters) + 1;
- SetLength(gMonsters, Length(gMonsters) + 32);
+ result := i;
+ exit;
end;
+ end;
+
+ olen := Length(gMonsters);
+ if (olen = 0) then
+ begin
+ SetLength(gMonsters, 64);
+ result := 0;
+ end
+ else
+ begin
+ result := olen;
+ SetLength(gMonsters, Length(gMonsters)+32);
+ end;
end;
+
function IsFriend(a, b: Byte): Boolean;
begin
Result := True;
end;
function isCorpse(o: PObj; immediately: Boolean): Integer;
+
+ function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
+ begin
+ result := false; // don't stop
+ if (mon.FState = STATE_DEAD) and g_Obj_Collide(o, @mon.FObj) then
+ begin
+ case mon.FMonsterType of // Íå âîñêðåñèòü:
+ MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER,
+ MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: exit;
+ end;
+ // Îñòàëüíûõ ìîæíî âîñêðåñèòü
+ result := true;
+ end;
+ end;
+
var
a: Integer;
+ mon: TMonster;
begin
- Result := -1;
-
-// Åñëè íóæíà âåðîÿòíîñòü:
- if not immediately then
- if Random(8) <> 0 then
- Exit;
+ result := -1;
- if gMonsters = nil then
- Exit;
+ // Åñëè íóæíà âåðîÿòíîñòü
+ if not immediately and (Random(8) <> 0) then exit;
-// Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè:
- for a := 0 to High(gMonsters) do
- if (gMonsters[a] <> nil) and (gMonsters[a].FState = STATE_DEAD) then
- if g_Obj_Collide(o, @gMonsters[a].FObj) then
+ // Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè
+ if gmon_debug_use_sqaccel then
+ begin
+ mon := monsTree.aabbQuery(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height, monsCollCheck);
+ if (mon <> nil) then result := mon.arrIdx;
+ end
+ else
+ begin
+ for a := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[a] <> nil) and (gMonsters[a].FState = STATE_DEAD) and g_Obj_Collide(o, @gMonsters[a].FObj) then
+ begin
case gMonsters[a].FMonsterType of // Íå âîñêðåñèòü:
MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER,
MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: Continue;
Exit;
end;
end;
+ end;
+ end;
+ end;
end;
procedure g_Monsters_LoadData();
g_Sound_CreateWADEx('SOUND_MONSTER_SPIDER_WALK', GameWAD+':MSOUNDS\SPIDER_WALK');
g_Sound_CreateWADEx('SOUND_MONSTER_FISH_ATTACK', GameWAD+':MSOUNDS\FISH_ATTACK');
+
+ monsTree := TDynAABBTreeMons.Create();
end;
procedure g_Monsters_FreeData();
g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK');
g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK');
+
+ monsTree.Free();
end;
procedure g_Monsters_Init();
for a := 0 to High(gMonsters) do
gMonsters[a].Free();
+ monsTree.reset();
gMonsters := nil;
end;
Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
var
find_id: DWORD;
+ mon: TMonster;
begin
result := nil;
soulcount := soulcount + 1;
end;
- find_id := FindMonster();
+ find_id := allocMonster();
- gMonsters[find_id] := TMonster.Create(MonsterType, find_id, ForcedUID);
+ mon := TMonster.Create(MonsterType, find_id, ForcedUID);
+ gMonsters[find_id] := mon;
+ mon.arrIdx := find_id;
+ mon.treeNode := -1;
// Íàñòðàèâàåì ïîëîæåíèå
- with gMonsters[find_id] do
+ with mon do
begin
if AdjCoord then
begin
FStartY := GameY;
end;
- result := {find_id}gMonsters[find_id];
+ mon.positionChanged();
+
+ result := mon;
end;
procedure g_Monsters_killedp();
FFirePainTime := 0;
FFireAttacker := 0;
+ treeNode := -1;
+ arrIdx := -1;
+
if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then
FBloodKind := BLOOD_SPARKS
else
it := g_Items_Create(FObj.X + (FObj.Rect.Width div 2),
FObj.Y + (FObj.Rect.Height div 2),
c, True, False);
- g_Obj_Push(g_ItemObjByIdx(it), (FObj.Vel.X div 2)-3+Random(7),
- (FObj.Vel.Y div 2)-Random(4));
+ g_Obj_Push(g_Items_ObjByIdx(it), (FObj.Vel.X div 2)-3+Random(7),
+ (FObj.Vel.Y div 2)-Random(4));
positionChanged(); // this updates spatial accelerators
if g_Game_IsServer and g_Game_IsNet then
MH_SEND_ItemSpawn(True, it);
vilefire.Free();
+ 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);
+ end;
+
inherited Destroy();
end;
FObj.X := X - FObj.Rect.X;
FObj.Y := Y - FObj.Rect.Y;
+ positionChanged();
if dir = 1 then
FDirection := D_LEFT
if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then
begin // "Ñòîèò" òâåðäî
// Ðûáà òðåïûõàåòñÿ íà ïîâåðõíîñòè:
- if FObj.Accel.Y = 0 then
- FObj.Vel.Y := -6;
+ if FObj.Accel.Y = 0 then FObj.Vel.Y := -6;
FObj.Accel.X := FObj.Accel.X - 8 + Random(17);
end;
begin
g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî
end;
- positionChanged(); // this updates spatial accelerators
end;
end;
end;
end;
end;
-//WARNING! call this after monster position was changed, or coldet will not work right!
-procedure TMonster.positionChanged ();
-begin
-end;
-
// ////////////////////////////////////////////////////////////////////////// //
function g_Mons_ForEach (cb: TEachMonsterCB): Boolean;
end;
-function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean;
+function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
+
+ function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
+ begin
+ result := (mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj));
+ end;
+
var
idx: Integer;
begin
result := false;
if (width < 1) or (height < 1) then exit;
- for idx := 0 to High(gMonsters) do
+ if gmon_debug_use_sqaccel then
begin
- if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+ end
+ else
+ begin
+ for idx := 0 to High(gMonsters) do
begin
- if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
begin
- result := True;
- exit;
+ if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ begin
+ result := true;
+ exit;
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
+
+ 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);
+ end;
+
+var
+ idx: Integer;
+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);
+ end
+ else
+ begin
+ for idx := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ begin
+ if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ begin
+ result := cb(idx, gMonsters[idx]);
+ if result then exit;
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+function g_Mons_ForEachAtAlive (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);
+ end;
+
+var
+ idx: Integer;
+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
+ result := (monsTree.pointQuery(x, y, monsCollCheck) <> nil);
+ end
+ else
+ begin
+ result := (monsTree.aabbQuery(x, y, width, height, monsCollCheck) <> nil);
+ end;
+ end
+ else
+ begin
+ for idx := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[idx] <> nil) and gMonsters[idx].Live then
+ begin
+ if g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then
+ begin
+ result := cb(idx, gMonsters[idx]);
+ if result then exit;
+ end;
end;
end;
end;