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