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 "../entity.h"
25 #include "../graphics/animation.h"
26 #include "../hud.h"
27 #include "../system/error.h"
28 #include "../system/properties.h"
29 #include "target.h"
30
31 extern Entity *self;
32 extern Game game;
33
34 static void autoMove(void);
35 static void entityWait(void);
36 static void findTarget(int);
37 static void entityWait(void);
38 static void setToStart(void);
39 static void moveToTarget(void);
40 static void touch(Entity *);
41 static Target *getLiftTarget(char *, int);
42
addLift(char * name,int startX,int startY,int type)43 Entity *addLift(char *name, int startX, int startY, int type)
44 {
45 Entity *e = getFreeEntity();
46
47 if (e == NULL)
48 {
49 showErrorAndExit("No free slots to add a Lift");
50 }
51
52 loadProperties(name, e);
53
54 e->type = type;
55
56 e->activate = &findTarget;
57
58 e->touch = &touch;
59
60 e->action = &setToStart;
61
62 e->draw = &drawLoopingAnimationToMap;
63
64 e->x = e->startX = startX;
65 e->y = e->startY = startY;
66
67 e->flags |= OBSTACLE;
68
69 setEntityAnimation(e, "STAND");
70
71 return e;
72 }
73
touch(Entity * other)74 static void touch(Entity *other)
75 {
76 int bottomBefore;
77 float dirX;
78 Entity *temp;
79 Target *t;
80
81 /* Test the horizontal movement */
82
83 if (other->type == PROJECTILE)
84 {
85 temp = self;
86
87 self = other;
88
89 self->die();
90
91 self = temp;
92
93 return;
94 }
95
96 else if (other->type == WEAPON && (other->flags & ATTACKING))
97 {
98 return;
99 }
100
101 else if ((other->dirY > 0 || (other->flags & ON_GROUND)) && other->touch != NULL)
102 {
103 /* Trying to move down */
104
105 if (collision(other->x, other->y, other->w, other->h, self->x, self->y, self->w, self->h) == 1)
106 {
107 bottomBefore = other->y + other->h - other->dirY - 1;
108
109 if (abs(bottomBefore - self->y) < self->h - 1)
110 {
111 if (self->dirY < 0)
112 {
113 other->y -= self->dirY;
114
115 other->dirY = self->dirY;
116
117 dirX = other->dirX;
118
119 other->dirX = 0;
120
121 checkToMap(other);
122
123 other->dirX = dirX;
124
125 if (other->dirY == 0)
126 {
127 self->y = other->y + other->h;
128
129 if (self->type == AUTO_LIFT)
130 {
131 self->targetX = (self->targetX == self->endX ? self->startX : self->endX);
132 self->targetY = (self->targetY == self->endY ? self->startY : self->endY);
133 }
134
135 else
136 {
137 self->health--;
138
139 t = getLiftTarget(self->objectiveName, self->health);
140
141 self->targetX = t->x;
142 self->targetY = t->y;
143 }
144 }
145 }
146
147 /* Place the player as close to the solid tile as possible */
148
149 other->y = self->y;
150 other->y -= other->h;
151
152 other->standingOn = self;
153 other->dirY = 0;
154 other->flags |= ON_GROUND;
155
156 if (self->type == MANUAL_LIFT && game.showHints == TRUE && other->type == PLAYER && self->dirY == 0)
157 {
158 setInfoBoxMessage(0, 255, 255, 255, _("Push Up or Down to use this lift"));
159 }
160 }
161 }
162 }
163 }
164
getLiftTarget(char * name,int targetID)165 static Target *getLiftTarget(char *name, int targetID)
166 {
167 char targetName[MAX_VALUE_LENGTH];
168
169 SNPRINTF(targetName, sizeof(targetName), "%s_TARGET_%d", name, targetID);
170
171 /* Search for the lift's target */
172
173 return getTargetByName(targetName);
174 }
175
findTarget(int val)176 static void findTarget(int val)
177 {
178 Target *t;
179
180 if (self->active == TRUE)
181 {
182 if (self->action == &moveToTarget || self->type == AUTO_LIFT)
183 {
184 return;
185 }
186
187 self->health += val;
188
189 if (self->health < 0)
190 {
191 self->health = 0;
192 }
193
194 /* Search for the lift's target */
195
196 t = getLiftTarget(self->objectiveName, self->health);
197
198 if (t != NULL)
199 {
200 if (t->x == (int)self->x && t->y == (int)self->y)
201 {
202 self->action = &entityWait;
203 }
204
205 else
206 {
207 self->targetX = t->x;
208 self->targetY = t->y;
209
210 self->action = &moveToTarget;
211
212 self->mental = playSoundToMap("sound/common/mine_lift", -1, self->x, self->y, 0);
213 }
214 }
215
216 else
217 {
218 self->health -= val;
219 }
220 }
221
222 else
223 {
224 setInfoBoxMessage(60, 255, 255, 255, _("This lift is not active"));
225 }
226 }
227
moveToTarget()228 static void moveToTarget()
229 {
230 /* Allow Manual lifts to continue moving even if they're suddenly inactive */
231
232 if (self->active == TRUE || self->type == MANUAL_LIFT)
233 {
234 if (abs(self->x - self->targetX) > self->speed)
235 {
236 self->dirX = (self->x < self->targetX ? self->speed : -self->speed);
237 }
238
239 else
240 {
241 self->x = self->targetX;
242 }
243
244 if (abs(self->y - self->targetY) > self->speed)
245 {
246 self->dirY = (self->y < self->targetY ? self->speed : -self->speed);
247 }
248
249 else
250 {
251 self->y = self->targetY;
252 }
253
254 if (self->x == self->targetX && self->y == self->targetY)
255 {
256 self->dirX = self->dirY = 0;
257
258 if (self->type == AUTO_LIFT)
259 {
260 self->targetX = (self->targetX == self->endX ? self->startX : self->endX);
261 self->targetY = (self->targetY == self->endY ? self->startY : self->endY);
262
263 self->health = (self->x == self->endX && self->y == self->endY) ? 0 : 1;
264
265 self->thinkTime = self->maxThinkTime;
266
267 self->action = &autoMove;
268 }
269
270 else
271 {
272 stopSound(self->mental);
273
274 self->action = &entityWait;
275 }
276 }
277
278 else
279 {
280 self->x += self->dirX;
281 self->y += self->dirY;
282 }
283 }
284
285 else
286 {
287 self->dirX = self->dirY = 0;
288 }
289 }
290
entityWait()291 static void entityWait()
292 {
293
294 }
295
autoMove()296 static void autoMove()
297 {
298 if (self->active == TRUE)
299 {
300 if (self->thinkTime > 0)
301 {
302 self->thinkTime--;
303 }
304
305 else
306 {
307 self->action = &moveToTarget;
308 }
309 }
310
311 else
312 {
313 self->dirY = self->dirX = 0;
314
315 self->thinkTime = 0;
316 }
317 }
318
setToStart()319 static void setToStart()
320 {
321 char targetName[MAX_VALUE_LENGTH];
322 Target *t;
323
324 self->face = RIGHT;
325
326 if (self->type == AUTO_LIFT)
327 {
328 /* Auto lifts just use their start and end points */
329
330 self->action = &autoMove;
331
332 if (self->health == 1 || (self->x == self->startX && self->y == self->startY))
333 {
334 self->targetX = self->endX;
335 self->targetY = self->endY;
336
337 self->health = 2;
338 }
339
340 else
341 {
342 self->targetX = self->startX;
343 self->targetY = self->startY;
344
345 self->health = 1;
346 }
347
348 if (self->active == FALSE)
349 {
350 self->thinkTime = 0;
351 }
352 }
353
354 else
355 {
356 SNPRINTF(targetName, sizeof(targetName), "%s_TARGET_%d", self->objectiveName, self->health);
357
358 /* Search for the lift's target */
359
360 t = getTargetByName(targetName);
361
362 if (t != NULL)
363 {
364 self->x = t->x;
365 self->y = t->y;
366 }
367
368 else
369 {
370 printf("Could not find target %s for lift %s at %f %f!\n", targetName, self->objectiveName, self->x, self->y);
371
372 addTarget(self->x, self->y, targetName);
373 }
374
375 self->action = &entityWait;
376 }
377 }
378