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 "../graphics/animation.h"
27 #include "../inventory.h"
28 #include "../player.h"
29 #include "../projectile.h"
30 #include "../system/error.h"
31 #include "../system/properties.h"
32 #include "../system/random.h"
33 #include "item.h"
34 #include "key_items.h"
35
36 extern Entity *self;
37 extern Entity player;
38 extern Game game;
39
40 static void respawn(void);
41 static void noTouch(Entity *);
42
addPermanentItem(char * name,int x,int y)43 Entity *addPermanentItem(char *name, int x, int y)
44 {
45 Entity *e = getFreeEntity();
46
47 if (e == NULL)
48 {
49 showErrorAndExit("No free slots to add item %s", name);
50 }
51
52 loadProperties(name, e);
53
54 e->x = x;
55 e->y = y;
56
57 e->action = &doNothing;
58 e->draw = &drawLoopingAnimationToMap;
59 e->die = &entityDie;
60 e->fallout = &itemFallout;
61
62 e->creditsAction = &doNothing;
63
64 if (e->type == HEALTH)
65 {
66 e->touch = &healthTouch;
67 }
68
69 else if (e->type == WEAPON || e->type == SHIELD)
70 {
71 e->touch = &keyItemTouch;
72
73 e->fallout = &keyItemFallout;
74
75 if (e->type == WEAPON)
76 {
77 if (strcmpignorecase("weapon/normal_arrow", e->name) == 0)
78 {
79 e->activate = &setBowAmmo;
80 }
81
82 else if (strcmpignorecase("weapon/flaming_arrow", e->name) == 0)
83 {
84 e->activate = &setBowAmmo;
85 }
86
87 else
88 {
89 e->activate = &setPlayerWeapon;
90 }
91 }
92
93 else
94 {
95 e->activate = &setPlayerShield;
96 }
97 }
98
99 else if ((e->flags & PUSHABLE) || (e->flags & OBSTACLE))
100 {
101 e->touch = &pushEntity;
102
103 e->frameSpeed = 0;
104 }
105
106 else if (e->flags & NO_DRAW)
107 {
108 e->touch = &noTouch;
109 }
110
111 else
112 {
113 e->touch = &keyItemTouch;
114 }
115
116 setEntityAnimation(e, "STAND");
117
118 return e;
119 }
120
addTemporaryItem(char * name,int x,int y,int face,float dirX,float dirY)121 Entity *addTemporaryItem(char *name, int x, int y, int face, float dirX, float dirY)
122 {
123 Entity *e = getFreeEntity();
124
125 if (e == NULL)
126 {
127 showErrorAndExit("No free slots to add item %s", name);
128 }
129
130 loadProperties(name, e);
131
132 e->x = x;
133 e->y = y;
134
135 if (e->thinkTime <= 0)
136 {
137 showErrorAndExit("No valid thinkTime defined for %s", name);
138 }
139
140 e->dirX = dirX;
141 e->dirY = dirY;
142
143 e->face = face;
144
145 e->action = &generalItemAction;
146 e->draw = &drawLoopingAnimationToMap;
147
148 e->fallout = &entityDieNoDrop;
149
150 e->touch = &noTouch;
151
152 e->creditsAction = &generalItemAction;
153
154 switch (e->type)
155 {
156 case HEALTH:
157 e->touch = &healthTouch;
158
159 e->flags |= DO_NOT_PERSIST;
160 break;
161
162 case WEAPON:
163 e->touch = &keyItemTouch;
164
165 e->activate = &setBowAmmo;
166
167 e->flags |= DO_NOT_PERSIST;
168 break;
169
170 default:
171 if (e->element == LIGHTNING)
172 {
173 e->touch = &lightningChargeTouch;
174
175 e->flags |= DO_NOT_PERSIST;
176 }
177
178 else
179 {
180 e->type = TEMP_ITEM;
181 }
182 break;
183 }
184
185 setEntityAnimationByID(e, 0);
186
187 return e;
188 }
189
dropCollectableItem(char * name,int x,int y,int face)190 Entity *dropCollectableItem(char *name, int x, int y, int face)
191 {
192 Entity *e = addTemporaryItem(name, x, y, face, 0, ITEM_JUMP_HEIGHT);
193
194 e->type = ITEM;
195
196 e->touch = &keyItemTouch;
197
198 return e;
199 }
200
dropRandomItem(int x,int y)201 void dropRandomItem(int x, int y)
202 {
203 Entity *e;
204
205 if (prand() % 3 == 0)
206 {
207 addTemporaryItem("item/heart", x, y, RIGHT, 0, ITEM_JUMP_HEIGHT);
208 }
209
210 if (hasBow() == TRUE && prand() % 5 == 0)
211 {
212 e = addTemporaryItem("weapon/normal_arrow", x, y, RIGHT, 0, ITEM_JUMP_HEIGHT);
213
214 e->health = 1 + (prand() % 3);
215 }
216
217 if (hasLightningSword() == TRUE && prand() % 5 == 0)
218 {
219 e = addTemporaryItem("item/lightning_charge", x, y, RIGHT, 0, ITEM_JUMP_HEIGHT);
220
221 e->health = 1 + (prand() % 3);
222 }
223 }
224
generalItemAction()225 void generalItemAction()
226 {
227 self->thinkTime--;
228
229 if ((self->flags & ON_GROUND) || (self->standingOn != NULL))
230 {
231 self->dirX = self->standingOn == NULL ? 0 : self->standingOn->dirX;
232 }
233
234 if (self->thinkTime < 90)
235 {
236 if (self->thinkTime % 3 == 0)
237 {
238 self->flags ^= NO_DRAW;
239 }
240 }
241
242 if (self->thinkTime <= 0)
243 {
244 self->inUse = FALSE;
245 }
246
247 checkToMap(self);
248
249 self->standingOn = NULL;
250 }
251
healthTouch(Entity * other)252 void healthTouch(Entity *other)
253 {
254 if (other->type == PLAYER)
255 {
256 other->health += self->health;
257
258 if (other->health > other->maxHealth)
259 {
260 other->health = other->maxHealth;
261 }
262
263 self->inUse = FALSE;
264 }
265 }
266
lightningChargeTouch(Entity * other)267 void lightningChargeTouch(Entity *other)
268 {
269 if (other->type == PLAYER)
270 {
271 addChargesToWeapon();
272
273 self->inUse = FALSE;
274 }
275 }
276
throwItem(int val)277 void throwItem(int val)
278 {
279 Entity *e;
280
281 if (game.status == IN_GAME && self->thinkTime <= 0 && !(player.flags & BLOCKING) && player.element != WATER)
282 {
283 e = addProjectile(self->name, &player, player.x + (player.face == RIGHT ? player.w : 0), player.y, player.face == LEFT ? -self->speed : self->speed, 0);
284
285 e->y = player.y + (player.h - e->h) / 2;
286
287 e->type = PROJECTILE;
288
289 e->flags |= FLY;
290
291 e->touch = &entityTouch;
292
293 e->damage = self->damage;
294
295 e->parent = &player;
296
297 e->thinkTime = 600;
298
299 self->health--;
300
301 self->thinkTime = 15;
302
303 if (self->health <= 0)
304 {
305 self->inUse = FALSE;
306 }
307
308 playSoundToMap("sound/common/throw", EDGAR_CHANNEL, player.x, player.y, 0);
309 }
310 }
311
itemFallout()312 void itemFallout()
313 {
314 self->thinkTime = 120;
315
316 self->action = &respawn;
317 }
318
respawn()319 static void respawn()
320 {
321 self->thinkTime--;
322
323 checkToMap(self);
324
325 if (self->thinkTime <= 0)
326 {
327 self->x = self->startX;
328 self->y = self->startY;
329
330 setCustomAction(self, &invulnerable, 60, 0, 0);
331
332 self->action = &doNothing;
333 }
334 }
335
noTouch(Entity * other)336 static void noTouch(Entity *other)
337 {
338
339 }
340