From 72cd305a5cb0c20f691a2f05c3af84fab4c62ab3 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Thu, 22 Jun 2023 00:01:39 -0300 Subject: [PATCH 1/4] Call Movement Action + Wait for Single Movement( can Detect fail cases) Call Movement Action: `@raw 2050, "SetMoveSpeed", 0, 10001, 1, 3` @2050("typeOfAction",[targetIsVar,target, parameterIsvar, parameter]) ```js @raw 2051, "", 0, 10001 //Wait for Single Movement @raw 20140, "Fails to move x times", 8 // start fail branch @raw 10 //empty space for more cmds @raw 20141 // end of fail branch ``` ------------------------------- old commits comments: Update Feature - Support Detecting when Move Route Fails Thanks @MackValentine Mack for solving some Core Issues --- src/game_character.cpp | 17 ++++++++ src/game_character.h | 3 ++ src/game_interpreter.cpp | 85 ++++++++++++++++++++++++++++++++++++++++ src/game_interpreter.h | 2 + 4 files changed, 107 insertions(+) diff --git a/src/game_character.cpp b/src/game_character.cpp index 8c9150db36..b1e7e12dfe 100644 --- a/src/game_character.cpp +++ b/src/game_character.cpp @@ -797,6 +797,23 @@ void Game_Character::CancelMoveRoute() { SetMoveRouteFinished(false); } +bool Game_Character::isStuck(int i) { + if(i == 0 ) i =1; + int currentPose = 500; + if (GetMoveRouteIndex() < GetMoveRoute().move_commands.size()) { + if (IsStopping() && GetMoveRoute().move_commands[GetMoveRouteIndex()].command_id < 12) { + failsMove++; + } else { + failsMove = 0; + } + } else { + failsMove = 0; + } + + //Output::Warning("stuck? - {} {} {} -- {}", failsMove, IsPaused(), i, GetMaxStopCount()); + return failsMove > i && (GetStopCount() == 0 || GetStopCount() > GetMaxStopCount()); +} + int Game_Character::GetSpriteX() const { int x = GetX() * SCREEN_TILE_SIZE; diff --git a/src/game_character.h b/src/game_character.h index 64dcf2e4d8..98c80ee830 100644 --- a/src/game_character.h +++ b/src/game_character.h @@ -882,6 +882,9 @@ class Game_Character { static constexpr int GetDxFromDirection(int dir); static constexpr int GetDyFromDirection(int dir); + int failsMove = 0; + bool isStuck(int i); + protected: explicit Game_Character(Type type, lcf::rpg::SaveMapEventBase* d); /** Check for and fix incorrect data after loading save game */ diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index acb5227440..44be1392de 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -825,6 +825,10 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); + case static_cast(2050): //Cmd::EasyRpg_CallMovement + return CommandCallMovement(com); + case static_cast(2051): //Cmd::EasyRpg_WaitForMovement + return CommandWaitForMovement(com); default: return true; } @@ -5052,6 +5056,87 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co return true; } +bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { + // CommandSetMovement("moveCommand",[useVarID, ID, useVarOutput, output]) + + int eventID = ValueOrVariable(com.parameters[0], com.parameters[1]); + int outputParam = ValueOrVariable(com.parameters[2], com.parameters[3]); + + Game_Character* event = GetCharacter(eventID); + Game_Character* target; + + std::string moveCommand = ToString(com.string); + std::string outputString = event->GetSpriteName(); + + std::size_t pos = moveCommand.find('/'); + + if (pos != std::string::npos) { + outputString = moveCommand.substr(pos + 1); + moveCommand = moveCommand.substr(0, pos); + } + + if (moveCommand == "SetMoveSpeed")event->SetMoveSpeed(outputParam); + if (moveCommand == "SetMoveFrequency")event->SetMoveFrequency(outputParam); + if (moveCommand == "SetTransparency")event->SetTransparency(outputParam); + + if (moveCommand == "Event2Event") { + target = GetCharacter(outputParam); + event->SetFacing(target->GetFacing()); + event->SetDirection(target->GetDirection()); + event->SetX(target->GetX()); + event->SetY(target->GetY()); + } + + if (moveCommand == "SetFacingLocked")event->SetFacingLocked(outputParam); + if (moveCommand == "SetLayer")event->SetLayer(outputParam); + if (moveCommand == "SetFlying")event->SetFlying(outputParam); //FIXME: I wish any event could imitate an airship, lacks more work. + if (moveCommand == "ChangeCharset")event->SetSpriteGraphic(outputString,outputParam); // syntax ChangeCharset/actor1 + + if (moveCommand == "StopMovement")event->CancelMoveRoute(); + + return true; +} + +bool Game_Interpreter::CommandWaitForMovement(lcf::rpg::EventCommand const& com) { + // CommandWaitForMovement(useVarID, ID) + + // Needed for ShowChoice + auto* frame = GetFramePtr(); + const auto& list = frame->commands; + auto& index = frame->current_command; + + // Retrieve event ID + int eventID = ValueOrVariable(com.parameters[0], com.parameters[1]); + if (eventID == 0) + eventID = GetCurrentEventId(); + + // Get the character associated with the event ID + Game_Character* ev = GetCharacter(eventID); + + // Check if movement exists and if it's currently running + bool movementExists = !ev->GetMoveRoute().move_commands.empty(); + bool movementIsRunning = movementExists && (ev->IsMoveRouteOverwritten() && !ev->IsMoveRouteFinished()); + + int i = frame->current_command + 1; + + // Check the next command for a specific condition + const auto& cmd = list[i]; + const int32_t failBranch = static_cast(Cmd::ShowChoiceOption); + + // If the next command is "Fails to move x times" and the character is stuck, cancel movement + if (cmd.code == failBranch && cmd.string == "Fails to move x times") + if (ev->isStuck(cmd.parameters[0])) { + ev->failsMove = 0; + ev->CancelMoveRoute(); + frame->current_command = i + 1; + return true; + } + + // Return false if movement is still in progress + if(!movementIsRunning) ev->failsMove = 0; + return !movementIsRunning; +} + Game_Interpreter& Game_Interpreter::GetForegroundInterpreter() { return Game_Battle::IsBattleRunning() ? Game_Battle::GetInterpreter() diff --git a/src/game_interpreter.h b/src/game_interpreter.h index f349b39188..5e41b43e9b 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -289,6 +289,8 @@ class Game_Interpreter bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com); bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com); bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com); + bool CommandCallMovement(lcf::rpg::EventCommand const& com); + bool CommandWaitForMovement(lcf::rpg::EventCommand const& com); int DecodeInt(lcf::DBArray::const_iterator& it); const std::string DecodeString(lcf::DBArray::const_iterator& it); From e0c567a57503e9a2ccc0cf24aed576a561a330fd Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Sun, 8 Oct 2023 10:20:41 -0300 Subject: [PATCH 2/4] FaceTowards and FaceAway Commands ```js @raw 2050, "FaceTowards", eventAIsVar,eventA, eventBisVar, eventB ``` ```js @raw 2050, "FaceAway", eventAIsVar,eventA, eventBisVar, eventB ``` --- src/game_character.cpp | 90 +++++++++++++++++++++++----------------- src/game_character.h | 13 +++--- src/game_event.cpp | 6 +-- src/game_interpreter.cpp | 13 ++++++ 4 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/game_character.cpp b/src/game_character.cpp index b1e7e12dfe..3ea0ad125e 100644 --- a/src/game_character.cpp +++ b/src/game_character.cpp @@ -291,10 +291,10 @@ void Game_Character::UpdateMoveRoute(int32_t& current_index, const lcf::rpg::Mov TurnRandom(); break; case Code::move_towards_hero: - TurnTowardHero(); + TurnTowardCharacter(CharPlayer); break; case Code::move_away_from_hero: - TurnAwayFromHero(); + TurnAwayFromCharacter(CharPlayer); break; case Code::move_forward: break; @@ -348,10 +348,10 @@ void Game_Character::UpdateMoveRoute(int32_t& current_index, const lcf::rpg::Mov TurnRandom(); break; case Code::face_hero: - TurnTowardHero(); + TurnTowardCharacter(CharPlayer); break; case Code::face_away_from_hero: - TurnAwayFromHero(); + TurnAwayFromCharacter(CharPlayer); break; default: break; @@ -543,20 +543,21 @@ void Game_Character::Turn90DegreeLeftOrRight() { } } -int Game_Character::GetDirectionToHero() { - int sx = DistanceXfromPlayer(); - int sy = DistanceYfromPlayer(); +int Game_Character::GetDirectionToCharacter(const Game_Character* target) { + int sx = DistanceXfromCharacter(target); + int sy = DistanceYfromCharacter(target); - if ( std::abs(sx) > std::abs(sy) ) { + if (std::abs(sx) > std::abs(sy)) { return (sx > 0) ? Left : Right; - } else { + } + else { return (sy > 0) ? Up : Down; } } -int Game_Character::GetDirectionAwayHero() { - int sx = DistanceXfromPlayer(); - int sy = DistanceYfromPlayer(); +int Game_Character::GetDirectionAwayCharacter(const Game_Character* target) { + int sx = DistanceXfromCharacter(target); + int sy = DistanceYfromCharacter(target); if ( std::abs(sx) > std::abs(sy) ) { return (sx > 0) ? Right : Left; @@ -565,12 +566,18 @@ int Game_Character::GetDirectionAwayHero() { } } -void Game_Character::TurnTowardHero() { - SetDirection(GetDirectionToHero()); +void Game_Character::TurnTowardCharacter(int targetID) { + Game_Character* target = GetCharacter(targetID, targetID); + if (target) { + SetDirection(GetDirectionToCharacter(target)); + } } -void Game_Character::TurnAwayFromHero() { - SetDirection(GetDirectionAwayHero()); +void Game_Character::TurnAwayFromCharacter(int targetID) { + Game_Character* target = GetCharacter(targetID, targetID); + if (target) { + SetDirection(GetDirectionAwayCharacter(target)); + } } void Game_Character::TurnRandom() { @@ -606,10 +613,10 @@ bool Game_Character::BeginMoveRouteJump(int32_t& current_index, const lcf::rpg:: TurnRandom(); break; case Code::move_towards_hero: - TurnTowardHero(); + TurnTowardCharacter(CharPlayer); break; case Code::move_away_from_hero: - TurnAwayFromHero(); + TurnAwayFromCharacter(CharPlayer); break; case Code::move_forward: break; @@ -650,10 +657,10 @@ bool Game_Character::BeginMoveRouteJump(int32_t& current_index, const lcf::rpg:: TurnRandom(); break; case Code::face_hero: - TurnTowardHero(); + TurnTowardCharacter(CharPlayer); break; case Code::face_away_from_hero: - TurnAwayFromHero(); + TurnAwayFromCharacter(CharPlayer); break; default: break; @@ -739,32 +746,39 @@ bool Game_Character::Jump(int x, int y) { return true; } -int Game_Character::DistanceXfromPlayer() const { - int sx = GetX() - Main_Data::game_player->GetX(); - if (Game_Map::LoopHorizontal()) { - if (std::abs(sx) > Game_Map::GetTilesX() / 2) { - if (sx > 0) - sx -= Game_Map::GetTilesX(); - else - sx += Game_Map::GetTilesX(); +int Game_Character::DistanceXfromCharacter(const Game_Character* target) const { + if (target) { + int sx = GetX() - target->GetX(); + if (Game_Map::LoopHorizontal()) { + if (std::abs(sx) > Game_Map::GetTilesX() / 2) { + if (sx > 0) + sx -= Game_Map::GetTilesX(); + else + sx += Game_Map::GetTilesX(); + } } + return sx; } - return sx; + return 0; // Return a default value or handle the case where target is nullptr. } -int Game_Character::DistanceYfromPlayer() const { - int sy = GetY() - Main_Data::game_player->GetY(); - if (Game_Map::LoopVertical()) { - if (std::abs(sy) > Game_Map::GetTilesY() / 2) { - if (sy > 0) - sy -= Game_Map::GetTilesY(); - else - sy += Game_Map::GetTilesY(); +int Game_Character::DistanceYfromCharacter(const Game_Character* target) const { + if (target) { + int sy = GetY() - target->GetY(); + if (Game_Map::LoopVertical()) { + if (std::abs(sy) > Game_Map::GetTilesY() / 2) { + if (sy > 0) + sy -= Game_Map::GetTilesY(); + else + sy += Game_Map::GetTilesY(); + } } + return sy; } - return sy; + return 0; // Return a default value or handle the case where target is nullptr. } + void Game_Character::ForceMoveRoute(const lcf::rpg::MoveRoute& new_route, int frequency) { if (!IsMoveRouteOverwritten()) { diff --git a/src/game_character.h b/src/game_character.h index 98c80ee830..c7c0e12ce2 100644 --- a/src/game_character.h +++ b/src/game_character.h @@ -628,10 +628,10 @@ class Game_Character { void Turn90DegreeLeftOrRight(); /** @return the direction we would need to face the hero. */ - int GetDirectionToHero(); + int GetDirectionToCharacter(const Game_Character* target); /** @return the direction we would need to face away from hero. */ - int GetDirectionAwayHero(); + int GetDirectionAwayCharacter(const Game_Character* target); /** * @param dir input direction @@ -662,13 +662,12 @@ class Game_Character { /** * Character looks towards the hero. */ - void TurnTowardHero(); + void TurnTowardCharacter(int targetID); /** * Character looks away from the hero. */ - void TurnAwayFromHero(); - + void TurnAwayFromCharacter(int targetID); /** * Character waits for 20 frames more. */ @@ -764,8 +763,8 @@ class Game_Character { */ void SetAnimationType(AnimType anim_type); - int DistanceXfromPlayer() const; - int DistanceYfromPlayer() const; + int DistanceXfromCharacter(const Game_Character* target) const; + int DistanceYfromCharacter(const Game_Character* target) const; /** * Tests if the character is currently on the tile at x/y or moving diff --git a/src/game_event.cpp b/src/game_event.cpp index 83d0be0c4b..318fa24339 100644 --- a/src/game_event.cpp +++ b/src/game_event.cpp @@ -349,7 +349,7 @@ bool Game_Event::ScheduleForegroundExecution(bool by_decision_key, bool face_pla } if (face_player && !(IsFacingLocked() || IsSpinning())) { - SetFacing(GetDirectionToHero()); + SetFacing(GetDirectionToCharacter(GetCharacter(CharPlayer, CharPlayer))); } data()->waiting_execution = true; @@ -570,8 +570,8 @@ void Game_Event::MoveTypeTowardsOrAwayPlayer(bool towards) { dir = Rand::GetRandomNumber(0, 3); } else { dir = towards - ? GetDirectionToHero() - : GetDirectionAwayHero(); + ? GetDirectionToCharacter(GetCharacter(CharPlayer, CharPlayer)) + : GetDirectionAwayCharacter(GetCharacter(CharPlayer, CharPlayer)); } } diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 44be1392de..3c4e3f2304 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5087,6 +5087,19 @@ bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { event->SetY(target->GetY()); } + if (moveCommand == "FaceTowards"){ + if(!event->IsMoving()) { + event->TurnTowardCharacter(outputParam); + event->UpdateFacing(); + } +} + if (moveCommand == "FaceAway"){ + if (!event->IsMoving()) { + event->TurnAwayFromCharacter(outputParam); + event->UpdateFacing(); + } + } + if (moveCommand == "SetFacingLocked")event->SetFacingLocked(outputParam); if (moveCommand == "SetLayer")event->SetLayer(outputParam); if (moveCommand == "SetFlying")event->SetFlying(outputParam); //FIXME: I wish any event could imitate an airship, lacks more work. From 6be99f85d94b764bab5389b70aed055001caf477 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:08:28 -0300 Subject: [PATCH 3/4] WaitForMovement - Refactor code It now uses a single command instead of a chain of commands. ```cpp CommandWaitForMovement(useVarID, ID, useVarTargetVariable, targetVariable, useVarFailuresAmount, failuresAmount) ``` You'll store if a Move Route failed or succeed on a target variable. Then use that variable to define an outcome. --- src/game_interpreter.cpp | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 3c4e3f2304..5aaea46094 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5111,17 +5111,13 @@ bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { } bool Game_Interpreter::CommandWaitForMovement(lcf::rpg::EventCommand const& com) { - // CommandWaitForMovement(useVarID, ID) + // CommandWaitForMovement(useVarID, ID, useVarTargetVariable, targetVariable, useVarFailuresAmount, failuresAmount) - // Needed for ShowChoice - auto* frame = GetFramePtr(); - const auto& list = frame->commands; - auto& index = frame->current_command; - - // Retrieve event ID int eventID = ValueOrVariable(com.parameters[0], com.parameters[1]); - if (eventID == 0) - eventID = GetCurrentEventId(); + if (eventID == 0) eventID = GetCurrentEventId(); + + int outputVariable = ValueOrVariable(com.parameters[2], com.parameters[3]); + int failuresAmount = ValueOrVariable(com.parameters[4], com.parameters[5]); // Get the character associated with the event ID Game_Character* ev = GetCharacter(eventID); @@ -5130,24 +5126,24 @@ bool Game_Interpreter::CommandWaitForMovement(lcf::rpg::EventCommand const& com) bool movementExists = !ev->GetMoveRoute().move_commands.empty(); bool movementIsRunning = movementExists && (ev->IsMoveRouteOverwritten() && !ev->IsMoveRouteFinished()); - int i = frame->current_command + 1; - - // Check the next command for a specific condition - const auto& cmd = list[i]; - const int32_t failBranch = static_cast(Cmd::ShowChoiceOption); - - // If the next command is "Fails to move x times" and the character is stuck, cancel movement - if (cmd.code == failBranch && cmd.string == "Fails to move x times") - if (ev->isStuck(cmd.parameters[0])) { + // failed to move X times (0 = wait till finish) + if (failuresAmount > 0) + if (ev->isStuck(failuresAmount)) { ev->failsMove = 0; ev->CancelMoveRoute(); - frame->current_command = i + 1; + Main_Data::game_variables->Set(outputVariable, 0); + Game_Map::SetNeedRefresh(true); return true; } // Return false if movement is still in progress - if(!movementIsRunning) ev->failsMove = 0; - return !movementIsRunning; + if (!movementIsRunning) { + ev->failsMove = 0; + Main_Data::game_variables->Set(outputVariable, 1); + Game_Map::SetNeedRefresh(true); + return true; + } + return false; } Game_Interpreter& Game_Interpreter::GetForegroundInterpreter() { From ee8b2ad6c9eb2bb74e7740730709dba91a772ea9 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Fri, 8 Mar 2024 04:32:25 -0300 Subject: [PATCH 4/4] New subcommands: JumpTo and SetAnimationType --- src/game_interpreter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 5aaea46094..988b25181f 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5061,6 +5061,7 @@ bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { int eventID = ValueOrVariable(com.parameters[0], com.parameters[1]); int outputParam = ValueOrVariable(com.parameters[2], com.parameters[3]); + int outputParamB = ValueOrVariable(com.parameters[4], com.parameters[5]); Game_Character* event = GetCharacter(eventID); Game_Character* target; @@ -5078,6 +5079,8 @@ bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { if (moveCommand == "SetMoveSpeed")event->SetMoveSpeed(outputParam); if (moveCommand == "SetMoveFrequency")event->SetMoveFrequency(outputParam); if (moveCommand == "SetTransparency")event->SetTransparency(outputParam); + if (moveCommand == "SetAnimationType")event->SetAnimationType(static_cast(outputParam)); + if (moveCommand == "Event2Event") { target = GetCharacter(outputParam); @@ -5104,7 +5107,9 @@ bool Game_Interpreter::CommandCallMovement(lcf::rpg::EventCommand const& com) { if (moveCommand == "SetLayer")event->SetLayer(outputParam); if (moveCommand == "SetFlying")event->SetFlying(outputParam); //FIXME: I wish any event could imitate an airship, lacks more work. if (moveCommand == "ChangeCharset")event->SetSpriteGraphic(outputString,outputParam); // syntax ChangeCharset/actor1 - + + if (moveCommand == "JumpTo")event->Game_Character::Jump(outputParam, outputParamB); + if (moveCommand == "StopMovement")event->CancelMoveRoute(); return true;