1 /*
2 Copyright © 2012-2016 Justin Jacobs
3 
4 This file is part of FLARE.
5 
6 FLARE is free software: you can redistribute it and/or modify it under the terms
7 of the GNU General Public License as published by the Free Software Foundation,
8 either version 3 of the License, or (at your option) any later version.
9 
10 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 FLARE.  If not, see http://www.gnu.org/licenses/
16 */
17 
18 /**
19  * class EffectManager
20  */
21 
22 #include "Animation.h"
23 #include "AnimationManager.h"
24 #include "AnimationSet.h"
25 #include "EffectManager.h"
26 #include "EngineSettings.h"
27 #include "Hazard.h"
28 #include "PowerManager.h"
29 #include "Settings.h"
30 #include "SharedGameResources.h"
31 #include "SharedResources.h"
32 #include "Stats.h"
33 
EffectDef()34 EffectDef::EffectDef()
35 	: id("")
36 	, type(Effect::NONE)
37 	, name("")
38 	, icon(-1)
39 	, animation("")
40 	, can_stack(true)
41 	, max_stacks(-1)
42 	, group_stack(false)
43 	, render_above(false)
44 	, color_mod(255, 255, 255)
45 	, alpha_mod(255)
46 	, attack_speed_anim("") {
47 }
48 
Effect()49 Effect::Effect()
50 	: id("")
51 	, name("")
52 	, icon(-1)
53 	, timer()
54 	, type(Effect::NONE)
55 	, magnitude(0)
56 	, magnitude_max(0)
57 	, animation_name("")
58 	, animation(NULL)
59 	, item(false)
60 	, trigger(-1)
61 	, render_above(false)
62 	, passive_id(0)
63 	, source_type(Power::SOURCE_TYPE_HERO)
64 	, group_stack(false)
65 	, color_mod(Color(255,255,255).encodeRGBA())
66 	, alpha_mod(255)
67 	, attack_speed_anim("") {
68 }
69 
Effect(const Effect & other)70 Effect::Effect(const Effect& other) {
71 	animation = NULL;
72 	*this = other;
73 }
74 
operator =(const Effect & other)75 Effect& Effect::operator=(const Effect& other) {
76 	if (this == &other)
77 		return *this;
78 
79 	unloadAnimation();
80 	animation_name = other.animation_name;
81 	loadAnimation(animation_name);
82 	if (animation && other.animation)
83 		animation->syncTo(other.animation);
84 
85 	id = other.id;
86 	name = other.name;
87 	icon = other.icon;
88 	timer = other.timer;
89 	type = other.type;
90 	magnitude = other.magnitude;
91 	magnitude_max = other.magnitude_max;
92 	item = other.item;
93 	trigger = other.trigger;
94 	render_above = other.render_above;
95 	passive_id = other.passive_id;
96 	source_type = other.source_type;
97 	group_stack = other.group_stack;
98 	color_mod = other.color_mod;
99 	alpha_mod = other.alpha_mod;
100 	attack_speed_anim = other.attack_speed_anim;
101 
102 	return *this;
103 }
104 
~Effect()105 Effect::~Effect() {
106 	unloadAnimation();
107 }
108 
loadAnimation(const std::string & s)109 void Effect::loadAnimation(const std::string &s) {
110 	if (!s.empty()) {
111 		animation_name = s;
112 		anim->increaseCount(animation_name);
113 		AnimationSet *animationSet = anim->getAnimationSet(animation_name);
114 		animation = animationSet->getAnimation("");
115 	}
116 }
117 
unloadAnimation()118 void Effect::unloadAnimation() {
119 	if (animation) {
120 		if (!animation_name.empty())
121 			anim->decreaseCount(animation_name);
122 		delete animation;
123 		animation = NULL;
124 	}
125 }
126 
getTypeFromString(const std::string & type_str)127 int Effect::getTypeFromString(const std::string& type_str) {
128 	if (type_str.empty()) return Effect::NONE;
129 
130 	if (type_str == "damage") return Effect::DAMAGE;
131 	else if (type_str == "damage_percent") return Effect::DAMAGE_PERCENT;
132 	else if (type_str == "hpot") return Effect::HPOT;
133 	else if (type_str == "hpot_percent") return Effect::HPOT_PERCENT;
134 	else if (type_str == "mpot") return Effect::MPOT;
135 	else if (type_str == "mpot_percent") return Effect::MPOT_PERCENT;
136 	else if (type_str == "speed") return Effect::SPEED;
137 	else if (type_str == "attack_speed") return Effect::ATTACK_SPEED;
138 	else if (type_str == "immunity") return Effect::IMMUNITY;
139 	else if (type_str == "immunity_damage") return Effect::IMMUNITY_DAMAGE;
140 	else if (type_str == "immunity_slow") return Effect::IMMUNITY_SLOW;
141 	else if (type_str == "immunity_stun") return Effect::IMMUNITY_STUN;
142 	else if (type_str == "immunity_hp_steal") return Effect::IMMUNITY_HP_STEAL;
143 	else if (type_str == "immunity_mp_steal") return Effect::IMMUNITY_MP_STEAL;
144 	else if (type_str == "immunity_knockback") return Effect::IMMUNITY_KNOCKBACK;
145 	else if (type_str == "immunity_damage_reflect") return Effect::IMMUNITY_DAMAGE_REFLECT;
146 	else if (type_str == "immunity_stat_debuff") return Effect::IMMUNITY_STAT_DEBUFF;
147 	else if (type_str == "stun") return Effect::STUN;
148 	else if (type_str == "revive") return Effect::REVIVE;
149 	else if (type_str == "convert") return Effect::CONVERT;
150 	else if (type_str == "fear") return Effect::FEAR;
151 	else if (type_str == "death_sentence") return Effect::DEATH_SENTENCE;
152 	else if (type_str == "shield") return Effect::SHIELD;
153 	else if (type_str == "heal") return Effect::HEAL;
154 	else if (type_str == "knockback") return Effect::KNOCKBACK;
155 	else {
156 		for (int i=0; i<Stats::COUNT; ++i) {
157 			if (type_str == Stats::KEY[i]) {
158 				return Effect::TYPE_COUNT + i;
159 			}
160 		}
161 
162 		for (size_t i=0; i<eset->damage_types.list.size(); ++i) {
163 			if (type_str == eset->damage_types.list[i].min) {
164 				return Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(i*2);
165 			}
166 			else if (type_str == eset->damage_types.list[i].max) {
167 				return Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(i*2) + 1;
168 			}
169 		}
170 
171 		for (size_t i=0; i<eset->elements.list.size(); ++i) {
172 			if (type_str == eset->elements.list[i].id + "_resist") {
173 				return Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count + i);
174 			}
175 		}
176 
177 		for (size_t i=0; i<eset->primary_stats.list.size(); ++i) {
178 			if (type_str == eset->primary_stats.list[i].id) {
179 				return Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) + static_cast<int>(eset->elements.list.size() + i);
180 			}
181 		}
182 	}
183 
184 	Utils::logError("EffectManager: '%s' is not a valid effect type.", type_str.c_str());
185 	return Effect::NONE;
186 }
187 
typeIsStat(int t)188 bool Effect::typeIsStat(int t) {
189 	return t >= Effect::TYPE_COUNT && t < Effect::TYPE_COUNT + Stats::COUNT;
190 }
191 
typeIsDmgMin(int t)192 bool Effect::typeIsDmgMin(int t) {
193 	return t >= Effect::TYPE_COUNT + Stats::COUNT && t < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) && (t - Stats::COUNT - Effect::TYPE_COUNT) % 2 == 0;
194 }
195 
typeIsDmgMax(int t)196 bool Effect::typeIsDmgMax(int t) {
197 	return t >= Effect::TYPE_COUNT + Stats::COUNT && t < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) && (t - Stats::COUNT - Effect::TYPE_COUNT) % 2 == 1;
198 }
199 
typeIsResist(int t)200 bool Effect::typeIsResist(int t) {
201 	return t >= Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) && t < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) + static_cast<int>(eset->elements.list.size());
202 }
203 
typeIsPrimary(int t)204 bool Effect::typeIsPrimary(int t) {
205 	return t >= Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) + static_cast<int>(eset->elements.list.size()) && t < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) + static_cast<int>(eset->elements.list.size()) + static_cast<int>(eset->primary_stats.list.size());
206 }
207 
getStatFromType(int t)208 int Effect::getStatFromType(int t) {
209 	return t - Effect::TYPE_COUNT;
210 }
211 
getDmgFromType(int t)212 size_t Effect::getDmgFromType(int t) {
213 	return static_cast<size_t>(t - Effect::TYPE_COUNT - Stats::COUNT);
214 }
215 
getResistFromType(int t)216 size_t Effect::getResistFromType(int t) {
217 	return static_cast<size_t>(t - Effect::TYPE_COUNT - Stats::COUNT) - eset->damage_types.count;
218 }
219 
getPrimaryFromType(int t)220 size_t Effect::getPrimaryFromType(int t) {
221 	return static_cast<size_t>(t - Effect::TYPE_COUNT - Stats::COUNT) - eset->damage_types.count - eset->elements.list.size();
222 }
223 
EffectManager()224 EffectManager::EffectManager()
225 	: bonus(std::vector<int>(Stats::COUNT + eset->damage_types.count, 0))
226 	, bonus_resist(std::vector<int>(eset->elements.list.size(), 0))
227 	, bonus_primary(std::vector<int>(eset->primary_stats.list.size(), 0))
228 	, triggered_others(false)
229 	, triggered_block(false)
230 	, triggered_hit(false)
231 	, triggered_halfdeath(false)
232 	, triggered_joincombat(false)
233 	, triggered_death(false)
234 	, refresh_stats(false) {
235 	clearStatus();
236 }
237 
~EffectManager()238 EffectManager::~EffectManager() {
239 }
240 
clearStatus()241 void EffectManager::clearStatus() {
242 	damage = 0;
243 	damage_percent = 0;
244 	hpot = 0;
245 	hpot_percent = 0;
246 	mpot = 0;
247 	mpot_percent = 0;
248 	speed = 100;
249 	immunity_damage = false;
250 	immunity_slow = false;
251 	immunity_stun = false;
252 	immunity_hp_steal = false;
253 	immunity_mp_steal = false;
254 	immunity_knockback = false;
255 	immunity_damage_reflect = false;
256 	immunity_stat_debuff = false;
257 	stun = false;
258 	revive = false;
259 	convert = false;
260 	death_sentence = false;
261 	fear = false;
262 	knockback_speed = 0;
263 
264 	for (unsigned i=0; i<Stats::COUNT + eset->damage_types.count; i++) {
265 		bonus[i] = 0;
266 	}
267 
268 	for (unsigned i=0; i<bonus_resist.size(); i++) {
269 		bonus_resist[i] = 0;
270 	}
271 
272 	for (unsigned i=0; i<bonus_primary.size(); i++) {
273 		bonus_primary[i] = 0;
274 	}
275 }
276 
logic()277 void EffectManager::logic() {
278 	clearStatus();
279 
280 	for (size_t i=0; i<effect_list.size(); ++i) {
281 		Effect& ei = effect_list[i];
282 
283 		// @CLASS EffectManager|Description of "type" in powers/effects.txt
284 		// expire timed effects and total up magnitudes of active effects
285 		if (ei.timer.getDuration() > 0) {
286 			if (ei.timer.isEnd()) {
287 				//death sentence is only applied at the end of the timer
288 				// @TYPE death_sentence|Causes sudden death at the end of the effect duration.
289 				if (ei.type == Effect::DEATH_SENTENCE) death_sentence = true;
290 				removeEffect(i);
291 				i--;
292 				continue;
293 			}
294 		}
295 
296 		bool do_timed_effect = ei.timer.isWholeSecond() || (ei.timer.getDuration() < settings->max_frames_per_sec && ei.timer.isBegin());
297 
298 		// @TYPE damage|Damage per second
299 		if (ei.type == Effect::DAMAGE && do_timed_effect) damage += ei.magnitude;
300 		// @TYPE damage_percent|Damage per second (percentage of max HP)
301 		else if (ei.type == Effect::DAMAGE_PERCENT && do_timed_effect) damage_percent += ei.magnitude;
302 		// @TYPE hpot|HP restored per second
303 		else if (ei.type == Effect::HPOT && do_timed_effect) hpot += ei.magnitude;
304 		// @TYPE hpot_percent|HP restored per second (percentage of max HP)
305 		else if (ei.type == Effect::HPOT_PERCENT && do_timed_effect) hpot_percent += ei.magnitude;
306 		// @TYPE mpot|MP restored per second
307 		else if (ei.type == Effect::MPOT && do_timed_effect) mpot += ei.magnitude;
308 		// @TYPE mpot_percent|MP restored per second (percentage of max MP)
309 		else if (ei.type == Effect::MPOT_PERCENT && do_timed_effect) mpot_percent += ei.magnitude;
310 		// @TYPE speed|Changes movement speed. A magnitude of 100 is 100% speed (aka normal speed).
311 		else if (ei.type == Effect::SPEED) speed = (static_cast<float>(ei.magnitude) * speed) / 100.f;
312 		// @TYPE attack_speed|Changes attack speed. A magnitude of 100 is 100% speed (aka normal speed).
313 		// attack speed is calculated when getAttackSpeed() is called
314 
315 		// @TYPE immunity|Applies all immunity effects. Magnitude is ignored.
316 		else if (ei.type == Effect::IMMUNITY) {
317 			immunity_damage = true;
318 			immunity_slow = true;
319 			immunity_stun = true;
320 			immunity_hp_steal = true;
321 			immunity_mp_steal = true;
322 			immunity_knockback = true;
323 			immunity_damage_reflect = true;
324 			immunity_stat_debuff = true;
325 		}
326 		// @TYPE immunity_damage|Removes and prevents damage over time. Magnitude is ignored.
327 		else if (ei.type == Effect::IMMUNITY_DAMAGE) immunity_damage = true;
328 		// @TYPE immunity_slow|Removes and prevents slow effects. Magnitude is ignored.
329 		else if (ei.type == Effect::IMMUNITY_SLOW) immunity_slow = true;
330 		// @TYPE immunity_stun|Removes and prevents stun effects. Magnitude is ignored.
331 		else if (ei.type == Effect::IMMUNITY_STUN) immunity_stun = true;
332 		// @TYPE immunity_hp_steal|Prevents HP stealing. Magnitude is ignored.
333 		else if (ei.type == Effect::IMMUNITY_HP_STEAL) immunity_hp_steal = true;
334 		// @TYPE immunity_mp_steal|Prevents MP stealing. Magnitude is ignored.
335 		else if (ei.type == Effect::IMMUNITY_MP_STEAL) immunity_mp_steal = true;
336 		// @TYPE immunity_knockback|Removes and prevents knockback effects. Magnitude is ignored.
337 		else if (ei.type == Effect::IMMUNITY_KNOCKBACK) immunity_knockback = true;
338 		// @TYPE immunity_damage_reflect|Prevents damage reflection. Magnitude is ignored.
339 		else if (ei.type == Effect::IMMUNITY_DAMAGE_REFLECT) immunity_damage_reflect = true;
340 		// @TYPE immunity_stat_debuff|Prevents stat value altering effects that have a magnitude less than 0. Magnitude is ignored.
341 		else if (ei.type == Effect::IMMUNITY_STAT_DEBUFF) immunity_stat_debuff = true;
342 
343 		// @TYPE stun|Can't move or attack. Being attacked breaks stun.
344 		else if (ei.type == Effect::STUN) stun = true;
345 		// @TYPE revive|Revives the player. Typically attached to a power that triggers when the player dies.
346 		else if (ei.type == Effect::REVIVE) revive = true;
347 		// @TYPE convert|Causes an enemy or an ally to switch allegiance
348 		else if (ei.type == Effect::CONVERT) convert = true;
349 		// @TYPE fear|Causes enemies to run away
350 		else if (ei.type == Effect::FEAR) fear = true;
351 		// @TYPE knockback|Pushes the target away from the source caster. Speed is the given value divided by the framerate cap.
352 		else if (ei.type == Effect::KNOCKBACK) knockback_speed = static_cast<float>(ei.magnitude)/static_cast<float>(settings->max_frames_per_sec);
353 
354 		// @TYPE ${STATNAME}|Increases ${STATNAME}, where ${STATNAME} is any of the base stats. Examples: hp, avoidance, xp_gain
355 		// @TYPE ${DAMAGE_TYPE}|Increases a damage min or max, where ${DAMAGE_TYPE} is any 'min' or 'max' value found in engine/damage_types.txt. Example: dmg_melee_min
356 		else if (ei.type >= Effect::TYPE_COUNT && ei.type < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count)) {
357 			bonus[ei.type - Effect::TYPE_COUNT] += ei.magnitude;
358 		}
359 		// @TYPE ${ELEMENT}_resist|Increase Resistance % to ${ELEMENT}, where ${ELEMENT} is any found in engine/elements.txt. Example: fire_resist
360 		else if (ei.type >= Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) && ei.type < Effect::TYPE_COUNT + Stats::COUNT + static_cast<int>(eset->damage_types.count) + static_cast<int>(eset->elements.list.size())) {
361 			bonus_resist[ei.type - Effect::TYPE_COUNT - Stats::COUNT - eset->damage_types.count] += ei.magnitude;
362 		}
363 		// @TYPE ${PRIMARYSTAT}|Increases ${PRIMARYSTAT}, where ${PRIMARYSTAT} is any of the primary stats defined in engine/primary_stats.txt. Example: physical
364 		else if (ei.type >= Effect::TYPE_COUNT) {
365 			bonus_primary[ei.type - Effect::TYPE_COUNT - Stats::COUNT - eset->damage_types.count - eset->elements.list.size()] += ei.magnitude;
366 		}
367 
368 		ei.timer.tick();
369 
370 		// expire shield effects
371 		if (ei.magnitude_max > 0 && ei.magnitude == 0) {
372 			// @TYPE shield|Create a damage absorbing barrier based on Mental damage stat. Duration is ignored.
373 			if (ei.type == Effect::SHIELD) {
374 				removeEffect(i);
375 				i--;
376 				continue;
377 			}
378 		}
379 		// expire effects based on animations
380 		if ((ei.animation && ei.animation->isLastFrame()) || !ei.animation) {
381 			// @TYPE heal|Restore HP based on Mental damage stat.
382 			if (ei.type == Effect::HEAL) {
383 				removeEffect(i);
384 				i--;
385 				continue;
386 			}
387 		}
388 
389 		// animate
390 		if (ei.animation) {
391 			if (!ei.animation->isCompleted())
392 				ei.animation->advanceFrame();
393 		}
394 	}
395 }
396 
addEffect(EffectDef & effect,int duration,int magnitude,int source_type,PowerID power_id)397 void EffectManager::addEffect(EffectDef &effect, int duration, int magnitude, int source_type, PowerID power_id) {
398 	addEffectInternal(effect, duration, magnitude, source_type, false, power_id);
399 }
400 
addItemEffect(EffectDef & effect,int duration,int magnitude)401 void EffectManager::addItemEffect(EffectDef &effect, int duration, int magnitude) {
402 	// only the hero can wear items, so use Power::SOURCE_TYPE_HERO
403 	addEffectInternal(effect, duration, magnitude, Power::SOURCE_TYPE_HERO, true, NO_POWER);
404 }
405 
addEffectInternal(EffectDef & effect,int duration,int magnitude,int source_type,bool item,PowerID power_id)406 void EffectManager::addEffectInternal(EffectDef &effect, int duration, int magnitude, int source_type, bool item, PowerID power_id) {
407 	refresh_stats = true;
408 
409 	// if we're already immune, don't add negative effects
410 	if (immunity_damage && (effect.type == Effect::DAMAGE || effect.type == Effect::DAMAGE_PERCENT))
411 		return;
412 	else if (immunity_slow && effect.type == Effect::SPEED && magnitude < 100)
413 		return;
414 	else if (immunity_stun && effect.type == Effect::STUN)
415 		return;
416 	else if (immunity_knockback && effect.type == Effect::KNOCKBACK)
417 		return;
418 	else if (immunity_stat_debuff && effect.type > Effect::TYPE_COUNT && magnitude < 0)
419 		return;
420 
421 	// only allow one knockback effect at a time
422 	if (effect.type == Effect::KNOCKBACK && knockback_speed != 0)
423 		return;
424 
425 	bool insert_effect = false;
426 	size_t insert_pos;
427 	int stacks_applied = 0;
428 	int trigger = power_id > 0 ? powers->powers[power_id].passive_trigger : -1;
429 	size_t passive_id = (power_id > 0 && powers->powers[power_id].passive) ? power_id : 0;
430 
431 	for (size_t i=effect_list.size(); i>0; i--) {
432 		Effect& ei = effect_list[i-1];
433 
434 		// while checking only id would be sufficient, it is a slow string compare
435 		// so we check the type first, which is an int compare, before taking the slow path
436 		if (ei.type == effect.type && ei.id == effect.id) {
437 			if (trigger > -1 && ei.trigger == trigger)
438 				return; // trigger effects can only be cast once per trigger
439 
440 			if (!effect.can_stack) {
441 				removeEffect(i-1);
442 			}
443 			else{
444 				if (effect.type == Effect::SHIELD && effect.group_stack){
445 					ei.magnitude += magnitude;
446 
447 					if (effect.max_stacks == -1
448 						|| (magnitude != 0 && ei.magnitude_max/magnitude < effect.max_stacks)){
449 						ei.magnitude_max += magnitude;
450 					}
451 
452 					if (ei.magnitude > ei.magnitude_max){
453 						ei.magnitude = ei.magnitude_max;
454 					}
455 
456 					return;
457 				}
458 
459 				if (insert_effect == false && effect.max_stacks != -1) {
460 					// to keep stackable effects together, they are inserted after the most recent matching effect
461 					// otherwise, they are added to the end of the effect list
462 					insert_effect = true;
463 					insert_pos = i;
464 				}
465 
466 				stacks_applied++;
467 			}
468 		}
469 		// if we're adding an immunity effect, remove all negative effects
470 		if (effect.type == Effect::IMMUNITY)
471 			clearNegativeEffects();
472 		else if (effect.type == Effect::IMMUNITY_DAMAGE)
473 			clearNegativeEffects(Effect::IMMUNITY_DAMAGE);
474 		else if (effect.type == Effect::IMMUNITY_SLOW)
475 			clearNegativeEffects(Effect::IMMUNITY_SLOW);
476 		else if (effect.type == Effect::IMMUNITY_STUN)
477 			clearNegativeEffects(Effect::IMMUNITY_STUN);
478 		else if (effect.type == Effect::IMMUNITY_KNOCKBACK)
479 			clearNegativeEffects(Effect::IMMUNITY_KNOCKBACK);
480 	}
481 
482 	Effect e;
483 
484 	e.id = effect.id;
485 	e.name = effect.name;
486 	e.icon = effect.icon;
487 	e.type = effect.type;
488 	e.render_above = effect.render_above;
489 	e.group_stack = effect.group_stack;
490 	e.color_mod = effect.color_mod.encodeRGBA();
491 	e.alpha_mod = effect.alpha_mod;
492 	e.attack_speed_anim = effect.attack_speed_anim;
493 
494 	if (!effect.animation.empty()) {
495 		e.loadAnimation(effect.animation);
496 	}
497 
498 	e.timer.setDuration(duration);
499 	e.magnitude = e.magnitude_max = magnitude;
500 	e.item = item;
501 	e.trigger = trigger;
502 	e.passive_id = passive_id;
503 	e.source_type = source_type;
504 
505 	if (insert_effect) {
506 		if (effect.max_stacks != -1 && stacks_applied >= effect.max_stacks){
507 			//Remove the oldest effect of the type
508 			removeEffect(insert_pos-stacks_applied);
509 
510 			//All elements have shifted to left
511 			insert_pos--;
512 		}
513 
514 		effect_list.insert(effect_list.begin() + insert_pos, e);
515 	}
516 	else {
517 		effect_list.push_back(e);
518 	}
519 }
520 
removeEffect(size_t id)521 void EffectManager::removeEffect(size_t id) {
522 	effect_list.erase(effect_list.begin()+id);
523 	refresh_stats = true;
524 }
525 
removeEffectType(const int type)526 void EffectManager::removeEffectType(const int type) {
527 	for (size_t i=effect_list.size(); i > 0; i--) {
528 		if (effect_list[i-1].type == type) removeEffect(i-1);
529 	}
530 }
531 
removeEffectPassive(size_t id)532 void EffectManager::removeEffectPassive(size_t id) {
533 	for (size_t i=effect_list.size(); i > 0; i--) {
534 		if (effect_list[i-1].passive_id == id) removeEffect(i-1);
535 	}
536 }
537 
removeEffectID(const std::vector<std::pair<std::string,int>> & remove_effects)538 void EffectManager::removeEffectID(const std::vector< std::pair<std::string, int> >& remove_effects) {
539 	for (size_t i = 0; i < remove_effects.size(); i++) {
540 		int count = remove_effects[i].second;
541 		bool remove_all = (count == 0 ? true : false);
542 
543 		for (size_t j = effect_list.size(); j > 0; j--) {
544 			if (!remove_all && count <= 0)
545 				break;
546 
547 			if (effect_list[j-1].id == remove_effects[i].first) {
548 				removeEffect(j-1);
549 				count--;
550 			}
551 		}
552 	}
553 }
554 
clearEffects()555 void EffectManager::clearEffects() {
556 	for (size_t i=effect_list.size(); i > 0; i--) {
557 		removeEffect(i-1);
558 	}
559 
560 	clearStatus();
561 
562 	// clear triggers
563 	triggered_others = triggered_block = triggered_hit = triggered_halfdeath = triggered_joincombat = triggered_death = false;
564 }
565 
clearNegativeEffects(int type)566 void EffectManager::clearNegativeEffects(int type) {
567 	for (size_t i=effect_list.size(); i > 0; i--) {
568 		if ((type == -1 || type == Effect::IMMUNITY_DAMAGE) && effect_list[i-1].type == Effect::DAMAGE)
569 			removeEffect(i-1);
570 		else if ((type == -1 || type == Effect::IMMUNITY_DAMAGE) && effect_list[i-1].type == Effect::DAMAGE_PERCENT)
571 			removeEffect(i-1);
572 		else if ((type == -1 || type == Effect::IMMUNITY_SLOW) && effect_list[i-1].type == Effect::SPEED && effect_list[i-1].magnitude_max < 100)
573 			removeEffect(i-1);
574 		else if ((type == -1 || type == Effect::IMMUNITY_STUN) && effect_list[i-1].type == Effect::STUN)
575 			removeEffect(i-1);
576 		else if ((type == -1 || type == Effect::IMMUNITY_KNOCKBACK) && effect_list[i-1].type == Effect::KNOCKBACK)
577 			removeEffect(i-1);
578 		else if ((type == -1 || type == Effect::IMMUNITY_STAT_DEBUFF) && effect_list[i-1].type > Effect::TYPE_COUNT && effect_list[i-1].magnitude_max < 0)
579 			removeEffect(i-1);
580 	}
581 }
582 
clearItemEffects()583 void EffectManager::clearItemEffects() {
584 	for (size_t i=effect_list.size(); i > 0; i--) {
585 		if (effect_list[i-1].item) removeEffect(i-1);
586 	}
587 }
588 
clearTriggerEffects(int trigger)589 void EffectManager::clearTriggerEffects(int trigger) {
590 	for (size_t i=effect_list.size(); i > 0; i--) {
591 		if (effect_list[i-1].trigger > -1 && effect_list[i-1].trigger == trigger) removeEffect(i-1);
592 	}
593 }
594 
damageShields(int dmg)595 int EffectManager::damageShields(int dmg) {
596 	int over_dmg = dmg;
597 
598 	for (unsigned i=0; i<effect_list.size(); i++) {
599 		if (effect_list[i].magnitude_max > 0 && effect_list[i].type == Effect::SHIELD) {
600 			effect_list[i].magnitude -= over_dmg;
601 			if (effect_list[i].magnitude < 0) {
602 				over_dmg = abs(effect_list[i].magnitude);
603 				effect_list[i].magnitude = 0;
604 			}
605 			else {
606 				return 0;
607 			}
608 		}
609 	}
610 
611 	return over_dmg;
612 }
613 
isDebuffed()614 bool EffectManager::isDebuffed() {
615 	for (size_t i=effect_list.size(); i > 0; i--) {
616 		if (effect_list[i-1].type == Effect::DAMAGE) return true;
617 		else if (effect_list[i-1].type == Effect::DAMAGE_PERCENT) return true;
618 		else if (effect_list[i-1].type == Effect::SPEED && effect_list[i-1].magnitude_max < 100) return true;
619 		else if (effect_list[i-1].type == Effect::STUN) return true;
620 		else if (effect_list[i-1].type == Effect::KNOCKBACK) return true;
621 		else if (effect_list[i-1].type > Effect::TYPE_COUNT && effect_list[i-1].magnitude_max < 0) return true;
622 	}
623 	return false;
624 }
625 
getCurrentColor(Color & color_mod)626 void EffectManager::getCurrentColor(Color& color_mod) {
627 	uint32_t default_color = color_mod.encodeRGBA();
628 	uint32_t no_color = Color(255, 255, 255).encodeRGBA();
629 
630 	for (size_t i=effect_list.size(); i > 0; i--) {
631 		Effect& ei = effect_list[i-1];
632 		if (ei.color_mod == no_color)
633 			continue;
634 
635 		if (ei.color_mod != default_color) {
636 			color_mod.decodeRGBA(ei.color_mod);
637 			return;
638 		}
639 	}
640 }
641 
getCurrentAlpha(uint8_t & alpha_mod)642 void EffectManager::getCurrentAlpha(uint8_t& alpha_mod) {
643 	uint8_t default_alpha = alpha_mod;
644 	uint8_t no_alpha = 255;
645 
646 	for (size_t i=effect_list.size(); i > 0; i--) {
647 		Effect& ei = effect_list[i-1];
648 		if (ei.alpha_mod == no_alpha)
649 			continue;
650 
651 		if (ei.alpha_mod != default_alpha) {
652 			alpha_mod = ei.alpha_mod;
653 			return;
654 		}
655 	}
656 }
657 
hasEffect(const std::string & id,int req_count)658 bool EffectManager::hasEffect(const std::string& id, int req_count) {
659 	if (req_count <= 0)
660 		return false;
661 
662 	int count = 0;
663 
664 	for (size_t i=effect_list.size(); i > 0; i--) {
665 		if (effect_list[i-1].id == id)
666 			count++;
667 	}
668 
669 	return count >= req_count;
670 }
671 
getAttackSpeed(const std::string & anim_name)672 float EffectManager::getAttackSpeed(const std::string& anim_name) {
673 	float attack_speed = 100;
674 
675 	for (size_t i = 0; i < effect_list.size(); ++i) {
676 		if (effect_list[i].type != Effect::ATTACK_SPEED)
677 			continue;
678 
679 		if (effect_list[i].attack_speed_anim.empty() || effect_list[i].attack_speed_anim == anim_name) {
680 			attack_speed = (static_cast<float>(effect_list[i].magnitude) * attack_speed) / 100.0f;
681 		}
682 	}
683 
684 	return attack_speed;
685 }
686 
getDamageSourceType(int dmg_mode)687 int EffectManager::getDamageSourceType(int dmg_mode) {
688 	if (!(dmg_mode == Effect::DAMAGE || dmg_mode == Effect::DAMAGE_PERCENT))
689 		return -1;
690 
691 	int source_type = Power::SOURCE_TYPE_NEUTRAL;
692 
693 	for (size_t i = 0; i < effect_list.size(); ++i) {
694 		Effect& ei = effect_list[i];
695 		if (ei.type == dmg_mode) {
696 			// anything other than ally source type take precedence, so we can return early
697 			if (ei.source_type != Power::SOURCE_TYPE_ALLY)
698 				return ei.source_type;
699 
700 			source_type = ei.source_type;
701 		}
702 	}
703 
704 	return source_type;
705 }
706