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 #include "attribute.h"
18 #include <lcf/rpg/item.h>
19 #include <lcf/rpg/skill.h>
20 #include <lcf/rpg/attribute.h>
21 #include <lcf/reader_util.h>
22 #include <lcf/data.h>
23 #include "game_battler.h"
24 #include "game_actor.h"
25 #include "output.h"
26 #include "player.h"
27 #include <climits>
28
29 namespace Attribute {
30
GetAttributeRateModifier(int attribute_id,int rate)31 int GetAttributeRateModifier(int attribute_id, int rate) {
32 const auto* attribute = lcf::ReaderUtil::GetElement(lcf::Data::attributes, attribute_id);
33
34 if (!attribute) {
35 Output::Warning("GetAttributeRate: Invalid attribute ID {}", attribute_id);
36 return 0;
37 }
38
39 return GetAttributeRateModifier(*attribute, rate);
40 }
41
GetAttributeRateModifier(const lcf::rpg::Attribute & attr,int rate)42 int GetAttributeRateModifier(const lcf::rpg::Attribute& attr, int rate) {
43 switch (rate) {
44 case 0:
45 return attr.a_rate;
46 case 1:
47 return attr.b_rate;
48 case 2:
49 return attr.c_rate;
50 case 3:
51 return attr.d_rate;
52 case 4:
53 return attr.e_rate;
54 default:
55 break;
56 }
57
58 return 0;
59 }
60
HasAttribute(Span<const lcf::DBBitArray * > attribute_sets,int id)61 static bool HasAttribute(Span<const lcf::DBBitArray*> attribute_sets, int id) {
62 for (auto* as: attribute_sets) {
63 const auto idx = id - 1;
64 if (idx < static_cast<int>(as->size()) && (*as)[idx]) {
65 return true;
66 }
67 }
68 return false;
69 }
70
ApplyAttributeMultiplier(int effect,const Game_Battler & target,Span<const lcf::DBBitArray * > attribute_sets)71 int ApplyAttributeMultiplier(int effect, const Game_Battler& target, Span<const lcf::DBBitArray*> attribute_sets) {
72 int physical = INT_MIN;
73 int magical = INT_MIN;
74
75 int n = 0;
76 for (auto* as: attribute_sets) {
77 n = std::max(static_cast<int>(as->size()), n);
78 }
79
80 for (int i = 0; i < n; ++i) {
81 const auto id = i + 1;
82
83 if (!HasAttribute(attribute_sets, id)) {
84 continue;
85 }
86
87 const auto* attr = lcf::ReaderUtil::GetElement(lcf::Data::attributes, id);
88 if (!attr) {
89 Output::Warning("ApplyAttributeMultipler: Invalid attribute ID {}", id);
90 break;
91 }
92
93 const auto rate = target.GetAttributeRate(id);
94 const auto mod = GetAttributeRateModifier(id, rate);
95 if (attr->type == lcf::rpg::Attribute::Type_physical) {
96 physical = std::max(physical, mod);
97 } else {
98 magical = std::max(magical, mod);
99 }
100 }
101
102 // Negative attributes not supported in 2k.
103 auto limit = Player::IsRPG2k() ? -1 : INT_MIN;
104
105 if (physical > limit && magical > limit) {
106 if (physical >= 0 && magical >= 0) {
107 effect = magical * (physical * effect / 100) / 100;
108 } else {
109 effect = effect * std::max(physical, magical) / 100;
110 }
111 } else if (physical > limit) {
112 effect = physical * effect / 100;
113 } else if (magical > limit) {
114 effect = magical * effect / 100;
115 }
116 return effect;
117 }
118
ApplyAttributeNormalAttackMultiplier(int effect,const Game_Battler & source_battler,const Game_Battler & target,Game_Battler::Weapon weapon)119 int ApplyAttributeNormalAttackMultiplier(int effect, const Game_Battler& source_battler, const Game_Battler& target, Game_Battler::Weapon weapon) {
120 if (source_battler.GetType() != Game_Battler::Type_Ally) {
121 return effect;
122 }
123 auto& source = static_cast<const Game_Actor&>(source_battler);
124
125 std::array<const lcf::DBBitArray*, 2> attribute_sets = {{}};
126
127 size_t n = 0;
128 auto add = [&](int i) {
129 if (weapon == Game_Battler::Weapon(i + 1) || weapon == Game_Battler::WeaponAll) {
130 auto* item = source.GetEquipment(i + 1);
131 if (item && item->type == lcf::rpg::Item::Type_weapon) {
132 attribute_sets[n++] = &item->attribute_set;
133 }
134 }
135 };
136
137 for (int i = 0; i < static_cast<int>(attribute_sets.size()); ++i) {
138 add(i);
139 }
140
141 return ApplyAttributeMultiplier(effect, target, Span<const lcf::DBBitArray*>(attribute_sets.data(), n));
142 }
143
ApplyAttributeSkillMultiplier(int effect,const Game_Battler & target,const lcf::rpg::Skill & skill)144 int ApplyAttributeSkillMultiplier(int effect, const Game_Battler& target, const lcf::rpg::Skill& skill) {
145 auto attribute_sets = Utils::MakeArray(&skill.attribute_effects);
146 return ApplyAttributeMultiplier(effect, target, MakeSpan(attribute_sets));
147 }
148
149 } // namespace Attribute
150