diff --git a/src/dynrpg_easyrpg.cpp b/src/dynrpg_easyrpg.cpp index 276fef5225..965a47abe4 100644 --- a/src/dynrpg_easyrpg.cpp +++ b/src/dynrpg_easyrpg.cpp @@ -95,6 +95,59 @@ 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_event_anim_reset") { + auto func_name = "easyrpg_event_anim_reset"; + bool okay = false; + int char_id; + + std::tie(char_id) = DynRpg::ParseArgs(func_name, args, &okay); + if (!okay) return true; + + Game_Character* target = interpreter->GetCharacter(char_id, func_name); + if (target) { + target->ClearCustomAnimationSequence(); + } + return true; + } + else if (func == "easyrpg_get_event_frame") { + auto func_name = "easyrpg_get_event_frame"; + bool okay = false; + int char_id, var_id; + + 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) { + int frame = target->GetAnimFrame(); + Main_Data::game_variables->Set(var_id, frame); + Game_Map::SetNeedRefreshForVarChange(var_id); + } + return true; } return false; } diff --git a/src/game_character.cpp b/src/game_character.cpp index b9ade33e44..8d5231de93 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; + // Reset to standard middle/standing frame + data()->anim_frame = 1; + SetAnimCount(0); +} + +void Game_Character::UpdateAnimation() { + const auto speed = Utils::Clamp(GetMoveSpeed(), 1, 6); + + if (is_custom_anim && !custom_sequence.empty()) { + // --- NEW CUSTOM BRANCH --- + 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); + } + } else { + // --- ORIGINAL ENGINE BRANCH --- + 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..6edbc29231 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,6 +949,10 @@ 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