/** * Checks whether a collision occurs between self and other if self * moves from (x,y) to (new_x, new_y) in direction d. * * If other is a tile event, also indicates if the player can use it * as a "bridge" to step across without hitting the underlying tile * layer. */ static CollisionResult TestCollisionDuringMove( int x, int y, int new_x, int new_y, int d, const Game_Character& self, const Game_Event& other ) { if (!other.GetActive()) { return NoCollision; } if (&self == &other) { return NoCollision; } if (other.GetThrough()) { return NoCollision; } if (!other.IsInPosition(x, y) && !other.IsInPosition(new_x, new_y)) { return NoCollision; } if (&self != Main_Data::game_player.get()) { if (other.IsInPosition(new_x, new_y) && (self.IsOverlapForbidden() || other.IsOverlapForbidden())) { return Collision; } } if (other.IsInPosition(new_x, new_y) && self.GetLayer() == other.GetLayer()) { return Collision; } if (other.GetLayer() == RPG::EventPage::Layers_below) { int tile_id = other.GetTileId(); if ((passages_up[tile_id] & Passable::Above) != 0) { return NoCollision; } if (other.IsInPosition(x,y) && (passages_up[tile_id] & DirToMask(d)) != 0) { return CanStepOffCurrentTile; } else if (other.IsInPosition(new_x, new_y) && (passages_up[tile_id] & DirToMask(Game_Character::ReverseDir(d))) != 0) { return CanStepOntoNewTile; } else { return Collision; } } return NoCollision; }
void Game_Screen::ShowBattleAnimation(int animation_id, int target_id, bool global) { data.battleanim_id = animation_id; data.battleanim_target = target_id; data.battleanim_global = global; Game_Character* target = Game_Character::GetCharacter(target_id, target_id); animation.reset(new BattleAnimation(target->GetScreenX(), target->GetScreenY(), &Data::animations[animation_id - 1])); animation->SetVisible(true); // FIXME: target // FIXME: global }
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_Interpreter_Map::CommandEnemyEncounter(RPG::EventCommand const& com) { // code 10710 if (Game_Message::visible) { return false; } Game_Temp::battle_troop_id = ValueOrVariable(com.parameters[0], com.parameters[1]); Game_Character *player = Main_Data::game_player.get(); Game_Battle::SetTerrainId(Game_Map::GetTerrainTag(player->GetX(), player->GetY())); switch (com.parameters[2]) { case 0: Game_Map::SetupBattle(); break; case 1: Game_Temp::battle_background = com.string; if (Player::IsRPG2k()) Game_Temp::battle_formation = 0; else Game_Temp::battle_formation = com.parameters[7]; break; case 2: Game_Battle::SetTerrainId(com.parameters[8]); break; default: return false; } Game_Temp::battle_escape_mode = com.parameters[3]; // 0 disallow, 1 end event processing, 2 victory/escape custom handler Game_Temp::battle_defeat_mode = com.parameters[4]; // 0 game over, 1 victory/defeat custom handler Game_Temp::battle_first_strike = com.parameters[5] != 0; if (Player::IsRPG2k()) Game_Battle::SetBattleMode(0); else Game_Battle::SetBattleMode(com.parameters[6]); // 0 normal, 1 initiative, 2 surround, 3 back attack, 4 pincer Game_Temp::battle_result = Game_Temp::BattleVictory; scene_call = Scene::Battle; SetContinuation(static_cast<ContinuationFunction>(&Game_Interpreter_Map::ContinuationEnemyEncounter)); return false; }
bool Game_Interpreter_Map::CommandFlashSprite(RPG::EventCommand const& com) { // code 11320 int event_id = com.parameters[0]; Color color(com.parameters[1] * 255 / 31, com.parameters[2] * 255 / 31, com.parameters[3] * 255 / 31, com.parameters[4] * 255 / 31); int tenths = com.parameters[5]; bool wait = com.parameters[6] > 0; Game_Character* event = GetCharacter(event_id); if (event != NULL) { event->Flash(color, tenths * DEFAULT_FPS / 10); if (wait) SetupWait(tenths); } return true; }
void Game_Map::ShowBattleAnimation(int animation_id, int target_id, bool global) { const RPG::Animation* anim = ReaderUtil::GetElement(Data::animations, animation_id); if (!anim) { Output::Warning("ShowBattleAnimation: Invalid battle animation ID %d", animation_id); return; } Main_Data::game_data.screen.battleanim_id = animation_id; Main_Data::game_data.screen.battleanim_target = target_id; Main_Data::game_data.screen.battleanim_global = global; Game_Character* chara = Game_Character::GetCharacter(target_id, target_id); if (chara) { chara->SetFlashTimeLeft(0); // Any flash always ends if (global) { animation.reset(new BattleAnimationGlobal(*anim)); } else { animation.reset(new BattleAnimationChara(*anim, *chara)); } } }
// Command control vars bool Game_Interpreter::CommandControlVariables(RPG::EventCommand const& com) { // Code ControlVars int i, value = 0; Game_Actor* actor; Game_Character* character; switch (com.parameters[4]) { case 0: // Constant value = com.parameters[5]; break; case 1: // Var A ops B value = Game_Variables[com.parameters[5]]; break; case 2: // Number of var A ops B value = Game_Variables[Game_Variables[com.parameters[5]]]; break; case 3: // Random between range int a, b; a = max(com.parameters[5], com.parameters[6]); b = min(com.parameters[5], com.parameters[6]); value = rand() % (a-b+1)+b; break; case 4: // Items switch (com.parameters[6]) { case 0: // Number of items posessed value = Game_Party::ItemNumber(com.parameters[5]); break; case 1: // How often the item is equipped value = Game_Party::ItemNumber(com.parameters[5], true); break; } break; case 5: // Hero actor = Game_Actors::GetActor(com.parameters[5]); if (actor != NULL) { switch (com.parameters[6]) { case 0: // Level value = actor->GetLevel(); break; case 1: // Experience value = actor->GetExp(); break; case 2: // Current HP value = actor->GetHp(); break; case 3: // Current MP value = actor->GetSp(); break; case 4: // Max HP value = actor->GetMaxHp(); break; case 5: // Max MP value = actor->GetMaxSp(); break; case 6: // Attack value = actor->GetAtk(); break; case 7: // Defense value = actor->GetDef(); break; case 8: // Intelligence value = actor->GetSpi(); break; case 9: // Agility value = actor->GetAgi(); break; case 10: // Weapon ID value = actor->GetWeaponId(); break; case 11: // Shield ID value = actor->GetShieldId(); break; case 12: // Armor ID value = actor->GetArmorId(); break; case 13: // Helmet ID value = actor->GetHelmetId(); break; case 14: // Accesory ID value = actor->GetAccessoryId(); break; } } break; case 6: // Characters if (com.parameters[6] != 0){ character = GetCharacter(com.parameters[5]); } else { // Special case for Player Map ID character = NULL; value = Game_Map::GetMapId(); } // Other cases if (character != NULL) { switch (com.parameters[6]) { case 1: // X Coordinate value = character->GetX(); break; case 2: // Y Coordinate value = character->GetY(); break; case 3: // TODO Orientation // Needs testing value = character->GetDirection(); break; case 4: // Screen X value = character->GetScreenX(); break; case 5: // Screen Y value = character->GetScreenY(); } } break; case 7: // More switch (com.parameters[5]) { case 0: // Gold value = Game_Party::GetGold(); break; case 1: value = Game_Party::ReadTimer(Game_Party::Timer1); break; case 2: // Number of heroes in party value = Game_Party::GetActors().size(); break; case 3: // Number of saves value = Game_System::GetSaveCount(); break; case 4: // Number of battles value = Game_Party::GetBattleCount(); break; case 5: // Number of wins value = Game_Party::GetWinCount(); break; case 6: // Number of defeats value = Game_Party::GetDefeatCount(); break; case 7: // Number of escapes (aka run away) value = Game_Party::GetRunCount(); break; case 8: // TODO: MIDI play position break; case 9: value = Game_Party::ReadTimer(Game_Party::Timer2); break; } break; default: ; } switch (com.parameters[0]) { case 0: case 1: // Single and Var range for (i = com.parameters[1]; i <= com.parameters[2]; i++) { switch (com.parameters[3]) { case 0: // Assignement Game_Variables[i] = value; break; case 1: // Addition Game_Variables[i] += value; break; case 2: // Subtraction Game_Variables[i] -= value; break; case 3: // Multiplication Game_Variables[i] *= value; break; case 4: // Division if (value != 0) { Game_Variables[i] /= value; } break; case 5: // Module if (value != 0) { Game_Variables[i] %= value; } else { Game_Variables[i] = 0; } } if (Game_Variables[i] > MaxSize) { Game_Variables[i] = MaxSize; } if (Game_Variables[i] < MinSize) { Game_Variables[i] = MinSize; } } break; case 2: switch (com.parameters[3]) { case 0: // Assignement Game_Variables[com.parameters[1]] = value; break; case 1: // Addition Game_Variables[com.parameters[1]] += value; break; case 2: // Subtraction Game_Variables[com.parameters[1]] -= value; break; case 3: // Multiplication Game_Variables[com.parameters[1]] *= value; break; case 4: // Division if (value != 0) { Game_Variables[com.parameters[1]] /= value; } break; case 5: // Module if (value != 0) { Game_Variables[com.parameters[1]] %= value; } } if (Game_Variables[com.parameters[1]] > MaxSize) { Game_Variables[com.parameters[1]] = MaxSize; } if (Game_Variables[com.parameters[1]] < MinSize) { Game_Variables[com.parameters[1]] = MinSize; } } Game_Map::SetNeedRefresh(true); return true; }
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); }
static bool WouldCollide(const Game_Character& self, const Game_Character& other, bool self_conflict) { if (self.GetThrough() || other.GetThrough()) { return false; } if (self.IsFlying() || other.IsFlying()) { return false; } if (!self.IsActive() || !other.IsActive()) { return false; } if (self.GetType() == Game_Character::Event && other.GetType() == Game_Character::Event && (self.IsOverlapForbidden() || other.IsOverlapForbidden())) { return true; } if (other.GetLayer() == RPG::EventPage::Layers_same && self_conflict) { return true; } if (self.GetLayer() == other.GetLayer()) { return true; } return false; }