1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #define _USE_MATH_DEFINES
20 #include <cmath>
21 #include <algorithm>
22 #include <lcf/data.h>
23 #include <lcf/rpg/enemy.h>
24 #include "game_battle.h"
25 #include "game_enemy.h"
26 #include "game_party.h"
27 #include "game_switches.h"
28 #include <lcf/reader_util.h>
29 #include "output.h"
30 #include "utils.h"
31 #include "player.h"
32 #include "attribute.h"
33 #include "rand.h"
34
Game_Enemy(const lcf::rpg::TroopMember * member)35 Game_Enemy::Game_Enemy(const lcf::rpg::TroopMember* member)
36 : troop_member(member)
37 {
38 if (troop_member == nullptr) {
39 return;
40 }
41 Transform(troop_member->enemy_id);
42
43 hp = GetMaxHp();
44 sp = GetMaxSp();
45 ResetBattle();
46
47 SetHidden(troop_member->invisible);
48 SetBattlePosition(GetOriginalPosition());
49 }
50
MaxHpValue() const51 int Game_Enemy::MaxHpValue() const {
52 auto& val = lcf::Data::system.easyrpg_max_enemy_hp;
53 if (val == -1) {
54 return Player::IsRPG2k() ? 9999 : 99999;
55 }
56 return val;
57 }
58
MaxSpValue() const59 int Game_Enemy::MaxSpValue() const {
60 auto& val = lcf::Data::system.easyrpg_max_enemy_sp;
61 if (val == -1) {
62 return 9999;
63 }
64 return val;
65 }
66
MaxStatBattleValue() const67 int Game_Enemy::MaxStatBattleValue() const {
68 auto& val = lcf::Data::system.easyrpg_max_stat_battle_value;
69 if (val == -1) {
70 return 9999;
71 }
72 return val;
73 }
74
MaxStatBaseValue() const75 int Game_Enemy::MaxStatBaseValue() const {
76 auto& val = lcf::Data::system.easyrpg_max_stat_base_value;
77 if (val == -1) {
78 return 999;
79 }
80 return val;
81 }
82
GetStateProbability(int state_id) const83 int Game_Enemy::GetStateProbability(int state_id) const {
84 int rate = 1; // Enemies have only B as the default state rank
85
86 if (state_id >= 1 && state_id <= (int)enemy->state_ranks.size()) {
87 rate = enemy->state_ranks[state_id - 1];
88 }
89
90 return GetStateRate(state_id, rate);
91 }
92
GetBaseAttributeRate(int attribute_id) const93 int Game_Enemy::GetBaseAttributeRate(int attribute_id) const {
94 int rate = 2; // C - default
95
96 if (attribute_id >= 1 && attribute_id <= (int)enemy->attribute_ranks.size()) {
97 rate = enemy->attribute_ranks[attribute_id - 1];
98 }
99
100 return rate;
101 }
102
SetHp(int _hp)103 int Game_Enemy::SetHp(int _hp) {
104 hp = Utils::Clamp(_hp, 0, GetMaxHp());
105 return hp;
106 }
107
SetSp(int _sp)108 int Game_Enemy::SetSp(int _sp) {
109 sp = Utils::Clamp(_sp, 0, GetMaxSp());
110 return sp;
111 }
112
GetOriginalPosition() const113 Point Game_Enemy::GetOriginalPosition() const {
114 return { troop_member->x, troop_member->y };
115 }
116
makeDummyEnemy()117 static lcf::rpg::Enemy makeDummyEnemy() {
118 lcf::rpg::Enemy enemy;
119 enemy.ID = 1;
120 return enemy;
121 }
122
Transform(int new_enemy_id)123 void Game_Enemy::Transform(int new_enemy_id) {
124 enemy = lcf::ReaderUtil::GetElement(lcf::Data::enemies, new_enemy_id);
125
126 if (!enemy) {
127 // Some games (e.g. Battle 5 in Embric) have invalid monsters in the battle.
128 // This case will fail in RPG Maker and the game will exit with an error message.
129 // Create a warning instead and continue the battle.
130 Output::Warning("Invalid enemy ID {}", new_enemy_id);
131 // This generates an invisible monster with 0 HP
132 static auto dummy = makeDummyEnemy();
133 enemy = &dummy;
134 }
135
136 auto* sprite = GetEnemyBattleSprite();
137 if (sprite) {
138 sprite->Refresh();
139 }
140 }
141
GetHitChance(Weapon) const142 int Game_Enemy::GetHitChance(Weapon) const {
143 return enemy->miss ? 70 : 90;
144 }
145
GetCriticalHitChance(Weapon) const146 float Game_Enemy::GetCriticalHitChance(Weapon) const {
147 return enemy->critical_hit ? (1.0f / enemy->critical_hit_chance) : 0.0f;
148 }
149
GetFlyingOffset() const150 int Game_Enemy::GetFlyingOffset() const {
151 // 2k does not support flying, albeit mentioned in the help file
152 if (!Player::IsRPG2k3() || !IsFlying()) {
153 return 0;
154 }
155
156 const auto frame = GetBattleFrameCounter();
157 auto offset = Utils::RoundTo<int>(std::sin(2 * M_PI * static_cast<double>(frame) / 256.0) * 4.0);
158 return offset;
159 }
160
UpdateBattle()161 void Game_Enemy::UpdateBattle() {
162 if (blink_timer > 0) --blink_timer;
163 if (death_timer > 0) --death_timer;
164 if (explode_timer > 0) --explode_timer;
165
166 Game_Battler::UpdateBattle();
167 }
168
ResetBattle()169 void Game_Enemy::ResetBattle() {
170 Game_Battler::ResetBattle();
171
172 blink_timer = 0;
173 death_timer = 0;
174 explode_timer = 0;
175 }
176