bool Game_Map::MakeWay(int x, int y, int d, const Game_Character& self, bool force_through) { int new_x = RoundX(x + (d == Game_Character::Right ? 1 : d == Game_Character::Left ? -1 : 0)); int new_y = RoundY(y + (d == Game_Character::Down ? 1 : d == Game_Character::Up ? -1 : 0)); if (!Game_Map::IsValid(new_x, new_y)) return false; if (self.GetThrough() || force_through) return true; // A character can move to a position with an impassable tile by // standing on top of an event below it. These flags track whether // we stepped off an event and therefore don't need to check the // passability of the tile layer below. bool stepped_off_event = false; bool stepped_onto_event = false; for (Game_Event& other : GetEvents()) { CollisionResult result = TestCollisionDuringMove(x, y, new_x, new_y, d, self, other); if (result == Collision) { // Try updating the offending event to give it a chance to move out of the // way and recheck. other.UpdateParallel(); if (TestCollisionDuringMove(x, y, new_x, new_y, d, self, other) == Collision) { return false; } } else if (result == CanStepOffCurrentTile) { stepped_off_event = true; } else if (result == CanStepOntoNewTile) { stepped_onto_event = true; } } if (vehicles[0]->IsInPosition(new_x, new_y) || vehicles[1]->IsInPosition(new_x, new_y)) { return false; } if (Main_Data::game_player->IsInPosition(new_x, new_y) && !Main_Data::game_player->GetThrough() && self.GetLayer() == RPG::EventPage::Layers_same) { // Update the Player to see if they'll move and recheck. Main_Data::game_player->Update(!first_frame); if (Main_Data::game_player->IsInPosition(new_x, new_y)) { return false; } } return (stepped_off_event || IsPassableTile(DirToMask(d), x + y * GetWidth())) && (stepped_onto_event || IsPassableTile(DirToMask(Game_Character::ReverseDir(d)), new_x + new_y * GetWidth())); }
bool Game_Map::IsLandable(int x, int y, const Game_Character *self_event) { if (!Game_Map::IsValid(x, y)) return false; int tile_id; int bit = Passable::Down | Passable::Right | Passable::Left | Passable::Up; if (self_event) { for (Game_Event& ev : events) { if (&ev != self_event && ev.IsInPosition(x, y)) { if (!ev.GetThrough()) { if (ev.GetLayer() == RPG::EventPage::Layers_same) { return false; } else if (ev.GetTileId() >= 0 && ev.GetLayer() == RPG::EventPage::Layers_below) { // Event layer Chipset Tile tile_id = ev.GetTileId(); return (passages_up[tile_id] & bit) != 0; } } } } if (self_event->GetVehicleType() > 0) { return Game_Map::IsPassableVehicle(x, y, (Game_Vehicle::Type) self_event->GetVehicleType()); } } return IsPassableTile(bit, x + y * GetWidth()); }
bool Game_Map::IsPassable(int x, int y, int d, const Game_Character* self_event) { // TODO: this and MakeWay share a lot of code. if (!Game_Map::IsValid(x, y)) return false; int bit = DirToMask(d); int tile_id; if (self_event) { bool pass = false; std::vector<Game_Event*> events; std::vector<Game_Event*>::iterator it; Game_Map::GetEventsXY(events, x, y); for (it = events.begin(); it != events.end(); ++it) { if (*it == self_event || (*it)->GetThrough()) { continue; } if (self_event != Main_Data::game_player.get()) { if (self_event->IsOverlapForbidden() || (*it)->IsOverlapForbidden()) return false; } if ((*it)->GetLayer() == self_event->GetLayer()) { if (self_event->IsInPosition(x, y)) pass = true; else return false; } else if ((*it)->GetLayer() == RPG::EventPage::Layers_below) { // Event layer Chipset Tile tile_id = (*it)->GetTileId(); if ((passages_up[tile_id] & Passable::Above) != 0) continue; if ((passages_up[tile_id] & bit) != 0) pass = true; else return false; } } if (!self_event->IsInPosition(x, y) && (vehicles[0]->IsInPosition(x, y) || vehicles[1]->IsInPosition(x, y))) return false; if (pass) // All events here are passable return true; } return IsPassableTile(bit, x + y * GetWidth()); }
bool Game_Map::CanDisembarkShip(Game_Player& player, int x, int y) { if (!Game_Map::IsValid(x, y)) { return false; } for (auto& ev: GetEvents()) { if (ev.IsInPosition(x, y) && ev.GetLayer() == RPG::EventPage::Layers_same && ev.IsActive()) { return false; } } int bit = GetPassableMask(x, y, player.GetX(), player.GetY()); return IsPassableTile(nullptr, bit, x, y); }
bool Game_Map::CanEmbarkShip(Game_Player& player, int x, int y) { auto bit = GetPassableMask(player.GetX(), player.GetY(), x, y); return IsPassableTile(&player, bit, player.GetX(), player.GetY()); }
bool Game_Map::MakeWay(const Game_Character& self, int x, int y) { // Moving to same tile (used for jumps) always succeeds if (x == self.GetX() && y == self.GetY()) { return true; } if (!self.IsJumping() && x != self.GetX() && y != self.GetY()) { // Handle diagonal stepping. // Must be able to step on at least one of the 2 adjacent tiles and also the target tile. // Verified behavior: Always checks vertical first, only checks horizontal if vertical fails. bool vertical_ok = MakeWay(self, self.GetX(), y); if (!vertical_ok) { bool horizontal_ok = MakeWay(self, x, self.GetY()); if (!horizontal_ok) { return false; } } } // Infer directions before we do any rounding. const auto bit_from = GetPassableMask(self.GetX(), self.GetY(), x, y); const auto bit_to = GetPassableMask(x, y, self.GetX(), self.GetY()); // Now round for looping maps. x = Game_Map::RoundX(x); y = Game_Map::RoundY(y); // Note, even for diagonal, if the tile is invalid we still check vertical/horizontal first! if (!Game_Map::IsValid(x, y)) { return false; } if (self.GetThrough()) { return true; } const auto vehicle_type = static_cast<Game_Vehicle::Type>(self.GetVehicleType()); bool self_conflict = false; if (!self.IsJumping()) { // Check for self conflict. // If this event has a tile graphic and the tile itself has passage blocked in the direction // we want to move, flag it as "self conflicting" for use later. if (self.GetLayer() == RPG::EventPage::Layers_below && self.GetTileId() != 0) { int tile_id = self.GetTileId(); if ((passages_up[tile_id] & bit_from) == 0) { self_conflict = true; } } if (vehicle_type == Game_Vehicle::None) { // Check that we are allowed to step off of the current tile. // Note: Vehicles can always step off a tile. if (!IsPassableTile(&self, bit_from, self.GetX(), self.GetY())) { return false; } } } if (vehicle_type != Game_Vehicle::Airship) { // Check for collision with events on the target tile. for (auto& other: GetEvents()) { if (MakeWayCollideEvent(x, y, self, other, self_conflict)) { return false; } } auto& player = Main_Data::game_player; if (player->GetVehicleType() == Game_Vehicle::None) { if (MakeWayCollideEvent(x, y, self, *Main_Data::game_player, self_conflict)) { return false; } } for (auto vid: { Game_Vehicle::Boat, Game_Vehicle::Ship}) { auto& other = vehicles[vid - 1]; if (other->IsInCurrentMap()) { if (MakeWayCollideEvent(x, y, self, *other, self_conflict)) { return false; } } } auto& airship = vehicles[Game_Vehicle::Airship - 1]; if (airship->IsInCurrentMap() && self.GetType() != Game_Character::Player) { if (MakeWayCollideEvent(x, y, self, *airship, self_conflict)) { return false; } } } int bit = bit_to; if (self.IsJumping()) { bit = Passable::Down | Passable::Up | Passable::Left | Passable::Right; } return IsPassableTile(&self, bit, x, y); }