diff --git a/src/game/g_items.pas b/src/game/g_items.pas
index d94b996e05b7436b9b8d487f3e181a81d508ccba..a69ceaaf811bd9f84f5b07ec41b0e6e2fcf11537 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
- * 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
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*)
-{$MODE DELPHI}
+{$INCLUDE ../shared/a_modes.inc}
unit g_items;
interface
uses
- g_textures, g_phys, g_saveload, BinEditor, MAPDEF;
+ SysUtils, Classes,
+ MAPDEF, g_textures, g_phys, g_saveload;
Type
+ PItem = ^TItem;
TItem = record
- ItemType: Byte;
- Respawnable: Boolean;
- InitX, InitY: Integer;
- RespawnTime: Word;
- Live: Boolean;
- Fall: Boolean;
- QuietRespawn: Boolean;
- SpawnTrigger: Integer;
- Obj: TObj;
- Animation: TAnimation;
+ private
+ //treeNode: Integer;
+ slotIsUsed: Boolean;
+ arrIdx: Integer; // in ggItems
+
+ public
+ 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
+ NeedSend: Boolean;
+
+ procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
+ procedure getMapBox (out x, y, w, h: Integer); inline;
+ procedure moveBy (dx, dy: Integer); inline;
+
+ property myid: Integer read arrIdx;
end;
procedure g_Items_LoadData();
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_PreUpdate();
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_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 ();
+
+function g_Items_ValidId (idx: Integer): Boolean; inline;
+function g_Items_ByIdx (idx: Integer): PItem;
+function g_Items_ObjByIdx (idx: Integer): PObj;
+
+procedure g_Items_EmitPickupSound (idx: Integer); // at item position
+procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
+
+procedure g_Items_AddDynLights();
+
+
+type
+ TItemEachAliveCB = function (it: PItem): Boolean is nested; // return `true` to stop
+
+function g_Items_ForEachAlive (cb: TItemEachAliveCB; backwards: Boolean=false): Boolean;
+function g_Items_NextAlive (startIdx: Integer): PItem;
var
- gItems: Array of TItem = nil;
gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
- gMaxDist: Integer = 1;
- ITEM_RESPAWNTIME: Integer = 60 * 36;
+ gMaxDist: Integer = 1; // for sounds
implementation
uses
+ Math,
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;
+ g_game, g_triggers, g_console, g_player, g_net, g_netmsg,
+ e_log,
+ g_grid, binheap, idpool, utils, xstreams;
+
+
+var
+ ggItems: Array of TItem = nil;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+var
+ freeIds: TIdPool = nil;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Items_ValidId (idx: Integer): Boolean; inline;
+begin
+ result := false;
+ if (idx < 0) or (idx > High(ggItems)) then exit;
+ if not ggItems[idx].slotIsUsed then exit;
+ result := true;
+end;
+
+
+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 not result.slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+end;
+
+
+function g_Items_ObjByIdx (idx: Integer): PObj;
+begin
+ if (idx < 0) or (idx > High(ggItems)) then raise Exception.Create('g_ItemObjByIdx: invalid index');
+ if not ggItems[idx].slotIsUsed then raise Exception.Create('g_ItemObjByIdx: requested inexistent item');
+ result := @ggItems[idx].Obj;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure TItem.positionChanged ();
+begin
+ NeedSend := NeedSend or (Obj.X <> Obj.oldX) or (Obj.Y <> Obj.oldY);
+end;
+
+procedure TItem.getMapBox (out x, y, w, h: Integer); inline;
+begin
+ x := Obj.X+Obj.Rect.X;
+ y := Obj.Y+Obj.Rect.Y;
+ w := Obj.Rect.Width;
+ h := Obj.Rect.Height;
+end;
+procedure TItem.moveBy (dx, dy: Integer); inline;
+begin
+ if (dx <> 0) or (dy <> 0) then
+ begin
+ Obj.X += dx;
+ Obj.Y += dy;
+ positionChanged();
+ end;
+end;
+
+// ////////////////////////////////////////////////////////////////////////// //
const
ITEM_SIGNATURE = $4D455449; // 'ITEM'
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
- 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_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_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();
+
+ freeIds := TIdPool.Create();
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_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_Texture_Delete('ITEM_SUIT');
g_Texture_Delete('ITEM_WEAPON_KASTET');
g_Texture_Delete('ITEM_MEDKIT_BLACK');
- g_Texture_Delete('ITEM_JETPACK');
+
+ freeIds.Free();
+ freeIds := nil;
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');
+ if not freeIds.hasAlloced[LongWord(idx)] then raise Exception.Create('releaseItem: trying to release unallocated item (0)');
+ it := @ggItems[idx];
+ 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');
+ it.slotIsUsed := false;
+ if (it.Animation <> nil) then
+ begin
+ it.Animation.Free();
+ it.Animation := nil;
+ end;
+ it.alive := False;
+ it.SpawnTrigger := -1;
+ it.ItemType := ITEM_NONE;
+ it.NeedSend := false;
+ freeIds.release(LongWord(idx));
+end;
- if gItems = nil then
- begin
- SetLength(gItems, 32);
- Result := 0;
- end
- else
- begin
- Result := High(gItems) + 1;
- SetLength(gItems, Length(gItems) + 32);
- end;
+
+procedure growItemArrayTo (newsz: Integer);
+var
+ i, olen: Integer;
+ it: PItem;
+begin
+ if (newsz < Length(ggItems)) then exit;
+ // no free slots
+ olen := Length(ggItems);
+ SetLength(ggItems, newsz);
+ for i := olen to High(ggItems) do
+ begin
+ 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;
+ it.NeedSend := false;
+ //if not freeIds.hasFree[LongWord(i)] then raise Exception.Create('internal error in item idx manager');
+ end;
end;
-procedure g_Items_Init();
+
+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
- a, b: Integer;
+ olen: Integer;
+ it: PItem;
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 growItemArrayTo(slot+64);
- if gMapInfo.Width > gPlayerScreenSize.X then
- b := gMapInfo.Width - gPlayerScreenSize.X
+ it := @ggItems[slot];
+ if not it.slotIsUsed then
+ begin
+ freeIds.alloc(LongWord(slot));
+ end
else
- b := gMapInfo.Width;
+ begin
+ if not freeIds.hasAlloced[slot] then raise Exception.Create('wantItemSlot: internal error in item idx manager');
+ end;
+ it.slotIsUsed := false;
+ 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;
+ 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];
+
+ if (it.arrIdx <> Integer(find_id)) then raise Exception.Create('g_Items_Create: arrIdx inconsistency');
+ //it.arrIdx := find_id;
+ it.slotIsUsed := true;
- 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;
-
-// Êîîðäèíàòû îòíîñèòåëüíî öåíòðà íèæíåãî ðåáðà:
+ it.ItemType := ItemType;
+ it.Respawnable := Respawnable;
+ it.InitX := X;
+ it.InitY := Y;
+ it.RespawnTime := 0;
+ it.Fall := Fall;
+ it.alive := True;
+ it.QuietRespawn := False;
+ it.dropped := false;
+ it.NeedSend := false;
+
+ 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;
+ it.Obj.oldX := it.Obj.X;
+ it.Obj.oldY := it.Obj.Y;
+
+ // Óñòàíîâêà àíèìàöèè
+ 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_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_PreUpdate ();
+var
+ i: Integer;
+begin
+ if (ggItems = nil) then Exit;
+ for i := 0 to High(ggItems) do
+ if (ggItems[i].ItemType <> ITEM_NONE) and ggItems[i].slotIsUsed then
+ begin
+ ggItems[i].Obj.oldX := ggItems[i].Obj.X;
+ ggItems[i].Obj.oldY := ggItems[i].Obj.Y;
+ end;
+end;
+
+procedure g_Items_Update ();
var
i, j, k: Integer;
- ID: DWORD;
+ ID: DWord;
Anim: TAnimation;
- m: Word;
+ m, ItemRespawnTime: 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;
+
+ // respawn items in 15 seconds regardless of settings during warmup
+ ItemRespawnTime := IfThen(gLMSRespawn = LMS_RESPAWN_NONE, gGameSettings.ItemRespawnTime, 15);
+
+ 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;
+
+ if alive then
+ begin
+ if Fall then
begin
- nxt := False;
+ m := g_Obj_Move(@Obj, True, True);
+ positionChanged(); // this updates spatial accelerators
- if Live then
+ // Ñîïðîòèâëåíèå âîçäóõà
+ 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 Fall then
+ if SpawnTrigger = -1 then
begin
- m := g_Obj_Move(@Obj, True, True);
-
- // Ñîïðîòèâëåíèå âîçäóõà:
- 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 Respawnable and g_Game_IsServer then
+ // Åñëè èãðîêè ïîáëèçîñòè
+ if (gPlayers <> nil) then
+ begin
+ j := Random(Length(gPlayers))-1;
+
+ 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].alive 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_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
+ 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
+ }
+ g_Items_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 and (ItemRespawnTime > 0)) 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;
+ if nxt then continue;
+ end;
- Live := True;
+ if Respawnable and g_Game_IsServer then
+ begin
+ DecMin(RespawnTime, 0);
+ if (RespawnTime = 0) and (not alive) 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.oldX := InitX;
+ Obj.oldY := InitY;
+ 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
+
+ alive := 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 itemsDrawInternal (dropflag: Boolean);
var
- i: Integer;
+ i, fX, fY: Integer;
+ it: PItem;
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
+ 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 it^ do
+ begin
+ if g_Collide(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, sX, sY, sWidth, sHeight) then
+ begin
+ Obj.lerp(gLerpFactor, fX, fY);
+ if (Animation = nil) then
+ begin
+ e_Draw(gItemsTexturesID[ItemType], fX, fY, 0, true, false);
+ end
+ else
+ begin
+ Animation.Draw(fX, fY, TMirrorType.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_Draw ();
begin
- gItems[ID].Live := False;
- gItems[ID].RespawnTime := ITEM_RESPAWNTIME;
+ itemsDrawInternal(false);
end;
-procedure g_Items_Remove(ID: DWORD);
+procedure g_Items_DrawDrop ();
begin
- gItems[ID].ItemType := ITEM_NONE;
+ itemsDrawInternal(true);
+end;
+
- if gItems[ID].Animation <> nil then
+procedure g_Items_SetDrop (ID: DWORD);
+begin
+ if (ID < Length(ggItems)) then
begin
- gItems[ID].Animation.Free();
- gItems[ID].Animation := nil;
+ ggItems[ID].dropped := true;
end;
+end;
- gItems[ID].Live := False;
- if gItems[ID].SpawnTrigger > -1 then
+procedure g_Items_Pick (ID: DWORD);
+begin
+ if (ID < Length(ggItems)) then
begin
- g_Triggers_DecreaseSpawner(gItems[ID].SpawnTrigger);
- gItems[ID].SpawnTrigger := -1;
+ ggItems[ID].Obj.oldX := ggItems[ID].Obj.X;
+ ggItems[ID].Obj.oldY := ggItems[ID].Obj.Y;
+ ggItems[ID].alive := false;
+ ggItems[ID].RespawnTime := IfThen(gLMSRespawn = LMS_RESPAWN_NONE, gGameSettings.ItemRespawnTime, 15) * 36;
end;
end;
-procedure g_Items_SaveState(var Mem: TBinMemoryWriter);
+
+procedure g_Items_Remove (ID: DWORD);
var
- count, i: Integer;
- sig: DWORD;
+ it: PItem;
+ trig: Integer;
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 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];
+ if (it.arrIdx <> Integer(ID)) then raise Exception.Create('g_Items_Remove: arrIdx desync');
+
+ it.Obj.oldX := it.Obj.X;
+ it.Obj.oldY := it.Obj.Y;
+ trig := it.SpawnTrigger;
- Mem := TBinMemoryWriter.Create((count+1) * 60);
+ releaseItem(ID);
-// Êîëè÷åñòâî ïðåäìåòîâ:
- Mem.WriteInt(count);
+ if (trig > -1) then g_Triggers_DecreaseSpawner(trig);
+end;
+
+
+procedure g_Items_SaveState (st: TStream);
+var
+ count, i: Integer;
+ tt: Byte;
+begin
+ // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
+ count := 0;
+ for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) and (ggItems[i].slotIsUsed) then Inc(count);
- if count = 0 then
- Exit;
+ // Êîëè÷åñòâî ïðåäìåòîâ
+ utils.writeInt(st, LongInt(count));
+ 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) and (ggItems[i].slotIsUsed) 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);
+ // Ñèãíàòóðà ïðåäìåòà
+ utils.writeSign(st, 'ITEM');
+ utils.writeInt(st, Byte(0));
+ // Òèï ïðåäìåòà
+ tt := ggItems[i].ItemType;
+ if ggItems[i].dropped then tt := tt or $80;
+ utils.writeInt(st, Byte(tt));
+ // Åñòü ëè ðåñïàóí
+ utils.writeBool(st, ggItems[i].Respawnable);
+ // Êîîðäèíàòû ðåñïóíà
+ utils.writeInt(st, LongInt(ggItems[i].InitX));
+ utils.writeInt(st, LongInt(ggItems[i].InitY));
+ // Âðåìÿ äî ðåñïàóíà
+ utils.writeInt(st, Word(ggItems[i].RespawnTime));
+ // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
+ utils.writeBool(st, ggItems[i].alive);
+ // Ìîæåò ëè îí ïàäàòü
+ utils.writeBool(st, ggItems[i].Fall);
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
+ utils.writeInt(st, LongInt(ggItems[i].SpawnTrigger));
+ // Îáúåêò ïðåäìåòà
+ Obj_SaveState(st, @ggItems[i].Obj);
end;
+ end;
end;
-procedure g_Items_LoadState(var Mem: TBinMemoryReader);
+
+procedure g_Items_LoadState (st: TStream);
var
count, i, a: Integer;
- sig: DWORD;
b: Byte;
begin
- if Mem = nil then
- Exit;
+ assert(st <> nil);
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
- // Ñèãíàòóðà ïðåäìåòà:
- Mem.ReadDWORD(sig);
- if sig <> ITEM_SIGNATURE then // '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');
+ // Òèï ïðåäìåòà
+ b := utils.readByte(st); // bit7=1: monster drop
+ // Ñîçäàåì ïðåäìåò
+ i := g_Items_Create(0, 0, b and $7F, False, False);
+ if ((b and $80) <> 0) then g_Items_SetDrop(i);
+ // Åñòü ëè ðåñïàóí
+ ggItems[i].Respawnable := utils.readBool(st);
+ // Êîîðäèíàòû ðåñïóíà
+ ggItems[i].InitX := utils.readLongInt(st);
+ ggItems[i].InitY := utils.readLongInt(st);
+ // Âðåìÿ äî ðåñïàóíà
+ ggItems[i].RespawnTime := utils.readWord(st);
+ // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
+ ggItems[i].alive := utils.readBool(st);
+ // Ìîæåò ëè îí ïàäàòü
+ ggItems[i].Fall := utils.readBool(st);
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
+ ggItems[i].SpawnTrigger := utils.readLongInt(st);
+ // Îáúåêò ïðåäìåòà
+ Obj_LoadState(@ggItems[i].Obj, st);
+ end;
+end;
+
+
+procedure g_Items_RestartRound ();
+var
+ i: Integer;
+ it: PItem;
+begin
+ for i := 0 to High(ggItems) do
+ begin
+ it := @ggItems[i];
+ it.Obj.oldX := it.Obj.X;
+ it.Obj.oldY := it.Obj.Y;
+ if not it.slotIsUsed then continue;
+ if it.Respawnable and (it.ItemType <> ITEM_NONE) 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].alive and ggItems[idx].slotIsUsed then
+ begin
+ result := cb(@ggItems[idx]);
+ if result then exit;
+ end;
+ end;
+ end
+ else
+ begin
+ for idx := 0 to High(ggItems) do
begin
- raise EBinSizeError.Create('g_Items_LoadState: Wrong Item Signature');
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
+ begin
+ result := cb(@ggItems[idx]);
+ if result then exit;
+ end;
end;
- // Òèï ïðåäìåòà:
- 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);
end;
end;
+function g_Items_NextAlive (startIdx: Integer): PItem;
+var
+ idx: Integer;
+begin
+ result := nil;
+ if (ggItems = nil) or (startIdx >= High(ggItems)) then exit;
+ for idx := startIdx + 1 to High(ggItems) do
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
+ begin
+ result := @ggItems[idx];
+ exit;
+ end;
+end;
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure g_Items_EmitPickupSound (idx: Integer);
+var
+ it: PItem;
+begin
+ if not g_Items_ValidId(idx) then exit;
+ it := @ggItems[idx];
+ g_Items_EmitPickupSoundAt(idx, it.Obj.X, it.Obj.Y);
+end;
+
+procedure g_Items_EmitPickupSoundAt (idx, x, y: Integer);
+var
+ it: PItem;
+begin
+ if not g_Items_ValidId(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;
+
+
+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.