index e496521d4485a3ada649639ff4b7ebec96ae0bcf..d8adad853402974c802ba9bfbd3f58761809835e 100644 (file)
--- a/src/game/g_monsters.pas
+++ b/src/game/g_monsters.pas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
MONSTATE_REVIVE = 10;
MONSTATE_RUNOUT = 11;
+ MON_BURN_TIME = 100;
+
{ in mapdef now
BH_NORMAL = 0;
BH_KILLER = 1;
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);
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 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 ();
FStartDirection := Direction;
FStartX := GameX;
FStartY := GameY;
+ FObj.oldX := FObj.X;
+ FObj.oldY := FObj.Y;
end;
mon.positionChanged();
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
Result := False;
+// Ìîíñòð ñòàòè÷åí ïîêà èäåò warmup
+ if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
+
// Óìèðàåò, óìåð èëè âîñêðåøàåòñÿ => óðîí äåëàòü íåêîìó:
if (FState = MONSTATE_DEAD) or (FState = MONSTATE_DIE) or (FState = MONSTATE_REVIVE) then
Exit;
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;
procedure TMonster.Draw();
var
m: TMirrorType;
- dx, dy, c: Integer;
+ dx, dy, c, fX, fY: Integer;
o: TObj;
begin
//e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, 'TYPE: '+IntToStr(FMonsterType));
//e_CharFont_Print(gMenuSmallFont, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y+16, 'STATE: '+IntToStr(FState));
+ FObj.lerp(gLerpFactor, fX, fY);
+
// Åñëè êîëäóí ñòðåëÿåò, òî ðèñóåì îãîíü:
if FMonsterType = MONSTER_VILE then
if FState = MONSTATE_SHOOT then
end;
// Ðèñóåì:
- FAnim[FCurAnim, FDirection].Draw(Obj.X+dx, Obj.Y+dy, m);
+ FAnim[FCurAnim, FDirection].Draw(fX+dx, fY+dy, m);
end;
if g_debug_Frames then
FObj.X := X - FObj.Rect.X;
FObj.Y := Y - FObj.Rect.Y;
+ FObj.oldX := FObj.X; // don't interpolate after teleport
+ FObj.oldY := FObj.Y;
positionChanged();
if dir = 1 then
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;
st: Word;
o, co: TObj;
- fall: Boolean;
+ fall, bubbles: Boolean;
mon: TMonster;
+ mit: PMonster;
+ it: TMonsterGrid.Iter;
label
_end;
begin
fall := True;
+ bubbles := True;
+
+// Ìîíñòð ñòàòè÷åí ïîêà èäåò warmup
+ if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
// Ðûáû "ëåòàþò" òîëüêî â âîäå:
if FMonsterType = MONSTER_FISH then
st := g_Obj_Move(@FObj, fall, True, True);
positionChanged(); // this updates spatial accelerators
+// Åñëè ãîðèì - ïîäæèãàåì äðóãèõ ìîíñòðîâ, íî íå íà 100 òèêîâ êàæäûé ðàç:
+ if FFireTime > 0 then
+ begin
+ it := monsGrid.forEachInAABB(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height);
+ for mit in it do
+ if mit.UID <> FUID then
+ mit.CatchFire(FFireAttacker, FFireTime);
+ end;
+
// Âûëåòåë çà êàðòó - óäàëÿåì è çàïóñêàåì òðèããåðû:
if WordBool(st and MOVE_FALLOUT) or (FObj.X < -1000) or
(FObj.X > gMapInfo.Width+1000) or (FObj.Y < -1000) then
// Âîçìîæíî, ñîçäàåì ïóçûðüêè â âîäå:
if WordBool(st and MOVE_INWATER) and (Random(32) = 0) then
+ begin
case FMonsterType of
MONSTER_FISH:
- if Random(4) = 0 then
+ if Random(4) <> 0 then bubbles := False else
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
MONSTER_ROBO, MONSTER_BARREL:
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
- else begin
+ else
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width-4),
FObj.Y+FObj.Rect.Y + Random(4), 5, 4, 4);
- if Random(2) = 0 then
- g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj.X, FObj.Y)
- else
- g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj.X, FObj.Y);
- end;
end;
+ if bubbles then if Random(2) = 0
+ then g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj.X, FObj.Y)
+ else g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj.X, FObj.Y);
+ end;
// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ:
if FMonsterType = MONSTER_BARREL then
// Ðûáà ïëûâåò ââåðõ:
if FObj.Vel.Y < 0 then
- if not g_Obj_CollideWater(@FObj, 0, -16) then
+ if not g_Obj_CollideLiquid(@FObj, 0, -16) then
begin
// Âñïëûëè äî ïîâåðõíîñòè - ñòîï:
FObj.Vel.Y := 0;
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;
// Åñëè öåëü âûñîêî, òî, âîçìîæíî, ïðûãàåì:
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:
a, b, sx, sy, oldvelx: Integer;
st: Word;
o, co: TObj;
- fall: Boolean;
+ fall, bubbles: Boolean;
label
_end;
begin
sx := 0; // SHUT UP COMPILER
sy := 0;
fall := True;
+ bubbles := 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 WordBool(st and MOVE_INWATER) and (Random(32) = 0) then
+ begin
case FMonsterType of
MONSTER_FISH:
- if Random(4) = 0 then
+ if Random(4) <> 0 then bubbles := False else
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
MONSTER_ROBO, MONSTER_BARREL:
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
- else begin
+ else
g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width-4),
FObj.Y+FObj.Rect.Y + Random(4), 5, 4, 4);
- if Random(2) = 0 then
- g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj.X, FObj.Y)
- else
- g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj.X, FObj.Y);
- end;
end;
+ if bubbles then if Random(2) = 0
+ then g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj.X, FObj.Y)
+ else g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj.X, FObj.Y);
+ end;
// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ:
if FMonsterType = MONSTER_BARREL then
// Ðûáà ïëûâåò ââåðõ:
if FObj.Vel.Y < 0 then
- if not g_Obj_CollideWater(@FObj, 0, -16) then
+ if not g_Obj_CollideLiquid(@FObj, 0, -16) then
begin
// Âñïëûëè äî ïîâåðõíîñòè - ñòîï:
FObj.Vel.Y := 0;
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;
FAnim[i, TDirection.D_RIGHT].LoadState(st);
end;
end;
+ // update cache
+ self.positionChanged
end;
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;