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