index 5fb769dbdab703eb9daff973c5877a6ea556025b..3d9e43d2a279aebff18278c46f3db96cf3a3a373 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*)
{$INCLUDE ../shared/a_modes.inc}
+{$M+}
+{.$DEFINE D2F_DEBUG_MONS_MOVE}
unit g_monsters;
interface
uses
+ SysUtils, Classes,
+ mempool,
g_basic, e_graphics, g_phys, g_textures, g_grid,
- g_saveload, BinEditor, g_panel, xprofiler;
+ g_saveload, g_panel, xprofiler;
const
MONSTATE_SLEEP = 0;
MONSTATE_REVIVE = 10;
MONSTATE_RUNOUT = 11;
+ MON_BURN_TIME = 100;
+
+{ in mapdef now
BH_NORMAL = 0;
BH_KILLER = 1;
BH_MANIAC = 2;
BH_INSANE = 3;
BH_CANNIBAL = 4;
BH_GOOD = 5;
+}
type
- TMonster = Class (TObject)
+ PMonster = ^TMonster;
+ TMonster = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
private
FMonsterType: Byte;
FUID: Word;
FMaxHealth: Integer;
FState: Byte;
FCurAnim: Byte;
- FAnim: Array of Array [D_LEFT..D_RIGHT] of TAnimation;
+ FAnim: Array of Array [TDirection.D_LEFT..TDirection.D_RIGHT] of TAnimation;
FTargetUID: Word;
FTargetTime: Integer;
FBehaviour: Byte;
FDieTriggers: Array of Integer;
FSpawnTrigger: Integer;
+ mNeedSend: Boolean; // for network
+
+ mEDamageType: Integer;
+
procedure Turn();
function findNewPrey(): Boolean;
procedure ActivateTriggers();
+ procedure setGameX (v: Integer); inline;
+ procedure setGameY (v: Integer); inline;
+
+ procedure doDamage (v: Integer);
+
public
FNoRespawn: Boolean;
FFireTime: Integer;
trapCheckFrameId: DWord; // for `g_weapons.CheckTrap()`
+ mplatCheckFrameId: LongWord;
constructor Create(MonsterType: Byte; aID: Integer; ForcedUID: Integer = -1);
destructor Destroy(); override;
function Collide(Panel: TPanel): Boolean; overload;
function Collide(X, Y: Integer): Boolean; overload;
function TeleportTo(X, Y: Integer; silent: Boolean; dir: Byte): Boolean;
- function Live(): Boolean;
+ function alive(): Boolean;
procedure SetHealth(aH: Integer);
procedure Push(vx, vy: Integer);
function Damage(aDamage: Word; VelX, VelY: Integer; SpawnerUID: Word; t: Byte): Boolean;
function Heal(Value: Word): Boolean;
procedure BFGHit();
+ procedure PreUpdate();
procedure Update();
procedure ClientUpdate();
procedure ClientAttack(wx, wy, atx, aty: Integer);
procedure AddTrigger(t: Integer);
procedure ClearTriggers();
procedure Respawn();
- procedure SaveState(var Mem: TBinMemoryWriter);
- procedure LoadState(var Mem: TBinMemoryReader);
+ procedure SaveState (st: TStream);
+ procedure LoadState (st: TStream);
procedure SetState(State: Byte; ForceAnim: Byte = 255);
procedure MakeBloodVector(Count: Word; VelX, VelY: Integer);
procedure MakeBloodSimple(Count: Word);
function AnimIsReverse: Boolean;
function shoot(o: PObj; immediately: Boolean): Boolean;
function kick(o: PObj): Boolean;
- procedure CatchFire(Attacker: Word);
+ procedure CatchFire(Attacker: Word; Timeout: Integer = MON_BURN_TIME);
procedure OnFireFlame(Times: DWORD = 1);
procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
+ procedure setPosition (ax, ay: Integer; callPosChanged: Boolean=true); inline;
+ procedure moveBy (dx, dy: Integer); inline;
+
procedure getMapBox (out x, y, w, h: Integer); inline;
+ // get-and-clear
+ function gncNeedSend (): Boolean; inline;
+ procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later
+
+ public
+ property Obj: TObj read FObj;
+
+ property proxyId: Integer read mProxyId;
+ property arrIdx: Integer read mArrIdx;
+
property MonsterType: Byte read FMonsterType;
property MonsterHealth: Integer read FHealth write FHealth;
property MonsterAmmo: Integer read FAmmo write FAmmo;
property MonsterPain: Integer read FPain write FPain;
property MonsterAnim: Byte read FCurAnim write FCurAnim;
- property Obj: TObj read FObj;
property UID: Word read FUID write FUID;
property SpawnTrigger: Integer read FSpawnTrigger write FSpawnTrigger;
- property GameX: Integer read FObj.X write FObj.X;
- property GameY: Integer read FObj.Y write FObj.Y;
+ property GameX: Integer read FObj.X write setGameX;
+ property GameY: Integer read FObj.Y write setGameY;
property GameVelX: Integer read FObj.Vel.X write FObj.Vel.X;
property GameVelY: Integer read FObj.Vel.Y write FObj.Vel.Y;
property GameAccelX: Integer read FObj.Accel.X write FObj.Accel.X;
property StartID: Integer read FStartID;
- property proxyId: Integer read mProxyId;
- property arrIdx: Integer read mArrIdx;
+ published
+ property eMonsterType: Byte read FMonsterType;
+ property eMonsterHealth: Integer read FHealth write FHealth;
+ property eMonsterAmmo: Integer read FAmmo write FAmmo;
+ property eMonsterTargetUID: Word read FTargetUID write FTargetUID;
+ property eMonsterTargetTime: Integer read FTargetTime write FTargetTime;
+ property eMonsterBehaviour: Byte read FBehaviour write FBehaviour;
+ property eMonsterSleep: Integer read FSleep write FSleep;
+ property eMonsterState: Byte read FState write FState;
+ property eMonsterRemoved: Boolean read FRemoved;
+ property eMonsterPain: Integer read FPain write FPain;
+ property eMonsterAnim: Byte read FCurAnim;
+
+ property eUID: Word read FUID;
+ property eSpawnTrigger: Integer read FSpawnTrigger;
+
+ property eGameX: Integer read FObj.X write setGameX;
+ property eGameY: Integer read FObj.Y write setGameY;
+ property eGameVelX: Integer read FObj.Vel.X write FObj.Vel.X;
+ property eGameVelY: Integer read FObj.Vel.Y write FObj.Vel.Y;
+ property eGameAccelX: Integer read FObj.Accel.X write FObj.Accel.X;
+ property eGameAccelY: Integer read FObj.Accel.Y write FObj.Accel.Y;
+ property eGameDirection: TDirection read FDirection write FDirection;
+
+ property eStartID: Integer read FStartID;
+
+ // set this before assigning something to `eDamage`
+ property eDamageType: Integer read mEDamageType write mEDamageType;
+ property eDamage: Integer write doDamage;
end;
procedure g_Monsters_LoadData ();
procedure g_Monsters_FreeData ();
procedure g_Monsters_Init ();
-procedure g_Monsters_Free ();
+procedure g_Monsters_Free (clearGrid: Boolean=true);
function g_Monsters_Create (MonsterType: Byte; X, Y: Integer; Direction: TDirection;
AdjCoord: Boolean = False; ForcedUID: Integer = -1): TMonster;
+procedure g_Monsters_PreUpdate ();
procedure g_Monsters_Update ();
procedure g_Monsters_Draw ();
procedure g_Monsters_DrawHealth ();
function g_Monsters_ByUID (UID: Word): TMonster;
procedure g_Monsters_killedp ();
-procedure g_Monsters_SaveState (var Mem: TBinMemoryWriter);
-procedure g_Monsters_LoadState (var Mem: TBinMemoryReader);
-function g_Monsters_GetIDByName (name: String): Integer;
-function g_Monsters_GetNameByID (MonsterType: Byte): String;
-function g_Monsters_GetKilledBy (MonsterType: Byte): String;
+procedure g_Monsters_SaveState (st: TStream);
+procedure g_Monsters_LoadState (st: TStream);
+
+function g_Mons_SpawnAt (monType: Integer; x, y: Integer; dir: TDirection=TDirection.D_LEFT): TMonster; overload;
+function g_Mons_SpawnAt (const typeName: AnsiString; x, y: Integer; dir: TDirection=TDirection.D_LEFT): TMonster; overload;
+
+function g_Mons_TypeLo (): Integer; inline;
+function g_Mons_TypeHi (): Integer; inline;
+
+function g_Mons_TypeIdByName (const name: AnsiString): Integer;
+function g_Mons_NameByTypeId (monType: Integer): AnsiString;
+function g_Mons_GetKilledByTypeId (monType: Integer): AnsiString;
type
function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
-function g_Mons_getNewTrapFrameId (): DWord;
+function g_Mons_getNewTrapFrameId (): DWord; inline;
+function g_Mons_getNewMPlatFrameId (): LongWord; inline;
+{
type
TMonsAlongLineCB = function (mon: TMonster; tag: Integer): Boolean is nested;
function g_Mons_AlongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB; log: Boolean=false): TMonster;
+}
var
TMonsterGrid = specialize TBodyGridBase<TMonster>;
var
- monsGrid: TMonsterGrid = nil;
+ monsGrid: TMonsterGrid = nil; // DO NOT USE! public for debugging only!
var
implementation
uses
- e_log, g_main, g_sound, g_gfx, g_player, g_game,
+ e_log, e_texture, g_main, g_sound, g_gfx, g_player, g_game,
g_weapons, g_triggers, MAPDEF, g_items, g_options,
- g_console, g_map, Math, SysUtils, g_menu, wadreader,
- g_language, g_netmsg;
+ g_console, g_map, Math, g_menu, wadreader,
+ g_language, g_netmsg, idpool, utils, xstreams;
+
// ////////////////////////////////////////////////////////////////////////// //
procedure g_Mons_ProfilersBegin ();
begin
if (profMonsLOS = nil) then profMonsLOS := TProfiler.Create('LOS CALC', g_profile_history_size);
- profMonsLOS.mainBegin(g_profile_los);
- if g_profile_los then
+ if (profMonsLOS <> nil) then profMonsLOS.mainBegin(g_profile_los);
+ if g_profile_los and (profMonsLOS <> nil) then
begin
profMonsLOS.sectionBegin('loscalc');
profMonsLOS.sectionEnd();
procedure g_Mons_ProfilersEnd ();
begin
- if (profMonsLOS <> nil) and (g_profile_los) then profMapCollision.mainEnd();
+ if (profMonsLOS <> nil) and (g_profile_los) then profMonsLOS.mainEnd();
end;
procedure g_Mons_LOS_Start (); inline;
begin
- profMonsLOS.sectionBeginAccum('loscalc');
+ if (profMonsLOS <> nil) then profMonsLOS.sectionBeginAccum('loscalc');
end;
procedure g_Mons_LOS_End (); inline;
begin
- profMonsLOS.sectionEnd();
+ if (profMonsLOS <> nil) then profMonsLOS.sectionEnd();
end;
// ////////////////////////////////////////////////////////////////////////// //
var
- monCheckTrapLastFrameId: DWord;
+ monCheckTrapLastFrameId: DWord = 0;
+ monCheckMPlatLastFrameId: LongWord = 0;
procedure TMonster.getMapBox (out x, y, w, h: Integer); inline;
h := FObj.Rect.Height;
end;
+function TMonster.gncNeedSend (): Boolean; inline; begin result := mNeedSend; mNeedSend := false; end;
+
+procedure TMonster.setDirty (); inline; begin mNeedSend := true; end;
+
// ////////////////////////////////////////////////////////////////////////// //
+{
function g_Mons_AlongLine (x0, y0, x1, y1: Integer; cb: TMonsAlongLineCB; log: Boolean=false): TMonster;
begin
if not assigned(cb) then begin result := nil; exit; end;
result := monsGrid.forEachAlongLine(x0, y0, x1, y1, cb, -1, log);
end;
+}
//WARNING! call this after monster position was changed, or coldet will not work right!
procedure TMonster.positionChanged ();
var
- x, y: Integer;
+ x, y, w, h: Integer;
+ nx, ny, nw, nh: Integer;
begin
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
//e_WriteLog(Format('monster #%d(%u): pos=(%d,%d); rpos=(%d,%d)', [mArrIdx, UID, FObj.X, FObj.Y, FObj.Rect.X, FObj.Rect.Y]), MSG_NOTIFY);
{$ENDIF}
if (mProxyId = -1) then
begin
+ //mNeedSend := true;
mProxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
monsGrid.getBodyXY(mProxyId, x, y);
- e_WriteLog(Format('monster #%d(%u): inserted into the grid; mProxyid=%d; x=%d; y=%d', [mArrIdx, UID, mProxyId, x, y]), MSG_NOTIFY);
+ e_WriteLog(Format('monster #%d:(%u): inserted into the grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
{$ENDIF}
end
else
begin
- monsGrid.getBodyXY(mProxyId, x, y);
- if (FObj.X+FObj.Rect.X = x) and (FObj.Y+FObj.Rect.Y = y) then exit; // nothing to do
- {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}e_WriteLog(Format('monster #%d(%u): updating tree; mProxyid=%d; x=%d; y=%d', [mArrIdx, UID, mProxyId, x, y]), MSG_NOTIFY);{$ENDIF}
-
- {$IF TRUE}
- monsGrid.moveBody(mProxyId, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y);
- {$ELSE}
- monsGrid.removeBody(mProxyId);
- mProxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
- {$ENDIF}
+ monsGrid.getBodyDims(mProxyId, x, y, w, h);
+ getMapBox(nx, ny, nw, nh);
+ if (w <> nw) or (h <> nh) then
+ begin
+ //mNeedSend := true;
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+ e_WriteLog(Format('monster #%d:(%u): resized; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
+ {$ENDIF}
+ monsGrid.moveResizeBody(mProxyId, nx, ny, nw, nh);
+ end
+ else if (x <> nx) or (y <> ny) then
+ begin
+ //mNeedSend := true;
+ {$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
+ e_WriteLog(Format('monster #%d:(%u): updating grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
+ {$ENDIF}
+ monsGrid.moveBody(mProxyId, nx, ny);
+ end
+ else
+ begin
+ exit; // nothing to do
+ end;
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
monsGrid.getBodyXY(mProxyId, x, y);
- e_WriteLog(Format('monster #%d(%u): updated tree; mProxyid=%d; x=%d; y=%d', [mArrIdx, UID, mProxyId, x, y]), MSG_NOTIFY);
+ e_WriteLog(Format('monster #%d:(%u): updated grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY);
{$ENDIF}
end;
end;
LeftAnim: Boolean;
wX, wY: Integer; // Îòêóäà âûëåòèò ïóëÿ
AnimSpeed: Array [ANIM_SLEEP..ANIM_PAIN] of Byte;
- AnimDeltaRight: Array [ANIM_SLEEP..ANIM_PAIN] of TPoint;
- AnimDeltaLeft: Array [ANIM_SLEEP..ANIM_PAIN] of TPoint;
+ AnimDeltaRight: Array [ANIM_SLEEP..ANIM_PAIN] of TDFPoint;
+ AnimDeltaLeft: Array [ANIM_SLEEP..ANIM_PAIN] of TDFPoint;
end = // SLEEP GO DIE MESS ATTACK ATTACK2 PAIN
((LeftAnim: False; wX: 54; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //DEMON
AnimDeltaRight: ((X: 1; Y: 4), (X: 1; Y: 4), (X: 0; Y: 4), (X: 0; Y: 4), (X: 2; Y: 6), (X: 2; Y: 6), (X: 2; Y: 5));
AnimDeltaRight: ((X: 8; Y: -4), (X: 8; Y: -4), (X: -2; Y: -1), (X: 3; Y: -2), (X: 14; Y: -4), (X: 14; Y: -4), (X: -5; Y: -4));
AnimDeltaLeft: ((X: 8; Y: -4), (X: 8; Y: -4), (X: -2; Y: -1), (X: 3; Y: -2), (X: 14; Y: -4), (X: 14; Y: -4), (X: -5; Y: -4))),
- (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //ZOMBY
+ (LeftAnim: True; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //ZOMBY
AnimDeltaRight: ((X: 1; Y: -4), (X: 1; Y: -4), (X: 3; Y: -1), (X: 2; Y: -1), (X: 2; Y: -4), (X: 2; Y: -4), (X: 1; Y: -4));
AnimDeltaLeft: ((X: 1; Y: -4), (X: 1; Y: -4), (X: 3; Y: -1), (X: 2; Y: -1), (X: 2; Y: -4), (X: 2; Y: -4), (X: 1; Y: -4))),
- (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //SERG
+ (LeftAnim: True; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //SERG
AnimDeltaRight: ((X: 0; Y: -4), (X: 0; Y: -4), (X: -3; Y: -1), (X: -4; Y: -1), (X: 1; Y: -4), (X: 1; Y: -4), (X: 0; Y: -4));
AnimDeltaLeft: ((X: 0; Y: -4), (X: 0; Y: -4), (X: -3; Y: -1), (X: -4; Y: -1), (X: 1; Y: -4), (X: 1; Y: -4), (X: 0; Y: -4))),
MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ
+// ////////////////////////////////////////////////////////////////////////// //
var
gMonsters: array of TMonster;
uidMap: array [0..65535] of TMonster; // monster knows it's index
+ freeInds: TIdPool = nil;
procedure clearUidMap ();
idx: Integer;
begin
for idx := 0 to High(uidMap) do uidMap[idx] := nil;
+ freeInds.clear();
end;
-function g_Mons_getNewTrapFrameId (): DWord;
+function g_Mons_getNewTrapFrameId (): DWord; inline;
var
f: Integer;
begin
Inc(monCheckTrapLastFrameId);
- if monCheckTrapLastFrameId = 0 then
+ if (monCheckTrapLastFrameId = 0) then
begin
// wraparound
monCheckTrapLastFrameId := 1;
end;
+function g_Mons_getNewMPlatFrameId (): LongWord; inline;
+var
+ f: Integer;
+begin
+ Inc(monCheckMPlatLastFrameId);
+ if (monCheckMPlatLastFrameId = 0) then
+ begin
+ // wraparound
+ monCheckMPlatLastFrameId := 1;
+ for f := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[f] <> nil) then gMonsters[f].mplatCheckFrameId := 0;
+ end;
+ end;
+ result := monCheckMPlatLastFrameId;
+end;
+
+
var
pt_x: Integer = 0;
pt_xs: Integer = 1;
soulcount: Integer = 0;
-function allocMonster(): DWORD;
+function allocMonster (): DWORD;
var
- i, olen: Integer;
+ f, olen: Integer;
begin
- for i := 0 to High(gMonsters) do
+ result := freeInds.alloc();
+ if (result > High(gMonsters)) then
begin
- if (gMonsters[i] = nil) then
- begin
- result := i;
- exit;
- end;
- end;
-
- olen := Length(gMonsters);
- if (olen = 0) then
- begin
- SetLength(gMonsters, 64);
- result := 0;
- end
- else
- begin
- result := olen;
- SetLength(gMonsters, Length(gMonsters)+32);
+ olen := Length(gMonsters);
+ SetLength(gMonsters, result+64);
+ for f := olen to High(gMonsters) do gMonsters[f] := nil;
end;
end;
function isCorpse (o: PObj; immediately: Boolean): Integer;
+ (*
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
atag := atag; // shut up, fpc!
result := true;
end;
end;
+ *)
var
a: Integer;
- mon: TMonster;
+ mon: PMonster;
+ mres: TMonster = nil;
+ it: TMonsterGrid.Iter;
begin
result := -1;
// Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè
if gmon_debug_use_sqaccel then
begin
- mon := monsGrid.forEachInAABB(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height, monsCollCheck);
- if (mon <> nil) then result := mon.mArrIdx;
+ //mon := monsGrid.forEachInAABB(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height, monsCollCheck);
+ //if (mon <> nil) then result := mon.mArrIdx;
+ it := monsGrid.forEachInAABB(o.X+o.Rect.X, o.Y+o.Rect.Y, o.Rect.Width, o.Rect.Height);
+ for mon in it do
+ begin
+ case mon.FMonsterType of // Íå âîñêðåñèòü:
+ MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER,
+ MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: begin end;
+ // Îñòàëüíûõ ìîæíî âîñêðåñèòü
+ else mres := mon^;
+ end;
+ if (mres <> nil) then break;
+ end;
+ it.release();
+ if (mres <> nil) then result := mres.mArrIdx;
end
else
begin
procedure g_Monsters_LoadData();
begin
- e_WriteLog('Loading monsters data...', MSG_NOTIFY);
+ e_WriteLog('Loading monsters data...', TMsgType.Notify);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 0%', 0, False);
+ g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES], 133, False);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARREL_SLEEP', GameWAD+':MTEXTURES\BARREL_SLEEP', 64, 64, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARREL_DIE', GameWAD+':MTEXTURES\BARREL_DIE', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARREL_PAIN', GameWAD+':MTEXTURES\BARREL_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_SLEEP', GameWAD+':MTEXTURES\ZOMBY_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_GO', GameWAD+':MTEXTURES\ZOMBY_GO', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_DIE', GameWAD+':MTEXTURES\ZOMBY_DIE', 64, 64, 6);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 5%', 0, True);
+ g_Game_StepLoading(6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_MESS', GameWAD+':MTEXTURES\ZOMBY_MESS', 64, 64, 9);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_ATTACK', GameWAD+':MTEXTURES\ZOMBY_ATTACK', 64, 64, 2);
+ g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_ATTACK_L', GameWAD+':MTEXTURES\ZOMBY_ATTACK_L', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ZOMBY_PAIN', GameWAD+':MTEXTURES\ZOMBY_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_SLEEP', GameWAD+':MTEXTURES\SERG_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_GO', GameWAD+':MTEXTURES\SERG_GO', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_DIE', GameWAD+':MTEXTURES\SERG_DIE', 64, 64, 5);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 10%', 0, True);
+ g_Game_StepLoading(13);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_MESS', GameWAD+':MTEXTURES\SERG_MESS', 64, 64, 9);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_ATTACK', GameWAD+':MTEXTURES\SERG_ATTACK', 64, 64, 2);
+ g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_ATTACK_L', GameWAD+':MTEXTURES\SERG_ATTACK_L', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SERG_PAIN', GameWAD+':MTEXTURES\SERG_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_SLEEP', GameWAD+':MTEXTURES\MAN_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_GO', GameWAD+':MTEXTURES\MAN_GO', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_DIE', GameWAD+':MTEXTURES\MAN_DIE', 64, 64, 7);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 15%', 0, True);
+ g_Game_StepLoading(20);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_MESS', GameWAD+':MTEXTURES\MAN_MESS', 64, 64, 9);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_ATTACK', GameWAD+':MTEXTURES\MAN_ATTACK', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MAN_PAIN', GameWAD+':MTEXTURES\MAN_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_SLEEP', GameWAD+':MTEXTURES\CGUN_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_SLEEP_L', GameWAD+':MTEXTURES\CGUN_SLEEP_L', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_GO', GameWAD+':MTEXTURES\CGUN_GO', 64, 64, 4);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 20%', 0, True);
+ g_Game_StepLoading(26);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_GO_L', GameWAD+':MTEXTURES\CGUN_GO_L', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_DIE', GameWAD+':MTEXTURES\CGUN_DIE', 64, 64, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_MESS', GameWAD+':MTEXTURES\CGUN_MESS', 64, 64, 6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_ATTACK', GameWAD+':MTEXTURES\CGUN_ATTACK', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_ATTACK_L', GameWAD+':MTEXTURES\CGUN_ATTACK_L', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_PAIN', GameWAD+':MTEXTURES\CGUN_PAIN', 64, 64, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 25%', 0, True);
+ g_Game_StepLoading(32);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CGUN_PAIN_L', GameWAD+':MTEXTURES\CGUN_PAIN_L', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_SLEEP', GameWAD+':MTEXTURES\IMP_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_GO', GameWAD+':MTEXTURES\IMP_GO', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_DIE', GameWAD+':MTEXTURES\IMP_DIE', 64, 64, 5);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_MESS', GameWAD+':MTEXTURES\IMP_MESS', 64, 64, 8);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_ATTACK', GameWAD+':MTEXTURES\IMP_ATTACK', 64, 64, 3);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 30%', 0, True);
+ g_Game_StepLoading(38);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_IMP_PAIN', GameWAD+':MTEXTURES\IMP_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_DEMON_SLEEP', GameWAD+':MTEXTURES\DEMON_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_DEMON_GO', GameWAD+':MTEXTURES\DEMON_GO', 64, 64, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_DEMON_DIE', GameWAD+':MTEXTURES\DEMON_DIE', 64, 64, 6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_DEMON_ATTACK', GameWAD+':MTEXTURES\DEMON_ATTACK', 64, 64, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_DEMON_PAIN', GameWAD+':MTEXTURES\DEMON_PAIN', 64, 64, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 35%', 0, True);
+ g_Game_StepLoading(44);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SOUL_SLEEP', GameWAD+':MTEXTURES\SOUL_SLEEP', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SOUL_GO', GameWAD+':MTEXTURES\SOUL_GO', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SOUL_PAIN', GameWAD+':MTEXTURES\SOUL_PAIN', 64, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SOUL_ATTACK', GameWAD+':MTEXTURES\SOUL_ATTACK', 64, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SOUL_DIE', GameWAD+':MTEXTURES\SOUL_DIE', 128, 128, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_FISH_SLEEP', GameWAD+':MTEXTURES\FISH_SLEEP', 32, 32, 2);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 40%', 0, True);
+ g_Game_StepLoading(50);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_FISH_GO', GameWAD+':MTEXTURES\FISH_GO', 32, 32, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_FISH_PAIN', GameWAD+':MTEXTURES\FISH_PAIN', 32, 32, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_FISH_ATTACK', GameWAD+':MTEXTURES\FISH_ATTACK', 32, 32, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_FISH_DIE', GameWAD+':MTEXTURES\FISH_DIE', 32, 32, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_SLEEP', GameWAD+':MTEXTURES\SPIDER_SLEEP', 256, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_GO', GameWAD+':MTEXTURES\SPIDER_GO', 256, 128, 6);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 45%', 0, True);
+ g_Game_StepLoading(56);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_PAIN', GameWAD+':MTEXTURES\SPIDER_PAIN', 256, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_PAIN_L', GameWAD+':MTEXTURES\SPIDER_PAIN_L', 256, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_ATTACK', GameWAD+':MTEXTURES\SPIDER_ATTACK', 256, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SPIDER_DIE', GameWAD+':MTEXTURES\SPIDER_DIE', 256, 128, 10);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_SLEEP', GameWAD+':MTEXTURES\BSP_SLEEP', 128, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_GO', GameWAD+':MTEXTURES\BSP_GO', 128, 64, 6);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 50%', 0, True);
+ g_Game_StepLoading(62);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_PAIN', GameWAD+':MTEXTURES\BSP_PAIN', 128, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_PAIN_L', GameWAD+':MTEXTURES\BSP_PAIN_L', 128, 64, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_ATTACK', GameWAD+':MTEXTURES\BSP_ATTACK', 128, 64, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BSP_DIE', GameWAD+':MTEXTURES\BSP_DIE', 128, 64, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CACO_SLEEP', GameWAD+':MTEXTURES\CACO_SLEEP', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CACO_GO', GameWAD+':MTEXTURES\CACO_GO', 128, 128, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 55%', 0, True);
+ g_Game_StepLoading(68);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CACO_PAIN', GameWAD+':MTEXTURES\CACO_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CACO_ATTACK', GameWAD+':MTEXTURES\CACO_ATTACK', 128, 128, 6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CACO_DIE', GameWAD+':MTEXTURES\CACO_DIE', 128, 128, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_PAIN_SLEEP', GameWAD+':MTEXTURES\PAIN_SLEEP', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_PAIN_GO', GameWAD+':MTEXTURES\PAIN_GO', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_PAIN_PAIN', GameWAD+':MTEXTURES\PAIN_PAIN', 128, 128, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 60%', 0, True);
+ g_Game_StepLoading(74);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_PAIN_ATTACK', GameWAD+':MTEXTURES\PAIN_ATTACK', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_PAIN_DIE', GameWAD+':MTEXTURES\PAIN_DIE', 128, 128, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_SLEEP', GameWAD+':MTEXTURES\BARON_SLEEP', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_SLEEP_L', GameWAD+':MTEXTURES\BARON_SLEEP_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_GO', GameWAD+':MTEXTURES\BARON_GO', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_PAIN', GameWAD+':MTEXTURES\BARON_PAIN', 128, 128, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 65%', 0, True);
+ g_Game_StepLoading(80);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_PAIN_L', GameWAD+':MTEXTURES\BARON_PAIN_L', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_ATTACK', GameWAD+':MTEXTURES\BARON_ATTACK', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_ATTACK_L', GameWAD+':MTEXTURES\BARON_ATTACK_L', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_BARON_DIE', GameWAD+':MTEXTURES\BARON_DIE', 128, 128, 7);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_SLEEP', GameWAD+':MTEXTURES\KNIGHT_SLEEP', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_SLEEP_L', GameWAD+':MTEXTURES\KNIGHT_SLEEP_L', 128, 128, 2);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 70%', 0, True);
+ g_Game_StepLoading(86);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_GO', GameWAD+':MTEXTURES\KNIGHT_GO', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_PAIN', GameWAD+':MTEXTURES\KNIGHT_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_PAIN_L', GameWAD+':MTEXTURES\KNIGHT_PAIN_L', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_ATTACK', GameWAD+':MTEXTURES\KNIGHT_ATTACK', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_ATTACK_L', GameWAD+':MTEXTURES\KNIGHT_ATTACK_L', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_KNIGHT_DIE', GameWAD+':MTEXTURES\KNIGHT_DIE', 128, 128, 7);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 75%', 0, True);
+ g_Game_StepLoading(92);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MANCUB_SLEEP', GameWAD+':MTEXTURES\MANCUB_SLEEP', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MANCUB_GO', GameWAD+':MTEXTURES\MANCUB_GO', 128, 128, 6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MANCUB_PAIN', GameWAD+':MTEXTURES\MANCUB_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MANCUB_ATTACK', GameWAD+':MTEXTURES\MANCUB_ATTACK', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_MANCUB_DIE', GameWAD+':MTEXTURES\MANCUB_DIE', 128, 128, 10);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_SLEEP', GameWAD+':MTEXTURES\SKEL_SLEEP', 128, 128, 2);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 80%', 0, True);
+ g_Game_StepLoading(98);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_SLEEP_L', GameWAD+':MTEXTURES\SKEL_SLEEP_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_GO', GameWAD+':MTEXTURES\SKEL_GO', 128, 128, 6);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_PAIN', GameWAD+':MTEXTURES\SKEL_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_PAIN_L', GameWAD+':MTEXTURES\SKEL_PAIN_L', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_ATTACK', GameWAD+':MTEXTURES\SKEL_ATTACK', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_ATTACK_L', GameWAD+':MTEXTURES\SKEL_ATTACK_L', 128, 128, 2);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 85%', 0, True);
+ g_Game_StepLoading(104);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_ATTACK2', GameWAD+':MTEXTURES\SKEL_ATTACK2', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_ATTACK2_L', GameWAD+':MTEXTURES\SKEL_ATTACK2_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_SKEL_DIE', GameWAD+':MTEXTURES\SKEL_DIE', 128, 128, 5);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_SLEEP', GameWAD+':MTEXTURES\VILE_SLEEP', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_SLEEP_L', GameWAD+':MTEXTURES\VILE_SLEEP_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_GO', GameWAD+':MTEXTURES\VILE_GO', 128, 128, 6);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 90%', 0, True);
+ g_Game_StepLoading(110);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_PAIN', GameWAD+':MTEXTURES\VILE_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_PAIN_L', GameWAD+':MTEXTURES\VILE_PAIN_L', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_ATTACK', GameWAD+':MTEXTURES\VILE_ATTACK', 128, 128, 10);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_ATTACK_L', GameWAD+':MTEXTURES\VILE_ATTACK_L', 128, 128, 10);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_ATTACK2', GameWAD+':MTEXTURES\VILE_ATTACK2', 128, 128, 3);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_ATTACK2_L', GameWAD+':MTEXTURES\VILE_ATTACK2_L', 128, 128, 3);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 95%', 0, True);
+ g_Game_StepLoading(116);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_VILE_DIE', GameWAD+':MTEXTURES\VILE_DIE', 128, 128, 9);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ROBO_SLEEP', GameWAD+':MTEXTURES\ROBO_SLEEP', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ROBO_GO', GameWAD+':MTEXTURES\ROBO_GO', 128, 128, 12);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ROBO_ATTACK', GameWAD+':MTEXTURES\ROBO_ATTACK', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ROBO_ATTACK2', GameWAD+':MTEXTURES\ROBO_ATTACK2', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_ROBO_DIE', GameWAD+':MTEXTURES\ROBO_DIE', 128, 128, 1);
- g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 100%', 0, True);
+ g_Game_StepLoading(122);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_SLEEP', GameWAD+':MTEXTURES\CYBER_SLEEP', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_SLEEP_L', GameWAD+':MTEXTURES\CYBER_SLEEP_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_GO', GameWAD+':MTEXTURES\CYBER_GO', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_GO_L', GameWAD+':MTEXTURES\CYBER_GO_L', 128, 128, 4);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_PAIN', GameWAD+':MTEXTURES\CYBER_PAIN', 128, 128, 1);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_PAIN_L', GameWAD+':MTEXTURES\CYBER_PAIN_L', 128, 128, 1);
+ g_Game_StepLoading(128);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_ATTACK', GameWAD+':MTEXTURES\CYBER_ATTACK', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_ATTACK_L', GameWAD+':MTEXTURES\CYBER_ATTACK_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_ATTACK2', GameWAD+':MTEXTURES\CYBER_ATTACK2', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_ATTACK2_L', GameWAD+':MTEXTURES\CYBER_ATTACK2_L', 128, 128, 2);
g_Frames_CreateWAD(nil, 'FRAMES_MONSTER_CYBER_DIE', GameWAD+':MTEXTURES\CYBER_DIE', 128, 128, 9);
+ g_Game_StepLoading(133);
g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_SOUNDS], 0, False);
g_Sound_CreateWADEx('SOUND_MONSTER_FISH_ATTACK', GameWAD+':MSOUNDS\FISH_ATTACK');
+ freeInds := TIdPool.Create();
clearUidMap();
monCheckTrapLastFrameId := 0;
+ monCheckMPlatLastFrameId := 0;
end;
procedure g_Monsters_FreeData();
begin
- e_WriteLog('Releasing monsters data...', MSG_NOTIFY);
+ e_WriteLog('Releasing monsters data...', TMsgType.Notify);
g_Frames_DeleteByName('FRAMES_MONSTER_BARREL_SLEEP');
g_Frames_DeleteByName('FRAMES_MONSTER_BARREL_PAIN');
g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_DIE');
g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_MESS');
g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_ATTACK');
+ g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_ATTACK_L');
g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_PAIN');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_SLEEP');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_GO');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_DIE');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_MESS');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_ATTACK');
+ g_Frames_DeleteByName('FRAMES_MONSTER_SERG_ATTACK_L');
g_Frames_DeleteByName('FRAMES_MONSTER_SERG_PAIN');
g_Frames_DeleteByName('FRAMES_MONSTER_MAN_SLEEP');
g_Frames_DeleteByName('FRAMES_MONSTER_MAN_GO');
g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK');
g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK');
+
+ freeInds.Free();
+ freeInds := nil;
end;
procedure g_Monsters_Init();
soulcount := 0;
end;
-procedure g_Monsters_Free();
+procedure g_Monsters_Free (clearGrid: Boolean=true);
var
a: Integer;
begin
- monsGrid.Free();
- monsGrid := nil;
+ e_LogWritefln('Cleared monster data (clearGrid=%s)', [clearGrid]);
+ if (clearGrid) then
+ begin
+ monsGrid.Free();
+ monsGrid := nil;
+ end;
for a := 0 to High(gMonsters) do gMonsters[a].Free();
gMonsters := nil;
clearUidMap();
monCheckTrapLastFrameId := 0;
+ monCheckMPlatLastFrameId := 0;
end;
begin
monsGrid.Free();
monsGrid := TMonsterGrid.Create(x, y, w, h);
+ //clearUidMap(); // why not?
+ e_LogWritefln('%s', ['Recreated monster tree']);
end;
FStartDirection := Direction;
FStartX := GameX;
FStartY := GameY;
+ FObj.oldX := FObj.X;
+ FObj.oldY := FObj.Y;
end;
mon.positionChanged();
if gMonsters = nil then
Exit;
-// Ïðèêîëèñò ñìååòñÿ íàä ñìåðòüþ èãðîêà:
+ // Ïðèêîëèñò ñìååòñÿ íàä ñìåðòüþ èãðîêà:
h := High(gMonsters);
for a := 0 to h do
+ begin
if (gMonsters[a] <> nil) then
+ begin
with gMonsters[a] do
+ begin
if (FMonsterType = MONSTER_MAN) and
(FState <> MONSTATE_DEAD) and
(FState <> MONSTATE_SLEEP) and
g_Sound_PlayExAt('SOUND_MONSTER_TRUP', FObj.X, FObj.Y);
Exit;
end;
+ end;
+ end;
+ end;
+end;
+
+procedure g_Monsters_PreUpdate();
+var
+ a: Integer;
+begin
+ if gMonsters = nil then Exit;
+ for a := 0 to High(gMonsters) do
+ if (gMonsters[a] <> nil) and (not gMonsters[a].FRemoved) then
+ gMonsters[a].PreUpdate();
end;
procedure g_Monsters_Update();
a: Integer;
begin
if gMonsters <> nil then
+ begin
for a := 0 to High(gMonsters) do
- if gMonsters[a] <> nil then
- gMonsters[a].Draw();
+ begin
+ if (gMonsters[a] <> nil) then gMonsters[a].Draw();
+ end;
+ end;
end;
procedure g_Monsters_DrawHealth();
e_TextureFontGetSize(gStdFont, fW, fH);
for a := 0 to High(gMonsters) do
+ begin
if gMonsters[a] <> nil then
begin
e_TextureFontPrint(gMonsters[a].FObj.X + gMonsters[a].FObj.Rect.X,
gMonsters[a].FObj.Y + gMonsters[a].FObj.Rect.Y + gMonsters[a].FObj.Rect.Height - fH,
IntToStr(gMonsters[a].FHealth), gStdFont);
end;
+ end;
end;
function g_Monsters_ByUID (UID: Word): TMonster;
-//var a: Integer;
begin
result := uidMap[UID];
- {
- Result := nil;
- if gMonsters <> nil then
- for a := 0 to High(gMonsters) do
- if (gMonsters[a] <> nil) and
- (gMonsters[a].FUID = UID) then
- begin
- Result := gMonsters[a];
- Break;
- end;
- }
end;
-procedure g_Monsters_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Monsters_SaveState (st: TStream);
var
count, i: Integer;
- b: Byte;
begin
-// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ìîíñòðîâ:
+ // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ìîíñòðîâ
count := 0;
- if gMonsters <> nil then
- for i := 0 to High(gMonsters) do
- if gMonsters[i] <> nil then
- if gMonsters[i].FMonsterType <> MONSTER_NONE then
- count := count + 1;
-
- Mem := TBinMemoryWriter.Create((count+1) * 350);
+ for i := 0 to High(gMonsters) do
+ begin
+ if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then count += 1;
+ end;
-// Ñîõðàíÿåì èíôîðìàöèþ öåëåóêàçàòåëÿ:
- Mem.WriteInt(pt_x);
- Mem.WriteInt(pt_xs);
- Mem.WriteInt(pt_y);
- Mem.WriteInt(pt_ys);
+ // Ñîõðàíÿåì èíôîðìàöèþ öåëåóêàçàòåëÿ
+ utils.writeInt(st, LongInt(pt_x));
+ utils.writeInt(st, LongInt(pt_xs));
+ utils.writeInt(st, LongInt(pt_y));
+ utils.writeInt(st, LongInt(pt_ys));
-// Êîëè÷åñòâî ìîíñòðîâ:
- Mem.WriteInt(count);
+ // Êîëè÷åñòâî ìîíñòðîâ
+ utils.writeInt(st, LongInt(count));
- if count = 0 then
- Exit;
+ if (count = 0) then exit;
-// Ñîõðàíÿåì ìîíñòðîâ:
+ // Ñîõðàíÿåì ìîíñòðîâ
for i := 0 to High(gMonsters) do
- if gMonsters[i] <> nil then
- if gMonsters[i].FMonsterType <> MONSTER_NONE then
- begin
- // Òèï ìîíñòðà:
- b := gMonsters[i].MonsterType;
- Mem.WriteByte(b);
+ begin
+ if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then
+ begin
+ // Òèï ìîíñòðà
+ utils.writeInt(st, Byte(gMonsters[i].MonsterType));
// Ñîõðàíÿåì äàííûå ìîíñòðà:
- gMonsters[i].SaveState(Mem);
- end;
+ gMonsters[i].SaveState(st);
+ end;
+ end;
end;
-procedure g_Monsters_LoadState(var Mem: TBinMemoryReader);
+
+procedure g_Monsters_LoadState (st: TStream);
var
count, a: Integer;
b: Byte;
mon: TMonster;
begin
- if Mem = nil then exit;
+ assert(st <> nil);
- g_Monsters_Free();
+ g_Monsters_Free(false);
// Çàãðóæàåì èíôîðìàöèþ öåëåóêàçàòåëÿ
- Mem.ReadInt(pt_x);
- Mem.ReadInt(pt_xs);
- Mem.ReadInt(pt_y);
- Mem.ReadInt(pt_ys);
+ pt_x := utils.readLongInt(st);
+ pt_xs := utils.readLongInt(st);
+ pt_y := utils.readLongInt(st);
+ pt_ys := utils.readLongInt(st);
// Êîëè÷åñòâî ìîíñòðîâ
- Mem.ReadInt(count);
+ count := utils.readLongInt(st);
- if count = 0 then exit;
+ if (count = 0) then exit;
+ if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid monster count');
// Çàãðóæàåì ìîíñòðîâ
for a := 0 to count-1 do
begin
// Òèï ìîíñòðà
- Mem.ReadByte(b);
+ b := utils.readByte(st);
// Ñîçäàåì ìîíñòðà
- mon := g_Monsters_Create(b, 0, 0, D_LEFT);
- if mon = nil then raise EBinSizeError.Create('g_Monsters_LoadState: ID = -1 (Can''t create)');
+ mon := g_Monsters_Create(b, 0, 0, TDirection.D_LEFT);
+ if (mon = nil) then raise XStreamError.Create('g_Monsters_LoadState: ID = -1 (can''t create)');
// Çàãðóæàåì äàííûå ìîíñòðà
- mon.LoadState(Mem);
+ mon.LoadState(st);
+ end;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Mons_SpawnAt (monType: Integer; x, y: Integer; dir: TDirection=TDirection.D_LEFT): TMonster; overload;
+begin
+ result := nil;
+ if (monType >= MONSTER_DEMON) and (monType <= MONSTER_MAN) then
+ begin
+ result := g_Monsters_Create(monType, x, y, dir);
end;
end;
-function g_Monsters_GetIDByName(name: String): Integer;
+
+function g_Mons_SpawnAt (const typeName: AnsiString; x, y: Integer; dir: TDirection=TDirection.D_LEFT): TMonster; overload;
+begin
+ result := g_Mons_SpawnAt(g_Mons_TypeIdByName(typeName), x, y, dir);
+end;
+
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+function g_Mons_TypeLo (): Integer; inline; begin result := Low(MONSTERTABLE); end;
+function g_Mons_TypeHi (): Integer; inline; begin result := High(MONSTERTABLE); end;
+
+
+function g_Mons_TypeIdByName (const name: String): Integer;
var
i: Integer;
begin
- name := UpperCase(name);
i := MONSTER_DEMON;
while (i <= MONSTER_MAN) do
begin
- if name = MONSTERTABLE[i].Name then
+ if (CompareText(name, MONSTERTABLE[i].Name) = 0) then
begin
- Result := i;
- Exit;
+ result := i;
+ exit;
end;
Inc(i);
end;
-
- Result := -1;
+ result := -1;
+ // HACK!
+ if (CompareText(name, 'zombie') = 0) then result := MONSTER_ZOMBY;
end;
-function g_Monsters_GetNameByID(MonsterType: Byte): String;
+
+function g_Mons_NameByTypeId (monType: Integer): AnsiString;
begin
- if MonsterType in [MONSTER_DEMON..MONSTER_MAN] then
- Result := MONSTERTABLE[MonsterType].Name
+ if (monType >= MONSTER_DEMON) and (monType <= MONSTER_MAN) then
+ result := MONSTERTABLE[monType].Name
else
- Result := '?';
+ result := '?';
end;
-function g_Monsters_GetKilledBy(MonsterType: Byte): String;
+
+function g_Mons_GetKilledByTypeId (monType: Integer): AnsiString;
begin
- if MonsterType in [MONSTER_DEMON..MONSTER_MAN] then
- Result := KilledByMonster[MonsterType]
+ if (monType >= MONSTER_DEMON) and (monType <= MONSTER_MAN) then
+ Result := KilledByMonster[monType]
else
Result := '?';
end;
+
+// ////////////////////////////////////////////////////////////////////////// //
{ T M o n s t e r : }
+procedure TMonster.setGameX (v: Integer); inline; begin FObj.X := v; positionChanged(); end;
+procedure TMonster.setGameY (v: Integer); inline; begin FObj.Y := v; positionChanged(); end;
+
+procedure TMonster.setPosition (ax, ay: Integer; callPosChanged: Boolean=true); inline; begin FObj.X := ax; FObj.Y := ay; if callPosChanged then positionChanged(); end;
+
+procedure TMonster.moveBy (dx, dy: Integer); inline;
+begin
+ if (dx <> 0) or (dy <> 0) then
+ begin
+ FObj.X += dx;
+ FObj.Y += dy;
+ positionChanged();
+ end;
+end;
+
+procedure TMonster.doDamage (v: Integer);
+begin
+ if (v <= 0) then exit;
+ if (v > 32767) then v := 32767;
+ Damage(v, 0, 0, 0, mEDamageType);
+end;
+
procedure TMonster.ActionSound();
begin
case FMonsterType of
FObj.Accel.X := 0;
FObj.Accel.Y := 0;
FDirection := FStartDirection;
- GameX := FStartX;
- GameY := FStartY;
+ {GameX}FObj.X := FStartX;
+ {GameY}FObj.Y := FStartY;
FObj.Rect := MONSTERTABLE[FMonsterType].Rect;
FHealth := MONSTERTABLE[FMonsterType].Health;
FAmmo := 0;
FFireTime := 0;
FFirePainTime := 0;
FFireAttacker := 0;
+ mEDamageType := HIT_SOME;
mProxyId := -1;
mArrIdx := -1;
trapCheckFrameId := 0;
+ mplatCheckFrameId := 0;
+ mNeedSend := false;
if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then
FBloodKind := BLOOD_SPARKS
for a := 0 to High(FAnim) do
begin
- FAnim[a, D_LEFT] := nil;
- FAnim[a, D_RIGHT] := nil;
+ FAnim[a, TDirection.D_LEFT] := nil;
+ FAnim[a, TDirection.D_RIGHT] := nil;
end;
for a := ANIM_SLEEP to ANIM_PAIN do
if g_Frames_Get(FramesID, 'FRAMES_MONSTER_'+MONSTERTABLE[MonsterType].Name+
'_'+ANIMTABLE[ANIM_DIE].name) then
begin
- FAnim[a, D_RIGHT] := TAnimation.Create(FramesID, ANIMTABLE[ANIM_DIE].loop,
+ FAnim[a, TDirection.D_RIGHT] := TAnimation.Create(FramesID, ANIMTABLE[ANIM_DIE].loop,
MONSTER_ANIMTABLE[MonsterType].AnimSpeed[ANIM_DIE]);
- FAnim[a, D_LEFT] := TAnimation.Create(FramesID, ANIMTABLE[ANIM_DIE].loop,
+ FAnim[a, TDirection.D_LEFT] := TAnimation.Create(FramesID, ANIMTABLE[ANIM_DIE].loop,
MONSTER_ANIMTABLE[MonsterType].AnimSpeed[ANIM_DIE]);
Continue;
end;
end;
- FAnim[a, D_RIGHT] := TAnimation.Create(FramesID, ANIMTABLE[a].loop,
+ FAnim[a, TDirection.D_RIGHT] := TAnimation.Create(FramesID, ANIMTABLE[a].loop,
MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a]);
// Åñëè åñòü îòäåëüíàÿ ëåâàÿ àíèìàöèÿ - çàãðóæàåì:
g_Frames_Get(FramesID, s);
end;
- FAnim[a, D_LEFT] := TAnimation.Create(FramesID, ANIMTABLE[a].loop,
+ FAnim[a, TDirection.D_LEFT] := TAnimation.Create(FramesID, ANIMTABLE[a].loop,
MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a]);
end;
begin
Result := False;
+// Ìîíñòð ñòàòè÷åí ïîêà èäåò warmup
+ if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
+
// Óìèðàåò, óìåð èëè âîñêðåøàåòñÿ => óðîí äåëàòü íåêîìó:
if (FState = MONSTATE_DEAD) or (FState = MONSTATE_DIE) or (FState = MONSTATE_REVIVE) then
Exit;
if (t = HIT_ELECTRO) and (FMonsterType = MONSTER_FISH) and g_Game_IsServer then
begin
FSleep := 20;
- if Random(2) = 0 then
- FDirection := D_RIGHT
- else
- FDirection := D_LEFT;
+ if Random(2) = 0 then FDirection := TDirection.D_RIGHT else FDirection := TDirection.D_LEFT;
Result := True;
SetState(MONSTATE_RUN);
Exit;
end;
+// Àð÷è íå ãîðÿò, ÷åðåïà óæå ãîðÿò
+ if (t = HIT_FLAME) and (FMonsterType in [MONSTER_VILE, MONSTER_SOUL]) then
+ begin
+ // Ïðîñíóòüñÿ âñå-òàêè ñòîèò
+ if FState = MONSTATE_SLEEP then
+ SetState(MONSTATE_GO);
+ Exit;
+ end;
+
// Ëîâóøêà óáèâàåò ñðàçó:
if t = HIT_TRAP then
FHealth := -100;
it := g_Items_Create(FObj.X + (FObj.Rect.Width div 2),
FObj.Y + (FObj.Rect.Height div 2),
c, True, False);
+ g_Items_SetDrop(it); // mark it as monster drop
g_Obj_Push(g_Items_ObjByIdx(it), (FObj.Vel.X div 2)-3+Random(7),
(FObj.Vel.Y div 2)-Random(4));
- positionChanged(); // this updates spatial accelerators
+ //positionChanged(); // this updates spatial accelerators
if g_Game_IsServer and g_Game_IsNet then
MH_SEND_ItemSpawn(True, it);
end;
begin
FObj.Rect.Y := FObj.Rect.Y + FObj.Rect.Height-12;
FObj.Rect.Height := 12;
+ positionChanged();
end;
// Óðîí áûë ñèëüíûì => ñëàáûå - â êàøó:
Result := False;
if g_Game_IsClient then
Exit;
- if not Live then
+ if not alive then
Exit;
if FHealth < FMaxHealth then
begin
for a := 0 to High(FAnim) do
begin
- FAnim[a, D_LEFT].Free();
- FAnim[a, D_RIGHT].Free();
+ FAnim[a, TDirection.D_LEFT].Free();
+ FAnim[a, TDirection.D_RIGHT].Free();
end;
vilefire.Free();
begin
monsGrid.removeBody(mProxyId);
{$IF DEFINED(D2F_DEBUG_MONS_MOVE)}
- e_WriteLog(Format('monster #%d(%u): removed from tree; mProxyid=%d', [mArrIdx, UID, mProxyId]), MSG_NOTIFY);
+ e_WriteLog(Format('monster #%d:(%u): removed from grid; mProxyid=%d', [mArrIdx, UID, mProxyId]), MSG_NOTIFY);
{$ENDIF}
end;
mProxyId := -1;
end;
- if (mArrIdx <> -1) and (mArrIdx < Length(gMonsters)) then gMonsters[mArrIdx] := nil;
+ if (mArrIdx <> -1) and (mArrIdx < Length(gMonsters)) then
+ begin
+ freeInds.release(mArrIdx);
+ gMonsters[mArrIdx] := nil;
+ end;
mArrIdx := -1;
uidMap[FUID] := nil;
procedure TMonster.Draw();
var
m: TMirrorType;
- dx, dy, c: Integer;
+ dx, dy, c, fX, fY: Integer;
o: TObj;
begin
//e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, 'TYPE: '+IntToStr(FMonsterType));
//e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y+16, 'STATE: '+IntToStr(FState));
+ FObj.lerp(gLerpFactor, fX, fY);
+
// Åñëè êîëäóí ñòðåëÿåò, òî ðèñóåì îãîíü:
if FMonsterType = MONSTER_VILE then
if FState = MONSTATE_SHOOT then
if GetPos(FTargetUID, @o) then
vilefire.Draw(o.X+o.Rect.X+(o.Rect.Width div 2)-32,
- o.Y+o.Rect.Y+o.Rect.Height-128, M_NONE);
+ o.Y+o.Rect.Y+o.Rect.Height-128, TMirrorType.None);
// Íå â îáëàñòè ðèñîâàíèÿ íå ðåñóåì:
- if not g_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height,
- sX-128, sY-128, sWidth+256, sHeight+256) then
- Exit;
+//FIXME!
+ if (g_dbg_scale = 1.0) then
+ begin
+ if not g_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height,
+ sX-128, sY-128, sWidth+256, sHeight+256) then
+ Exit;
+ end;
// Ýòè ìîíñòðû, óìèðàÿ, íå îñòàâëÿþò òðóïîâ:
if FState = MONSTATE_DEAD then
if FAnim[FCurAnim, FDirection] <> nil then
begin
// Åñëè íåò ëåâîé àíèìàöèè èëè îíà ñîâïàäàåò ñ ïðàâîé => îòðàæàåì ïðàâóþ:
- if (FDirection = D_LEFT) and
+ if (FDirection = TDirection.D_LEFT) and
((not MONSTER_ANIMTABLE[FMonsterType].LeftAnim) or
- (FAnim[FCurAnim, D_LEFT].FramesID = FAnim[FCurAnim, D_RIGHT].FramesID)) and
+ (FAnim[FCurAnim, TDirection.D_LEFT].FramesID = FAnim[FCurAnim, TDirection.D_RIGHT].FramesID)) and
(FMonsterType <> MONSTER_BARREL) then
- m := M_HORIZONTAL
+ m := TMirrorType.Horizontal
else
- m := M_NONE;
+ m := TMirrorType.None;
// Ëåâàÿ àíèìàöèÿ => ìåíÿåì ñìåùåíèå îòíîñèòåëüíî öåíòðà:
- if (FDirection = D_LEFT) and
+ if (FDirection = TDirection.D_LEFT) and
(FMonsterType <> MONSTER_BARREL) then
begin
dx := MONSTER_ANIMTABLE[FMonsterType].AnimDeltaLeft[FCurAnim].X;
dy := MONSTER_ANIMTABLE[FMonsterType].AnimDeltaLeft[FCurAnim].Y;
- if m = M_HORIZONTAL then
+ if m = TMirrorType.Horizontal then
begin // Íåò îòäåëüíîé ëåâîé àíèìàöèè
// Ðàññòîÿíèå îò êðàÿ òåêñòóðû äî êðàÿ âèçóàëüíîãî ïîëîæåíèÿ îáúåêòà íà òåêñòóðå:
c := (MONSTERTABLE[FMonsterType].Rect.X - dx) + MONSTERTABLE[FMonsterType].Rect.Width;
end;
// Ðèñóåì:
- FAnim[FCurAnim, FDirection].Draw(Obj.X+dx, Obj.Y+dy, m);
+ FAnim[FCurAnim, FDirection].Draw(fX+dx, fY+dy, m);
end;
if g_debug_Frames then
FObj.X := X - FObj.Rect.X;
FObj.Y := Y - FObj.Rect.Y;
+ FObj.oldX := FObj.X; // don't interpolate after teleport
+ FObj.oldY := FObj.Y;
positionChanged();
if dir = 1 then
- FDirection := D_LEFT
+ FDirection := TDirection.D_LEFT
else
if dir = 2 then
- FDirection := D_RIGHT
+ FDirection := TDirection.D_RIGHT
else
if dir = 3 then
begin // îáðàòíîå
- if FDirection = D_RIGHT then
- FDirection := D_LEFT
+ if FDirection = TDirection.D_RIGHT then
+ FDirection := TDirection.D_LEFT
else
- FDirection := D_RIGHT;
+ FDirection := TDirection.D_RIGHT;
end;
// Ýôôåêò òåëåïîðòà â òî÷êå íàçíà÷åíèÿ:
Result := True;
end;
+procedure TMonster.PreUpdate();
+begin
+ FObj.oldX := FObj.X;
+ FObj.oldY := FObj.Y;
+end;
+
procedure TMonster.Update();
var
a, b, sx, sy, wx, wy, oldvelx: Integer;
o, co: TObj;
fall: Boolean;
mon: TMonster;
+ mit: PMonster;
+ it: TMonsterGrid.Iter;
label
_end;
begin
fall := True;
+// Ìîíñòð ñòàòè÷åí ïîêà èäåò warmup
+ if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
+
// Ðûáû "ëåòàþò" òîëüêî â âîäå:
if FMonsterType = MONSTER_FISH then
if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_WATER or PANEL_ACID1 or PANEL_ACID2) then
st := g_Obj_Move(@FObj, fall, True, True);
positionChanged(); // this updates spatial accelerators
+// Åñëè ãîðèì - ïîäæèãàåì äðóãèõ ìîíñòðîâ, íî íå íà 100 òèêîâ êàæäûé ðàç:
+ if FFireTime > 0 then
+ begin
+ it := monsGrid.forEachInAABB(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
+ for mit in it do
+ if mit.UID <> FUID then
+ mit.CatchFire(FFireAttacker, FFireTime);
+ end;
+
// Âûëåòåë çà êàðòó - óäàëÿåì è çàïóñêàåì òðèããåðû:
if WordBool(st and MOVE_FALLOUT) or (FObj.X < -1000) or
(FObj.X > gMapInfo.Width+1000) or (FObj.Y < -1000) then
begin
FRemoved := True;
- if Live and (gLMSRespawn = LMS_RESPAWN_NONE) then
+ if alive and (gLMSRespawn = LMS_RESPAWN_NONE) then
begin
Inc(gCoopMonstersKilled);
if g_Game_IsNet then
// Åñëè åñòü èãðîê ðÿäîì, ïðîñûïàåìñÿ è èäåì ê íåìó:
if (gPlayers <> nil) then
for a := 0 to High(gPlayers) do
- if (gPlayers[a] <> nil) and (gPlayers[a].Live)
+ if (gPlayers[a] <> nil) and (gPlayers[a].alive)
and (not gPlayers[a].NoTarget) and (gPlayers[a].FMegaRulez[MR_INVIS] < gTime) then
with gPlayers[a] do
if g_Look(@FObj, @Obj, FDirection) then
// Åñëè åñòü ïîäõîäÿùèé ìîíñòð ðÿäîì:
if gMonsters <> nil then
for a := 0 to High(gMonsters) do
- if (gMonsters[a] <> nil) and (gMonsters[a].Live) and
+ if (gMonsters[a] <> nil) and (gMonsters[a].alive) and
(gMonsters[a].FUID <> FUID) then
begin
// Ìàíüÿêè íàïàäàþò íà âñåõ ìîíñòðîâ, êðîìå äðóçåé
// Ïîâîðà÷èâàåìñÿ â ñòîðîíó öåëè:
if sx > 0 then
- FDirection := D_RIGHT
+ FDirection := TDirection.D_RIGHT
else
- FDirection := D_LEFT;
+ FDirection := TDirection.D_LEFT;
// Åñëè ìîíñòð óìååò ñòðåëÿòü è åñòü ïî êîìó - ñòðåëÿåì:
if canShoot(FMonsterType) and (FTargetUID <> 0) then
FSleep := 15;
SetState(MONSTATE_RUN);
if Random(2) = 0 then
- FDirection := D_LEFT
+ FDirection := TDirection.D_LEFT
else
- FDirection := D_RIGHT;
+ FDirection := TDirection.D_RIGHT;
goto _end;
end;
// Ðûáà ïëûâåò ââåðõ:
if FObj.Vel.Y < 0 then
- if not g_Obj_CollideWater(@FObj, 0, -16) then
+ if not g_Obj_CollideLiquid(@FObj, 0, -16) then
begin
// Âñïëûëè äî ïîâåðõíîñòè - ñòîï:
FObj.Vel.Y := 0;
// Ïëàâàåì òóäà-ñþäà:
if Random(2) = 0 then
- FDirection := D_LEFT
+ FDirection := TDirection.D_LEFT
else
- FDirection := D_RIGHT;
+ FDirection := TDirection.D_RIGHT;
FSleep := 20;
SetState(MONSTATE_RUN);
end;
if b > 1 then b := b * (Random(8 div b) + 1);
for a := 0 to High(gGibs) do
begin
- if gGibs[a].Live and
+ if gGibs[a].alive and
g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
FObj.Rect.Width, 8, @gGibs[a].Obj) and (Random(3) = 0) then
begin
FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
// Ïèíàåì òðóïû
if FObj.Vel.X < 0 then
- gCorpses[a].Damage(b*2, -b, Random(7)) // íàëåâî
+ gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // íàëåâî
else
- gCorpses[a].Damage(b*2, b, Random(7)); // íàïðàâî
+ gCorpses[a].Damage(b*2, FUID, b, Random(7)); // íàïðàâî
end;
end;
// Åñëè öåëü âûñîêî, òî, âîçìîæíî, ïðûãàåì:
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
if (FMonsterType = MONSTER_PAIN) then
begin
mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-30,
- FObj.Y+FObj.Rect.Y+20, D_LEFT);
+ FObj.Y+FObj.Rect.Y+20, TDirection.D_LEFT);
if mon <> nil then
begin
mon.SetState(MONSTATE_GO);
end;
mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
- FObj.Y+FObj.Rect.Y+20, D_RIGHT);
+ FObj.Y+FObj.Rect.Y+20, TDirection.D_RIGHT);
if mon <> nil then
begin
mon.SetState(MONSTATE_GO);
end;
mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
- FObj.Y+FObj.Rect.Y, D_RIGHT);
+ FObj.Y+FObj.Rect.Y, TDirection.D_RIGHT);
if mon <> nil then
begin
mon.SetState(MONSTATE_GO);
if FCurAnim = ANIM_ATTACK2 then
begin
o := FObj;
- o.Vel.X := IfThen(FDirection = D_RIGHT, 1, -1)*IfThen(FMonsterType = MONSTER_CYBER, 60, 50);
+ o.Vel.X := IfThen(FDirection = TDirection.D_RIGHT, 1, -1)*IfThen(FMonsterType = MONSTER_CYBER, 60, 50);
if g_Weapon_Hit(@o, IfThen(FMonsterType = MONSTER_CYBER, 33, 50), FUID, HIT_SOME) <> 0 then
g_Sound_PlayExAt('SOUND_MONSTER_SKEL_HIT', FObj.X, FObj.Y);
end;
// Âû÷èñëÿåì êîîðäèíàòû, îòêóäà âûëåòèò ïóëÿ:
wx := MONSTER_ANIMTABLE[FMonsterType].wX;
- if FDirection = D_LEFT then
+ if FDirection = TDirection.D_LEFT then
begin
wx := MONSTER_ANIMTABLE[FMonsterType].wX-(MONSTERTABLE[FMonsterType].Rect.X+(MONSTERTABLE[FMonsterType].Rect.Width div 2));
wx := MONSTERTABLE[FMonsterType].Rect.X+(MONSTERTABLE[FMonsterType].Rect.Width div 2)-wx;
wx := FObj.X + wx;
wy := FObj.Y + MONSTER_ANIMTABLE[FMonsterType].wY;
+ // Ìîíñòð íå ìîæåò öåëèòüñÿ â îáúåêò çà ñïèíîé, ñòðåëÿÿ âëåâî:
+ if (FDirection = TDirection.D_LEFT) and (tx > wx) then
+ begin
+ tx := wx - 32;
+ ty := wy + Random(11) - 5;
+ end;
+ // È àíàëîãè÷íî, ñòðåëÿÿ âïðàâî:
+ if (FDirection = TDirection.D_RIGHT) and (tx < wx) then
+ begin
+ tx := wx + 32;
+ ty := wy + Random(11) - 5;
+ end;
+
// Äåëàåì âûñòðåë íóæíûì îðóæèåì:
case FMonsterType of
MONSTER_IMP:
MONSTER_ZOMBY:
begin
g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', wx, wy);
- g_Weapon_gun(wx, wy, tx, ty, 1, 3, FUID, False);
+ g_Weapon_gun(wx, wy, tx, ty, 1, 3, FUID, True);
g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
end;
MONSTER_SERG:
sx := 0; // SHUT UP COMPILER
sy := 0;
fall := True;
+
+// Ìîíñòð ñòàòè÷åí ïîêà èäåò warmup
+ if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
+
// Ðûáû "ëåòàþò" òîëüêî â âîäå:
if FMonsterType = MONSTER_FISH then
if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_WATER or PANEL_ACID1 or PANEL_ACID2) then
// Ðûáà ïëûâåò ââåðõ:
if FObj.Vel.Y < 0 then
- if not g_Obj_CollideWater(@FObj, 0, -16) then
+ if not g_Obj_CollideLiquid(@FObj, 0, -16) then
begin
// Âñïëûëè äî ïîâåðõíîñòè - ñòîï:
FObj.Vel.Y := 0;
if b > 1 then b := b * (Random(8 div b) + 1);
for a := 0 to High(gGibs) do
begin
- if gGibs[a].Live and
+ if gGibs[a].alive and
g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
FObj.Rect.Width, 8, @gGibs[a].Obj) and (Random(3) = 0) then
begin
FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
// Ïèíàåì òðóïû
if FObj.Vel.X < 0 then
- gCorpses[a].Damage(b*2, -b, Random(7)) // íàëåâî
+ gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // íàëåâî
else
- gCorpses[a].Damage(b*2, b, Random(7)); // íàïðàâî
+ gCorpses[a].Damage(b*2, FUID, b, Random(7)); // íàïðàâî
end;
end;
end;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
end;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
if FCurAnim = ANIM_ATTACK2 then
begin
o := FObj;
- o.Vel.X := IfThen(FDirection = D_RIGHT, 1, -1)*IfThen(FMonsterType = MONSTER_CYBER, 60, 50);
+ o.Vel.X := IfThen(FDirection = TDirection.D_RIGHT, 1, -1)*IfThen(FMonsterType = MONSTER_CYBER, 60, 50);
g_Weapon_Hit(@o, IfThen(FMonsterType = MONSTER_CYBER, 33, 50), FUID, HIT_SOME);
end;
procedure TMonster.Turn();
begin
// Ðàçâîðà÷èâàåìñÿ:
- if FDirection = D_LEFT then
- FDirection := D_RIGHT
- else
- FDirection := D_LEFT;
+ if FDirection = TDirection.D_LEFT then FDirection := TDirection.D_RIGHT else FDirection := TDirection.D_LEFT;
// Áåæèì â âûáðàííóþ ñòîðîíó:
- if FDirection = D_RIGHT then
+ if FDirection = TDirection.D_RIGHT then
FObj.Vel.X := MONSTERTABLE[FMonsterType].RunVel
else
FObj.Vel.X := -MONSTERTABLE[FMonsterType].RunVel;
if (gPlayers <> nil) and (FBehaviour <> BH_INSANE) and
(FBehaviour <> BH_CANNIBAL) and (FBehaviour <> BH_GOOD) then
for a := 0 to High(gPlayers) do
- if (gPlayers[a] <> nil) and (gPlayers[a].Live)
+ if (gPlayers[a] <> nil) and (gPlayers[a].alive)
and (not gPlayers[a].NoTarget) and (gPlayers[a].FMegaRulez[MR_INVIS] < gTime) then
begin
if g_Look(@FObj, @gPlayers[a].Obj, FDirection) then
// Êèëëåðû è äîáðûå íå òðîãàþò ìîíñòðîâ
if (gMonsters <> nil) and (FBehaviour <> BH_KILLER) and (FBehaviour <> BH_GOOD) then
for a := 0 to High(gMonsters) do
- if (gMonsters[a] <> nil) and (gMonsters[a].Live) and
+ if (gMonsters[a] <> nil) and (gMonsters[a].alive) and
(gMonsters[a].FUID <> FUID) then
begin
if (FBehaviour = BH_CANNIBAL) and (gMonsters[a].FMonsterType <> FMonsterType) then
Result := True;
end;
-function TMonster.Live(): Boolean;
+function TMonster.alive(): Boolean;
begin
Result := (FState <> MONSTATE_DIE) and (FState <> MONSTATE_DEAD) and (FHealth > 0);
end;
WakeUpSound();
end;
-procedure TMonster.SaveState(var Mem: TBinMemoryWriter);
+procedure TMonster.SaveState (st: TStream);
var
i: Integer;
- sig: DWORD;
b: Byte;
anim: Boolean;
begin
- if Mem = nil then
- Exit;
-
-// Ñèãíàòóðà ìîíñòðà:
- sig := MONSTER_SIGNATURE; // 'MONS'
- Mem.WriteDWORD(sig);
-// UID ìîíñòðà:
- Mem.WriteWord(FUID);
-// Íàïðàâëåíèå:
- if FDirection = D_LEFT then
- b := 1
- else // D_RIGHT
- b := 2;
- Mem.WriteByte(b);
-// Íàäî ëè óäàëèòü åãî:
- Mem.WriteBoolean(FRemoved);
-// Îñòàëîñü çäîðîâüÿ:
- Mem.WriteInt(FHealth);
-// Ñîñòîÿíèå:
- Mem.WriteByte(FState);
-// Òåêóùàÿ àíèìàöèÿ:
- Mem.WriteByte(FCurAnim);
-// UID öåëè:
- Mem.WriteWord(FTargetUID);
-// Âðåìÿ ïîñëå ïîòåðè öåëè:
- Mem.WriteInt(FTargetTime);
-// Ïîâåäåíèå ìîíñòðà:
- Mem.WriteByte(FBehaviour);
-// Ãîòîâíîñòü ê âûñòðåëó:
- Mem.WriteInt(FAmmo);
-// Áîëü:
- Mem.WriteInt(FPain);
-// Âðåìÿ îæèäàíèÿ:
- Mem.WriteInt(FSleep);
-// Îçâó÷èâàòü ëè áîëü:
- Mem.WriteBoolean(FPainSound);
-// Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè:
- Mem.WriteBoolean(FWaitAttackAnim);
-// Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå:
- Mem.WriteBoolean(FChainFire);
-// Ïîäëåæèò ëè ðåñïàâíó:
- Mem.WriteBoolean(FNoRespawn);
-// Êîîðäèíàòû öåëè:
- Mem.WriteInt(tx);
- Mem.WriteInt(ty);
-// ID ìîíñòðà ïðè ñòàðòå êàðòû:
- Mem.WriteInt(FStartID);
-// Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà:
- Mem.WriteInt(FSpawnTrigger);
-// Îáúåêò ìîíñòðà:
- Obj_SaveState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà:
- anim := vilefire <> nil;
- Mem.WriteBoolean(anim);
-// Åñëè åñòü - ñîõðàíÿåì:
- if anim then
- vilefire.SaveState(Mem);
-// Àíèìàöèè:
+ assert(st <> nil);
+
+ // Ñèãíàòóðà ìîíñòðà:
+ utils.writeSign(st, 'MONS');
+ utils.writeInt(st, Byte(0)); // version
+ // UID ìîíñòðà:
+ utils.writeInt(st, Word(FUID));
+ // Íàïðàâëåíèå
+ if FDirection = TDirection.D_LEFT then b := 1 else b := 2; // D_RIGHT
+ utils.writeInt(st, Byte(b));
+ // Íàäî ëè óäàëèòü åãî
+ utils.writeBool(st, FRemoved);
+ // Îñòàëîñü çäîðîâüÿ
+ utils.writeInt(st, LongInt(FHealth));
+ // Ñîñòîÿíèå
+ utils.writeInt(st, Byte(FState));
+ // Òåêóùàÿ àíèìàöèÿ
+ utils.writeInt(st, Byte(FCurAnim));
+ // UID öåëè
+ utils.writeInt(st, Word(FTargetUID));
+ // Âðåìÿ ïîñëå ïîòåðè öåëè
+ utils.writeInt(st, LongInt(FTargetTime));
+ // Ïîâåäåíèå ìîíñòðà
+ utils.writeInt(st, Byte(FBehaviour));
+ // Ãîòîâíîñòü ê âûñòðåëó
+ utils.writeInt(st, LongInt(FAmmo));
+ // Áîëü
+ utils.writeInt(st, LongInt(FPain));
+ // Âðåìÿ îæèäàíèÿ
+ utils.writeInt(st, LongInt(FSleep));
+ // Îçâó÷èâàòü ëè áîëü
+ utils.writeBool(st, FPainSound);
+ // Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè
+ utils.writeBool(st, FWaitAttackAnim);
+ // Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå
+ utils.writeBool(st, FChainFire);
+ // Ïîäëåæèò ëè ðåñïàâíó
+ utils.writeBool(st, FNoRespawn);
+ // Êîîðäèíàòû öåëè
+ utils.writeInt(st, LongInt(tx));
+ utils.writeInt(st, LongInt(ty));
+ // ID ìîíñòðà ïðè ñòàðòå êàðòû
+ utils.writeInt(st, LongInt(FStartID));
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà
+ utils.writeInt(st, LongInt(FSpawnTrigger));
+ // Îáúåêò ìîíñòðà
+ Obj_SaveState(st, @FObj);
+ // Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà
+ anim := (vilefire <> nil);
+ utils.writeBool(st, anim);
+ // Åñëè åñòü - ñîõðàíÿåì:
+ if anim then vilefire.SaveState(st);
+ // Àíèìàöèè
for i := ANIM_SLEEP to ANIM_PAIN do
begin
- // Åñòü ëè ëåâàÿ àíèìàöèÿ:
- anim := FAnim[i, D_LEFT] <> nil;
- Mem.WriteBoolean(anim);
- // Åñëè åñòü - ñîõðàíÿåì:
- if anim then
- FAnim[i, D_LEFT].SaveState(Mem);
- // Åñòü ëè ïðàâàÿ àíèìàöèÿ:
- anim := FAnim[i, D_RIGHT] <> nil;
- Mem.WriteBoolean(anim);
- // Åñëè åñòü - ñîõðàíÿåì:
- if anim then
- FAnim[i, D_RIGHT].SaveState(Mem);
+ // Åñòü ëè ëåâàÿ àíèìàöèÿ
+ anim := (FAnim[i, TDirection.D_LEFT] <> nil);
+ utils.writeBool(st, anim);
+ // Åñëè åñòü - ñîõðàíÿåì
+ if anim then FAnim[i, TDirection.D_LEFT].SaveState(st);
+ // Åñòü ëè ïðàâàÿ àíèìàöèÿ
+ anim := (FAnim[i, TDirection.D_RIGHT] <> nil);
+ utils.writeBool(st, anim);
+ // Åñëè åñòü - ñîõðàíÿåì
+ if anim then FAnim[i, TDirection.D_RIGHT].SaveState(st);
end;
end;
-procedure TMonster.LoadState(var Mem: TBinMemoryReader);
+
+procedure TMonster.LoadState (st: TStream);
var
i: Integer;
- sig: DWORD;
b: Byte;
anim: Boolean;
begin
- if Mem = nil then
- Exit;
+ assert(st <> nil);
-// Ñèãíàòóðà ìîíñòðà:
- Mem.ReadDWORD(sig);
- if sig <> MONSTER_SIGNATURE then // 'MONS'
- begin
- raise EBinSizeError.Create('TMonster.LoadState: Wrong Monster Signature');
- end;
-// UID ìîíñòðà:
- Mem.ReadWord(FUID);
-// Íàïðàâëåíèå:
- Mem.ReadByte(b);
- if b = 1 then
- FDirection := D_LEFT
- else // b = 2
- FDirection := D_RIGHT;
-// Íàäî ëè óäàëèòü åãî:
- Mem.ReadBoolean(FRemoved);
-// Îñòàëîñü çäîðîâüÿ:
- Mem.ReadInt(FHealth);
-// Ñîñòîÿíèå:
- Mem.ReadByte(FState);
-// Òåêóùàÿ àíèìàöèÿ:
- Mem.ReadByte(FCurAnim);
-// UID öåëè:
- Mem.ReadWord(FTargetUID);
-// Âðåìÿ ïîñëå ïîòåðè öåëè:
- Mem.ReadInt(FTargetTime);
-// Ïîâåäåíèå ìîíñòðà:
- Mem.ReadByte(FBehaviour);
-// Ãîòîâíîñòü ê âûñòðåëó:
- Mem.ReadInt(FAmmo);
-// Áîëü:
- Mem.ReadInt(FPain);
-// Âðåìÿ îæèäàíèÿ:
- Mem.ReadInt(FSleep);
-// Îçâó÷èâàòü ëè áîëü:
- Mem.ReadBoolean(FPainSound);
-// Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè:
- Mem.ReadBoolean(FWaitAttackAnim);
-// Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå:
- Mem.ReadBoolean(FChainFire);
-// Ïîäëåæèò ëè ðåñïàâíó
- Mem.ReadBoolean(FNoRespawn);
-// Êîîðäèíàòû öåëè:
- Mem.ReadInt(tx);
- Mem.ReadInt(ty);
-// ID ìîíñòðà ïðè ñòàðòå êàðòû:
- Mem.ReadInt(FStartID);
-// Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà:
- Mem.ReadInt(FSpawnTrigger);
-// Îáúåêò ìîíñòðà:
- Obj_LoadState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà:
- Mem.ReadBoolean(anim);
-// Åñëè åñòü - çàãðóæàåì:
+ // Ñèãíàòóðà ìîíñòðà:
+ if not utils.checkSign(st, 'MONS') then raise XStreamError.Create('invalid monster signature');
+ if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid monster version');
+ if (uidMap[FUID] <> nil) and (uidMap[FUID] <> self) then raise Exception.Create('internal error in monster loader (0)');
+ uidMap[FUID] := nil;
+ // UID ìîíñòðà:
+ FUID := utils.readWord(st);
+ //if (arrIdx = -1) then raise Exception.Create('internal error in monster loader');
+ if (uidMap[FUID] <> nil) then raise Exception.Create('internal error in monster loader (1)');
+ uidMap[FUID] := self;
+ // Íàïðàâëåíèå
+ b := utils.readByte(st);
+ if b = 1 then FDirection := TDirection.D_LEFT else FDirection := TDirection.D_RIGHT; // b = 2
+ // Íàäî ëè óäàëèòü åãî
+ FRemoved := utils.readBool(st);
+ // Îñòàëîñü çäîðîâüÿ
+ FHealth := utils.readLongInt(st);
+ // Ñîñòîÿíèå
+ FState := utils.readByte(st);
+ // Òåêóùàÿ àíèìàöèÿ
+ FCurAnim := utils.readByte(st);
+ // UID öåëè
+ FTargetUID := utils.readWord(st);
+ // Âðåìÿ ïîñëå ïîòåðè öåëè
+ FTargetTime := utils.readLongInt(st);
+ // Ïîâåäåíèå ìîíñòðà
+ FBehaviour := utils.readByte(st);
+ // Ãîòîâíîñòü ê âûñòðåëó
+ FAmmo := utils.readLongInt(st);
+ // Áîëü
+ FPain := utils.readLongInt(st);
+ // Âðåìÿ îæèäàíèÿ
+ FSleep := utils.readLongInt(st);
+ // Îçâó÷èâàòü ëè áîëü
+ FPainSound := utils.readBool(st);
+ // Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè
+ FWaitAttackAnim := utils.readBool(st);
+ // Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå
+ FChainFire := utils.readBool(st);
+ // Ïîäëåæèò ëè ðåñïàâíó
+ FNoRespawn := utils.readBool(st);
+ // Êîîðäèíàòû öåëè
+ tx := utils.readLongInt(st);
+ ty := utils.readLongInt(st);
+ // ID ìîíñòðà ïðè ñòàðòå êàðòû
+ FStartID := utils.readLongInt(st);
+ // Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà
+ FSpawnTrigger := utils.readLongInt(st);
+ // Îáúåêò ìîíñòðà
+ Obj_LoadState(@FObj, st);
+ // Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà
+ anim := utils.readBool(st);
+ // Åñëè åñòü - çàãðóæàåì:
if anim then
begin
Assert(vilefire <> nil, 'TMonster.LoadState: no vilefire anim');
- vilefire.LoadState(Mem);
+ vilefire.LoadState(st);
end;
-// Àíèìàöèè:
+ // Àíèìàöèè
for i := ANIM_SLEEP to ANIM_PAIN do
begin
- // Åñòü ëè ëåâàÿ àíèìàöèÿ:
- Mem.ReadBoolean(anim);
- // Åñëè åñòü - çàãðóæàåì:
+ // Åñòü ëè ëåâàÿ àíèìàöèÿ
+ anim := utils.readBool(st);
+ // Åñëè åñòü - çàãðóæàåì
if anim then
begin
- Assert(FAnim[i, D_LEFT] <> nil,
- 'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
- FAnim[i, D_LEFT].LoadState(Mem);
+ Assert(FAnim[i, TDirection.D_LEFT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
+ FAnim[i, TDirection.D_LEFT].LoadState(st);
end;
- // Åñòü ëè ïðàâàÿ àíèìàöèÿ:
- Mem.ReadBoolean(anim);
- // Åñëè åñòü - çàãðóæàåì:
+ // Åñòü ëè ïðàâàÿ àíèìàöèÿ
+ anim := utils.readBool(st);
+ // Åñëè åñòü - çàãðóæàåì
if anim then
begin
- Assert(FAnim[i, D_RIGHT] <> nil,
- 'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
- FAnim[i, D_RIGHT].LoadState(Mem);
+ Assert(FAnim[i, TDirection.D_RIGHT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
+ FAnim[i, TDirection.D_RIGHT].LoadState(st);
end;
end;
+ // update cache
+ self.positionChanged
end;
+
procedure TMonster.ActivateTriggers();
var
a: Integer;
SetLength(FDieTriggers, 0);
end;
-procedure TMonster.CatchFire(Attacker: Word);
+procedure TMonster.CatchFire(Attacker: Word; Timeout: Integer = MON_BURN_TIME);
begin
- FFireTime := 100;
+ if FMonsterType in [MONSTER_SOUL, MONSTER_VILE] then
+ exit; // àð÷è íå ãîðÿò, ÷åðåïà óæå ãîðÿò
+ if Timeout <= 0 then exit;
+ if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_WATER or PANEL_ACID1 or PANEL_ACID2) then
+ exit; // íå ïîäãîðàåì â âîäå íà âñÿêèé ñëó÷àé
+ if FFireTime <= 0 then
+ g_Sound_PlayExAt('SOUND_IGNITE', FObj.X, FObj.Y);
+ FFireTime := Timeout;
FFireAttacker := Attacker;
if g_Game_IsNet and g_Game_IsServer then MH_SEND_MonsterState(FUID);
end;
for idx := 0 to High(gMonsters) do
begin
mon := gMonsters[idx];
- if (mon <> nil) and mon.Live then
+ if (mon <> nil) and mon.alive then
begin
result := cb(mon);
if result then exit;
function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean;
-
+ (*
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
- result := mon.Live;// and g_Obj_Collide(x, y, width, height, @mon.Obj));
+ result := mon.alive;// and g_Obj_Collide(x, y, width, height, @mon.Obj));
end;
-
+ *)
var
idx: Integer;
mon: TMonster;
+ mit: PMonster;
+ it: TMonsterGrid.Iter;
begin
result := false;
if (width < 1) or (height < 1) then exit;
if gmon_debug_use_sqaccel then
begin
- result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
+ //result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
+ it := monsGrid.forEachInAABB(x, y, width, height);
+ for mit in it do if (mit.alive) then begin result := true; break; end;
+ it.release();
end
else
begin
for idx := 0 to High(gMonsters) do
begin
mon := gMonsters[idx];
- if (mon <> nil) and mon.Live then
+ if (mon <> nil) and mon.alive then
begin
if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin
function g_Mons_ForEachAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
-
+ (*
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
result := cb(mon);
end;
-
+ *)
var
idx: Integer;
mon: TMonster;
+ mit: PMonster;
+ it: TMonsterGrid.Iter;
begin
result := false;
if (width < 1) or (height < 1) then exit;
if gmon_debug_use_sqaccel then
begin
- result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
+ //result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
+ it := monsGrid.forEachInAABB(x, y, width, height);
+ for mit in it do if (cb(mit^)) then begin result := true; break; end;
+ it.release();
end
else
begin
for idx := 0 to High(gMonsters) do
begin
mon := gMonsters[idx];
- if (mon <> nil) and mon.Live then
+ if (mon <> nil) and mon.alive then
begin
if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin
function g_Mons_ForEachAliveAt (x, y: Integer; width, height: Integer; cb: TEachMonsterCB): Boolean;
-
+ (*
function monsCollCheck (mon: TMonster; atag: Integer): Boolean;
begin
//result := false;
- //if mon.Live and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon);
- if mon.Live then result := cb(mon) else result := false;
+ //if mon.alive and g_Obj_Collide(x, y, width, height, @mon.Obj) then result := cb(mon);
+ if mon.alive then result := cb(mon) else result := false;
end;
-
+ *)
var
idx: Integer;
mon: TMonster;
+ mit: PMonster;
+ it: TMonsterGrid.Iter;
begin
result := false;
if (width < 1) or (height < 1) then exit;
if gmon_debug_use_sqaccel then
begin
+ {
if (width = 1) and (height = 1) then
begin
result := (monsGrid.forEachAtPoint(x, y, monsCollCheck) <> nil);
begin
result := (monsGrid.forEachInAABB(x, y, width, height, monsCollCheck) <> nil);
end;
+ }
+ it := monsGrid.forEachInAABB(x, y, width, height);
+ for mit in it do
+ begin
+ if (mit^.alive) then
+ begin
+ if (cb(mit^)) then begin result := true; break; end;
+ end;
+ end;
+ it.release();
end
else
begin
for idx := 0 to High(gMonsters) do
begin
mon := gMonsters[idx];
- if (mon <> nil) and mon.Live then
+ if (mon <> nil) and mon.alive then
begin
if g_Obj_Collide(x, y, width, height, @mon.Obj) then
begin