diff --git a/src/game/g_items.pas b/src/game/g_items.pas
index 028f9d0a86024e5c6b8343c6f45e98429228467c..a69ceaaf811bd9f84f5b07ec41b0e6e2fcf11537 100644 (file)
--- a/src/game/g_items.pas
+++ b/src/game/g_items.pas
*
* 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
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;
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();
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
gItemsTexturesID: Array [1..ITEM_MAX] of DWORD;
gMaxDist: Integer = 1; // for sounds
- ITEM_RESPAWNTIME: Integer = 60 * 36;
implementation
// ////////////////////////////////////////////////////////////////////////// //
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
it.alive := False;
it.SpawnTrigger := -1;
it.ItemType := ITEM_NONE;
+ it.NeedSend := false;
freeIds.release(LongWord(idx));
end;
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;
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.alive := True;
it.QuietRespawn := False;
it.dropped := false;
+ it.NeedSend := false;
g_Obj_Init(@it.Obj);
it.Obj.X := X;
end;
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);
result := find_id;
end;
+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;
Anim: TAnimation;
- m: Word;
+ m, ItemRespawnTime: Word;
r, nxt: Boolean;
begin
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
// Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòüñÿ ñ äðóãèì èãðîêîì
if r then
begin
- if not Respawnable then g_Items_Remove(i) else g_Items_Pick(i);
+ 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;
Anim.Free();
end;
+ Obj.oldX := InitX;
+ Obj.oldY := InitY;
Obj.X := InitX;
Obj.Y := InitY;
Obj.Vel.X := 0;
procedure itemsDrawInternal (dropflag: Boolean);
var
- i: Integer;
+ i, fX, fY: Integer;
it: PItem;
begin
if (ggItems = nil) then exit;
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;
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], Obj.X, Obj.Y, 0, true, false);
+ e_Draw(gItemsTexturesID[ItemType], fX, fY, 0, true, false);
end
else
begin
- Animation.Draw(Obj.X, Obj.Y, TMirrorType.None);
+ Animation.Draw(fX, fY, TMirrorType.None);
end;
if g_debug_Frames then
begin
if (ID < Length(ggItems)) then
begin
+ ggItems[ID].Obj.oldX := ggItems[ID].Obj.X;
+ ggItems[ID].Obj.oldY := ggItems[ID].Obj.Y;
ggItems[ID].alive := false;
- ggItems[ID].RespawnTime := ITEM_RESPAWNTIME;
+ ggItems[ID].RespawnTime := IfThen(gLMSRespawn = LMS_RESPAWN_NONE, gGameSettings.ItemRespawnTime, 15) * 36;
end;
end;
it: PItem;
trig: Integer;
begin
- if not g_Items_ValidId(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];
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;
releaseItem(ID);
begin
// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
count := 0;
- for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) then Inc(count);
+ for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) and (ggItems[i].slotIsUsed) then Inc(count);
// Êîëè÷åñòâî ïðåäìåòîâ
utils.writeInt(st, LongInt(count));
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
// Ñèãíàòóðà ïðåäìåòà
utils.writeSign(st, 'ITEM');
for i := 0 to High(ggItems) do
begin
it := @ggItems[i];
- if it.Respawnable then
+ 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;
begin
for idx := High(ggItems) downto 0 do
begin
- if ggItems[idx].alive then
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
begin
result := cb(@ggItems[idx]);
if result then exit;
begin
for idx := 0 to High(ggItems) do
begin
- if ggItems[idx].alive then
+ if ggItems[idx].alive and ggItems[idx].slotIsUsed then
begin
result := cb(@ggItems[idx]);
if result then exit;
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);