| 70 | } |
| 71 | |
| 72 | int ApplyAttributeMultiplier(int effect, const Game_Battler& target, Span<const lcf::DBBitArray*> attribute_sets) { |
| 73 | int physical = INT_MIN; |
| 74 | int magical = INT_MIN; |
| 75 | |
| 76 | int n = 0; |
| 77 | for (auto* as: attribute_sets) { |
| 78 | n = std::max(static_cast<int>(as->size()), n); |
| 79 | } |
| 80 | |
| 81 | for (int i = 0; i < n; ++i) { |
| 82 | const auto id = i + 1; |
| 83 | |
| 84 | if (!HasAttribute(attribute_sets, id)) { |
| 85 | continue; |
| 86 | } |
| 87 | |
| 88 | const auto* attr = lcf::ReaderUtil::GetElement(lcf::Data::attributes, id); |
| 89 | if (!attr) { |
| 90 | Output::Warning("ApplyAttributeMultipler: Invalid attribute ID {}", id); |
| 91 | break; |
| 92 | } |
| 93 | |
| 94 | const auto rate = target.GetAttributeRate(id); |
| 95 | const auto mod = GetAttributeRateModifier(id, rate); |
| 96 | if (attr->type == lcf::rpg::Attribute::Type_physical) { |
| 97 | physical = std::max(physical, mod); |
| 98 | } else { |
| 99 | magical = std::max(magical, mod); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | // Negative attributes not supported in 2k. |
| 104 | auto limit = Player::IsRPG2k() ? -1 : INT_MIN; |
| 105 | |
| 106 | if (physical > limit && magical > limit) { |
| 107 | if (physical >= 0 && magical >= 0) { |
| 108 | effect = magical * (physical * effect / 100) / 100; |
| 109 | } else { |
| 110 | effect = effect * std::max(physical, magical) / 100; |
| 111 | } |
| 112 | } else if (physical > limit) { |
| 113 | effect = physical * effect / 100; |
| 114 | } else if (magical > limit) { |
| 115 | effect = magical * effect / 100; |
| 116 | } |
| 117 | return effect; |
| 118 | } |
| 119 | |
| 120 | int ApplyAttributeNormalAttackMultiplier(int effect, const Game_Battler& source_battler, const Game_Battler& target, Game_Battler::Weapon weapon) { |
| 121 | if (source_battler.GetType() != Game_Battler::Type_Ally && source_battler.GetType() != Game_Battler::Type_Enemy) { |
no test coverage detected