|
2 | 2 |
|
3 | 3 | #include "apply_effect.h"
|
4 | 4 |
|
| 5 | +#include <unordered_map> |
| 6 | + |
| 7 | +#include "error/error.h" |
| 8 | + |
| 9 | +#include "gamestate/api/effect.h" |
| 10 | +#include "gamestate/api/resistance.h" |
| 11 | +#include "gamestate/api/types.h" |
| 12 | +#include "gamestate/component/api/apply_effect.h" |
| 13 | +#include "gamestate/component/api/live.h" |
| 14 | +#include "gamestate/component/api/resistance.h" |
| 15 | +#include "gamestate/component/types.h" |
| 16 | +#include "gamestate/game_entity.h" |
| 17 | +#include "gamestate/game_state.h" |
| 18 | + |
5 | 19 |
|
6 | 20 | namespace openage::gamestate::system {
|
7 | 21 |
|
8 |
| -const time::time_t ApplyEffect::apply_effect(const std::shared_ptr<gamestate::GameEntity> &entity, |
| 22 | +const time::time_t ApplyEffect::apply_effect(const std::shared_ptr<gamestate::GameEntity> &effector, |
9 | 23 | const std::shared_ptr<openage::gamestate::GameState> &state,
|
10 |
| - const std::shared_ptr<gamestate::GameEntity> &target, |
| 24 | + const std::shared_ptr<gamestate::GameEntity> &resistor, |
11 | 25 | const time::time_t &start_time) {
|
12 |
| - // TODO: Implement |
13 |
| - return start_time; |
| 26 | + auto effects_component = std::dynamic_pointer_cast<component::ApplyEffect>( |
| 27 | + effector->get_component(component::component_t::APPLY_EFFECT)); |
| 28 | + auto effect_ability = effects_component->get_ability(); |
| 29 | + auto batches = effect_ability.get_set("ApplyEffect.batches"); |
| 30 | + |
| 31 | + auto resistance_component = std::dynamic_pointer_cast<component::Resistance>( |
| 32 | + resistor->get_component(component::component_t::RESISTANCE)); |
| 33 | + auto resistance_ability = resistance_component->get_ability(); |
| 34 | + auto resistances_set = resistance_ability.get_set("Resistance.resistances"); |
| 35 | + |
| 36 | + auto live_component = std::dynamic_pointer_cast<component::Live>( |
| 37 | + resistor->get_component(component::component_t::LIVE)); |
| 38 | + |
| 39 | + // Extract the effects from the ability |
| 40 | + std::unordered_map<api::effect_t, std::vector<nyan::Object>> effects{}; |
| 41 | + for (auto &batch : batches) { |
| 42 | + std::shared_ptr<nyan::ObjectValue> batch_obj_val = std::dynamic_pointer_cast<nyan::ObjectValue>(batch.get_ptr()); |
| 43 | + auto batch_obj = effect_ability.get_view()->get_object(batch_obj_val->get_name()); |
| 44 | + auto batch_effects = batch_obj.get_set("EffectBatch.effects"); |
| 45 | + |
| 46 | + for (auto &batch_effect : batch_effects) { |
| 47 | + std::shared_ptr<nyan::ObjectValue> effect_obj_val = std::dynamic_pointer_cast<nyan::ObjectValue>(batch_effect.get_ptr()); |
| 48 | + auto effect_obj = effect_ability.get_view()->get_object(effect_obj_val->get_name()); |
| 49 | + auto effect_type = api::APIEffect::get_type(effect_obj); |
| 50 | + |
| 51 | + if (effects.contains(effect_type)) { |
| 52 | + effects.emplace(effect_type, std::vector<nyan::Object>{}); |
| 53 | + } |
| 54 | + |
| 55 | + effects[effect_type].push_back(effect_obj); |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + // Extract the resistances from the ability |
| 60 | + std::unordered_map<api::effect_t, std::vector<nyan::Object>> resistances{}; |
| 61 | + for (auto &resistance : resistances_set) { |
| 62 | + std::shared_ptr<nyan::ObjectValue> resistance_obj_val = std::dynamic_pointer_cast<nyan::ObjectValue>(resistance.get_ptr()); |
| 63 | + auto resistance_obj = resistance_ability.get_view()->get_object(resistance_obj_val->get_name()); |
| 64 | + auto resistance_type = api::APIResistance::get_effect_type(resistance_obj); |
| 65 | + |
| 66 | + if (resistances.contains(resistance_type)) { |
| 67 | + resistances.emplace(resistance_type, std::vector<nyan::Object>{}); |
| 68 | + } |
| 69 | + |
| 70 | + resistances[resistance_type].push_back(resistance_obj); |
| 71 | + } |
| 72 | + |
| 73 | + time::time_t end_time = start_time; |
| 74 | + |
| 75 | + // Check for matching effects and resistances |
| 76 | + for (auto &effect : effects) { |
| 77 | + auto effect_type = effect.first; |
| 78 | + auto effect_objs = effect.second; |
| 79 | + |
| 80 | + if (!resistances.contains(effect_type)) { |
| 81 | + continue; |
| 82 | + } |
| 83 | + |
| 84 | + auto resistance_objs = resistances[effect_type]; |
| 85 | + |
| 86 | + switch (effect_type) { |
| 87 | + case api::effect_t::DISCRETE_FLAC_DECREASE: |
| 88 | + case api::effect_t::DISCRETE_FLAC_INCREASE: { |
| 89 | + // TODO: Filter effects by AttributeChangeType |
| 90 | + auto attribute_amount = effect_objs[0].get_object("FlatAttributeChange.change_value"); |
| 91 | + auto attribute = attribute_amount.get_object("AttributeAmount.type"); |
| 92 | + auto applied_value = get_applied_discrete_flac(effect_objs, resistance_objs); |
| 93 | + |
| 94 | + // TODO: Check if delay is necessary |
| 95 | + auto delay = effect_ability.get_float("ApplyDiscreteEffect.application_delay"); |
| 96 | + auto reload_time = effect_ability.get_float("ApplyDiscreteEffect.reload_time"); |
| 97 | + end_time += delay + reload_time; |
| 98 | + |
| 99 | + // Record the time when the effects were applied |
| 100 | + effects_component->set_init_time(start_time + delay); |
| 101 | + effects_component->set_last_used(end_time); |
| 102 | + |
| 103 | + // Apply the effect to the live component |
| 104 | + live_component->set_attribute(end_time, attribute.get_name(), applied_value); |
| 105 | + } break; |
| 106 | + default: |
| 107 | + throw Error(MSG(err) << "Effect type not implemented: " << static_cast<int>(effect_type)); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + return end_time; |
| 112 | +} |
| 113 | + |
| 114 | + |
| 115 | +const component::attribute_value_t ApplyEffect::get_applied_discrete_flac(const std::vector<nyan::Object> &effects, |
| 116 | + const std::vector<nyan::Object> &resistances) { |
| 117 | + component::attribute_value_t applied_value = 0; |
| 118 | + component::attribute_value_t min_change = component::attribute_value_t::min_value(); |
| 119 | + component::attribute_value_t max_change = component::attribute_value_t::max_value(); |
| 120 | + |
| 121 | + for (auto &effect : effects) { |
| 122 | + auto change_amount = effect.get_object("FlatAttributeChange.value"); |
| 123 | + auto min_change_amount = effect.get_optional<nyan::Object>("FlatAttributeChange.min_change_value"); |
| 124 | + auto max_change_amount = effect.get_optional<nyan::Object>("FlatAttributeChange.max_change_value"); |
| 125 | + |
| 126 | + // Get value from change amount |
| 127 | + // TODO: Ensure that the attribute is the same for all effects |
| 128 | + auto change_value = change_amount.get_int("AttributeAmount.amount"); |
| 129 | + applied_value += change_value; |
| 130 | + |
| 131 | + // TODO: The code below creates a clamp range from the lowest min and highest max values. |
| 132 | + // This could create some uintended side effects where the clamped range is much larger |
| 133 | + // than expected. Maybe this should be defined better. |
| 134 | + |
| 135 | + // Get min change value |
| 136 | + if (min_change_amount) { |
| 137 | + component::attribute_value_t min_change_value = (*min_change_amount)->get_int("AttributeAmount.amount"); |
| 138 | + min_change = std::min(min_change_value, min_change); |
| 139 | + } |
| 140 | + |
| 141 | + // Get max change value |
| 142 | + if (max_change_amount) { |
| 143 | + component::attribute_value_t max_change_value = (*max_change_amount)->get_int("AttributeAmount.amount"); |
| 144 | + max_change = std::max(max_change_value, max_change); |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + // TODO: Match effects to exactly one resistance to avoid multi resiatance. |
| 149 | + // idea: move effect type to Effect object and make Resistance.resistances a dict. |
| 150 | + |
| 151 | + for (auto &resistance : resistances) { |
| 152 | + auto block_amount = resistance.get_object("FlatAttributeChange.value"); |
| 153 | + auto min_block_amount = resistance.get_optional<nyan::Object>("FlatAttributeChange.min_change_value"); |
| 154 | + auto max_block_amount = resistance.get_optional<nyan::Object>("FlatAttributeChange.max_change_value"); |
| 155 | + |
| 156 | + // Get value from block amount |
| 157 | + // TODO: Ensure that the attribute is the same attribute used in the effects |
| 158 | + auto block_value = block_amount.get_int("AttributeAmount.amount"); |
| 159 | + applied_value -= block_value; |
| 160 | + } |
| 161 | + |
| 162 | + // Clamp the applied value |
| 163 | + applied_value = std::clamp(applied_value, min_change, max_change); |
| 164 | + |
| 165 | + return applied_value; |
14 | 166 | }
|
15 | 167 |
|
16 | 168 | } // namespace openage::gamestate::system
|
0 commit comments