1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2009 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ////////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file battle_effects.cpp
12 *** \author Tyler Olsen, roots@allacrost.org
13 *** \brief Source file for battle actor effects.
14 *** ***************************************************************************/
15
16 #include "script.h"
17 #include "system.h"
18 #include "video.h"
19
20 #include "global.h"
21
22 #include "battle.h"
23 #include "battle_actors.h"
24 #include "battle_effects.h"
25 #include "battle_utils.h"
26
27 using namespace std;
28
29 using namespace hoa_utils;
30
31 using namespace hoa_system;
32 using namespace hoa_script;
33 using namespace hoa_video;
34
35 using namespace hoa_global;
36
37 namespace hoa_battle {
38
39 namespace private_battle {
40
41 ////////////////////////////////////////////////////////////////////////////////
42 // BattleStatusEffect class
43 ////////////////////////////////////////////////////////////////////////////////
44
BattleStatusEffect(GLOBAL_STATUS type,GLOBAL_INTENSITY intensity,BattleActor * actor)45 BattleStatusEffect::BattleStatusEffect(GLOBAL_STATUS type, GLOBAL_INTENSITY intensity, BattleActor* actor) :
46 GlobalStatusEffect(type, intensity),
47 _name(GetStatusName(type)),
48 _affected_actor(actor),
49 _timer(0),
50 _apply_function(NULL),
51 _icon_image(NULL)
52 {
53 if ((type <= GLOBAL_STATUS_INVALID) || (type >= GLOBAL_STATUS_TOTAL)) {
54 IF_PRINT_WARNING(GLOBAL_DEBUG) << "constructor received an invalid type argument: " << type << endl;
55 return;
56 }
57 if ((intensity <= GLOBAL_INTENSITY_INVALID) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
58 IF_PRINT_WARNING(GLOBAL_DEBUG) << "constructor received an invalid intensity argument: " << intensity << endl;
59 return;
60 }
61 if (actor == NULL) {
62 IF_PRINT_WARNING(BATTLE_DEBUG) << "constructor received NULL actor argument" << endl;
63 return;
64 }
65
66 uint32 table_id = static_cast<uint32>(type);
67 ReadScriptDescriptor& script_file = GlobalManager->GetStatusEffectsScript();
68 if (script_file.DoesTableExist(table_id) == false) {
69 IF_PRINT_WARNING(GLOBAL_DEBUG) << "Lua definition file contained no entry for status effect: " << table_id << endl;
70 return;
71 }
72
73 script_file.OpenTable(table_id);
74 if (script_file.DoesFunctionExist("Apply")) {
75 _apply_function = new ScriptObject();
76 (*_apply_function) = script_file.ReadFunctionPointer("Apply");
77 }
78 else {
79 PRINT_WARNING << "no apply function found in Lua definition file for status: " << table_id << endl;
80 }
81 script_file.CloseTable();
82
83 if (script_file.IsErrorDetected()) {
84 if (BATTLE_DEBUG) {
85 PRINT_WARNING << "one or more errors occurred while reading status effect data - they are listed below" << endl;
86 cerr << script_file.GetErrorMessages() << endl;
87 }
88 }
89
90 _ApplyChange();
91 }
92
93
94
~BattleStatusEffect()95 BattleStatusEffect::~BattleStatusEffect(){
96 if (_apply_function != NULL)
97 delete _apply_function;
98 _apply_function = NULL;
99 }
100
101
102
103
SetIntensity(hoa_global::GLOBAL_INTENSITY intensity)104 void BattleStatusEffect::SetIntensity(hoa_global::GLOBAL_INTENSITY intensity) {
105 if ((intensity < GLOBAL_INTENSITY_NEUTRAL) || (intensity >= GLOBAL_INTENSITY_TOTAL)) {
106 IF_PRINT_WARNING(BATTLE_DEBUG) << "attempted to set status effect to invalid intensity: " << intensity << endl;
107 return;
108 }
109
110 if (_intensity == intensity) {
111 _timer.Reset();
112 _timer.Run();
113 return;
114 }
115
116 _intensity = intensity;
117 _ApplyChange();
118 }
119
120
121
122
IncrementIntensity(uint8 amount)123 bool BattleStatusEffect::IncrementIntensity(uint8 amount) {
124 bool change = GlobalStatusEffect::IncrementIntensity(amount);
125 if (change == true)
126 _ApplyChange();
127 return change;
128 }
129
130
131
DecrementIntensity(uint8 amount)132 bool BattleStatusEffect::DecrementIntensity(uint8 amount) {
133 bool change = GlobalStatusEffect::DecrementIntensity(amount);
134 if (change == true)
135 _ApplyChange();
136 return change;
137 }
138
139
140
_ApplyChange()141 void BattleStatusEffect::_ApplyChange() {
142 _icon_image = BattleMode::CurrentInstance()->GetStatusIcon(_type, _intensity);
143
144 _timer.Reset();
145 ScriptCallFunction<void>(*_apply_function, this);
146 _timer.Run();
147 }
148
149 ////////////////////////////////////////////////////////////////////////////////
150 // EffectsSupervisor class
151 ////////////////////////////////////////////////////////////////////////////////
152
EffectsSupervisor(BattleActor * actor)153 EffectsSupervisor::EffectsSupervisor(BattleActor* actor) :
154 _actor(actor)
155 {
156 if (actor == NULL)
157 IF_PRINT_WARNING(BATTLE_DEBUG) << "contructor received NULL actor argument" << endl;
158 }
159
160
161
~EffectsSupervisor()162 EffectsSupervisor::~EffectsSupervisor() {
163 for (map<GLOBAL_STATUS, BattleStatusEffect*>::iterator i = _status_effects.begin(); i != _status_effects.end(); i++)
164 delete (i->second);
165 _status_effects.clear();
166 }
167
168
169
Update()170 void EffectsSupervisor::Update() {
171 // Update the timers for all active status effects
172 SystemTimer* effect_timer = NULL;
173 for (map<GLOBAL_STATUS, BattleStatusEffect*>::iterator i = _status_effects.begin(); i != _status_effects.end(); i++) {
174 effect_timer = i->second->GetTimer();
175 effect_timer->Update();
176
177 // Decrease the intensity of the status by one level when its timer expires. This may result in
178 // the status effect being removed from the actor if its intensity changes to the neutral level.
179 if (effect_timer->IsFinished() == true) {
180 _actor->RegisterStatusChange(i->first, GLOBAL_INTENSITY_NEG_LESSER);
181 }
182
183 }
184 }
185
186
187
Draw()188 void EffectsSupervisor::Draw() {
189 for (map<GLOBAL_STATUS, BattleStatusEffect*>::iterator i = _status_effects.begin(); i != _status_effects.end(); i++) {
190 i->second->GetIconImage()->Draw();
191 VideoManager->MoveRelative(25.0f, 0.0f);
192 }
193 }
194
195
196
ChangeStatus(GLOBAL_STATUS status,GLOBAL_INTENSITY & intensity)197 GLOBAL_INTENSITY EffectsSupervisor::ChangeStatus(GLOBAL_STATUS status, GLOBAL_INTENSITY& intensity) {
198 if ((status <= GLOBAL_STATUS_INVALID) || (status >= GLOBAL_STATUS_TOTAL)) {
199 IF_PRINT_WARNING(BATTLE_DEBUG) << "function received invalid status argument: " << status << endl;
200 return GLOBAL_INTENSITY_INVALID;
201 }
202
203 bool increase_intensity;
204 if ((intensity >= GLOBAL_INTENSITY_NEG_EXTREME) && (intensity < GLOBAL_INTENSITY_NEUTRAL)) {
205 increase_intensity = false;
206 }
207 else if ((intensity <= GLOBAL_INTENSITY_POS_EXTREME) && (intensity > GLOBAL_INTENSITY_NEUTRAL)) {
208 increase_intensity = true;
209 }
210 else {
211 IF_PRINT_WARNING(BATTLE_DEBUG) << "function received invalid intensity argument: " << intensity << endl;
212 return GLOBAL_INTENSITY_INVALID;
213 }
214
215 // TODO: account for opposite status effects. I think perhaps a recursive function call may be useful
216
217 // The intensity of this status effect before this function call was made, used as the function return value
218 GLOBAL_INTENSITY old_intensity = GLOBAL_INTENSITY_INVALID;
219 // Holds the unsigned amount of change in intensity in either a positive or negative dgree
220 uint8 change_amount = abs(static_cast<int8>(intensity));
221 // An iterator to the status effect if it already exists on the actor
222 map<GLOBAL_STATUS, BattleStatusEffect*>::iterator existing_entry = _status_effects.find(status);
223 // Set to true if the status effect was already active on the actor prior to this function call
224 bool status_active = (existing_entry != _status_effects.end());
225
226 // Case 1: Decrease intensity when status was not active -- no change in status
227 if ((status_active == false) && (increase_intensity == false)) {
228 // No warning message is printed for the case where the status was not found. This is done because
229 // certain skills/abilities want to remove status effects. It makes the implementation of those
230 // abilities easier if they do not have to worry about checking whether or not the status effect is
231 // active on the target.
232 old_intensity = GLOBAL_INTENSITY_INVALID;
233 }
234 // Case 2: Increase intensity when status was not active -- add the new status
235 else if ((status_active == false) && (increase_intensity == true)) {
236 old_intensity = GLOBAL_INTENSITY_NEUTRAL;
237 BattleStatusEffect* new_effect = new BattleStatusEffect(status, intensity, _actor);
238 _status_effects.insert(make_pair(status, new_effect));
239 }
240 // Case 3: Decrease intensity when status was active -- decrease intensity and possibly remove status
241 else if ((status_active == true) && (increase_intensity == false)) {
242 old_intensity = existing_entry->second->GetIntensity();
243 existing_entry->second->DecrementIntensity(change_amount);
244 intensity = existing_entry->second->GetIntensity();
245
246 if (intensity == GLOBAL_INTENSITY_NEUTRAL) {
247 delete existing_entry->second;
248 _status_effects.erase(existing_entry);
249 }
250 }
251 // Case 4: Increase intensity when status was active --
252 else { // ((status_active == true) && (increase_intensity == true))
253 old_intensity = existing_entry->second->GetIntensity();
254 existing_entry->second->IncrementIntensity(change_amount);
255 intensity = existing_entry->second->GetIntensity();
256 }
257
258 return old_intensity;
259 } // GLOBAL_INTENSITY EffectsSupervisor::ChangeStatus(GLOBAL_STATUS status, GLOBAL_INTENSITY& intensity)
260
261 } // namespace private_battle
262
263 } // namespace hoa_battle
264