summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 0fdf4d6)
raw | patch | inline | side by side (parent: 0fdf4d6)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sat, 19 Aug 2017 19:16:23 +0000 (22:16 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sat, 19 Aug 2017 19:17:13 +0000 (22:17 +0300) |
diff --git a/src/game/g_basic.pas b/src/game/g_basic.pas
index 2c540aad0a617fc7c2c51f3bc8060e7e4d057861..8430c9f98eecc88eba2aceba3096c4078e3f1704 100644 (file)
--- a/src/game/g_basic.pas
+++ b/src/game/g_basic.pas
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_CollideItem(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;
end;
end;
-function g_CollideItem(X, Y: Integer; Width, Height: Word): Boolean;
-var
- a: Integer;
-begin
- Result := False;
-
- if gItems = nil then
- Exit;
-
- for a := 0 to High(gItems) do
- if gItems[a].Live then
- if g_Obj_Collide(X, Y, Width, Height, @gItems[a].Obj) then
- begin
- Result := True;
- Exit;
- end;
-end;
-
function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean;
var
i: Integer;
diff --git a/src/game/g_game.pas b/src/game/g_game.pas
index d4af70f7fbf8376458c98d9a69a220ede0ced087..497567b23ccf7e36e3e8e5f011c411211d65502c 100644 (file)
--- a/src/game/g_game.pas
+++ b/src/game/g_game.pas
gPlayer2 := g_Player_Get(gLMSPID2);
end;
- for i := Low(gItems) to High(gItems) do
- begin
- if gItems[i].Respawnable then
- begin
- gItems[i].QuietRespawn := True;
- gItems[i].RespawnTime := 0;
- end
- else
- begin
- g_Items_Remove(i);
- if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
- end;
- end;
+ g_Items_RestartRound();
for i := Low(gMonsters) to High(gMonsters) do
begin
diff --git a/src/game/g_items.pas b/src/game/g_items.pas
index e35fd3399dd7e8eb2dd83773b7ffbab1ccee11b1..6d7cf5a10a7918ea8bc0480160d33c9769c0099e 100644 (file)
--- a/src/game/g_items.pas
+++ b/src/game/g_items.pas
g_textures, g_phys, g_saveload, BinEditor, MAPDEF;
Type
+ PItem = ^TItem;
TItem = record
+ private
+ treeNode: Integer;
+ arrIdx: Integer; // in ggItems
+
+ public
ItemType: Byte;
Respawnable: Boolean;
InitX, InitY: Integer;
Animation: TAnimation;
procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
+
+ property myid: Integer read arrIdx;
end;
procedure g_Items_LoadData();
procedure g_Items_SaveState(var Mem: TBinMemoryWriter);
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;
+
+procedure g_Item_EmitPickupSound (idx: Integer); // at item position
+procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer);
+
+
+type
+ TItemEachAliveCB = function (it: PItem): Boolean is nested; // return `true` to stop
+
+function g_Items_ForEachAlive (cb: TItemEachAliveCB; backwards: Boolean=false): Boolean;
+
+
var
- gItems: Array of TItem = nil;
gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
gMaxDist: Integer = 1;
ITEM_RESPAWNTIME: Integer = 60 * 36;
uses
g_basic, e_graphics, g_sound, g_main, g_gfx, g_map,
Math, g_game, g_triggers, g_console, SysUtils, g_player, g_net, g_netmsg,
- e_log;
+ e_log,
+ g_grid, z_aabbtree, binheap;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+var
+ itemTree: TDynAABBTree = nil;
+ ggItems: Array of TItem = nil;
+ freeIds: TBinaryHeapInt = nil; // free item ids
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_ItemValidId (idx: Integer): Boolean; inline;
+begin
+ result := false;
+ if (idx < 0) or (idx > High(ggItems)) then exit;
+ if (ggItems[idx].treeNode = -1) then exit;
+ result := true;
+end;
+
+
+function g_ItemByIdx (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');
+end;
+
+function g_ItemObjByIdx (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');
+ result := @ggItems[idx].Obj;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+ TDynAABBTreeItem = class(TDynAABBTree)
+ function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; override;
+ end;
+
+function TDynAABBTreeItem.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean;
+var
+ it: PItem;
+begin
+ result := false;
+ if (flesh = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
+ //if not g_ItemValidId(tag) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
+ if (tag < 0) or (tag > High(ggItems)) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
+ it := @ggItems[tag];
+ if (it.Obj.Rect.Width < 1) or (it.Obj.Rect.Height < 1) then exit;
+ aabb := AABB2D.Create(it.Obj.X, it.Obj.Y, it.Obj.X+it.Obj.Rect.Width, it.Obj.Y+it.Obj.Rect.Height);
+ if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
+ result := true;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure TItem.positionChanged ();
+var
+ x, y: Integer;
+begin
+ if (treeNode = -1) then
+ begin
+ treeNode := itemTree.insertObject(itemTree{doesn't matter}, arrIdx, true); // static object
+ itemTree.getNodeXY(treeNode, x, y);
+ {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('item #%d: inserted into the tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
+ end
+ else
+ begin
+ itemTree.getNodeXY(treeNode, x, y);
+ if (Obj.X = x) and (Obj.Y = y) then exit; // nothing to do
+ {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('item #%d: updating tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
+
+ {$IFDEF TRUE}
+ itemTree.updateObject(treeNode);
+ {$ELSE}
+ itemTree.removeObject(treeNode);
+ treeNode := itemTree.insertObject(itemTree{doesn't matter}, arrIdx, true); // static object
+ {$ENDIF}
+
+ itemTree.getNodeXY(treeNode, x, y);
+ {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('item #%d: updated tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
+ end;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
const
ITEM_SIGNATURE = $4D455449; // 'ITEM'
g_Texture_CreateWADEx('ITEM_JETPACK', GameWAD+':TEXTURES\JETPACK');
InitTextures();
+
+ itemTree := TDynAABBTreeItem.Create();
+ freeIds := binHeapNewIntLess();
end;
+
procedure g_Items_FreeData();
begin
e_WriteLog('Releasing items data...', MSG_NOTIFY);
g_Texture_Delete('ITEM_WEAPON_KASTET');
g_Texture_Delete('ITEM_MEDKIT_BLACK');
g_Texture_Delete('ITEM_JETPACK');
+
+ itemTree.Free();
+ freeIds.Free();
end;
-function FindItem(): DWORD;
+
+procedure releaseItem (idx: Integer);
var
- i: Integer;
+ it: PItem;
begin
- if gItems <> nil then
- for i := 0 to High(gItems) do
- if gItems[i].ItemType = ITEM_NONE then
- begin
- Result := i;
- Exit;
- end;
+ 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.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency');
+ itemTree.removeObject(it.treeNode);
+ it.treeNode := -1;
+ if (it.Animation <> nil) then
+ begin
+ it.Animation.Free();
+ it.Animation := nil;
+ end;
+ it.Live := False;
+ it.SpawnTrigger := -1;
+ it.ItemType := ITEM_NONE;
+ freeIds.insert(it.arrIdx);
+end;
- if gItems = nil then
- begin
- SetLength(gItems, 32);
- Result := 0;
- end
- else
+
+function allocItem (): DWORD;
+var
+ i, olen: Integer;
+ it: PItem;
+begin
+ if (freeIds.count = 0) then
+ begin
+ // no free slots
+ olen := Length(ggItems);
+ SetLength(ggItems, olen+64);
+ for i := olen to High(ggItems) do
begin
- Result := High(gItems) + 1;
- SetLength(gItems, Length(gItems) + 32);
+ it := @ggItems[i];
+ it.treeNode := -1;
+ it.arrIdx := i;
+ it.ItemType := ITEM_NONE;
+ it.Animation := nil;
+ it.Live := false;
+ it.SpawnTrigger := -1;
+ it.Respawnable := false;
+ freeIds.insert(i);
end;
+ end;
+
+ result := freeIds.front;
+ freeIds.popFront();
+
+ if (result > High(ggItems)) then raise Exception.Create('allocItem: freeid list corrupted');
+ if (ggItems[result].arrIdx <> result) then raise Exception.Create('allocItem: arrIdx inconsistency');
end;
-procedure g_Items_Init();
+
+// it will be slow if the slot is free (we have to rebuild the heap)
+function wantItemSlot (slot: Integer): Integer;
var
- a, b: Integer;
+ i, olen: Integer;
+ it: PItem;
+ rebuildFreeList: Boolean = true;
begin
- if gMapInfo.Height > gPlayerScreenSize.Y then
- a := gMapInfo.Height - gPlayerScreenSize.Y
- else
- a := gMapInfo.Height;
+ if (slot < 0) or (slot > $0fffffff) then raise Exception.Create('wantItemSlot: bad item slot request');
+ // do we need to grow item storate?
+ olen := Length(ggItems);
+ if (slot >= olen) then
+ begin
+ // need more spice!
+ SetLength(ggItems, slot+64);
+ // add free slots to free list
+ for i := olen to High(ggItems) do
+ begin
+ it := @ggItems[i];
+ it.treeNode := -1;
+ it.arrIdx := i;
+ it.ItemType := ITEM_NONE;
+ it.Animation := nil;
+ it.Live := false;
+ it.SpawnTrigger := -1;
+ it.Respawnable := false;
+ if (i <> slot) then freeIds.insert(i);
+ end;
+ rebuildFreeList := false;
+ end;
- if gMapInfo.Width > gPlayerScreenSize.X then
- b := gMapInfo.Width - gPlayerScreenSize.X
+ it := @ggItems[slot];
+ if (it.treeNode = -1) then
+ begin
+ // this is unused slot; get it, and rebuild id list
+ if rebuildFreeList then
+ begin
+ freeIds.clear();
+ for i := 0 to High(ggItems) do
+ begin
+ if (i <> slot) and (ggItems[i].treeNode = -1) then freeIds.insert(i);
+ end;
+ end;
+ end
else
- b := gMapInfo.Width;
+ begin
+ // it will be readded
+ itemTree.removeObject(it.treeNode);
+ it.treeNode := -1;
+ end;
+
+ result := slot;
+end;
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure g_Items_Init ();
+var
+ a, b: Integer;
+begin
+ if gMapInfo.Height > gPlayerScreenSize.Y then a := gMapInfo.Height-gPlayerScreenSize.Y else a := gMapInfo.Height;
+ if gMapInfo.Width > gPlayerScreenSize.X then b := gMapInfo.Width-gPlayerScreenSize.X else b := gMapInfo.Width;
gMaxDist := Trunc(Hypot(a, b));
end;
-procedure g_Items_Free();
+
+procedure g_Items_Free ();
var
i: Integer;
begin
- if gItems <> nil then
+ if (ggItems <> nil) then
begin
- for i := 0 to High(gItems) do
- gItems[i].Animation.Free();
- gItems := nil;
+ for i := 0 to High(ggItems) do ggItems[i].Animation.Free();
+ ggItems := nil;
end;
+ if (itemTree <> nil) then itemTree.reset();
+ freeIds.clear();
end;
-function g_Items_Create(X, Y: Integer; ItemType: Byte;
+
+function g_Items_Create (X, Y: Integer; ItemType: Byte;
Fall, Respawnable: Boolean; AdjCoord: Boolean = False; ForcedID: Integer = -1): DWORD;
var
find_id: DWORD;
ID: DWORD;
+ it: PItem;
begin
- if ForcedID < 0 then
- find_id := FindItem()
- else
- begin
- find_id := ForcedID;
- if Integer(find_id) > High(gItems) then
- SetLength(gItems, find_id + 32);
- end;
+ if ForcedID < 0 then find_id := allocItem() else find_id := wantItemSlot(ForcedID);
+
+ {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('allocated item #%d', [Integer(find_id)]), MSG_NOTIFY);{$ENDIF}
+
+ it := @ggItems[find_id];
+
+ it.ItemType := ItemType;
+ it.Respawnable := Respawnable;
+ if g_Game_IsServer and (ITEM_RESPAWNTIME = 0) then it.Respawnable := False;
+ it.InitX := X;
+ it.InitY := Y;
+ it.RespawnTime := 0;
+ it.Fall := Fall;
+ it.Live := True;
+ it.QuietRespawn := False;
- gItems[find_id].ItemType := ItemType;
- gItems[find_id].Respawnable := Respawnable;
- if g_Game_IsServer and (ITEM_RESPAWNTIME = 0) then
- gItems[find_id].Respawnable := False;
- gItems[find_id].InitX := X;
- gItems[find_id].InitY := Y;
- gItems[find_id].RespawnTime := 0;
- gItems[find_id].Fall := Fall;
- gItems[find_id].Live := True;
- gItems[find_id].QuietRespawn := False;
-
- g_Obj_Init(@gItems[find_id].Obj);
- gItems[find_id].Obj.X := X;
- gItems[find_id].Obj.Y := Y;
- gItems[find_id].Obj.Rect.Width := ITEMSIZE[ItemType][0];
- gItems[find_id].Obj.Rect.Height := ITEMSIZE[ItemType][1];
-
- gItems[find_id].Animation := nil;
- gItems[find_id].SpawnTrigger := -1;
-
-// Êîîðäèíàòû îòíîñèòåëüíî öåíòðà íèæíåãî ðåáðà:
+ 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;
+ it.Obj.Rect.Width := ITEMSIZE[ItemType][0];
+ it.Obj.Rect.Height := ITEMSIZE[ItemType][1];
+
+ it.Animation := nil;
+ it.SpawnTrigger := -1;
+
+ // Êîîðäèíàòû îòíîñèòåëüíî öåíòðà íèæíåãî ðåáðà
if AdjCoord then
- with gItems[find_id] do
+ begin
+ with it^ do
begin
Obj.X := X - (Obj.Rect.Width div 2);
Obj.Y := Y - Obj.Rect.Height;
InitX := Obj.X;
InitY := Obj.Y;
end;
+ end;
-// Óñòàíîâêà àíèìàöèè:
- with gItems[find_id] do
- begin
- case ItemType of
- ITEM_ARMOR_GREEN:
- if g_Frames_Get(ID, 'FRAMES_ITEM_ARMORGREEN') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_ARMOR_BLUE:
- if g_Frames_Get(ID, 'FRAMES_ITEM_ARMORBLUE') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_SPHERE_BLUE:
- if g_Frames_Get(ID, 'FRAMES_ITEM_BLUESPHERE') then
- Animation := TAnimation.Create(ID, True, 15);
- ITEM_SPHERE_WHITE:
- if g_Frames_Get(ID, 'FRAMES_ITEM_WHITESPHERE') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_INVUL:
- if g_Frames_Get(ID, 'FRAMES_ITEM_INVUL') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_INVIS:
- if g_Frames_Get(ID, 'FRAMES_ITEM_INVIS') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_BOTTLE:
- if g_Frames_Get(ID, 'FRAMES_ITEM_BOTTLE') then
- Animation := TAnimation.Create(ID, True, 20);
- ITEM_HELMET:
- if g_Frames_Get(ID, 'FRAMES_ITEM_HELMET') then
- Animation := TAnimation.Create(ID, True, 20);
- end;
+ // Óñòàíîâêà àíèìàöèè
+ case it.ItemType of
+ ITEM_ARMOR_GREEN: if g_Frames_Get(ID, 'FRAMES_ITEM_ARMORGREEN') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_ARMOR_BLUE: if g_Frames_Get(ID, 'FRAMES_ITEM_ARMORBLUE') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_SPHERE_BLUE: if g_Frames_Get(ID, 'FRAMES_ITEM_BLUESPHERE') then it.Animation := TAnimation.Create(ID, True, 15);
+ ITEM_SPHERE_WHITE: if g_Frames_Get(ID, 'FRAMES_ITEM_WHITESPHERE') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_INVUL: if g_Frames_Get(ID, 'FRAMES_ITEM_INVUL') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_INVIS: if g_Frames_Get(ID, 'FRAMES_ITEM_INVIS') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_BOTTLE: if g_Frames_Get(ID, 'FRAMES_ITEM_BOTTLE') then it.Animation := TAnimation.Create(ID, True, 20);
+ ITEM_HELMET: if g_Frames_Get(ID, 'FRAMES_ITEM_HELMET') then it.Animation := TAnimation.Create(ID, True, 20);
end;
- Result := find_id;
+ it.positionChanged();
+
+ result := find_id;
end;
-procedure g_Items_Update();
+
+procedure g_Items_Update ();
var
i, j, k: Integer;
- ID: DWORD;
+ ID: DWord;
Anim: TAnimation;
m: Word;
r, nxt: Boolean;
begin
- if gItems <> nil then
- for i := 0 to High(gItems) do
- if gItems[i].ItemType <> ITEM_NONE then
- with gItems[i] do
+ if (ggItems = nil) then exit;
+
+ for i := 0 to High(ggItems) do
+ begin
+ if (ggItems[i].ItemType = ITEM_NONE) then continue;
+
+ with ggItems[i] do
+ begin
+ nxt := False;
+
+ if Live then
+ begin
+ if Fall then
begin
- nxt := False;
+ m := g_Obj_Move(@Obj, True, True);
+ positionChanged(); // this updates spatial accelerators
+
+ // Ñîïðîòèâëåíèå âîçäóõà
+ if gTime mod (GAME_TICK*2) = 0 then Obj.Vel.X := z_dec(Obj.Vel.X, 1);
- if Live then
+ // Åñëè âûïàë çà êàðòó
+ if WordBool(m and MOVE_FALLOUT) then
begin
- if Fall then
+ if SpawnTrigger = -1 then
begin
- m := g_Obj_Move(@Obj, True, True);
- positionChanged(); // this updates spatial accelerators
-
- // Ñîïðîòèâëåíèå âîçäóõà:
- if gTime mod (GAME_TICK*2) = 0 then
- Obj.Vel.X := z_dec(Obj.Vel.X, 1);
- // Åñëè âûïàë çà êàðòó:
- if WordBool(m and MOVE_FALLOUT) then
- begin
- if SpawnTrigger = -1 then
- g_Items_Pick(i)
- else begin
- g_Items_Remove(i);
- if g_Game_IsServer and g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
- end;
- Continue;
- end;
- end;
-
- // Åñëè èãðîêè ïîáëèçîñòè:
- if gPlayers <> nil then
+ g_Items_Pick(i);
+ end
+ else
begin
- j := Random(Length(gPlayers)) - 1;
-
- for k := 0 to High(gPlayers) do
- begin
- Inc(j);
- if j > High(gPlayers) then
- j := 0;
-
- if (gPlayers[j] <> nil) and gPlayers[j].Live and
- g_Obj_Collide(@gPlayers[j].Obj, @Obj) then
- begin
- if g_Game_IsClient then Continue;
-
- if not gPlayers[j].PickItem(ItemType, Respawnable, r) then
- Continue;
-
- if g_Game_IsNet then MH_SEND_PlayerStats(gPlayers[j].UID);
-
-{
- Doom 2D: Original:
- 1. I_NONE,I_CLIP,I_SHEL,I_ROCKET,I_CELL,I_AMMO,I_SBOX,I_RBOX,I_CELP,I_BPACK,I_CSAW,I_SGUN,I_SGUN2,I_MGUN,I_LAUN,I_PLAS,I_BFG,I_GUN2
- +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
-}
-
- if gSoundEffectsDF then
- begin
- if ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
- ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y)
- else
- if ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
- ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
- ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER,
- ITEM_AMMO_BACKPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y)
- else
- g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y);
- end
- else
- begin
- if ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
- ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y)
- else
- if ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
- ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
- ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER] then
- g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y)
- else
- g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
- gPlayers[j].Obj.X, gPlayers[j].Obj.Y);
- end;
-
- // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì:
- if r then
- begin
- if not Respawnable then
- g_Items_Remove(i)
- else
- g_Items_Pick(i);
-
- if g_Game_IsNet then MH_SEND_ItemDestroy(False, i);
- nxt := True;
- Break;
- end;
- end;
- end;
+ g_Items_Remove(i);
+ if g_Game_IsServer and g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
end;
-
- if nxt then
- Continue;
+ continue;
end;
+ end;
+
+ // Åñëè èãðîêè ïîáëèçîñòè
+ if (gPlayers <> nil) then
+ begin
+ j := Random(Length(gPlayers))-1;
- if Respawnable and g_Game_IsServer then
+ for k := 0 to High(gPlayers) do
begin
- DecMin(RespawnTime, 0);
- if (RespawnTime = 0) and (not Live) then
+ Inc(j);
+ if j > High(gPlayers) then j := 0;
+
+ if (gPlayers[j] <> nil) and gPlayers[j].Live and g_Obj_Collide(@gPlayers[j].Obj, @Obj) then
begin
- if not QuietRespawn then
- g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', InitX, InitY);
+ if g_Game_IsClient then continue;
+
+ if not gPlayers[j].PickItem(ItemType, Respawnable, r) then continue;
+
+ if g_Game_IsNet then MH_SEND_PlayerStats(gPlayers[j].UID);
- if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
+ {
+ Doom 2D: Original:
+ 1. I_NONE,I_CLIP,I_SHEL,I_ROCKET,I_CELL,I_AMMO,I_SBOX,I_RBOX,I_CELP,I_BPACK,I_CSAW,I_SGUN,I_SGUN2,I_MGUN,I_LAUN,I_PLAS,I_BFG,I_GUN2
+ +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);
+
+ // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì
+ if r then
begin
- Anim := TAnimation.Create(ID, False, 4);
- g_GFX_OnceAnim(InitX+(Obj.Rect.Width div 2)-16, InitY+(Obj.Rect.Height div 2)-16, Anim);
- Anim.Free();
+ if not Respawnable then g_Items_Remove(i) else g_Items_Pick(i);
+ if g_Game_IsNet then MH_SEND_ItemDestroy(False, i);
+ nxt := True;
+ break;
end;
+ end;
+ end;
+ end;
- Obj.X := InitX;
- Obj.Y := InitY;
- Obj.Vel.X := 0;
- Obj.Vel.Y := 0;
- Obj.Accel.X := 0;
- Obj.Accel.Y := 0;
- positionChanged(); // this updates spatial accelerators
+ if nxt then continue;
+ end;
- Live := True;
+ if Respawnable and g_Game_IsServer then
+ begin
+ DecMin(RespawnTime, 0);
+ if (RespawnTime = 0) and (not Live) then
+ begin
+ if not QuietRespawn then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', InitX, InitY);
- if g_Game_IsNet then MH_SEND_ItemSpawn(QuietRespawn, i);
- QuietRespawn := False;
- end;
+ if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
+ begin
+ Anim := TAnimation.Create(ID, False, 4);
+ g_GFX_OnceAnim(InitX+(Obj.Rect.Width div 2)-16, InitY+(Obj.Rect.Height div 2)-16, Anim);
+ Anim.Free();
end;
- if Animation <> nil then
- Animation.Update();
+ Obj.X := InitX;
+ Obj.Y := InitY;
+ Obj.Vel.X := 0;
+ Obj.Vel.Y := 0;
+ Obj.Accel.X := 0;
+ Obj.Accel.Y := 0;
+ positionChanged(); // this updates spatial accelerators
+
+ Live := true;
+
+ if g_Game_IsNet then MH_SEND_ItemSpawn(QuietRespawn, i);
+ QuietRespawn := false;
end;
+ end;
+
+ if (Animation <> nil) then Animation.Update();
+ end;
+ end;
end;
-procedure g_Items_Draw();
+
+procedure g_Items_Draw ();
var
i: Integer;
begin
- if gItems <> nil then
- for i := 0 to High(gItems) do
- if gItems[i].Live then
- with gItems[i] do
- if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height,
- sX, sY, sWidth, sHeight) then
- begin
- if Animation = nil then
- e_Draw(gItemsTexturesID[ItemType], Obj.X, Obj.Y, 0, True, False)
- else
- Animation.Draw(Obj.X, Obj.Y, M_NONE);
+ if (ggItems = nil) then exit;
- if g_debug_Frames then
- begin
- e_DrawQuad(Obj.X+Obj.Rect.X,
- Obj.Y+Obj.Rect.Y,
- Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
- Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
- 0, 255, 0);
- end;
- end;
+ for i := 0 to High(ggItems) do
+ begin
+ if not ggItems[i].Live then continue;
+
+ with ggItems[i] do
+ begin
+ if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, sX, sY, sWidth, sHeight) then
+ begin
+ if (Animation = nil) then
+ begin
+ e_Draw(gItemsTexturesID[ItemType], Obj.X, Obj.Y, 0, true, false);
+ end
+ else
+ begin
+ Animation.Draw(Obj.X, Obj.Y, M_NONE);
+ end;
+
+ if g_debug_Frames then
+ begin
+ e_DrawQuad(Obj.X+Obj.Rect.X,
+ Obj.Y+Obj.Rect.Y,
+ Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
+ Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
+ 0, 255, 0);
+ end;
+ end;
+ end;
+ end;
end;
-procedure g_Items_Pick(ID: DWORD);
+
+procedure g_Items_Pick (ID: DWORD);
begin
- gItems[ID].Live := False;
- gItems[ID].RespawnTime := ITEM_RESPAWNTIME;
+ ggItems[ID].Live := false;
+ ggItems[ID].RespawnTime := ITEM_RESPAWNTIME;
end;
-procedure g_Items_Remove(ID: DWORD);
+
+procedure g_Items_Remove (ID: DWORD);
+var
+ it: PItem;
+ trig: Integer;
+ x, y: Integer;
begin
- gItems[ID].ItemType := ITEM_NONE;
+ if not g_ItemValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id');
- if gItems[ID].Animation <> nil then
- begin
- gItems[ID].Animation.Free();
- gItems[ID].Animation := nil;
- end;
+ it := @ggItems[ID];
+ if (it.arrIdx <> ID) then raise Exception.Create('g_Items_Remove: arrIdx desync');
- gItems[ID].Live := False;
+ itemTree.getNodeXY(it.treeNode, x, y);
+ {$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('removing item #%d: updating tree; nodeid=%d; x=%d; y=%d (%d,%d)', [it.arrIdx, it.treeNode, x, y, it.Obj.X, it.Obj.Y]), MSG_NOTIFY);{$ENDIF}
- if gItems[ID].SpawnTrigger > -1 then
- begin
- g_Triggers_DecreaseSpawner(gItems[ID].SpawnTrigger);
- gItems[ID].SpawnTrigger := -1;
- end;
+ trig := it.SpawnTrigger;
+
+ releaseItem(ID);
+
+ if (trig > -1) then g_Triggers_DecreaseSpawner(trig);
end;
-procedure g_Items_SaveState(var Mem: TBinMemoryWriter);
+
+procedure g_Items_SaveState (var Mem: TBinMemoryWriter);
var
count, i: Integer;
sig: DWORD;
begin
-// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ:
+ // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
count := 0;
- if gItems <> nil then
- for i := 0 to High(gItems) do
- if gItems[i].ItemType <> ITEM_NONE then
- count := count + 1;
+ if (ggItems <> nil) then
+ begin
+ for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) then Inc(count);
+ end;
Mem := TBinMemoryWriter.Create((count+1) * 60);
-// Êîëè÷åñòâî ïðåäìåòîâ:
+ // Êîëè÷åñòâî ïðåäìåòîâ
Mem.WriteInt(count);
- if count = 0 then
- Exit;
+ if (count = 0) then exit;
- for i := 0 to High(gItems) do
- if gItems[i].ItemType <> ITEM_NONE then
+ for i := 0 to High(ggItems) do
+ begin
+ if (ggItems[i].ItemType <> ITEM_NONE) then
begin
- // Ñèãíàòóðà ïðåäìåòà:
+ // Ñèãíàòóðà ïðåäìåòà
sig := ITEM_SIGNATURE; // 'ITEM'
Mem.WriteDWORD(sig);
- // Òèï ïðåäìåòà:
- Mem.WriteByte(gItems[i].ItemType);
- // Åñòü ëè ðåñïàóí:
- Mem.WriteBoolean(gItems[i].Respawnable);
- // Êîîðäèíàòû ðåñïóíà:
- Mem.WriteInt(gItems[i].InitX);
- Mem.WriteInt(gItems[i].InitY);
- // Âðåìÿ äî ðåñïàóíà:
- Mem.WriteWord(gItems[i].RespawnTime);
- // Ñóùåñòâóåò ëè ýòîò ïðåäìåò:
- Mem.WriteBoolean(gItems[i].Live);
- // Ìîæåò ëè îí ïàäàòü:
- Mem.WriteBoolean(gItems[i].Fall);
- // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò:
- Mem.WriteInt(gItems[i].SpawnTrigger);
- // Îáúåêò ïðåäìåòà:
- Obj_SaveState(@gItems[i].Obj, Mem);
+ // Òèï ïðåäìåòà
+ Mem.WriteByte(ggItems[i].ItemType);
+ // Åñòü ëè ðåñïàóí
+ Mem.WriteBoolean(ggItems[i].Respawnable);
+ // Êîîðäèíàòû ðåñïóíà
+ Mem.WriteInt(ggItems[i].InitX);
+ Mem.WriteInt(ggItems[i].InitY);
+ // Âðåìÿ äî ðåñïàóíà
+ Mem.WriteWord(ggItems[i].RespawnTime);
+ // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
+ Mem.WriteBoolean(ggItems[i].Live);
+ // Ìîæåò ëè îí ïàäàòü
+ Mem.WriteBoolean(ggItems[i].Fall);
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
+ Mem.WriteInt(ggItems[i].SpawnTrigger);
+ // Îáúåêò ïðåäìåòà
+ Obj_SaveState(@ggItems[i].Obj, Mem);
end;
+ end;
end;
-procedure g_Items_LoadState(var Mem: TBinMemoryReader);
+
+procedure g_Items_LoadState (var Mem: TBinMemoryReader);
var
count, i, a: Integer;
sig: DWORD;
b: Byte;
begin
- if Mem = nil then
- Exit;
+ if (Mem = nil) then exit;
g_Items_Free();
-// Êîëè÷åñòâî ïðåäìåòîâ:
+ // Êîëè÷åñòâî ïðåäìåòîâ
Mem.ReadInt(count);
- if count = 0 then
- Exit;
+ if (count = 0) then Exit;
for a := 0 to count-1 do
begin
- // Ñèãíàòóðà ïðåäìåòà:
+ // Ñèãíàòóðà ïðåäìåòà
Mem.ReadDWORD(sig);
- if sig <> ITEM_SIGNATURE then // 'ITEM'
- begin
- raise EBinSizeError.Create('g_Items_LoadState: Wrong Item Signature');
- end;
- // Òèï ïðåäìåòà:
+ if (sig <> ITEM_SIGNATURE) then raise EBinSizeError.Create('g_Items_LoadState: Wrong Item Signature'); // 'ITEM'
+ // Òèï ïðåäìåòà
Mem.ReadByte(b);
- // Ñîçäàåì ïðåäìåò:
+ // Ñîçäàåì ïðåäìåò
i := g_Items_Create(0, 0, b, False, False);
- // Åñòü ëè ðåñïàóí:
- Mem.ReadBoolean(gItems[i].Respawnable);
- // Êîîðäèíàòû ðåñïóíà:
- Mem.ReadInt(gItems[i].InitX);
- Mem.ReadInt(gItems[i].InitY);
- // Âðåìÿ äî ðåñïàóíà:
- Mem.ReadWord(gItems[i].RespawnTime);
- // Ñóùåñòâóåò ëè ýòîò ïðåäìåò:
- Mem.ReadBoolean(gItems[i].Live);
- // Ìîæåò ëè îí ïàäàòü:
- Mem.ReadBoolean(gItems[i].Fall);
- // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò:
- Mem.ReadInt(gItems[i].SpawnTrigger);
- // Îáúåêò ïðåäìåòà:
- Obj_LoadState(@gItems[i].Obj, Mem);
+ // Åñòü ëè ðåñïàóí
+ Mem.ReadBoolean(ggItems[i].Respawnable);
+ // Êîîðäèíàòû ðåñïóíà
+ Mem.ReadInt(ggItems[i].InitX);
+ Mem.ReadInt(ggItems[i].InitY);
+ // Âðåìÿ äî ðåñïàóíà
+ Mem.ReadWord(ggItems[i].RespawnTime);
+ // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
+ Mem.ReadBoolean(ggItems[i].Live);
+ // Ìîæåò ëè îí ïàäàòü
+ Mem.ReadBoolean(ggItems[i].Fall);
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
+ Mem.ReadInt(ggItems[i].SpawnTrigger);
+ // Îáúåêò ïðåäìåòà
+ Obj_LoadState(@ggItems[i].Obj, Mem);
end;
end;
-procedure TItem.positionChanged ();
+procedure g_Items_RestartRound ();
+var
+ i: Integer;
+ it: PItem;
+begin
+ for i := 0 to High(ggItems) do
+ begin
+ it := @ggItems[i];
+ if it.Respawnable then
+ begin
+ it.QuietRespawn := True;
+ it.RespawnTime := 0;
+ end
+ else
+ begin
+ g_Items_Remove(i);
+ if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
+ end;
+ end;
+end;
+
+
+function g_Items_ForEachAlive (cb: TItemEachAliveCB; backwards: Boolean=false): Boolean;
+var
+ idx: Integer;
+begin
+ result := false;
+ if (ggItems = nil) or not assigned(cb) then exit;
+
+ if backwards then
+ begin
+ for idx := High(ggItems) downto 0 do
+ begin
+ if ggItems[idx].Live then
+ begin
+ result := cb(@ggItems[idx]);
+ if result then exit;
+ end;
+ end;
+ end
+ else
+ begin
+ for idx := 0 to High(ggItems) do
+ begin
+ if ggItems[idx].Live then
+ begin
+ result := cb(@ggItems[idx]);
+ if result then exit;
+ end;
+ end;
+ end;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure g_Item_EmitPickupSound (idx: Integer);
+var
+ it: PItem;
begin
+ if not g_ItemValidId(idx) then exit;
+ it := @ggItems[idx];
+ g_Item_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
+end;
+
+procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer);
+var
+ it: PItem;
+begin
+ if not g_ItemValidId(idx) then exit;
+
+ it := @ggItems[idx];
+ if gSoundEffectsDF then
+ begin
+ if it.ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
+ ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ', x, y);
+ end
+ else if it.ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
+ ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
+ ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER,
+ ITEM_AMMO_BACKPACK] then
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON', x, y);
+ end
+ else
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETITEM', x, y);
+ end;
+ end
+ else
+ begin
+ if it.ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
+ ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ', x, y);
+ end
+ else if it.ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
+ ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
+ ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER] then
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON', x, y);
+ end
+ else
+ begin
+ g_Sound_PlayExAt('SOUND_ITEM_GETITEM', x, y);
+ end;
+ end;
end;
end.
diff --git a/src/game/g_map.pas b/src/game/g_map.pas
index f3e15a21ef10847f71df7a34d342ff75b3dece9b..cd2aff9951cc4908138407a965ef2acb17d5b77d 100644 (file)
--- a/src/game/g_map.pas
+++ b/src/game/g_map.pas
FLAG_SIGNATURE = $47414C46; // 'FLAG'
-
function panelTypeToTag (panelType: Word): Integer;
begin
case panelType of
type
TDynAABBTreeMap = class(TDynAABBTree)
- function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; override;
+ function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; override;
end;
-function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean;
+function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean;
var
pan: TPanel;
begin
index 0d8f3841d1eab4f1127def30e016d7eabdd7852b..690626078d17713d17936ec942a69007ca21ae1d 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.pas
it := g_Items_Create(FObj.X + (FObj.Rect.Width div 2),
FObj.Y + (FObj.Rect.Height div 2),
c, True, False);
- g_Obj_Push(@gItems[it].Obj, (FObj.Vel.X div 2)-3+Random(7),
- (FObj.Vel.Y div 2)-Random(4));
+ g_Obj_Push(g_ItemObjByIdx(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);
diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas
index 237c9dec3e939dd4148dc572683ef36042eaa833..33273f896a9ab3e339d1ca731a0a4ec53a129959 100644 (file)
--- a/src/game/g_netmsg.pas
+++ b/src/game/g_netmsg.pas
// GAME (SEND)
procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
+
+ function sendItemRespawn (it: PItem): Boolean;
+ begin
+ result := false; // don't stop
+ MH_SEND_ItemSpawn(True, it.myid, ID);
+ end;
+
var
I: Integer;
begin
if gPlayers <> nil then
+ begin
for I := Low(gPlayers) to High(gPlayers) do
+ begin
if gPlayers[I] <> nil then
begin
if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
end;
-
- if gItems <> nil then
- begin
- for I := High(gItems) downto Low(gItems) do
- if gItems[I].Live then
- MH_SEND_ItemSpawn(True, I, ID);
+ end;
end;
+ g_Items_ForEachAlive(sendItemRespawn, true); // backwards
+
if gMonsters <> nil then
for I := 0 to High(gMonsters) do
if gMonsters[I] <> nil then
// ITEM (SEND)
procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
+var
+ it: PItem;
begin
+ it := g_ItemByIdx(IID);
+
e_Buffer_Write(@NetOut, Byte(NET_MSG_ISPAWN));
e_Buffer_Write(@NetOut, IID);
e_Buffer_Write(@NetOut, Byte(Quiet));
- e_Buffer_Write(@NetOut, gItems[IID].ItemType);
- e_Buffer_Write(@NetOut, Byte(gItems[IID].Fall));
- e_Buffer_Write(@NetOut, Byte(gItems[IID].Respawnable));
- e_Buffer_Write(@NetOut, gItems[IID].Obj.X);
- e_Buffer_Write(@NetOut, gItems[IID].Obj.Y);
- e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.X);
- e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.Y);
+ e_Buffer_Write(@NetOut, it.ItemType);
+ e_Buffer_Write(@NetOut, Byte(it.Fall));
+ e_Buffer_Write(@NetOut, Byte(it.Respawnable));
+ e_Buffer_Write(@NetOut, it.Obj.X);
+ e_Buffer_Write(@NetOut, it.Obj.Y);
+ e_Buffer_Write(@NetOut, it.Obj.Vel.X);
+ e_Buffer_Write(@NetOut, it.Obj.Vel.Y);
g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
end;
T: Byte;
Quiet, Fall{, Resp}: Boolean;
Anim: TAnimation;
+ it: PItem;
begin
if not gGameOn then Exit;
ID := e_Raw_Read_Word(P);
VY := e_Raw_Read_LongInt(P);
g_Items_Create(X, Y, T, Fall, False, False, ID);
- gItems[ID].Obj.Vel.X := VX;
- gItems[ID].Obj.Vel.Y := VY;
+
+ it := g_ItemByIdx(ID);
+ it.Obj.Vel.X := VX;
+ it.Obj.Vel.Y := VY;
if not Quiet then
begin
if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
begin
Anim := TAnimation.Create(AID, False, 4);
- g_GFX_OnceAnim(X+(gItems[ID].Obj.Rect.Width div 2)-16, Y+(gItems[ID].Obj.Rect.Height div 2)-16, Anim);
+ g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim);
Anim.Free();
end;
end;
if not gGameOn then Exit;
ID := e_Raw_Read_Word(P);
Quiet := e_Raw_Read_Byte(P) <> 0;
- if gItems = nil then Exit;
- if (ID > High(gItems)) then Exit;
- if not Quiet then
- if gSoundEffectsDF then
- begin
- if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
- ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
- gItems[ID].Obj.X, gItems[ID].Obj.Y)
- else
- if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
- ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
- ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER,
- ITEM_AMMO_BACKPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
- gItems[ID].Obj.X, gItems[ID].Obj.Y)
- else
- g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
- gItems[ID].Obj.X, gItems[ID].Obj.Y);
- end
- else
- begin
- if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
- ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
- g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
- gItems[ID].Obj.X, gItems[ID].Obj.Y)
- else
- if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
- ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
- ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER] then
- g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
- gItems[ID].Obj.X, gItems[ID].Obj.Y)
- else
- g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
- gItems[ID].Obj.X, gItems[ID].Obj.Y);
- end;
+ if not g_ItemValidId(ID) then exit;
+
+ if not Quiet then g_Item_EmitPickupSound(ID);
g_Items_Remove(ID);
end;
diff --git a/src/game/g_player.pas b/src/game/g_player.pas
index f4e3b0fff39acd98b14a65fa9bc9bcfe82bdb5d6..0ee9365f2adbee0cf6183a774a83500781d6ea23 100644 (file)
--- a/src/game/g_player.pas
+++ b/src/game/g_player.pas
DoFrags: Boolean;
OldLR: Byte;
KP: TPlayer;
+ it: PItem;
procedure PushItem(t: Byte);
var
id: DWORD;
begin
id := g_Items_Create(FObj.X, FObj.Y, t, True, False);
+ it := g_ItemByIdx(id);
if KillType = K_EXTRAHARDKILL then // -7..+7; -8..0
begin
- g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-7+Random(15),
- (FObj.Vel.Y div 2)-Random(9));
- gItems[id].positionChanged(); // this updates spatial accelerators
+ g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-7+Random(15),
+ (FObj.Vel.Y div 2)-Random(9));
+ it.positionChanged(); // this updates spatial accelerators
end
else
begin
if KillType = K_HARDKILL then // -5..+5; -5..0
begin
- g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-5+Random(11),
- (FObj.Vel.Y div 2)-Random(6));
+ g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-5+Random(11),
+ (FObj.Vel.Y div 2)-Random(6));
end
else // -3..+3; -3..0
begin
- g_Obj_Push(@gItems[id].Obj, (FObj.Vel.X div 2)-3+Random(7),
- (FObj.Vel.Y div 2)-Random(4));
+ g_Obj_Push(@it.Obj, (FObj.Vel.X div 2)-3+Random(7),
+ (FObj.Vel.Y div 2)-Random(4));
end;
- gItems[id].positionChanged(); // this updates spatial accelerators
+ it.positionChanged(); // this updates spatial accelerators
end;
if g_Game_IsNet and g_Game_IsServer then
index 5283e7008751eee7805ac02aca4c6eff48534159..7238c8046c4bec7b4a75921f1de6f47b38a1807c 100644 (file)
--- a/src/game/g_triggers.pas
+++ b/src/game/g_triggers.pas
Anim: TAnimation;
UIDType: Byte;
TargetUID: Word;
+ it: PItem;
begin
Result := False;
if g_Game_IsClient then
if Data.ItemMax > 0 then
begin
- gItems[iid].SpawnTrigger := ID;
+ it := g_ItemByIdx(iid);
+ it.SpawnTrigger := ID;
Inc(SpawnedCount);
end;
case Data.ItemEffect of
EFFECT_TELEPORT: begin
+ it := g_ItemByIdx(iid);
if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
begin
Anim := TAnimation.Create(FramesID, False, 3);
g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.ItemPos.X, Data.ItemPos.Y);
- g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-32, Anim);
+ g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+ it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
Anim.Free();
end;
if g_Game_IsServer and g_Game_IsNet then
- MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-32, 1,
+ MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+ it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
NET_GFX_TELE);
end;
EFFECT_RESPAWN: begin
+ it := g_ItemByIdx(iid);
if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
begin
Anim := TAnimation.Create(FramesID, False, 4);
g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.ItemPos.X, Data.ItemPos.Y);
- g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-16,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-16, Anim);
+ g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
+ it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
Anim.Free();
end;
if g_Game_IsServer and g_Game_IsNet then
- MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-16,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+(gItems[iid].Obj.Rect.Height div 2)-16, 1,
+ MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
+ it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
NET_GFX_RESPAWN);
end;
EFFECT_FIRE: begin
+ it := g_ItemByIdx(iid);
if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
begin
Anim := TAnimation.Create(FramesID, False, 4);
g_Sound_PlayExAt('SOUND_FIRE', Data.ItemPos.X, Data.ItemPos.Y);
- g_GFX_OnceAnim(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+gItems[iid].Obj.Rect.Height-128, Anim);
+ g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+ it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
Anim.Free();
end;
if g_Game_IsServer and g_Game_IsNet then
- MH_SEND_Effect(gItems[iid].Obj.X+gItems[iid].Obj.Rect.X+(gItems[iid].Obj.Rect.Width div 2)-32,
- gItems[iid].Obj.Y+gItems[iid].Obj.Rect.Y+gItems[iid].Obj.Rect.Height-128, 1,
+ MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
+ it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
NET_GFX_FIRE);
end;
end;
index 5b3fa623d9b13dd14d4f8a6c695d5242f1b4ce50..a64e6322b7cbd98518e4ee9671cdb2a70aaccd94 100644 (file)
--- a/src/game/z_aabbtree.pas
+++ b/src/game/z_aabbtree.pas
//nextNodeId: Integer;
// a node is either a leaf (has data) or is an internal node (has children)
children: array [0..1] of Integer; // left and right child of the node (children[0] = left child)
- //TODO: `flesh` can be united with `children`
- flesh: TTreeFlesh;
// height of the node in the tree (-1 for free nodes)
height: SmallInt;
// fat axis aligned bounding box (AABB) corresponding to the node
aabb: AABB2D;
+ //TODO: `flesh` can be united with `children`
+ flesh: TTreeFlesh;
+ fleshX, fleshY: TreeNumber;
tag: Integer; // just a user-defined tag
public
// return true if the node is a leaf of the tree
function isfree (): Boolean; inline;
property nextNodeId: Integer read parentId write parentId;
//property flesh: Integer read children[0] write children[0];
+
+ procedure dumpToLog ();
end;
TVisitCheckerCB = function (node: PTreeNode): Boolean is nested;
function getNodeObjectId (nodeid: Integer): TTreeFlesh; inline;
procedure getNodeFatAABB (var aabb: AABB2D; nodeid: Integer); inline;
+ // returns `false` if nodeid is not leaf
+ function getNodeXY (nodeid: Integer; out x, y: Integer): Boolean; inline;
+
// return `false` for invalid flesh
- function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; virtual; abstract;
+ function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; virtual; abstract;
// insert an object into the tree
// this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
*
* return `true` if the tree was modified.
*)
- function updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean;
+ function updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
+ function updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
function aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
function pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB): TTreeFlesh;
aabb.maxY := 0;
end;
+procedure TDynAABBTree.TTreeNode.dumpToLog ();
+begin
+ e_WriteLog(Format('NODE: parentId=%d; children=[%d,%d]; height=%d; tag=%d; fleshX=%d; fleshY=%d; aabb=(%d,%d)-(%d,%d)',
+ [parentId, children[0], children[1], Integer(height), tag, fleshX, fleshY, aabb.minX, aabb.minY, aabb.maxX, aabb.maxY]),
+ MSG_NOTIFY);
+end;
+
// ////////////////////////////////////////////////////////////////////////// //
// allocate and return a node to use in the tree
begin
{$IFDEF aabbtree_many_asserts}assert(mNodeCount = mAllocCount);{$ENDIF}
// allocate more nodes in the tree
- if (mAllocCount < 32768) then newsz := mAllocCount*2 else newsz := mAllocCount+16384;
+ if (mAllocCount <= 16384) then newsz := mAllocCount*2 else newsz := mAllocCount+16384;
SetLength(mNodes, newsz);
mAllocCount := newsz;
// initialize the allocated nodes
end;
// get the next free node
freeNodeId := mFreeNodeId;
- {$IFDEF aabbtree_many_asserts}assert((freeNodeId >= mNodeCount) and (freeNodeId < mAllocCount));{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}assert(freeNodeId < mAllocCount);{$ENDIF}
node := @mNodes[freeNodeId];
mFreeNodeId := node.nextNodeId;
node.clear();
node.height := 0;
Inc(mNodeCount);
result := freeNodeId;
+
+ //e_WriteLog(Format('tree: allocated node #%d', [result]), MSG_NOTIFY);
end;
mNodes[nodeId].flesh := nil;
mFreeNodeId := nodeId;
Dec(mNodeCount);
+
+ //e_WriteLog(Format('tree: released node #%d', [nodeId]), MSG_NOTIFY);
end;
function TDynAABBTree.insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
var
nodeId: Integer;
+ node: PTreeNode;
begin
// get the next available node (or allocate new ones if necessary)
nodeId := allocateNode();
+ node := @mNodes[nodeId];
+
// create the fat aabb to use in the tree
- mNodes[nodeId].aabb := AABB2D.Create(aabb);
+ node.aabb := AABB2D.Create(aabb);
if (not staticObject) then
begin
- mNodes[nodeId].aabb.minX -= mExtraGap;
- mNodes[nodeId].aabb.minY -= mExtraGap;
- mNodes[nodeId].aabb.maxX += mExtraGap;
- mNodes[nodeId].aabb.maxY += mExtraGap;
+ node.aabb.minX -= mExtraGap;
+ node.aabb.minY -= mExtraGap;
+ node.aabb.maxX += mExtraGap;
+ node.aabb.maxY += mExtraGap;
end;
// set the height of the node in the tree
- mNodes[nodeId].height := 0;
+ node.height := 0;
// insert the new leaf node in the tree
insertLeafNode(nodeId);
- {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
- {$IFDEF aabbtree_many_asserts}assert(nodeId >= 0);{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}node := @mNodes[nodeId];{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}assert(node.leaf);{$ENDIF}
// return the id of the node
result := nodeId;
{$ENDIF}
if pNode.leaf then
begin
- getFleshAABB(aabb, pNode.flesh);
+ getFleshAABB(aabb, pNode.flesh, pNode.tag);
{$IFDEF aabbtree_use_floats}
e_WriteLog(Format(' LEAF AABB:(%f,%f)-(%f,%f); valid=%d; volume=%f', [aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, Integer(aabb.valid), aabb.volume]), MSG_NOTIFY);
{$ELSE}
@@ -1299,7 +1320,7 @@ function TDynAABBTree.computeTreeHeight (): Integer; begin result := computeHeig
// return the root AABB of the tree
procedure TDynAABBTree.getRootAABB (var aabb: AABB2D);
begin
- {$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mNodeCount));{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mAllocCount));{$ENDIF}
aabb := mNodes[mRootNodeId].aabb;
end;
// WARNING: ids of removed objects can be reused on later insertions!
function TDynAABBTree.isValidId (id: Integer): Boolean;
begin
- result := (id >= 0) and (id < mNodeCount) and (mNodes[id].leaf);
+ result := (id >= 0) and (id < mAllocCount) and (mNodes[id].leaf);
end;
// get object by nodeid; can return nil for invalid ids
function TDynAABBTree.getNodeObjectId (nodeid: Integer): TTreeFlesh;
begin
- if (nodeid >= 0) and (nodeid < mNodeCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := nil;
+ if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := nil;
end;
// get fat object AABB by nodeid; returns random shit for invalid ids
procedure TDynAABBTree.getNodeFatAABB (var aabb: AABB2D; nodeid: Integer);
begin
- if (nodeid >= 0) and (nodeid < mNodeCount) and (not mNodes[nodeid].isfree) then aabb.copyFrom(mNodes[nodeid].aabb) else aabb.setDims(0, 0, 0, 0);
+ if (nodeid >= 0) and (nodeid < mAllocCount) and (not mNodes[nodeid].isfree) then aabb.copyFrom(mNodes[nodeid].aabb) else aabb.setDims(0, 0, 0, 0);
+end;
+
+function TDynAABBTree.getNodeXY (nodeid: Integer; out x, y: Integer): Boolean; inline;
+begin
+ if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then
+ begin
+ result := true;
+ {$IFDEF aabbtree_use_floats}
+ x := round(mNodes[nodeid].fleshX);
+ y := round(mNodes[nodeid].fleshY);
+ {$ELSE}
+ x := mNodes[nodeid].fleshX;
+ y := mNodes[nodeid].fleshY;
+ {$ENDIF}
+ end
+ else
+ begin
+ result := false;
+ x := 0;
+ y := 0;
+ //if (nodeid >= 0) and (nodeid < mAllocCount) then mNodes[nodeid].dumpToLog();
+ end;
end;
function TDynAABBTree.insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer;
var
aabb: AABB2D;
- nodeId: Integer;
+ nodeId, fx, fy: Integer;
begin
- if not getFleshAABB(aabb, flesh) then
+ if not getFleshAABB(aabb, flesh, tag) then
begin
{$IFDEF aabbtree_use_floats}
e_WriteLog(Format('trying to insert FUCKED FLESH:(%f,%f)-(%f,%f); volume=%f; valid=%d', [aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, aabb.volume, Integer(aabb.valid)]), MSG_WARNING);
exit;
end;
//e_WriteLog(Format('inserting AABB:(%f,%f)-(%f,%f); volume=%f; valid=%d', [aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, aabb.volume, Integer(aabb.valid)]), MSG_NOTIFY);
+ fx := aabb.minX;
+ fy := aabb.minY;
nodeId := insertObjectInternal(aabb, staticObject);
{$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
mNodes[nodeId].flesh := flesh;
mNodes[nodeId].tag := tag;
+ mNodes[nodeId].fleshX := fx;
+ mNodes[nodeId].fleshY := fy;
result := nodeId;
end;
// WARNING: ids of removed objects can be reused on later insertions!
procedure TDynAABBTree.removeObject (nodeId: Integer);
begin
- if (nodeId < 0) or (nodeId >= mNodeCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
// remove the node from the tree
removeLeafNode(nodeId);
releaseNode(nodeId);
end;
-function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean;
+function TDynAABBTree.updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
var
newAABB: AABB2D;
+ dispX, dispY: TreeNumber;
begin
- if (nodeId < 0) or (nodeId >= mNodeCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
- if not getFleshAABB(newAABB, mNodes[nodeId].flesh) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
+ if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
+ dispX := newAABB.minX-mNodes[nodeId].fleshX;
+ dispY := newAABB.minY-mNodes[nodeId].fleshY;
+
+ if (dispX < -16) then dispX := -16 else if (dispX > 16) then dispX := 16;
+ if (dispY < -16) then dispY := -16 else if (dispY > 16) then dispY := 16;
+
+ result := updateObject(nodeId, dispX, dispY, forceReinsert);
+end;
+
+function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
+var
+ newAABB: AABB2D;
+ fx, fy: Integer;
+ node: PTreeNode;
+begin
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
+
+ if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
+ if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
+
+ fx := newAABB.minX;
+ fy := newAABB.minY;
+
// if the new AABB is still inside the fat AABB of the node
- if (not forceReinsert) and (mNodes[nodeId].aabb.contains(newAABB)) then begin result := false; exit; end;
+ if (not forceReinsert) and (mNodes[nodeId].aabb.contains(newAABB)) then
+ begin
+ node := @mNodes[nodeId];
+ node.fleshX := fx;
+ node.fleshY := fy;
+ result := false;
+ exit;
+ end;
// if the new AABB is outside the fat AABB, we remove the corresponding node
removeLeafNode(nodeId);
+ node := @mNodes[nodeId];
+
// compute the fat AABB by inflating the AABB with a constant gap
- mNodes[nodeId].aabb := newAABB;
+ node.aabb.copyFrom(newAABB);
+ node.fleshX := fx;
+ node.fleshY := fy;
+
if (not forceReinsert) and ((dispX <> 0) or (dispY <> 0)) then
begin
- mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX-mExtraGap;
- mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY-mExtraGap;
- mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+mExtraGap;
- mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+mExtraGap;
+ node.aabb.minX -= mExtraGap;
+ node.aabb.minY += mExtraGap;
+ node.aabb.maxX += mExtraGap;
+ node.aabb.maxY += mExtraGap;
end;
// inflate the fat AABB in direction of the linear motion of the AABB
if (dispX < 0) then
begin
- mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX+LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
+ node.aabb.minX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
end
else
begin
- mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
+ node.aabb.maxX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
end;
+
if (dispY < 0) then
begin
- mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY+LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
+ node.aabb.minY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
end
else
begin
- mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
+ node.aabb.maxY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
end;
- {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].aabb.contains(newAABB));{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}assert(node.aabb.contains(newAABB));{$ENDIF}
// reinsert the node into the tree
insertLeafNode(nodeId);
if not assigned(cb) then cb := dummycb;
caabb := AABB2D.Create(ax, ay, ax+1, ay+1);
nid := visit(caabb, ModePoint, checker, cb);
- {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mNodeCount) and (mNodes[nid].leaf)));{$ENDIF}
+ {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mAllocCount) and (mNodes[nid].leaf)));{$ENDIF}
if (nid >= 0) then result := mNodes[nid].flesh else result := nil;
end;
diff --git a/src/shared/binheap.pas b/src/shared/binheap.pas
index 968c5f94221e63a73ced2a96a109eda6ff99daf0..50531ed6f453d79044597739bf48d813dfe96c6f 100644 (file)
--- a/src/shared/binheap.pas
+++ b/src/shared/binheap.pas
type
- TBinaryHeapLessFn = function (a, b: TObject): Boolean;
+ // WARNING! don't put structures into heap, use ponters or ids!
+ generic TBinaryHeapBase<ITP> = class(TObject)
+ private
+ type
+ TBinaryHeapLessFn = function (a, b: ITP): Boolean;
- TBinaryHeapObj = class(TObject)
private
- elem: array of TObject;
+ elem: array of ITP;
elemUsed: Integer;
lessfn: TBinaryHeapLessFn;
procedure clear ();
- procedure insert (val: TObject);
+ procedure insert (val: ITP);
- function front (): TObject;
+ function front (): ITP;
procedure popFront ();
property count: Integer read elemUsed;
end;
+type
+ TBinaryHeapObj = specialize TBinaryHeapBase<TObject>;
+ TBinaryHeapInt = specialize TBinaryHeapBase<Integer>;
+
+
+function binHeapNewIntLess (): TBinaryHeapInt;
+function binHeapNewIntGreat (): TBinaryHeapInt;
+
+
implementation
uses
// ////////////////////////////////////////////////////////////////////////// //
-constructor TBinaryHeapObj.Create (alessfn: TBinaryHeapLessFn);
+function intLess (a, b: Integer): Boolean; begin result := (a < b); end;
+function intGreat (a, b: Integer): Boolean; begin result := (a > b); end;
+
+
+function binHeapNewIntLess (): TBinaryHeapInt; begin result := TBinaryHeapInt.Create(@intLess); end;
+function binHeapNewIntGreat (): TBinaryHeapInt; begin result := TBinaryHeapInt.Create(@intGreat); end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+constructor TBinaryHeapBase.Create (alessfn: TBinaryHeapLessFn);
begin
if not assigned(alessfn) then raise Exception.Create('wutafuck?!');
lessfn := alessfn;
- SetLength(elem, 8192); // 'cause why not?
+ SetLength(elem, 128); // 'cause why not?
elemUsed := 0;
end;
-destructor TBinaryHeapObj.Destroy ();
+destructor TBinaryHeapBase.Destroy ();
begin
elem := nil;
inherited;
end;
-procedure TBinaryHeapObj.clear ();
+procedure TBinaryHeapBase.clear ();
begin
elemUsed := 0;
end;
-procedure TBinaryHeapObj.heapify (root: Integer);
+procedure TBinaryHeapBase.heapify (root: Integer);
var
smallest, right: Integer;
- tmp: TObject;
+ tmp: ITP;
begin
while true do
begin
end;
-procedure TBinaryHeapObj.insert (val: TObject);
+procedure TBinaryHeapBase.insert (val: ITP);
var
i, par: Integer;
begin
- if (val = nil) then exit;
+ //if (val = nil) then exit;
i := elemUsed;
- if (i = Length(elem)) then SetLength(elem, Length(elem)+16384); // arbitrary number
+ // grow?
+ if (i = Length(elem)) then
+ begin
+ if (i <= 65536) then par := i*2 else par := i+65536; // arbitrary numbers
+ SetLength(elem, par);
+ end;
+ // increase counter
Inc(elemUsed);
+ // insert element
while (i <> 0) do
begin
par := (i-1) div 2; // parent
elem[i] := val;
end;
-function TBinaryHeapObj.front (): TObject;
+function TBinaryHeapBase.front (): ITP;
begin
- if elemUsed > 0 then result := elem[0] else result := nil;
+ if elemUsed > 0 then result := elem[0] else result := Default(ITP);
end;
-procedure TBinaryHeapObj.popFront ();
+procedure TBinaryHeapBase.popFront ();
begin
if (elemUsed > 0) then
begin