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