music_quiz: 버튼 interaction 0.5칸 어긋남 + btn_prep 파싱 에러 근본 수정

- btn.mcfunction 의 positioned $(x) $(y) $(z) → $(x).0 $(y).0 $(z).0.
  마인크래프트 vec3 인자는 정수만 쓰면 자동으로 +0.5 보정 (블록 중심으로
  잡힘) 되기 때문에 positioned 2773 86 5968 이 실제로는 (2773.5, 86,
  5968.5) 가 되고, 거기서 ~0.375 ~0.0625 같은 오프셋을 더해 interaction
  을 소환하면 박스가 블록 중앙으로 0.5 칸 밀린다. decimal 형태로 강제해
  보정 회피.

- btn_prep.mcfunction 을 execute-unless-data 방식에서 defaults 컴파운드 +
  merge 방식으로 재작성. 이전 v1.0.19 의 다중 공백 정규화는 근본 원인이
  아니었음 — `execute unless data storage mq:tmp btn.label run ...` 구문
  자체를 MC 26.1.2 파서가 거부. data modify ... merge from 으로 source 의
  키가 target 을 덮어쓰는 머지를 활용하면 같은 기능을 문제 라인 없이 구현.

- temp/ 부분 적용 패키지의 README 와 두 파일을 동기화. 진짜 원인 설명으로
  재작성.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Claude (owner)
2026-05-18 21:40:32 +09:00
parent 5d610cf01a
commit b803a422a3
5 changed files with 127 additions and 64 deletions

View File

@@ -26,6 +26,12 @@
# 인접: 중심 0.375 / 0.5 / 0.625, 각 폭 0.125 → 합 [0.3125, 0.6875]).
# interaction Y 는 hitbox 바닥 → 소환 y = block y + 0.375, height = 0.25.
#
# ---- positioned 의 .5 보정 회피 ----
# MC 의 vec3 인자는 정수만 쓰면 자동으로 +0.5 보정됨 (블록 중심으로 잡힘).
# positioned 2773 86 5968 → 실제로는 (2773.5, 86, 5968.5). 거기서 ~ 오프셋
# 을 더하면 박스 전체가 0.5 칸 어긋남. $(x).0 $(y).0 $(z).0 처럼 decimal
# 형태로 넘기면 보정 없이 정확한 블록 origin (minimal corner) 이 됨.
#
# ---- text_display 위치 (버튼 바로 아래 같은 벽면에 부착) ----
# 같은 벽 (button 의 머리 normal 반대편 블록) 의 visible 면에 살짝 띄워
# 부착. 텍스트 entity Y 는 텍스트 baseline 근방 → 아래 블록 바닥에 두면
@@ -50,28 +56,28 @@ $execute if score $(n) buttons matches -1 run kill @e[type=minecraft:interaction
$execute if score $(n) buttons matches -1 run kill @e[type=minecraft:text_display,tag=mq,tag=$(n)]
# south: 깊이축=z(+0.0625), 가로축=x, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.375 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.5 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.625 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"south"} positioned $(x) $(y) $(z) run summon minecraft:text_display ~0.5 ~-1 ~0.01 {Tags:["mq","$(n)"],Rotation:[0f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.375 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.5 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.625 ~0.375 ~0.0625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.5 ~-1 ~0.01 {Tags:["mq","$(n)"],Rotation:[0f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# north: 깊이축=z(+0.9375), 가로축=x, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.375 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.5 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.625 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"north"} positioned $(x) $(y) $(z) run summon minecraft:text_display ~0.5 ~-1 ~0.99 {Tags:["mq","$(n)"],Rotation:[180f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.375 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.5 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.625 ~0.375 ~0.9375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.5 ~-1 ~0.99 {Tags:["mq","$(n)"],Rotation:[180f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# east: 깊이축=x(+0.0625), 가로축=z, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.0625 ~0.375 ~0.375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.0625 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.0625 ~0.375 ~0.625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"east"} positioned $(x) $(y) $(z) run summon minecraft:text_display ~0.01 ~-1 ~0.5 {Tags:["mq","$(n)"],Rotation:[-90f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.0625 ~0.375 ~0.375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.0625 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.0625 ~0.375 ~0.625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.01 ~-1 ~0.5 {Tags:["mq","$(n)"],Rotation:[-90f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# west: 깊이축=x(+0.9375), 가로축=z, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.9375 ~0.375 ~0.375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.9375 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.9375 ~0.375 ~0.625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"west"} positioned $(x) $(y) $(z) run summon minecraft:text_display ~0.99 ~-1 ~0.5 {Tags:["mq","$(n)"],Rotation:[90f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.9375 ~0.375 ~0.375 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.9375 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.9375 ~0.375 ~0.625 {Tags:["mq","$(n)"],width:0.125f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.99 ~-1 ~0.5 {Tags:["mq","$(n)"],Rotation:[90f,0f],background:0,text:'{"text":"$(label)","color":"$(label_color)","font":"$(label_font)"}',transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,0f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 run scoreboard players set $(n) buttons 0
@@ -81,9 +87,9 @@ $execute if score $(n) buttons matches -1 run scoreboard players set $(n) button
# 한 버튼에 interaction 3개지만 `on target` 은 클릭된 1개만 통과
# (나머지는 target 부재로 체인 중단). limit=1 을 두면 MC 가 임의로 1개를
# 골라 잘못된 entity 만 검사하므로 limit 두지 않음.
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x) $(y) $(z) run playsound minecraft:block.stone_button.click_on block @s ~ ~ ~ 1 1
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x) $(y) $(z) if score init main matches 0 run $(c)
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x) $(y) $(z) unless score init main matches 0 run trigger $(n)
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x).0 $(y).0 $(z).0 run playsound minecraft:block.stone_button.click_on block @s ~ ~ ~ 1 1
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x).0 $(y).0 $(z).0 if score init main matches 0 run $(c)
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] on target as @s positioned $(x).0 $(y).0 $(z).0 unless score init main matches 0 run trigger $(n)
# ---- 처리 후 attack/interaction NBT 클리어 (다음 tick 중복 발화 방지) ----
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] at @s run data remove entity @s attack

View File

@@ -6,8 +6,16 @@
# label_color : 기본 "black"
# label_font : 기본 "minecraft:default"
# label_scale : 기본 "1.0" (Vector3f 의 한 축, 3축 동일하게 사용됨)
#
# 구현: defaults 컴파운드를 먼저 만들고 entry (mq:tmp.btn) 를 그 위에 merge
# 한 뒤 다시 mq:tmp.btn 으로 되돌린다. data modify ... merge from 은 source
# compound 의 키로 target compound 를 덮어쓰므로 entry 에 있는 값은 보존되고
# entry 에 없는 키만 default 값으로 채워진다.
#
# (이전에 `execute unless data storage mq:tmp btn.label run data modify ...`
# 방식이었으나 MC 26.1.2 parser 가 해당 라인을 거부했음. merge 방식은 문제
# 난 execute-unless-data 구문 자체를 제거.)
execute unless data storage mq:tmp btn.label run data modify storage mq:tmp btn.label set value ""
execute unless data storage mq:tmp btn.label_color run data modify storage mq:tmp btn.label_color set value "black"
execute unless data storage mq:tmp btn.label_font run data modify storage mq:tmp btn.label_font set value "minecraft:default"
execute unless data storage mq:tmp btn.label_scale run data modify storage mq:tmp btn.label_scale set value "1.0"
data modify storage mq:tmp btn_default set value {label:"",label_color:"black",label_font:"minecraft:default",label_scale:"1.0"}
data modify storage mq:tmp btn_default merge from storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:tmp btn_default