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