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