/* Copyright (C) 2004 Parallel Realities This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "trains.h" /** * Opens a door and converts it into a normal door (TR_DOOR) * at the same time. Uses up keys if needed * @param train The door to open. */ void openDoor(Train *train) { if (train->active) { return; } switch (train->type) { case TR_GOLD_DOOR: case TR_GOLD_SLIDEDOOR: engine.setInfoMessage("Used Gold Key", 1, INFO_NORMAL); break; case TR_SILVER_DOOR: case TR_SILVER_SLIDEDOOR: engine.setInfoMessage("Used Silver Key", 1, INFO_NORMAL); break; case TR_BRONZE_DOOR: case TR_BRONZE_SLIDEDOOR: engine.setInfoMessage("Used Bronze Key", 1, INFO_NORMAL); break; } if ((train->type != TR_LOCKED_DOOR) && (train->type != TR_LOCKED_SLIDEDOOR)) { if (train->type < TR_SLIDEDOOR) { train->type = TR_DOOR; } else { train->type = TR_SLIDEDOOR; } } train->active = true; audio.playSound(SND_OPENDOOR, CH_TOUCH); } /** * Blocks an entity from moving any further and shows * a message for the reason the door would not open * @param ent The entity to block * @param message The message to show when blocking the Player * @param train The door that performs the blocking * @param dir The direction the entity was moving in (required for clipping) */ void trainBlockEntity(Entity *ent, char *message, Train *train, int dir) { if (ent == &player) { if ((train->isReady()) && (!train->active)) { engine.setInfoMessage(message, 1, INFO_NORMAL); audio.playSound(SND_LOCKEDDOOR, CH_TOUCH); } } if ((ent->flags & ENT_BULLET) && (!(ent->flags & ENT_BOUNCES))) { if (dir == 0) { if (ent->dx < 0) ent->x = train->x + train->sprite->image[0]->w; if (ent->dx > 0) ent->x = train->x - ent->width; } } if (dir == 1) { if ((ent->dy >= 0) && (train->type >= TR_SLIDEDOOR)) { ent->dy = 0; ent->falling = false; } } } /** * Checks to see if an entity has touched this train. Depending on * the trains status certain other functions will be invoked * @param ent The entity to test * @param dir The direction the entity was moving in * @return Whether a collision took place */ bool checkTrainContact(Entity *ent, int dir) { Train *train = (Train*)map.trainList.getHead(); bool collision = false; int x, y, mapAttribute; while (train->next != NULL) { train = (Train*)train->next; if (dir == 0) { collision = (Collision::collision(ent->x + ent->dx, ent->y, ent->width, ent->height - 1, train->x, train->y, train->width, train->height)); } else { collision = (Collision::collision(ent->x, ent->y + ent->dy, ent->width, ent->height - 1, train->x, train->y, train->width, train->height)); } if (collision) { switch (train->type) { case TR_TRAIN: if (ent->flags & ENT_BULLET) { return true; } if (ent->flags & ENT_FLIES) { return false; } if ((ent == &player) && (train->waitsForPlayer())) { train->active = true; } x = (int)(ent->x + ent->dx) >> BRICKSHIFT; y = (int)(ent->y + ent->height - 1) >> BRICKSHIFT; mapAttribute = map.data[x][y]; evaluateMapAttribute(ent, mapAttribute); if (ent->dy >= 0) { if (train->active) { if (!map.isIceLevel) { ent->x -= train->getDX(); } } ent->dy = 1; ent->y = train->y; ent->y -= ent->height; ent->falling = false; } break; case TR_DOOR: case TR_SLIDEDOOR: if (!(ent->flags & ENT_BULLET)) { openDoor(train); } if (dir == 1) { ent->dy = 0; ent->falling = false; } return true; break; case TR_LOCKED_DOOR: case TR_LOCKED_SLIDEDOOR: trainBlockEntity(ent, "Door is locked", train, dir); return true; break; case TR_GOLD_DOOR: case TR_GOLD_SLIDEDOOR: if ((ent == &player) && (carryingItem("Gold Key"))) { openDoor(train); } else { trainBlockEntity(ent, "Gold Key Required", train, dir); } return true; break; case TR_SILVER_DOOR: case TR_SILVER_SLIDEDOOR: if ((ent == &player) && (carryingItem("Silver Key"))) { openDoor(train); } else { trainBlockEntity(ent, "Silver Key Required", train, dir); } return true; break; case TR_BRONZE_DOOR: case TR_BRONZE_SLIDEDOOR: if ((ent == &player) && (carryingItem("Bronze Key"))) { openDoor(train); } else { trainBlockEntity(ent, "Bronze Key Required", train, dir); } return true; break; } } } return false; } /** * Lazy way of setting the sprite for the train * @param train The train to set the Sprite for */ void setTrainSprite(Train *train) { switch (train->type) { case TR_TRAIN: train->sprite = graphics.getSprite("Platform", true); break; case TR_DOOR: case TR_LOCKED_DOOR: train->sprite = graphics.getSprite("NormalDoor", true); break; case TR_GOLD_DOOR: train->sprite = graphics.getSprite("GoldDoor", true); break; case TR_SILVER_DOOR: train->sprite = graphics.getSprite("SilverDoor", true); break; case TR_BRONZE_DOOR: train->sprite = graphics.getSprite("BronzeDoor", true); break; case TR_SLIDEDOOR: case TR_LOCKED_SLIDEDOOR: train->sprite = graphics.getSprite("SlideDoor", true); break; case TR_GOLD_SLIDEDOOR: train->sprite = graphics.getSprite("GoldSlideDoor", true); break; case TR_SILVER_SLIDEDOOR: train->sprite = graphics.getSprite("SilverSlideDoor", true); break; case TR_BRONZE_SLIDEDOOR: train->sprite = graphics.getSprite("BronzeSlideDoor", true); break; } } /** * Checks to see if a door has attempted to open or close on the Player * or an enemy * @param train The door to perform the check on * @return Whether an entity was in the path of the door */ bool doorClosedOnEntity(Train *train) { // allow entities to stand on an horizontal moving door without blocking its movement. int y = (train->type < TR_SLIDEDOOR) ? (int)train->y : (int)train->y + 1; if (Collision::collision(player.x, player.y, player.width, player.height, train->x, y, train->width, train->height)) { return true; } Entity *enemy = (Entity*)map.enemyList.getHead(); while (enemy->next != NULL) { enemy = (Entity*)enemy->next; if (Collision::collision(enemy->x, enemy->y, enemy->width, enemy->height, train->x, y, train->width, train->height)) { return true; } } return false; } /** * Peforms actions for all the trains on the level. Moves them, etc. */ void doTrains() { Train *train = (Train*)map.trainList.getHead(); int x, y, oldX, oldY; int playSound = false; while (train->next != NULL) { train = (Train*)train->next; x = (int)(train->x - engine.playerPosX); y = (int)(train->y - engine.playerPosY); if ((train->type == TR_TRAIN) && (train->active)) { train->move(); } if (train->type != TR_TRAIN) { oldX = (int)train->x; oldY = (int)train->y; playSound = false; playSound = train->openClose(); // only check if the door actually moved(!) if ((oldX != (int)train->x) || (oldY != (int)train->y)) { if (doorClosedOnEntity(train)) { train->x = oldX; train->y = oldY; } else if (playSound) { audio.playSound(SND_DOOROPENED, CH_TOUCH); } } } if (train->sprite == NULL) { setTrainSprite(train); } if ((abs(x) <= 800) && (abs(y) <= 600)) { graphics.blit(train->sprite->getCurrentFrame(), x, y, graphics.screen, false); } } }