bool Game_Character::MakeWay(int x, int y, int d) const { if (d > 3) { int dx = (d == UpRight || d == DownRight) - (d == DownLeft || d == UpLeft); int dy = (d == DownRight || d == DownLeft) - (d == UpRight || d == UpLeft); return ((MakeWay(x, y, dy + 1) && MakeWay(x, y + dy, -dx + 2)) || (MakeWay(x, y, -dx + 2) && MakeWay(x + dx, y, dy + 1))); } return Game_Map::MakeWay(x, y, d, *this); }
void Game_Character::Move(int dir) { int dx = (dir == Right || dir == UpRight || dir == DownRight) - (dir == Left || dir == DownLeft || dir == UpLeft); int dy = (dir == Down || dir == DownRight || dir == DownLeft) - (dir == Up || dir == UpRight || dir == UpLeft); SetDirection(dir); if (!(IsDirectionFixed() || IsFacingLocked() || IsSpinning())) { if (dir > 3) // Diagonal SetSpriteDirection(GetSpriteDirection() % 2 ? -dx + 2 : dy + 1); else SetSpriteDirection(dir); } if (jumping) { jump_plus_x += dx; jump_plus_y += dy; return; } move_failed = !MakeWay(GetX(), GetY(), dir); if (move_failed) { if (!CheckEventTriggerTouch(Game_Map::RoundX(GetX() + dx), Game_Map::RoundY(GetY() + dy))) return; } else { SetX(Game_Map::RoundX(GetX() + dx)); SetY(Game_Map::RoundY(GetY() + dy)); remaining_step = SCREEN_TILE_WIDTH; BeginMove(); } stop_count = 0; max_stop_count = (GetMoveFrequency() > 7) ? 0 : pow(2.0, 9 - GetMoveFrequency()); }
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); }