1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19 
20 #include "../headers.h"
21 
22 #include "../audio/audio.h"
23 #include "../collisions.h"
24 #include "../custom_actions.h"
25 #include "../entity.h"
26 #include "../game.h"
27 #include "../graphics/animation.h"
28 #include "../hud.h"
29 #include "../system/error.h"
30 #include "../system/properties.h"
31 #include "../system/random.h"
32 
33 extern Entity *self;
34 
35 static void walk(void);
36 static void entityWait(void);
37 static void changeWalkDirectionStart(void);
38 static void changeWalkDirection(void);
39 static void changeWalkDirectionFinish(void);
40 static void electrifyStart(void);
41 static void electrify(void);
42 static void electrifyFinish(void);
43 static void createElectricity(void);
44 static void takeDamage(Entity *, int);
45 static void doElectricity(void);
46 static void creditsMove(void);
47 static void die(void);
48 
addLightningTortoise(int x,int y,char * name)49 Entity *addLightningTortoise(int x, int y, char *name)
50 {
51 	Entity *e = getFreeEntity();
52 
53 	if (e == NULL)
54 	{
55 		showErrorAndExit("No free slots to add a Lightning Tortoise");
56 	}
57 
58 	loadProperties(name, e);
59 
60 	e->x = x;
61 	e->y = y;
62 
63 	e->action = &walk;
64 
65 	e->draw = &drawLoopingAnimationToMap;
66 	e->touch = &entityTouch;
67 	e->die = ¨
68 	e->takeDamage = &takeDamage;
69 	e->reactToBlock = &changeDirection;
70 
71 	e->creditsAction = &creditsMove;
72 
73 	e->type = ENEMY;
74 
75 	setEntityAnimation(e, "STAND");
76 
77 	return e;
78 }
79 
walk()80 static void walk()
81 {
82 	moveLeftToRight();
83 
84 	self->thinkTime--;
85 
86 	if (self->thinkTime <= 0)
87 	{
88 		self->dirX = 0;
89 
90 		if (prand() % 5 == 0)
91 		{
92 			self->action = &changeWalkDirectionStart;
93 		}
94 
95 		else
96 		{
97 			self->thinkTime = 60;
98 
99 			self->action = &electrifyStart;
100 		}
101 	}
102 }
103 
changeWalkDirectionStart()104 static void changeWalkDirectionStart()
105 {
106 	setEntityAnimation(self, "CUSTOM_1");
107 
108 	self->action = &entityWait;
109 
110 	self->animationCallback = &changeWalkDirection;
111 
112 	self->thinkTime = 60;
113 
114 	checkToMap(self);
115 }
116 
changeWalkDirection()117 static void changeWalkDirection()
118 {
119 	self->thinkTime--;
120 
121 	self->action = &changeWalkDirection;
122 
123 	setEntityAnimation(self, "CUSTOM_3");
124 
125 	if (self->thinkTime <= 0)
126 	{
127 		self->face = self->face == LEFT ? RIGHT : LEFT;
128 
129 		self->frameSpeed = -1;
130 
131 		setEntityAnimation(self, "CUSTOM_1");
132 
133 		self->animationCallback = &changeWalkDirectionFinish;
134 
135 		self->action = &entityWait;
136 	}
137 
138 	checkToMap(self);
139 }
140 
changeWalkDirectionFinish()141 static void changeWalkDirectionFinish()
142 {
143 	self->frameSpeed = 1;
144 
145 	setEntityAnimation(self, "STAND");
146 
147 	self->action = &walk;
148 
149 	self->dirX = self->face == LEFT ? -self->speed : self->speed;
150 
151 	self->thinkTime = 120 + prand() % 120;
152 
153 	checkToMap(self);
154 }
155 
entityWait()156 static void entityWait()
157 {
158 	checkToMap(self);
159 }
160 
electrifyStart()161 static void electrifyStart()
162 {
163 	self->dirX = 0;
164 
165 	self->frameSpeed = 0;
166 
167 	self->thinkTime--;
168 
169 	if (self->thinkTime <= 0)
170 	{
171 		self->frameSpeed = 1;
172 
173 		setEntityAnimation(self, "CUSTOM_1");
174 
175 		self->animationCallback = &createElectricity;
176 	}
177 
178 	checkToMap(self);
179 }
180 
createElectricity()181 static void createElectricity()
182 {
183 	Entity *e = getFreeEntity();
184 
185 	if (e == NULL)
186 	{
187 		showErrorAndExit("No free slots to add Tortoise electricity");
188 	}
189 
190 	loadProperties("enemy/tortoise_electricity", e);
191 
192 	playSoundToMap("sound/enemy/tortoise/tortoise_electric", -1, self->x, self->y, 0);
193 
194 	setEntityAnimation(e, "STAND");
195 
196 	e->action = &doElectricity;
197 
198 	e->creditsAction = &doElectricity;
199 
200 	e->touch = &entityTouch;
201 
202 	e->takeDamage = &takeDamage;
203 
204 	e->draw = &drawLoopingAnimationToMap;
205 
206 	e->target = self;
207 
208 	e->face = self->face;
209 
210 	e->x = self->x;
211 
212 	e->y = self->y;
213 
214 	self->target = e;
215 
216 	self->frameSpeed = 1;
217 
218 	setEntityAnimation(self, "ATTACK_1");
219 
220 	self->action = &electrify;
221 
222 	self->creditsAction = &electrify;
223 
224 	self->thinkTime = 120;
225 }
226 
electrify()227 static void electrify()
228 {
229 	self->thinkTime--;
230 
231 	self->element = LIGHTNING;
232 
233 	if (self->thinkTime <= 0)
234 	{
235 		self->frameSpeed = -1;
236 
237 		setEntityAnimation(self, "CUSTOM_1");
238 
239 		self->animationCallback = &electrifyFinish;
240 
241 		self->action = &entityWait;
242 
243 		self->creditsAction = &entityWait;
244 
245 		self->element = NO_ELEMENT;
246 
247 		self->target->inUse = FALSE;
248 	}
249 
250 	checkToMap(self);
251 }
252 
electrifyFinish()253 static void electrifyFinish()
254 {
255 	setEntityAnimation(self, "STAND");
256 
257 	self->frameSpeed = 1;
258 
259 	self->action = &walk;
260 
261 	self->creditsAction = &creditsMove;
262 
263 	self->dirX = self->face == LEFT ? -self->speed : self->speed;
264 
265 	self->thinkTime = 120 + prand() % 120;
266 
267 	checkToMap(self);
268 }
269 
takeDamage(Entity * other,int damage)270 static void takeDamage(Entity *other, int damage)
271 {
272 	Entity *temp;
273 
274 	if (damage != 0)
275 	{
276 		if (self->element == NO_ELEMENT)
277 		{
278 			if (other->element == LIGHTNING)
279 			{
280 				if (self->flags & INVULNERABLE)
281 				{
282 					return;
283 				}
284 
285 				if (damage != 0)
286 				{
287 					self->health += damage;
288 
289 					if (other->type == PROJECTILE)
290 					{
291 						other->target = self;
292 					}
293 
294 					setCustomAction(self, &flashWhite, 6, 0, 0);
295 
296 					/* Don't make an enemy invulnerable from a projectile hit, allows multiple hits */
297 
298 					if (other->type != PROJECTILE)
299 					{
300 						setCustomAction(self, &invulnerableNoFlash, HIT_INVULNERABLE_TIME, 0, 0);
301 					}
302 
303 					if (self->pain != NULL)
304 					{
305 						self->pain();
306 					}
307 
308 					if (prand() % 5 == 0)
309 					{
310 						setInfoBoxMessage(90, 255, 255, 255, _("The damage from this weapon is being absorbed..."));
311 					}
312 				}
313 			}
314 
315 			else
316 			{
317 				entityTakeDamageNoFlinch(other, damage);
318 			}
319 		}
320 
321 		else
322 		{
323 			if (other->type == WEAPON)
324 			{
325 				/* Damage the player instead */
326 
327 				temp = self;
328 
329 				self = other->parent;
330 
331 				self->takeDamage(temp, temp->damage);
332 
333 				self = temp;
334 
335 				return;
336 			}
337 
338 			else if (other->type == PROJECTILE)
339 			{
340 				self->health -= damage;
341 
342 				other->target = self;
343 			}
344 
345 			if (self->health > 0)
346 			{
347 				setCustomAction(self, &flashWhite, 6, 0, 0);
348 				setCustomAction(self, &invulnerableNoFlash, HIT_INVULNERABLE_TIME, 0, 0);
349 
350 				if (self->pain != NULL)
351 				{
352 					self->pain();
353 				}
354 			}
355 
356 			else
357 			{
358 				self->damage = 0;
359 
360 				increaseKillCount();
361 
362 				self->die();
363 			}
364 		}
365 
366 		if (other->type == PROJECTILE)
367 		{
368 			temp = self;
369 
370 			self = other;
371 
372 			self->die();
373 
374 			self = temp;
375 		}
376 	}
377 }
378 
die()379 static void die()
380 {
381 	playSoundToMap("sound/enemy/tortoise/tortoise_die", -1, self->x, self->y, 0);
382 
383 	entityDie();
384 }
385 
doElectricity()386 static void doElectricity()
387 {
388 	if (self->target->health <= 0)
389 	{
390 		self->inUse = FALSE;
391 	}
392 
393 	self->x = self->target->x + self->target->w / 2 - self->w / 2;
394 	self->y = self->target->y + self->target->h - self->h;
395 }
396 
creditsMove()397 static void creditsMove()
398 {
399 	self->mental++;
400 
401 	setEntityAnimation(self, "STAND");
402 
403 	self->dirX = self->speed;
404 
405 	checkToMap(self);
406 
407 	if (self->dirX == 0)
408 	{
409 		self->inUse = FALSE;
410 	}
411 
412 	if (self->mental != 0 && (self->mental % 300) == 0)
413 	{
414 		self->thinkTime = 60;
415 
416 		self->creditsAction = &electrifyStart;
417 	}
418 }
419