diff --git a/src/dynrpg_easyrpg.cpp b/src/dynrpg_easyrpg.cpp index 276fef5225..213952d3d3 100644 --- a/src/dynrpg_easyrpg.cpp +++ b/src/dynrpg_easyrpg.cpp @@ -95,7 +95,62 @@ bool DynRpg::EasyRpgPlugin::Invoke(std::string_view func, dyn_arg_list args, boo return EasyOput(args); } else if (func == "easyrpg_add") { return EasyAdd(args); - } + } else if (func == "easyrpg_set_event_frame") { + auto func_name = "easyrpg_set_event_frame"; + bool okay = false; + int char_id; + std::string sequence_str; + + std::tie(char_id, sequence_str) = DynRpg::ParseArgs(func_name, args, &okay); + if (!okay) return true; + + Game_Character* target = interpreter->GetCharacter(char_id, func_name); + if (target) { + std::vector seq; + auto tokens = Utils::Tokenize(sequence_str, [](char32_t c) { return c == ','; }); + for (const auto& t : tokens) { + try { + seq.push_back(std::stoi(t)); + } catch (...) { + Output::Warning("Invalid frame index in sequence: {}", t); + } + } + target->SetCustomAnimationSequence(seq); + } + return true; + } else if (func == "easyrpg_get_event_frame") { + auto func_name = "easyrpg_get_event_frame"; + bool okay = false; + int char_id, var_id; + + // Arguments: Event ID (0=this, -1=player, >0=ID), Variable ID to store result + std::tie(char_id, var_id) = DynRpg::ParseArgs(func_name, args, &okay); + if (!okay) return true; + + Game_Character* target = interpreter->GetCharacter(char_id, func_name); + if (target) { + // Get the current logical animation frame + int frame = target->GetAnimFrame(); + + // Store it in the requested variable + Main_Data::game_variables->Set(var_id, frame); + + // Ensure the map refreshes if any event depends on this variable + Game_Map::SetNeedRefreshForVarChange(var_id); + } + return true; + } else if (func == "easyrpg_event_anim_reset") { + bool okay = false; + int char_id; + std::tie(char_id) = DynRpg::ParseArgs("easyrpg_event_anim_reset", args, &okay); + if (!okay) return true; + + Game_Character* target = interpreter->GetCharacter(char_id, "easyrpg_event_anim_reset"); + if (target) { + target->ClearCustomAnimationSequence(); + } + return true; +} return false; } diff --git a/src/game_character.cpp b/src/game_character.cpp index b9ade33e44..ab4e05520c 100644 --- a/src/game_character.cpp +++ b/src/game_character.cpp @@ -173,45 +173,76 @@ void Game_Character::UpdateMovement(int amount) { SetStopCount(0); } -void Game_Character::UpdateAnimation() { - const auto speed = Utils::Clamp(GetMoveSpeed(), 1, 6); - - if (IsSpinning()) { - const auto limit = GetSpinAnimFrames(speed); - - IncAnimCount(); - - if (GetAnimCount() >= limit) { - SetFacing((GetFacing() + 1) % 4); - SetAnimCount(0); - } - return; - } - - if (IsAnimPaused() || IsJumping()) { - ResetAnimation(); - return; - } - - if (!IsAnimated()) { - return; - } - - const auto stationary_limit = GetStationaryAnimFrames(speed); - const auto continuous_limit = GetContinuousAnimFrames(speed); - - if (IsContinuous() - || GetStopCount() == 0 - || data()->anim_frame == lcf::rpg::EventPage::Frame_left || data()->anim_frame == lcf::rpg::EventPage::Frame_right - || GetAnimCount() < stationary_limit - 1) { - IncAnimCount(); - } - - if (GetAnimCount() >= continuous_limit - || (GetStopCount() == 0 && GetAnimCount() >= stationary_limit)) { - IncAnimFrame(); - return; - } +void Game_Character::SetCustomAnimationSequence(const std::vector& sequence) { + is_custom_anim = true; + custom_sequence = sequence; + sequence_index = 0; + SetAnimCount(0); + if (!custom_sequence.empty()) { + data()->anim_frame = custom_sequence[0]; + } +} + +void Game_Character::ClearCustomAnimationSequence() { + is_custom_anim = false; + custom_sequence.clear(); + sequence_index = 0; + data()->anim_frame = 1; + SetAnimCount(0); +} + +void Game_Character::UpdateAnimation() { + const auto speed = Utils::Clamp(GetMoveSpeed(), 1, 6); + + // CUSTOM SEQUENCE LOGIC + if (is_custom_anim && !custom_sequence.empty()) { + const auto limit = GetStationaryAnimFrames(speed); + IncAnimCount(); + if (GetAnimCount() >= limit) { + sequence_index = (sequence_index + 1) % custom_sequence.size(); + data()->anim_frame = custom_sequence[sequence_index]; + SetAnimCount(0); + } + return; + } + + // ORIGINAL ENGINE LOGIC + if (IsSpinning()) { + const auto limit = GetSpinAnimFrames(speed); + + IncAnimCount(); + + if (GetAnimCount() >= limit) { + SetFacing((GetFacing() + 1) % 4); + SetAnimCount(0); + } + return; + } + + if (IsAnimPaused() || IsJumping()) { + ResetAnimation(); + return; + } + + if (!IsAnimated()) { + return; + } + + const auto stationary_limit = GetStationaryAnimFrames(speed); + const auto continuous_limit = GetContinuousAnimFrames(speed); + + if (IsContinuous() + || GetStopCount() == 0 + || data()->anim_frame == lcf::rpg::EventPage::Frame_left || data()->anim_frame == lcf::rpg::EventPage::Frame_right + || GetAnimCount() < stationary_limit - 1) { + IncAnimCount(); + } + + if (GetAnimCount() >= continuous_limit + || (GetStopCount() == 0 && GetAnimCount() >= stationary_limit)) { + IncAnimFrame(); + return; + } } void Game_Character::UpdateFlash() { diff --git a/src/game_character.h b/src/game_character.h index 2549fe516d..04a1430842 100644 --- a/src/game_character.h +++ b/src/game_character.h @@ -922,7 +922,10 @@ class Game_Character { static Game_Character& GetPlayer(); static constexpr int GetDxFromDirection(int dir); - static constexpr int GetDyFromDirection(int dir); + static constexpr int GetDyFromDirection(int dir); + + void SetCustomAnimationSequence(const std::vector& sequence); + void ClearCustomAnimationSequence(); protected: explicit Game_Character(Type type, lcf::rpg::SaveMapEventBase* d); @@ -946,9 +949,14 @@ class Game_Character { lcf::rpg::SaveMapEventBase* data(); const lcf::rpg::SaveMapEventBase* data() const; + + bool is_custom_anim = false; + std::vector custom_sequence; + int sequence_index = 0; + int original_move_frequency = 2; - // contains if any movement (<= step_forward) of a forced move route was successful + // contains if any movement (<= step_forward) of a forced move route was successful Type _type = {}; lcf::rpg::SaveMapEventBase* _data = nullptr;