pre cw with probe and a bunch script to read game behavior

This commit is contained in:
CAtlas0784 committed 2026-10-21 20:40:13 +07:00
1 parent 5120219ae2
commit da8a32f60d
310 files changed
+219693 -872

No files matched your search

+247
View File
@@ -0,0 +1,247 @@
-- Extend the Client's existing M list. Game IDs/labels arrive from the
-- server-validated NavigationProfiles resource; this script owns no map IDs.
_G.__GAC_NAVIGATION_TICK = function()
local now = CS.UnityEngine.Time.unscaledTime
local state = _G.__GAC_NAVIGATION_STATE
if not state then
state = { next_check = 0, panels = setmetatable({}, { __mode = 'k' }) }
_G.__GAC_NAVIGATION_STATE = state
end
if now >= state.next_check and _G.__GAC_NAVIGATION_PROFILES then
state.next_check = now + 0.25
local ok, err = pcall(function()
local page = CS.RPG.Client.GlobalVars.s_UIManager:GetPageByName('SwitchAreaPage')
if not page or not page.Trans or not page.Trans.gameObject.activeInHierarchy then return end
local regional = page.LuaTable._regional_detail_panel
local list = regional and regional._map_area_list_panel
if not list or not list._tabs or not list._binder then return end
local installed = state.panels[list]
if not installed then installed = {}; state.panels[list] = installed end
state.step = 'reflection'
if not state.reflection then
-- System.Reflection.BindingFlags: Instance|Static|Public|NonPublic.
local all_members = 60
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), all_members)
state.reflection = {
flags = flags,
clone_method = typeof(CS.System.Object):GetMethod('MemberwiseClone', flags),
empty_args = CS.System.Array.CreateInstance(typeof(CS.System.Object), 0),
invoke_flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 0),
}
end
local flags = state.reflection.flags
local clone_method = state.reflection.clone_method
local empty_args = state.reflection.empty_args
local invoke_flags = state.reflection.invoke_flags
local function clone(value) return clone_method:Invoke(value, invoke_flags, nil, empty_args, nil) end
local function set_entry(sub, id)
-- Copy through a typed array so reflection receives a boxed
-- UInt32 rather than Lua's numeric type. The setter is private
-- and is not included in the generated Lua property wrapper.
local typed = CS.System.Array.CreateInstance(typeof(CS.System.UInt32), 1)
typed[0] = id
local args = CS.System.Array.CreateInstance(typeof(CS.System.Object), 1)
CS.System.Array.Copy(typed, args, 1)
sub:GetType():GetMethod('set_EntryID', flags):Invoke(sub, invoke_flags, nil, args, nil)
if sub.EntryID ~= id then error('EntryID backing field was not updated') end
end
local function apply_teleport_entry()
local sub = list.__gac_selected_variant or page.LuaTable._context.cur_sub_tab
local detail = regional._symbol_detail_panel
local teleport = detail and detail._map_teleport_detail_panel
if not sub or not teleport or teleport.entrance_id == nil then return end
for _, profile in ipairs(_G.__GAC_NAVIGATION_PROFILES) do
if sub.FloorID == profile.floor_id and
(sub.EntryID == profile.town_entry_id or sub.EntryID == profile.story_entry_id) then
if teleport.entrance_id ~= sub.EntryID then
CS.UnityEngine.Debug.Log('[GaC Navigation] Teleport entry '..teleport.entrance_id..' -> '..sub.EntryID)
teleport.entrance_id = sub.EntryID
end
return
end
end
end
local function select_profile(tab)
if not tab then return end
local sub = tab:GetSubTabByIndex(0)
if not sub then return end
for _, profile in ipairs(_G.__GAC_NAVIGATION_PROFILES) do
if sub.FloorID == profile.floor_id then
local id = tab.ID == profile.story_entry_id and profile.story_entry_id or profile.town_entry_id
set_entry(sub, id)
-- Native selection normalizes tabs by FloorID. Restore
-- the selected variant after that callback, so the
-- symbol/teleport panel receives its real EntryID.
page.LuaTable._context.cur_sub_tab = sub
regional._context.cur_sub_tab = sub
regional._cur_sub_tab = sub
list._selected_sub_tab = sub
list.__gac_selected_variant = sub
apply_teleport_entry()
CS.UnityEngine.Debug.Log('[GaC Navigation] Selected entry='..id..' floor='..sub.FloorID)
return
end
end
list.__gac_selected_variant = nil
end
for _, profile in ipairs(_G.__GAC_NAVIGATION_PROFILES) do
local current = installed[profile.floor_id]
if current then
current.id = profile.story_entry_id
current.label = profile.story_label
local found = false
for _, tab in ipairs(list._tabs) do
if tab.ID == profile.story_entry_id then found = true; break end
end
if not found then installed[profile.floor_id] = nil end
end
if not installed[profile.floor_id] then
for index, tab in ipairs(list._tabs) do
if not tab.IsComposedTab and tab.ID ~= profile.story_entry_id then
local sub = tab:GetSubTabByIndex(0)
if sub and sub.FloorID == profile.floor_id then
local row_field = tab:GetType():GetField('_Row', flags)
if not row_field then return end
state.step = 'clone tab'
local story = clone(tab)
state.step = 'clone subtab'
local story_sub = clone(sub)
state.step = 'clone row'
local story_row = clone(row_field:GetValue(tab))
-- Distinct local list identity, derived from the real entrance.
-- This does not register a new game floor or entrance.
state.step = 'set row'
story_row.ID = profile.story_entry_id
row_field:SetValue(story, story_row)
set_entry(story_sub, profile.story_entry_id)
story_sub:GetType():GetField('<Parent>k__BackingField', flags):SetValue(story_sub, story)
story:GetType():GetField('SubTabData', flags):SetValue(story, story_sub)
set_entry(sub, profile.town_entry_id)
table.insert(list._tabs, index + 1, story)
if regional._all_tabs ~= list._tabs then
for i, candidate in ipairs(regional._all_tabs or {}) do
if candidate == tab then table.insert(regional._all_tabs, i + 1, story); break end
end
end
installed[profile.floor_id] = { tab = story, id = profile.story_entry_id, label = profile.story_label }
state.step = 'refresh list'
list:refresh_map_area_list()
CS.UnityEngine.Debug.Log('[GaC Navigation] Added Story option for floor ' .. profile.floor_id)
break
end
end
end
end
end
local reordered = false
for _, profile in ipairs(_G.__GAC_NAVIGATION_PROFILES) do
local town_index, story_index
for i, tab in ipairs(list._tabs) do
local sub = tab:GetSubTabByIndex(0)
if sub and sub.FloorID == profile.floor_id then
if tab.ID == profile.story_entry_id then story_index = i else town_index = i end
end
end
if town_index and story_index and story_index ~= town_index + 1 then
local story = table.remove(list._tabs, story_index)
if story_index < town_index then town_index = town_index - 1 end
table.insert(list._tabs, town_index + 1, story)
reordered = true
end
end
if reordered then list:refresh_map_area_list() end
local selected_variant = list.__gac_selected_variant
local current_sub = page.LuaTable._context.cur_sub_tab
if selected_variant and current_sub and current_sub.FloorID == selected_variant.FloorID then
page.LuaTable._context.cur_sub_tab = selected_variant
regional._context.cur_sub_tab = selected_variant
regional._cur_sub_tab = selected_variant
list._selected_sub_tab = selected_variant
end
local detail = regional._symbol_detail_panel
local teleport = detail and detail._map_teleport_detail_panel
if teleport and not teleport.__gac_navigation_transfer_hooked then
teleport.__gac_navigation_transfer_hooked = true
-- The native icon stores its own entrance_id. Synchronize it
-- after view setup and immediately before either transfer path;
-- changing the selected SubTabData alone does not update it.
for _, method in ipairs({'_setup_teleport_view', '_on_btn_transfer_click', '_on_teleport_btn_transfer_click'}) do
local original = teleport[method]
if type(original) == 'function' then
if method == '_setup_teleport_view' then
teleport[method] = function(...)
local result = original(...)
apply_teleport_entry()
return result
end
else
teleport[method] = function(...)
apply_teleport_entry()
return original(...)
end
end
end
end
end
apply_teleport_entry()
-- LoopListView recycles rows. Set the label only on the row bound to
-- our Story object, after the Client has refreshed its localized title.
for _, child in ipairs(list._children or {}) do
if child._callback and (child._callback ~= child.__gac_navigation_callback or child.__gac_navigation_schema ~= 3) then
local row = child
if row._callback ~= row.__gac_navigation_callback then row.__gac_navigation_original = row._callback end
row.__gac_navigation_schema = 3
row.__gac_navigation_callback = function(...)
local clicked_tab = row._tab
local result = row.__gac_navigation_original(...)
local selected, failure = pcall(select_profile, clicked_tab)
if not selected then CS.UnityEngine.Debug.Log('[GaC Navigation] Selection failed: '..tostring(failure)) end
return result
end
row._callback = row.__gac_navigation_callback
end
for _, item in pairs(installed) do
if child._tab and child._tab.ID == item.id and child._binder and not child._binder.is_destroyed then
if child._binder.text_title then
child._binder.text_title:SetSimpleText(item.label)
end
if child._binder.text_item_title then
child._binder.text_item_title:SetSimpleText(item.label)
end
end
end
if child._tab and child._binder and not child._binder.is_destroyed and _G.__GAC_NAVIGATION_CURRENT_ENTRY then
local sub = child._tab:GetSubTabByIndex(0)
if sub then
for _, profile in ipairs(_G.__GAC_NAVIGATION_PROFILES) do
if sub.FloorID == profile.floor_id and child._binder.node_player_mark then
local id = child._tab.ID == profile.story_entry_id and profile.story_entry_id or profile.town_entry_id
child._binder.node_player_mark.gameObject:SetActive(id == _G.__GAC_NAVIGATION_CURRENT_ENTRY)
if id == profile.town_entry_id then
if child._binder.text_title then child._binder.text_title:SetTextID(child._tab.Name) end
if child._binder.text_item_title then child._binder.text_item_title:SetTextID(child._tab.Name) end
end
end
end
end
end
end
end)
local failure = tostring(state.step) .. ': ' .. tostring(err)
if not ok and state.last_error ~= failure then
state.last_error = failure
CS.UnityEngine.Debug.Log('[GaC Navigation] ' .. failure)
end
if not ok then state.next_check = now + 5 end
end
end
if not _G.__GAC_NAVIGATION_REGISTERED then
local ok = pcall(function()
CS.UnityEngine.Application.onBeforeRender('+', function()
if _G.__GAC_NAVIGATION_TICK then _G.__GAC_NAVIGATION_TICK() end
end)
end)
_G.__GAC_NAVIGATION_REGISTERED = ok
end
_G.__GAC_NAVIGATION_TICK()