]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/trains.cpp
Fix throwing grenades from moving platforms.
[quix0rs-blobwars.git] / src / trains.cpp
1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 */
21
22 #include "trains.h"
23
24 /**
25 * Opens a door and converts it into a normal door (TR_DOOR)
26 * at the same time. Uses up keys if needed
27 * @param train The door to open.
28 */
29 void openDoor(Train *train)
30 {
31         if (train->active)
32         {
33                 return;
34         }
35
36         switch (train->type)
37         {
38                 case TR_GOLD_DOOR:
39                 case TR_GOLD_SLIDEDOOR:
40                         engine.setInfoMessage("Used Gold Key", 1, INFO_NORMAL);
41                         break;
42                 case TR_SILVER_DOOR:
43                 case TR_SILVER_SLIDEDOOR:
44                         engine.setInfoMessage("Used Silver Key", 1, INFO_NORMAL);
45                         break;
46                 case TR_BRONZE_DOOR:
47                 case TR_BRONZE_SLIDEDOOR:
48                         engine.setInfoMessage("Used Bronze Key", 1, INFO_NORMAL);
49                         break;
50         }
51
52         if ((train->type != TR_LOCKED_DOOR) && (train->type != TR_LOCKED_SLIDEDOOR))
53         {
54                 if (train->type < TR_SLIDEDOOR)
55                 {
56                         train->type = TR_DOOR;
57                 }
58                 else
59                 {
60                         train->type = TR_SLIDEDOOR;
61                 }
62         }
63
64         train->active = true;
65
66         audio.playSound(SND_OPENDOOR, CH_TOUCH, train->x);
67 }
68
69 /**
70 * Blocks an entity from moving any further and shows
71 * a message for the reason the door would not open
72 * @param ent The entity to block
73 * @param message The message to show when blocking the Player
74 * @param train The door that performs the blocking
75 * @param dir The direction the entity was moving in (required for clipping)
76 */
77 void trainBlockEntity(Entity *ent, const char *message, Train *train, int dir)
78 {
79         if (ent == &player)
80         {
81                 if ((train->isReady()) && (!train->active))
82                 {
83                         engine.setInfoMessage(message, 1, INFO_NORMAL);
84                         audio.playSound(SND_LOCKEDDOOR, CH_TOUCH, train->x);
85                 }
86         }
87         
88         if ((ent->flags & ENT_BULLET) && (!(ent->flags & ENT_BOUNCES)))
89         {
90                 if (dir & DIR_X)
91                 {
92                         if (ent->dx < 0) ent->x = train->x + train->sprite->image[0]->w;
93                         if (ent->dx > 0) ent->x = train->x - ent->width;
94                 }
95         }
96
97         if (dir & DIR_Y)
98         {
99                 if ((ent->dy >= 0) && (train->type >= TR_SLIDEDOOR))
100                 {
101                         ent->dy = 0;
102                         ent->falling = false;
103                 }
104         }
105 }
106
107 /**
108 * Checks to see if an entity has touched this train. Depending on
109 * the trains status certain other functions will be invoked
110 * @param ent The entity to test
111 * @param dir The direction the entity was moving in
112 * @return Whether a collision took place
113 */
114 bool checkTrainContact(Entity *ent, int dir)
115 {
116         Train *train = (Train*)map.trainList.getHead();
117
118         bool collision = false;
119         int x, y, mapAttribute;
120
121         while (train->next != NULL)
122         {
123                 train = (Train*)train->next;
124
125                 if (dir == DIR_X)
126                 {
127                         collision = (Collision::collision(ent->x + ent->dx, ent->y, ent->width, ent->height - 1, train->x, train->y, train->width, train->height));
128                 }
129                 else if (dir == DIR_Y)
130                 {
131                         collision = (Collision::collision(ent->x, ent->y + ent->dy, ent->width, ent->height - 1, train->x, train->y, train->width, train->height));
132                 }
133                 else
134                 {
135                         collision = (Collision::collision(ent->x + ent->dx, ent->y + ent->dy, ent->width, ent->height - 1, train->x, train->y, train->width, train->height));
136                 }
137
138                 if (collision)
139                 {
140                         switch (train->type)
141                         {
142                                 case TR_TRAIN:
143                                         if (ent->flags & ENT_BULLET)
144                                         {
145                                                 return true;
146                                         }
147                                         
148                                         if (ent->flags & ENT_FLIES)
149                                         {
150                                                 return false;
151                                         }
152
153                                         if ((ent == &player) && (train->waitsForPlayer()))
154                                         {
155                                                 train->active = true;
156                                         }
157
158                                         x = (int)(ent->x + ent->dx) >> BRICKSHIFT;
159                                         y = (int)(ent->y + ent->height - 1) >> BRICKSHIFT;
160
161                                         mapAttribute = map.data[x][y];
162
163                                         evaluateMapAttribute(ent, mapAttribute);
164
165                                         if (ent->dy >= 0)
166                                         {
167                                                 if (train->active)
168                                                 {
169                                                         if (!map.isIceLevel)
170                                                         {
171                                                                 ent->x -= train->getDX();
172                                                         }
173                                                 }
174
175                                                 ent->dy = 1;
176
177                                                 ent->y = train->y;
178                                                 ent->y -= ent->height;
179
180                                                 ent->falling = false;
181                                         }
182
183                                         break;
184
185                                 case TR_DOOR:
186                                 case TR_SLIDEDOOR:
187                                         if (!(ent->flags & ENT_BULLET))
188                                         {
189                                                 openDoor(train);
190                                         }
191                                         
192                                         if (dir & DIR_Y)
193                                         {
194                                                 ent->dy = 0;
195                                                 ent->falling = false;
196                                         }
197                                         return true;
198                                         break;
199
200                                 case TR_LOCKED_DOOR:
201                                 case TR_LOCKED_SLIDEDOOR:
202                                         trainBlockEntity(ent, "Door is locked", train, dir);
203                                         return true;
204                                         break;
205
206                                 case TR_GOLD_DOOR:
207                                 case TR_GOLD_SLIDEDOOR:
208                                         if ((ent == &player) && (carryingItem("Gold Key")))
209                                         {
210                                                 openDoor(train);
211                                         }
212                                         else
213                                         {
214                                                 trainBlockEntity(ent, "Gold Key Required", train, dir);
215                                         }
216                                         return true;
217                                         break;
218
219                                 case TR_SILVER_DOOR:
220                                 case TR_SILVER_SLIDEDOOR:
221                                         if ((ent == &player) && (carryingItem("Silver Key")))
222                                         {
223                                                 openDoor(train);
224                                         }
225                                         else
226                                         {
227                                                 trainBlockEntity(ent, "Silver Key Required", train, dir);
228                                         }
229                                         return true;
230                                         break;
231
232                                 case TR_BRONZE_DOOR:
233                                 case TR_BRONZE_SLIDEDOOR:
234                                         if ((ent == &player) && (carryingItem("Bronze Key")))
235                                         {
236                                                 openDoor(train);
237                                         }
238                                         else
239                                         {
240                                                 trainBlockEntity(ent, "Bronze Key Required", train, dir);
241                                         }
242                                         return true;
243                                         break;
244                         }
245                 }
246         }
247
248         return false;
249 }
250
251 /**
252 * Lazy way of setting the sprite for the train
253 * @param train The train to set the Sprite for
254 */
255 void setTrainSprite(Train *train)
256 {
257         switch (train->type)
258         {
259                 case TR_TRAIN:
260                         train->sprite = graphics.getSprite("Platform", true);
261                         break;
262                 case TR_DOOR:
263                 case TR_LOCKED_DOOR:
264                         train->sprite = graphics.getSprite("NormalDoor", true);
265                         break;
266                 case TR_GOLD_DOOR:
267                         train->sprite = graphics.getSprite("GoldDoor", true);
268                         break;
269                 case TR_SILVER_DOOR:
270                         train->sprite = graphics.getSprite("SilverDoor", true);
271                         break;
272                 case TR_BRONZE_DOOR:
273                         train->sprite = graphics.getSprite("BronzeDoor", true);
274                         break;
275                 case TR_SLIDEDOOR:
276                 case TR_LOCKED_SLIDEDOOR:
277                         train->sprite = graphics.getSprite("SlideDoor", true);
278                         break;
279                 case TR_GOLD_SLIDEDOOR:
280                         train->sprite = graphics.getSprite("GoldSlideDoor", true);
281                         break;
282                 case TR_SILVER_SLIDEDOOR:
283                         train->sprite = graphics.getSprite("SilverSlideDoor", true);
284                         break;
285                 case TR_BRONZE_SLIDEDOOR:
286                         train->sprite = graphics.getSprite("BronzeSlideDoor", true);
287                         break;
288         }
289 }
290
291 /**
292 * Checks to see if a door has attempted to open or close on the Player
293 * or an enemy
294 * @param train The door to perform the check on
295 * @return Whether an entity was in the path of the door
296 */
297 bool doorClosedOnEntity(Train *train)
298 {
299         // allow entities to stand on an horizontal moving door without blocking its movement.
300         int y = (train->type < TR_SLIDEDOOR) ? (int)train->y : (int)train->y + 1;
301         
302         if (Collision::collision(player.x, player.y, player.width, player.height, train->x, y, train->width, train->height))
303         {
304                 return true;
305         }
306         
307         Entity *enemy = (Entity*)map.enemyList.getHead();
308
309         while (enemy->next != NULL)
310         {
311                 enemy = (Entity*)enemy->next;
312                 
313                 if (Collision::collision(enemy->x, enemy->y, enemy->width, enemy->height, train->x, y, train->width, train->height))
314                 {
315                         return true;
316                 }
317         }
318         
319         return false;
320 }
321
322 /**
323 * Peforms actions for all the trains on the level. Moves them, etc.
324 */
325 void doTrains()
326 {
327         Train *train = (Train*)map.trainList.getHead();
328
329         int x, y, oldX, oldY;
330         int playSound = false;
331
332         while (train->next != NULL)
333         {
334                 train = (Train*)train->next;
335
336                 x = (int)(train->x - engine.playerPosX);
337                 y = (int)(train->y - engine.playerPosY);
338
339                 if ((train->type == TR_TRAIN) && (train->active))
340                 {
341                         train->move();
342                 }
343
344                 if (train->type != TR_TRAIN)
345                 {
346                         oldX = (int)train->x;
347                         oldY = (int)train->y;
348                         playSound = false;
349                         
350                         playSound = train->openClose();
351                         
352                         // only check if the door actually moved(!)
353                         if ((oldX != (int)train->x) || (oldY != (int)train->y))
354                         {
355                                 if (doorClosedOnEntity(train))
356                                 {
357                                         train->x = oldX;
358                                         train->y = oldY;
359                                 }
360                                 else if (playSound)
361                                 {
362                                         audio.playSound(SND_DOOROPENED, CH_TOUCH, train->x);
363                                 }
364                         }
365                 }
366
367                 if (train->sprite == NULL)
368                 {
369                         setTrainSprite(train);
370                 }
371
372                 if ((abs(x) <= 800) && (abs(y) <= 600))
373                 {
374                         graphics.blit(train->sprite->getCurrentFrame(), x, y, graphics.screen, false);
375                 }
376         }
377 }