Update GaCy-GO

This commit is contained in:
CAtlas0784 committed 2026-10-28 18:54:43 +07:00
1 parent da8a32f60d
commit 0cbe460e49
345 files changed
+98202 -77559

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
if not _G.__GAC_FACECAM_PROBE_BATTLE then
local ok,err=pcall(function()
local flags=CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags),60)
local manager=CS.RPG.Client.GlobalVars.s_GamePhaseManager
local phase=typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase',flags):GetValue(manager)
if not phase or not typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase) then return end
_G.__GAC_FACECAM_PROBE_BATTLE=true
local t=phase:GetType()
CS.UnityEngine.Debug.Log('[FaceCam battle] phase='..tostring(t))
for _,members in ipairs({t:GetFields(flags),t:GetProperties(flags)}) do
for i=0,math.min(members.Length-1,199) do
local m=members[i];local n=tostring(m.Name):lower()
if n:find('actor') or n:find('entity') or n:find('avatar') or n:find('turn') or n:find('battle') or n:find('stage') then
CS.UnityEngine.Debug.Log('[FaceCam battle] member='..tostring(m.Name)..' detail='..tostring(m))
end
end
end
end)
if not ok then _G.__GAC_FACECAM_PROBE_BATTLE=true;CS.UnityEngine.Debug.Log('[FaceCam battle] error='..tostring(err)) end
end
+96 -4
View File
@@ -9,7 +9,28 @@ local GameObject = CS.UnityEngine.GameObject
_G.__FREECAM_ENABLED = _G.__FREECAM_ENABLED or false
_G.__FREECAM_SPEED = _G.__FREECAM_SPEED or 15.0
local RAINBOW_TEXT = "<color=#bb00ff>นี่คือเวอร์ชั่นทดสอบของเกย์ </color> <color=#FF0000>Ho</color><color=#FF7F00>nk</color><color=#FFFF00>ai</color> <color=#00FF00>St</color><color=#0000FF>ar</color> <color=#4B0082>Gay</color>"
local WATERMARK_PREFIX = "<color=#bb00ff>นี่คือเวอร์ชั่นทดสอบของเกย์ </color> "
local WATERMARK_NAME = "Honkai Star Gay"
-- A closely spaced hue gradient sweeps from right to left.
-- Keep the server name, UID, and the Freecam controls independent.
local function rainbow_watermark(now)
local parts = {WATERMARK_PREFIX}
for i = 1, #WATERMARK_NAME do
local h = ((now * 0.12 + (i - 1) * 0.035) % 1) * 6
local x = 1 - math.abs(h % 2 - 1)
local r, g, b
if h < 1 then r, g, b = 1, x, 0
elseif h < 2 then r, g, b = x, 1, 0
elseif h < 3 then r, g, b = 0, 1, x
elseif h < 4 then r, g, b = 0, x, 1
elseif h < 5 then r, g, b = x, 0, 1
else r, g, b = 1, 0, x end
parts[#parts + 1] = string.format("<color=#%02X%02X%02X>%s</color>",
math.floor(r * 255 + 0.5), math.floor(g * 255 + 0.5), math.floor(b * 255 + 0.5), WATERMARK_NAME:sub(i, i))
end
return table.concat(parts)
end
local function show_ui(msg)
pcall(function()
@@ -41,6 +62,42 @@ local function get_active_cam()
return nil
end
-- Resolve the character through the active camera's own follow target. A
-- humanoid head bone gives a model-relative point as the character moves.
-- Do not guess a world position if the Client has no usable target.
local function facecam_target(cam)
local ok, head, root = pcall(function()
local brain = cam:GetComponent(typeof(CS.Cinemachine.CinemachineBrain))
local virtual = brain and brain.ActiveVirtualCamera
local follow = virtual and virtual.Follow
if not follow then return nil, nil end
local animator = follow:GetComponentInChildren(typeof(CS.UnityEngine.Animator))
if not animator and follow.root then
animator = follow.root:GetComponentInChildren(typeof(CS.UnityEngine.Animator))
end
if not animator or not animator.isHuman then return nil, nil end
local bone = animator:GetBoneTransform(CS.UnityEngine.HumanBodyBones.Head)
return bone, animator.transform
end)
if ok then return head, root end
return nil, nil
end
local function update_facecam(cam, settings)
local head, root = _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT
if not head or not root or not head.gameObject.activeInHierarchy then return false end
local distance = tonumber(settings.distance) or 2
distance = math.max(0.5, math.min(5, distance))
local target = head.position
local direction = root.forward
local position = target + direction * distance
cam.transform.position = position
cam.transform.rotation = CS.UnityEngine.Quaternion.LookRotation(target - position, Vector3.up)
_G.__FC_POS = cam.transform.position
_G.__FC_EULER = cam.transform.eulerAngles
return true
end
-- Detach only the active camera brain and restore its recorded state. Virtual
-- cameras continue updating animation/effect state; do not scan/disable every
-- virtual camera on each render frame.
@@ -174,17 +231,42 @@ function _G.__FREECAM_TICK()
_G.__FC_LAST_TIME = nil
local cam = get_active_cam()
if _G.__FREECAM_ENABLED then
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
if settings.mode == "facecam" then
local head, root
if cam then head, root = facecam_target(cam) end
if not head or not root then
_G.__FREECAM_ENABLED = false
show_ui("<color=#FFAA44>FaceCam: character head unavailable</color>")
CS.UnityEngine.Debug.Log("[FaceCam] active camera follow target has no humanoid head")
return
end
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = head, root
end
if cam then
_G.__FC_POS = cam.transform.position
_G.__FC_EULER = cam.transform.eulerAngles
set_cinemachine_enabled(cam, false)
end
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look</color>")
if settings.mode == "facecam" then
show_ui("<color=#00FF88><b>[FaceCam: ON]</b> | Tab: Close</color>")
else
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look</color>")
end
else
if cam then
set_cinemachine_enabled(cam, true)
end
show_ui(RAINBOW_TEXT)
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil
show_ui(rainbow_watermark(Time.unscaledTime or 0))
end
end
if not _G.__FREECAM_ENABLED then
local now = Time.unscaledTime or 0
if now >= (_G.__FC_WATERMARK_NEXT or 0) then
_G.__FC_WATERMARK_NEXT = now + 0.05
show_ui(rainbow_watermark(now))
end
end
@@ -197,6 +279,16 @@ function _G.__FREECAM_TICK()
if _G.__FREECAM_ENABLED then
local cam = get_active_cam()
if cam then
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
if settings.mode == "facecam" then
if not update_facecam(cam, settings) then
set_cinemachine_enabled(cam, true)
_G.__FREECAM_ENABLED = false
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = nil, nil
show_ui("<color=#FFAA44>FaceCam: character unavailable</color>")
end
return
end
set_cinemachine_enabled(cam, false)
if not _G.__FC_POS then
@@ -287,7 +379,7 @@ if not _G.__FREECAM_REGISTERED then
end
if not _G.__FREECAM_ENABLED then
show_ui(RAINBOW_TEXT)
show_ui(rainbow_watermark(Time.unscaledTime or 0))
end
-- Restore the in-memory controls before the Client tears down its settings.
+190
View File
@@ -0,0 +1,190 @@
-- Color only the captured latency widget. Signal sprites contain all bars in
-- one atlas image: three UV slices preserve its alpha without replacing art.
do
local CS=CS
local U=CS.UnityEngine
local UI=U.UI
local VERSION=7
local SPEED,SPACING=0.16,0.13
local PATHS={
'UIRoot/Page/MazeMainPage(Clone)/RightTopArea/NetStatusPanel/NetStatusPanel(Clone)',
'UIRoot/Page/BattleGamePhaseUI(Clone)/FunctionArea/NetStatusPanel/NetStatusPanel',
}
local old=_G.__GAC_PING_RAINBOW_STATE
local function event(target,name,op,fn)
return pcall(function()target[name](op,fn)end)
end
if old and old.version~=VERSION then
if old.callback then
-- v5 tried both event binding forms; remove both possible subscriptions.
for _,entry in ipairs({{U.Camera,'onPreRender'},{U.Camera,'onPreCull'},{U.Application,'onBeforeRender'},{U.Canvas,'willRenderCanvases'}})do
event(entry[1],entry[2],'-',old.callback);event(entry[1],entry[2],'-',old.callback)
end
end
if old.dispose then pcall(old.dispose)end
old=nil
end
local s=old or {version=VERSION,targets={},nextScan=0,lastFrame=-1,nextColor=0}
_G.__GAC_PING_RAINBOW_STATE=s
local function log(message)U.Debug.Log('[GaC Ping Colors] '..message)end
local function color(h,a)
local v=(h%1)*6;local x=1-math.abs(v%2-1)
if v<1 then return U.Color(1,x,0,a)
elseif v<2 then return U.Color(x,1,0,a)
elseif v<3 then return U.Color(0,1,x,a)
elseif v<4 then return U.Color(0,x,1,a)
elseif v<5 then return U.Color(x,0,1,a)
else return U.Color(1,0,x,a)end
end
local function dispose_target(target)
for _,icon in ipairs(target.icons or {})do
for _,slice in ipairs(icon.slices or {})do pcall(function()U.Object.Destroy(slice.gameObject)end)end
if icon.mask then pcall(function()U.Object.Destroy(icon.mask)end)end
pcall(function()icon.image.enabled=icon.enabled;icon.image.color=icon.color end)
end
if target.text then pcall(function()
if target.text.text==target.rendered then target.text.text=target.base or ''end
target.text.color=target.textColor;target.text.supportRichText=target.rich
end)end
if target.mesh then pcall(function()U.Object.Destroy(target.mesh)end)end
end
s.dispose=function()for _,target in pairs(s.targets)do dispose_target(target)end;s.targets={}end
local function white_mask(sprite)
local r=sprite.textureRect
local width,height=math.floor(r.width),math.floor(r.height)
if width<=0 or height<=0 or width>128 or height>128 then error('signal mask outside small-widget bound')end
local texture,temporary
local previous=U.RenderTexture.active
local ok,err=pcall(function()
local source=sprite.texture
temporary=U.RenderTexture.GetTemporary(width,height,0,U.RenderTextureFormat.ARGB32)
U.Graphics.Blit(source,temporary,U.Vector2(r.width/source.width,r.height/source.height),U.Vector2(r.x/source.width,r.y/source.height))
U.RenderTexture.active=temporary
texture=U.Texture2D(width,height,U.TextureFormat.RGBA32,false)
texture:ReadPixels(U.Rect(0,0,width,height),0,0)
local pixels=texture:GetPixels32()
for i=0,pixels.Length-1 do pixels[i]=U.Color32(255,255,255,pixels[i].a)end
texture:SetPixels32(pixels);texture:Apply(false,true)
texture.name='GaCPingAlphaMask';texture.filterMode=U.FilterMode.Bilinear
texture.wrapMode=U.TextureWrapMode.Clamp
end)
U.RenderTexture.active=previous
if temporary then U.RenderTexture.ReleaseTemporary(temporary)end
if not ok then if texture then U.Object.Destroy(texture)end;error(err)end
return texture
end
local function make_slices(icon)
local sprite=icon.image.sprite
if not sprite then error('signal sprite unavailable')end
if sprite.packingRotation~=U.SpritePackingRotation.None then error('rotated signal atlas not supported')end
local rect=sprite.textureRect
if sprite.textureRectOffset.x~=0 or sprite.textureRectOffset.y~=0 or rect.width~=sprite.rect.width or rect.height~=sprite.rect.height then error('trimmed signal sprite not supported')end
icon.mask=white_mask(sprite)
for i=0,2 do
local go=U.GameObject('GaCPingColorSlice'..tostring(i),typeof(U.RectTransform),typeof(UI.RawImage))
local rt=go:GetComponent(typeof(U.RectTransform))
-- Track immediately so a later binding failure cannot leave an overlay.
local raw=go:GetComponent(typeof(UI.RawImage));icon.slices[#icon.slices+1]=raw
rt:SetParent(icon.image.transform,false)
rt.anchorMin=U.Vector2(i/3,0);rt.anchorMax=U.Vector2((i+1)/3,1)
rt.offsetMin=U.Vector2.zero;rt.offsetMax=U.Vector2.zero
raw.raycastTarget=false;raw.texture=icon.mask
raw.uvRect=U.Rect(i/3,0,1/3,1)
end
icon.image.enabled=false
end
local function discover(path)
local root=U.GameObject.Find(path)
if not root then return end
local target={root=root,id=root:GetInstanceID(),icons={}}
local ok,err=pcall(function()
local connected=root.transform:Find('Connected')
if not connected then error('Connected node unavailable')end
local time=connected:Find('Time');target.text=time and time:GetComponent(typeof(UI.Text))
if not target.text then error('latency Text unavailable')end
target.textColor=target.text.color;target.rich=target.text.supportRichText
target.mesh=U.Mesh();target.mesh.name='GaCPingLatencyColors'
for _,name in ipairs({'Level1','Level2','Level3'})do
local t=connected:Find(name);local img=t and t:GetComponent(typeof(UI.Image))
if img then target.icons[#target.icons+1]={image=img,enabled=img.enabled,color=img.color,slices={}}end
end
if #target.icons==0 then error('signal level Images unavailable')end
end)
if not ok then dispose_target(target);error(err)end
log('READY '..path..' icons='..tostring(#target.icons)..' mode=white-alpha-slices/text-vertex-colors real-latency=true')
return target
end
_G.__GAC_PING_RAINBOW_TICK=function()
local frame=U.Time.frameCount
if s.lastFrame==frame then return end;s.lastFrame=frame
local now=U.Time.unscaledTime
for _,path in ipairs(PATHS)do
local ok,err=pcall(function()
local target=s.targets[path]
if target then
local alive=pcall(function()return target.root.transform.childCount end)
if not alive then dispose_target(target);s.targets[path]=nil;target=nil end
end
if not target and now>=s.nextScan then target=discover(path);s.targets[path]=target end
if not target or not target.root.activeInHierarchy then return end
local phase=(now*SPEED)%1
for _,icon in ipairs(target.icons)do
if icon.image.gameObject.activeInHierarchy then
if #icon.slices==0 then make_slices(icon)end
for i,slice in ipairs(icon.slices)do slice.color=color(phase+(i-1)*SPACING,icon.color.a)end
end
end
-- Color the freshly generated native glyph mesh at the canvas boundary.
-- Never rewrite latency text: number changes cannot expose an untagged
-- cyan/white frame between a native setter and a rich-text repaint.
local text=target.text;target.base=tostring(text.text or '')
-- Unity 2019 exposes SetMesh but no CanvasRenderer.GetMesh. Its Text
-- generator retains the same glyph quads used by Text.OnPopulateMesh.
-- Captured here: "10ms" has 16 vertices, all four quads populated; this
-- engine does not append the unused quad found in older uGUI sources.
local verts=text.cachedTextGenerator.verts
local count=verts.Count
if count>0 and count<=512 and count%4==0 then
if target.vertexCount~=count then
target.mesh:Clear()
target.vertexCount=count
target.positions=CS.System.Array.CreateInstance(typeof(U.Vector3),count)
target.uvs=CS.System.Array.CreateInstance(typeof(U.Vector2),count)
target.colors=CS.System.Array.CreateInstance(typeof(U.Color32),count)
target.indices=CS.System.Array.CreateInstance(typeof(CS.System.Int32),count/4*6)
for q=0,count/4-1 do
local v=q*4;local t=q*6
target.indices[t]=v;target.indices[t+1]=v+1;target.indices[t+2]=v+2
target.indices[t+3]=v+2;target.indices[t+4]=v+3;target.indices[t+5]=v
end
end
local scale=1/text.pixelsPerUnit
local offset=text:PixelAdjustPoint(U.Vector2.zero)
for i=0,count-1 do
local vertex=verts[i];local pos=vertex.position
target.positions[i]=U.Vector3(pos.x*scale+offset.x,pos.y*scale+offset.y,pos.z*scale)
target.uvs[i]=U.Vector2(vertex.uv0.x,vertex.uv0.y)
local c=color(phase+(3+math.floor(i/4))*SPACING,1)
target.colors[i]=U.Color32(math.floor(c.r*255+0.5),math.floor(c.g*255+0.5),math.floor(c.b*255+0.5),vertex.color.a)
end
target.mesh.vertices=target.positions;target.mesh.uv=target.uvs
target.mesh.colors32=target.colors;target.mesh.triangles=target.indices
text.canvasRenderer:SetMesh(target.mesh)
end
end)
if not ok then
if s.targets[path]then dispose_target(s.targets[path]);s.targets[path]=nil end
if s.error~=tostring(err)then s.error=tostring(err);log('ERROR '..s.error)end
end
end
if now>=s.nextScan then s.nextScan=now+1 end
end
if not s.callback then
s.callback=function()if _G.__GAC_PING_RAINBOW_TICK then _G.__GAC_PING_RAINBOW_TICK()end end
s.registered=event(U.Canvas,'willRenderCanvases','+',s.callback)
if not s.registered then s.registered=event(U.Application,'onBeforeRender','+',s.callback)end
log('CALLBACK registered='..tostring(s.registered))
end
-- The heartbeat only installs/replaces the callback. A heartbeat-time paint
-- must not suppress the later canvas paint for that same frame.
end