Skip to content

Commit 5b96a18

Browse files
committed
gamestate: Calculate application for discrete FLAC effects.
1 parent 8bf49af commit 5b96a18

File tree

2 files changed

+184
-8
lines changed

2 files changed

+184
-8
lines changed

libopenage/gamestate/system/apply_effect.cpp

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,167 @@
22

33
#include "apply_effect.h"
44

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+
519

620
namespace openage::gamestate::system {
721

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,
923
const std::shared_ptr<openage::gamestate::GameState> &state,
10-
const std::shared_ptr<gamestate::GameEntity> &target,
24+
const std::shared_ptr<gamestate::GameEntity> &resistor,
1125
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;
14166
}
15167

16168
} // namespace openage::gamestate::system

libopenage/gamestate/system/apply_effect.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
#pragma once
44

5+
#include <memory>
6+
7+
#include <nyan/nyan.h>
8+
9+
#include "gamestate/component/types.h"
510
#include "time/time.h"
611

712

@@ -15,20 +20,39 @@ namespace system {
1520

1621

1722
class ApplyEffect {
23+
public:
1824
/**
1925
* Apply the effect of an ability to a game entity.
2026
*
21-
* @param entity Game entity applying the effects.
27+
* @param effector Game entity applying the effects.
2228
* @param state Game state.
23-
* @param target Target entity of the effects.
29+
* @param resistor Target entity of the effects.
2430
* @param start_time Start time of change.
2531
*
2632
* @return Runtime of the change in simulation time.
2733
*/
28-
static const time::time_t apply_effect(const std::shared_ptr<gamestate::GameEntity> &entity,
34+
static const time::time_t apply_effect(const std::shared_ptr<gamestate::GameEntity> &effector,
2935
const std::shared_ptr<openage::gamestate::GameState> &state,
30-
const std::shared_ptr<gamestate::GameEntity> &target,
36+
const std::shared_ptr<gamestate::GameEntity> &resistor,
3137
const time::time_t &start_time);
38+
39+
private:
40+
/**
41+
* Get the gross applied value for discrete FlatAttributeChange effects.
42+
*
43+
* The gross applied value is calculated as follows:
44+
*
45+
* applied_value = clamp(change_value - block_value, min_change, max_change)
46+
*
47+
* Effects and resistances MUST have the same attribute change type.
48+
*
49+
* @param effects Effects of the effector.
50+
* @param resistances Resistances of the resistor.
51+
*
52+
* @return Gross applied attribute change value.
53+
*/
54+
static const component::attribute_value_t get_applied_discrete_flac(const std::vector<nyan::Object> &effects,
55+
const std::vector<nyan::Object> &resistances);
3256
};
3357

3458
} // namespace system

0 commit comments

Comments
 (0)