X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_monsters.pas;h=3b1aad732636098f528204c006badae4448c2e8c;hb=abda6900c041e39944de6a49aa088a60c170715e;hp=ae4389c98ab48bb4b9c79c15ab9d8607de8967c2;hpb=98ad2c936a61bb810453f3552d7ed273eeaeaef3;p=d2df-sdl.git diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index ae4389c..3b1aad7 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -1,9 +1,8 @@ -(* 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 @@ -21,8 +20,11 @@ unit g_monsters; interface uses - g_basic, e_graphics, g_phys, g_textures, g_grid, - g_saveload, BinEditor, g_panel, xprofiler; + SysUtils, Classes, + mempool, + MAPDEF, + g_base, g_basic, g_phys, g_textures, g_grid, + g_saveload, g_panel, xprofiler; const MONSTATE_SLEEP = 0; @@ -38,15 +40,22 @@ 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) + ADirectedAnim = Array of Array [TDirection.D_LEFT..TDirection.D_RIGHT] of TAnimationState; + + PMonster = ^TMonster; + TMonster = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF} private FMonsterType: Byte; FUID: Word; @@ -58,7 +67,7 @@ type FMaxHealth: Integer; FState: Byte; FCurAnim: Byte; - FAnim: Array of Array [D_LEFT..D_RIGHT] of TAnimation; + FAnim: ADirectedAnim; FTargetUID: Word; FTargetTime: Integer; FBehaviour: Byte; @@ -80,21 +89,31 @@ type FShellType: Byte; FFirePainTime: Integer; FFireAttacker: Word; - vilefire: TAnimation; + vilefire: TAnimationState; 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; @@ -102,17 +121,17 @@ 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; function Heal(Value: Word): Boolean; procedure BFGHit(); + procedure PreUpdate(); procedure Update(); procedure ClientUpdate(); procedure ClientAttack(wx, wy, atx, aty: Integer); procedure SetDeadAnim; - procedure Draw(); procedure WakeUp(); procedure WakeUpSound(); procedure DieSound(); @@ -121,8 +140,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); @@ -130,20 +149,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; - published property MonsterType: Byte read FMonsterType; property MonsterHealth: Integer read FHealth write FHealth; property MonsterAmmo: Integer read FAmmo write FAmmo; @@ -159,8 +184,8 @@ type 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; @@ -168,6 +193,39 @@ type property GameDirection: TDirection read FDirection write FDirection; property StartID: Integer read FStartID; + + property VileFireAnim: TAnimationState read vilefire; + property DirAnim: ADirectedAnim read FAnim; + + 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; @@ -180,16 +238,22 @@ procedure g_Monsters_Init (); 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 @@ -211,13 +275,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 @@ -246,140 +313,19 @@ var gmon_debug_think: Boolean = true; gmon_debug_one_think_step: Boolean = false; + var (* private state *) + gMonsters: array of TMonster; -implementation - -uses - e_log, 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, idpool; - - -// ////////////////////////////////////////////////////////////////////////// // -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 - begin - profMonsLOS.sectionBegin('loscalc'); - profMonsLOS.sectionEnd(); - end; -end; - -procedure g_Mons_ProfilersEnd (); -begin - if (profMonsLOS <> nil) and (g_profile_los) then profMapCollision.mainEnd(); -end; - -procedure g_Mons_LOS_Start (); inline; -begin - profMonsLOS.sectionBeginAccum('loscalc'); -end; - -procedure g_Mons_LOS_End (); inline; -begin - profMonsLOS.sectionEnd(); -end; - - -// ////////////////////////////////////////////////////////////////////////// // -var - monCheckTrapLastFrameId: DWord; - - -procedure TMonster.getMapBox (out x, y, w, h: Integer); inline; -begin - x := FObj.X+FObj.Rect.X; - y := FObj.Y+FObj.Rect.Y; - w := FObj.Rect.Width; - h := FObj.Rect.Height; -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, 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 - 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; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); - {$ENDIF} - end - else - begin - monsGrid.getBodyDims(mProxyId, x, y, w, h); - getMapBox(nx, ny, nw, nh); - - if (w <> nw) or (h <> nh) then - begin - {$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 - {$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 grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); - {$ENDIF} - end; -end; - + const + ANIM_SLEEP = 0; + ANIM_GO = 1; + ANIM_DIE = 2; + ANIM_MESS = 3; + ANIM_ATTACK = 4; + ANIM_ATTACK2 = 5; + ANIM_PAIN = 6; -// ////////////////////////////////////////////////////////////////////////// // -const - ANIM_SLEEP = 0; - ANIM_GO = 1; - ANIM_DIE = 2; - ANIM_MESS = 3; - ANIM_ATTACK = 4; - ANIM_ATTACK2 = 5; - ANIM_PAIN = 6; - - MONSTER_SIGNATURE = $534E4F4D; // 'MONS' - -// Òàáëèöà òèïîâ àíèìàöèè ìîíñòðîâ: - ANIMTABLE: Array [ANIM_SLEEP..ANIM_PAIN] of - record - name: String; - loop: Boolean; - end = ((name: 'SLEEP'; loop: True), - (name: 'GO'; loop: True), - (name: 'DIE'; loop: False), - (name: 'MESS'; loop: False), - (name: 'ATTACK'; loop: False), - (name: 'ATTACK2'; loop: False), - (name: 'PAIN'; loop: False)); - -// Òàáëèöà õàðàêòåðèñòèê ìîíñòðîâ: +// Таблица характеристик монстров: MONSTERTABLE: Array [MONSTER_DEMON..MONSTER_MAN] of record Name: String; @@ -450,101 +396,272 @@ const (Name:'MAN'; Rect:(X:15; Y:6; Width:34; Height:52); Health:400; RunVel: 8; MinPain: 50; Pain: 70; Jump: 10)); -// Òàáëèöà ïàðàìåòðîâ àíèìàöèè ìîíñòðîâ: +// Таблица параметров анимации монстров: MONSTER_ANIMTABLE: Array [MONSTER_DEMON..MONSTER_MAN] of record LeftAnim: Boolean; - wX, wY: Integer; // Îòêóäà âûëåòèò ïóëÿ + wX, wY: Integer; // Откуда вылетит пуля AnimSpeed: Array [ANIM_SLEEP..ANIM_PAIN] of Byte; + AnimCount: Array [ANIM_SLEEP..ANIM_PAIN] of Byte; 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 + AnimCount: (2, 4, 6, 6, 3, 0, 1); 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)); AnimDeltaLeft: ((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))), (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 2, 3, 0, 4); //IMP + AnimCount: (2, 4, 5, 8, 3, 0, 1); 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 + AnimCount: (2, 4, 6, 9, 2, 0, 1); 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 + AnimCount: (2, 4, 5, 9, 2, 0, 1); 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))), (LeftAnim: True; wX: 70; wY: 73; AnimSpeed:(3, 3, 3, 3, 3, 4, 3); //CYBER + AnimCount: (2, 4, 9, 9, 2, 2, 1); AnimDeltaRight: ((X: 2; Y: -6), (X: 2; Y: -6), (X: -3; Y: -4), (X: -3; Y: -4), (X: 25; Y: -6), (X: 0; Y: -6), (X: -2; Y: -6)); AnimDeltaLeft: ((X: 3; Y: -3), (X: 3; Y: -3), (X: -3; Y: -4), (X: -3; Y: -4), (X:-26; Y: -3), (X:-1; Y: -3), (X: 1; Y: -3))), (LeftAnim: True; wX: 32; wY: 32; AnimSpeed:(3, 2, 2, 2, 1, 0, 4); //CGUN + AnimCount: (2, 4, 7, 6, 2, 0, 1); AnimDeltaRight: ((X: -1; Y: -2), (X: -1; Y: -2), (X: -2; Y: 0), (X: -2; Y: 0), (X: 0; Y: -3), (X: 0; Y: -3), (X: -1; Y: -2)); AnimDeltaLeft: ((X: -1; Y: -2), (X: -1; Y: -2), (X: -2; Y: 0), (X: -2; Y: 0), (X: -1; Y: -4), (X: -1; Y: -4), (X: 2; Y: -4))), (LeftAnim: True; wX: 64; wY: 64; AnimSpeed:(3, 2, 3, 4, 2, 0, 4); //BARON + AnimCount: (2, 4, 7, 7, 3, 0, 1); AnimDeltaRight: ((X: 4; Y: 0), (X: 2; Y: 0), (X: -1; Y: -1), (X: -1; Y: -1), (X: 1; Y: 0), (X: 1; Y: 0), (X: -1; Y: 0)); AnimDeltaLeft: ((X: 0; Y: 0), (X: 2; Y: 0), (X: -1; Y: -1), (X: -1; Y: -1), (X: -2; Y: 0), (X: -2; Y: 0), (X: 1; Y: 0))), (LeftAnim: True; wX: 64; wY: 64; AnimSpeed:(3, 2, 3, 4, 2, 0, 4); //KNIGHT + AnimCount: (2, 4, 7, 7, 3, 0, 1); AnimDeltaRight: ((X: 4; Y: 0), (X: 2; Y: 0), (X: -1; Y: -1), (X: -1; Y: -1), (X: 1; Y: 0), (X: 1; Y: 0), (X: -1; Y: 0)); AnimDeltaLeft: ((X: 0; Y: 0), (X: 2; Y: 0), (X: -1; Y: -1), (X: -1; Y: -1), (X: -2; Y: 0), (X: -2; Y: 0), (X: 1; Y: 0))), (LeftAnim: False; wX: 88; wY: 69; AnimSpeed:(3, 2, 3, 4, 2, 0, 4); //CACO + AnimCount: (1, 1, 7, 7, 6, 0, 1); AnimDeltaRight: ((X: 0; Y: -4), (X: 0; Y: -4), (X: 0; Y: -5), (X: 0; Y: -5), (X: 0; Y: -4), (X: 0; Y: -4), (X: 0; Y: -4)); AnimDeltaLeft: ((X: 0; Y: -4), (X: 0; Y: -4), (X: 0; Y: -5), (X: 0; Y: -5), (X: 0; Y: -4), (X: 0; Y: -4), (X: 0; Y: -4))), (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 2, 3, 4, 1, 0, 4); //SOUL + AnimCount: (2, 2, 7, 7, 2, 0, 1); // 10 AnimDeltaRight: ((X: 1; Y:-10), (X: 1; Y:-10), (X:-33; Y:-34), (X:-33; Y:-34), (X:-16; Y:-10), (X:-16; Y:-10), (X: -1; Y: -7)); AnimDeltaLeft: ((X: 1; Y:-10), (X: 1; Y:-10), (X:-33; Y:-34), (X:-33; Y:-34), (X:-16; Y:-10), (X:-16; Y:-10), (X: -1; Y: -7))), (LeftAnim: False; wX: 64; wY: 64; AnimSpeed:(3, 2, 3, 4, 2, 0, 4); //PAIN + AnimCount: (4, 4, 7, 7, 4, 0, 1); AnimDeltaRight: ((X: -1; Y: -3), (X: -1; Y: -3), (X: -3; Y: 0), (X: -3; Y: 0), (X: -1; Y: -3), (X: -1; Y: -3), (X: -1; Y: -4)); AnimDeltaLeft: ((X: -1; Y: -3), (X: -1; Y: -3), (X: -3; Y: 0), (X: -3; Y: 0), (X: -1; Y: -3), (X: -1; Y: -3), (X: -1; Y: -4))), (LeftAnim: True; wX: 128; wY: 64; AnimSpeed:(3, 2, 4, 4, 1, 0, 4); //SPIDER + AnimCount: (2, 6, 10, 10, 2, 0, 1); AnimDeltaRight: ((X: -4; Y: -4), (X: -4; Y: -4), (X: -2; Y: 8), (X: -2; Y: 8), (X: -3; Y: -3), (X: -3; Y: -3), (X: -3; Y: -4)); AnimDeltaLeft: ((X: -4; Y: -4), (X: -4; Y: -4), (X: -2; Y: 8), (X: -2; Y: 8), (X: -3; Y: -3), (X: -3; Y: -3), (X: 18; Y: -5))), (LeftAnim: True; wX: 64; wY: 32; AnimSpeed:(3, 2, 3, 4, 1, 0, 4); //BSP + AnimCount: (2, 6, 7, 7, 2, 0, 1); AnimDeltaRight: ((X: 0; Y: -1), (X: 0; Y: -1), (X: -3; Y: 5), (X: -3; Y: 5), (X: 7; Y: -1), (X: 7; Y: -1), (X: 1; Y: -3)); AnimDeltaLeft: ((X: 0; Y: -1), (X: 0; Y: -1), (X: -3; Y: 5), (X: -3; Y: 5), (X: 7; Y: -1), (X: 7; Y: -1), (X: 6; Y: -3))), (LeftAnim: False; wX: 64; wY: 64; AnimSpeed:(3, 2, 2, 4, 2, 0, 4); //MANCUB + AnimCount: (2, 6, 10, 10, 3, 0, 1); AnimDeltaRight: ((X: -2; Y: -7), (X: -2; Y: -7), (X: -4; Y: -2), (X: -4; Y: -2), (X: -4; Y: -7), (X: -4; Y: -7), (X:-14; Y: -7)); AnimDeltaLeft: ((X: -2; Y: -7), (X: -2; Y: -7), (X: -4; Y: -2), (X: -4; Y: -2), (X: -4; Y: -7), (X: -4; Y: -7), (X:-14; Y: -7))), (LeftAnim: True; wX: 64; wY: 32; AnimSpeed:(3, 3, 3, 3, 3, 3, 3); //SKEL + AnimCount: (2, 6, 5, 5, 2, 2, 1); AnimDeltaRight: ((X: -1; Y: 4), (X: -1; Y: 4), (X: -2; Y: 4), (X: -2; Y: 4), (X: -1; Y: 4), (X: 6; Y: 2), (X:-24; Y: 4)); AnimDeltaLeft: ((X: 1; Y: 4), (X: -1; Y: 4), (X: -2; Y: 4), (X: -2; Y: 4), (X: -2; Y: 2), (X: -5; Y: 4), (X: 26; Y: 4))), (LeftAnim: True; wX: 64; wY: 32; AnimSpeed:(3, 3, 3, 3, 3, 3, 3); //VILE + AnimCount: (2, 6, 9, 9, 10, 3, 1); AnimDeltaRight: ((X: 5; Y:-21), (X: 5; Y:-21), (X: 1; Y:-21), (X: 1; Y:-21), (X: 8; Y:-23), (X: -1; Y:-23), (X: 4; Y:-20)); AnimDeltaLeft: ((X: -8; Y:-21), (X: 5; Y:-21), (X: 1; Y:-21), (X: 1; Y:-21), (X:-10; Y:-24), (X: 3; Y:-23), (X: -4; Y:-22))), (LeftAnim: False; wX: 8; wY: 8; AnimSpeed:(2, 2, 2, 2, 3, 0, 1); //FISH + AnimCount: (2, 4, 1, 1, 2, 0, 3); AnimDeltaRight: ((X: -1; Y: 0), (X: -1; Y: 0), (X: -2; Y: -1), (X: -2; Y: -1), (X: -1; Y: -1), (X: -1; Y: -1), (X: -1; Y: -1)); AnimDeltaLeft: ((X: -1; Y: 0), (X: -1; Y: 0), (X: -2; Y: -1), (X: -2; Y: -1), (X: -1; Y: -1), (X: -1; Y: -1), (X: -1; Y: -1 ))), (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 0, 3, 0, 0, 0, 5); //BARREL + AnimCount: (3, 0, 4, 0, 0, 0, 1); AnimDeltaRight: ((X: 0; Y:-15), (X: 0; Y:-15), (X: -1; Y:-15), (X: -1; Y:-15), (X: 0; Y:-15), (X: 0; Y:-15), (X: 0; Y:-15)); AnimDeltaLeft: ((X: 0; Y:-15), (X: 0; Y:-15), (X: -1; Y:-15), (X: -1; Y:-15), (X: 0; Y:-15), (X: 0; Y:-15), (X: 0; Y:-15))), (LeftAnim: False; wX: 95; wY: 57; AnimSpeed:(1, 2, 1, 0, 1, 1, 0); //ROBO + AnimCount: (1, 12, 1, 0, 2, 4, 0); AnimDeltaRight: ((X: -2; Y:-26), (X: -2; Y:-26), (X: 0; Y:-26), (X: 0; Y:-26), (X: 2; Y:-26), (X: 15; Y:-26), (X: -2; Y:-26)); AnimDeltaLeft: ((X: -2; Y:-26), (X: -2; Y:-26), (X: 0; Y:-26), (X: 0; Y:-26), (X: 2; Y:-26), (X: 15; Y:-26), (X: -2; Y:-26))), (LeftAnim: False; wX: 32; wY: 32; AnimSpeed:(3, 2, 2, 2, 2, 0, 5); //MAN + AnimCount: (2, 4, 7, 9, 2, 0, 1); AnimDeltaRight: ((X: 0; Y: -6), (X: 0; Y: -6), (X: -2; Y: 0), (X: 2; Y: 0), (X: 1; Y: -6), (X: 1; Y: -6), (X: 0; Y: -6)); AnimDeltaLeft: ((X: 0; Y: -6), (X: 0; Y: -6), (X: -2; Y: 0), (X: 2; Y: 0), (X: 1; Y: -6), (X: 1; Y: -6), (X: 0; Y: -6))) ); - MAX_ATM = 89; // Âðåìÿ îæèäàíèÿ ïîñëå ïîòåðè öåëè - MAX_SOUL = 512; // Îãðàíè÷åíèå Lost_Soul'îâ +// Таблица типов анимации монстров: + ANIMTABLE: Array [ANIM_SLEEP..ANIM_PAIN] of + record + name: String; + loop: Boolean; + end = ((name: 'SLEEP'; loop: True), + (name: 'GO'; loop: True), + (name: 'DIE'; loop: False), + (name: 'MESS'; loop: False), + (name: 'ATTACK'; loop: False), + (name: 'ATTACK2'; loop: False), + (name: 'PAIN'; loop: False)); +implementation + +uses + e_log, g_sound, g_gfx, g_player, g_game, r_gfx, + g_weapons, g_triggers, g_items, g_options, + g_console, g_map, Math, g_menu, wadreader, + g_language, g_netmsg, idpool, utils, xstreams; + + +function g_Look(a, b: PObj; d: TDirection): Boolean; +begin + if not gmon_dbg_los_enabled then begin result := false; exit; end; // always "wall hit" + + if ((b^.X > a^.X) and (d = TDirection.D_LEFT)) or + ((b^.X < a^.X) and (d = TDirection.D_RIGHT)) then + begin + Result := False; + Exit; + end; + + Result := g_TraceVector(a^.X+a^.Rect.X+(a^.Rect.Width div 2), + a^.Y+a^.Rect.Y+(a^.Rect.Height div 2), + b^.X+b^.Rect.X+(b^.Rect.Width div 2), + b^.Y+b^.Rect.Y+(b^.Rect.Height div 2)); +end; + +// ////////////////////////////////////////////////////////////////////////// // +procedure g_Mons_ProfilersBegin (); +begin + if (profMonsLOS = nil) then profMonsLOS := TProfiler.Create('LOS CALC', g_profile_history_size); + if (profMonsLOS <> nil) then profMonsLOS.mainBegin(g_profile_los); + if g_profile_los and (profMonsLOS <> nil) then + begin + profMonsLOS.sectionBegin('loscalc'); + profMonsLOS.sectionEnd(); + end; +end; + +procedure g_Mons_ProfilersEnd (); +begin + if (profMonsLOS <> nil) and (g_profile_los) then profMonsLOS.mainEnd(); +end; + +procedure g_Mons_LOS_Start (); inline; +begin + if (profMonsLOS <> nil) then profMonsLOS.sectionBeginAccum('loscalc'); +end; + +procedure g_Mons_LOS_End (); inline; +begin + if (profMonsLOS <> nil) then profMonsLOS.sectionEnd(); +end; + + +// ////////////////////////////////////////////////////////////////////////// // +var + monCheckTrapLastFrameId: DWord = 0; + monCheckMPlatLastFrameId: LongWord = 0; + + +procedure TMonster.getMapBox (out x, y, w, h: Integer); inline; +begin + x := FObj.X+FObj.Rect.X; + y := FObj.Y+FObj.Rect.Y; + w := FObj.Rect.Width; + 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, 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; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); + {$ENDIF} + end + else + begin + 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 grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); + {$ENDIF} + end; +end; + + +// ////////////////////////////////////////////////////////////////////////// // +const + MONSTER_SIGNATURE = $534E4F4D; // 'MONS' + + MAX_ATM = 89; // Время ожидания после потери цели + MAX_SOUL = 512; // Ограничение Lost_Soul'ов +// ////////////////////////////////////////////////////////////////////////// // var - gMonsters: array of TMonster; uidMap: array [0..65535] of TMonster; // monster knows it's index freeInds: TIdPool = nil; @@ -558,12 +675,12 @@ begin 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; @@ -576,6 +693,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; @@ -602,26 +737,26 @@ function IsFriend(a, b: Byte): Boolean; begin Result := True; -// Áî÷êà - âñåì äðóã: +// Бочка - всем друг: if (a = MONSTER_BARREL) or (b = MONSTER_BARREL) then Exit; -// Ìîíñòðû îäíîãî âèäà: +// Монстры одного вида: if a = b then case a of MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO, MONSTER_SOUL, MONSTER_PAIN, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH: - Exit; // Ýòè íå áüþò ñâîèõ + Exit; // Эти не бьют своих end; -// Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à: +// Lost_Soul не может ранить Pain_Elemental'а: if (a = MONSTER_SOUL) and (b = MONSTER_PAIN) then Exit; -// Pain_Elemental íå ìîæåò ðàíèòü Lost_Soul'à: +// Pain_Elemental не может ранить Lost_Soul'а: if (b = MONSTER_SOUL) and (a = MONSTER_PAIN) then Exit; -//  îñòàëüíûõ ñëó÷àÿõ - áóäóò áèòü äðóã äðóãà: +// В остальных случаях - будут бить друг друга: Result := False; end; @@ -671,35 +806,52 @@ end; function isCorpse (o: PObj; immediately: Boolean): Integer; + (* function monsCollCheck (mon: TMonster; atag: Integer): Boolean; begin atag := atag; // shut up, fpc! result := false; // don't stop if (mon.FState = MONSTATE_DEAD) and g_Obj_Collide(o, @mon.FObj) then begin - case mon.FMonsterType of // Íå âîñêðåñèòü: + case mon.FMonsterType of // Не воскресить: MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER, MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: exit; end; - // Îñòàëüíûõ ìîæíî âîñêðåñèòü + // Остальных можно воскресить result := true; end; end; + *) var a: Integer; - mon: TMonster; + mon: PMonster; + mres: TMonster = nil; + it: TMonsterGrid.Iter; begin result := -1; - // Åñëè íóæíà âåðîÿòíîñòü + // Если нужна вероятность if not immediately and (Random(8) <> 0) then exit; - // Èùåì ìåðòâûõ ìîíñòðîâ ïîáëèçîñòè + // Ищем мертвых монстров поблизости 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 @@ -707,10 +859,10 @@ begin begin if (gMonsters[a] <> nil) and (gMonsters[a].FState = MONSTATE_DEAD) and g_Obj_Collide(o, @gMonsters[a].FObj) then begin - case gMonsters[a].FMonsterType of // Íå âîñêðåñèòü: + case gMonsters[a].FMonsterType of // Не воскресить: MONSTER_SOUL, MONSTER_PAIN, MONSTER_CYBER, MONSTER_SPIDER, MONSTER_VILE, MONSTER_BARREL, MONSTER_ROBO: Continue; - else // Îñòàëüíûõ ìîæíî âîñêðåñèòü + else // Остальных можно воскресить begin Result := a; Exit; @@ -723,160 +875,10 @@ end; procedure g_Monsters_LoadData(); begin - e_WriteLog('Loading monsters data...', MSG_NOTIFY); - - g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES]+' 0%', 0, 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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); + e_WriteLog('Loading monsters data...', TMsgType.Notify); + + g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_TEXTURES], 133, False); + g_Game_StepLoading(133); g_Game_SetLoadingText(_lc[I_LOAD_MONSTER_SOUNDS], 0, False); @@ -962,143 +964,12 @@ begin freeInds := TIdPool.Create(); clearUidMap(); monCheckTrapLastFrameId := 0; + monCheckMPlatLastFrameId := 0; end; procedure g_Monsters_FreeData(); begin - e_WriteLog('Releasing monsters data...', MSG_NOTIFY); - - g_Frames_DeleteByName('FRAMES_MONSTER_BARREL_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARREL_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARREL_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_ZOMBY_GO'); - 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_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_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_MESS'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_MAN_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_GO_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_MESS'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_CGUN_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_MESS'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_IMP_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_DEMON_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_DEMON_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_DEMON_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_DEMON_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_DEMON_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_SOUL_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_SOUL_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_SOUL_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_SOUL_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_SOUL_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_FISH_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_FISH_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_FISH_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_FISH_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_FISH_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_SPIDER_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_BSP_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_CACO_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_CACO_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_CACO_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_CACO_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_CACO_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_PAIN_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_PAIN_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_PAIN_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_PAIN_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_PAIN_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_BARON_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_KNIGHT_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_MANCUB_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_MANCUB_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_MANCUB_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_MANCUB_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_MANCUB_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_ATTACK2'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_ATTACK2_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_SKEL_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_ATTACK2'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_ATTACK2_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_VILE_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_ROBO_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_ROBO_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_ROBO_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_ROBO_ATTACK2'); - g_Frames_DeleteByName('FRAMES_MONSTER_ROBO_DIE'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_SLEEP'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_SLEEP_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_GO'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_GO_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_PAIN'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_PAIN_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_ATTACK'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_ATTACK_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_ATTACK2'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_ATTACK2_L'); - g_Frames_DeleteByName('FRAMES_MONSTER_CYBER_DIE'); + e_WriteLog('Releasing monsters data...', TMsgType.Notify); g_Sound_Delete('SOUND_MONSTER_BARREL_DIE'); @@ -1202,6 +1073,7 @@ begin gMonsters := nil; clearUidMap(); monCheckTrapLastFrameId := 0; + monCheckMPlatLastFrameId := 0; end; @@ -1223,10 +1095,10 @@ var begin result := nil; - // Íåò òàêîãî ìîíñòðà + // Нет такого монстра if (MonsterType > MONSTER_MAN) or (MonsterType = 0) then exit; - // Ñîáëþäàåì îãðàíè÷åíèå Lost_Soul'îâ + // Соблюдаем ограничение Lost_Soul'ов if MonsterType = MONSTER_SOUL then begin if soulcount > MAX_SOUL then exit; @@ -1242,7 +1114,7 @@ begin uidMap[mon.FUID] := mon; - // Íàñòðàèâàåì ïîëîæåíèå + // Настраиваем положение with mon do begin if AdjCoord then @@ -1260,6 +1132,8 @@ begin FStartDirection := Direction; FStartX := GameX; FStartY := GameY; + FObj.oldX := FObj.X; + FObj.oldY := FObj.Y; end; mon.positionChanged(); @@ -1274,7 +1148,7 @@ begin if gMonsters = nil then Exit; - // Ïðèêîëèñò ñìååòñÿ íàä ñìåðòüþ èãðîêà: + // Приколист смеется над смертью игрока: h := High(gMonsters); for a := 0 to h do begin @@ -1295,11 +1169,21 @@ begin 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(); var a: Integer; begin - // Öåëåóêàçàòåëü + // Целеуказатель if gTime mod (GAME_TICK*2) = 0 then begin pt_x := pt_x+pt_xs; @@ -1308,7 +1192,7 @@ begin if abs(pt_y) > 100 then pt_ys := -pt_ys; end; - gMon := True; // Äëÿ ðàáîòû BlockMon'à + gMon := True; // Для работы BlockMon'а if gmon_debug_think or gmon_debug_one_think_step then begin @@ -1334,163 +1218,169 @@ begin gMon := False; end; -procedure g_Monsters_Draw(); -var - a: Integer; -begin - if gMonsters <> nil then - begin - for a := 0 to High(gMonsters) do - begin - if (gMonsters[a] <> nil) then gMonsters[a].Draw(); - end; - end; -end; - -procedure g_Monsters_DrawHealth(); -var - a: Integer; - fW, fH: Byte; -begin - if gMonsters = nil then Exit; - 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; begin result := uidMap[UID]; 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 begin - for i := 0 to High(gMonsters) do - begin - if (gMonsters[i] <> nil) then - begin - if (gMonsters[i].FMonsterType <> MONSTER_NONE) then count += 1; - end; - end; + if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then count += 1; end; - Mem := TBinMemoryWriter.Create((count+1) * 350); - -// Ñîõðàíÿåì èíôîðìàöèþ öåëåóêàçàòåëÿ: - 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 begin - if (gMonsters[i] <> nil) then + if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then begin - if (gMonsters[i].FMonsterType <> MONSTER_NONE) then - begin - // Òèï ìîíñòðà: - b := gMonsters[i].MonsterType; - Mem.WriteByte(b); - // Ñîõðàíÿåì äàííûå ìîíñòðà: - gMonsters[i].SaveState(Mem); - end; + // Тип монстра + utils.writeInt(st, Byte(gMonsters[i].MonsterType)); + // Сохраняем данные монстра: + 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(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); - // Ñîçäàåì ìîíñòðà - 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.LoadState(Mem); + // Тип монстра + b := utils.readByte(st); + // Создаем монстра + 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(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 @@ -1679,8 +1569,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; @@ -1705,11 +1595,7 @@ begin end; constructor TMonster.Create(MonsterType: Byte; aID: Integer; ForcedUID: Integer = -1); -var - a: Integer; - FramesID: DWORD = 0; - s: String; - res: Boolean; + var a: Integer; begin if ForcedUID < 0 then FUID := g_CreateUID(UID_MONSTER) @@ -1736,10 +1622,13 @@ begin 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 @@ -1765,67 +1654,13 @@ begin end; SetLength(FAnim, Length(ANIMTABLE)); - - for a := 0 to High(FAnim) do + for a := ANIM_SLEEP to ANIM_PAIN do begin - FAnim[a, D_LEFT] := nil; - FAnim[a, D_RIGHT] := nil; + FAnim[a, TDirection.D_RIGHT] := TAnimationState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]); + FAnim[a, TDirection.D_LEFT] := TAnimationState.Create(ANIMTABLE[a].loop, MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a], MONSTER_ANIMTABLE[MonsterType].AnimCount[a]); end; - - for a := ANIM_SLEEP to ANIM_PAIN do - if (ANIMTABLE[a].name <> '') and - (MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a] <> 0) then - begin - s := 'FRAMES_MONSTER_'+MONSTERTABLE[MonsterType].Name+ - '_'+ANIMTABLE[a].name; - - res := g_Frames_Exists(s); - - if res then - res := g_Frames_Get(FramesID, s); - - // Åñëè íåò òàêîé àíèìàöèè, òî ïðîáóåì çàìåíèòü åå íà àíèìàöèþ ñìåðòè: - if (not res) then - begin - // Çàìåíÿåì òîëüêî ANIM_MESS íà ANIM_DIE: - if a <> ANIM_MESS then - Continue; - - 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, - MONSTER_ANIMTABLE[MonsterType].AnimSpeed[ANIM_DIE]); - FAnim[a, 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, - MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a]); - - // Åñëè åñòü îòäåëüíàÿ ëåâàÿ àíèìàöèÿ - çàãðóæàåì: - if MONSTER_ANIMTABLE[MonsterType].LeftAnim then - begin - s := 'FRAMES_MONSTER_'+MONSTERTABLE[MonsterType].Name+ - '_'+ANIMTABLE[a].name+'_L'; - if g_Frames_Exists(s) then - g_Frames_Get(FramesID, s); - end; - - FAnim[a, D_LEFT] := TAnimation.Create(FramesID, ANIMTABLE[a].loop, - MONSTER_ANIMTABLE[MonsterType].AnimSpeed[a]); - end; - -// Äëÿ êîëäóíà çàãðóæàåì òàêæå àíèìàöèþ îãíÿ: if MonsterType = MONSTER_VILE then - begin - g_Frames_Get(FramesID, 'FRAMES_FIRE'); - vilefire := TAnimation.Create(FramesID, True, 2); - end - else - vilefire := nil; + vilefire := TAnimationState.Create(True, 2, 0); // !!! len end; function TMonster.Damage(aDamage: Word; VelX, VelY: Integer; SpawnerUID: Word; t: Byte): Boolean; @@ -1835,46 +1670,55 @@ var 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; -// Ðîáîòó óðîíà íåò: +// Роботу урона нет: if FMonsterType = MONSTER_ROBO then aDamage := 0; -// Íàíîñèì óðîí: +// Наносим урон: if g_Game_IsServer then Dec(FHealth, aDamage); -// Óñèëèâàåì áîëü ìîíñòðà îò óðîíà: +// Усиливаем боль монстра от урона: if FPain = 0 then FPain := 3; FPain := FPain+aDamage; -// Åñëè áîëü ñóùåñòâåííàÿ, òî ìåíÿåì ñîñòîÿíèå íà áîëåâîå: +// Если боль существенная, то меняем состояние на болевое: if FState <> MONSTATE_PAIN then if (FPain >= MONSTERTABLE[FMonsterType].MinPain) and (FMonsterType <> MONSTER_BARREL) then SetState(MONSTATE_PAIN); -// Åñëè ðàçðåøåíà êðîâü - ñîçäàåì áðûçãè êðîâè: +// Если разрешена кровь - создаем брызги крови: if (gBloodCount > 0) then begin c := Min(aDamage, 200); @@ -1889,17 +1733,17 @@ begin end; end; -// Òåïåðü öåëü - óäàðèâøèé, åñëè òîëüêî íå ñàì ñåáÿ: +// Теперь цель - ударивший, если только не сам себя: if (SpawnerUID <> FUID) and (BehaviourDamage(SpawnerUID, FBehaviour, FMonsterType)) then begin FTargetUID := SpawnerUID; FTargetTime := 0; end; -// Çäîðîâüå çàêîí÷èëîñü: +// Здоровье закончилось: if FHealth <= 0 then begin - // Åñëè ýòî íå áî÷êà è óáèë èãðîê, òî åìó +1: + // Если это не бочка и убил игрок, то ему +1: if (FMonsterType <> MONSTER_BARREL) then begin if (g_GetUIDType(SpawnerUID) = UID_PLAYER) then @@ -1922,7 +1766,7 @@ begin end; end; - // Âûáèðàåì ëóò: + // Выбираем лут: case FMonsterType of MONSTER_ZOMBY: c := ITEM_AMMO_BULLETS; MONSTER_SERG: c := ITEM_WEAPON_SHOTGUN1; @@ -1931,23 +1775,24 @@ begin else c := 0; end; - // Áðîñàåì ëóò: + // Бросаем лут: if c <> 0 then 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; - // Òðóï äàëüøå íå èäåò: + // Труп дальше не идет: FObj.Vel.X := 0; - // Ó òðóïà ðàçìåðû ìåíüøå: + // У трупа размеры меньше: if (FMonsterType <> MONSTER_FISH) and (FMonsterType <> MONSTER_PAIN) then begin FObj.Rect.Y := FObj.Rect.Y + FObj.Rect.Height-12; @@ -1955,7 +1800,7 @@ begin positionChanged(); end; - // Óðîí áûë ñèëüíûì => ñëàáûå - â êàøó: + // Урон был сильным => слабые - в кашу: if (FHealth <= -30) and ((FMonsterType = MONSTER_IMP) or (FMonsterType = MONSTER_ZOMBY) or (FMonsterType = MONSTER_SERG) or (FMonsterType = MONSTER_CGUN) or @@ -1970,14 +1815,14 @@ begin SetState(MONSTATE_DIE); end; - // Àêòèâèðîâàòü òðèããåðû, æäóùèå ñìåðòè ýòîãî ìîíñòðà: + // Активировать триггеры, ждущие смерти этого монстра: if g_Game_IsServer then ActivateTriggers(); FHealth := 0; end else if FState = MONSTATE_SLEEP then - begin // Ñïàë, ðàçáóäèëè íåñìåðòåëüíûì óäàðîì: + begin // Спал, разбудили несмертельным ударом: FPain := MONSTERTABLE[FMonsterType].Pain; SetState(MONSTATE_GO); end; @@ -1991,7 +1836,7 @@ begin Result := False; if g_Game_IsClient then Exit; - if not Live then + if not alive then Exit; if FHealth < FMaxHealth then @@ -2008,8 +1853,8 @@ 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(); @@ -2038,86 +1883,6 @@ begin inherited Destroy(); end; -procedure TMonster.Draw(); -var - m: TMirrorType; - dx, dy, c: 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)); - -// Åñëè êîëäóí ñòðåëÿåò, òî ðèñóåì îãîíü: - 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); - -// Íå â îáëàñòè ðèñîâàíèÿ íå ðåñóåì: - 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 - case FMonsterType of - MONSTER_BARREL, MONSTER_SOUL, MONSTER_PAIN: Exit; - end; - -// Åñòü ÷òî ðèñîâàòü ïðè òåêóùåì ïîâåäåíèè: - if FAnim[FCurAnim, FDirection] <> nil then - begin - // Åñëè íåò ëåâîé àíèìàöèè èëè îíà ñîâïàäàåò ñ ïðàâîé => îòðàæàåì ïðàâóþ: - if (FDirection = D_LEFT) and - ((not MONSTER_ANIMTABLE[FMonsterType].LeftAnim) or - (FAnim[FCurAnim, D_LEFT].FramesID = FAnim[FCurAnim, D_RIGHT].FramesID)) and - (FMonsterType <> MONSTER_BARREL) then - m := M_HORIZONTAL - else - m := M_NONE; - - // Ëåâàÿ àíèìàöèÿ => ìåíÿåì ñìåùåíèå îòíîñèòåëüíî öåíòðà: - if (FDirection = 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 - begin // Íåò îòäåëüíîé ëåâîé àíèìàöèè - // Ðàññòîÿíèå îò êðàÿ òåêñòóðû äî êðàÿ âèçóàëüíîãî ïîëîæåíèÿ îáúåêòà íà òåêñòóðå: - c := (MONSTERTABLE[FMonsterType].Rect.X - dx) + MONSTERTABLE[FMonsterType].Rect.Width; - // Ðàññòîÿíèå îò êðàÿ õèò áîêñà äî êðàÿ âèçóàëüíîãî ïîëîæåíèÿ îáúåêòà íà òåêñòóðå: - dx := FAnim[FCurAnim, FDirection].Width - c - MONSTERTABLE[FMonsterType].Rect.X; - // Ò.ê. äâèãàòü òåêñòóðó íóæíî áóäåò â ïðîòèâîïîëîæíîì íàïðàâëåíèè: - dx := -dx; - // Ýòî çíà÷èò: dX := -frameWidth - animDeltaX + hitX + hitWidth + hitX - end; - end - else // Ïðàâàÿ àíèìàöèÿ - begin - dx := MONSTER_ANIMTABLE[FMonsterType].AnimDeltaRight[FCurAnim].X; - dy := MONSTER_ANIMTABLE[FMonsterType].AnimDeltaRight[FCurAnim].Y; - end; - - // Ðèñóåì: - FAnim[FCurAnim, FDirection].Draw(Obj.X+dx, Obj.Y+dy, m); - end; - - if g_debug_Frames then - begin - e_DrawQuad(FObj.X+FObj.Rect.X, - FObj.Y+FObj.Rect.Y, - FObj.X+FObj.Rect.X+FObj.Rect.Width-1, - FObj.Y+FObj.Rect.Y+FObj.Rect.Height-1, - 0, 255, 0); - end; -end; - procedure TMonster.MakeBloodSimple(Count: Word); begin g_GFX_Blood(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)+8, @@ -2149,12 +1914,12 @@ procedure TMonster.SetState(State: Byte; ForceAnim: Byte = 255); var Anim: Byte; begin -// Åñëè ñîñòîÿíèå = íà÷àëè óìèðàòü, à ýòîò ìîíñòð = Lost_Soul, -// òî ñîáëþäàåì îãðàíè÷åíèå êîëè÷åñòâà Lost_Soul'îâ: +// Если состояние = начали умирать, а этот монстр = Lost_Soul, +// то соблюдаем ограничение количества Lost_Soul'ов: if (State = MONSTATE_DIE) and (MonsterType = MONSTER_SOUL) then soulcount := soulcount-1; -// Ïðèñìåðòè - íåëüçÿ ñðàçó íà÷èíàòü àòàêîâàòü èëè áåãàòü: +// Присмерти - нельзя сразу начинать атаковать или бегать: case FState of MONSTATE_DIE, MONSTATE_DEAD, MONSTATE_REVIVE: if (State <> MONSTATE_DEAD) and (State <> MONSTATE_REVIVE) and @@ -2162,12 +1927,12 @@ begin Exit; end; -// Ñìåíà ñîñòîÿíèÿ: +// Смена состояния: FState := State; if g_Game_IsServer and g_Game_IsNet then MH_SEND_MonsterState(FUID, ForceAnim); -// Íîâàÿ àíèìàöèÿ ïðè íîâîì ñîñòîÿíèè: +// Новая анимация при новом состоянии: case FState of MONSTATE_SLEEP: Anim := ANIM_SLEEP; MONSTATE_PAIN: Anim := ANIM_PAIN; @@ -2177,7 +1942,7 @@ begin MONSTATE_ATTACK: Anim := ANIM_ATTACK; MONSTATE_DIE: Anim := ANIM_DIE; MONSTATE_REVIVE: - begin // íà÷àëè âîñðåøàòüñÿ + begin // начали восрешаться Anim := FCurAnim; FAnim[Anim, FDirection].Revert(True); @@ -2189,11 +1954,11 @@ begin else Exit; end; -// Íàäî ñìåíèòü àíèìàöèþ íà íåñòàíäàðòíóþ: +// Надо сменить анимацию на нестандартную: if ForceAnim <> 255 then Anim := ForceAnim; -// Åñëè àíèìàöèÿ íîâàÿ - ïåðåçàïóñêàåì å¸: +// Если анимация новая - перезапускаем её: if FCurAnim <> Anim then if FAnim[Anim, FDirection] <> nil then begin @@ -2203,13 +1968,10 @@ begin end; function TMonster.TeleportTo(X, Y: Integer; silent: Boolean; dir: Byte): Boolean; -var - TA: TAnimation; - FramesID: DWORD; begin Result := False; -//  òî÷êå íàçíà÷åíèÿ ñòåíà: +// В точке назначения стена: if g_CollideLevel(X, Y, FObj.Rect.Width, FObj.Rect.Height) then begin g_Sound_PlayExAt('SOUND_GAME_NOTELEPORT', FObj.X, FObj.Y); @@ -2218,16 +1980,15 @@ begin Exit; end; - TA := nil; - -// Ýôôåêò òåëåïîðòà â ïîçèöèè ìîíñòðà: +// Эффект телепорта в позиции монстра: if not silent then begin - if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then - TA := TAnimation.Create(FramesID, False, 6); g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Obj.X, Obj.Y); - g_GFX_OnceAnim(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, - FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-32, TA); + r_GFX_OnceAnim( + R_GFX_TELEPORT, + FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, + FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-32 + ); if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, @@ -2237,28 +1998,32 @@ begin 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 + begin // обратное + if FDirection = TDirection.D_RIGHT then + FDirection := TDirection.D_LEFT else - FDirection := D_RIGHT; + FDirection := TDirection.D_RIGHT; end; -// Ýôôåêò òåëåïîðòà â òî÷êå íàçíà÷åíèÿ: - if not silent and (TA <> nil) then +// Эффект телепорта в точке назначения: + if not silent then begin - g_GFX_OnceAnim(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, - FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-32, TA); - TA.Free(); + r_GFX_OnceAnim( + R_GFX_TELEPORT, + FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, + FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)-32 + ); if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-32, @@ -2270,6 +2035,12 @@ begin 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; @@ -2277,18 +2048,23 @@ var 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 if (FState <> MONSTATE_DIE) and (FState <> MONSTATE_DEAD) then fall := False; -// Ëåòàþùèå ìîíòñðû: +// Летающие монтсры: if ((FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_CACO)) and @@ -2296,7 +2072,7 @@ begin (FState <> MONSTATE_DEAD) then fall := False; -// Ìåíÿåì ñêîðîñòü òîëüêî ïî ÷åòíûì êàäðàì: +// Меняем скорость только по четным кадрам: if gTime mod (GAME_TICK*2) <> 0 then begin g_Obj_Move(@FObj, fall, True, True); @@ -2309,16 +2085,25 @@ begin else FPainSound := False; -// Äâèãàåìñÿ: +// Двигаемся: 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 @@ -2330,7 +2115,7 @@ begin oldvelx := FObj.Vel.X; -// Ñîïðîòèâëåíèå âîçäóõà äëÿ òðóïà: +// Сопротивление воздуха для трупа: if (FState = MONSTATE_DIE) or (FState = MONSTATE_DEAD) then FObj.Vel.X := z_dec(FObj.Vel.X, 1); @@ -2353,11 +2138,11 @@ begin end; end; -// Ìåðòâûé íè÷åãî íå äåëàåò: +// Мертвый ничего не делает: if (FState = MONSTATE_DEAD) then goto _end; -// AI ìîíñòðîâ âûêëþ÷åí: +// AI монстров выключен: if g_debug_MonsterOff then begin FSleep := 1; @@ -2365,7 +2150,7 @@ begin SetState(MONSTATE_SLEEP); end; -// Âîçìîæíî, ñîçäàåì ïóçûðüêè â âîäå: +// Возможно, создаем пузырьки в воде: if WordBool(st and MOVE_INWATER) and (Random(32) = 0) then case FMonsterType of MONSTER_FISH: @@ -2385,7 +2170,7 @@ begin end; end; -// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ: +// Если прошел первый кадр анимации взрыва бочки, то взрыв: if FMonsterType = MONSTER_BARREL then begin if (FState = MONSTATE_DIE) and (FAnim[FCurAnim, FDirection].CurrentFrame = 1) and @@ -2395,7 +2180,7 @@ begin 60, FUID); end; -// Lost_Soul âûëåòåë èç âîäû => óñêîðÿåòñÿ: +// Lost_Soul вылетел из воды => ускоряется: if FMonsterType = MONSTER_SOUL then if WordBool(st and MOVE_HITAIR) then g_Obj_SetSpeed(@FObj, 16); @@ -2403,15 +2188,15 @@ begin if FAmmo < 0 then FAmmo := FAmmo + 1; -// Åñëè íà÷àëè âñïëûâàòü, òî ïðîäîëæàåì: +// Если начали всплывать, то продолжаем: if FObj.Vel.Y < 0 then if WordBool(st and MOVE_INWATER) then FObj.Vel.Y := -4; -// Òàéìåð - æäåì ïîñëå ïîòåðè öåëè: +// Таймер - ждем после потери цели: FTargetTime := FTargetTime + 1; -// Ãèëüçû +// Гильзы if FShellTimer > -1 then if FShellTimer = 0 then begin @@ -2431,7 +2216,7 @@ begin FShellTimer := -1; end else Dec(FShellTimer); -// Ïðîáóåì óâåðíóòüñÿ îò ëåòÿùåé ïóëè: +// Пробуем увернуться от летящей пули: if fall then if (FState in [MONSTATE_GO, MONSTATE_RUN, MONSTATE_RUNOUT, MONSTATE_ATTACK, MONSTATE_SHOOT]) then @@ -2442,9 +2227,9 @@ begin FObj.Vel.Y := -MONSTERTABLE[FMonsterType].Jump; case FState of - MONSTATE_PAIN: // Ñîñòîÿíèå - Áîëü + MONSTATE_PAIN: // Состояние - Боль begin - // Áîëü ñèëüíàÿ => ìîíñòð êðè÷èò: + // Боль сильная => монстр кричит: if FPain >= MONSTERTABLE[FMonsterType].Pain then begin FPain := MONSTERTABLE[FMonsterType].Pain; @@ -2453,10 +2238,10 @@ begin if (not gSoundEffectsDF) and (FPain >= MONSTERTABLE[FMonsterType].MinPain) then PainSound(); - // Ñíèæàåì áîëü ñî âðåìåíåì: + // Снижаем боль со временем: FPain := FPain - 5; - // Áîëü óæå íå îøóòèìàÿ => èäåì äàëüøå: + // Боль уже не ошутимая => идем дальше: if FPain <= MONSTERTABLE[FMonsterType].MinPain then begin FPain := 0; @@ -2465,23 +2250,23 @@ begin end; end; - MONSTATE_SLEEP: // Ñîñòîÿíèå - Ñîí + MONSTATE_SLEEP: // Состояние - Сон begin - // Ñïèì: + // Спим: FSleep := FSleep + 1; - // Ïðîñïàëè äîñòàòî÷íî: + // Проспали достаточно: if FSleep >= 18 then FSleep := 0 - else // åùå ñïèì + else // еще спим goto _end; - // Íà èãðîêîâ èäóò òîëüêî îáû÷íûå ìîíñòðû, êèëëåðû è ìàíüÿêè + // На игроков идут только обычные монстры, киллеры и маньяки if (FBehaviour = BH_NORMAL) or (FBehaviour = BH_KILLER) or (FBehaviour = BH_MANIAC) 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 @@ -2493,24 +2278,24 @@ begin Break; end; - // Íà ìîíñòðîâ òÿíåò ìàíüÿêîâ, ïîåõàâøèõ è êàííèáàëîâ + // На монстров тянет маньяков, поехавших и каннибалов if (FTargetUID = 0) and ((FBehaviour = BH_MANIAC) or (FBehaviour = BH_INSANE) or (FBehaviour = BH_CANNIBAL)) 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 (FBehaviour = BH_MANIAC) and (IsFriend(gMonsters[a].FMonsterType, FMonsterType)) then Continue; - // Ïîåõàâøèå òàêæå, íî ìîãóò îáîçëèòüñÿ íà áî÷êó + // Поехавшие также, но могут обозлиться на бочку if (FBehaviour = BH_INSANE) and (gMonsters[a].FMonsterType <> MONSTER_BARREL) and (IsFriend(gMonsters[a].FMonsterType, FMonsterType)) then Continue; - // Êàííèáàëû íàïàäàþò íà ñåáå ïîäîáíûõ + // Каннибалы нападают на себе подобных if (FBehaviour = BH_CANNIBAL) and (gMonsters[a].FMonsterType <> FMonsterType) then Continue; if g_Look(@FObj, @gMonsters[a].Obj, FDirection) then @@ -2524,19 +2309,19 @@ begin end; end; - MONSTATE_WAIT: // Ñîñòîÿíèå - Îæèäàíèå + MONSTATE_WAIT: // Состояние - Ожидание begin - // Æäåì: + // Ждем: FSleep := FSleep - 1; - // Âûæäàëè äîñòàòî÷íî - èäåì: + // Выждали достаточно - идем: if FSleep < 0 then SetState(MONSTATE_GO); end; - MONSTATE_GO: // Ñîñòîÿíèå - Äâèæåíèå (ñ îñìîòðîì ñèòóàöèè) + MONSTATE_GO: // Состояние - Движение (с осмотром ситуации) begin - // Åñëè íàòêíóëèñü íà ÁëîêÌîí - óáåãàåì îò íåãî: + // Если наткнулись на БлокМон - убегаем от него: if WordBool(st and MOVE_BLOCK) then begin Turn(); @@ -2546,7 +2331,7 @@ begin goto _end; end; - // Åñëè ìîíñòð - êîëäóí, òî ïðîáóåì âîñêðåñèòü êîãî-íèáóäü: + // Если монстр - колдун, то пробуем воскресить кого-нибудь: if (FMonsterType = MONSTER_VILE) then if isCorpse(@FObj, False) <> -1 then begin @@ -2556,10 +2341,10 @@ begin goto _end; end; - // Öåëü ïîãèáëà èëè äàâíî æäåì: + // Цель погибла или давно ждем: if (not GetPos(FTargetUID, @o)) or (FTargetTime > MAX_ATM) then if not findNewPrey() then - begin // Íîâûõ öåëåé íåò + begin // Новых целей нет FTargetUID := 0; o.X := FObj.X+pt_x; o.Y := FObj.Y+pt_y; @@ -2569,10 +2354,10 @@ begin o.Accel.Y := 0; o.Rect := _Rect(0, 0, 0, 1); end - else // Íîâàÿ öåëü åñòü - áåðåì åå êîîðäèíàòû + else // Новая цель есть - берем ее координаты GetPos(FTargetUID, @o); - // Öåëü î÷åíü áëèçêî - ïèíàåì: + // Цель очень близко - пинаем: if g_Obj_Collide(@FObj, @o) and (FTargetUID <> 0) then begin FTargetTime := 0; @@ -2583,42 +2368,42 @@ begin end; end; - // Ðàññòîÿíèå äî öåëè: + // Расстояние до цели: sx := o.X+o.Rect.X+(o.Rect.Width div 2)-(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)); sy := o.Y+o.Rect.Y+(o.Rect.Height div 2)-(FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2)); - // Ïîâîðà÷èâàåìñÿ â ñòîðîíó öåëè: + // Поворачиваемся в сторону цели: 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 - if Abs(sx) > Abs(sy) then // óãîë âûñòðåëà óäîáíûé + if Abs(sx) > Abs(sy) then // угол выстрела удобный if shoot(@o, False) then goto _end; - // Åñëè öåëü ïî÷òè íà îäíîé âåðòèêàëè - áåãàåì òóäà-ñþäà: + // Если цель почти на одной вертикали - бегаем туда-сюда: if Abs(sx) < 40 then if FMonsterType <> MONSTER_FISH then 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; - // Óïåðëèñü â ñòåíó: + // Уперлись в стену: if WordBool(st and MOVE_HITWALL) then begin if g_Triggers_PressR(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height, FUID, ACTIVATE_MONSTERPRESS) <> nil then - begin // Ñìîãëè íàæàòü êíîïêó - íåáîëüøîå îæèäàíèå + begin // Смогли нажать кнопку - небольшое ожидание FSleep := 4; SetState(MONSTATE_WAIT); @@ -2627,11 +2412,11 @@ begin case FMonsterType of MONSTER_CACO, MONSTER_SOUL, MONSTER_PAIN, MONSTER_FISH: ; - else // Íå ëåòàþò: + else // Не летают: if (g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj)) and (FObj.Accel.Y = 0) then - begin // Ñòîèì íà òâåðäîì ïîëó èëè ñòóïåíè - // Ïðûæîê ÷åðåç ñòåíó: + begin // Стоим на твердом полу или ступени + // Прыжок через стену: FObj.Vel.Y := -MONSTERTABLE[FMonsterType].Jump; SetState(MONSTATE_CLIMB); end; @@ -2640,84 +2425,84 @@ begin goto _end; end; - // Ìîíñòðû, íå ïîäâåðæåííûå ãðàâèòàöèè: + // Монстры, не подверженные гравитации: if (FMonsterType = MONSTER_CACO) or (FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_FISH) then begin if FMonsterType = MONSTER_FISH then - begin // Ðûáà + begin // Рыба if not WordBool(st and MOVE_INWATER) then - begin // Ðûáà âíå âîäû: + begin // Рыба вне воды: if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then - begin // "Ñòîèò" òâåðäî - // Ðûáà òðåïûõàåòñÿ íà ïîâåðõíîñòè: + begin // "Стоит" твердо + // Рыба трепыхается на поверхности: if FObj.Accel.Y = 0 then FObj.Vel.Y := -6; FObj.Accel.X := FObj.Accel.X - 8 + Random(17); end; - // Ðûáå áîëüíî: + // Рыбе больно: SetState(MONSTATE_PAIN); FPain := FPain + 50; end - else // Ðûáà â âîäå + else // Рыба в воде begin - // Ïëûâåì â ñòîðîíó öåëè ïî-âåðòèêàëè: + // Плывем в сторону цели по-вертикали: if Abs(sy) > 8 then FObj.Vel.Y := g_basic.Sign(sy)*4 else FObj.Vel.Y := 0; - // Ðûáà ïëûâåò ââåðõ: + // Рыба плывет вверх: 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; end; end - else // Ëåòàþùèå ìîíñòðû + else // Летающие монстры begin - // Ëåòèì â ñòîðîíó öåëè ïî-âåðòèêàëè: + // Летим в сторону цели по-вертикали: if Abs(sy) > 8 then FObj.Vel.Y := g_basic.Sign(sy)*4 else FObj.Vel.Y := 0; end; end - else // "Íàçåìíûå" ìîíñòðû + else // "Наземные" монстры begin - // Âîçìîæíî, ïèíàåì êóñêè: + // Возможно, пинаем куски: if (FObj.Vel.X <> 0) and (gGibs <> nil) then begin b := Abs(FObj.Vel.X); 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 - // Ïèíàåì êóñêè + // Пинаем куски if FObj.Vel.X < 0 then begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120); // íàëåâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120); // налево end else begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // направо end; end; end; end; - // Áîññû ìîãóò ïèíàòü òðóïû: + // Боссы могут пинать трупы: if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and (FObj.Vel.X <> 0) and (gCorpses <> nil) then begin @@ -2729,24 +2514,24 @@ begin co := gCorpses[a].Obj; if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4, 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; - // Åñëè öåëü âûñîêî, òî, âîçìîæíî, ïðûãàåì: + // Если цель высоко, то, возможно, прыгаем: if sy < -40 then if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then - // ñòîèò òâåðäî + // стоит твердо if (Random(4) = 0) and (FObj.Accel.Y = 0) then FObj.Vel.Y := -MONSTERTABLE[FMonsterType].Jump; end; FSleep := FSleep + 1; - // Èíîãäà ðû÷èì: + // Иногда рычим: if FSleep >= 8 then begin FSleep := 0; @@ -2754,23 +2539,23 @@ begin ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_RUN: // Ñîñòîÿíèå - Áåã + MONSTATE_RUN: // Состояние - Бег begin - // Åñëè íàòêíóëèñü íà ÁëîêÌîí - óáåãàåì îò íåãî: + // Если наткнулись на БлокМон - убегаем от него: if WordBool(st and MOVE_BLOCK) then begin Turn(); @@ -2782,78 +2567,78 @@ begin FSleep := FSleep - 1; - // Ïðîáåæàëè äîñòàòî÷íî èëè âðåçàëèñü â ñòåíó => ïåðåõîäèì íà øàã: + // Пробежали достаточно или врезались в стену => переходим на шаг: if (FSleep <= 0) or (WordBool(st and MOVE_HITWALL) and ((FObj.Vel.Y+FObj.Accel.Y) = 0)) then begin FSleep := 0; SetState(MONSTATE_GO); - // Ñòåíà - èäåì îáðàòíî: + // Стена - идем обратно: if WordBool(st and (MOVE_HITWALL or MOVE_BLOCK)) then Turn(); - // Èíîãäà ðû÷èì: + // Иногда рычим: if Random(8) = 0 then ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_RUNOUT: // Ñîñòîÿíèå - Óáåãàåò îò ÷åãî-òî + MONSTATE_RUNOUT: // Состояние - Убегает от чего-то begin - // Âûøëè èç ÁëîêÌîíà: + // Вышли из БлокМона: if (not WordBool(st and MOVE_BLOCK)) and (FSleep > 0) then FSleep := 0; FSleep := FSleep - 1; - // Óáàæåëè äîñòàòî÷íî äàëåêî => ïåðåõîäèì íà øàã: + // Убажели достаточно далеко => переходим на шаг: if FSleep <= -18 then begin FSleep := 0; SetState(MONSTATE_GO); - // Ñòåíà/ÁëîêÌîí - èäåì îáðàòíî: + // Стена/БлокМон - идем обратно: if WordBool(st and (MOVE_HITWALL or MOVE_BLOCK)) then Turn(); - // Èíîãäà ðû÷èì: + // Иногда рычим: if Random(8) = 0 then ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_CLIMB: // Ñîñòîÿíèå - Ïðûæîê (÷òîáû îáîéòè ñòåíó) + MONSTATE_CLIMB: // Состояние - Прыжок (чтобы обойти стену) begin - // Äîñòèãëè âûñøåé òî÷êè ïðûæêà èëè ñòåíà êîí÷èëàñü => ïåðåõîäèì íà øàã: + // Достигли высшей точки прыжка или стена кончилась => переходим на шаг: if ((FObj.Vel.Y+FObj.Accel.Y) >= 0) or (not WordBool(st and MOVE_HITWALL)) then begin FSleep := 0; SetState(MONSTATE_GO); - // Ñòåíà íå êîí÷èëàñü => áåæèì îò íåå: + // Стена не кончилась => бежим от нее: if WordBool(st and (MOVE_HITWALL or MOVE_BLOCK)) then begin Turn(); @@ -2862,24 +2647,24 @@ begin 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; - // Åñëè â âîäå, òî çàìåäëÿåìñÿ: + // Если в воде, то замедляемся: if WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_ATTACK, // Ñîñòîÿíèå - Àòàêà - MONSTATE_SHOOT: // Ñîñòîÿíèå - Ñòðåëüáà + MONSTATE_ATTACK, // Состояние - Атака + MONSTATE_SHOOT: // Состояние - Стрельба begin - // Lost_Soul âðåçàëñÿ â ñòåíó ïðè àòàêå => ïåðåõîäèò íà øàã: + // Lost_Soul врезался в стену при атаке => переходит на шаг: if FMonsterType = MONSTER_SOUL then begin if WordBool(st and (MOVE_HITWALL or MOVE_HITCEIL or MOVE_HITLAND)) then @@ -2888,14 +2673,14 @@ begin goto _end; end; - // Çàìåäëÿåìñÿ ïðè àòàêå: + // Замедляемся при атаке: if FMonsterType <> MONSTER_FISH then FObj.Vel.X := z_dec(FObj.Vel.X, 1); - // Íóæíî ñòðåëÿòü, à ìîíñòð - êîëäóí: + // Нужно стрелять, а монстр - колдун: if (FMonsterType = MONSTER_VILE) and (FState = MONSTATE_SHOOT) then begin - // Öåëü ïîãèáëà => èäåì äàëüøå: + // Цель погибла => идем дальше: if not GetPos(FTargetUID, @o) then begin SetState(MONSTATE_GO); @@ -2903,7 +2688,7 @@ begin goto _end; end; - // Öåëü íå âèäíî => èäåì äàëüøå: + // Цель не видно => идем дальше: if not g_Look(@FObj, @o, FDirection) then begin SetState(MONSTATE_GO); @@ -2911,7 +2696,7 @@ begin goto _end; end; - // Öåëü â âîäå - íå çàãîðèòñÿ => èäåì äàëüøå: + // Цель в воде - не загорится => идем дальше: if g_Obj_CollideWater(@o, 0, 0) then begin SetState(MONSTATE_GO); @@ -2919,7 +2704,7 @@ begin goto _end; end; - // Æàðèì öåëü: + // Жарим цель: tx := o.X+o.Rect.X+(o.Rect.Width div 2); ty := o.Y+o.Rect.Y+(o.Rect.Height div 2); g_Weapon_HitUID(FTargetUID, 2, FUID, HIT_SOME); @@ -2929,31 +2714,31 @@ begin _end: -// Ñîñòîÿíèå - Âîñêðåøåíèå: +// Состояние - Воскрешение: if FState = MONSTATE_REVIVE then if FAnim[FCurAnim, FDirection].Played then - begin // Îáðàòíàÿ àíèìàöèÿ óìèðàíèÿ çàêîí÷èëàñü - èäåì äàëüøå: + begin // Обратная анимация умирания закончилась - идем дальше: FAnim[FCurAnim, FDirection].Revert(False); SetState(MONSTATE_GO); end; -// Åñëè åñòü àíèìàöèÿ îãíÿ êîëäóíà - ïóñòü îíà èäåò: +// Если есть анимация огня колдуна - пусть она идет: if vilefire <> nil then vilefire.Update(); -// Ñîñòîÿíèå - Óìèðàåò è òåêóùàÿ àíèìàöèÿ ïðîèãðàíà: +// Состояние - Умирает и текущая анимация проиграна: if (FState = MONSTATE_DIE) and (FAnim[FCurAnim, FDirection] <> nil) and (FAnim[FCurAnim, FDirection].Played) then begin - // Óìåð: + // Умер: SetState(MONSTATE_DEAD); - // Pain_Elemental ïðè ñìåðòè âûïóñêàåò 3 Lost_Soul'à: + // Pain_Elemental при смерти выпускает 3 Lost_Soul'а: 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); @@ -2963,7 +2748,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); @@ -2973,7 +2758,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); @@ -2985,35 +2770,35 @@ _end: if g_Game_IsNet then MH_SEND_CoopStats(); end; - // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ: + // У этих монстров нет трупов: if (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_BARREL) then FRemoved := True; end; -// Ñîâåðøåíèå àòàêè è ñòðåëüáû: +// Совершение атаки и стрельбы: if (FState = MONSTATE_ATTACK) or (FState = MONSTATE_SHOOT) then if (FAnim[FCurAnim, FDirection] <> nil) then - // Àíèìàöèÿ àòàêè åñòü - ìîæíî àòàêîâàòü + // Анимация атаки есть - можно атаковать if (FAnim[FCurAnim, FDirection].Played) then - begin // Àíèìàöèÿ àòàêè çàêîí÷èëàñü => ïåðåõîäèì íà øàã + begin // Анимация атаки закончилась => переходим на шаг if FState = MONSTATE_ATTACK then - begin // Ñîñòîÿíèå - Àòàêà - // Åñëè ìîíñòð íå Lost_Soul, òî ïîñëå àòàêè ïåðåõîäèì íà øàã: + begin // Состояние - Атака + // Если монстр не Lost_Soul, то после атаки переходим на шаг: if FMonsterType <> MONSTER_SOUL then SetState(MONSTATE_GO); end - else // Ñîñòîÿíèå - Ñòðåëüáà + else // Состояние - Стрельба begin - // Ïåðåõîäèì íà øàã, åñëè íå íàäî ñòðåëÿòü åùå ðàç: + // Переходим на шаг, если не надо стрелять еще раз: if not FChainFire then SetState(MONSTATE_GO) else - begin // Íàäî ñòðåëÿòü åùå + begin // Надо стрелять еще FChainFire := False; - // Ò.ê. ñîñòîÿíèå íå èçìåíèëîñü, è íóæåí - // íîâûé öèêë îæèäàíèÿ çàâåðøåíèÿ àíèìàöèè: + // Т.к. состояние не изменилось, и нужен + // новый цикл ожидания завершения анимации: FAnim[FCurAnim, FDirection].Reset(); end; end; @@ -3021,80 +2806,93 @@ _end: FWaitAttackAnim := False; end - else // Àíèìàöèÿ àòàêè åùå èäåò (èñêëþ÷åíèå - Lost_Soul): + else // Анимация атаки еще идет (исключение - Lost_Soul): if (FMonsterType = MONSTER_SOUL) or ( (not FWaitAttackAnim) and (FAnim[FCurAnim, FDirection].CurrentFrame = (FAnim[FCurAnim, FDirection].TotalFrames div 2)) ) then - begin // Àòàêè åùå íå áûëî è ýòî ñåðåäèíà àíèìàöèè àòàêè + begin // Атаки еще не было и это середина анимации атаки if FState = MONSTATE_ATTACK then - begin // Ñîñòîÿíèå - Àòàêà - // Åñëè ýòî Lost_Soul, òî ñáðàñûâàåì àíèìàöèþ àòàêè: + begin // Состояние - Атака + // Если это Lost_Soul, то сбрасываем анимацию атаки: if FMonsterType = MONSTER_SOUL then FAnim[FCurAnim, FDirection].Reset(); case FMonsterType of MONSTER_SOUL, MONSTER_IMP, MONSTER_DEMON: - // Ãðûçåì ïåðâîãî ïîïàâøåãîñÿ: + // Грызем первого попавшегося: if g_Weapon_Hit(@FObj, 15, FUID, HIT_SOME) <> 0 then - // Lost_Soul óêóñèë êîãî-òî => ïåðåõîäèò íà øàã: + // Lost_Soul укусил кого-то => переходит на шаг: if FMonsterType = MONSTER_SOUL then SetState(MONSTATE_GO); MONSTER_FISH: - // Ðûáà êóñàåò ïåðâîãî ïîïàâøåãîñÿ ñî çâóêîì: + // Рыба кусает первого попавшегося со звуком: if g_Weapon_Hit(@FObj, 10, FUID, HIT_SOME) <> 0 then g_Sound_PlayExAt('SOUND_MONSTER_FISH_ATTACK', FObj.X, FObj.Y); MONSTER_SKEL, MONSTER_ROBO, MONSTER_CYBER: - // Ðîáîò, êèáåð èëè ñêåëåò ñèëüíî ïèíàþòñÿ: + // Робот, кибер или скелет сильно пинаются: 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; MONSTER_VILE: - // Êîëäóí ïûòàåòñÿ âîñêðåøàòü: + // Колдун пытается воскрешать: if FCurAnim = ANIM_ATTACK2 then begin sx := isCorpse(@FObj, True); if sx <> -1 then - begin // Íàøëè, êîãî âîñêðåñèòü + begin // Нашли, кого воскресить gMonsters[sx].SetState(MONSTATE_REVIVE); g_Sound_PlayExAt('SOUND_MONSTER_SLOP', FObj.X, FObj.Y); - // Âîñêðåøàòü - ñåáå âðåäèòü: + // Воскрешать - себе вредить: {g_Weapon_HitUID(FUID, 5, 0, HIT_SOME);} end; end; end; end - else // Ñîñòîÿíèå - Ñòðåëüáà + else // Состояние - Стрельба begin - // Âû÷èñëÿåì êîîðäèíàòû, îòêóäà âûëåòèò ïóëÿ: + // Вычисляем координаты, откуда вылетит пуля: 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; - end; // Ýòî çíà÷èò: wx := hitX + (hitWidth / 2) - (wx - (hitX + (hitWidth / 2))) + end; // Это значит: wx := hitX + (hitWidth / 2) - (wx - (hitX + (hitWidth / 2))) 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: g_Weapon_ball1(wx, wy, tx, ty, FUID); 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: @@ -3141,12 +2939,12 @@ _end: MONSTER_CACO: g_Weapon_ball2(wx, wy, tx, ty, FUID); MONSTER_PAIN: - begin // Ñîçäàåì Lost_Soul: + begin // Создаем Lost_Soul: mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+(FObj.Rect.Width div 2), FObj.Y+FObj.Rect.Y, FDirection); if mon <> nil then - begin // Öåëü - öåëü Pain_Elemental'à. Ëåòèì ê íåé: + begin // Цель - цель Pain_Elemental'а. Летим к ней: mon.FTargetUID := FTargetUID; GetPos(FTargetUID, @o); mon.FTargetTime := 0; @@ -3164,29 +2962,29 @@ _end: if g_Game_IsNet then MH_SEND_MonsterShot(FUID, wx, wy, tx, ty); - // Ñêîðîñòðåëüíûå ìîíñòðû: + // Скорострельные монстры: if (FMonsterType = MONSTER_CGUN) or (FMonsterType = MONSTER_SPIDER) or (FMonsterType = MONSTER_BSP) or (FMonsterType = MONSTER_MANCUB) or (FMonsterType = MONSTER_ROBO) then if not GetPos(FTargetUID, @o) then - // Öåëü ìåðòâà - èùåì íîâóþ: + // Цель мертва - ищем новую: findNewPrey() - else // Öåëü æèâà - ïðîäîëæàåì ñòðåëÿòü: + else // Цель жива - продолжаем стрелять: if shoot(@o, False) then FChainFire := True; end; - // Àòàêà òîëüêî 1 ðàç çà àíèìàöèþ àòàêè: + // Атака только 1 раз за анимацию атаки: FWaitAttackAnim := True; end; -// Ïîñëåäíèé êàäð òåêóùåé àíèìàöèè: +// Последний кадр текущей анимации: if FAnim[FCurAnim, FDirection].Counter = FAnim[FCurAnim, FDirection].Speed-1 then case FState of MONSTATE_GO, MONSTATE_RUN, MONSTATE_CLIMB, MONSTATE_RUNOUT: - // Çâóêè ïðè ïåðåäâèæåíèè: + // Звуки при передвижении: case FMonsterType of MONSTER_CYBER: if (FAnim[FCurAnim, FDirection].CurrentFrame = 0) or @@ -3211,7 +3009,7 @@ _end: not ((FState = MONSTATE_DEAD) or (FState = MONSTATE_DIE)) then FObj.Vel.X := oldvelx; -// Åñëè åñòü àíèìàöèÿ, òî ïóñòü îíà èäåò: +// Если есть анимация, то пусть она идет: if FAnim[FCurAnim, FDirection] <> nil then FAnim[FCurAnim, FDirection].Update(); end; @@ -3249,13 +3047,17 @@ begin 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 (FState <> MONSTATE_DIE) and (FState <> MONSTATE_DEAD) then fall := False; -// Ëåòàþùèå ìîíòñðû: +// Летающие монтсры: if ((FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_CACO)) and @@ -3263,7 +3065,7 @@ begin (FState <> MONSTATE_DEAD) then fall := False; -// Ìåíÿåì ñêîðîñòü òîëüêî ïî ÷åòíûì êàäðàì: +// Меняем скорость только по четным кадрам: if gTime mod (GAME_TICK*2) <> 0 then begin g_Obj_Move(@FObj, fall, True, True); @@ -3276,11 +3078,11 @@ begin else FPainSound := False; -// Äâèãàåìñÿ: +// Двигаемся: st := g_Obj_Move(@FObj, fall, True, True); positionChanged(); // this updates spatial accelerators -// Âûëåòåë çà êàðòó - óäàëÿåì è çàïóñêàåì òðèããåðû: +// Вылетел за карту - удаляем и запускаем триггеры: if WordBool(st and MOVE_FALLOUT) or (FObj.X < -1000) or (FObj.X > gMapInfo.Width+1000) or (FObj.Y < -1000) then begin @@ -3290,7 +3092,7 @@ begin oldvelx := FObj.Vel.X; -// Ñîïðîòèâëåíèå âîçäóõà äëÿ òðóïà: +// Сопротивление воздуха для трупа: if (FState = MONSTATE_DIE) or (FState = MONSTATE_DEAD) then FObj.Vel.X := z_dec(FObj.Vel.X, 1); @@ -3305,11 +3107,11 @@ begin end; end; -// Ìåðòâûé íè÷åãî íå äåëàåò: +// Мертвый ничего не делает: if (FState = MONSTATE_DEAD) then goto _end; -// Âîçìîæíî, ñîçäàåì ïóçûðüêè â âîäå: +// Возможно, создаем пузырьки в воде: if WordBool(st and MOVE_INWATER) and (Random(32) = 0) then case FMonsterType of MONSTER_FISH: @@ -3329,7 +3131,7 @@ begin end; end; -// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ: +// Если прошел первый кадр анимации взрыва бочки, то взрыв: if FMonsterType = MONSTER_BARREL then begin if (FState = MONSTATE_DIE) and (FAnim[FCurAnim, FDirection].CurrentFrame = 1) and @@ -3339,7 +3141,7 @@ begin 60, FUID); end; -// Lost_Soul âûëåòåë èç âîäû => óñêîðÿåòñÿ: +// Lost_Soul вылетел из воды => ускоряется: if FMonsterType = MONSTER_SOUL then if WordBool(st and MOVE_HITAIR) then g_Obj_SetSpeed(@FObj, 16); @@ -3347,12 +3149,12 @@ begin if FAmmo < 0 then FAmmo := FAmmo + 1; -// Åñëè íà÷àëè âñïëûâàòü, òî ïðîäîëæàåì: +// Если начали всплывать, то продолжаем: if FObj.Vel.Y < 0 then if WordBool(st and MOVE_INWATER) then FObj.Vel.Y := -4; -// Òàéìåð - æäåì ïîñëå ïîòåðè öåëè: +// Таймер - ждем после потери цели: FTargetTime := FTargetTime + 1; if FShellTimer > -1 then @@ -3374,7 +3176,7 @@ begin FShellTimer := -1; end else Dec(FShellTimer); -// Ïðîáóåì óâåðíóòüñÿ îò ëåòÿùåé ïóëè: +// Пробуем увернуться от летящей пули: if fall then if (FState in [MONSTATE_GO, MONSTATE_RUN, MONSTATE_RUNOUT, MONSTATE_ATTACK, MONSTATE_SHOOT]) then @@ -3385,9 +3187,9 @@ begin FObj.Vel.Y := -MONSTERTABLE[FMonsterType].Jump; case FState of - MONSTATE_PAIN: // Ñîñòîÿíèå - Áîëü + MONSTATE_PAIN: // Состояние - Боль begin - // Áîëü ñèëüíàÿ => ìîíñòð êðè÷èò: + // Боль сильная => монстр кричит: if FPain >= MONSTERTABLE[FMonsterType].Pain then begin FPain := MONSTERTABLE[FMonsterType].Pain; @@ -3396,10 +3198,10 @@ begin if (not gSoundEffectsDF) and (FPain >= MONSTERTABLE[FMonsterType].MinPain) then PainSound(); - // Ñíèæàåì áîëü ñî âðåìåíåì: + // Снижаем боль со временем: FPain := FPain - 5; - // Áîëü óæå íå îøóòèìàÿ => èäåì äàëüøå: + // Боль уже не ошутимая => идем дальше: if FPain <= MONSTERTABLE[FMonsterType].MinPain then begin SetState(MONSTATE_GO); @@ -3407,27 +3209,27 @@ begin end; end; - MONSTATE_SLEEP: // Ñîñòîÿíèå - Ñîí + MONSTATE_SLEEP: // Состояние - Сон begin - // Ñïèì: + // Спим: FSleep := FSleep + 1; - // Ïðîñïàëè äîñòàòî÷íî: + // Проспали достаточно: if FSleep >= 18 then FSleep := 0 - else // åùå ñïèì + else // еще спим goto _end; end; - MONSTATE_WAIT: // Ñîñòîÿíèå - Îæèäàíèå + MONSTATE_WAIT: // Состояние - Ожидание begin - // Æäåì: + // Ждем: FSleep := FSleep - 1; end; - MONSTATE_GO: // Ñîñòîÿíèå - Äâèæåíèå (ñ îñìîòðîì ñèòóàöèè) + MONSTATE_GO: // Состояние - Движение (с осмотром ситуации) begin - // Åñëè íàòêíóëèñü íà ÁëîêÌîí - óáåãàåì îò íåãî: + // Если наткнулись на БлокМон - убегаем от него: if WordBool(st and MOVE_BLOCK) then begin Turn(); @@ -3437,7 +3239,7 @@ begin goto _end; end; - // Åñëè ìîíñòð - êîëäóí, òî ïðîáóåì âîñêðåñèòü êîãî-íèáóäü: + // Если монстр - колдун, то пробуем воскресить кого-нибудь: if (FMonsterType = MONSTER_VILE) then if isCorpse(@FObj, False) <> -1 then begin @@ -3447,7 +3249,7 @@ begin goto _end; end; - // Åñëè öåëü ïî÷òè íà îäíîé âåðòèêàëè - áåãàåì òóäà-ñþäà: + // Если цель почти на одной вертикали - бегаем туда-сюда: if Abs(sx) < 40 then if FMonsterType <> MONSTER_FISH then begin @@ -3457,16 +3259,16 @@ begin goto _end; end; - // Óïåðëèñü â ñòåíó: + // Уперлись в стену: if WordBool(st and MOVE_HITWALL) then begin case FMonsterType of MONSTER_CACO, MONSTER_SOUL, MONSTER_PAIN, MONSTER_FISH: ; - else // Íå ëåòàþò: + else // Не летают: if (g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj)) and (FObj.Accel.Y = 0) then - begin // Ñòîèì íà òâåðäîì ïîëó èëè ñòóïåíè - // Ïðûæîê ÷åðåç ñòåíó: + begin // Стоим на твердом полу или ступени + // Прыжок через стену: FObj.Vel.Y := -MONSTERTABLE[FMonsterType].Jump; SetState(MONSTATE_CLIMB); end; @@ -3475,82 +3277,82 @@ begin goto _end; end; - // Ìîíñòðû, íå ïîäâåðæåííûå ãðàâèòàöèè: + // Монстры, не подверженные гравитации: if (FMonsterType = MONSTER_CACO) or (FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_FISH) then begin if FMonsterType = MONSTER_FISH then - begin // Ðûáà + begin // Рыба if not WordBool(st and MOVE_INWATER) then - begin // Ðûáà âíå âîäû: + begin // Рыба вне воды: if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then - begin // "Ñòîèò" òâåðäî - // Ðûáà òðåïûõàåòñÿ íà ïîâåðõíîñòè: + begin // "Стоит" твердо + // Рыба трепыхается на поверхности: if FObj.Accel.Y = 0 then FObj.Vel.Y := -6; FObj.Accel.X := FObj.Accel.X - 8 + Random(17); end; - // Ðûáå áîëüíî: + // Рыбе больно: SetState(MONSTATE_PAIN); FPain := FPain + 50; end - else // Ðûáà â âîäå + else // Рыба в воде begin - // Ïëûâåì â ñòîðîíó öåëè ïî-âåðòèêàëè: + // Плывем в сторону цели по-вертикали: if Abs(sy) > 8 then FObj.Vel.Y := g_basic.Sign(sy)*4 else FObj.Vel.Y := 0; - // Ðûáà ïëûâåò ââåðõ: + // Рыба плывет вверх: 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; - // Ïëàâàåì òóäà-ñþäà: + // Плаваем туда-сюда: SetState(MONSTATE_RUN); FSleep := 20; end; end; end - else // Ëåòàþùèå ìîíñòðû + else // Летающие монстры begin - // Ëåòèì â ñòîðîíó öåëè ïî-âåðòèêàëè: + // Летим в сторону цели по-вертикали: if Abs(sy) > 8 then FObj.Vel.Y := g_basic.Sign(sy)*4 else FObj.Vel.Y := 0; end; end - else // "Íàçåìíûå" ìîíñòðû + else // "Наземные" монстры begin - // Âîçìîæíî, ïèíàåì êóñêè: + // Возможно, пинаем куски: if (FObj.Vel.X <> 0) and (gGibs <> nil) then begin b := Abs(FObj.Vel.X); 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 - // Ïèíàåì êóñêè + // Пинаем куски if FObj.Vel.X < 0 then begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120); // íàëåâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120); // налево end else begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // направо end; positionChanged(); // this updates spatial accelerators end; end; end; - // Áîññû ìîãóò ïèíàòü òðóïû: + // Боссы могут пинать трупы: if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and (FObj.Vel.X <> 0) and (gCorpses <> nil) then begin @@ -3562,18 +3364,18 @@ begin co := gCorpses[a].Obj; if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4, 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; FSleep := FSleep + 1; - // Èíîãäà ðû÷èì: + // Иногда рычим: if FSleep >= 8 then begin FSleep := 0; @@ -3581,23 +3383,23 @@ begin ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_RUN: // Ñîñòîÿíèå - Áåã + MONSTATE_RUN: // Состояние - Бег begin - // Åñëè íàòêíóëèñü íà ÁëîêÌîí - óáåãàåì îò íåãî: + // Если наткнулись на БлокМон - убегаем от него: if WordBool(st and MOVE_BLOCK) then begin SetState(MONSTATE_RUNOUT); @@ -3608,74 +3410,74 @@ begin FSleep := FSleep - 1; - // Ïðîáåæàëè äîñòàòî÷íî èëè âðåçàëèñü â ñòåíó => ïåðåõîäèì íà øàã: + // Пробежали достаточно или врезались в стену => переходим на шаг: if (FSleep <= 0) or (WordBool(st and MOVE_HITWALL) and ((FObj.Vel.Y+FObj.Accel.Y) = 0)) then begin SetState(MONSTATE_GO); FSleep := 0; - // Èíîãäà ðû÷èì: + // Иногда рычим: if Random(8) = 0 then ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_RUNOUT: // Ñîñòîÿíèå - Óáåãàåò îò ÷åãî-òî + MONSTATE_RUNOUT: // Состояние - Убегает от чего-то begin - // Âûøëè èç ÁëîêÌîíà: + // Вышли из БлокМона: if (not WordBool(st and MOVE_BLOCK)) and (FSleep > 0) then FSleep := 0; FSleep := FSleep - 1; - // Óáàæåëè äîñòàòî÷íî äàëåêî => ïåðåõîäèì íà øàã: + // Убажели достаточно далеко => переходим на шаг: if FSleep <= -18 then begin SetState(MONSTATE_GO); FSleep := 0; - // Èíîãäà ðû÷èì: + // Иногда рычим: if Random(8) = 0 then ActionSound(); 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 WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_CLIMB: // Ñîñòîÿíèå - Ïðûæîê (÷òîáû îáîéòè ñòåíó) + MONSTATE_CLIMB: // Состояние - Прыжок (чтобы обойти стену) begin - // Äîñòèãëè âûñøåé òî÷êè ïðûæêà èëè ñòåíà êîí÷èëàñü => ïåðåõîäèì íà øàã: + // Достигли высшей точки прыжка или стена кончилась => переходим на шаг: if ((FObj.Vel.Y+FObj.Accel.Y) >= 0) or (not WordBool(st and MOVE_HITWALL)) then begin SetState(MONSTATE_GO); FSleep := 0; - // Ñòåíà íå êîí÷èëàñü => áåæèì îò íåå: + // Стена не кончилась => бежим от нее: if WordBool(st and (MOVE_HITWALL or MOVE_BLOCK)) then begin SetState(MONSTATE_RUN); @@ -3683,24 +3485,24 @@ begin 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; - // Åñëè â âîäå, òî çàìåäëÿåìñÿ: + // Если в воде, то замедляемся: if WordBool(st and MOVE_INWATER) then FObj.Vel.X := FObj.Vel.X div 2 - else // Ðûáàì íå íóæíî çàìåäëÿòüñÿ + else // Рыбам не нужно замедляться if FMonsterType = MONSTER_FISH then FObj.Vel.X := 0; end; - MONSTATE_ATTACK, // Ñîñòîÿíèå - Àòàêà - MONSTATE_SHOOT: // Ñîñòîÿíèå - Ñòðåëüáà + MONSTATE_ATTACK, // Состояние - Атака + MONSTATE_SHOOT: // Состояние - Стрельба begin - // Lost_Soul âðåçàëñÿ â ñòåíó ïðè àòàêå => ïåðåõîäèò íà øàã: + // Lost_Soul врезался в стену при атаке => переходит на шаг: if FMonsterType = MONSTER_SOUL then begin if WordBool(st and (MOVE_HITWALL or MOVE_HITCEIL or MOVE_HITLAND)) then @@ -3709,14 +3511,14 @@ begin goto _end; end; - // Çàìåäëÿåìñÿ ïðè àòàêå: + // Замедляемся при атаке: if FMonsterType <> MONSTER_FISH then FObj.Vel.X := z_dec(FObj.Vel.X, 1); - // Íóæíî ñòðåëÿòü, à ìîíñòð - êîëäóí: + // Нужно стрелять, а монстр - колдун: if (FMonsterType = MONSTER_VILE) and (FState = MONSTATE_SHOOT) then begin - // Öåëü ïîãèáëà => èäåì äàëüøå: + // Цель погибла => идем дальше: if not GetPos(FTargetUID, @o) then begin SetState(MONSTATE_GO); @@ -3724,7 +3526,7 @@ begin goto _end; end; - // Öåëü íå âèäíî => èäåì äàëüøå: + // Цель не видно => идем дальше: if not g_Look(@FObj, @o, FDirection) then begin SetState(MONSTATE_GO); @@ -3732,7 +3534,7 @@ begin goto _end; end; - // Öåëü â âîäå - íå çàãîðèòñÿ => èäåì äàëüøå: + // Цель в воде - не загорится => идем дальше: if g_Obj_CollideWater(@o, 0, 0) then begin SetState(MONSTATE_GO); @@ -3745,27 +3547,27 @@ begin _end: -// Ñîñòîÿíèå - Âîñêðåøåíèå: +// Состояние - Воскрешение: if FState = MONSTATE_REVIVE then if FAnim[FCurAnim, FDirection].Played then - begin // Îáðàòíàÿ àíèìàöèÿ óìèðàíèÿ çàêîí÷èëàñü - èäåì äàëüøå: + begin // Обратная анимация умирания закончилась - идем дальше: FAnim[FCurAnim, FDirection].Revert(False); SetState(MONSTATE_GO); end; -// Åñëè åñòü àíèìàöèÿ îãíÿ êîëäóíà - ïóñòü îíà èäåò: +// Если есть анимация огня колдуна - пусть она идет: if vilefire <> nil then vilefire.Update(); -// Ñîñòîÿíèå - Óìèðàåò è òåêóùàÿ àíèìàöèÿ ïðîèãðàíà: +// Состояние - Умирает и текущая анимация проиграна: if (FState = MONSTATE_DIE) and (FAnim[FCurAnim, FDirection] <> nil) and (FAnim[FCurAnim, FDirection].Played) then begin - // Óìåð: + // Умер: SetState(MONSTATE_DEAD); - // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ: + // У этих монстров нет трупов: if (FMonsterType = MONSTER_PAIN) or (FMonsterType = MONSTER_SOUL) or (FMonsterType = MONSTER_BARREL) then @@ -3774,28 +3576,28 @@ _end: FAnim[FCurAnim, FDirection].CurrentFrame := FAnim[FCurAnim, FDirection].TotalFrames - 1; end; -// Ñîâåðøåíèå àòàêè è ñòðåëüáû: +// Совершение атаки и стрельбы: if (FState = MONSTATE_ATTACK) or (FState = MONSTATE_SHOOT) then if (FAnim[FCurAnim, FDirection] <> nil) then - // Àíèìàöèÿ àòàêè åñòü - ìîæíî àòàêîâàòü + // Анимация атаки есть - можно атаковать if (FAnim[FCurAnim, FDirection].Played) then - begin // Àíèìàöèÿ àòàêè çàêîí÷èëàñü => ïåðåõîäèì íà øàã + begin // Анимация атаки закончилась => переходим на шаг if FState = MONSTATE_ATTACK then - begin // Ñîñòîÿíèå - Àòàêà - // Åñëè ìîíñòð íå Lost_Soul, òî ïîñëå àòàêè ïåðåõîäèì íà øàã: + begin // Состояние - Атака + // Если монстр не Lost_Soul, то после атаки переходим на шаг: if FMonsterType <> MONSTER_SOUL then SetState(MONSTATE_GO); end - else // Ñîñòîÿíèå - Ñòðåëüáà + else // Состояние - Стрельба begin - // Ïåðåõîäèì íà øàã, åñëè íå íàäî ñòðåëÿòü åùå ðàç: + // Переходим на шаг, если не надо стрелять еще раз: if not FChainFire then SetState(MONSTATE_GO) else - begin // Íàäî ñòðåëÿòü åùå + begin // Надо стрелять еще FChainFire := False; - // Ò.ê. ñîñòîÿíèå íå èçìåíèëîñü, è íóæåí - // íîâûé öèêë îæèäàíèÿ çàâåðøåíèÿ àíèìàöèè: + // Т.к. состояние не изменилось, и нужен + // новый цикл ожидания завершения анимации: FAnim[FCurAnim, FDirection].Reset(); end; end; @@ -3803,24 +3605,24 @@ _end: FWaitAttackAnim := False; end - else // Àíèìàöèÿ àòàêè åùå èäåò (èñêëþ÷åíèå - Lost_Soul): + else // Анимация атаки еще идет (исключение - Lost_Soul): if (FMonsterType = MONSTER_SOUL) or ( (not FWaitAttackAnim) and (FAnim[FCurAnim, FDirection].CurrentFrame = (FAnim[FCurAnim, FDirection].TotalFrames div 2)) ) then - begin // Àòàêè åùå íå áûëî è ýòî ñåðåäèíà àíèìàöèè àòàêè + begin // Атаки еще не было и это середина анимации атаки if FState = MONSTATE_ATTACK then - begin // Ñîñòîÿíèå - Àòàêà - // Åñëè ýòî Lost_Soul, òî ñáðàñûâàåì àíèìàöèþ àòàêè: + begin // Состояние - Атака + // Если это Lost_Soul, то сбрасываем анимацию атаки: if FMonsterType = MONSTER_SOUL then FAnim[FCurAnim, FDirection].Reset(); case FMonsterType of MONSTER_SOUL, MONSTER_IMP, MONSTER_DEMON: - // Ãðûçåì ïåðâîãî ïîïàâøåãîñÿ: + // Грызем первого попавшегося: if g_Weapon_Hit(@FObj, 15, FUID, HIT_SOME) <> 0 then - // Lost_Soul óêóñèë êîãî-òî => ïåðåõîäèò íà øàã: + // Lost_Soul укусил кого-то => переходит на шаг: if FMonsterType = MONSTER_SOUL then SetState(MONSTATE_GO); @@ -3828,54 +3630,54 @@ _end: g_Weapon_Hit(@FObj, 10, FUID, HIT_SOME); MONSTER_SKEL, MONSTER_ROBO, MONSTER_CYBER: - // Ðîáîò, êèáåð èëè ñêåëåò ñèëüíî ïèíàþòñÿ: + // Робот, кибер или скелет сильно пинаются: 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; MONSTER_VILE: - // Êîëäóí ïûòàåòñÿ âîñêðåøàòü: + // Колдун пытается воскрешать: if FCurAnim = ANIM_ATTACK2 then begin sx := isCorpse(@FObj, True); if sx <> -1 then - begin // Íàøëè, êîãî âîñêðåñèòü + begin // Нашли, кого воскресить g_Sound_PlayExAt('SOUND_MONSTER_SLOP', FObj.X, FObj.Y); - // Âîñêðåøàòü - ñåáå âðåäèòü: + // Воскрешать - себе вредить: {g_Weapon_HitUID(FUID, 5, 0, HIT_SOME);} end; end; end; end - else // Ñîñòîÿíèå - Ñòðåëüáà + else // Состояние - Стрельба begin - // Ñêîðîñòðåëüíûå ìîíñòðû: + // Скорострельные монстры: if (FMonsterType = MONSTER_CGUN) or (FMonsterType = MONSTER_SPIDER) or (FMonsterType = MONSTER_BSP) or (FMonsterType = MONSTER_MANCUB) or (FMonsterType = MONSTER_ROBO) then if not GetPos(FTargetUID, @o) then - // Öåëü ìåðòâà - èùåì íîâóþ: + // Цель мертва - ищем новую: findNewPrey() - else // Öåëü æèâà - ïðîäîëæàåì ñòðåëÿòü: + else // Цель жива - продолжаем стрелять: if shoot(@o, False) then FChainFire := True; end; - // Àòàêà òîëüêî 1 ðàç çà àíèìàöèþ àòàêè: + // Атака только 1 раз за анимацию атаки: FWaitAttackAnim := True; end; -// Ïîñëåäíèé êàäð òåêóùåé àíèìàöèè: +// Последний кадр текущей анимации: if FAnim[FCurAnim, FDirection].Counter = FAnim[FCurAnim, FDirection].Speed-1 then case FState of MONSTATE_GO, MONSTATE_RUN, MONSTATE_CLIMB, MONSTATE_RUNOUT: - // Çâóêè ïðè ïåðåäâèæåíèè: + // Звуки при передвижении: case FMonsterType of MONSTER_CYBER: if (FAnim[FCurAnim, FDirection].CurrentFrame = 0) or @@ -3896,12 +3698,12 @@ _end: end; end; -// Êîñòûëü äëÿ ïîòîêîâ +// Костыль для потоков if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_LIFTLEFT or PANEL_LIFTRIGHT) and not ((FState = MONSTATE_DEAD) or (FState = MONSTATE_DIE)) then FObj.Vel.X := oldvelx; -// Åñëè åñòü àíèìàöèÿ, òî ïóñòü îíà èäåò: +// Если есть анимация, то пусть она идет: if FAnim[FCurAnim, FDirection] <> nil then FAnim[FCurAnim, FDirection].Update(); end; @@ -3952,14 +3754,11 @@ 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; @@ -3981,11 +3780,11 @@ begin PlayerNear := -1; MonsterNear := -1; - // Ïîåõàâøèå, êàííèáàëû, è äîáðûå èãðîêîâ íå òðîãàþò + // Поехавшие, каннибалы, и добрые игроков не трогают 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 @@ -4002,20 +3801,20 @@ begin end; end; - // Êèëëåðû è äîáðûå íå òðîãàþò ìîíñòðîâ + // Киллеры и добрые не трогают монстров 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 - Continue; // Êàííèáàëû àòàêóþò òîëüêî ñîðîäè÷åé + Continue; // Каннибалы атакуют только сородичей if (FBehaviour = BH_INSANE) and (gMonsters[a].FMonsterType <> MONSTER_BARREL) and (IsFriend(gMonsters[a].FMonsterType, FMonsterType)) then - Continue; // Ïîåõàâøèå íå òðîãàþò äðóçåé, íî èì íå íðàâÿòñÿ áî÷êè + Continue; // Поехавшие не трогают друзей, но им не нравятся бочки if ((FBehaviour = BH_NORMAL) or (FBehaviour = BH_MANIAC)) and (IsFriend(gMonsters[a].FMonsterType, FMonsterType)) then - Continue; // Îñòàâøèåñÿ òèïû, êðîìå êàííèáàëîâ, íå òðîãàþò ñâîèõ äðóçåé + Continue; // Оставшиеся типы, кроме каннибалов, не трогают своих друзей if g_Look(@FObj, @gMonsters[a].Obj, FDirection) then begin @@ -4034,25 +3833,25 @@ begin case FBehaviour of BH_NORMAL, BH_KILLER: begin - // Îáû÷íûé è êèëëåð ñíà÷àëà èùóò èãðîêîâ â ïîëå çðåíèÿ + // Обычный и киллер сначала ищут игроков в поле зрения if (FTargetUID = 0) and (Length(PlayersSee) > 0) then begin a := PlayersSee[Random(Length(PlayersSee))]; FTargetUID := gPlayers[a].UID; end; - // Çàòåì ïîáëèçîñòè + // Затем поблизости if (FTargetUID = 0) and (PlayerNear > -1) then begin a := PlayerNear; FTargetUID := gPlayers[a].UID; end; - // Ïîòîì îáû÷íûå èùóò ìîíñòðîâ â ïîëå çðåíèÿ + // Потом обычные ищут монстров в поле зрения if (FTargetUID = 0) and (Length(MonstersSee) > 0) then begin a := MonstersSee[Random(Length(MonstersSee))]; FTargetUID := gMonsters[a].UID; end; - // Çàòåì ïîáëèçîñòè + // Затем поблизости if (FTargetUID = 0) and (MonsterNear > -1) then begin a := MonsterNear; @@ -4061,7 +3860,7 @@ begin end; BH_MANIAC, BH_INSANE, BH_CANNIBAL: begin - // Ìàíüÿêè, ïîåõàâøèå è êàííèáàëû ñíà÷àëà èñòðåáëÿþò âñ¸ â ïîëå çðåíèÿ + // Маньяки, поехавшие и каннибалы сначала истребляют всё в поле зрения if (FTargetUID = 0) and (Length(PlayersSee) > 0) then begin a := PlayersSee[Random(Length(PlayersSee))]; @@ -4072,7 +3871,7 @@ begin a := MonstersSee[Random(Length(MonstersSee))]; FTargetUID := gMonsters[a].UID; end; - // Çàòåì èùóò êîãî-òî ïîáëèçîñòè + // Затем ищут кого-то поблизости if (FTargetUID = 0) and (PlayerNear > -1) then begin a := PlayerNear; @@ -4086,17 +3885,17 @@ begin end; end; -// Åñëè è ìîíñòðîâ íåò - íà÷èíàåì æäàòü öåëü: +// Если и монстров нет - начинаем ждать цель: if FTargetUID = 0 then begin - // Ïîåõàâøèé ïûòàåòñÿ ñàìîóáèòüñÿ + // Поехавший пытается самоубиться if FBehaviour = BH_INSANE then FTargetUID := FUID else FTargetTime := MAX_ATM; end else - begin // Öåëü íàøëè + begin // Цель нашли FTargetTime := 0; Result := True; end; @@ -4132,7 +3931,7 @@ begin end; MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO, MONSTER_MANCUB: - // Ýòè ìîíñòðû íå ïèíàþò - îíè ñòðåëÿþò â óïîð: + // Эти монстры не пинают - они стреляют в упор: if not g_Game_IsClient then Result := shoot(o, True); end; end; @@ -4143,19 +3942,19 @@ var begin Result := False; -// Ñòðåëÿòü ðàíî: +// Стрелять рано: if FAmmo < 0 then Exit; -// Æäàòü âðåìåíè ãîòîâíîñòè ê âûñòðåëó: +// Ждать времени готовности к выстрелу: if not immediately then case FMonsterType of MONSTER_FISH, MONSTER_BARREL, MONSTER_DEMON: - Exit; // íå ñòðåëÿþò + Exit; // не стреляют MONSTER_CGUN, MONSTER_BSP, MONSTER_ROBO: begin FAmmo := FAmmo + 1; - // Âðåìÿ âûñòðåëà óïóùåíî: + // Время выстрела упущено: if FAmmo >= 50 then FAmmo := IfThen(FMonsterType = MONSTER_ROBO, -200, -50); end; @@ -4163,24 +3962,24 @@ begin MONSTER_MANCUB: begin FAmmo := FAmmo + 1; - // Âðåìÿ âûñòðåëà óïóùåíî: + // Время выстрела упущено: if FAmmo >= 5 then FAmmo := -50; end; MONSTER_SPIDER: begin FAmmo := FAmmo + 1; - // Âðåìÿ âûñòðåëà óïóùåíî: + // Время выстрела упущено: if FAmmo >= 100 then FAmmo := -50; end; MONSTER_CYBER: begin - // Ñòðåëÿåò íå âñåãäà: + // Стреляет не всегда: if Random(2) = 0 then Exit; FAmmo := FAmmo + 1; - // Âðåìÿ âûñòðåëà óïóùåíî: + // Время выстрела упущено: if FAmmo >= 10 then FAmmo := -50; end; @@ -4191,7 +3990,7 @@ begin else if Random(16) <> 0 then Exit; end; -// Öåëè íå âèäíî: +// Цели не видно: if not g_Look(@FObj, o, FDirection) then Exit; @@ -4200,8 +3999,8 @@ begin tx := o^.X+o^.Rect.X+(o^.Rect.Width div 2)+((o^.Vel.X{+o^.Accel.X})*12); ty := o^.Y+o^.Rect.Y+(o^.Rect.Height div 2)+((o^.Vel.Y{+o^.Accel.Y})*12); -// Ðàçíèöà ïî âûñîòå áîëüøå ðàçíèöû ïî ãîðèçîíòàëè -// (íå ìîæåò ñòðåëÿòü ïîä òàêèì áîëüøèì óãëîì): +// Разница по высоте больше разницы по горизонтали +// (не может стрелять под таким большим углом): if Abs(tx-(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2))) < Abs(ty-(FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2))) then Exit; @@ -4218,7 +4017,7 @@ begin {nn} end; MONSTER_VILE: - begin // Çàæèãàåì îãîíü + begin // Зажигаем огонь tx := o^.X+o^.Rect.X+(o^.Rect.Width div 2); ty := o^.Y+o^.Rect.Y; SetState(MONSTATE_SHOOT); @@ -4229,7 +4028,7 @@ begin g_Sound_PlayExAt('SOUND_FIRE', o^.X, o^.Y); end; MONSTER_SOUL: - begin // Ëåòèò â ñòîðîíó öåëè: + begin // Летит в сторону цели: SetState(MONSTATE_ATTACK); g_Sound_PlayExAt('SOUND_MONSTER_SOUL_ATTACK', FObj.X, FObj.Y); @@ -4245,7 +4044,7 @@ begin MONSTER_MANCUB, MONSTER_ZOMBY, MONSTER_SERG, MONSTER_BSP, MONSTER_ROBO, MONSTER_CYBER, MONSTER_CGUN, MONSTER_SPIDER, MONSTER_PAIN, MONSTER_MAN: begin - // Ìàíêóáóñ ðÿâêàåò ïåðåä ïåðâîé àòàêîé: + // Манкубус рявкает перед первой атакой: if FMonsterType = MONSTER_MANCUB then if FAmmo = 1 then g_Sound_PlayExAt('SOUND_MONSTER_MANCUB_ATTACK', FObj.X, FObj.Y); @@ -4258,7 +4057,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; @@ -4281,187 +4080,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; + // Сигнатура монстра: + 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 ìîíñòðà: - Mem.ReadWord(FUID); + // 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; -// Íàïðàâëåíèå: - 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); -// Åñëè åñòü - çàãðóæàåì: + // Направление + 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; @@ -4487,31 +4269,31 @@ 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 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; procedure TMonster.OnFireFlame(Times: DWORD = 1); -var - id, i: DWORD; - Anim: TAnimation; + var i: DWORD; x, y: Integer; begin if (Random(10) = 1) and (Times = 1) then Exit; - if g_Frames_Get(id, 'FRAMES_FLAME') then + for i := 1 to Times do begin - for i := 1 to Times do - begin - Anim := TAnimation.Create(id, False, 3); - Anim.Alpha := 0; - g_GFX_OnceAnim(Obj.X+Obj.Rect.X+Random(Obj.Rect.Width+Times*2)-(Anim.Width div 2), - Obj.Y+8+Random(8+Times*2)+IfThen(FState = MONSTATE_DEAD, 16, 0), Anim, ONCEANIM_SMOKE); - Anim.Free(); - end; + x := Obj.X + Obj.Rect.X + Random(Obj.Rect.Width + Times * 2) - (R_GFX_FLAME_WIDTH div 2); + y := Obj.Y + 8 + Random(8 + Times * 2) + IfThen(FState = MONSTATE_DEAD, 16, 0); + r_GFX_OnceAnim(R_GFX_FLAME, x, y); end; end; @@ -4566,7 +4348,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; @@ -4576,28 +4358,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 @@ -4611,28 +4398,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 @@ -4646,22 +4438,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); @@ -4670,13 +4465,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