X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_monsters.pas;h=ca7ec00e32e8be568d538f6020515c42dbfcc9e6;hb=9a8ac2323ba1c23f248ff5effe2747135ecc14f6;hp=af98abae75f8936317a489592ebc0656dbb6e000;hpb=e0ef894b62b50d6a7bd7e1dfa42ee3f1a839403a;p=d2df-sdl.git diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index af98aba..ca7ec00 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -20,7 +20,7 @@ interface uses g_basic, e_graphics, g_phys, g_textures, - g_saveload, BinEditor, g_panel; + g_saveload, BinEditor, g_panel, z_aabbtree; const MONSTATE_SLEEP = 0; @@ -80,6 +80,9 @@ type FFireAttacker: Word; vilefire: TAnimation; + treeNode: Integer; // node in dyntree or -1 + arrIdx: Integer; // in gMonsters + FDieTriggers: Array of Integer; FSpawnTrigger: Integer; @@ -87,9 +90,12 @@ type function findNewPrey(): Boolean; procedure ActivateTriggers(); + function getMapAABB (): AABB2D; inline; + public FNoRespawn: Boolean; FFireTime: Integer; + trapCheckFrameId: DWord; // for `g_weapons.CheckTrap()` constructor Create(MonsterType: Byte; aID: Integer; ForcedUID: Integer = -1); destructor Destroy(); override; @@ -154,31 +160,32 @@ type 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; -procedure g_Monsters_LoadData(); -procedure g_Monsters_FreeData(); -procedure g_Monsters_Init(); -procedure g_Monsters_Free(); -function g_Monsters_Create(MonsterType: Byte; X, Y: Integer; - Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster; -procedure g_Monsters_Update(); -procedure g_Monsters_Draw(); -procedure g_Monsters_DrawHealth(); -function g_Monsters_Get(UID: Word): TMonster; -procedure g_Monsters_killedp(); -procedure g_Monsters_SaveState(var Mem: TBinMemoryWriter); -procedure g_Monsters_LoadState(var Mem: TBinMemoryReader); -function g_Monsters_GetIDByName(name: String): Integer; -function g_Monsters_GetNameByID(MonsterType: Byte): String; -function g_Monsters_GetKilledBy(MonsterType: Byte): String; +procedure g_Monsters_LoadData (); +procedure g_Monsters_FreeData (); +procedure g_Monsters_Init (); +procedure g_Monsters_Free (); +function g_Monsters_Create (MonsterType: Byte; X, Y: Integer; Direction: TDirection; + AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster; +procedure g_Monsters_Update (); +procedure g_Monsters_Draw (); +procedure g_Monsters_DrawHealth (); +function g_Monsters_ByUID (UID: Word): TMonster; +procedure g_Monsters_killedp (); +procedure g_Monsters_SaveState (var Mem: TBinMemoryWriter); +procedure g_Monsters_LoadState (var Mem: TBinMemoryReader); +function g_Monsters_GetIDByName (name: String): Integer; +function g_Monsters_GetNameByID (MonsterType: Byte): String; +function g_Monsters_GetKilledBy (MonsterType: Byte): String; -type - TEachMonsterCB = function (monidx: Integer; mon: TMonster): Boolean is nested; // return `true` to stop -function g_Mons_ForEach (cb: TEachMonsterCB): Boolean; +type + TEachMonsterCB = function (mon: TMonster): Boolean is nested; // return `true` to stop // throws on invalid uid function g_Mons_ByIdx (uid: Integer): TMonster; inline; @@ -186,7 +193,19 @@ function g_Mons_ByIdx (uid: Integer): TMonster; inline; // 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_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_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean; + +function g_Mons_getNewTrapFrameId (): DWord; + + +var + gmon_debug_use_sqaccel: Boolean = true; implementation @@ -197,6 +216,80 @@ uses g_console, g_map, Math, SysUtils, g_menu, wadreader, g_language, g_netmsg; + +// ////////////////////////////////////////////////////////////////////////// // +var + 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; + + 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); + aabb := flesh.getMapAABB(); + 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; @@ -397,8 +490,37 @@ const MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ + var gMonsters: array of TMonster; + uidMap: array [0..65535] of TMonster; // monster knows it's index + + +procedure clearUidMap (); +var + idx: Integer; +begin + for idx := 0 to High(uidMap) do uidMap[idx] := nil; +end; + + +function g_Mons_getNewTrapFrameId (): DWord; +var + f: Integer; +begin + Inc(monCheckTrapLastFrameId); + if monCheckTrapLastFrameId = 0 then + begin + // wraparound + monCheckTrapLastFrameId := 1; + for f := 0 to High(gMonsters) do + begin + if (gMonsters[f] <> nil) then gMonsters[f].trapCheckFrameId := 0; + end; + end; + result := monCheckTrapLastFrameId; +end; + var pt_x: Integer = 0; @@ -407,30 +529,34 @@ var 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; @@ -458,6 +584,7 @@ begin Result := False; end; + function BehaviourDamage(SpawnerUID: Word; BH, SelfType: Byte): Boolean; var m: TMonster; @@ -469,7 +596,7 @@ begin UIDType := g_GetUIDType(SpawnerUID); if UIDType = UID_MONSTER then begin - m := g_Monsters_Get(SpawnerUID); + m := g_Monsters_ByUID(SpawnerUID); if m = nil then Exit; MonsterType := m.FMonsterType; end; @@ -487,6 +614,7 @@ begin end; end; + function canShoot(m: Byte): Boolean; begin Result := False; @@ -499,24 +627,44 @@ begin end; end; -function isCorpse(o: PObj; immediately: Boolean): Integer; + +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; + result := -1; -// Åñëè íóæíà âåðîÿòíîñòü: - if not immediately then - if Random(8) <> 0 then - Exit; - - 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; @@ -526,6 +674,9 @@ begin Exit; end; end; + end; + end; + end; end; procedure g_Monsters_LoadData(); @@ -765,6 +916,10 @@ begin 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(); + clearUidMap(); + monCheckTrapLastFrameId := 0; end; procedure g_Monsters_FreeData(); @@ -981,6 +1136,8 @@ begin g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK'); g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK'); + + monsTree.Free(); end; procedure g_Monsters_Init(); @@ -992,17 +1149,18 @@ procedure g_Monsters_Free(); var a: Integer; begin - if gMonsters <> nil then - 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; end; function g_Monsters_Create(MonsterType: Byte; X, Y: Integer; Direction: TDirection; AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster; var find_id: DWORD; + mon: TMonster; begin result := nil; @@ -1016,12 +1174,17 @@ begin 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; + + uidMap[mon.FUID] := mon; // Íàñòðàèâàåì ïîëîæåíèå - with gMonsters[find_id] do + with mon do begin if AdjCoord then begin @@ -1040,7 +1203,9 @@ begin FStartY := GameY; end; - result := {find_id}gMonsters[find_id]; + mon.positionChanged(); + + result := mon; end; procedure g_Monsters_killedp(); @@ -1069,34 +1234,33 @@ procedure g_Monsters_Update(); var a: Integer; begin -// Öåëåóêàçàòåëü: + // Öåëåóêàçàòåëü if gTime mod (GAME_TICK*2) = 0 then begin pt_x := pt_x+pt_xs; pt_y := pt_y+pt_ys; - if Abs(pt_x) > 246 then - pt_xs := -pt_xs; - if Abs(pt_y) > 100 then - pt_ys := -pt_ys; + if abs(pt_x) > 246 then pt_xs := -pt_xs; + if abs(pt_y) > 100 then pt_ys := -pt_ys; end; gMon := True; // Äëÿ ðàáîòû BlockMon'à - if gMonsters <> nil then - for a := 0 to High(gMonsters) do - if (gMonsters[a] <> nil) then - if not gMonsters[a].FRemoved then - begin - if g_Game_IsClient then - gMonsters[a].ClientUpdate() - else - gMonsters[a].Update(); - end - else - begin - gMonsters[a].Free(); - gMonsters[a] := nil; - end; + for a := 0 to High(gMonsters) do + begin + if (gMonsters[a] = nil) then continue; + if not gMonsters[a].FRemoved then + begin + if g_Game_IsClient then + gMonsters[a].ClientUpdate() + else + gMonsters[a].Update(); + end + else + begin + gMonsters[a].Free(); + gMonsters[a] := nil; + end; + end; gMon := False; end; @@ -1128,12 +1292,12 @@ begin end; end; -function g_Monsters_Get(UID: Word): TMonster; -var - a: Integer; +function g_Monsters_ByUID (UID: Word): TMonster; +//var a: Integer; begin + result := uidMap[UID]; + { Result := nil; - if gMonsters <> nil then for a := 0 to High(gMonsters) do if (gMonsters[a] <> nil) and @@ -1142,6 +1306,7 @@ begin Result := gMonsters[a]; Break; end; + } end; procedure g_Monsters_SaveState(var Mem: TBinMemoryWriter); @@ -1501,6 +1666,10 @@ begin FFirePainTime := 0; FFireAttacker := 0; + treeNode := -1; + arrIdx := -1; + trapCheckFrameId := 0; + if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then FBloodKind := BLOOD_SPARKS else @@ -1650,8 +1819,7 @@ begin end; // Òåïåðü öåëü - óäàðèâøèé, åñëè òîëüêî íå ñàì ñåáÿ: - if (SpawnerUID <> FUID) and - (BehaviourDamage(SpawnerUID, FBehaviour, FMonsterType)) then + if (SpawnerUID <> FUID) and (BehaviourDamage(SpawnerUID, FBehaviour, FMonsterType)) then begin FTargetUID := SpawnerUID; FTargetTime := 0; @@ -1698,8 +1866,8 @@ begin 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); @@ -1757,8 +1925,7 @@ begin if FHealth < FMaxHealth then begin IncMax(FHealth, Value, FMaxHealth); - if g_Game_IsServer and g_Game_IsNet then - MH_SEND_MonsterState(FUID); + if g_Game_IsServer and g_Game_IsNet then MH_SEND_MonsterState(FUID); Result := True; end; end; @@ -1775,6 +1942,24 @@ begin vilefire.Free(); + if (treeNode <> -1) then + begin + if monsTree.isValidId(treeNode) 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; + end; + + if (arrIdx <> -1) then + begin + gMonsters[arrIdx] := nil; + end; + + uidMap[FUID] := nil; + inherited Destroy(); end; @@ -1879,8 +2064,7 @@ procedure TMonster.Push(vx, vy: Integer); begin FObj.Accel.X := FObj.Accel.X + vx; FObj.Accel.Y := FObj.Accel.Y + vy; - if g_Game_IsServer and g_Game_IsNet then - MH_SEND_MonsterPos(FUID); + if g_Game_IsServer and g_Game_IsNet then MH_SEND_MonsterPos(FUID); end; procedure TMonster.SetState(State: Byte; ForceAnim: Byte = 255); @@ -1975,6 +2159,7 @@ begin FObj.X := X - FObj.Rect.X; FObj.Y := Y - FObj.Rect.Y; + positionChanged(); if dir = 1 then FDirection := D_LEFT @@ -2003,8 +2188,7 @@ begin NET_GFX_TELE); end; - if g_Game_IsServer and g_Game_IsNet then - MH_SEND_MonsterPos(FUID); + if g_Game_IsServer and g_Game_IsNet then MH_SEND_MonsterPos(FUID); Result := True; end; @@ -2389,8 +2573,7 @@ begin 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; @@ -2453,7 +2636,6 @@ begin begin g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî end; - positionChanged(); // this updates spatial accelerators end; end; end; @@ -2699,7 +2881,7 @@ _end: 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), @@ -2709,7 +2891,7 @@ _end: 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, @@ -2719,9 +2901,10 @@ _end: 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; // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ: @@ -2894,7 +3077,7 @@ _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; @@ -4224,8 +4407,7 @@ procedure TMonster.CatchFire(Attacker: Word); begin FFireTime := 100; FFireAttacker := Attacker; - if g_Game_IsNet and g_Game_IsServer then - MH_SEND_MonsterState(FUID); + if g_Game_IsNet and g_Game_IsServer then MH_SEND_MonsterState(FUID); end; procedure TMonster.OnFireFlame(Times: DWORD = 1); @@ -4249,61 +4431,170 @@ begin end; end; -//WARNING! call this after monster position was changed, or coldet will not work right! -procedure TMonster.positionChanged (); + +// ////////////////////////////////////////////////////////////////////////// // +// 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; +function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean; +var + idx: Integer; + mon: TMonster; begin - result := g_Mons_ByIdx_NC(uid); - if (result = nil) then raise Exception.Create('g_Mons_ByIdx: invalid monster id'); + 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; -// can return null -function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline; +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; + mon: TMonster; begin - if (uid < 0) or (uid > High(gMonsters)) then begin result := nil; exit; end; - result := gMonsters[uid]; + 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 + mon := gMonsters[idx]; + if (mon <> nil) and mon.Live then + begin + if g_Obj_Collide(x, y, width, height, @mon.Obj) then + begin + result := true; + exit; + end; + end; + end; + end; end; -function g_Mons_AnyAt (x, y: Integer; width, height: Integer): Boolean; +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); + 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); + end + else + begin + for idx := 0 to High(gMonsters) do + begin + mon := gMonsters[idx]; + if (mon <> nil) and mon.Live then + begin + if g_Obj_Collide(x, y, width, height, @mon.Obj) then + begin + result := cb(mon); + if result then exit; + end; + end; + end; + end; +end; - for idx := 0 to High(gMonsters) do + +function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean; + + function monsCollCheck (mon: TMonster; atag: Integer): Boolean; begin - if (gMonsters[idx] <> nil) and gMonsters[idx].Live then + result := false; + 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 + 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 g_Obj_Collide(x, y, width, height, @gMonsters[idx].Obj) then + mon := gMonsters[idx]; + if (mon <> nil) and mon.Live then begin - result := True; - exit; + if g_Obj_Collide(x, y, width, height, @mon.Obj) then + begin + result := cb(mon); + if result then exit; + end; end; end; end;