Skip to content
This repository was archived by the owner on Sep 21, 2026. It is now read-only.
Merged

. #170

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# Only touches the cart owned by THIS player (uid match), and only widget items in the inventory.
scoreboard players operation #uid guikit.tmp = @s guikit.uid
execute store result storage guikit:ctx uid int 1 run scoreboard players get @s guikit.uid
function guikit:internal/cont_unbind with storage guikit:ctx
execute as @e[type=#guikit:container,tag=guikit.cart] if score @s guikit.uid = #uid guikit.tmp run function guikit:internal/dispose_cart
function guikit:internal/summon/unbind with storage guikit:ctx
execute as @e[type=#guikit:container,tag=guikit.cart] if score @s guikit.uid = #uid guikit.tmp run function guikit:internal/pad/dispose_cart

function guikit:internal/safe_clear

Expand All @@ -15,7 +15,7 @@ scoreboard players reset @s guikit.uid
scoreboard players reset @s guikit.slots
scoreboard players reset @s guikit.click
function #guikit:clear_tags
function guikit:internal/cleanup_player
function guikit:internal/clear/player

# hook: menu packs can clean up their own state here. Runs AFTER the cart is gone and this
# player's guikit scores are reset, so a listener must NOT call guikit:api/open / refresh from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Input (storage guikit:in): {menu:"<ns>:<id>", page:0, timer:900}
# Result: #ok guikit.const = 1 opened, 0 failed
#
# function guikit:internal/clear_in
# function guikit:internal/clear/in
# data merge storage guikit:in {menu:"ns:id"}
# function guikit:api/open

Expand All @@ -24,7 +24,7 @@ scoreboard players operation @s guikit.uid = #next_uid guikit.const
scoreboard players add #next_uid guikit.const 1

# summon + bind cart to owner
function guikit:internal/summon with storage guikit:in
function guikit:internal/summon/main with storage guikit:in
execute unless entity @e[type=#guikit:container,tag=guikit.new,distance=..1] run return run function guikit:internal/open_fail
scoreboard players operation @e[type=#guikit:container,tag=guikit.new,distance=..1,limit=1] guikit.uid = @s guikit.uid
tag @e[type=#guikit:container,tag=guikit.new,distance=..1] remove guikit.new
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
# leaf types above (a nested all/any counts as failed)
# `not:1b` inverts the result. Unknown type / missing key = fails (closed).
#
# function guikit:internal/clear_cond
# function guikit:internal/clear/cond
# data merge storage guikit:cond {type:"score", obj:"coins", min:10}
# function guikit:cond/check
# execute if score #cond guikit.tmp matches 1 run ...
#
# One small function per type (no big execute chains), same as the rest of the pack.
scoreboard players set #cond guikit.tmp 0
execute if data storage guikit:cond {type:"all"} run return run function guikit:cond/t_all
execute if data storage guikit:cond {type:"any"} run return run function guikit:cond/t_any
execute if data storage guikit:cond {type:"all"} run function guikit:cond/t_all
execute if data storage guikit:cond {type:"any"} run function guikit:cond/t_any
execute if data storage guikit:cond {type:"score"} run function guikit:cond/t_score
execute if data storage guikit:cond {type:"item_count"} run function guikit:cond/t_item_count
execute if data storage guikit:cond {type:"tag"} if data storage guikit:cond tag run function guikit:cond/t_tag with storage guikit:cond
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ scoreboard players operation #cond guikit.tmp = #cacc guikit.tmp
execute if score #cnot guikit.tmp matches 1 run function guikit:cond/negate
data remove storage guikit:cnd items
data remove storage guikit:cnd cur
function guikit:internal/clear_cond
function guikit:internal/clear/cond
return run scoreboard players get #cond guikit.tmp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
execute unless score #ci guikit.tmp < #cn guikit.tmp run return 0
execute store result storage guikit:cnd i int 1 run scoreboard players get #ci guikit.tmp
function guikit:cond/comp_pick with storage guikit:cnd
function guikit:internal/clear_cond
function guikit:internal/clear/cond
function guikit:cond/load_cur
scoreboard players set #cond guikit.tmp 0
execute unless data storage guikit:cond {type:"all"} unless data storage guikit:cond {type:"any"} run function guikit:cond/check
Expand Down
18 changes: 9 additions & 9 deletions packs/guikit-datapack/data/guikit/function/core/load.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ scoreboard players set #version guikit.const 2
execute unless score #next_uid guikit.const matches 0.. run scoreboard players set #next_uid guikit.const 1

# transient scratch storage / scores are dropped on every reload (stale state from a previous run)
function guikit:internal/clear_in
function guikit:internal/clear_w
function guikit:internal/clear_cond
function guikit:internal/clear_tmp
function guikit:internal/clear_btn_cur
function guikit:internal/clear_mtr
function guikit:internal/cleanup_scores
function guikit:internal/clear/in
function guikit:internal/clear/w
function guikit:internal/clear/cond
function guikit:internal/clear/tmp
function guikit:internal/btn/clear_cur
function guikit:internal/clear/mtr
function guikit:internal/clear/scores

# carts that lost their owner across a reload / relog are removed
function guikit:internal/sweep_orphans
function guikit:internal/sweep/orphans

# registry is rebuilt on every reload by the #guikit:register listeners
data modify storage guikit:reg menus set value {}
function guikit:internal/containers_builtin
function guikit:internal/summon/builtin
data modify storage guikit:cont bound set value {}
data modify storage guikit:btn defs set value {}
function #guikit:register
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ scoreboard players set #found guikit.tmp 0
execute as @e[type=#guikit:container,tag=guikit.cart] if score @s guikit.uid = #uid guikit.tmp run function guikit:internal/follow
execute if score #found guikit.tmp matches 0 run return run function guikit:api/close

execute as @e[type=#guikit:container,tag=guikit.cart] if score @s guikit.uid = #uid guikit.tmp run function guikit:internal/sweep/foreign_cart

# click detection: a GUI item in the inventory == the player left-clicked it.
# `clear ... 0` only COUNTS. Everything below is gated on that count, so a tick with no
# GUI item in the inventory never reaches a destructive clear.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# guikit :: internal/btn_afford as player reads storage guikit:btn cur.cost
# guikit :: internal/btn/afford as player reads storage guikit:btn cur.cost
# cost:{obj:"coins", amount:5} score cost
# cost:{item:"minecraft:diamond", [count:3]} item cost (count defaults to 1)
# Result in #cond guikit.tmp (1 = enough). Does not charge.
scoreboard players set #cond guikit.tmp 1
execute unless data storage guikit:btn cur.cost.count run data modify storage guikit:btn cur.cost.count set value 1
execute if data storage guikit:btn cur.cost.obj run function guikit:internal/btn_afford_score with storage guikit:btn cur.cost
execute if data storage guikit:btn cur.cost.item run function guikit:internal/btn_afford_item
execute if data storage guikit:btn cur.cost.obj run function guikit:internal/btn/afford_score with storage guikit:btn cur.cost
execute if data storage guikit:btn cur.cost.item run function guikit:internal/btn/afford_item
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# guikit :: internal/btn_afford_item as player
# guikit :: internal/btn/afford_item as player
# Reuses the item_count condition, which does not count the clicked widget item (see cond/t_item_count_do).
scoreboard players set #cond guikit.tmp 0
function guikit:internal/clear_cond
function guikit:internal/clear/cond
data modify storage guikit:cond item set from storage guikit:btn cur.cost.item
data modify storage guikit:cond min set from storage guikit:btn cur.cost.count
function guikit:cond/t_item_count_do with storage guikit:cond
function guikit:internal/clear_cond
function guikit:internal/clear/cond
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# guikit :: internal/clear_btn_cur
# guikit :: internal/btn/clear_cur
data remove storage guikit:btn cur
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ data remove storage guikit:btn cur
$data modify storage guikit:btn cur set from storage guikit:btn defs."$(id)"
execute unless data storage guikit:btn cur run return 0

function guikit:internal/btn_cond
execute if score #cond guikit.tmp matches 0 run return run function guikit:internal/btn_deny
function guikit:internal/btn/cond
execute if score #cond guikit.tmp matches 0 run return run function guikit:internal/btn/deny

# cost: charged only now (cond passed), before the command runs
scoreboard players set #paid guikit.tmp 1
execute if data storage guikit:btn cur.cost run function guikit:internal/btn_pay
execute if score #paid guikit.tmp matches 0 run return run function guikit:internal/btn_poor
execute if data storage guikit:btn cur.cost run function guikit:internal/btn/pay
execute if score #paid guikit.tmp matches 0 run return run function guikit:internal/btn/poor

# decide now: the command may overwrite guikit:btn cur (e.g. by running another button)
execute store success score #btn_close guikit.tmp if data storage guikit:btn cur{close:1b}
execute if data storage guikit:btn cur.cmd run function guikit:internal/btn_cmd with storage guikit:btn cur
execute if data storage guikit:btn cur.url run function guikit:internal/btn_url with storage guikit:btn cur
execute if data storage guikit:btn cur.cmd run function guikit:internal/btn/cmd with storage guikit:btn cur
execute if data storage guikit:btn cur.url run function guikit:internal/btn/url with storage guikit:btn cur
execute if score #btn_close guikit.tmp matches 1 if score @s guikit.uid matches 1.. run function guikit:api/close

# transient: nothing may leak into the next click
function guikit:internal/clear_btn_cur
function guikit:internal/btn/clear_cur
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# guikit :: internal/btn_cond as player reads storage guikit:btn cur.cond
# guikit :: internal/btn/cond as player reads storage guikit:btn cur.cond
# Result: #cond guikit.tmp = 1 when the button has no condition or it passes, else 0.
scoreboard players set #cond guikit.tmp 1
execute unless data storage guikit:btn cur.cond run return 1
function guikit:internal/clear_cond
function guikit:internal/clear/cond
data remove storage guikit:cnd cur
data modify storage guikit:cnd cur set from storage guikit:btn cur.cond
function guikit:cond/load_cur
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# guikit :: internal/btn_deny as player reads storage guikit:btn cur.deny
function guikit:internal/clear_in
# guikit :: internal/btn/deny as player reads storage guikit:btn cur.deny
function guikit:internal/clear/in
data modify storage guikit:in msg set value "Not available."
execute if data storage guikit:btn cur.deny run data modify storage guikit:in msg set from storage guikit:btn cur.deny
data modify storage guikit:in color set value "red"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# guikit :: internal/btn_gate as player reads storage guikit:btn cur
# guikit :: internal/btn/gate as player reads storage guikit:btn cur
# Result: #cond guikit.tmp = 1 when the button's cond passes AND its cost is affordable, else 0.
# Nothing is charged here (used when drawing, to decide the locked look).
function guikit:internal/btn_cond
function guikit:internal/btn/cond
execute if score #cond guikit.tmp matches 0 run return 0
execute if data storage guikit:btn cur.cost run function guikit:internal/btn_afford
execute if data storage guikit:btn cur.cost run function guikit:internal/btn/afford
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# guikit :: internal/btn_pay as player reads storage guikit:btn cur.cost
# guikit :: internal/btn/pay as player reads storage guikit:btn cur.cost
# Charges the cost through the existing helpers. Result in #paid guikit.tmp (1 = paid, 0 = not enough).
execute unless data storage guikit:btn cur.cost.count run data modify storage guikit:btn cur.cost.count set value 1
execute if data storage guikit:btn cur.cost.obj run function guikit:internal/pay_score with storage guikit:btn cur.cost
execute if data storage guikit:btn cur.cost.item run function guikit:internal/btn_pay_item
execute if data storage guikit:btn cur.cost.item run function guikit:internal/btn/pay_item
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# guikit :: internal/btn_pay_item as player
# guikit :: internal/btn/pay_item as player
# The clicked widget is still in the inventory here. Remove widget items first so pay_item can neither
# count it nor clear it instead of the player's real items (a button that costs its own item type).
function guikit:internal/safe_clear
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# guikit :: internal/btn_poor as player reads storage guikit:btn cur.poor
function guikit:internal/clear_in
# guikit :: internal/btn/poor as player reads storage guikit:btn cur.poor
function guikit:internal/clear/in
data modify storage guikit:in msg set value "You can't afford that."
execute if data storage guikit:btn cur.poor run data modify storage guikit:in msg set from storage guikit:btn cur.poor
data modify storage guikit:in color set value "red"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
data remove storage guikit:btn cur
$data modify storage guikit:btn cur set from storage guikit:btn defs."$(id)"
execute unless data storage guikit:btn cur.cond unless data storage guikit:btn cur.cost run return 0
function guikit:internal/btn_gate
function guikit:internal/btn/gate
execute if score #cond guikit.tmp matches 1 run return 0
data modify storage guikit:w item set value "minecraft:barrier"
execute if data storage guikit:btn cur.locked_item run data modify storage guikit:w item set from storage guikit:btn cur.locked_item
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# guikit:in is deliberately NOT cleared here: api/open calls api/close in the middle of its own
# run (to close a previous menu) and still needs guikit:in {menu,page,timer} afterwards.
# The caller owns guikit:in (see internal/clear_in, called before every `data merge storage guikit:in`).
function guikit:internal/clear_w
function guikit:internal/clear_cond
function guikit:internal/clear_tmp
function guikit:internal/clear_mtr
function guikit:internal/clear/w
function guikit:internal/clear/cond
function guikit:internal/clear/tmp
function guikit:internal/clear/mtr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Tells the player how long the cooldown still runs. Call it when widget/cooldown_start
# returned 0 (before this, a refused click gave the player no feedback at all).
#
# execute unless function guikit:widget/cooldown_start run function guikit:internal/cd_notify
# execute unless function guikit:widget/cooldown_start run function guikit:internal/cooldown/notify
#
# Do NOT call cooldown_start first and then test `guikit.cd matches 1..`: on success it has just
# SET the cooldown, so that test is true then too and the player would be told to wait after a
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# guikit :: internal/meter_hit
# macro: $(id) $(c) $(obj) $(max) as player (called by internal/meter_probe_loop)
# guikit :: internal/meter/hit
# macro: $(id) $(c) $(obj) $(max) as player (called by internal/meter/probe_loop)
# Non-destructive presence test for cell $(c) of meter $(id) (same trick as widget/probe). On a
# hit, sets $(obj) to the cell's scaled value using score #mw guikit.tmp (width, set by
# widget/meter_probe) and marks the menu dirty for redraw.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# guikit :: internal/meter_loop (recursive -- like internal/progress_loop, but draws
# guikit :: internal/meter/loop (recursive -- like internal/progress_loop, but draws
# clickable "meter" cells instead of static "progress" ones). Reads score #f/#i/#left guikit.tmp
# set by internal/progress_calc, and storage guikit:pg set by widget/meter_draw.
execute if score #left guikit.tmp matches ..0 run return 0
Expand All @@ -7,7 +7,7 @@ scoreboard players operation #d guikit.tmp -= #i guikit.tmp
data modify storage guikit:pg item set from storage guikit:pg empty
execute if score #d guikit.tmp matches 1.. run data modify storage guikit:pg item set from storage guikit:pg full
execute store result storage guikit:pg cell int 1 run scoreboard players get #i guikit.tmp
function guikit:internal/meter_slot with storage guikit:pg
function guikit:internal/meter/slot with storage guikit:pg
scoreboard players add #i guikit.tmp 1
scoreboard players remove #left guikit.tmp 1
function guikit:internal/meter_loop
function guikit:internal/meter/loop
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# guikit :: internal/meter_probe_loop (recursive)
# guikit :: internal/meter/probe_loop (recursive)
# Reads storage guikit:mtr {id, obj, max} and score #mc/#mw guikit.tmp (current cell / width),
# set by widget/meter_probe. Stops as soon as a cell hits (internal/meter_hit sets #mhit).
# set by widget/meter_probe. Stops as soon as a cell hits (internal/meter/hit sets #mhit).
execute if score #mc guikit.tmp >= #mw guikit.tmp run return 0
execute store result storage guikit:mtr c int 1 run scoreboard players get #mc guikit.tmp
function guikit:internal/meter_hit with storage guikit:mtr
function guikit:internal/meter/hit with storage guikit:mtr
execute if score #mhit guikit.tmp matches 1 run return 0
scoreboard players add #mc guikit.tmp 1
function guikit:internal/meter_probe_loop
function guikit:internal/meter/probe_loop
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
# entity type tag #guikit:container (then the cart could never be bound to its owner and would
# live forever). Remove it and roll the half-opened state back.
execute store result storage guikit:ctx uid int 1 run scoreboard players get @s guikit.uid
function guikit:internal/cont_unbind with storage guikit:ctx
function guikit:internal/summon/unbind with storage guikit:ctx
kill @e[tag=guikit.new,distance=..1]
scoreboard players reset @s guikit.uid
scoreboard players reset @s guikit.slots
function guikit:internal/clear_tmp
function guikit:internal/clear/tmp
tellraw @s [{"text":"[guikit] ","color":"gray"},{"text":"could not open the menu: its container entity was not created or is not in #guikit:container","color":"red"}]
scoreboard players set #ok guikit.const 0
return 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# guikit :: internal/pad/dispose_cart as cart - empty first so kill does not drop GUI items
# Only clears the cart's own slot count (guikit.slots, set at summon time): a hard-coded
# container.0..26 here would run item replace on slots past a hopper_minecart's real 5
# (or any future non-27-slot container), which fails per slot and littered the log without
# actually stopping the kill that follows.
scoreboard players operation #ds guikit.tmp = @s guikit.slots
execute unless score #ds guikit.tmp matches 1.. run scoreboard players set #ds guikit.tmp 27
function guikit:internal/pad/dispose_slot
kill @s
Loading
Loading