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 "../event/global_trigger.h"
27 #include "../event/trigger.h"
28 #include "../graphics/animation.h"
29 #include "../graphics/decoration.h"
30 #include "../hud.h"
31 #include "../item/item.h"
32 #include "../projectile.h"
33 #include "../system/error.h"
34 #include "../system/properties.h"
35 #include "../system/random.h"
36
37 extern Entity *self, player;
38
39 static void lookForPlayer(void);
40 static void skullAttackChargeUp(void);
41 static void skullAttack(void);
42 static void skullAttackFinish(void);
43 static void takeDamage(Entity *, int);
44 static void skullShotMove(void);
45 static void touch(Entity *);
46 static void skullShotReflect(Entity *);
47 static void die(void);
48
addSkullDoor(int x,int y,char * name)49 Entity *addSkullDoor(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 Skull Door");
56 }
57
58 loadProperties(name, e);
59
60 e->x = x;
61 e->y = y;
62
63 e->draw = &drawLoopingAnimationToMap;
64 e->touch = &touch;
65 e->die = ¨
66 e->takeDamage = &takeDamage;
67
68 e->action = &lookForPlayer;
69
70 e->type = ENEMY;
71
72 setEntityAnimation(e, "STAND");
73
74 return e;
75 }
76
lookForPlayer()77 static void lookForPlayer()
78 {
79 if (player.health > 0)
80 {
81 if (collision(self->x + (self->face == LEFT ? -400 : self->w), self->y, 400, self->h, player.x, player.y, player.w, player.h) == 1)
82 {
83 setEntityAnimation(self, "CHARGE");
84
85 self->animationCallback = &skullAttackChargeUp;
86 }
87 }
88
89 checkToMap(self);
90 }
91
skullAttackChargeUp()92 static void skullAttackChargeUp()
93 {
94 setEntityAnimation(self, "CHARGE_WAIT");
95
96 self->thinkTime = 30;
97
98 self->action = &skullAttack;
99 }
100
skullAttack()101 static void skullAttack()
102 {
103 Entity *e;
104
105 self->thinkTime--;
106
107 if (self->thinkTime <= 0)
108 {
109 setEntityAnimation(self, "ATTACK");
110
111 e = addProjectile("enemy/skull_shot", self, self->x, self->y, self->face == LEFT ? -6 : 6, 0);
112
113 playSoundToMap("sound/boss/snake_boss/snake_boss_shot", -1, self->x, self->y, 0);
114
115 if (self->face == LEFT)
116 {
117 e->x = self->x + self->w - e->w - self->offsetX;
118 }
119
120 else
121 {
122 e->x = self->x + self->offsetX;
123 }
124
125 e->y = self->y + self->offsetY;
126
127 e->face = self->face;
128
129 e->action = &skullShotMove;
130
131 e->flags |= FLY;
132
133 e->reactToBlock = &skullShotReflect;
134
135 e->thinkTime = 1200;
136
137 e->mental = 2;
138
139 self->thinkTime = 60;
140
141 self->action = &skullAttackFinish;
142 }
143
144 checkToMap(self);
145 }
146
skullAttackFinish()147 static void skullAttackFinish()
148 {
149 self->thinkTime--;
150
151 if (self->thinkTime <= 0)
152 {
153 setEntityAnimation(self, "STAND");
154
155 self->action = &lookForPlayer;
156 }
157
158 checkToMap(self);
159 }
160
skullShotMove()161 static void skullShotMove()
162 {
163 Entity *e;
164
165 self->dirX = self->face == LEFT ? -fabs(self->dirX) : fabs(self->dirX);
166
167 self->mental--;
168
169 if (self->mental <= 0)
170 {
171 e = addBasicDecoration(self->x, self->y, "decoration/skull_trail");
172
173 if (e != NULL)
174 {
175 e->x = self->face == LEFT ? self->x + self->w - e->w : self->x;
176
177 e->y = self->y + self->h / 2 - e->h / 2;
178
179 e->y += (prand() % 8) * (prand() % 2 == 0 ? 1 : -1);
180
181 e->thinkTime = 15 + prand() % 15;
182
183 e->dirY = (1 + prand() % 10) * (prand() % 2 == 0 ? 1 : -1);
184
185 e->dirY /= 10;
186 }
187
188 self->mental = 2;
189 }
190
191 checkToMap(self);
192
193 if (self->dirX == 0 || self->thinkTime <= 0)
194 {
195 self->inUse = FALSE;
196 }
197 }
198
skullShotReflect(Entity * other)199 static void skullShotReflect(Entity *other)
200 {
201 if (other->element != PHANTASMAL)
202 {
203 self->inUse = FALSE;
204
205 return;
206 }
207
208 if (other->mental <= 7)
209 {
210 self->damage = 50;
211 }
212
213 else if (other->mental <= 15)
214 {
215 self->damage = 30;
216 }
217
218 else if (other->mental <= 30)
219 {
220 self->damage = 20;
221 }
222
223 else
224 {
225 self->damage = 5;
226 }
227
228 self->parent = other;
229
230 self->face = self->face == LEFT ? RIGHT : LEFT;
231 }
232
takeDamage(Entity * other,int damage)233 static void takeDamage(Entity *other, int damage)
234 {
235 Entity *temp;
236
237 if (self->flags & INVULNERABLE)
238 {
239 return;
240 }
241
242 if (other->element == PHANTASMAL)
243 {
244 entityTakeDamageNoFlinch(other, damage);
245 }
246
247 else
248 {
249 playSoundToMap("sound/common/dink", -1, self->x, self->y, 0);
250
251 setCustomAction(self, &invulnerableNoFlash, HIT_INVULNERABLE_TIME, 0, 0);
252
253 if (other->reactToBlock != NULL)
254 {
255 temp = self;
256
257 self = other;
258
259 self->reactToBlock(temp);
260
261 self = temp;
262 }
263
264 if (other->type != PROJECTILE && prand() % 10 == 0)
265 {
266 setInfoBoxMessage(60, 255, 255, 255, _("This weapon is not having any effect..."));
267 }
268
269 damage = 0;
270 }
271 }
272
die()273 static void die()
274 {
275 int i;
276 Entity *e;
277 char name[MAX_VALUE_LENGTH];
278
279 playSoundToMap("sound/common/crumble", -1, self->x, self->y, 0);
280
281 SNPRINTF(name, sizeof(name), "%s_piece", self->name);
282
283 fireTrigger(self->objectiveName);
284
285 fireGlobalTrigger(self->objectiveName);
286
287 for (i=0;i<9;i++)
288 {
289 e = addTemporaryItem(name, self->x, self->y, self->face, 0, 0);
290
291 e->x += (self->w - e->w) / 2;
292 e->y += (self->w - e->w) / 2;
293
294 e->dirX = (prand() % 5) * (prand() % 2 == 0 ? -1 : 1);
295 e->dirY = ITEM_JUMP_HEIGHT + (prand() % ITEM_JUMP_HEIGHT);
296
297 setEntityAnimationByID(e, i);
298
299 e->thinkTime = 60 + (prand() % 180);
300 }
301
302 self->damage = 0;
303
304 if (!(self->flags & INVULNERABLE))
305 {
306 self->touch = &entityTouch;
307
308 self->flags &= ~FLY;
309
310 self->flags |= (DO_NOT_PERSIST|NO_DRAW);
311
312 self->thinkTime = 60;
313
314 setCustomAction(self, &invulnerableNoFlash, 240, 0, 0);
315
316 self->frameSpeed = 0;
317
318 self->action = &standardDie;
319
320 self->damage = 0;
321 }
322 }
323
touch(Entity * other)324 static void touch(Entity *other)
325 {
326 if (!(other->parent != NULL && other->parent == self))
327 {
328 if (other->flags & ATTACKING)
329 {
330 takeDamage(other, other->damage);
331 }
332
333 if (self->inUse == TRUE && self->touch != NULL)
334 {
335 pushEntity(other);
336 }
337 }
338 }
339