ScummVM API documentation
combat.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef HARVESTER_COMBAT_H
23 #define HARVESTER_COMBAT_H
24 
25 #include "common/error.h"
26 #include "harvester/scene/room_support.h"
27 
28 namespace Harvester {
29 
30 class CheatSystem;
31 class Entity;
32 class EntityManager;
33 class HarvesterEngine;
34 class CombatEffects;
35 class InteractionCallbacks;
36 class Interaction;
37 class SceneEntities;
38 class Player;
39 
40 extern const int kRoomMonsterAnimationRate;
41 extern const int kNativeMonsterAttackAnimationRate;
42 extern const int kNativeMonsterHitAnimationRate;
43 extern const uint32 kNativeMonsterAttackCooldownBaseTicks;
44 extern const float kNativeNpcMonsterZExtent;
45 extern const int kRoomNpcAmbientLastFrame;
46 extern const float kNativeMonsterPursuitZTolerance;
47 extern const float kNativeMonsterHorizontalWaypointTolerance;
48 extern const uint32 kNativeKeyboardAttackRepeatTicks;
49 extern const int kNativeAttackSideWindow;
50 extern const int kNativeCombatHitKnockbackDecayStep;
51 extern const char *const kNativeHitEffectResourcePath;
52 extern const int kNativeHitEffectAnimationRate;
53 extern const float kNativeHitEffectRenderZBias;
54 extern const uint32 kCombatDebugDamagePopupDurationMs;
55 extern const int kCombatDebugDamagePopupRisePixels;
56 extern const int kCombatDebugDamagePopupVerticalGap;
57 extern const char *const kInnerSanctumRoomName;
58 extern const char *const kHerrillLogNpcName;
59 extern const char *const kMuckeyMonsterName;
60 extern const char *const kActivateInnerSanctumDoorActionTag;
61 extern const int kNativeDefaultNpcDeathDamageType;
62 
65  constexpr AttackAnimationRange(int firstFrame, int lastFrame, int resumeFacing)
66  : firstFrame(firstFrame), lastFrame(lastFrame), resumeFacing(resumeFacing) {}
67 
68  int firstFrame = 0;
69  int lastFrame = 0;
70  int resumeFacing = 0;
71 };
72 
75  DeathAnimationRange(int firstFrame, int lastFrame)
76  : firstFrame(firstFrame), lastFrame(lastFrame) {}
77 
78  int firstFrame = -1;
79  int lastFrame = -1;
80 };
81 
84  HitAnimationRange(int firstFrame, int lastFrame, int resumeFacing, int knockbackX)
85  : firstFrame(firstFrame), lastFrame(lastFrame), resumeFacing(resumeFacing),
86  knockbackX(knockbackX) {}
87 
88  int firstFrame = -1;
89  int lastFrame = -1;
90  int resumeFacing = -1;
91  int knockbackX = 0;
92 };
93 
95  bool attackActive = false;
96  int attackFirstFrame = -1;
97  int attackLastFrame = -1;
98  int attackContactFrame = -1;
99  int attackResumeFacing = -1;
100  bool attackSoundPlayed = false;
101  bool attackContactResolved = false;
102  Common::String attackTargetName;
103  bool hitActive = false;
104  int hitFirstFrame = -1;
105  int hitLastFrame = -1;
106  int hitResumeFacing = -1;
107  int hitKnockbackRemainingX = 0;
108  int hitKnockbackDecayStep = 0;
109  bool hitSoundPlayed = false;
110  bool deathActive = false;
111  int deathFirstFrame = -1;
112  int deathLastFrame = -1;
113  int deathDamageType = 0;
114  int loggedPursuitStallDirections = 0;
115  bool allowInitialPursuitStep = true;
116  uint32 nextMovementTick = 0;
117  uint32 attackCooldownSeedTick = 0;
118 };
119 
121  bool deathActive = false;
122  int deathFirstFrame = -1;
123  int deathLastFrame = -1;
124  int deathDamageType = 0;
125 };
126 
129  bool finalizedDeath = false;
130 };
131 
132 class Combat {
133 public:
134  Combat(HarvesterEngine &engine, SceneState &scene, Player &player,
135  EntityManager *entityManager, SceneEntities &sceneRuntime, CombatEffects &combatEffects,
136  const CheatSystem &cheats, InteractionCallbacks &callbacks, Interaction &interactionProcessor);
137  Combat(const Combat &) = delete;
138  Combat &operator=(const Combat &) = delete;
139 
140  void initializeCombatStates();
141  // Keep prepared state separate until scene-entity mutations succeed.
142  Common::Array<MonsterCombatState> prepareMonsterCombatStatesForSceneChange(
143  const Common::Array<MonsterRecord> &previousMonsters, const Common::Array<MonsterRecord> &updatedMonsters) const;
144  void commitMonsterCombatStatesForSceneChange(Common::Array<MonsterCombatState> &&states);
145  void resetForSceneRefresh();
146 
147  void capturePlayerAttackTargetForKeyboard(bool attackLeft, bool attackRight, bool attackUp, bool attackDown) const;
148  MonsterRecord *resolveKillTarget(const Common::Point &mousePos) const;
149  // The caller clears the captured target before resolving the room hover state.
150  void capturePlayerAttackTarget(const Common::Point &mousePos, const NpcRecord *hoverNpc) const;
151  void captureOverlappingNpcAttackTarget() const;
152  NpcRecord *selectNpcAttackContactTarget(Entity *&npcEntity) const;
153  MonsterRecord *selectMonsterAttackContactTarget(Entity *&monsterEntity) const;
154 
155  Common::Error killDebugActiveMonster(const Common::Point &mousePos, bool &needsRedraw);
156 
157  Common::Error updateRoomMonsterCombat(bool &needsRedraw);
158  Common::Error resolvePlayerAttackContact(bool &needsRedraw);
159 
160 private:
161  Common::Error resolvePlayerNpcAttackContact(Script &script, int damageType, bool &needsRedraw);
162  Common::Error resolvePlayerMonsterAttackContact(Script &script,
163  int damageType, int damageAmount, bool &needsRedraw);
164 
165  struct MonsterPursuitResult;
166 
167  // Returns true when player defeat must end the entire combat update.
168  bool updateMonsterAttack(Script &script, const MonsterRecord &monster,
169  Entity &entity, MonsterCombatState &combatState, bool &playerAlive, bool &needsRedraw);
170  MonsterPursuitResult updateMonsterPursuit(Script &script, MonsterRecord &monster,
171  Entity &entity, MonsterCombatState &combatState, int depthStep, bool &needsRedraw) const;
172  void beginMonsterAttack(MonsterRecord &monster, Entity &entity, MonsterCombatState &combatState,
173  const MonsterPursuitResult &pursuit, uint32 now, bool &needsRedraw) const;
174 
175  void resizeCombatStates();
176  bool queueInnerSanctumHerrillDeathAfterMuck(const MonsterRecord &monster) const;
177  // Called after resizeCombatStates; NPC finalization ends the entire combat update.
178  NpcCombatUpdateResult updateRoomNpcCombat(bool &needsRedraw);
179 
180  bool beginNpcDeathTransition(NpcRecord &npc, Entity &npcEntity, NpcCombatState &combatState,
181  int deathDamageType, bool &needsRedraw) const;
182  Common::Error finalizeNpcDeathTransition(NpcRecord &npc, NpcCombatState &combatState, bool &needsRedraw) const;
183 
184  void startMonsterHitReaction(MonsterRecord &monster, Entity &monsterEntity, MonsterCombatState *combatState,
185  int attackerAttackFirstFrame, bool &needsRedraw) const;
186 
187  void updateMonsterHitReaction(Script &script, MonsterRecord &monster,
188  Entity &entity, MonsterCombatState &combatState, bool &needsRedraw) const;
189  void startMonsterDeathAnimation(const MonsterRecord &monster, Entity &monsterEntity,
190  MonsterCombatState &combatState, const DeathAnimationRange &deathRange, int deathDamageType) const;
191  Common::Error finalizeMonsterDeathTransition(Script &script, MonsterRecord &monster,
192  Entity &entity, MonsterCombatState &combatState, bool &needsRedraw) const;
193 
194  static Entity *findSceneRuntimeEntity(EntityManager *entityManager, const Common::String &name);
195  static NpcRecord *findRoomNpcRecordByName(RoomSetupState &state, const Common::String &name);
196  static Entity *selectMonsterEntity(EntityManager *entityManager, const MonsterRecord &monster);
197 
198  MonsterRecord *findMonsterTargetAtPoint(const Common::Point &point) const;
199  MonsterRecord *findOverlappingMonsterTarget() const;
200  NpcRecord *findOverlappingNpcTarget() const;
201  bool playerAttackCanReachTarget(Entity *targetEntity, int engageDistance = 0) const;
202  MonsterRecord *findProjectileContactMonsterTarget() const;
203 
204  HarvesterEngine &_engine;
205  // Refresh assigns new room data into this scene object.
206  SceneState &_scene;
207  Player &_player;
208  EntityManager *const _entityManager;
209  SceneEntities &_sceneRuntime;
210  CombatEffects &_combatEffects;
211  const CheatSystem &_cheats;
212  InteractionCallbacks &_callbacks;
213  Interaction &_interactionProcessor;
214  Common::Array<MonsterCombatState> _monsterCombatStates;
215  Common::Array<NpcCombatState> _npcCombatStates;
216 };
217 
219  Common::String entityName;
220  Common::String followTargetName;
221  Common::Point anchorPoint;
222  float renderZ = 0.0f;
223  bool finished = false;
224 };
225 
227  Common::String followTargetName;
228  Common::Point anchorPoint;
229  uint32 startTick = 0;
230  int damageAmount = 0;
231 };
232 
234 public:
235  explicit CombatEffects(EntityManager *entityManager);
236 
237  void clearHitEffects();
238  bool hasDamagePopups() const { return !_damagePopupStates.empty(); }
239  bool spawnCombatHitEffect(Entity &sourceEntity, const Common::String &followTargetName);
240  void spawnCombatDamagePopup(Entity &targetEntity, const Common::String &followTargetName, int damageAmount);
241  bool syncCombatHitEffects();
242  void drawCombatDamagePopups(Graphics::Screen &screen, bool combatDebugEnabled);
243  bool pruneCombatHitEffects();
244  bool pruneCombatDamagePopups();
245 
246 private:
247  static void resolveHitEffectAnchor(const Entity &targetEntity, Common::Point &anchorPoint, float &renderZ);
248  static void resolveCombatDamagePopupAnchor(const Entity &targetEntity, Common::Point &anchorPoint);
249 
250  EntityManager *_entityManager;
251  Common::Array<HitEffectState> _hitEffectStates;
252  Common::Array<CombatDamagePopupState> _damagePopupStates;
253  uint _nextCombatEffectId = 0;
254 };
255 
257  uint iconIndex = 0;
258  int maxVisibleCount = 0;
259  const char *unitLabel = nullptr;
260 };
261 
263  ScopedDeferredLiveNpcDeathTransitions(Script *script) : _script(script) {
264  if (_script)
265  _script->pushDeferredLiveNpcDeathTransitions();
266  }
267 
269  if (_script)
270  _script->popDeferredLiveNpcDeathTransitions();
271  }
272 
273  Script *_script;
274 };
275 
276 const char *describeCombatTargetClass(int classId);
277 bool shouldRetainNpcDeathEntityInCurrentRoom(const NpcRecord &npc);
278 bool isRetainedCurrentRoomNpcDeathRecord(const NpcRecord &npc);
279 bool resolveCombatLoadoutHudInfo(int loadout, CombatLoadoutHudInfo &info);
280 Common::String buildCombatLoadoutStatusMessage(Script &script, const ObjectRecord &object,
281  int previousLoadout, int currentLoadout);
282 bool setRoomMonsterAnimation(Entity &entity, int facing, bool walking);
283 bool runtimeEntityHasFrameRange(const Entity &entity, int firstFrame, int lastFrame);
284 bool runtimeEntityHasDrawableFrameRange(const Entity &entity, int firstFrame, int lastFrame);
285 bool resolveMonsterAttackAnimationRange(HarvesterEngine &engine,
286  const Entity &entity, int actorCenterX, int targetCenterX,
287  AttackAnimationRange &range);
288 bool resolveMonsterDeathAnimationRange(const Entity &entity, int facing,
289  int deathDamageType, bool goreEnabled, DeathAnimationRange &range);
290 bool resolveNpcDeathAnimationRange(const Entity &entity, bool hasMonsterfyTarget,
291  int deathDamageType, bool goreEnabled, DeathAnimationRange &range);
292 int stepTowardsRoomCombatInt(int current, int target, int step);
293 int computeRuntimeEntityHorizontalGap(const Entity &left, const Entity &right);
294 bool doRuntimeEntityDepthExtentsOverlap(const Entity &first, const Entity &second);
295 bool areCombatantsWithinRoomCombatReach(const RoomSetupState &state,
296  const Entity &attacker, float attackerZ,
297  const Entity &target, float targetZ, int engageDistance);
298 bool isWithinNativeMonsterAttackEntryRange(const Entity &monster, float monsterZ,
299  const Entity &player, float playerZ, int engageDistance);
300 bool playRandomRoomAttackSound(HarvesterEngine &engine, const Common::String &sound1,
301  const Common::String &sound2, const Common::String &sound3);
302 bool resolveRoomMonsterHitAnimationRange(const Entity &entity,
303  int attackerAttackFirstFrame, HitAnimationRange &range);
304 float clampRoomDepthForEvent(const RoomSetupState &state, float z);
305 int roundRoomCombatFloat(float value);
306 int mapRoomDepthToScreenYForCombat(const RoomSetupState &state, float z, int fallbackY);
307 int resolveMonsterAttackContactFrameOffset(const MonsterRecord &monster);
308 void clearRoomMonsterCombatState(MonsterCombatState &combatState);
309 void clearRoomNpcCombatState(NpcCombatState &combatState);
310 void clearRoomMonsterAttackState(MonsterCombatState &combatState);
311 void clearRoomMonsterHitState(MonsterCombatState &combatState);
312 
313 } // End of namespace Harvester
314 
315 #endif // HARVESTER_COMBAT_H
Definition: script.h:237
Definition: str.h:59
Definition: art.h:31
Definition: script.h:132
Definition: combat.h:94
Definition: error.h:81
Definition: combat.h:82
Definition: combat.h:218
Definition: array.h:52
Definition: script.h:342
Definition: room_support.h:46
Definition: interaction.h:43
No error occurred.
Definition: error.h:48
Definition: script.h:86
Definition: screen.h:47
Definition: combat.h:132
Definition: entity.h:66
Definition: entity.h:184
Definition: scene_entities.h:31
Definition: combat.h:73
Definition: interaction.h:104
Definition: rect.h:144
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
Definition: player.h:105
Definition: harvester.h:47
Definition: combat.h:120
Definition: combat.h:63
Definition: cheats.h:52
Definition: combat.h:233
Definition: monster.h:31
Definition: combat.h:226
Definition: combat.h:256
Definition: combat.h:127