-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathui.lua
More file actions
180 lines (180 loc) · 10.2 KB
/
Copy pathui.lua
File metadata and controls
180 lines (180 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
local UI={}
function UI.install(L)
local old_update,old_key=love.update,love.keypressed
local old_mouse,old_release=love.mousepressed,love.mousereleased
local old_move=love.mousemoved
local offset={x=-0.15,y=-0.15};local drag
L.view={count='0',rate='0 /s',stage='Ready',target='10K',tags='0'}
L.target=10000
local panel,signature
local function short(n)
if n>=1000000 then return string.format('%.1fM',n/1000000) end
if n>=10000 then return string.format('%.0fK',n/1000) end
return tostring(n)
end
local function label(value,scale,colour,ref)
local c={scale=scale,colour=colour or G.C.UI.TEXT_LIGHT,shadow=true}
if ref then c.ref_table=L.view;c.ref_value=value else c.text=value end
return {n=G.UIT.T,config=c}
end
local function row(nodes)
return {n=G.UIT.R,config={align='cm',padding=0.035},nodes=nodes}
end
local function button(name,fn,colour,w)
return UIBox_button{label={name},button=fn,colour=colour,minw=w or 1.45,minh=0.48,scale=0.32,padding=0.045,col=true}
end
local function tags_indicator()
return {n=G.UIT.C,config={align='cm',minw=1.45,minh=0.48,padding=0.025,r=0.1,colour=G.C.BLUE,emboss=0.05},nodes={
{n=G.UIT.R,config={align='cm'},nodes={label('Double tags',0.18)}},
{n=G.UIT.R,config={align='cm'},nodes={label('tags',0.28,G.C.MONEY,true)}}}}
end
local function callback(name,fn)
G.FUNCS[name]=function() L.safe(fn);signature=nil end
end
local function rebuild()
local keep_handle
if panel and not panel.REMOVED and panel.ghost_expanded~=L.visible then
local handle=panel:get_UIE_by_ID('ghost_loop_handle')
if handle then keep_handle={x=handle.T.x+handle.T.w/2,y=handle.T.y+handle.T.h/2} end
end
if panel and not panel.REMOVED then panel:remove() end
callback('ghost_loop_toggle',function() L.visible=not L.visible end)
callback('ghost_loop_pause',L.pause)
callback('ghost_loop_stop',L.cancel)
callback('ghost_loop_new',function()
if not L.confirm_new then L.confirm_new=true else L.confirm_new=false;L.new_run() end
end)
callback('ghost_loop_guide',function() L.start_route(L.route_next and L.route_next>1 and not L.route_complete) end)
callback('ghost_loop_run',function() L.start_cycles(L.target) end)
callback('ghost_loop_count',function()
local choices={1000,10000,100000,1000000,10000000}
for i,v in ipairs(choices) do if v==L.target then L.target=choices[i%#choices+1];return end end
end)
local handle=UIBox_button{label={L.visible and '-' or '+'},button='ghost_loop_toggle',id='ghost_loop_handle',
colour=G.C.GREY,minw=0.4,minh=0.48,scale=0.32,padding=0.045,col=true}
local nodes
if L.visible then
nodes={{n=G.UIT.R,config={align='cm',padding=0.035},nodes={
{n=G.UIT.C,config={id='ghost_loop_title',align='cl',minw=2.65,minh=0.48},nodes={label('GHOST LOOP',0.35)}},handle}}}
nodes[#nodes+1]=row({label('count',0.58,G.C.MONEY,true),label(' cycles',0.28)})
nodes[#nodes+1]=row({label('rate',0.32,G.C.GREEN,true)})
nodes[#nodes+1]=row({label('stage',0.28,nil,true)})
if L.running then
nodes[#nodes+1]=row({button(L.paused and 'Resume' or 'Pause','ghost_loop_pause',G.C.BLUE),tags_indicator()})
nodes[#nodes+1]=row({
{n=G.UIT.C,config={align='cm',minw=1.45,minh=0.48},nodes={label(short(L.mode=='cycles' and L.batch_count or L.target),0.32)}},
button('Stop','ghost_loop_stop',G.C.RED)})
else
nodes[#nodes+1]=row({button(L.confirm_new and 'Confirm' or 'New run','ghost_loop_new',G.C.RED),tags_indicator()})
nodes[#nodes+1]=row({button(short(L.target),'ghost_loop_count',G.C.GREY),button('Run','ghost_loop_run',G.C.GREEN)})
end
else nodes={handle} end
panel=UIBox{definition={n=G.UIT.ROOT,config={align='cm',padding=L.visible and 0.12 or 0,
r=L.visible and 0.12 or nil,colour=L.visible and G.C.UI.BACKGROUND_DARK or {0,0,0,0},emboss=L.visible and 0.08 or nil},nodes=nodes},
config={align='bri',offset=offset,major=G.ROOM_ATTACH,bond='Weak'}}
panel.ghost_expanded=L.visible
if keep_handle then
local next_handle=panel:get_UIE_by_ID('ghost_loop_handle')
offset.x=offset.x+keep_handle.x-next_handle.T.x-next_handle.T.w/2
offset.y=offset.y+keep_handle.y-next_handle.T.y-next_handle.T.h/2
panel:align_to_major();panel:hard_set_VT();panel.UIRoot:initialize_VT()
end
panel.attention_text=true
L.panel=panel
end
local last_t,last_c,last_cash=love.timer.getTime(),0,0
local frame_at=love.timer.getTime()
love.update=function(dt)
local frame_start=love.timer.getTime()
if L.running and L.fast then
local ms=(frame_start-frame_at)*1000
L.max_frame_ms=math.max(L.max_frame_ms or 0,ms)
if ms>100 then L.slow_frames=(L.slow_frames or 0)+1 end
end
frame_at=frame_start
old_update(dt)
L.safe(function() L.update(dt) end)
if L.running and L.fast then L.max_update_ms=math.max(L.max_update_ms or 0,(love.timer.getTime()-frame_start)*1000) end
if not G or not G.ROOM_ATTACH or not G.FUNCS or not UIBox_button then return end
local now=love.timer.getTime();local count=L.cycles+L.money_cycles
if now-last_t>=0.5 then
local rate=(count-last_c)/(now-last_t)
L.live_rate=rate;L.live_money_rate=((G.GAME.dollars or 0)-last_cash)/(now-last_t)
L.view.rate=string.format('%s /s',short(math.floor(rate+0.5)))
local tags=0
for _,tag in ipairs(G.GAME.tags or {}) do if tag.key=='tag_double' then tags=tags+1 end end
L.double_tags=tags;L.view.tags=short(tags)
last_t,last_c,last_cash=now,count,G.GAME.dollars or 0
end
L.view.count=short(count)
L.view.stage=L.error and 'Stopped - F7 details' or L.paused and 'Paused' or L.running and
(L.mode=='route' and string.format('%d / %d %dx',L.step,#L.route.steps,L.route_speed or 4) or 'Turbo') or L.status
local sig=tostring(L.visible)..tostring(L.running)..tostring(L.paused)..tostring(L.confirm_new)..L.target
if sig~=signature or not panel or panel.REMOVED then rebuild();signature=sig end
if os.getenv('GHOST_LOOP_DIAGNOSTICS') and now-(L.diag_at or 0)>1 then
L.diag_at=now
local locks={};for k,v in pairs(G.CONTROLLER.locks) do if v then locks[#locks+1]=k end end
local f=io.open(os.getenv('GHOST_LOOP_DIAGNOSTICS'),'w')
if f then
f:write(string.format('time=%.3f\nstep=%d\ncycles=%d\nmoney_cycles=%d\nmoney=%s\nrate=%.1f\nphase=%s\nready=%s\nrunning=%s\nerror=%s\nlocks=%s\nlocked=%s\nstop_use=%s\n',now,L.step,L.cycles,L.money_cycles,tostring(G.GAME.dollars),L.live_rate or 0,L.phase(),tostring(L.ready()),tostring(L.running),tostring(L.error),table.concat(locks,','),tostring(G.CONTROLLER.locked),tostring(G.GAME.STOP_USE)))
f:write(string.format('heap_mb=%.1f\nmax_gc_ms=%.2f\nmax_save_ms=%.2f\nmax_update_ms=%.2f\nmax_frame_ms=%.2f\nslow_frames=%d\n',collectgarbage('count')/1024,L.max_gc_ms or 0,L.max_save_ms or 0,L.max_update_ms or 0,L.max_frame_ms or 0,L.slow_frames or 0))
f:write(string.format('batch_cycles=%d\nbatch_elapsed=%.6f\n',L.started_cycles and count-L.started_cycles or 0,L.elapsed or (L.started and now-L.started) or 0))
f:write(string.format('double_tags=%d\n',L.double_tags or 0))
f:close()
end
end
end
love.keypressed=function(key,...)
if key=='f6' then if not L.running then G.FUNCS.ghost_loop_guide() end;return end
if key=='f8' then L.visible=not L.visible;signature=nil;return end
if key=='f9' then L.pause();return end
if key=='f10' then L.cancel();return end
if key=='f7' and L.error then print('[Ghost Loop] '..L.error);return end
if L.running and not L.paused and key~='escape' then return end
return old_key(key,...)
end
-- Native UI hit testing stays responsive even while card animations lock
-- ordinary game input. It does not unlock or change the game's controller.
love.mousepressed=function(x,y,b,...)
if b==1 and panel and not panel.REMOVED then
local point={x=x/(G.TILESCALE*G.TILESIZE),y=y/(G.TILESCALE*G.TILESIZE)}
local handle=not L.visible and panel:get_UIE_by_ID('ghost_loop_handle')
if handle and handle:collides_with_point(point) then
drag={x=x,y=y,ox=offset.x,oy=offset.y,toggle=true,moved=false}
L.captured_mouse=true;return
end
local function hit(node)
if node.config and node.config.button and node:collides_with_point(point) then node:click();return true end
for _,child in pairs(node.children or {}) do if hit(child) then return true end end
end
if hit(panel.UIRoot) then L.captured_mouse=true;return end
local title=panel:get_UIE_by_ID('ghost_loop_title')
if title and title:collides_with_point(point) then
drag={x=x,y=y,ox=offset.x,oy=offset.y};L.captured_mouse=true;return
end
end
if L.running then return end
return old_mouse(x,y,b,...)
end
love.mousemoved=function(x,y,dx,dy,...)
if drag and panel then
if drag.toggle and not drag.moved and (x-drag.x)^2+(y-drag.y)^2<25 then return end
drag.moved=true
local scale=G.TILESCALE*G.TILESIZE
offset.x=drag.ox+(x-drag.x)/scale
offset.y=drag.oy+(y-drag.y)/scale
panel:align_to_major();return
end
if old_move then return old_move(x,y,dx,dy,...) end
end
love.mousereleased=function(...)
if L.captured_mouse then
local expand=drag and drag.toggle and not drag.moved
drag=nil;L.captured_mouse=false
if expand then L.visible=true;signature=nil end
return
end
return old_release(...)
end
end
return UI