diff --git a/src/game_actor.h b/src/game_actor.h index 5627a8731b..c3b34e63d6 100644 --- a/src/game_actor.h +++ b/src/game_actor.h @@ -243,6 +243,13 @@ class Game_Actor final : public Game_Battler { */ StringView GetName() const override; + /** + * Gets actor name from the database. + * + * @return name. + */ + StringView GetOriginalName() const; + /** * Gets actor character sprite filename. * @@ -250,6 +257,13 @@ class Game_Actor final : public Game_Battler { */ StringView GetSpriteName() const override; + /** + * Gets actor character sprite filename from the database. + * + * @return character sprite filename. + */ + StringView GetOriginalSpriteName() const; + /** * Gets actor character sprite index. * @@ -257,11 +271,23 @@ class Game_Actor final : public Game_Battler { */ int GetSpriteIndex() const; + /** + * Gets actor character sprite index from the database. + * + * @return character sprite index. + */ + int GetOriginalSpriteIndex() const; + /** * Gets the transparency level of the actor sprite */ int GetSpriteTransparency() const; + /** + * Gets the transparency level of the actor sprite from the database. + */ + int GetOriginalSpriteTransparency() const; + /** * Gets actor face graphic filename. * @@ -269,6 +295,13 @@ class Game_Actor final : public Game_Battler { */ StringView GetFaceName() const; + /** + * Gets actor face graphic filename from the database. + * + * @return face graphic filename. + */ + StringView GetOriginalFaceName() const; + /** * Gets actor face graphic index. * @@ -276,6 +309,13 @@ class Game_Actor final : public Game_Battler { */ int GetFaceIndex() const; + /** + * Gets actor face graphic index from the database. + * + * @return face graphic index. + */ + int GetOriginalFaceIndex() const; + /** * Gets actor title. * @@ -283,6 +323,13 @@ class Game_Actor final : public Game_Battler { */ StringView GetTitle() const; + /** + * Gets actor title from the database. + * + * @return title. + */ + StringView GetOriginalTitle() const; + /** * Gets actor equipped weapon ID. * @@ -960,6 +1007,10 @@ inline StringView Game_Actor::GetName() const { : StringView(dbActor->name); } +inline StringView Game_Actor::GetOriginalName() const { + return dbActor->name; +} + inline void Game_Actor::SetTitle(const std::string &new_title) { data.title = (new_title != dbActor->title) ? new_title @@ -972,36 +1023,60 @@ inline StringView Game_Actor::GetTitle() const { : StringView(dbActor->title); } +inline StringView Game_Actor::GetOriginalTitle() const { + return dbActor->title; +} + inline StringView Game_Actor::GetSpriteName() const { return (!data.sprite_name.empty()) ? StringView(data.sprite_name) : StringView(dbActor->character_name); } +inline StringView Game_Actor::GetOriginalSpriteName() const { + return dbActor->character_name; +} + inline int Game_Actor::GetSpriteIndex() const { return (!data.sprite_name.empty()) ? data.sprite_id : dbActor->character_index; } +inline int Game_Actor::GetOriginalSpriteIndex() const { + return dbActor->character_index; +} + inline int Game_Actor::GetSpriteTransparency() const { return (!data.sprite_name.empty()) ? data.transparency : (dbActor->transparent ? 3 : 0); } +inline int Game_Actor::GetOriginalSpriteTransparency() const { + return dbActor->transparent ? 3 : 0; +} + inline StringView Game_Actor::GetFaceName() const { return (!data.face_name.empty()) ? StringView(data.face_name) : StringView(dbActor->face_name); } +inline StringView Game_Actor::GetOriginalFaceName() const { + return dbActor->face_name; +} + inline int Game_Actor::GetFaceIndex() const { return (!data.face_name.empty()) ? data.face_id : dbActor->face_index; } +inline int Game_Actor::GetOriginalFaceIndex() const { + return dbActor->face_index; +} + inline int Game_Actor::GetLevel() const { return data.level; } diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 6aafa6b074..4ce1d2092d 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -788,6 +788,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); + case Cmd::Maniac_GetGameInfo: + return CommandManiacGetGameInfo(com); case Cmd::EasyRpg_SetInterpreterFlag: return CommandEasyRpgSetInterpreterFlag(com); case Cmd::EasyRpg_ProcessJson: @@ -1507,20 +1509,20 @@ std::vector Game_Interpreter::GetActors(int mode, int id) { return actors; } -Game_Character* Game_Interpreter::GetCharacter(int event_id) const { +Game_Character* Game_Interpreter::GetCharacter(int event_id, StringView origin) const { if (event_id == Game_Character::CharThisEvent) { event_id = GetThisEventId(); // Is a common event if (event_id == 0) { // With no map parent - Output::Warning("Can't use ThisEvent in common event: Not called from a map event"); + Output::Warning("{}: Can't use ThisEvent in common event: Not called from a map event", origin); return nullptr; } } Game_Character* ch = Game_Character::GetCharacter(event_id, event_id); if (!ch) { - Output::Warning("Unknown event with id {}", event_id); + Output::Warning("{}: Unknown event with id {}", origin, event_id); } return ch; } @@ -2274,7 +2276,7 @@ bool Game_Interpreter::CommandSetVehicleLocation(lcf::rpg::EventCommand const& c bool Game_Interpreter::CommandChangeEventLocation(lcf::rpg::EventCommand const& com) { // Code 10860 int event_id = com.parameters[0]; - Game_Character *event = GetCharacter(event_id); + Game_Character *event = GetCharacter(event_id, "ChangeEventLocation"); if (event != nullptr) { const auto x = ValueOrVariable(com.parameters[1], com.parameters[2]); const auto y = ValueOrVariable(com.parameters[1], com.parameters[3]); @@ -2299,8 +2301,8 @@ bool Game_Interpreter::CommandTradeEventLocations(lcf::rpg::EventCommand const& int event1_id = com.parameters[0]; int event2_id = com.parameters[1]; - Game_Character *event1 = GetCharacter(event1_id); - Game_Character *event2 = GetCharacter(event2_id); + Game_Character *event1 = GetCharacter(event1_id, "TradeEventLocations"); + Game_Character *event2 = GetCharacter(event2_id, "TradeEventLocations"); if (event1 != nullptr && event2 != nullptr) { auto m1 = event1->GetMapId(); @@ -3060,8 +3062,8 @@ bool Game_Interpreter::CommandPlayerVisibility(lcf::rpg::EventCommand const& com bool Game_Interpreter::CommandMoveEvent(lcf::rpg::EventCommand const& com) { // code 11330 int event_id = ValueOrVariableBitfield(com.parameters[2], 2, com.parameters[0]); int repeat = ManiacBitmask(com.parameters[2], 0x1); - - Game_Character* event = GetCharacter(event_id); + + Game_Character* event = GetCharacter(event_id, "MoveEvent"); if (event != NULL) { // If the event is a vehicle in use, push the commands to the player instead if (event_id >= Game_Character::CharBoat && event_id <= Game_Character::CharAirship) @@ -3530,7 +3532,7 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co chara_id = ValueOrVariable(com.parameters[3], chara_id); } - character = GetCharacter(chara_id); + character = GetCharacter(chara_id, "ConditionalBranch"); if (character != NULL) { result = character->GetFacing() == com.parameters[2]; } @@ -3939,7 +3941,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Game_Event* event = static_cast(GetCharacter(evt_id)); + Game_Event* event = static_cast(GetCharacter(evt_id, "CallEvent")); if (!event) { Output::Warning("CallEvent: Can't call non-existent event {}", evt_id); return true; @@ -4071,6 +4073,168 @@ bool Game_Interpreter::CommandOpenVideoOptions(lcf::rpg::EventCommand const& /* return false; } +bool Game_Interpreter::CommandManiacGetGameInfo(lcf::rpg::EventCommand const& com) { + if (!Player::IsPatchManiac()) { + return true; + } + + int event_id; + int var = com.parameters[2]; + + switch (com.parameters[1]) { + case 0: // Get map size + Main_Data::game_variables->Set(var, Game_Map::GetTilesX()); + Main_Data::game_variables->Set(var + 1, Game_Map::GetTilesY()); + break; + case 1: // Get tile info + // FIXME: figure out how 'Tile Info' works + Output::Warning("GetGameInfo: Option 'Tile Info' not implemented."); + break; + case 2: // Get window size + Main_Data::game_variables->Set(var, Player::screen_width); + Main_Data::game_variables->Set(var + 1, Player::screen_height); + break; + case 3: // Get pixel info + // FIXME: figure out how 'Pixel info' works + Output::Warning("GetGameInfo: Option 'Pixel Info' not implemented."); + break; + case 4: // Get command interpreter state + // FIXME: figure out how 'command interpreter state' works + Output::Warning("GetGameInfo: Option 'Command Interpreter State' not implemented."); + break; + case 5: // Get tileset ID + Main_Data::game_variables->Set(var, Game_Map::GetChipset()); + break; + case 6: // Get actor/message face graphic + if (com.parameters[4] == 1) { + // Message + Main_Data::game_strings->Asg(var, Main_Data::game_system->GetMessageFaceName()); + Main_Data::game_variables->Set(com.parameters[3], Main_Data::game_system->GetMessageFaceIndex()); + } else { + // Actor + event_id = ValueOrVariableBitfield(com.parameters[0], 0, com.parameters[5]); + + auto* actor = Main_Data::game_actors->GetActor(event_id); + if (!actor) { + Output::Warning("GetGameInfo: Invalid actor ID {}", event_id); + return true; + } + + if (com.parameters[6] == 1) { + // Dynamic + Main_Data::game_strings->Asg(var, actor->GetFaceName()); + Main_Data::game_variables->Set(com.parameters[3], actor->GetFaceIndex()); + } else { + // Original + Main_Data::game_strings->Asg(var, actor->GetOriginalFaceName()); + Main_Data::game_variables->Set(com.parameters[3], actor->GetOriginalFaceIndex()); + } + } + break; + case 7: { // Get actor/event body graphic + event_id = ValueOrVariableBitfield(com.parameters[0], 0, com.parameters[5]); + auto* character = GetCharacter(event_id, "GetGameInfo"); + if (!character) { + return true; + } + + if (com.parameters[4] == 1) { + // Get event graphic + // Bug: .static 10001 gives current sprite of Player. .dynamic 10001 gives out nothing. + // Bug: Cannot get .static 10005 sprite of self. .dynamic 10005 works however + if (com.parameters[6] == 1) { + // Dynamic + if (event_id == Game_Character::CharPlayer) { + // Return nothing as per Maniac Patch + Main_Data::game_strings->Asg(var, ""); + Main_Data::game_variables->Set(com.parameters[3], 0); + break; + } + Main_Data::game_strings->Asg(var, StringView(character->GetSpriteName())); + Main_Data::game_variables->Set(com.parameters[3], character->GetSpriteIndex()); + break; + } else { + // Static + switch (event_id) { + case Game_Character::CharPlayer: + // Return dynamic player sprite + Main_Data::game_strings->Asg(var, StringView(character->GetSpriteName())); + Main_Data::game_variables->Set(com.parameters[3], character->GetSpriteIndex()); + break; + case Game_Character::CharBoat: + Main_Data::game_strings->Asg(var, StringView(lcf::Data::system.boat_name)); + Main_Data::game_variables->Set(com.parameters[3], lcf::Data::system.boat_index); + break; + case Game_Character::CharShip: + Main_Data::game_strings->Asg(var, StringView(lcf::Data::system.ship_name)); + Main_Data::game_variables->Set(com.parameters[3], lcf::Data::system.ship_index); + break; + case Game_Character::CharAirship: + Main_Data::game_strings->Asg(var, StringView(lcf::Data::system.airship_name)); + Main_Data::game_variables->Set(com.parameters[3], lcf::Data::system.airship_index); + break; + default: { + auto* event = static_cast(character); + auto* page = event->GetActivePage(); + if (page == nullptr) { + // return nothing + Main_Data::game_strings->Asg(var, ""); + Main_Data::game_variables->Set(com.parameters[3], 0); + } else { + Main_Data::game_strings->Asg(var, StringView(page->character_name)); + Main_Data::game_variables->Set(com.parameters[3], page->character_index); + } + } + } + } + } else { + // Get actor graphic + auto* actor = Main_Data::game_actors->GetActor(event_id); + if (!actor) { + Output::Warning("GetGameInfo: Invalid actor ID {}", event_id); + return true; + } + + if (com.parameters[6] == 1) { + // Dynamic + Main_Data::game_strings->Asg(var, actor->GetSpriteName()); + Main_Data::game_variables->Set(com.parameters[3], actor->GetSpriteIndex()); + } else { + // Default one + Main_Data::game_strings->Asg(var, actor->GetOriginalSpriteName()); + Main_Data::game_variables->Set(com.parameters[3], actor->GetOriginalSpriteIndex()); + } + } + break; + } + case 8: + // Screen position + Main_Data::game_variables->Set(var, Game_Map::GetPositionX() >> 4); + Main_Data::game_variables->Set(var + 1, Game_Map::GetPositionY() >> 4); + break; + case 9: + // Screen shake + Main_Data::game_variables->Set(var, Main_Data::game_screen->GetShakeOffsetX()); + Main_Data::game_variables->Set(var + 1, Main_Data::game_screen->GetShakeOffsetY()); + break; + case 10: { + // Current BGM + const auto& bgm = Main_Data::game_system->GetCurrentBGM(); + Main_Data::game_strings->Asg(var, bgm.name); + Main_Data::game_variables->Set(com.parameters[3], bgm.fadein); + Main_Data::game_variables->Set(com.parameters[3] + 1, bgm.volume); + Main_Data::game_variables->Set(com.parameters[3] + 2, bgm.tempo); + Main_Data::game_variables->Set(com.parameters[3] + 3, bgm.balance); + break; + } + } + + Game_Map::SetNeedRefresh(true); + + return true; +} + + bool Game_Interpreter::CommandManiacGetSaveInfo(lcf::rpg::EventCommand const& com) { if (!Player::IsPatchManiac()) { return true; diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 4956ebb44d..04bf3afda3 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -95,7 +95,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext lcf::rpg::SaveEventExecState GetSaveState(); /** @return Game_Character of the passed event_id */ - Game_Character* GetCharacter(int event_id) const override; + Game_Character* GetCharacter(int event_id, StringView origin) const override; /** @return the event_id of the current frame */ int GetCurrentEventId() const; @@ -301,6 +301,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext bool CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com); bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com); bool CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com); + bool CommandManiacGetGameInfo(lcf::rpg::EventCommand const& com); void SetSubcommandIndex(int indent, int idx); uint8_t& ReserveSubcommandIndex(int indent); diff --git a/src/game_interpreter_control_variables.cpp b/src/game_interpreter_control_variables.cpp index 6e08bcb89b..dc49678c02 100644 --- a/src/game_interpreter_control_variables.cpp +++ b/src/game_interpreter_control_variables.cpp @@ -156,7 +156,7 @@ int ControlVariables::Party(int op, int party_idx) { } int ControlVariables::Event(int op, int event_id, const Game_BaseInterpreterContext& interpreter) { - auto character = interpreter.GetCharacter(event_id); + auto character = interpreter.GetCharacter(event_id, "ControlVariables::Event"); if (character) { switch (op) { case 0: @@ -212,8 +212,6 @@ int ControlVariables::Event(int op, int event_id, const Game_BaseInterpreterCont } Output::Warning("ControlVariables::Event: Unknown op {}", op); - } else { - Output::Warning("ControlVariables::Event: Bad event_id {}", event_id); } return 0; diff --git a/src/game_interpreter_map.cpp b/src/game_interpreter_map.cpp index 8984bba26f..b74a1a970f 100644 --- a/src/game_interpreter_map.cpp +++ b/src/game_interpreter_map.cpp @@ -695,7 +695,7 @@ bool Game_Interpreter_Map::CommandShowBattleAnimation(lcf::rpg::EventCommand con bool waiting_battle_anim = com.parameters[2] > 0; bool global = com.parameters[3] > 0; - Game_Character* chara = GetCharacter(evt_id); + Game_Character* chara = GetCharacter(evt_id, "ShowBattleAnimation"); if (chara == NULL) return true; @@ -720,7 +720,7 @@ bool Game_Interpreter_Map::CommandFlashSprite(lcf::rpg::EventCommand const& com) int tenths = com.parameters[5]; bool wait = com.parameters[6] > 0; - Game_Character* event = GetCharacter(event_id); + Game_Character* event = GetCharacter(event_id, "FlashSprite"); if (event != NULL) { event->Flash(r, g, b, p, tenths * DEFAULT_FPS / 10); @@ -901,7 +901,7 @@ bool Game_Interpreter_Map::CommandEasyRpgWaitForSingleMovement(lcf::rpg::EventCo _state.easyrpg_active = false; - Game_Character* chara = GetCharacter(event_id); + Game_Character* chara = GetCharacter(event_id, "EasyRpgWaitForSingleMovement"); if (chara == nullptr) { return true; } diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index cbb11cb17f..42c27990de 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -145,7 +145,7 @@ class Game_BaseInterpreterContext { virtual ~Game_BaseInterpreterContext() {} virtual int GetThisEventId() const = 0; - virtual Game_Character* GetCharacter(int event_id) const = 0; + virtual Game_Character* GetCharacter(int event_id, StringView origin) const = 0; virtual const lcf::rpg::SaveEventExecFrame& GetFrame() const = 0; protected: diff --git a/src/game_strings.h b/src/game_strings.h index a6054985d3..739b8f77c8 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -54,6 +54,9 @@ class Game_Strings { struct Str_Params { int string_id = 0, hex = 0, extract = 0; + + Str_Params(int string_id) : string_id(string_id) {} + Str_Params(int string_id, int hex, int extract) : string_id(string_id), hex(hex), extract(extract) {} }; enum StringEvalMode : std::int8_t { // 4 bits