diff --git a/src/game/g_items.pas b/src/game/g_items.pas
index 6d7cf5a10a7918ea8bc0480160d33c9769c0099e..9693ad07f31270f0d4e691a33ebcd66de8cbe04d 100644 (file)
--- a/src/game/g_items.pas
+++ b/src/game/g_items.pas
-(* Copyright (C) DooM 2D:Forever Developers
+(* Copyright (C) Doom 2D: Forever Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
interface
uses
interface
uses
- g_textures, g_phys, g_saveload, BinEditor, MAPDEF;
+ SysUtils, Classes,
+ MAPDEF, g_textures, g_phys, g_saveload;
Type
PItem = ^TItem;
TItem = record
private
Type
PItem = ^TItem;
TItem = record
private
- treeNode: Integer;
+ //treeNode: Integer;
+ slotIsUsed: Boolean;
arrIdx: Integer; // in ggItems
public
arrIdx: Integer; // in ggItems
public
- ItemType: Byte;
- Respawnable: Boolean;
- InitX, InitY: Integer;
- RespawnTime: Word;
- Live: Boolean;
- Fall: Boolean;
- QuietRespawn: Boolean;
- SpawnTrigger: Integer;
- Obj: TObj;
- Animation: TAnimation;
+ ItemType: Byte;
+ Respawnable: Boolean;
+ InitX, InitY: Integer;
+ RespawnTime: Word;
+ alive: Boolean;
+ Fall: Boolean;
+ QuietRespawn: Boolean;
+ SpawnTrigger: Integer;
+ Obj: TObj;
+ Animation: TAnimation;
+ dropped: Boolean; // dropped from the monster? drops should be rendered after corpses, so zombie corpse will not obscure ammo container, for example
procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
procedure g_Items_Free();
function g_Items_Create(X, Y: Integer; ItemType: Byte;
Fall, Respawnable: Boolean; AdjCoord: Boolean = False; ForcedID: Integer = -1): DWORD;
procedure g_Items_Free();
function g_Items_Create(X, Y: Integer; ItemType: Byte;
Fall, Respawnable: Boolean; AdjCoord: Boolean = False; ForcedID: Integer = -1): DWORD;
+procedure g_Items_SetDrop (ID: DWORD);
procedure g_Items_Update();
procedure g_Items_Draw();
procedure g_Items_Update();
procedure g_Items_Draw();
+procedure g_Items_DrawDrop();
procedure g_Items_Pick(ID: DWORD);
procedure g_Items_Remove(ID: DWORD);
procedure g_Items_Pick(ID: DWORD);
procedure g_Items_Remove(ID: DWORD);
-procedure g_Items_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Items_LoadState(var Mem: TBinMemoryReader);
+procedure g_Items_SaveState (st: TStream);
+procedure g_Items_LoadState (st: TStream);
procedure g_Items_RestartRound ();
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);
+
+procedure g_Items_AddDynLights();
type
type
@@ -76,100 +82,57 @@ function g_Items_ForEachAlive (cb: TItemEachAliveCB; backwards: Boolean=false):
var
gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
var
gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
- gMaxDist: Integer = 1;
+ gMaxDist: Integer = 1; // for sounds
ITEM_RESPAWNTIME: Integer = 60 * 36;
implementation
uses
ITEM_RESPAWNTIME: Integer = 60 * 36;
implementation
uses
+ Math,
g_basic, e_graphics, g_sound, g_main, g_gfx, g_map,
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,
+ g_game, g_triggers, g_console, g_player, g_net, g_netmsg,
e_log,
e_log,
- g_grid, z_aabbtree, binheap;
+ g_grid, binheap, idpool, utils, xstreams;
-// ////////////////////////////////////////////////////////////////////////// //
var
var
- itemTree: TDynAABBTree = nil;
ggItems: Array of TItem = nil;
ggItems: Array of TItem = nil;
- freeIds: TBinaryHeapInt = nil; // free item ids
// ////////////////////////////////////////////////////////////////////////// //
// ////////////////////////////////////////////////////////////////////////// //
-function g_ItemValidId (idx: Integer): Boolean; inline;
+var
+ freeIds: TIdPool = nil;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Items_ValidId (idx: Integer): Boolean; inline;
begin
result := false;
if (idx < 0) or (idx > High(ggItems)) then exit;
begin
result := false;
if (idx < 0) or (idx > High(ggItems)) then exit;
- if (ggItems[idx].treeNode = -1) then exit;
+ if not ggItems[idx].slotIsUsed then exit;
result := true;
end;
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];
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 not result.slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
end;
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');
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 not ggItems[idx].slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
result := @ggItems[idx].Obj;
end;
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 ();
// ////////////////////////////////////////////////////////////////////////// //
procedure TItem.positionChanged ();
-var
- x, y: Integer;
begin
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;
end;
g_Texture_Get('ITEM_KEY_BLUE', gItemsTexturesID[ITEM_KEY_BLUE]);
g_Texture_Get('ITEM_WEAPON_KASTET', gItemsTexturesID[ITEM_WEAPON_KASTET]);
g_Texture_Get('ITEM_WEAPON_PISTOL', gItemsTexturesID[ITEM_WEAPON_PISTOL]);
g_Texture_Get('ITEM_KEY_BLUE', gItemsTexturesID[ITEM_KEY_BLUE]);
g_Texture_Get('ITEM_WEAPON_KASTET', gItemsTexturesID[ITEM_WEAPON_KASTET]);
g_Texture_Get('ITEM_WEAPON_PISTOL', gItemsTexturesID[ITEM_WEAPON_PISTOL]);
- g_Texture_Get('ITEM_JETPACK', gItemsTexturesID[ITEM_JETPACK]);
end;
procedure g_Items_LoadData();
begin
end;
procedure g_Items_LoadData();
begin
- e_WriteLog('Loading items data...', MSG_NOTIFY);
+ e_WriteLog('Loading items data...', TMsgType.Notify);
g_Sound_CreateWADEx('SOUND_ITEM_RESPAWNITEM', GameWAD+':SOUNDS\RESPAWNITEM');
g_Sound_CreateWADEx('SOUND_ITEM_GETRULEZ', GameWAD+':SOUNDS\GETRULEZ');
g_Sound_CreateWADEx('SOUND_ITEM_RESPAWNITEM', GameWAD+':SOUNDS\RESPAWNITEM');
g_Sound_CreateWADEx('SOUND_ITEM_GETRULEZ', GameWAD+':SOUNDS\GETRULEZ');
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_WHITESPHERE', GameWAD+':TEXTURES\SWHITE', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_ARMORGREEN', GameWAD+':TEXTURES\ARMORGREEN', 32, 16, 3, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_ARMORBLUE', GameWAD+':TEXTURES\ARMORBLUE', 32, 16, 3, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_WHITESPHERE', GameWAD+':TEXTURES\SWHITE', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_ARMORGREEN', GameWAD+':TEXTURES\ARMORGREEN', 32, 16, 3, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_ARMORBLUE', GameWAD+':TEXTURES\ARMORBLUE', 32, 16, 3, True);
+ g_Frames_CreateWAD(nil, 'FRAMES_ITEM_JETPACK', GameWAD+':TEXTURES\JETPACK', 32, 32, 3, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_INVUL', GameWAD+':TEXTURES\INVUL', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_INVIS', GameWAD+':TEXTURES\INVIS', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_RESPAWN', GameWAD+':TEXTURES\ITEMRESPAWN', 32, 32, 5, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_INVUL', GameWAD+':TEXTURES\INVUL', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_INVIS', GameWAD+':TEXTURES\INVIS', 32, 32, 4, True);
g_Frames_CreateWAD(nil, 'FRAMES_ITEM_RESPAWN', GameWAD+':TEXTURES\ITEMRESPAWN', 32, 32, 5, True);
g_Texture_CreateWADEx('ITEM_SUIT', GameWAD+':TEXTURES\SUIT');
g_Texture_CreateWADEx('ITEM_WEAPON_KASTET', GameWAD+':TEXTURES\KASTET');
g_Texture_CreateWADEx('ITEM_MEDKIT_BLACK', GameWAD+':TEXTURES\BMED');
g_Texture_CreateWADEx('ITEM_SUIT', GameWAD+':TEXTURES\SUIT');
g_Texture_CreateWADEx('ITEM_WEAPON_KASTET', GameWAD+':TEXTURES\KASTET');
g_Texture_CreateWADEx('ITEM_MEDKIT_BLACK', GameWAD+':TEXTURES\BMED');
- g_Texture_CreateWADEx('ITEM_JETPACK', GameWAD+':TEXTURES\JETPACK');
InitTextures();
InitTextures();
- itemTree := TDynAABBTreeItem.Create();
- freeIds := binHeapNewIntLess();
+ freeIds := TIdPool.Create();
end;
procedure g_Items_FreeData();
begin
end;
procedure g_Items_FreeData();
begin
- e_WriteLog('Releasing items data...', MSG_NOTIFY);
+ e_WriteLog('Releasing items data...', TMsgType.Notify);
g_Sound_Delete('SOUND_ITEM_RESPAWNITEM');
g_Sound_Delete('SOUND_ITEM_GETRULEZ');
g_Sound_Delete('SOUND_ITEM_RESPAWNITEM');
g_Sound_Delete('SOUND_ITEM_GETRULEZ');
g_Frames_DeleteByName('FRAMES_ITEM_WHITESPHERE');
g_Frames_DeleteByName('FRAMES_ITEM_ARMORGREEN');
g_Frames_DeleteByName('FRAMES_ITEM_ARMORBLUE');
g_Frames_DeleteByName('FRAMES_ITEM_WHITESPHERE');
g_Frames_DeleteByName('FRAMES_ITEM_ARMORGREEN');
g_Frames_DeleteByName('FRAMES_ITEM_ARMORBLUE');
+ g_Frames_DeleteByName('FRAMES_ITEM_JETPACK');
g_Frames_DeleteByName('FRAMES_ITEM_INVUL');
g_Frames_DeleteByName('FRAMES_ITEM_INVIS');
g_Frames_DeleteByName('FRAMES_ITEM_RESPAWN');
g_Frames_DeleteByName('FRAMES_ITEM_INVUL');
g_Frames_DeleteByName('FRAMES_ITEM_INVIS');
g_Frames_DeleteByName('FRAMES_ITEM_RESPAWN');
g_Texture_Delete('ITEM_SUIT');
g_Texture_Delete('ITEM_WEAPON_KASTET');
g_Texture_Delete('ITEM_MEDKIT_BLACK');
g_Texture_Delete('ITEM_SUIT');
g_Texture_Delete('ITEM_WEAPON_KASTET');
g_Texture_Delete('ITEM_MEDKIT_BLACK');
- g_Texture_Delete('ITEM_JETPACK');
- itemTree.Free();
freeIds.Free();
freeIds.Free();
+ freeIds := nil;
end;
end;
it: PItem;
begin
if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('releaseItem: invalid item id');
it: PItem;
begin
if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('releaseItem: invalid item id');
+ if not freeIds.hasAlloced[LongWord(idx)] then raise Exception.Create('releaseItem: trying to release unallocated item (0)');
it := @ggItems[idx];
it := @ggItems[idx];
- 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 (1)');
if (it.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency');
if (it.arrIdx <> idx) then raise Exception.Create('releaseItem: arrIdx inconsistency');
- itemTree.removeObject(it.treeNode);
- it.treeNode := -1;
+ it.slotIsUsed := false;
if (it.Animation <> nil) then
begin
it.Animation.Free();
it.Animation := nil;
end;
if (it.Animation <> nil) then
begin
it.Animation.Free();
it.Animation := nil;
end;
- it.Live := False;
+ it.alive := False;
it.SpawnTrigger := -1;
it.ItemType := ITEM_NONE;
it.SpawnTrigger := -1;
it.ItemType := ITEM_NONE;
- freeIds.insert(it.arrIdx);
+ freeIds.release(LongWord(idx));
end;
end;
-function allocItem (): DWORD;
+procedure growItemArrayTo (newsz: Integer);
var
i, olen: Integer;
it: PItem;
begin
var
i, olen: Integer;
it: PItem;
begin
- if (freeIds.count = 0) then
+ if (newsz < Length(ggItems)) then exit;
+ // no free slots
+ olen := Length(ggItems);
+ SetLength(ggItems, newsz);
+ for i := olen to High(ggItems) do
begin
begin
- // no free slots
- olen := Length(ggItems);
- SetLength(ggItems, olen+64);
- 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;
- freeIds.insert(i);
- end;
+ it := @ggItems[i];
+ it.slotIsUsed := false;
+ it.arrIdx := i;
+ it.ItemType := ITEM_NONE;
+ it.Animation := nil;
+ it.alive := false;
+ it.SpawnTrigger := -1;
+ it.Respawnable := false;
+ //if not freeIds.hasFree[LongWord(i)] then raise Exception.Create('internal error in item idx manager');
end;
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');
+function allocItem (): DWORD;
+begin
+ result := freeIds.alloc();
+ if (result >= Length(ggItems)) then growItemArrayTo(Integer(result)+64);
+ if (Integer(result) > High(ggItems)) then raise Exception.Create('allocItem: freeid list corrupted');
+ if (ggItems[result].arrIdx <> Integer(result)) then raise Exception.Create('allocItem: arrIdx inconsistency');
end;
// it will be slow if the slot is free (we have to rebuild the heap)
function wantItemSlot (slot: Integer): Integer;
var
end;
// it will be slow if the slot is free (we have to rebuild the heap)
function wantItemSlot (slot: Integer): Integer;
var
- i, olen: Integer;
+ olen: Integer;
it: PItem;
it: PItem;
- rebuildFreeList: Boolean = true;
begin
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);
begin
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 (slot >= olen) then growItemArrayTo(slot+64);
it := @ggItems[slot];
it := @ggItems[slot];
- if (it.treeNode = -1) then
+ if not it.slotIsUsed then
begin
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;
+ freeIds.alloc(LongWord(slot));
end
else
begin
end
else
begin
- // it will be readded
- itemTree.removeObject(it.treeNode);
- it.treeNode := -1;
+ if not freeIds.hasAlloced[slot] then raise Exception.Create('wantItemSlot: internal error in item idx manager');
end;
end;
+ it.slotIsUsed := false;
result := slot;
end;
result := slot;
end;
for i := 0 to High(ggItems) do ggItems[i].Animation.Free();
ggItems := nil;
end;
for i := 0 to High(ggItems) do ggItems[i].Animation.Free();
ggItems := nil;
end;
- if (itemTree <> nil) then itemTree.reset();
freeIds.clear();
end;
freeIds.clear();
end;
begin
if ForcedID < 0 then find_id := allocItem() else find_id := wantItemSlot(ForcedID);
begin
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}
+ //{$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('allocated item #%d', [Integer(find_id)]), MSG_NOTIFY);{$ENDIF}
it := @ggItems[find_id];
it := @ggItems[find_id];
+ if (it.arrIdx <> Integer(find_id)) then raise Exception.Create('g_Items_Create: arrIdx inconsistency');
+ //it.arrIdx := find_id;
+ it.slotIsUsed := true;
+
it.ItemType := ItemType;
it.Respawnable := Respawnable;
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.InitX := X;
it.InitY := Y;
it.RespawnTime := 0;
it.Fall := Fall;
- it.Live := True;
+ it.alive := True;
it.QuietRespawn := False;
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;
+ it.dropped := false;
g_Obj_Init(@it.Obj);
it.Obj.X := X;
g_Obj_Init(@it.Obj);
it.Obj.X := X;
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);
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_JETPACK: if g_Frames_Get(ID, 'FRAMES_ITEM_JETPACK') then it.Animation := TAnimation.Create(ID, True, 15);
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_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);
for i := 0 to High(ggItems) do
begin
if (ggItems[i].ItemType = ITEM_NONE) then continue;
for i := 0 to High(ggItems) do
begin
if (ggItems[i].ItemType = ITEM_NONE) then continue;
+ if not ggItems[i].slotIsUsed then continue; // just in case
with ggItems[i] do
begin
nxt := False;
with ggItems[i] do
begin
nxt := False;
- if Live then
+ if alive then
begin
if Fall then
begin
begin
if Fall then
begin
Inc(j);
if j > High(gPlayers) then j := 0;
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
+ if (gPlayers[j] <> nil) and gPlayers[j].alive and g_Obj_Collide(@gPlayers[j].Obj, @Obj) then
begin
if g_Game_IsClient then continue;
begin
if g_Game_IsClient then continue;
+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
}
+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
begin
// Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì
if r then
begin
- if not Respawnable then g_Items_Remove(i) else g_Items_Pick(i);
+ if not (Respawnable and (ITEM_RESPAWNTIME > 0)) then
+ g_Items_Remove(i)
+ else
+ g_Items_Pick(i);
if g_Game_IsNet then MH_SEND_ItemDestroy(False, i);
nxt := True;
break;
if g_Game_IsNet then MH_SEND_ItemDestroy(False, i);
nxt := True;
break;
if Respawnable and g_Game_IsServer then
begin
DecMin(RespawnTime, 0);
if Respawnable and g_Game_IsServer then
begin
DecMin(RespawnTime, 0);
- if (RespawnTime = 0) and (not Live) then
+ if (RespawnTime = 0) and (not alive) then
begin
if not QuietRespawn then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', InitX, InitY);
begin
if not QuietRespawn then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', InitX, InitY);
Obj.Accel.Y := 0;
positionChanged(); // this updates spatial accelerators
Obj.Accel.Y := 0;
positionChanged(); // this updates spatial accelerators
- Live := true;
+ alive := true;
if g_Game_IsNet then MH_SEND_ItemSpawn(QuietRespawn, i);
QuietRespawn := false;
if g_Game_IsNet then MH_SEND_ItemSpawn(QuietRespawn, i);
QuietRespawn := false;
end;
end;
-procedure g_Items_Draw ();
+procedure itemsDrawInternal (dropflag: Boolean);
var
i: Integer;
var
i: Integer;
+ it: PItem;
begin
if (ggItems = nil) then exit;
for i := 0 to High(ggItems) do
begin
begin
if (ggItems = nil) then exit;
for i := 0 to High(ggItems) do
begin
- if not ggItems[i].Live then continue;
+ it := @ggItems[i];
+ if (not it.slotIsUsed) or (it.ItemType = ITEM_NONE) then continue; // just in case
+ if not it.alive then continue;
+ if (it.dropped <> dropflag) then continue;
- with ggItems[i] do
+ with it^ do
begin
if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, sX, sY, sWidth, sHeight) then
begin
begin
if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, sX, sY, sWidth, sHeight) then
begin
end
else
begin
end
else
begin
- Animation.Draw(Obj.X, Obj.Y, M_NONE);
+ Animation.Draw(Obj.X, Obj.Y, TMirrorType.None);
end;
if g_debug_Frames then
end;
if g_debug_Frames then
end;
end;
+procedure g_Items_Draw ();
+begin
+ itemsDrawInternal(false);
+end;
+
+procedure g_Items_DrawDrop ();
+begin
+ itemsDrawInternal(true);
+end;
+
+
+procedure g_Items_SetDrop (ID: DWORD);
+begin
+ if (ID < Length(ggItems)) then
+ begin
+ ggItems[ID].dropped := true;
+ end;
+end;
+
+
procedure g_Items_Pick (ID: DWORD);
begin
procedure g_Items_Pick (ID: DWORD);
begin
- ggItems[ID].Live := false;
- ggItems[ID].RespawnTime := ITEM_RESPAWNTIME;
+ if (ID < Length(ggItems)) then
+ begin
+ ggItems[ID].alive := false;
+ ggItems[ID].RespawnTime := ITEM_RESPAWNTIME;
+ end;
end;
end;
var
it: PItem;
trig: Integer;
var
it: PItem;
trig: Integer;
- x, y: Integer;
begin
begin
- if not g_ItemValidId(ID) then raise Exception.Create('g_Items_Remove: invalid item id');
+ if not g_Items_ValidId(ID) then
+ begin
+ //writeln('g_Items_Remove: invalid item id: ', ID);
+ raise Exception.Create('g_Items_Remove: invalid item id');
+ //exit;
+ end;
it := @ggItems[ID];
it := @ggItems[ID];
- if (it.arrIdx <> ID) then raise Exception.Create('g_Items_Remove: arrIdx desync');
-
- 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 (it.arrIdx <> Integer(ID)) then raise Exception.Create('g_Items_Remove: arrIdx desync');
trig := it.SpawnTrigger;
trig := it.SpawnTrigger;
end;
end;
-procedure g_Items_SaveState (var Mem: TBinMemoryWriter);
+procedure g_Items_SaveState (st: TStream);
var
count, i: Integer;
var
count, i: Integer;
- sig: DWORD;
+ tt: Byte;
begin
// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
count := 0;
begin
// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
count := 0;
- 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);
+ for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) and (ggItems[i].slotIsUsed) then Inc(count);
// Êîëè÷åñòâî ïðåäìåòîâ
// Êîëè÷åñòâî ïðåäìåòîâ
- Mem.WriteInt(count);
-
+ utils.writeInt(st, LongInt(count));
if (count = 0) then exit;
for i := 0 to High(ggItems) do
begin
if (count = 0) then exit;
for i := 0 to High(ggItems) do
begin
- if (ggItems[i].ItemType <> ITEM_NONE) then
+ if (ggItems[i].ItemType <> ITEM_NONE) and (ggItems[i].slotIsUsed) then
begin
// Ñèãíàòóðà ïðåäìåòà
begin
// Ñèãíàòóðà ïðåäìåòà
- sig := ITEM_SIGNATURE; // 'ITEM'
- Mem.WriteDWORD(sig);
+ utils.writeSign(st, 'ITEM');
+ utils.writeInt(st, Byte(0));
// Òèï ïðåäìåòà
// Òèï ïðåäìåòà
- Mem.WriteByte(ggItems[i].ItemType);
+ tt := ggItems[i].ItemType;
+ if ggItems[i].dropped then tt := tt or $80;
+ utils.writeInt(st, Byte(tt));
// Åñòü ëè ðåñïàóí
// Åñòü ëè ðåñïàóí
- Mem.WriteBoolean(ggItems[i].Respawnable);
+ utils.writeBool(st, ggItems[i].Respawnable);
// Êîîðäèíàòû ðåñïóíà
// Êîîðäèíàòû ðåñïóíà
- Mem.WriteInt(ggItems[i].InitX);
- Mem.WriteInt(ggItems[i].InitY);
+ utils.writeInt(st, LongInt(ggItems[i].InitX));
+ utils.writeInt(st, LongInt(ggItems[i].InitY));
// Âðåìÿ äî ðåñïàóíà
// Âðåìÿ äî ðåñïàóíà
- Mem.WriteWord(ggItems[i].RespawnTime);
+ utils.writeInt(st, Word(ggItems[i].RespawnTime));
// Ñóùåñòâóåò ëè ýòîò ïðåäìåò
// Ñóùåñòâóåò ëè ýòîò ïðåäìåò
- Mem.WriteBoolean(ggItems[i].Live);
+ utils.writeBool(st, ggItems[i].alive);
// Ìîæåò ëè îí ïàäàòü
// Ìîæåò ëè îí ïàäàòü
- Mem.WriteBoolean(ggItems[i].Fall);
+ utils.writeBool(st, ggItems[i].Fall);
// Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
// Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
- Mem.WriteInt(ggItems[i].SpawnTrigger);
+ utils.writeInt(st, LongInt(ggItems[i].SpawnTrigger));
// Îáúåêò ïðåäìåòà
// Îáúåêò ïðåäìåòà
- Obj_SaveState(@ggItems[i].Obj, Mem);
+ Obj_SaveState(st, @ggItems[i].Obj);
end;
end;
end;
end;
end;
end;
-procedure g_Items_LoadState (var Mem: TBinMemoryReader);
+procedure g_Items_LoadState (st: TStream);
var
count, i, a: Integer;
var
count, i, a: Integer;
- sig: DWORD;
b: Byte;
begin
b: Byte;
begin
- if (Mem = nil) then exit;
+ assert(st <> nil);
g_Items_Free();
// Êîëè÷åñòâî ïðåäìåòîâ
g_Items_Free();
// Êîëè÷åñòâî ïðåäìåòîâ
- Mem.ReadInt(count);
-
- if (count = 0) then Exit;
+ count := utils.readLongInt(st);
+ if (count = 0) then exit;
+ if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid number of items');
for a := 0 to count-1 do
begin
// Ñèãíàòóðà ïðåäìåòà
for a := 0 to count-1 do
begin
// Ñèãíàòóðà ïðåäìåòà
- Mem.ReadDWORD(sig);
- if (sig <> ITEM_SIGNATURE) then raise EBinSizeError.Create('g_Items_LoadState: Wrong Item Signature'); // 'ITEM'
+ if not utils.checkSign(st, 'ITEM') then raise XStreamError.Create('invalid item signature');
+ if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid item version');
// Òèï ïðåäìåòà
// Òèï ïðåäìåòà
- Mem.ReadByte(b);
+ b := utils.readByte(st); // bit7=1: monster drop
// Ñîçäàåì ïðåäìåò
// Ñîçäàåì ïðåäìåò
- i := g_Items_Create(0, 0, b, False, False);
+ i := g_Items_Create(0, 0, b and $7F, False, False);
+ if ((b and $80) <> 0) then g_Items_SetDrop(i);
// Åñòü ëè ðåñïàóí
// Åñòü ëè ðåñïàóí
- Mem.ReadBoolean(ggItems[i].Respawnable);
+ ggItems[i].Respawnable := utils.readBool(st);
// Êîîðäèíàòû ðåñïóíà
// Êîîðäèíàòû ðåñïóíà
- Mem.ReadInt(ggItems[i].InitX);
- Mem.ReadInt(ggItems[i].InitY);
+ ggItems[i].InitX := utils.readLongInt(st);
+ ggItems[i].InitY := utils.readLongInt(st);
// Âðåìÿ äî ðåñïàóíà
// Âðåìÿ äî ðåñïàóíà
- Mem.ReadWord(ggItems[i].RespawnTime);
+ ggItems[i].RespawnTime := utils.readWord(st);
// Ñóùåñòâóåò ëè ýòîò ïðåäìåò
// Ñóùåñòâóåò ëè ýòîò ïðåäìåò
- Mem.ReadBoolean(ggItems[i].Live);
+ ggItems[i].alive := utils.readBool(st);
// Ìîæåò ëè îí ïàäàòü
// Ìîæåò ëè îí ïàäàòü
- Mem.ReadBoolean(ggItems[i].Fall);
+ ggItems[i].Fall := utils.readBool(st);
// Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
// Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
- Mem.ReadInt(ggItems[i].SpawnTrigger);
+ ggItems[i].SpawnTrigger := utils.readLongInt(st);
// Îáúåêò ïðåäìåòà
// Îáúåêò ïðåäìåòà
- Obj_LoadState(@ggItems[i].Obj, Mem);
+ Obj_LoadState(@ggItems[i].Obj, st);
end;
end;
end;
end;
for i := 0 to High(ggItems) do
begin
it := @ggItems[i];
for i := 0 to High(ggItems) do
begin
it := @ggItems[i];
- if it.Respawnable then
+ if not it.slotIsUsed then continue;
+ if it.Respawnable and (it.ItemType <> ITEM_NONE) then
begin
it.QuietRespawn := True;
it.RespawnTime := 0;
begin
it.QuietRespawn := True;
it.RespawnTime := 0;
begin
for idx := High(ggItems) downto 0 do
begin
begin
for idx := High(ggItems) downto 0 do
begin
- if ggItems[idx].Live then
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
begin
result := cb(@ggItems[idx]);
if result then exit;
begin
result := cb(@ggItems[idx]);
if result then exit;
begin
for idx := 0 to High(ggItems) do
begin
begin
for idx := 0 to High(ggItems) do
begin
- if ggItems[idx].Live then
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
begin
result := cb(@ggItems[idx]);
if result then exit;
begin
result := cb(@ggItems[idx]);
if result then exit;
// ////////////////////////////////////////////////////////////////////////// //
// ////////////////////////////////////////////////////////////////////////// //
-procedure g_Item_EmitPickupSound (idx: Integer);
+procedure g_Items_EmitPickupSound (idx: Integer);
var
it: PItem;
begin
var
it: PItem;
begin
- if not g_ItemValidId(idx) then exit;
+ if not g_Items_ValidId(idx) then exit;
it := @ggItems[idx];
it := @ggItems[idx];
- g_Item_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
+ g_Items_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
end;
end;
-procedure g_Item_EmitPickupSoundAt (idx, x, y: Integer);
+procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
var
it: PItem;
begin
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
it := @ggItems[idx];
if gSoundEffectsDF then
end;
end;
end;
end;
+
+procedure g_Items_AddDynLights();
+var
+ f: Integer;
+ it: PItem;
+begin
+ for f := 0 to High(ggItems) do
+ begin
+ it := @ggItems[f];
+ if not it.alive then continue;
+ case it.ItemType of
+ ITEM_KEY_RED: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24, 1.0, 0.0, 0.0, 0.6);
+ ITEM_KEY_GREEN: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24, 0.0, 1.0, 0.0, 0.6);
+ ITEM_KEY_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 24, 0.0, 0.0, 1.0, 0.6);
+ ITEM_ARMOR_GREEN: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 42, 0.0, 1.0, 0.0, 0.6);
+ ITEM_ARMOR_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 42, 0.0, 0.0, 1.0, 0.6);
+ ITEM_JETPACK: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32, 1.0, 1.0, 1.0, 0.6);
+ ITEM_SPHERE_BLUE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32, 0.0, 1.0, 0.0, 0.6);
+ ITEM_SPHERE_WHITE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32, 1.0, 1.0, 1.0, 0.6);
+ ITEM_INVUL: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32, 1.0, 0.0, 0.0, 0.6);
+ ITEM_INVIS: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 32, 1.0, 1.0, 0.0, 0.6);
+ ITEM_BOTTLE: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 16, 0.0, 0.0, 0.8, 0.6);
+ ITEM_HELMET: g_AddDynLight(it.Obj.X+(it.Obj.Rect.Width div 2), it.Obj.Y+(it.Obj.Rect.Height div 2), 16, 0.0, 0.8, 0.0, 0.6);
+ end;
+ end;
+end;
+
+
end.
end.