From: Ketmar Dark Date: Sun, 20 Aug 2017 03:35:37 +0000 (+0300) Subject: some tree code for monsters X-Git-Url: http://deadsoftware.ru/gitweb?a=commitdiff_plain;h=829537fc7b2b29baaab7287e54fba2cdd8d00b5c;p=d2df-sdl.git some tree code for monsters --- diff --git a/src/game/g_basic.pas b/src/game/g_basic.pas index e001f85..626596b 100644 --- a/src/game/g_basic.pas +++ b/src/game/g_basic.pas @@ -47,7 +47,6 @@ function g_CollideLevel(X, Y: Integer; Width, Height: Word): Boolean; function g_CollideAround(X1, Y1: Integer; Width1, Height1: Word; X2, Y2: Integer; Width2, Height2: Word): Boolean; function g_CollidePlayer(X, Y: Integer; Width, Height: Word): Boolean; -function g_CollideMonster(X, Y: Integer; Width, Height: Word): Boolean; function g_PatchLength(X1, Y1, X2, Y2: Integer): Word; function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean; function g_GetAcidHit(X, Y: Integer; Width, Height: Word): Byte; @@ -143,11 +142,6 @@ begin end; end; -function g_CollideMonster(X, Y: Integer; Width, Height: Word): Boolean; -begin - result := g_Mons_AnyAt(X, Y, Width, Height); -end; - function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean; var i: Integer; diff --git a/src/game/g_grid.pas b/src/game/g_grid.pas index d1149ad..8ff01a6 100644 --- a/src/game/g_grid.pas +++ b/src/game/g_grid.pas @@ -171,7 +171,7 @@ begin mProxyCount := 0; mProxyMaxCount := 0; mUData := 0; - mTagMask := 0; + mTagMask := -1; mItCB := nil; e_WriteLog(Format('created grid with size: %dx%d (tile size: %d); pix: %dx%d', [mWidth, mHeight, mTileSize, mWidth*mTileSize, mHeight*mTileSize]), MSG_NOTIFY); end; @@ -501,7 +501,7 @@ begin begin if (pi.bodies[f] = -1) then break; px := @mProxies[pi.bodies[f]]; - if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then + if (px.mQueryMark <> mLastQuery) and ((mTagMask = -1) or ((px.mTag and mTagMask) <> 0)) then begin //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY); px.mQueryMark := mLastQuery; @@ -513,7 +513,7 @@ begin if (mCells[idx].body <> -1) then begin px := @mProxies[mCells[idx].body]; - if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then + if (px.mQueryMark <> mLastQuery) and ((mTagMask = -1) or ((px.mTag and mTagMask) <> 0)) then begin //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY); px.mQueryMark := mLastQuery; diff --git a/src/game/g_items.pas b/src/game/g_items.pas index 95ea919..eb122d8 100644 --- a/src/game/g_items.pas +++ b/src/game/g_items.pas @@ -25,7 +25,8 @@ Type PItem = ^TItem; TItem = record private - treeNode: Integer; + //treeNode: Integer; + slotIsUsed: Boolean; arrIdx: Integer; // in ggItems public @@ -60,12 +61,12 @@ procedure g_Items_LoadState(var Mem: TBinMemoryReader); procedure g_Items_RestartRound (); -function g_ItemValidId (idx: Integer): Boolean; inline; -function g_ItemByIdx (idx: Integer): PItem; -function g_ItemObjByIdx (idx: Integer): PObj; +function g_Items_ValidId (idx: Integer): Boolean; inline; +function g_Items_ByIdx (idx: Integer): PItem; +function g_Items_ObjByIdx (idx: Integer): PObj; -procedure g_Item_EmitPickupSound (idx: Integer); // at item position -procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer); +procedure g_Items_EmitPickupSound (idx: Integer); // at item position +procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer); type @@ -115,6 +116,7 @@ begin end; } + // ////////////////////////////////////////////////////////////////////////// // var //itemTree: TDynAABBTreeItem = nil; @@ -122,27 +124,30 @@ var // ////////////////////////////////////////////////////////////////////////// // -function g_ItemValidId (idx: Integer): Boolean; inline; +function g_Items_ValidId (idx: Integer): Boolean; inline; begin result := false; if (idx < 0) or (idx > High(ggItems)) then exit; - if (ggItems[idx].treeNode = -1) then exit; + //if (ggItems[idx].treeNode = -1) then exit; + if not ggItems[idx].slotIsUsed then exit; result := true; end; -function g_ItemByIdx (idx: Integer): PItem; +function g_Items_ByIdx (idx: Integer): PItem; begin if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('g_ItemObjByIdx: invalid index'); result := @ggItems[idx]; - if (result.treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); + //if (result.treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); + if not result.slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); end; -function g_ItemObjByIdx (idx: Integer): PObj; +function g_Items_ObjByIdx (idx: Integer): PObj; begin if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('g_ItemObjByIdx: invalid index'); - if (ggItems[idx].treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); + //if (ggItems[idx].treeNode = -1) then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); + if not ggItems[idx].slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item'); result := @ggItems[idx].Obj; end; @@ -385,10 +390,12 @@ var begin if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('releaseItem: invalid item id'); it := @ggItems[idx]; - if (it.treeNode = -1) then raise Exception.Create('releaseItem: trying to release unallocated item'); + //if (it.treeNode = -1) then raise Exception.Create('releaseItem: trying to release unallocated item'); + if not it.slotIsUsed then raise Exception.Create('releaseItem: trying to release unallocated item'); if (it.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency'); //itemTree.removeObject(it.treeNode); - it.treeNode := -1; + //it.treeNode := -1; + it.slotIsUsed := false; if (it.Animation <> nil) then begin it.Animation.Free(); @@ -414,7 +421,8 @@ begin for i := olen to High(ggItems) do begin it := @ggItems[i]; - it.treeNode := -1; + //it.treeNode := -1; + it.slotIsUsed := false; it.arrIdx := i; it.ItemType := ITEM_NONE; it.Animation := nil; @@ -451,7 +459,8 @@ begin for i := olen to High(ggItems) do begin it := @ggItems[i]; - it.treeNode := -1; + //it.treeNode := -1; + it.slotIsUsed := false; it.arrIdx := i; it.ItemType := ITEM_NONE; it.Animation := nil; @@ -464,7 +473,7 @@ begin end; it := @ggItems[slot]; - if (it.treeNode = -1) then + if {(it.treeNode = -1)} not it.slotIsUsed then begin // this is unused slot; get it, and rebuild id list if rebuildFreeList then @@ -472,7 +481,7 @@ begin freeIds.clear(); for i := 0 to High(ggItems) do begin - if (i <> slot) and (ggItems[i].treeNode = -1) then freeIds.insert(i); + if (i <> slot) and {(ggItems[i].treeNode = -1)} (not it.slotIsUsed) then freeIds.insert(i); end; end; end @@ -480,7 +489,8 @@ begin begin // it will be readded //itemTree.removeObject(it.treeNode); - it.treeNode := -1; + //it.treeNode := -1; + it.slotIsUsed := false; end; result := slot; @@ -525,6 +535,12 @@ begin it := @ggItems[find_id]; + //if (it.treeNode <> -1) then raise Exception.Create('g_Items_Create: trying to reuse already allocated item'); + if (it.arrIdx <> find_id) then raise Exception.Create('g_Items_Create: arrIdx inconsistency'); + //it.treeNode := -1; + //it.arrIdx := find_id; + it.slotIsUsed := true; + it.ItemType := ItemType; it.Respawnable := Respawnable; if g_Game_IsServer and (ITEM_RESPAWNTIME = 0) then it.Respawnable := False; @@ -535,11 +551,6 @@ begin it.Live := True; it.QuietRespawn := False; - if (it.treeNode <> -1) then raise Exception.Create('g_Items_Create: trying to reuse already allocated item'); - if (it.arrIdx <> find_id) then raise Exception.Create('g_Items_Create: arrIdx inconsistency'); - //it.treeNode := -1; - //it.arrIdx := find_id; - g_Obj_Init(@it.Obj); it.Obj.X := X; it.Obj.Y := Y; @@ -647,7 +658,7 @@ begin +2. I_MEGA,I_INVL,I_SUPER 3. I_STIM,I_MEDI,I_ARM1,I_ARM2,I_AQUA,I_KEYR,I_KEYG,I_KEYB,I_SUIT,I_RTORCH,I_GTORCH,I_BTORCH,I_GOR1,I_FCAN } - g_Item_EmitPickupSoundAt(i, gPlayers[j].Obj.X, gPlayers[j].Obj.Y); + g_Items_EmitPickupSoundAt(i, gPlayers[j].Obj.X, gPlayers[j].Obj.Y); // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì if r then @@ -751,7 +762,7 @@ var //x, y: Integer; {$ENDIF} begin - if not g_ItemValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id'); + if not g_Items_ValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id'); it := @ggItems[ID]; if (it.arrIdx <> ID) then raise Exception.Create('g_Items_Remove: arrIdx desync'); @@ -915,20 +926,20 @@ end; // ////////////////////////////////////////////////////////////////////////// // -procedure g_Item_EmitPickupSound (idx: Integer); +procedure g_Items_EmitPickupSound (idx: Integer); var it: PItem; begin - if not g_ItemValidId(idx) then exit; + if not g_Items_ValidId(idx) then exit; it := @ggItems[idx]; - g_Item_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y); + g_Items_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y); end; -procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer); +procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer); var it: PItem; begin - if not g_ItemValidId(idx) then exit; + if not g_Items_ValidId(idx) then exit; it := @ggItems[idx]; if gSoundEffectsDF then diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index af98aba..59b5506 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -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; @@ -186,7 +189,14 @@ 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_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 @@ -195,8 +205,70 @@ 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; + g_language, g_netmsg, z_aabbtree; + + +// ////////////////////////////////////////////////////////////////////////// // +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); + 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,6 +469,7 @@ const MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ + var gMonsters: array of TMonster; @@ -407,30 +480,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; @@ -500,23 +577,42 @@ begin 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; @@ -526,6 +622,9 @@ begin Exit; end; end; + end; + end; + end; end; procedure g_Monsters_LoadData(); @@ -765,6 +864,8 @@ 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(); end; procedure g_Monsters_FreeData(); @@ -981,6 +1082,8 @@ begin g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK'); g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK'); + + monsTree.Free(); end; procedure g_Monsters_Init(); @@ -996,6 +1099,7 @@ begin for a := 0 to High(gMonsters) do gMonsters[a].Free(); + monsTree.reset(); gMonsters := nil; end; @@ -1003,6 +1107,7 @@ 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 +1121,15 @@ 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; // Íàñòðàèâàåì ïîëîæåíèå - with gMonsters[find_id] do + with mon do begin if AdjCoord then begin @@ -1040,7 +1148,9 @@ begin FStartY := GameY; end; - result := {find_id}gMonsters[find_id]; + mon.positionChanged(); + + result := mon; end; procedure g_Monsters_killedp(); @@ -1501,6 +1611,9 @@ begin FFirePainTime := 0; FFireAttacker := 0; + treeNode := -1; + arrIdx := -1; + if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then FBloodKind := BLOOD_SPARKS else @@ -1698,8 +1811,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); @@ -1775,6 +1888,14 @@ begin 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; @@ -1975,6 +2096,7 @@ begin FObj.X := X - FObj.Rect.X; FObj.Y := Y - FObj.Rect.Y; + positionChanged(); if dir = 1 then FDirection := D_LEFT @@ -2389,8 +2511,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 +2574,6 @@ begin begin g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî end; - positionChanged(); // this updates spatial accelerators end; end; end; @@ -4249,11 +4369,6 @@ begin 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; @@ -4289,21 +4404,111 @@ begin 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; diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index 7499fc2..9432628 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -1190,7 +1190,7 @@ procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYON var it: PItem; begin - it := g_ItemByIdx(IID); + it := g_Items_ByIdx(IID); NetOut.Write(Byte(NET_MSG_ISPAWN)); NetOut.Write(IID); @@ -2366,7 +2366,7 @@ begin g_Items_Create(X, Y, T, Fall, False, False, ID); - it := g_ItemByIdx(ID); + it := g_Items_ByIdx(ID); it.Obj.Vel.X := VX; it.Obj.Vel.Y := VY; @@ -2391,9 +2391,9 @@ begin ID := M.ReadWord(); Quiet := M.ReadByte() <> 0; - if not g_ItemValidId(ID) then exit; + if not g_Items_ValidId(ID) then exit; - if not Quiet then g_Item_EmitPickupSound(ID); + if not Quiet then g_Items_EmitPickupSound(ID); g_Items_Remove(ID); end; diff --git a/src/game/g_player.pas b/src/game/g_player.pas index 75366d3..c1b7c08 100644 --- a/src/game/g_player.pas +++ b/src/game/g_player.pas @@ -2980,7 +2980,7 @@ var id: DWORD; begin id := g_Items_Create(FObj.X, FObj.Y, t, True, False); - it := g_ItemByIdx(id); + it := g_Items_ByIdx(id); if KillType = K_EXTRAHARDKILL then // -7..+7; -8..0 begin g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-7+Random(15), diff --git a/src/game/g_triggers.pas b/src/game/g_triggers.pas index ec3bb71..4fefb5d 100644 --- a/src/game/g_triggers.pas +++ b/src/game/g_triggers.pas @@ -144,7 +144,7 @@ begin with gWalls[PanelID] do begin if g_CollidePlayer(X, Y, Width, Height) or - g_CollideMonster(X, Y, Width, Height) then Exit; + g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit; if not Enabled then begin @@ -181,7 +181,7 @@ begin with gWalls[gDoorMap[c, b]] do begin if g_CollidePlayer(X, Y, Width, Height) or - g_CollideMonster(X, Y, Width, Height) then Exit; + g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit; end; if not NoSound then @@ -1373,14 +1373,14 @@ begin if Data.ItemMax > 0 then begin - it := g_ItemByIdx(iid); + it := g_Items_ByIdx(iid); it.SpawnTrigger := ID; Inc(SpawnedCount); end; case Data.ItemEffect of EFFECT_TELEPORT: begin - it := g_ItemByIdx(iid); + it := g_Items_ByIdx(iid); if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then begin Anim := TAnimation.Create(FramesID, False, 3); @@ -1395,7 +1395,7 @@ begin NET_GFX_TELE); end; EFFECT_RESPAWN: begin - it := g_ItemByIdx(iid); + it := g_Items_ByIdx(iid); if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then begin Anim := TAnimation.Create(FramesID, False, 4); @@ -1410,7 +1410,7 @@ begin NET_GFX_RESPAWN); end; EFFECT_FIRE: begin - it := g_ItemByIdx(iid); + it := g_Items_ByIdx(iid); if g_Frames_Get(FramesID, 'FRAMES_FIRE') then begin Anim := TAnimation.Create(FramesID, False, 4); @@ -2196,7 +2196,7 @@ var a, b, i: Integer; Affected: array of Integer; - function monsNear (monidx: Integer; mon: TMonster): Boolean; + {function monsNear (monidx: Integer; mon: TMonster): Boolean; begin result := false; // don't stop if mon.Collide(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height) then @@ -2204,6 +2204,13 @@ var gTriggers[a].ActivateUID := mon.UID; ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE); end; + end;} + + function monsNear (monidx: Integer; mon: TMonster): Boolean; + begin + result := false; // don't stop + gTriggers[a].ActivateUID := mon.UID; + ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE); end; begin @@ -2440,13 +2447,14 @@ begin if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è begin - g_Mons_ForEach(monsNear); + //g_Mons_ForEach(monsNear); + g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear); end; // "Ìîíñòðîâ íåò" if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and (TimeOut = 0) and (Keys = 0) then - if not g_CollideMonster(X, Y, Width, Height) then + if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then begin gTriggers[a].ActivateUID := 0; ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER); diff --git a/src/game/g_weapons.pas b/src/game/g_weapons.pas index 2cdfe87..f8028ee 100644 --- a/src/game/g_weapons.pas +++ b/src/game/g_weapons.pas @@ -392,10 +392,10 @@ end; function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte; - function monsCheck (monidx: Integer; mon: TMonster): Boolean; + {function monsCheck (monidx: Integer; mon: TMonster): Boolean; begin result := false; // don't stop - if (mon <> nil) and mon.Live and mon.Collide(X, Y) then + if mon.Live and mon.Collide(X, Y) then begin if HitMonster(mon, dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then begin @@ -403,6 +403,16 @@ function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer; result := true; end; end; + end;} + + function monsCheck (monidx: Integer; 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; var @@ -423,7 +433,8 @@ begin if Result <> 0 then Exit; - if g_Mons_ForEach(monsCheck) then result := 2; + //if g_Mons_ForEach(monsCheck) then result := 2; + if g_Mons_ForEachAtAlive(X, Y, 1, 1, monsCheck) then result := 2; end; procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); diff --git a/src/game/z_aabbtree.pas b/src/game/z_aabbtree.pas index 3371a1e..996230d 100644 --- a/src/game/z_aabbtree.pas +++ b/src/game/z_aabbtree.pas @@ -68,6 +68,8 @@ type constructor Create (var aabb: AABB2D); overload; constructor Create (var aabb0, aabb1: AABB2D); overload; + constructor CreateWH (ax, ay, w, h: TreeNumber); + procedure copyFrom (var aabb: AABB2D); inline; procedure setDims (x0, y0, x1, y1: TreeNumber); inline; @@ -273,7 +275,7 @@ type // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error // AABB for static object will not be "fat" (simple optimization) // WARNING! inserting the same object several times *WILL* break everything! - function insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer; + function insertObject (flesh: TTreeFlesh; tag: Integer=-1; staticObject: Boolean=false): Integer; // remove an object from the tree // WARNING: ids of removed objects can be reused on later insertions! @@ -394,6 +396,14 @@ begin setMergeTwo(aabb0, aabb1); end; +constructor AABB2D.CreateWH (ax, ay, w, h: TreeNumber); +begin + minX := ax; + minY := ay; + maxX := ax+w-1; + maxY := ay+h-1; +end; + function AABB2D.getvalid (): Boolean; inline; begin result := (minX <= maxX) and (minY <= maxY); end; {$IFDEF aabbtree_use_floats} @@ -1298,7 +1308,7 @@ begin begin // call visitor on it {$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited);{$ENDIF} - if ((node.tag and tagmask) <> 0) then + if (tagmask = -1) or ((node.tag and tagmask) <> 0) then begin if assigned(visitor) then begin