index a19fa4b568e5dbab3163ff1eb8d256b8c5234d82..875db7f0808e43f8f55293a62797790bad5704ca 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
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 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 ();
Exit; // Ýòè íå áüþò ñâîèõ
end;
-// Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
- if (a = MONSTER_SOUL) and (b = MONSTER_PAIN) then
- Exit;
-// Pain_Elemental íå ìîæåò ðàíèòü Lost_Soul'à:
- if (b = MONSTER_SOUL) and (a = MONSTER_PAIN) then
+// Pain_Elemental è Lost_Soul íå âîþþò äðóã ñ äðóãîì:
+ if [a, b] = [MONSTER_PAIN, MONSTER_SOUL] then
Exit;
//  îñòàëüíûõ ñëó÷àÿõ - áóäóò áèòü äðóã äðóãà:
if MonsterType = MONSTER_SOUL then
begin
if soulcount > MAX_SOUL then exit;
- soulcount := soulcount + 1;
+ soulcount += 1;
end;
find_id := allocMonster();
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;
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
MONSTATE_ATTACK: Anim := ANIM_ATTACK;
MONSTATE_DIE: Anim := ANIM_DIE;
MONSTATE_REVIVE:
- begin // íà÷àëè âîñðåøàòüñÿ
+ begin // íà÷àëè âîñêðåøàòüñÿ
Anim := FCurAnim;
FAnim[Anim, FDirection].Revert(True);
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;
+ a, b, sx, sy, wx, wy, oldvelx, i: Integer;
st: Word;
o, co: TObj;
fall: Boolean;
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
case FMonsterType of
MONSTER_FISH:
if Random(4) = 0 then
- g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
- FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
+ g_Game_Effect_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
- 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;
+ g_Game_Effect_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
+ FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
+ else
+ g_Game_Effect_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width-4),
+ FObj.Y+FObj.Rect.Y + Random(4), 5, 4, 4);
end;
// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ:
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 vilefire <> nil then
vilefire.Update();
-// Ñîñòîÿíèå - Óìèðàåò è òåêóùàÿ àíèìàöèÿ ïðîèãðàíà:
- if (FState = MONSTATE_DIE) and
- (FAnim[FCurAnim, FDirection] <> nil) and
- (FAnim[FCurAnim, FDirection].Played) then
- begin
- // Óìåð:
+ // Ñîñòîÿíèå - Óìèðàåò è òåêóùàÿ àíèìàöèÿ ïðîèãðàíà:
+ if (FState = MONSTATE_DIE) and (FAnim[FCurAnim, FDirection] <> nil) then
+ begin
+ if FAnim[FCurAnim, FDirection].Played then
+ begin // Óìåð:
SetState(MONSTATE_DEAD);
+ if g_Game_IsNet then MH_SEND_CoopStats();
- // Pain_Elemental ïðè ñìåðòè âûïóñêàåò 3 Lost_Soul'à:
- if (FMonsterType = MONSTER_PAIN) then
+ // Ó ýòèõ ìîíñòðîâ íåò òðóïîâ:
+ if FMonsterType in [MONSTER_PAIN, MONSTER_SOUL, MONSTER_BARREL] then
+ FRemoved := True;
+ end
+ else if (FMonsterType = MONSTER_PAIN) and
+ (FAnim[FCurAnim, FDirection].CurrentFrame =
+ FAnim[FCurAnim, FDirection].TotalFrames div 2 + 1) and
+ (FAnim[FCurAnim, FDirection].Counter = 0) then
+ for i := 0 to 2 do // Pain_Elemental ïðè ñìåðòè âûïóñêàåò 3 Lost_Soul'à ïîñåðåäèíå àíèìàöèè:
begin
- mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-30,
- FObj.Y+FObj.Rect.Y+20, TDirection.D_LEFT);
+ // FIXME: lostsouls may stuck in walls here
+ mon := g_Monsters_Create(MONSTER_SOUL,
+ FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)+RandomRange(-15,16),
+ FObj.Y+FObj.Rect.Y+RandomRange(-7,8), FDirection);
if mon <> nil then
begin
mon.SetState(MONSTATE_GO);
mon.FNoRespawn := True;
- Inc(gTotalMonsters);
+ gTotalMonsters += 1;
if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
end;
-
- mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2),
- FObj.Y+FObj.Rect.Y+20, TDirection.D_RIGHT);
- if mon <> nil then
- begin
- mon.SetState(MONSTATE_GO);
- mon.FNoRespawn := True;
- Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
- end;
-
- mon := g_Monsters_Create(MONSTER_SOUL, FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2)-15,
- FObj.Y+FObj.Rect.Y, TDirection.D_RIGHT);
- if mon <> nil then
- begin
- mon.SetState(MONSTATE_GO);
- mon.FNoRespawn := True;
- Inc(gTotalMonsters);
- if g_Game_IsNet then MH_SEND_MonsterSpawn(mon.UID);
- 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;
+ end;
// Ñîâåðøåíèå àòàêè è ñòðåëüáû:
if (FState = MONSTATE_ATTACK) or (FState = MONSTATE_SHOOT) then
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
case FMonsterType of
MONSTER_FISH:
if Random(4) = 0 then
- g_GFX_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
- FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
+ g_Game_Effect_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
- 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;
+ g_Game_Effect_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width),
+ FObj.Y+FObj.Rect.Y + Random(4), 1, 0, 0);
+ else
+ g_Game_Effect_Bubbles(FObj.X+FObj.Rect.X + Random(FObj.Rect.Width-4),
+ FObj.Y+FObj.Rect.Y + Random(4), 5, 4, 4);
end;
// Åñëè ïðîøåë ïåðâûé êàäð àíèìàöèè âçðûâà áî÷êè, òî âçðûâ:
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;
function TMonster.alive(): Boolean;
begin
- Result := (FState <> MONSTATE_DIE) and (FState <> MONSTATE_DEAD) and (FHealth > 0);
+ Result := (FHealth > 0) and not (FState in [MONSTATE_DIE, MONSTATE_DEAD]);
end;
procedure TMonster.SetHealth(aH: Integer);
FAnim[i, TDirection.D_RIGHT].LoadState(st);
end;
end;
+ // update cache
+ self.positionChanged
end;
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;