#include "cracker.hpp" #include #include #include #include #include "ui/ui.hpp" #include "ui/hook.hpp" #include "utils.hpp" #include "il2cpp.hpp" #include "skincache.hpp" using fnset_TimeScaleT = void(*)(float); static fnset_TimeScaleT get_Time_set_timeScale() { static fnset_TimeScaleT fn = []() { Il2CppDomain* domain = il2cpp_domain_get(); size_t assembly_count = 0; Il2CppAssembly** assemblies = il2cpp_domain_get_assemblies(domain, &assembly_count); Il2CppClass* cls = nullptr; for (size_t i = 0; i < assembly_count; ++i) { Il2CppImage* image = il2cpp_assembly_get_image(assemblies[i]); Il2CppClass* klass = il2cpp_class_from_name(image, "UnityEngine", "Time"); if (klass != nullptr) { cls = klass; break; } } if (cls != nullptr) { MethodInfo* method_info = il2cpp_class_get_method_from_name(cls, "set_timeScale", 1); if (method_info != nullptr) { auto method = reinterpret_cast(method_info->methodPointer); SPDLOG_INFO("Found Time.set_timeScale at {}", (void*)method_info->methodPointer); return method; } } SPDLOG_ERROR("Failed to get Time.set_timeScale"); throw std::runtime_error("Failed to get Time.set_timeScale"); }(); return fn; } #define IS_ENABLED(f) m_flag.f.load() #define ENABLE(n) \ SPDLOG_INFO("Enable {}", #n); \ m_flag.n.store(true) #define DISABLE(n) \ SPDLOG_INFO("Disable {}", #n); \ m_flag.n.store(false) #define CALLED(f) \ if (IS_ENABLED(HOOKED_LUA_FUNCTION_TRACE)) { \ SPDLOG_INFO("{} called", #f); \ } LuaStatePauser::LuaStatePauser(lua_State* L) : m_L(L) {} void LuaStatePauser::do_when_paused(const std::function& func) { auto _ = std::lock_guard(m_atomic_pause); ensure_debug_hook(); auto lk = std::binary_semaphore(0); g_pause_flag.store(&lk); g_lua_state_operation.store(&func); lk.acquire(); SPDLOG_INFO("lua operation finished"); } void LuaStatePauser::ensure_debug_hook() { if (lua_gethook(m_L) != LuaStatePauser::debug_hook) { register_debug_hook(); } } std::atomic LuaStatePauser::g_pause_flag = nullptr; std::atomic*> LuaStatePauser::g_lua_state_operation = nullptr; void LuaStatePauser::register_debug_hook() { SPDLOG_INFO("registering debug hook"); int ret = lua_sethook(m_L, LuaStatePauser::debug_hook, LUA_MASKCALL | LUA_MASKRET, 0); if (ret != 1) { SPDLOG_INFO("set pause hook failed with code: {}", ret); } } extern bool g_is_game_loaded; void LuaStatePauser::debug_hook(lua_State *L, lua_Debug *ar) { if (g_is_game_loaded) { auto callback = g_lua_state_operation.load(); if (!callback) { return; } SPDLOG_INFO("pause triggered, operate lua state"); (*callback)(); auto f = g_pause_flag.load(); g_lua_state_operation.store(nullptr); g_pause_flag.store(nullptr); f->release(); } } Cracker::Cracker(bool need_pause) : m_state([]() -> lua_State* { auto new_tr = lua_newthread(Utils::get_lua_state()); SPDLOG_INFO("new lua_State: {}", (void*)new_tr); return new_tr; }()) , m_lua_state_pauser(Utils::get_lua_state()) { try { if (need_pause) { load_cracker_with_pause(); } else { load_cracker_without_pause(); } } catch(std::exception& e) { SPDLOG_ERROR("Cracker load error: {}", e.what()); exit(-1); } } #ifdef ENABLE_CRACKER_UI std::mutex g_cracker_lock; Cracker& Cracker::Instance() { std::lock_guard guard(g_cracker_lock); static auto* instance = new Cracker; return *instance; } #else Cracker& Cracker::Instance(bool need_pause) { static auto* instance = new Cracker(need_pause); return *instance; } #endif int Cracker::get_coin() { ensure_data_proxies(); double coin = m_data_proxy.player["data"]["gold"]; return static_cast(coin); } int Cracker::get_oil() { ensure_data_proxies(); double oil = m_data_proxy.player["data"]["oil"]; return static_cast(oil); } int Cracker::get_gems() { ensure_data_proxies(); double gems = m_data_proxy.player["data"]["awardGem"]; return static_cast(gems); } int Cracker::get_level() { ensure_data_proxies(); double level = m_data_proxy.player["data"]["level"]; return static_cast(level); } int Cracker::get_exp() { ensure_data_proxies(); double exp = m_data_proxy.player["data"]["exp"]; return static_cast(exp); } int Cracker::get_merit() { ensure_data_proxies(); double merit = m_data_proxy.player["data"]["exploit"]; return static_cast(merit); } int Cracker::get_guild_coin() { ensure_data_proxies(); double guild_coin = m_data_proxy.player["data"]["guildCoin"]; return static_cast(guild_coin); } int Cracker::get_design_prt() { ensure_data_proxies(); double design_prt = m_data_proxy.player["data"]["design_prt"]; return static_cast(design_prt); } #define CORE_DATA_ITEM_ID 59900 int Cracker::get_core_data() { ensure_data_proxies(); Lua::Table storage = m_data_proxy.storage["data"]; double core_data = storage.get(CORE_DATA_ITEM_ID)["count"]; return static_cast(core_data); } #define MEDAL_ITEM_ID 15001 int Cracker::get_medal() { ensure_data_proxies(); Lua::Table storage = m_data_proxy.storage["data"]; double medal = storage.get(MEDAL_ITEM_ID)["count"]; return static_cast(medal); } int Cracker::get_pt() { ensure_data_proxies(); double pt = m_data_proxy.player["data"]["pt"]; return static_cast(pt); } #define SPECIALIZED_CORE_ITEM_ID 59010 int Cracker::get_specialized_core() { ensure_data_proxies(); Lua::Table storage = m_data_proxy.storage["data"]; double specialized_core = storage.get(SPECIALIZED_CORE_ITEM_ID)["count"]; return static_cast(specialized_core); } int Cracker::get_curr_action_point() { ensure_data_proxies(); double curr_action_point = m_data_proxy.world["world"]["fields"]["staminaMgr"]["fields"]["stamina"]; return static_cast(curr_action_point); } std::map Cracker::scan_dock() { ensure_data_proxies(); std::map ships; Lua::Table dock = m_data_proxy.dock["data"]; for (auto& [ship_id, ship_info] : dock) { Lua::Table ship_data = ship_info.as(); double id = ship_data["id"]; ships[static_cast(id)] = { .config_id = static_cast(ship_data.get("configId")), .emotion = static_cast(ship_data.get("energy")), .rarity = static_cast(ship_data.get("star")), .level = static_cast(ship_data.get("level")), .exp = static_cast(ship_data.get("exp")), .curr_star = static_cast(ship_data.get("state")), }; } return ships; } std::map Cracker::scan_storage() { ensure_data_proxies(); std::map items; Lua::Table storage = m_data_proxy.storage["data"]; for (auto& [item_id, item_info] : storage) { Lua::Table item_data = item_info.as(); double config_id = item_data["configId"]; double count = item_data["count"]; items[static_cast(config_id)] = static_cast(count); } return items; } std::optional Cracker::get_ship_info(ShipId ship_id) { ensure_data_proxies(); double id = static_cast(ship_id); Lua::Table dock = m_data_proxy.dock["data"]; try { Lua::Table ship_data = dock.get(id); return ShipInfo{ .config_id = static_cast(ship_data.get("configId")), .emotion = static_cast(ship_data.get("energy")), .rarity = static_cast(ship_data.get("star")), .level = static_cast(ship_data.get("level")), .exp = static_cast(ship_data.get("exp")), .curr_star = static_cast(ship_data.get("state")), }; } catch (const std::exception& e) { return std::nullopt; } return std::nullopt; } std::optional Cracker::get_storage_item_count(StorageItemId item_id) { ensure_data_proxies(); double id = static_cast(item_id); Lua::Table storage = m_data_proxy.storage["data"]; try { Lua::Table item_data = storage.get(id); return static_cast(item_data.get("count")); } catch (const std::exception& e) { return std::nullopt; } return std::nullopt; } void Cracker::disable_all() { disable_hooked_lua_function_trace(); disable_global_ship_properties_crack(); disable_fast_stage_move(); disable_remove_hard_mode_ship_properties_limit(); disable_remove_hard_mode_ship_type_limit(); disable_fake_player(); disable_no_bb_animation(); disable_no_emotion_warning(); disable_opsi_fast_move(); disable_gg_factor(); disable_global_speedup(); disable_exercise_god_mode(); disable_exercise_more_power(); disable_fast_wave(); disable_monster_kill_self(); disable_skip_battle_celebrate(); disable_better_global_speedup(); disable_no_damage(); disable_opsi_force_auto(); disable_opsi_no_map_fog(); disable_skip_ship_gain_show(); disable_chapter_force_enable_auto_fight(); disable_chapter_skip_precombat(); disable_chapter_auto_next_battle(); disable_chapter_auto_ambush(); disable_chapter_auto_clear(); disable_skip_story(); disable_infinite_battle(); disable_skip_air_strike_animation(); disable_auto_once_again(); } void Cracker::enable_auto_once_again() { ENABLE(AUTO_ONCE_AGAIN); } void Cracker::disable_auto_once_again() { DISABLE(AUTO_ONCE_AGAIN); } void Cracker::enable_skip_air_strike_animation() { ENABLE(SKIP_AIR_STRIKE_ANIMATION); } void Cracker::disable_skip_air_strike_animation() { DISABLE(INFINITE_BATTLE); } void Cracker::enable_infinite_battle() { ENABLE(INFINITE_BATTLE); } void Cracker::disable_infinite_battle() { DISABLE(INFINITE_BATTLE); } void Cracker::enable_hooked_lua_function_trace() { ENABLE(HOOKED_LUA_FUNCTION_TRACE); } void Cracker::disable_hooked_lua_function_trace() { DISABLE(HOOKED_LUA_FUNCTION_TRACE); } void Cracker::enable_skip_story() { ENABLE(SKIP_STORY); } void Cracker::disable_skip_story() { DISABLE(SKIP_STORY); } void Cracker::set_chapter_auto_clear_step_interval(double interval) { m_mover.interval.store(interval); } void Cracker::enable_chapter_auto_clear() { ENABLE(CHAPTER_AUTO_CLEAR); } void Cracker::disable_chapter_auto_clear() { DISABLE(CHAPTER_AUTO_CLEAR); } void Cracker::enable_chapter_skip_precombat() { ENABLE(CHAPTER_SKIP_PRECOMBAT); } void Cracker::disable_chapter_skip_precombat() { DISABLE(CHAPTER_SKIP_PRECOMBAT); } void Cracker::enable_chapter_auto_next_battle() { ENABLE(CHAPTER_AUTO_NEXT_BATTLE); } void Cracker::disable_chapter_auto_next_battle() { DISABLE(CHAPTER_AUTO_NEXT_BATTLE); } void Cracker::enable_chapter_force_enable_auto_fight() { ENABLE(CHAPTER_FORCE_ENABLE_AUTO_FIGHT); } void Cracker::disable_chapter_force_enable_auto_fight() { DISABLE(CHAPTER_FORCE_ENABLE_AUTO_FIGHT); } void Cracker::enable_chapter_auto_ambush() { ENABLE(CHAPTER_AUTO_AMBUSH); } void Cracker::disable_chapter_auto_ambush() { DISABLE(CHAPTER_AUTO_AMBUSH); } void Cracker::enable_skip_ship_gain_show() { ENABLE(SKIP_SHIP_GAIN_SHOW); } void Cracker::disable_skip_ship_gain_show() { DISABLE(SKIP_SHIP_GAIN_SHOW); } void Cracker::enable_opsi_force_auto() { ENABLE(OPSI_FORCE_AUTO); } void Cracker::disable_opsi_force_auto() { DISABLE(OPSI_FORCE_AUTO); } void Cracker::enable_opsi_no_map_fog() { ENABLE(OPSI_NO_MAP_FOG); } void Cracker::disable_opsi_no_map_fog() { DISABLE(OPSI_NO_MAP_FOG); } void Cracker::enable_all_skin() { ENABLE(ALL_SKIN); } void Cracker::disable_all_skin() { DISABLE(ALL_SKIN); } void Cracker::enable_no_damage() { ENABLE(NO_DAMAGE); } void Cracker::disable_no_damage() { DISABLE(NO_DAMAGE); } void Cracker::enable_better_global_speedup() { ENABLE(BETTER_GLOBAL_SPEEDUP); better_global_speedup_set_rate(m_better_global_speedup_rate.load()); } void Cracker::update_better_global_speedup_rate(double rate) { m_better_global_speedup_rate.store(rate); if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { better_global_speedup_set_rate(rate); } } void Cracker::disable_better_global_speedup() { DISABLE(BETTER_GLOBAL_SPEEDUP); better_global_speedup_set_rate(1.0); } void Cracker::skip_battle_celebrate_pause(bool need_pause) { m_skip_battle_celebrate_need_pause.store(need_pause); } void Cracker::enable_skip_battle_celebrate() { ENABLE(SKIP_BATTLE_CELEBRATE); skip_battle_celebrate_set_duration(0.01); } void Cracker::disable_skip_battle_celebrate() { DISABLE(SKIP_BATTLE_CELEBRATE); skip_battle_celebrate_set_duration(3); } void Cracker::enable_monster_kill_self() { ENABLE(MONSTER_KILL_SELF); } void Cracker::disable_monster_kill_self() { DISABLE(MONSTER_KILL_SELF); } void Cracker::enable_fast_wave() { ENABLE(FAST_WAVE); } void Cracker::disable_fast_wave() { DISABLE(FAST_WAVE); } void Cracker::enable_exercise_more_power() { ENABLE(EXERCISE_MORE_POWER); } void Cracker::update_exercise_more_power_rate(double rate) { m_exercise_more_power_rate.store(rate); } void Cracker::disable_exercise_more_power() { DISABLE(EXERCISE_MORE_POWER); } void Cracker::enable_exercise_god_mode() { ENABLE(EXERCISE_GOD_MOD); } void Cracker::disable_exercise_god_mode() { DISABLE(EXERCISE_GOD_MOD); } void Cracker::enable_global_speedup() { m_global_speedup_timer.stop(); m_global_speedup_timer.setInterval([this]() { get_Time_set_timeScale()(m_global_speedup_rate.load()); }, 500); ENABLE(GLOBAL_SPEEDUP); } void Cracker::update_global_speedup_rate(float rate) { m_global_speedup_rate.store(rate); } void Cracker::disable_global_speedup() { m_global_speedup_timer.stop(); get_Time_set_timeScale()(1.0); DISABLE(GLOBAL_SPEEDUP); } void Cracker::enable_gg_factor() { ENABLE(GG_FACTOR); } void Cracker::disable_gg_factor() { DISABLE(GG_FACTOR); } void Cracker::update_gg_factor(double factor) { m_gg_factor = factor; } void Cracker::enable_opsi_fast_move() { ENABLE(OPSI_FAST_MOVE); } void Cracker::disable_opsi_fast_move() { DISABLE(OPSI_FAST_MOVE); } void Cracker::enable_no_emotion_warning() { ENABLE(NO_EMOTION_WARNING); } void Cracker::disable_no_emotion_warning() { DISABLE(NO_EMOTION_WARNING); } void Cracker::enable_no_bb_animation() { ENABLE(NO_BB_ANIMATION); } void Cracker::disable_no_bb_animation() { DISABLE(NO_BB_ANIMATION); } void Cracker::enable_fake_player() { ENABLE(FAKE_PLAYER_INFO); } void Cracker::disable_fake_player() { DISABLE(FAKE_PLAYER_INFO); } void Cracker::update_fake_player_info(const FakePlayerInfo& fake_info) { m_fake_player_info = fake_info; } void Cracker::enable_remove_hard_mode_ship_type_limit() { ENABLE(REMOVE_HARD_MODE_SHIP_TYPE_LIMIT); } void Cracker::disable_remove_hard_mode_ship_type_limit() { DISABLE(REMOVE_HARD_MODE_SHIP_TYPE_LIMIT); } void Cracker::enable_remove_hard_mode_ship_properties_limit() { ENABLE(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT); } void Cracker::disable_remove_hard_mode_ship_properties_limit() { DISABLE(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT); } void Cracker::fast_stage_move_pause(bool need_pause) { m_fast_stage_move_need_pause.store(need_pause); } void Cracker::enable_fast_stage_move() { ENABLE(FAST_STAGE_MOVE_CRACK); fast_stage_move_set_duration(0.0); } void Cracker::disable_fast_stage_move() { DISABLE(FAST_STAGE_MOVE_CRACK); fast_stage_move_set_duration(0.5); } void Cracker::update_global_ship_properties(const ShipProperties& properties) { m_globle_ship_properties = properties; } void Cracker::enable_global_ship_properties_crack() { ENABLE(GLOBAL_SHIP_PROPERTIES_CRACK); } void Cracker::disable_global_ship_properties_crack() { DISABLE(GLOBAL_SHIP_PROPERTIES_CRACK); } void Cracker::fast_stage_move_set_duration(double duration) { if (m_fast_stage_move_need_pause.load()) { fast_stage_move_set_duration_with_pause(duration); } else { fast_stage_move_set_duration_without_pause(duration); } } void Cracker::fast_stage_move_set_duration_with_pause(double duration) { m_lua_state_pauser.do_when_paused([&] () { m_state["ChapterConst"]["ShipStepDuration"] = duration; m_state["ChapterConst"]["ShipStepQuickPlayScale"] = duration; }); } void Cracker::fast_stage_move_set_duration_without_pause(double duration) { m_state["ChapterConst"]["ShipStepDuration"] = duration; m_state["ChapterConst"]["ShipStepQuickPlayScale"] = duration; } void Cracker::skip_battle_celebrate_set_duration(double duration) { if (m_skip_battle_celebrate_need_pause.load()) { skip_battle_celebrate_set_duration_with_pause(duration); } else { skip_battle_celebrate_set_duration_without_pause(duration); } } void Cracker::skip_battle_celebrate_set_duration_with_pause(double duration) { m_lua_state_pauser.do_when_paused([&]() { m_state["ys"]["Battle"]["BattleConfig"]["CelebrateDuration"] = duration; }); } void Cracker::skip_battle_celebrate_set_duration_without_pause(double duration) { m_state["ys"]["Battle"]["BattleConfig"]["CelebrateDuration"] = duration; } void Cracker::print_table_field(std::vector& path) { Lua::Table t = m_state.globals(); for (const auto& p : path) { t = t[p]; } Utils::Lua::print_table_fields_type(t); } void Cracker::print_value(std::vector& path) { Lua::Table t = m_state.globals(); for (auto it = path.begin(); it != path.end() - 1; ++it) { t = t[*it]; } std::string key = path.back(); Lua::Object value = t[key]; auto type = value.get_type(); if (type == sol::type::number) { SPDLOG_INFO("value: {}", value.as()); } else if (type == sol::type::string) { SPDLOG_INFO("value: {}", value.as()); } else if (type == sol::type::boolean) { SPDLOG_INFO("value: {}", value.as()); } else if (type == sol::type::nil) { SPDLOG_INFO("value: nil"); } else if (type == sol::type::table) { Lua::Table table = value.as(); Utils::Lua::print_table_fields_type(table); } } void Cracker::better_global_speedup_pause(bool is_need) { m_better_global_speedup_need_pause.store(is_need); } void Cracker::load_lua_resources() { SPDLOG_INFO("Load lua resources"); SPDLOG_INFO("Load lua res"); m_lua_res.Clone = m_state["Clone"]; m_lua_res.Ship_getIntimacyLevel = m_state["Ship"]["getIntimacyLevel"]; m_lua_res.pg_intimacy_template = m_state["pg"]["intimacy_template"]; Lua::Table AttributeType = m_state["AttributeType"]; m_lua_res.AttributeType = { .Durability = AttributeType.get("Durability"), .Cannon = AttributeType.get("Cannon"), .Torpedo = AttributeType.get("Torpedo"), .AntiAircraft = AttributeType.get("AntiAircraft"), .AntiSub = AttributeType.get("AntiSub"), .Air = AttributeType.get("Air"), .Reload = AttributeType.get("Reload"), .Hit = AttributeType.get("Hit"), .Dodge = AttributeType.get("Dodge"), }; m_lua_res.BattleResultMediator_GET_NEW_SHIP = m_state["BattleResultMediator"]["GET_NEW_SHIP"]; m_lua_res.NewBattleResultMediator_GET_NEW_SHIP = m_state["NewBattleResultMediator"]["GET_NEW_SHIP"]; m_lua_res.getProxy = m_state["getProxy"]; m_lua_res.ChapterProxy = m_state["ChapterProxy"]; m_lua_res.ChapterProxy_GetChapterAutoFlag = m_state["ChapterProxy"]["GetChapterAutoFlag"]; m_lua_res.ChapterProxy_SetChapterAutoFlag = m_state["ChapterProxy"]["SetChapterAutoFlag"]; m_lua_res.Chapter_getChapterCell = m_state["Chapter"]["getChapterCell"]; m_lua_res.LevelStageView_emit = m_state["LevelStageView"]["emit"]; m_lua_res.Chapter_emit = m_state["Chapter"]["emit"]; m_lua_res.ChapterConst_OpMove = m_state["ChapterConst"]["OpMove"]; m_lua_res.ChapterConst_CellFlagActive = m_state["ChapterConst"]["CellFlagActive"]; m_lua_res.ChapterConst_IsEnemyAttach = m_state["ChapterConst"]["IsEnemyAttach"]; m_lua_res.LevelMediator2_ON_OP = m_state["LevelMediator2"]["ON_OP"]; m_lua_res.Timer_New = m_state["Timer"]["New"]; m_lua_res.Timer_Start = m_state["Timer"]["Start"]; m_lua_res.ChapterConst_IsBossCell = m_state["ChapterConst"]["IsBossCell"]; m_lua_res.ChapterConst_CellFlagDisabled = m_state["ChapterConst"]["CellFlagDisabled"]; m_lua_res.Chapter_checkAnyInteractive = m_state["Chapter"]["checkAnyInteractive"]; m_lua_res.Chapter_GetFleetOfDuty = m_state["Chapter"]["GetFleetOfDuty"]; m_lua_res.Chapter_findPath = m_state["Chapter"]["findPath"]; m_lua_res.ChapterConst_SubjectPlayer = m_state["ChapterConst"]["SubjectPlayer"]; m_lua_res.Ship_ENERGY_LOW = m_state["Ship"]["ENERGY_LOW"]; m_lua_res.BaseUI_closeView = m_state["BaseUI"]["closeView"]; m_lua_res.LevelMediator2_ON_RETRACKING = m_state["LevelMediator2"]["ON_RETRACKING"]; SPDLOG_INFO("Load lua functions"); m_original.GetBattleCheckResult = m_state["GetBattleCheckResult"]; m_original.FinishStageCommand_GeneralPackage = m_state["FinishStageCommand"]["GeneralPackage"]; m_original.Ship_intimacyAdditions = m_state["Ship"]["intimacyAdditions"]; m_original.Ship_cosumeEnergy = m_state["Ship"]["cosumeEnergy"]; m_original.Ship_getEnergy = m_state["Ship"]["getEnergy"]; m_original.ys_Battle_BattleManualWeaponAutoBot_SetActive = m_state["ys"]["Battle"]["BattleManualWeaponAutoBot"]["SetActive"]; m_original.PlayerVitaeDetailPage_UpdateInfo = m_state["PlayerVitaeDetailPage"]["UpdateInfo"]; m_original.Chapter_getConfig = m_state["Chapter"]["getConfig"]; m_original.WorldFleetSelectLayer_CheckValid = m_state["WorldFleetSelectLayer"]["CheckValid"]; m_original.BossSingleBattleFleetSelectSubPanel_CheckValid = m_state["BossSingleBattleFleetSelectSubPanel"]["CheckValid"]; m_original.Chapter_IsEliteFleetLegal = m_state["Chapter"]["IsEliteFleetLegal"]; m_original.Ship_getShipProperties = m_state["Ship"]["getShipProperties"]; m_original.ys_Battle_BattleGateGuild_GeneralPackage = m_state["ys"]["Battle"]["BattleGateGuild"]["GeneralPackage"]; m_original.ys_Battle_BattleDataFunction_GetBuffTemplate = m_state["ys"]["Battle"]["BattleDataFunction"]["GetBuffTemplate"]; m_original.WorldConst_GetTerrainMoveStepDuration = m_state["WorldConst"]["GetTerrainMoveStepDuration"]; m_original.ys_Battle_BattleDataFunction_GetDungeonTmpDataByID = m_state["ys"]["Battle"]["BattleDataFunction"]["GetDungeonTmpDataByID"]; m_original.ys_Battle_BattleEnemyUnit_GetTemplate = m_state["ys"]["Battle"]["BattleEnemyUnit"]["GetTemplate"]; m_original.ys_Battle_BattleState_ExitBattle = m_state["ys"]["Battle"]["BattleState"]["ExitBattle"]; m_original.ys_Battle_BattleState_ScaleTimer = m_state["ys"]["Battle"]["BattleState"]["ScaleTimer"]; m_original.ShipSkin_IsShareSkin = m_state["ShipSkin"]["IsShareSkin"]; m_original.ShipSkin_CanUseShareSkinForShip = m_state["ShipSkin"]["CanUseShareSkinForShip"]; m_original.ShipSkinProxy_hasSkin = m_state["ShipSkinProxy"]["hasSkin"]; m_original.ShipSkinProxy_hasNonLimitSkin = m_state["ShipSkinProxy"]["hasNonLimitSkin"]; m_original.pg_ConnectionMgr_Send = m_state["pg"]["ConnectionMgr"]["Send"]; m_original.Ship_getSkinId = m_state["Ship"]["getSkinId"]; m_original.WorldMap_CanAutoFight = m_state["WorldMap"]["CanAutoFight"]; m_original.WorldMap_UpdateVisionFlag = m_state["WorldMap"]["UpdateVisionFlag"]; m_original.NewBattleResultDisplayAwardPage_ShowShips = m_state["NewBattleResultDisplayAwardPage"]["ShowShips"]; m_original.BaseUI_emit = m_state["BaseUI"]["emit"]; m_original.Chapter_CanActivateAutoFight = m_state["Chapter"]["CanActivateAutoFight"]; m_original.Chapter_IsAutoFight = m_state["Chapter"]["IsAutoFight"]; m_original.Chapter_IsSkipPrecombat = m_state["Chapter"]["IsSkipPrecombat"]; m_original.Chapter_writeBack = m_state["Chapter"]["writeBack"]; m_original.LevelStageView_tryAutoTrigger = m_state["LevelStageView"]["tryAutoTrigger"]; m_original.LevelStageView_TryAutoFight = m_state["LevelStageView"]["TryAutoFight"]; m_original.Story_Ctor = m_state["Story"]["Ctor"]; m_original.Fleet_EnergyCheck = m_state["Fleet"]["EnergyCheck"]; m_original.ContinuousOperationRuntimeData_ConsumeBattleTime = m_state["ContinuousOperationRuntimeData"]["ConsumeBattleTime"]; m_original.ActivityProxy_UseContinuousTime = m_state["ActivityProxy"]["UseContinuousTime"]; m_original.LevelScene_doPlayAirStrike = m_state["LevelScene"]["doPlayAirStrike"]; m_original.LevelStageTotalRewardPanel_UpdateView = m_state["LevelStageTotalRewardPanel"]["UpdateView"]; } void Cracker::hook_all_lua_functions() { SPDLOG_INFO("Hook lua functions"); // auto_once_again m_state["LevelStageTotalRewardPanel"]["UpdateView"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(LevelStageTotalRewardPanel.UpdateView) m_original.LevelStageTotalRewardPanel_UpdateView(L, args); if (IS_ENABLED(AUTO_ONCE_AGAIN)) { Lua::Table self = args[0]; Lua::Object chap = self["contextData"]["chapter"]; Lua::Object auto_fight_timer = m_lua_res.Timer_New(L, [this, self, chap](sol::this_state l, Lua::VariadicArgs) { m_original.BaseUI_emit(l, self, m_lua_res.LevelMediator2_ON_RETRACKING, chap, true); m_lua_res.BaseUI_closeView(l, self); }, 1, 1); m_lua_res.Timer_Start(L, auto_fight_timer); } }; // skip_air_strike_animation m_state["LevelScene"]["doPlayAirStrike"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(LevelScene.doPlayAirStrike) if (IS_ENABLED(SKIP_AIR_STRIKE_ANIMATION)) { Lua::Object callback = args[3]; if (callback.is()) { callback.as()(L); } else { m_original.LevelScene_doPlayAirStrike(L, args); } return; } m_original.LevelScene_doPlayAirStrike(L, args); }; // infinite_battle m_state["ContinuousOperationRuntimeData"]["ConsumeBattleTime"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(ContinuousOperationRuntimeData.ConsumeBattleTime); if (IS_ENABLED(INFINITE_BATTLE)) { return; } m_original.ContinuousOperationRuntimeData_ConsumeBattleTime(L, args); }; m_state["ActivityProxy"]["UseContinuousTime"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(ActivityProxy.UseContinuousTime); if (IS_ENABLED(INFINITE_BATTLE)) { return; } m_original.ActivityProxy_UseContinuousTime(L, args); }; // skip_story m_state["Story"]["Ctor"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(Story.Ctor); m_original.Story_Ctor(L, args); if (IS_ENABLED(SKIP_STORY)) { Lua::Table self = args[0]; self["skipAll"] = true; self["isAuto"] = false; } }; // chapter_auto_clear m_state["LevelStageView"]["TryAutoFight"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(LevelStageView.TryAutoFight); if (!IS_ENABLED(CHAPTER_AUTO_CLEAR)) { m_original.LevelStageView_TryAutoFight(L, args); return; } my_LevelStageView_TryAutoFight(L, args); }; // chapter_auto_ambush m_state["LevelStageView"]["tryAutoTrigger"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(LevelStageView.tryAutoTrigger); if (!IS_ENABLED(CHAPTER_AUTO_AMBUSH)) { return m_original.LevelStageView_tryAutoTrigger(L, args); } Lua::Table self = args[0]; std::optional chapterVO = self["contextData"]["chapterVO"]; if (chapterVO.has_value()) { auto& vo = chapterVO.value(); Lua::Object row = vo["fleet"]["line"]["row"]; Lua::Object column = vo["fleet"]["line"]["column"]; std::optional cell = m_lua_res.Chapter_getChapterCell(L, vo, row, column); if (cell.has_value()) { auto& cell_v = cell.value(); int attachment = cell_v["attachment"]; int flag = cell_v["flag"]; if (attachment == 5 && flag == 2) { Lua::Table trigger_arg = m_state.create_table(); int fleet_id = vo["fleet"]["id"]; trigger_arg["type"] = 4; trigger_arg["id"] = fleet_id; trigger_arg["arg1"] = 1; m_lua_res.LevelStageView_emit(L, self, sol::make_object(L, "LevelMediator2:ON_OP"), trigger_arg); return sol::make_object(L, true); } } } return m_original.LevelStageView_tryAutoTrigger(L, args); }; // chapter_auto_next_battle m_state["Chapter"]["writeBack"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(Chapter.writeBack); if (!IS_ENABLED(CHAPTER_AUTO_NEXT_BATTLE)) { m_original.Chapter_writeBack(L, args); return; } m_original.Chapter_writeBack(L, args); bool is_win = args[1]; if (!is_win) { return; } Lua::Table self = args[0]; Lua::Object chapter_proxy = m_lua_res.getProxy(L, m_lua_res.ChapterProxy); int id = self["id"]; m_lua_res.ChapterProxy_SetChapterAutoFlag(L, chapter_proxy, id, true); }; // chapter_skip_precombat m_state["Chapter"]["IsSkipPrecombat"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Chapter.IsSkipPrecombat); if (!IS_ENABLED(CHAPTER_SKIP_PRECOMBAT)) { return m_original.Chapter_IsSkipPrecombat(L, args); } return sol::make_object(L, true); }; // force_enable_auto_fight m_state["Chapter"]["CanActivateAutoFight"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Chapter.CanActivateAutoFight); if (!IS_ENABLED(CHAPTER_FORCE_ENABLE_AUTO_FIGHT)) { return m_original.Chapter_CanActivateAutoFight(L, args); } return sol::make_object(L, true); }; m_state["Chapter"]["IsAutoFight"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Chapter.IsAutoFight); if (!IS_ENABLED(CHAPTER_FORCE_ENABLE_AUTO_FIGHT)) { return m_original.Chapter_IsAutoFight(L, args); } Lua::Object chapter_proxy = m_lua_res.getProxy(L, m_lua_res.ChapterProxy); int id = args[0].as()["id"]; Lua::Object flag = m_lua_res.ChapterProxy_GetChapterAutoFlag(L, chapter_proxy, id); auto flag_type = flag.get_type(); if (flag_type == sol::type::nil) { return sol::make_object(L, true); } else if (flag_type == sol::type::number) { return sol::make_object(L, flag.as() != 0); } else { return sol::make_object(L, false); } return sol::make_object(L, false); }; // skip_ship_gain_show m_state["NewBattleResultDisplayAwardPage"]["ShowShips"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(NewBattleResultDisplayAwardPage.ShowShips); if (!IS_ENABLED(SKIP_SHIP_GAIN_SHOW)) { m_original.NewBattleResultDisplayAwardPage_ShowShips(L, args); return; } Lua::Function callback = args[2]; callback(L); }; m_state["BaseUI"]["emit"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(BaseUI.emit); if (!IS_ENABLED(SKIP_SHIP_GAIN_SHOW)) { m_original.BaseUI_emit(L, args); return; } std::string event_name = args[1]; if (event_name == m_lua_res.BattleResultMediator_GET_NEW_SHIP || event_name == m_lua_res.NewBattleResultMediator_GET_NEW_SHIP) { for (int i = 2; i <= args.size(); ++i) { Lua::Object obj = args[i]; if (obj.get_type() == sol::type::function) { obj.as()(); return; } return; } } m_original.BaseUI_emit(L, args); }; // opsi_force_auto m_state["WorldMap"]["CanAutoFight"] = [this](sol::this_state L, Lua::VariadicArgs args) -> bool { if (!IS_ENABLED(OPSI_FORCE_AUTO)) { return m_original.WorldMap_CanAutoFight(L, args); } return true; }; // opsi_no_map_fog m_state["WorldMap"]["UpdateVisionFlag"] = [this](sol::this_state L, Lua::VariadicArgs args) { if (!IS_ENABLED(OPSI_NO_MAP_FOG)) { m_original.WorldMap_UpdateVisionFlag(L, args); return; } auto new_args = std::vector(); new_args.push_back(args[0]); new_args.push_back(sol::make_object(L, true)); m_original.WorldMap_UpdateVisionFlag(L, sol::as_args(new_args)); }; // all_skin class AlwaysOn { public: AlwaysOn(Cracker* cracker, Lua::Function& orig_func):m_cracker(cracker), m_orig_func(orig_func) {} bool operator()(sol::this_state L, Lua::VariadicArgs args) { if (m_cracker->m_flag.ALL_SKIN.load()) { return true; } return m_orig_func(L, args); } private: Cracker* m_cracker; Lua::Function& m_orig_func; }; m_state["ShipSkin"]["IsShareSkin"] = AlwaysOn(this, m_original.ShipSkin_IsShareSkin); m_state["ShipSkin"]["CanUseShareSkinForShip"] = AlwaysOn(this, m_original.ShipSkin_CanUseShareSkinForShip); m_state["ShipSkinProxy"]["hasSkin"] = AlwaysOn(this, m_original.ShipSkinProxy_hasSkin); m_state["ShipSkinProxy"]["hasNonLimitSkin"] = AlwaysOn(this, m_original.ShipSkinProxy_hasNonLimitSkin); m_state["pg"]["ConnectionMgr"]["Send"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(pg.ConnectionMgr.Send); if (IS_ENABLED(ALL_SKIN)) { int req_id = args[1]; if (req_id == 12202) { Lua::Table req_arg = args[2]; int ship_id = req_arg["ship_id"]; int skin_id = req_arg["skin_id"]; SkinCache::get_instance().save(ship_id, skin_id); Lua::Table fake_result = m_state.create_table(); fake_result["result"] = 0; Lua::Function callback = args[4]; callback(L, fake_result); return; } } m_original.pg_ConnectionMgr_Send(L, args); }; m_state["Ship"]["getSkinId"] = [this](sol::this_state L, Lua::VariadicArgs args) -> int { CALLED(Ship.getSkinId); if (!IS_ENABLED(ALL_SKIN)) { return m_original.Ship_getSkinId(L, args); } Lua::Table self = args[0]; int ship_id = self["id"]; auto& skin_cache = SkinCache::get_instance(); if (skin_cache.has_ship(ship_id)) { return skin_cache.get_skin(ship_id); } return m_original.Ship_getSkinId(L, args); }; // better_global_speedup m_state["ys"]["Battle"]["BattleState"]["ExitBattle"] = [this](sol::this_state L, Lua::VariadicArgs args) { m_original.ys_Battle_BattleState_ExitBattle(L, args); if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { better_global_speedup_pause(false); better_global_speedup_set_rate(m_better_global_speedup_rate.load()); } }; m_state["ys"]["Battle"]["BattleState"]["ScaleTimer"] = [this](sol::this_state L, Lua::VariadicArgs args) { m_original.ys_Battle_BattleState_ScaleTimer(L, args); if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { better_global_speedup_pause(false); better_global_speedup_set_rate(m_better_global_speedup_rate.load()); } }; // monster_kill_self m_state["ys"]["Battle"]["BattleEnemyUnit"]["GetTemplate"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(ys.Battle.BattleEnemyUnit.GetTemplate); if (!IS_ENABLED(MONSTER_KILL_SELF)) { return m_original.ys_Battle_BattleEnemyUnit_GetTemplate(L, args); } Lua::Table result_original = m_original.ys_Battle_BattleEnemyUnit_GetTemplate(L, args); Lua::Table result = m_lua_res.Clone(result_original); Lua::Table buff_list = m_state.create_table(); Lua::Table buff1 = m_state.create_table(); buff1["ID"] = 201229; buff1["LV"] = 1; buff_list.add(buff1); Lua::Table buff2 = m_state.create_table(); buff2["ID"] = 800382; buff2["LV"] = 1; buff_list.add(buff2); result["buff_list"] = buff_list; return result; }; // fast_wave m_state["ys"]["Battle"]["BattleDataFunction"]["GetDungeonTmpDataByID"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Table { CALLED(ys.Battle.BattleDataFunction.GetDungeonTmpDataByID); if (!IS_ENABLED(FAST_WAVE)) { return m_original.ys_Battle_BattleDataFunction_GetDungeonTmpDataByID(L, args); } Lua::Table result_original = m_original.ys_Battle_BattleDataFunction_GetDungeonTmpDataByID(L, args); Lua::Table result = m_lua_res.Clone(result_original); Lua::Table stages = result["stages"]; for (int i = 1; i <= stages.size(); ++i) { Lua::Table stage = stages[i]; Lua::Table waves = stage["waves"]; for(int j = 1; j <= waves.size(); ++j) { Lua::Table wave = waves[j]; sol::optional preWaves = wave["preWaves"]; if (preWaves.has_value()) { preWaves.value().clear(); } sol::optional triggerParams = wave["triggerParams"]; if (triggerParams.has_value()) { triggerParams.value()["timeout"] = 0.1; } sol::optional airFighterTable = wave["airFighter"]; if (airFighterTable.has_value()) { auto& airFighters = airFighterTable.value(); for (int k = 1; k <= airFighters.size(); ++k) { Lua::Table airFighter = airFighters[k]; airFighter["interval"] = 0; airFighter["onceNumber"] = 0; airFighter["totalNumber"] = 0; airFighter["delay"] = 0; } } } } return result; }; // enable_global_speedup m_state["GetBattleCheckResult"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(GetBattleCheckResult); if (IS_ENABLED(GLOBAL_SPEEDUP)) { goto hook_handler; } if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { if (IS_ENABLED(MONSTER_KILL_SELF)) { goto original_ret; } else { goto hook_handler; } } original_ret: return m_original.GetBattleCheckResult(L, args); hook_handler: double speedup_rate; if (IS_ENABLED(GLOBAL_SPEEDUP)) { speedup_rate = m_global_speedup_rate.load(); } else if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { speedup_rate = m_better_global_speedup_rate.load(); } else { return m_original.GetBattleCheckResult(L, args);; } double total_time = args[2]; std::vector new_args(args.begin(), args.end()); new_args[2] = sol::make_object(L, total_time / speedup_rate); return m_original.GetBattleCheckResult(L, sol::as_args(new_args)); }; m_state["ys"]["Battle"]["BattleGateGuild"]["GeneralPackage"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Table { CALLED(ys.Battle.BattleGateGuild.GeneralPackage); if (IS_ENABLED(GLOBAL_SPEEDUP)) { goto hook_handler; } if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { if (IS_ENABLED(MONSTER_KILL_SELF)) { goto original_ret; } else { goto hook_handler; } } original_ret: return m_original.ys_Battle_BattleGateGuild_GeneralPackage(L, args); hook_handler: Lua::Table ret = m_original.ys_Battle_BattleGateGuild_GeneralPackage(args); double speedup_rate; if (IS_ENABLED(GLOBAL_SPEEDUP)) { speedup_rate = m_global_speedup_rate.load(); } else if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { speedup_rate = m_better_global_speedup_rate.load(); } else { return ret; } double total_time = ret["total_time"]; ret["total_time"] = total_time / speedup_rate; return ret; }; m_state["FinishStageCommand"]["GeneralPackage"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Table { CALLED(FinishStageCommand.GeneralPackage); if (IS_ENABLED(GLOBAL_SPEEDUP)) { goto hook_handler; } if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { if (IS_ENABLED(MONSTER_KILL_SELF)) { goto original_ret; } else { goto hook_handler; } } if (IS_ENABLED(NO_DAMAGE)) { goto hook_handler; } original_ret: return m_original.FinishStageCommand_GeneralPackage(L, args); hook_handler: Lua::Table ret; if (IS_ENABLED(NO_DAMAGE)) { Lua::Table battle_into = args[0]; Lua::Table battle_statistics = battle_into["statistics"]; Lua::Table battle_ship_info = args[1]; for(int i = 1; i <= battle_ship_info.size(); ++i) { double ship_id = battle_ship_info[i]["id"]; Lua::Table item = battle_statistics[ship_id]; item["output"] = 0.0; item["damage"] = 0.0; item["maxDamageOnce"] = 0.0; item["kill_count"] = 0.0; } std::vector new_args(args.begin(), args.end()); new_args[0] = sol::make_object(L, battle_into); ret = m_original.FinishStageCommand_GeneralPackage(L, sol::as_args(new_args)); } else { ret = m_original.FinishStageCommand_GeneralPackage(L, args); } double speedup_rate; if (IS_ENABLED(GLOBAL_SPEEDUP)) { speedup_rate = m_global_speedup_rate.load(); } else if (IS_ENABLED(BETTER_GLOBAL_SPEEDUP)) { speedup_rate = m_better_global_speedup_rate.load(); } else { return ret; } double total_time = ret["total_time"]; ret["total_time"] = total_time / speedup_rate; return ret; }; // enable_gg_factor m_state["Ship"]["intimacyAdditions"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Ship.intimacyAdditions); if (!IS_ENABLED(GG_FACTOR)) { return m_original.Ship_intimacyAdditions(L, args); } if (args.size() < 2) { return m_original.Ship_intimacyAdditions(L, args); } Lua::Table self = args[0]; int intimacy_level = m_lua_res.Ship_getIntimacyLevel(self); Lua::Table attr_bonus_table = m_lua_res.pg_intimacy_template.get(intimacy_level); double attr_bonus = attr_bonus_table.get("attr_bonus"); double calculated_attr_bonus = attr_bonus * 0.0001; Lua::Table attributes = args[1]; for (auto& [attr_name, attr_value] : attributes) { if (attr_name.get_type() == sol::type::string) { std::string attr_name_str = attr_name.as(); const auto& attr = m_lua_res.AttributeType; if ( attr_name_str == attr.Durability || attr_name_str == attr.Cannon || attr_name_str == attr.Torpedo || attr_name_str == attr.AntiAircraft || attr_name_str == attr.AntiSub || attr_name_str == attr.Air || attr_name_str == attr.Reload || attr_name_str == attr.Hit || attr_name_str == attr.Dodge ) { double old_value = attributes[attr_name_str]; attributes[attr_name_str] = old_value * (calculated_attr_bonus + m_gg_factor); } } } return sol::nil; }; // enable_no_emotion_warning m_state["Ship"]["cosumeEnergy"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Ship.cosumeEnergy); if (!IS_ENABLED(NO_EMOTION_WARNING)) { return m_original.Ship_cosumeEnergy(L, args); } return sol::nil; }; m_state["Ship"]["getEnergy"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Ship.getEnergy); if (!IS_ENABLED(NO_EMOTION_WARNING)) { return m_original.Ship_getEnergy(L, args); } return sol::make_object(L, 150); }; m_state["Fleet"]["EnergyCheck"] = [this](sol::this_state L, Lua::VariadicArgs args) { CALLED(Fleet.EnergyCheck); bool no_emotion_warning_enabled = IS_ENABLED(NO_EMOTION_WARNING); if (no_emotion_warning_enabled) { m_state["Ship"]["ENERGY_LOW"] = -1; } m_original.Fleet_EnergyCheck(L, args); if (no_emotion_warning_enabled) { m_state["Ship"]["ENERGY_LOW"] = m_lua_res.Ship_ENERGY_LOW; } }; // enable_no_bb_animation m_state["ys"]["Battle"]["BattleManualWeaponAutoBot"]["SetActive"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(ys.Battle.BattleManualWeaponAutoBot.SetActive); if (!IS_ENABLED(NO_BB_ANIMATION)) { return m_original.ys_Battle_BattleManualWeaponAutoBot_SetActive(L, args); } if (args.size() < 3) { return m_original.ys_Battle_BattleManualWeaponAutoBot_SetActive(L, args); } std::vector new_args(args.begin(), args.end()); new_args[2] = sol::make_object(L, false); Lua::Object res = m_original.ys_Battle_BattleManualWeaponAutoBot_SetActive(sol::as_args(new_args)); return res; }; // enable_fake_player m_state["PlayerVitaeDetailPage"]["UpdateInfo"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(PlayerVitaeDetailPage.UpdateInfo); if (!IS_ENABLED(FAKE_PLAYER_INFO)) { return m_original.PlayerVitaeDetailPage_UpdateInfo(L, args); } Lua::Object res = m_original.PlayerVitaeDetailPage_UpdateInfo(args); Lua::Table t = args[0]; if (!m_fake_player_info.name.empty()) { t["nameTxt"]["text"] = m_fake_player_info.name; } if (!m_fake_player_info.level.empty()) { t["levelTxt"]["text"] = "LV." + m_fake_player_info.level; } if (!m_fake_player_info.id.empty()) { t["idTxt"]["text"] = m_fake_player_info.id; } return res; }; // enable_remove_hard_mode_ship_type_limit m_state["Chapter"]["getConfig"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Chapter.getConfig); if (!IS_ENABLED(REMOVE_HARD_MODE_SHIP_TYPE_LIMIT)) { return m_original.Chapter_getConfig(L, args); } bool limitation = false; if (args.size() == 2) { Lua::Object first_arg = args[1]; if (first_arg.get_type() == sol::type::string && first_arg.as() == "limitation") { limitation = true; } } Lua::Object res = m_original.Chapter_getConfig(L, args); if (limitation) { sol::table table = res; clear_hard_mode_ship_properties_limit(table); } return res; }; // enable_remove_hard_mode_ship_properties_limit m_state["WorldFleetSelectLayer"]["CheckValid"] = [this](sol::this_state L, Lua::VariadicArgs args) -> bool { CALLED(WorldFleetSelectLayer.CheckValid); if (!IS_ENABLED(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT)) { return m_original.WorldFleetSelectLayer_CheckValid(L, args); } return true; }; m_state["BossSingleBattleFleetSelectSubPanel"]["CheckValid"] = [this](sol::this_state L, Lua::VariadicArgs args) -> bool { CALLED(BossSingleBattleFleetSelectSubPanel.CheckValid); if (!IS_ENABLED(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT)) { return m_original.BossSingleBattleFleetSelectSubPanel_CheckValid(L, args); } return true; }; m_state["Chapter"]["IsEliteFleetLegal"] = [this](sol::this_state L, Lua::VariadicArgs args) -> bool { CALLED(Chapter.IsEliteFleetLegal); if (!IS_ENABLED(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT)) { return m_original.Chapter_IsEliteFleetLegal(L, args); } return true; }; // enable_global_ship_properties_crack m_state["Ship"]["getShipProperties"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(Ship.getShipProperties); if (!IS_ENABLED(GLOBAL_SHIP_PROPERTIES_CRACK)) { return m_original.Ship_getShipProperties(L, args); } Lua::Table properties = m_original.Ship_getShipProperties(L, args); modify_ship_properties(properties, m_globle_ship_properties); return properties; }; // exercise m_state["ys"]["Battle"]["BattleDataFunction"]["GetBuffTemplate"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Table { CALLED(ys.Battle.BattleDataFunction.GetBuffTemplate); auto is_enabled_EXERCISE_GOD_MOD = IS_ENABLED(EXERCISE_GOD_MOD); auto is_enabled_EXERCISE_MORE_POWER = IS_ENABLED(EXERCISE_MORE_POWER); auto is_enabled_MONSTER_KILL_SELF = IS_ENABLED(MONSTER_KILL_SELF); if (!is_enabled_EXERCISE_GOD_MOD && !is_enabled_EXERCISE_MORE_POWER && !is_enabled_MONSTER_KILL_SELF) { return m_original.ys_Battle_BattleDataFunction_GetBuffTemplate(L, args); } double buff_id = args[0]; Lua::Table buff_original = m_original.ys_Battle_BattleDataFunction_GetBuffTemplate(L, args); Lua::Table buff = m_lua_res.Clone(buff_original); if (buff_id == 19) { // buff_19 sol::table effect_list = m_state.create_table(); if (is_enabled_EXERCISE_GOD_MOD) { sol::table entry1 = m_state.create_table(); entry1["type"] = "BattleBuffCastSkill"; sol::table trigger1 = m_state.create_table(); trigger1.add("onBeforeFatalDamage"); entry1["trigger"] = trigger1; sol::table arg_list1 = m_state.create_table(); arg_list1["skill_id"] = 11011; arg_list1["target"] = "TargetSelf"; entry1["arg_list"] = arg_list1; sol::table entry2 = m_state.create_table(); entry2["type"] = "BattleBuffCancelBuff"; sol::table trigger2 = m_state.create_table(); trigger2.add("onBeforeFatalDamage"); entry2["trigger"] = trigger2; sol::table arg_list2 = m_state.create_table(); arg_list2["count"] = INT_MAX; entry2["arg_list"] = arg_list2; sol::table entry3 = m_state.create_table(); entry3["type"] = "BattleBuffAddBuff"; sol::table trigger3 = m_state.create_table(); trigger3.add("onRemove"); entry3["trigger"] = trigger3; sol::table arg_list3 = m_state.create_table(); arg_list3["buff_id"] = 11016; entry3["arg_list"] = arg_list3; sol::table entry4 = m_state.create_table(); entry4["type"] = "BattleBuffAddAttr"; sol::table trigger4 = m_state.create_table(); trigger4.add("onAttach"); entry4["trigger"] = trigger4; sol::table arg_list4 = m_state.create_table(); arg_list4["number"] = 1; arg_list4["target"] = "TargetSelf"; arg_list4["attr"] = "isInvincible"; entry4["arg_list"] = arg_list4; effect_list.add(entry1); effect_list.add(entry2); effect_list.add(entry3); effect_list.add(entry4); } if (is_enabled_EXERCISE_MORE_POWER) { sol::table entry = m_state.create_table(); entry["type"] = "BattleBuffAddBulletAttr"; sol::table trigger = m_state.create_table(); trigger.add("onBulletCreate"); entry["trigger"] = trigger; sol::table arg_list = m_state.create_table(); arg_list["number"] = m_exercise_more_power_rate.load(); arg_list["attr"] = "damageRatioBullet"; entry["arg_list"] = arg_list; effect_list.add(entry); } if (is_enabled_EXERCISE_GOD_MOD || is_enabled_EXERCISE_MORE_POWER) { buff["effect_list"] = effect_list; } } else if (buff_id == 800382) { if (is_enabled_MONSTER_KILL_SELF) { buff["effect_list"][1]["arg_list"]["number"] = -9999999; } } return buff; }; m_state["WorldConst"]["GetTerrainMoveStepDuration"] = [this](sol::this_state L, Lua::VariadicArgs args) -> Lua::Object { CALLED(WorldConst.GetTerrainMoveStepDuration); if (!IS_ENABLED(OPSI_FAST_MOVE)) { return m_original.WorldConst_GetTerrainMoveStepDuration(L, args); } return sol::make_object(L, 0); }; } void Cracker::better_global_speedup_set_rate(double rate) { if (m_better_global_speedup_need_pause.load()) { better_global_speedup_set_rate_with_pause(rate); } else { better_global_speedup_set_rate_without_pause(rate); } } void Cracker::better_global_speedup_set_rate_with_pause(double rate) { m_lua_state_pauser.do_when_paused([&]() { m_state["ys"]["Battle"]["BattleConfig"]["BASIC_TIME_SCALE"] = rate; m_state["Time"]["timeScale"] = rate; }); } void Cracker::better_global_speedup_set_rate_without_pause(double rate) { m_state["ys"]["Battle"]["BattleConfig"]["BASIC_TIME_SCALE"] = rate; m_state["Time"]["timeScale"] = rate; } #define SET_CONFIG_FLAG(n) .n = m_flag.n.load() Cracker::Config Cracker::get_config() { Config config = { .flag = { SET_CONFIG_FLAG(HOOKED_LUA_FUNCTION_TRACE), SET_CONFIG_FLAG(GLOBAL_SHIP_PROPERTIES_CRACK), SET_CONFIG_FLAG(FAST_STAGE_MOVE_CRACK), SET_CONFIG_FLAG(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT), SET_CONFIG_FLAG(REMOVE_HARD_MODE_SHIP_TYPE_LIMIT), SET_CONFIG_FLAG(FAKE_PLAYER_INFO), SET_CONFIG_FLAG(NO_BB_ANIMATION), SET_CONFIG_FLAG(NO_EMOTION_WARNING), SET_CONFIG_FLAG(OPSI_FAST_MOVE), SET_CONFIG_FLAG(GG_FACTOR), SET_CONFIG_FLAG(GLOBAL_SPEEDUP), SET_CONFIG_FLAG(EXERCISE_GOD_MOD), SET_CONFIG_FLAG(EXERCISE_MORE_POWER), SET_CONFIG_FLAG(FAST_WAVE), SET_CONFIG_FLAG(MONSTER_KILL_SELF), SET_CONFIG_FLAG(SKIP_BATTLE_CELEBRATE), SET_CONFIG_FLAG(BETTER_GLOBAL_SPEEDUP), SET_CONFIG_FLAG(NO_DAMAGE), SET_CONFIG_FLAG(OPSI_FORCE_AUTO), SET_CONFIG_FLAG(OPSI_NO_MAP_FOG), SET_CONFIG_FLAG(SKIP_SHIP_GAIN_SHOW), SET_CONFIG_FLAG(CHAPTER_FORCE_ENABLE_AUTO_FIGHT), SET_CONFIG_FLAG(CHAPTER_SKIP_PRECOMBAT), SET_CONFIG_FLAG(CHAPTER_AUTO_NEXT_BATTLE), SET_CONFIG_FLAG(CHAPTER_AUTO_AMBUSH), SET_CONFIG_FLAG(CHAPTER_AUTO_CLEAR), SET_CONFIG_FLAG(SKIP_STORY), SET_CONFIG_FLAG(INFINITE_BATTLE), SET_CONFIG_FLAG(SKIP_AIR_STRIKE_ANIMATION), SET_CONFIG_FLAG(AUTO_ONCE_AGAIN), }, .globle_ship_properties = m_globle_ship_properties, .global_speedup_rate = static_cast(m_global_speedup_rate), .gg_factor = m_gg_factor, .exercise_more_power_rate = static_cast(m_exercise_more_power_rate.load() * 100), .better_global_speedup_rate = static_cast(m_better_global_speedup_rate.load()), .chapter_auto_clear_step_interval = m_mover.interval.load(), }; return config; } #define IS_CONFIG_ENABLED(n) config.flag.n void Cracker::apply_config(Config& config) { if(IS_CONFIG_ENABLED(AUTO_ONCE_AGAIN)) { enable_auto_once_again(); } else { disable_auto_once_again(); } if(IS_CONFIG_ENABLED(SKIP_AIR_STRIKE_ANIMATION)) { enable_skip_air_strike_animation(); } else { disable_skip_air_strike_animation(); } if(IS_CONFIG_ENABLED(INFINITE_BATTLE)) { enable_infinite_battle(); } else { disable_infinite_battle(); } if(IS_CONFIG_ENABLED(HOOKED_LUA_FUNCTION_TRACE)) { enable_hooked_lua_function_trace(); } else { disable_hooked_lua_function_trace(); } if(IS_CONFIG_ENABLED(SKIP_STORY)) { enable_skip_story(); } else { disable_skip_story(); } if(IS_CONFIG_ENABLED(CHAPTER_AUTO_CLEAR)) { set_chapter_auto_clear_step_interval(config.chapter_auto_clear_step_interval); enable_chapter_auto_clear(); } else { disable_chapter_auto_clear(); } if(IS_CONFIG_ENABLED(CHAPTER_AUTO_AMBUSH)) { enable_chapter_auto_ambush(); } else { disable_chapter_auto_ambush(); } if(IS_CONFIG_ENABLED(CHAPTER_SKIP_PRECOMBAT)) { enable_chapter_skip_precombat(); } else { disable_chapter_skip_precombat(); } if(IS_CONFIG_ENABLED(CHAPTER_FORCE_ENABLE_AUTO_FIGHT)) { enable_chapter_force_enable_auto_fight(); } else { disable_chapter_force_enable_auto_fight(); } if(IS_CONFIG_ENABLED(CHAPTER_AUTO_NEXT_BATTLE)) { enable_chapter_auto_next_battle(); } else { disable_chapter_auto_next_battle(); } if(IS_CONFIG_ENABLED(SKIP_SHIP_GAIN_SHOW)) { enable_skip_ship_gain_show(); } else { disable_skip_ship_gain_show(); } if(IS_CONFIG_ENABLED(OPSI_FORCE_AUTO)) { enable_opsi_force_auto(); } else { disable_opsi_force_auto(); } if(IS_CONFIG_ENABLED(OPSI_NO_MAP_FOG)) { enable_opsi_no_map_fog(); } else { disable_opsi_no_map_fog(); } if(IS_CONFIG_ENABLED(ALL_SKIN)) { enable_all_skin(); } else { disable_all_skin(); } if(IS_CONFIG_ENABLED(GLOBAL_SHIP_PROPERTIES_CRACK)) { update_global_ship_properties(config.globle_ship_properties); enable_global_ship_properties_crack(); } else { disable_global_ship_properties_crack(); } if(IS_CONFIG_ENABLED(FAST_STAGE_MOVE_CRACK)) { enable_fast_stage_move(); } else { disable_fast_stage_move(); } if(IS_CONFIG_ENABLED(REMOVE_HARD_MODE_SHIP_PROPERTIES_LIMIT)) { enable_remove_hard_mode_ship_properties_limit(); } else { disable_remove_hard_mode_ship_properties_limit(); } if(IS_CONFIG_ENABLED(REMOVE_HARD_MODE_SHIP_TYPE_LIMIT)) { enable_remove_hard_mode_ship_type_limit(); } else { disable_remove_hard_mode_ship_type_limit(); } if(IS_CONFIG_ENABLED(NO_BB_ANIMATION)) { enable_no_bb_animation(); } else { disable_no_bb_animation(); } if(IS_CONFIG_ENABLED(NO_EMOTION_WARNING)) { enable_no_emotion_warning(); } else { disable_no_emotion_warning(); } if(IS_CONFIG_ENABLED(OPSI_FAST_MOVE)) { enable_opsi_fast_move(); } else { disable_opsi_fast_move(); } if(IS_CONFIG_ENABLED(GG_FACTOR)) { update_gg_factor(config.gg_factor); enable_gg_factor(); } else { disable_gg_factor(); } if(IS_CONFIG_ENABLED(GLOBAL_SPEEDUP)) { update_global_speedup_rate(config.global_speedup_rate); enable_global_speedup(); } else { disable_global_speedup(); } if(IS_CONFIG_ENABLED(EXERCISE_GOD_MOD)) { enable_exercise_god_mode(); } else { disable_exercise_god_mode(); } if(IS_CONFIG_ENABLED(EXERCISE_MORE_POWER)) { update_exercise_more_power_rate(config.exercise_more_power_rate / 100.0); enable_exercise_more_power(); } else { disable_exercise_more_power(); } if(IS_CONFIG_ENABLED(FAST_WAVE)) { enable_fast_wave(); } else { disable_fast_wave(); } if(IS_CONFIG_ENABLED(MONSTER_KILL_SELF)) { enable_monster_kill_self(); } else { disable_monster_kill_self(); } if(IS_CONFIG_ENABLED(SKIP_BATTLE_CELEBRATE)) { enable_skip_battle_celebrate(); } else { disable_skip_battle_celebrate(); } if(IS_CONFIG_ENABLED(BETTER_GLOBAL_SPEEDUP)) { update_better_global_speedup_rate(config.better_global_speedup_rate); enable_better_global_speedup(); } else { disable_better_global_speedup(); } if(IS_CONFIG_ENABLED(NO_DAMAGE)) { enable_no_damage(); } else { disable_no_damage(); } } void Cracker::load_cracker_with_pause() { m_lua_state_pauser.do_when_paused([&]() { SPDLOG_INFO("Load cracker with lua pause"); load_lua_resources(); hook_all_lua_functions(); load_real_lua_func(); }); } void Cracker::load_cracker_without_pause() { SPDLOG_INFO("Load cracker directly"); load_lua_resources(); hook_all_lua_functions(); load_real_lua_func(); } void Cracker::load_real_lua_func() { m_real_func.Chapter_IsAutoFight = m_state["Chapter"]["IsAutoFight"]; m_real_func.LevelStageView_tryAutoTrigger = m_state["LevelStageView"]["tryAutoTrigger"]; } void Cracker::ensure_data_proxies() { if (m_proxy_data_loaded) { return; } SPDLOG_INFO("Load data proxies"); Lua::Table proxies = m_state["pg"]["proxyRegister"]["data"]; for (int i = 1; i <= proxies.size(); ++i) { Lua::Table proxy = proxies[i]; std::string proxy_name = proxy["proxyName"]; if (proxy_name == "PlayerProxy") { Lua::Table proxy_map = proxy["facade"]["model"]["proxyMap"]; m_data_proxy.player = proxy_map["PlayerProxy"]; m_data_proxy.dock = proxy_map["BayProxy"]; m_data_proxy.storage = proxy_map["BagProxy"]; } else if (proxy_name == "WorldProxy") { m_data_proxy.world = proxy["facade"]["model"]["proxyMap"]["WorldProxy"]; } } m_proxy_data_loaded = true; } void Cracker::mover_stop(sol::this_state& L) { if (m_mover.moving) { if (m_mover.view.has_value()) { auto& view_v = m_mover.view.value(); std::optional contextData = view_v["contextData"]; if (contextData.has_value()) { auto& contextData_v = contextData.value(); std::optional chapterVO = contextData_v["chapterVO"]; if (chapterVO.has_value() && m_mover.path.has_value()) { if (!m_mover.path.value().empty()) { Lua::Table lastP = m_mover.path.value()[m_mover.path.value().size()]; std::optional fl = chapterVO.value()["fleet"]["line"]; if (fl.has_value()) { auto& fl_v = fl.value(); int fl_row = fl_v["row"]; int fl_column = fl_v["column"]; int lastP_row = lastP["row"]; int lastP_column = lastP["column"]; if (fl_row != lastP_row || fl_column != lastP_column) { fl_v["row"] = lastP_row; fl_v["column"] = lastP_column; } } } } } } } m_mover.currentStepSize = m_mover.stepSize; m_mover.lastMoveStartP.reset(); m_mover.lastTargetIdx = 0; m_mover.moving = false; } void Cracker::mover_go(sol::this_state& L, Lua::Table& view, Lua::Table& path) { if (view.empty()) { return; } m_mover.path = path; m_mover.idx = 1; m_mover.moving = true; m_mover.view = view; Lua::Table chapterVO = m_mover.view.value()["contextData"]["chapterVO"]; Lua::Table lastMoveStartP = m_state.create_table(); lastMoveStartP["row"] = chapterVO["fleet"]["line"]["row"]; lastMoveStartP["column"] = chapterVO["fleet"]["line"]["column"]; m_mover.lastMoveStartP = lastMoveStartP; m_mover.currentStepSize = m_mover.stepSize; m_mover.lastTargetIdx = 0; mover_step(L); } void Cracker::mover_step(sol::this_state& L) { std::optional chapterVO = m_mover.view.value()["contextData"]["chapterVO"]; if (!chapterVO.has_value()) { mover_stop(L); return; } Lua::Table fl = chapterVO.value()["fleet"]["line"]; int fl_row = fl["row"]; int fl_column = fl["column"]; if (m_mover.idx > 1 && m_mover.lastMoveStartP.has_value()) { auto& lastMoveStartP_v = m_mover.lastMoveStartP.value(); int lastMoveStartP_row = lastMoveStartP_v["row"]; int lastMoveStartP_column = lastMoveStartP_v["column"]; if (fl_row == lastMoveStartP_row && fl_column == lastMoveStartP_column) { int new_step = std::max(1, m_mover.currentStepSize - 1); if (new_step < m_mover.currentStepSize) { m_mover.currentStepSize = new_step; m_mover.idx = m_mover.lastTargetIdx - (m_mover.currentStepSize - 1); m_mover.idx = std::max(1, m_mover.idx); } else if (new_step == 1) { mover_stop(L); return; } } else { if (m_mover.currentStepSize < m_mover.stepSize) { m_mover.currentStepSize = m_mover.stepSize; } } } if (!m_mover.moving || m_mover.idx > m_mover.path.value().size()) { mover_stop(L); return; } Lua::Table lastMoveStartP = m_state.create_table(); lastMoveStartP["row"] = fl_row; lastMoveStartP["column"] = fl_column; m_mover.lastMoveStartP = lastMoveStartP; int targetIdx = std::min(m_mover.idx + m_mover.currentStepSize - 1, static_cast(m_mover.path.value().size())); Lua::Table p = m_mover.path.value()[targetIdx]; m_mover.lastTargetIdx = targetIdx; int p_row = p["row"]; int p_column = p["column"]; Lua::Table emit_args = m_state.create_table(); int id = chapterVO.value()["fleet"]["id"]; emit_args["type"] = m_lua_res.ChapterConst_OpMove; emit_args["id"] = id; emit_args["arg1"] = p_row; emit_args["arg2"] = p_column; m_lua_res.LevelStageView_emit(L, m_mover.view.value(), m_lua_res.LevelMediator2_ON_OP, emit_args); m_mover.idx = targetIdx + 1; if (m_mover.idx <= m_mover.path.value().size()) { Lua::Object tr = m_lua_res.Timer_New(L, [this](sol::this_state l, Lua::VariadicArgs args) { mover_step(l); }, m_mover.interval.load(), 1); m_lua_res.Timer_Start(L, tr); } else { mover_stop(L); } } bool Cracker::my_ChapterConst_IsBossCell(sol::this_state& L, Lua::Table& cell) { std::optional result = m_lua_res.ChapterConst_IsBossCell(L, cell); if (!result.has_value()) { return false; } auto& result_v = result.value(); if (result_v.get_type() == sol::type::nil) { return false; } return result_v.as(); } int Cracker::CntNonBoss(sol::this_state& L, Lua::Table& chap) { int n = 0; std::optional chap_cells = chap["cells"]; if (chap_cells.has_value()) { chap_cells->for_each([&](sol::object key, sol::object value) { if (!value.is()) { return; } Lua::Table cell = value.as(); std::optional cell_flag = cell["flag"]; if(cell_flag.has_value()) { if (!cell_flag->is()) { return; } int cell_attachment = cell["attachment"]; bool is_enemy_attach = m_lua_res.ChapterConst_IsEnemyAttach(L, cell_attachment); bool is_boss_cell = my_ChapterConst_IsBossCell(L, cell); if((cell_flag->as() == m_lua_res.ChapterConst_CellFlagActive) && (is_enemy_attach) && (!is_boss_cell)) { n += 1; } } }); } std::optional chap_champions = chap["champions"]; if(chap_champions.has_value()) { chap_champions->for_each([&](sol::object key, sol::object value) { if(!value.is()) { return; } Lua::Table cp = value.as(); std::optional cp_flag = cp["flag"]; if(cp_flag.has_value()) { if(!cp_flag->is()) { return; } bool is_boss_cell = my_ChapterConst_IsBossCell(L, cp); if((cp_flag->as() != m_lua_res.ChapterConst_CellFlagDisabled) && (!is_boss_cell)) { n += 1; } } }); } return n; } std::optional Cracker::NearestEnemy(sol::this_state& L, Lua::Table& chap, bool farming) { bool hasNoneBoss = CntNonBoss(L, chap) > 0; Lua::Table fl = chap["fleet"]["line"]; std::optional best; std::optional bestDist; auto IsValidEnemy = [&](Lua::Table& cell) { std::optional cell_flag_o = cell["flag"]; if (!cell_flag_o.has_value() || !cell_flag_o->is()) { return false; } int cell_flag = cell_flag_o.value().as(); if (cell_flag == m_lua_res.ChapterConst_CellFlagDisabled) { return false; } std::optional cell_attachment_o = cell["attachment"]; if (!cell_attachment_o.has_value()) { return false; } bool is_enemy_attach = m_lua_res.ChapterConst_IsEnemyAttach(cell_attachment_o.value()); if (!is_enemy_attach) { return false; } int cell_row = cell["row"]; int cell_column = cell["column"]; std::optional cell_data = m_lua_res.Chapter_getChapterCell(L, chap, cell_row, cell_column); if (cell_data.has_value()) { std::optional cell_data_flag_o = cell_data.value()["flag"]; if (!cell_data_flag_o.has_value() || !cell_data_flag_o->is()) { return false; } int cell_data_flag = cell_data_flag_o.value().as(); if (cell_data_flag == m_lua_res.ChapterConst_CellFlagDisabled) { return false; } } return true; }; auto CheckCell = [&] (Lua::Table& cell) { if (!IsValidEnemy(cell)) { return; } if (farming && hasNoneBoss) { if (bool is_boss_cell = m_lua_res.ChapterConst_IsBossCell(L, cell)) { return; } } int cell_row = cell["row"]; int cell_column = cell["column"]; int fl_row = fl["row"]; int fl_column = fl["column"]; if (cell_row == fl_row && cell_column == fl_column) { return; } std::tuple result = m_lua_res.Chapter_findPath(L, chap, m_lua_res.ChapterConst_SubjectPlayer, fl, cell); auto length = std::get<0>(result); auto& path = std::get<1>(result); if (length == 0) { return; } if (!bestDist.has_value()) { bestDist = length; best = Cracker::Cell{cell_row, cell_column}; } else { if (length < bestDist.value()) { bestDist = length; best = Cracker::Cell{cell_row, cell_column}; } } // SPDLOG_INFO("Check [{}, {}], fleet=[{}, {}], best = [{}, {}], bestDist = {}", cell_row, cell_column, fl_row, fl_column, best->row, best->column, bestDist.value()); }; std::optional chap_cells = chap["cells"]; if (chap_cells.has_value()) { chap_cells->for_each([&](sol::object key, sol::object value) { if (!value.is()) { return; } Lua::Table cell = value.as(); CheckCell(cell); }); } std::optional chap_champions = chap["champions"]; if(chap_champions.has_value()) { chap_champions->for_each([&](sol::object key, sol::object value) { if(!value.is()) { return; } Lua::Table cp = value.as(); CheckCell(cp); }); } return best; } #define FALLBACK_TO_ORIGINAL() \ m_original.LevelStageView_TryAutoFight(L, args); \ return; void Cracker::my_LevelStageView_TryAutoFight(sol::this_state& L, Lua::VariadicArgs& args) { Lua::Table self = args[0]; std::optional chap = self["contextData"]["chapterVO"]; if (!chap.has_value()) { FALLBACK_TO_ORIGINAL(); } bool is_auto_fight = m_real_func.Chapter_IsAutoFight(L, chap.value()); if (!is_auto_fight) { FALLBACK_TO_ORIGINAL(); } auto& chap_v = chap.value(); bool is_any_interactive = m_lua_res.Chapter_checkAnyInteractive(L, chap_v); if (is_any_interactive) { m_real_func.LevelStageView_tryAutoTrigger(L, self, sol::nil, true); return; } int chap_progress = chap_v["progress"]; if (chap_progress >= 100) { FALLBACK_TO_ORIGINAL(); } if (m_mover.moving) { return; } auto enemy = NearestEnemy(L, chap_v, true); if (!enemy.has_value()) { Lua::Object auto_fight_timer = m_lua_res.Timer_New(L, [self, chap, this](sol::this_state l, Lua::VariadicArgs) { bool auto_fight = m_real_func.Chapter_IsAutoFight(l, chap.value()); if (auto_fight) { Lua::Function TryAutoFight_func = self["TryAutoFight"]; TryAutoFight_func(l, self); } }, 1, 1); m_lua_res.Timer_Start(L, auto_fight_timer); return; } auto& enemy_v = enemy.value(); std::optional fleet = m_lua_res.Chapter_GetFleetOfDuty(L, chap_v, true); if (!fleet.has_value()) { return; } auto& fleet_v = fleet.value(); int fleet_row = fleet_v["line"]["row"]; int fleet_column = fleet_v["line"]["column"]; if (fleet_row == enemy_v.row && fleet_column == enemy_v.column) { m_real_func.LevelStageView_tryAutoTrigger(L, self, sol::nil, true); return; } Lua::Table enemy_cell = m_state.create_table(); enemy_cell["row"] = enemy_v.row; enemy_cell["column"] = enemy_v.column; Lua::Table fleet_line = fleet_v["line"]; std::tuple result = m_lua_res.Chapter_findPath(L, chap_v, m_lua_res.ChapterConst_SubjectPlayer, fleet_line, enemy_cell); auto& path = std::get<1>(result); int path_size = path.size(); if (path_size == 0) { return; } else { int path_row = path[path_size]["row"]; int path_column = path[path_size]["column"]; if (path_row != enemy_v.row || path_column != enemy_v.column) { path.add(enemy_cell); } } mover_go(L, self, path); } #define MODIFY_PROPERTY(name) \ if (new_properties.name >= 0) { \ properties[#name] = new_properties.name; \ } void Cracker::modify_ship_properties(Lua::Table& properties, const ShipProperties& new_properties) { MODIFY_PROPERTY(armor) MODIFY_PROPERTY(speed) MODIFY_PROPERTY(antiaircraft) MODIFY_PROPERTY(oxy_recovery_bench) MODIFY_PROPERTY(torpedo) MODIFY_PROPERTY(hit) MODIFY_PROPERTY(sonarRange) MODIFY_PROPERTY(attack_duration) MODIFY_PROPERTY(raid_distance) MODIFY_PROPERTY(oxy_recovery_surface) MODIFY_PROPERTY(oxy_recovery) MODIFY_PROPERTY(dodge) MODIFY_PROPERTY(luck) MODIFY_PROPERTY(reload) MODIFY_PROPERTY(oxy_cost) MODIFY_PROPERTY(durability) MODIFY_PROPERTY(air) MODIFY_PROPERTY(oxy_max) MODIFY_PROPERTY(cannon) MODIFY_PROPERTY(antisub) } void Cracker::clear_hard_mode_ship_properties_limit(Lua::Table& t) { for (int i = 1, n = t.size(); i <= n; ++i) { sol::table fleet = t[i]; for (int j = 1, m = fleet.size(); j <= m; ++j) { sol::table team = fleet[j]; for (int k = 1, l = team.size(); k <= l; ++k) { team[k] = 0.0; } } } }