1 /*
2 Copyright © 2011-2012 Clint Bellanger
3 Copyright © 2012-2016 Justin Jacobs
4 
5 This file is part of FLARE.
6 
7 FLARE is free software: you can redistribute it and/or modify it under the terms
8 of the GNU General Public License as published by the Free Software Foundation,
9 either version 3 of the License, or (at your option) any later version.
10 
11 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along with
16 FLARE.  If not, see http://www.gnu.org/licenses/
17 */
18 
19 /**
20  * class Hazard
21  *
22  * Stand-alone object that can harm the hero or creatures
23  * These are generated whenever something makes any attack
24  */
25 
26 #include "Animation.h"
27 #include "AnimationSet.h"
28 #include "AnimationManager.h"
29 #include "Hazard.h"
30 #include "MapCollision.h"
31 #include "PowerManager.h"
32 #include "RenderDevice.h"
33 #include "SharedResources.h"
34 #include "StatBlock.h"
35 #include "UtilsMath.h"
36 #include "UtilsParsing.h"
37 
38 #include <cmath>
39 
Hazard(MapCollision * _collider)40 Hazard::Hazard(MapCollision *_collider)
41 	: active(true)
42 	, remove_now(false)
43 	, hit_wall(false)
44 	, relative_pos(false)
45 	, sfx_hit_played(false)
46 	, dmg_min(0)
47 	, dmg_max(0)
48 	, crit_chance(0)
49 	, accuracy(0)
50 	, source_type(0)
51 	, base_speed(0)
52 	, lifespan(1)
53 	, animationKind(0)
54 	, delay_frames(0)
55 	, angle(0)
56 	, src_stats(NULL)
57 	, power(NULL)
58 	, power_index(0)
59 	, parent(NULL)
60 	, collider(_collider)
61 	, activeAnimation(NULL)
62 	, animation_name("")
63 {
64 }
65 
Hazard(const Hazard & other)66 Hazard::Hazard(const Hazard& other) {
67 	activeAnimation = NULL;
68 	*this = other;
69 }
70 
operator =(const Hazard & other)71 Hazard& Hazard::operator=(const Hazard& other) {
72 	if (this == &other)
73 		return *this;
74 
75 	active = other.active;
76 	remove_now = other.remove_now;
77 	hit_wall = other.hit_wall;
78 	relative_pos = other.relative_pos;
79 	sfx_hit_played = other.sfx_hit_played;
80 
81 	dmg_min = other.dmg_min;
82 	dmg_max = other.dmg_max;
83 	crit_chance = other.crit_chance;
84 	accuracy = other.accuracy;
85 	source_type = other.source_type;
86 	base_speed = other.base_speed;
87 	lifespan = other.lifespan;
88 	animationKind = other.animationKind;
89 	delay_frames = other.delay_frames;
90 	angle = other.angle;
91 
92 	src_stats = other.src_stats;
93 	power = other.power;
94 	power_index = other.power_index;
95 
96 	pos = other.pos;
97 	speed = other.speed;
98 	pos_offset = other.pos_offset;
99 
100 	parent = other.parent;
101 	children = other.children;
102 
103 	if (!other.animation_name.empty()) {
104 		animation_name = other.animation_name;
105 		loadAnimation(animation_name);
106 	}
107 
108 	collider = other.collider;
109 	entitiesCollided = other.entitiesCollided;
110 
111 	return (*this);
112 }
113 
~Hazard()114 Hazard::~Hazard() {
115 	if (!parent && !children.empty()) {
116 		// make the next child the parent for the existing children
117 		Hazard* new_parent = children[0];
118 		new_parent->parent = NULL;
119 
120 		for (size_t i = 1; i < children.size(); ++i) {
121 			children[i]->parent = new_parent;
122 			new_parent->children.push_back(children[i]);
123 		}
124 
125 		for (size_t i = 0; i < entitiesCollided.size(); ++i) {
126 			new_parent->addEntity(entitiesCollided[i]);
127 		}
128 	}
129 	else if (parent) {
130 		// remove this hazard from the parent's list of children
131 		for (size_t i = 0; i < parent->children.size(); ++i) {
132 			if (parent->children[i] == this) {
133 				parent->children.erase(parent->children.begin() + i);
134 				break;
135 			}
136 		}
137 	}
138 
139 	if (!animation_name.empty()) {
140 		anim->decreaseCount(animation_name);
141 	}
142 
143 	if (activeAnimation) {
144 		delete activeAnimation;
145 	}
146 
147 	anim->cleanUp();
148 }
149 
logic()150 void Hazard::logic() {
151 
152 	// if the hazard is on delay, take no action
153 	if (delay_frames > 0) {
154 		delay_frames--;
155 		return;
156 	}
157 
158 	// handle tickers
159 	if (lifespan > 0) lifespan--;
160 
161 	if (power->expire_with_caster && !src_stats->alive)
162 		lifespan = 0;
163 
164 	if (activeAnimation)
165 		activeAnimation->advanceFrame();
166 
167 	prev_pos = pos;
168 
169 	// handle movement
170 	bool check_collide = false;
171 	if (!(speed.x == 0 && speed.y == 0)) {
172 		pos.x += speed.x;
173 		pos.y += speed.y;
174 		check_collide = true;
175 	}
176 	else if (!(pos_offset.x == 0 && pos_offset.y == 0)) {
177 		pos.x = src_stats->pos.x - pos_offset.x;
178 		pos.y = src_stats->pos.y - pos_offset.y;
179 		check_collide = true;
180 	}
181 	else if (relative_pos) {
182 		pos.x = src_stats->pos.x;
183 		pos.y = src_stats->pos.y;
184 	}
185 
186 	if (check_collide) {
187 		// very simplified collider, could skim around corners
188 		// or even pass through thin walls if speed > tilesize
189 		if (!collider->isValidPosition(pos.x, pos.y, power->movement_type, MapCollision::COLLIDE_NO_ENTITY)) {
190 
191 			hit_wall = true;
192 
193 			if (power->wall_reflect) {
194 				this->reflect();
195 			}
196 			else {
197 				lifespan = 0;
198 				if (collider->isOutsideMap(pos.x, pos.y))
199 					remove_now = true;
200 		    }
201 		}
202 	}
203 }
204 
reflect()205 void Hazard::reflect() {
206   if (!collider->isWall(pos.x - speed.x, pos.y)) {
207     speed.x *= -1;
208 	pos.x += speed.x;
209   }
210   else if (!collider->isWall(pos.x, pos.y - speed.y)) {
211     speed.y *= -1;
212 	pos.y += speed.y;
213   }
214   else {
215     speed.x *= -1;
216 	speed.y *= -1;
217 	pos.x += speed.x;
218 	pos.y += speed.y;
219   }
220 
221   if (power->directional)
222 	animationKind = Utils::calcDirection(pos.x, pos.y, pos.x + speed.x, pos.y + speed.y);
223 }
224 
loadAnimation(const std::string & s)225 void Hazard::loadAnimation(const std::string &s) {
226 	if (!animation_name.empty()) {
227 		anim->decreaseCount(animation_name);
228 	}
229 	if (activeAnimation) {
230 		delete activeAnimation;
231 	}
232 	activeAnimation = NULL;
233 	animation_name = s;
234 	if (animation_name != "") {
235 		anim->increaseCount(animation_name);
236 		AnimationSet *animationSet = anim->getAnimationSet(animation_name);
237 		activeAnimation = animationSet->getAnimation("");
238 	}
239 
240 	anim->cleanUp();
241 }
242 
isDangerousNow()243 bool Hazard::isDangerousNow() {
244 	return active && (delay_frames == 0) &&
245 		   ( (activeAnimation != NULL && activeAnimation->isActiveFrame())
246 			 || activeAnimation == NULL);
247 }
248 
hasEntity(Entity * ent)249 bool Hazard::hasEntity(Entity *ent) {
250 	if (power->multihit) {
251 		return false;
252 	}
253 
254 	if (parent) {
255 		return parent->hasEntity(ent);
256 	}
257 	else {
258 		for(std::vector<Entity*>::iterator it = entitiesCollided.begin(); it != entitiesCollided.end(); ++it)
259 			if(*it == ent) return true;
260 		return false;
261 	}
262 }
263 
addEntity(Entity * ent)264 void Hazard::addEntity(Entity *ent) {
265 	if (parent) {
266 		parent->addEntity(ent);
267 	}
268 	else {
269 		entitiesCollided.push_back(ent);
270 	}
271 }
272 
addRenderable(std::vector<Renderable> & r,std::vector<Renderable> & r_dead)273 void Hazard::addRenderable(std::vector<Renderable> &r, std::vector<Renderable> &r_dead) {
274 	if (delay_frames == 0 && activeAnimation) {
275 		Renderable re = activeAnimation->getCurrentFrame(animationKind);
276 		re.map_pos.x = pos.x;
277 		re.map_pos.y = pos.y;
278 		re.prio = (power->on_floor ? 0 : 2);
279 		(power->on_floor ? r_dead : r).push_back(re);
280 	}
281 }
282 
setAngle(const float & _angle)283 void Hazard::setAngle(const float& _angle) {
284 	angle = _angle;
285 	while (angle >= static_cast<float>(M_PI)*2) angle -= static_cast<float>(M_PI)*2;
286 	while (angle < 0.0) angle += static_cast<float>(M_PI)*2;
287 
288 	speed.x = base_speed * cosf(angle);
289 	speed.y = base_speed * sinf(angle);
290 
291 	if (power->directional)
292 		animationKind = Utils::calcDirection(pos.x, pos.y, pos.x + speed.x, pos.y + speed.y);
293 }
294