X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_monsters.pas;h=a19fa4b568e5dbab3163ff1eb8d256b8c5234d82;hb=e6a2ca524340877015dc54ec063f925989548a98;hp=4c354c30a1b3c60c086eaff684015df2d3bc0be0;hpb=81a68b9ff71207e598bed6f0d7c5fe8df5d7daae;p=d2df-sdl.git diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index 4c354c3..a19fa4b 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -1,4 +1,4 @@ -(* 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 @@ -14,13 +14,17 @@ * along with this program. If not, see . *) {$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; @@ -36,15 +40,20 @@ const 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; @@ -56,7 +65,7 @@ type 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; @@ -79,27 +88,30 @@ type FFirePainTime: Integer; FFireAttacker: Word; vilefire: TAnimation; - - {$IF DEFINED(D2F_DEBUG)} - public - {$ENDIF} - proxyId: Integer; // node in dyntree or -1 - arrIdx: Integer; // in gMonsters - {$IF DEFINED(D2F_DEBUG)} - private - {$ENDIF} + mProxyId: Integer; // node in dyntree or -1 + mArrIdx: Integer; // in gMonsters 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; @@ -107,7 +119,7 @@ type 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; @@ -126,8 +138,8 @@ type 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); @@ -135,13 +147,26 @@ type 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; @@ -154,12 +179,11 @@ type 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; @@ -167,6 +191,36 @@ type property GameDirection: TDirection read FDirection write FDirection; property StartID: Integer read FStartID; + + 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; @@ -176,7 +230,7 @@ procedure g_Mons_InitTree (x, y, w, h: Integer); 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_Update (); @@ -184,11 +238,18 @@ 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 @@ -200,6 +261,8 @@ function g_Mons_ByIdx (uid: Integer): TMonster; inline; // can return null function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline; +function g_Mons_TotalCount (): Integer; inline; + function g_Mons_IsAnyAliveAt (x, y: Integer; width, height: Integer): Boolean; function g_Mons_ForEach (cb: TEachMonsterCB): Boolean; @@ -208,13 +271,16 @@ function g_Mons_ForEachAlive (cb: TEachMonsterCB): Boolean; 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 @@ -236,28 +302,30 @@ type TMonsterGrid = specialize TBodyGridBase; var - monsGrid: TMonsterGrid = nil; + monsGrid: TMonsterGrid = nil; // DO NOT USE! public for debugging only! var gmon_debug_think: Boolean = true; + gmon_debug_one_think_step: Boolean = false; 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(); @@ -266,23 +334,24 @@ end; 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; @@ -293,47 +362,67 @@ begin 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)', [arrIdx, UID, FObj.X, FObj.Y, FObj.Rect.X, FObj.Rect.Y]), MSG_NOTIFY); + //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 (proxyId = -1) then + if (mProxyId = -1) then begin - proxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height); + //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(proxyId, x, y); - e_WriteLog(Format('monster #%d(%u): inserted into the grid; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY); + monsGrid.getBodyXY(mProxyId, x, y); + 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(proxyId, 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; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY);{$ENDIF} - - {$IF TRUE} - monsGrid.moveBody(proxyId, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y); - {$ELSE} - monsGrid.removeBody(proxyId); - proxyId := 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(proxyId, x, y); - e_WriteLog(Format('monster #%d(%u): updated tree; proxyid=%d; x=%d; y=%d', [arrIdx, UID, proxyId, x, y]), MSG_NOTIFY); + monsGrid.getBodyXY(mProxyId, x, y); + 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; @@ -441,8 +530,8 @@ const 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)); @@ -452,11 +541,11 @@ const 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))), @@ -528,9 +617,11 @@ const 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 (); @@ -538,15 +629,16 @@ var 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; @@ -559,6 +651,24 @@ begin 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; @@ -567,29 +677,16 @@ var soulcount: Integer = 0; -function allocMonster(): DWORD; +function allocMonster (): DWORD; var - i, olen: Integer; + f, olen: Integer; begin - for i := 0 to High(gMonsters) do - 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 + result := freeInds.alloc(); + if (result > High(gMonsters)) then 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; @@ -667,6 +764,7 @@ end; function isCorpse (o: PObj; immediately: Boolean): Integer; + (* function monsCollCheck (mon: TMonster; atag: Integer): Boolean; begin atag := atag; // shut up, fpc! @@ -681,10 +779,13 @@ function isCorpse (o: PObj; immediately: Boolean): Integer; result := true; end; end; + *) var a: Integer; - mon: TMonster; + mon: PMonster; + mres: TMonster = nil; + it: TMonsterGrid.Iter; begin result := -1; @@ -694,8 +795,21 @@ begin // Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè 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.arrIdx; + //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 @@ -719,160 +833,164 @@ end; 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); @@ -955,13 +1073,15 @@ begin 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'); @@ -971,12 +1091,14 @@ begin 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'); @@ -1173,6 +1295,9 @@ begin g_Sound_Delete('SOUND_MONSTER_SPIDER_WALK'); g_Sound_Delete('SOUND_MONSTER_FISH_ATTACK'); + + freeInds.Free(); + freeInds := nil; end; procedure g_Monsters_Init(); @@ -1180,16 +1305,21 @@ begin 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; @@ -1198,6 +1328,8 @@ procedure g_Mons_InitTree (x, y, w, h: Integer); begin monsGrid.Free(); monsGrid := TMonsterGrid.Create(x, y, w, h); + //clearUidMap(); // why not? + e_LogWritefln('%s', ['Recreated monster tree']); end; @@ -1223,8 +1355,8 @@ begin mon := TMonster.Create(MonsterType, find_id, ForcedUID); gMonsters[find_id] := mon; - mon.arrIdx := find_id; - mon.proxyId := -1; + mon.mArrIdx := find_id; + mon.mProxyId := -1; uidMap[mon.FUID] := mon; @@ -1260,11 +1392,14 @@ begin 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 @@ -1273,6 +1408,9 @@ begin g_Sound_PlayExAt('SOUND_MONSTER_TRUP', FObj.X, FObj.Y); Exit; end; + end; + end; + end; end; procedure g_Monsters_Update(); @@ -1290,8 +1428,9 @@ begin gMon := True; // Äëÿ ðàáîòû BlockMon'à - if (gmon_debug_think) then + if gmon_debug_think or gmon_debug_one_think_step then begin + gmon_debug_one_think_step := false; for a := 0 to High(gMonsters) do begin if (gMonsters[a] = nil) then continue; @@ -1318,9 +1457,12 @@ var 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(); @@ -1332,142 +1474,179 @@ begin 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 @@ -1656,8 +1835,8 @@ begin 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; @@ -1713,10 +1892,13 @@ begin FFireTime := 0; FFirePainTime := 0; FFireAttacker := 0; + mEDamageType := HIT_SOME; - proxyId := -1; - arrIdx := -1; + mProxyId := -1; + mArrIdx := -1; trapCheckFrameId := 0; + mplatCheckFrameId := 0; + mNeedSend := false; if FMonsterType in [MONSTER_ROBO, MONSTER_BARREL] then FBloodKind := BLOOD_SPARKS @@ -1745,8 +1927,8 @@ begin 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 @@ -1771,15 +1953,15 @@ begin 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]); // Åñëè åñòü îòäåëüíàÿ ëåâàÿ àíèìàöèÿ - çàãðóæàåì: @@ -1791,7 +1973,7 @@ begin 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; @@ -1820,15 +2002,21 @@ begin 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; @@ -1914,9 +2102,10 @@ begin 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; @@ -1929,6 +2118,7 @@ begin begin FObj.Rect.Y := FObj.Rect.Y + FObj.Rect.Height-12; FObj.Rect.Height := 12; + positionChanged(); end; // Óðîí áûë ñèëüíûì => ñëàáûå - â êàøó: @@ -1967,7 +2157,7 @@ begin Result := False; if g_Game_IsClient then Exit; - if not Live then + if not alive then Exit; if FHealth < FMaxHealth then @@ -1984,26 +2174,30 @@ var 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(); - if (proxyId <> -1) then + if (mProxyId <> -1) then begin if (monsGrid <> nil) then begin - monsGrid.removeBody(proxyId); + monsGrid.removeBody(mProxyId); {$IF DEFINED(D2F_DEBUG_MONS_MOVE)} - e_WriteLog(Format('monster #%d(%u): removed from tree; proxyid=%d', [arrIdx, UID, proxyId]), MSG_NOTIFY); + e_WriteLog(Format('monster #%d:(%u): removed from grid; mProxyid=%d', [mArrIdx, UID, mProxyId]), MSG_NOTIFY); {$ENDIF} end; - proxyId := -1; + mProxyId := -1; end; - if (arrIdx <> -1) and (arrIdx < Length(gMonsters)) then gMonsters[arrIdx] := nil; - arrIdx := -1; + if (mArrIdx <> -1) and (mArrIdx < Length(gMonsters)) then + begin + freeInds.release(mArrIdx); + gMonsters[mArrIdx] := nil; + end; + mArrIdx := -1; uidMap[FUID] := nil; @@ -2024,12 +2218,16 @@ begin 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 @@ -2041,22 +2239,22 @@ begin 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; @@ -2209,17 +2407,17 @@ begin 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; // Ýôôåêò òåëåïîðòà â òî÷êå íàçíà÷åíèÿ: @@ -2246,6 +2444,8 @@ var o, co: TObj; fall: Boolean; mon: TMonster; + mit: PMonster; + it: TMonsterGrid.Iter; label _end; begin @@ -2282,12 +2482,21 @@ begin 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 @@ -2450,7 +2659,7 @@ begin // Åñëè åñòü èãðîê ðÿäîì, ïðîñûïàåìñÿ è èäåì ê íåìó: 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 @@ -2468,7 +2677,7 @@ begin // Åñëè åñòü ïîäõîäÿùèé ìîíñòð ðÿäîì: 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 // Ìàíüÿêè íàïàäàþò íà âñåõ ìîíñòðîâ, êðîìå äðóçåé @@ -2558,9 +2767,9 @@ 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 @@ -2575,9 +2784,9 @@ begin 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; @@ -2638,15 +2847,15 @@ begin // Ðûáà ïëûâåò ââåðõ: 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; @@ -2670,7 +2879,7 @@ begin 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 @@ -2724,7 +2933,7 @@ begin 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; @@ -2765,7 +2974,7 @@ begin 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; @@ -2800,7 +3009,7 @@ begin 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; @@ -2832,7 +3041,7 @@ begin 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; @@ -2922,7 +3131,7 @@ _end: 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); @@ -2932,7 +3141,7 @@ _end: 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); @@ -2942,7 +3151,7 @@ _end: 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); @@ -3021,7 +3230,7 @@ _end: 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; @@ -3047,7 +3256,7 @@ _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; @@ -3056,6 +3265,19 @@ _end: 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: @@ -3063,7 +3285,7 @@ _end: 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: @@ -3474,7 +3696,7 @@ begin // Ðûáà ïëûâåò ââåðõ: 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; @@ -3502,7 +3724,7 @@ begin 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 @@ -3551,7 +3773,7 @@ begin 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; @@ -3589,7 +3811,7 @@ begin 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; @@ -3622,7 +3844,7 @@ begin 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; @@ -3653,7 +3875,7 @@ begin 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; @@ -3801,7 +4023,7 @@ _end: 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; @@ -3922,13 +4144,10 @@ 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; @@ -3954,7 +4173,7 @@ begin 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 @@ -3974,7 +4193,7 @@ begin // Êèëëåðû è äîáðûå íå òðîãàþò ìîíñòðîâ 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 @@ -4227,7 +4446,7 @@ begin Result := True; end; -function TMonster.Live(): Boolean; +function TMonster.alive(): Boolean; begin Result := (FState <> MONSTATE_DIE) and (FState <> MONSTATE_DEAD) and (FHealth > 0); end; @@ -4250,181 +4469,170 @@ begin 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; end; + procedure TMonster.ActivateTriggers(); var a: Integer; @@ -4450,9 +4658,14 @@ begin 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 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; @@ -4487,7 +4700,6 @@ begin if (result = nil) then raise Exception.Create('g_Mons_ByIdx: invalid monster id'); end; - // can return null function g_Mons_ByIdx_NC (uid: Integer): TMonster; inline; begin @@ -4495,6 +4707,11 @@ begin result := gMonsters[uid]; end; +function g_Mons_TotalCount (): Integer; inline; +begin + result := Length(gMonsters); +end; + function g_Mons_ForEach (cb: TEachMonsterCB): Boolean; var @@ -4525,7 +4742,7 @@ 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 result := cb(mon); if result then exit; @@ -4535,28 +4752,33 @@ end; 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 @@ -4570,28 +4792,33 @@ end; 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 @@ -4605,22 +4832,25 @@ end; 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); @@ -4629,13 +4859,23 @@ begin 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