]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/trains.cpp
Prevent a segmentation fault when using the -map option without specifying a map.
[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);
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);
85                 }
86         }
87         
88         if ((ent->flags & ENT_BULLET) && (!(ent->flags & ENT_BOUNCES)))
89         {
90                 if (dir == 0)
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 == 1)
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 == 0)
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
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
134                 if (collision)
135                 {
136                         switch (train->type)
137                         {
138                                 case TR_TRAIN:
139                                         if (ent->flags & ENT_BULLET)
140                                         {
141                                                 return true;
142                                         }
143                                         
144                                         if (ent->flags & ENT_FLIES)
145                                         {
146                                                 return false;
147                                         }
148
149                                         if ((ent == &player) && (train->waitsForPlayer()))
150                                         {
151                                                 train->active = true;
152                                         }
153
154                                         x = (int)(ent->x + ent->dx) >> BRICKSHIFT;
155                                         y = (int)(ent->y + ent->height - 1) >> BRICKSHIFT;
156
157                                         mapAttribute = map.data[x][y];
158
159                                         evaluateMapAttribute(ent, mapAttribute);
160
161                                         if (ent->dy >= 0)
162                                         {
163                                                 if (train->active)
164                                                 {
165                                                         if (!map.isIceLevel)
166                                                         {
167                                                                 ent->x -= train->getDX();
168                                                         }
169                                                 }
170
171                                                 ent->dy = 1;
172
173                                                 ent->y = train->y;
174                                                 ent->y -= ent->height;
175
176                                                 ent->falling = false;
177                                         }
178
179                                         break;
180
181                                 case TR_DOOR:
182                                 case TR_SLIDEDOOR:
183                                         if (!(ent->flags & ENT_BULLET))
184                                         {
185                                                 openDoor(train);
186                                         }
187                                         
188                                         if (dir == 1)
189                                         {
190                                                 ent->dy = 0;
191                                                 ent->falling = false;
192                                         }
193                                         return true;
194                                         break;
195
196                                 case TR_LOCKED_DOOR:
197                                 case TR_LOCKED_SLIDEDOOR:
198                                         trainBlockEntity(ent, "Door is locked", train, dir);
199                                         return true;
200                                         break;
201
202                                 case TR_GOLD_DOOR:
203                                 case TR_GOLD_SLIDEDOOR:
204                                         if ((ent == &player) && (carryingItem("Gold Key")))
205                                         {
206                                                 openDoor(train);
207                                         }
208                                         else
209                                         {
210                                                 trainBlockEntity(ent, "Gold Key Required", train, dir);
211                                         }
212                                         return true;
213                                         break;
214
215                                 case TR_SILVER_DOOR:
216                                 case TR_SILVER_SLIDEDOOR:
217                                         if ((ent == &player) && (carryingItem("Silver Key")))
218                                         {
219                                                 openDoor(train);
220                                         }
221                                         else
222                                         {
223                                                 trainBlockEntity(ent, "Silver Key Required", train, dir);
224                                         }
225                                         return true;
226                                         break;
227
228                                 case TR_BRONZE_DOOR:
229                                 case TR_BRONZE_SLIDEDOOR:
230                                         if ((ent == &player) && (carryingItem("Bronze Key")))
231                                         {
232                                                 openDoor(train);
233                                         }
234                                         else
235                                         {
236                                                 trainBlockEntity(ent, "Bronze Key Required", train, dir);
237                                         }
238                                         return true;
239                                         break;
240                         }
241                 }
242         }
243
244         return false;
245 }
246
247 /**
248 * Lazy way of setting the sprite for the train
249 * @param train The train to set the Sprite for
250 */
251 void setTrainSprite(Train *train)
252 {
253         switch (train->type)
254         {
255                 case TR_TRAIN:
256                         train->sprite = graphics.getSprite("Platform", true);
257                         break;
258                 case TR_DOOR:
259                 case TR_LOCKED_DOOR:
260                         train->sprite = graphics.getSprite("NormalDoor", true);
261                         break;
262                 case TR_GOLD_DOOR:
263                         train->sprite = graphics.getSprite("GoldDoor", true);
264                         break;
265                 case TR_SILVER_DOOR:
266                         train->sprite = graphics.getSprite("SilverDoor", true);
267                         break;
268                 case TR_BRONZE_DOOR:
269                         train->sprite = graphics.getSprite("BronzeDoor", true);
270                         break;
271                 case TR_SLIDEDOOR:
272                 case TR_LOCKED_SLIDEDOOR:
273                         train->sprite = graphics.getSprite("SlideDoor", true);
274                         break;
275                 case TR_GOLD_SLIDEDOOR:
276                         train->sprite = graphics.getSprite("GoldSlideDoor", true);
277                         break;
278                 case TR_SILVER_SLIDEDOOR:
279                         train->sprite = graphics.getSprite("SilverSlideDoor", true);
280                         break;
281                 case TR_BRONZE_SLIDEDOOR:
282                         train->sprite = graphics.getSprite("BronzeSlideDoor", true);
283                         break;
284         }
285 }
286
287 /**
288 * Checks to see if a door has attempted to open or close on the Player
289 * or an enemy
290 * @param train The door to perform the check on
291 * @return Whether an entity was in the path of the door
292 */
293 bool doorClosedOnEntity(Train *train)
294 {
295         // allow entities to stand on an horizontal moving door without blocking its movement.
296         int y = (train->type < TR_SLIDEDOOR) ? (int)train->y : (int)train->y + 1;
297         
298         if (Collision::collision(player.x, player.y, player.width, player.height, train->x, y, train->width, train->height))
299         {
300                 return true;
301         }
302         
303         Entity *enemy = (Entity*)map.enemyList.getHead();
304
305         while (enemy->next != NULL)
306         {
307                 enemy = (Entity*)enemy->next;
308                 
309                 if (Collision::collision(enemy->x, enemy->y, enemy->width, enemy->height, train->x, y, train->width, train->height))
310                 {
311                         return true;
312                 }
313         }
314         
315         return false;
316 }
317
318 /**
319 * Peforms actions for all the trains on the level. Moves them, etc.
320 */
321 void doTrains()
322 {
323         Train *train = (Train*)map.trainList.getHead();
324
325         int x, y, oldX, oldY;
326         int playSound = false;
327
328         while (train->next != NULL)
329         {
330                 train = (Train*)train->next;
331
332                 x = (int)(train->x - engine.playerPosX);
333                 y = (int)(train->y - engine.playerPosY);
334
335                 if ((train->type == TR_TRAIN) && (train->active))
336                 {
337                         train->move();
338                 }
339
340                 if (train->type != TR_TRAIN)
341                 {
342                         oldX = (int)train->x;
343                         oldY = (int)train->y;
344                         playSound = false;
345                         
346                         playSound = train->openClose();
347                         
348                         // only check if the door actually moved(!)
349                         if ((oldX != (int)train->x) || (oldY != (int)train->y))
350                         {
351                                 if (doorClosedOnEntity(train))
352                                 {
353                                         train->x = oldX;
354                                         train->y = oldY;
355                                 }
356                                 else if (playSound)
357                                 {
358                                         audio.playSound(SND_DOOROPENED, CH_TOUCH);
359                                 }
360                         }
361                 }
362
363                 if (train->sprite == NULL)
364                 {
365                         setTrainSprite(train);
366                 }
367
368                 if ((abs(x) <= 800) && (abs(y) <= 600))
369                 {
370                         graphics.blit(train->sprite->getCurrentFrame(), x, y, graphics.screen, false);
371                 }
372         }
373 }