mirror of
https://github.com/opus-tango/tfc-forge-macro.git
synced 2026-07-18 07:22:43 +00:00
fix order and extra click issues in setup
This commit is contained in:
@@ -7,13 +7,14 @@ Flow:
|
|||||||
1. Anvil GUI: click top-left, then bottom-right of the bounding box (defines tl, br, width, height).
|
1. Anvil GUI: click top-left, then bottom-right of the bounding box (defines tl, br, width, height).
|
||||||
2. Click the center of each main inventory slot (i1–i27) and hotbar slot (h1–h9).
|
2. Click the center of each main inventory slot (i1–i27) and hotbar slot (h1–h9).
|
||||||
3. Recipe GUI: same top-left / bottom-right for the recipe panel.
|
3. Recipe GUI: same top-left / bottom-right for the recipe panel.
|
||||||
4. Writes coords.json with full anvil and recipe maps (recipe button offsets stay 0 until you edit or extend this script).
|
4. Writes coords.json. With -f/--full, every named offset is recorded; otherwise only anvil and recipe GUI rectangles (other offsets stay 0).
|
||||||
|
|
||||||
Press Esc to abort at any time.
|
Press Esc to abort at any time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@@ -66,8 +67,46 @@ def _empty_recipe_map() -> dict[str, Any]:
|
|||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
# Normalized offsets relative to anvil GUI box (after tl/br/width/height are set).
|
||||||
|
ANVIL_UI_OFFSET_KEYS = [
|
||||||
|
"weld",
|
||||||
|
"input1",
|
||||||
|
"input2",
|
||||||
|
"L",
|
||||||
|
"M",
|
||||||
|
"D",
|
||||||
|
"P",
|
||||||
|
"G",
|
||||||
|
"Y",
|
||||||
|
"O",
|
||||||
|
"R",
|
||||||
|
]
|
||||||
ANVIL_INVENTORY_KEYS = [f"i{i}" for i in range(1, 28)] + [f"h{i}" for i in range(1, 10)]
|
ANVIL_INVENTORY_KEYS = [f"i{i}" for i in range(1, 28)] + [f"h{i}" for i in range(1, 10)]
|
||||||
|
|
||||||
|
RECIPE_OFFSET_KEYS = ["rrb","rlb"] + [f"r{i}" for i in range(1, 19)]
|
||||||
|
|
||||||
|
_ANVIL_UI_LABELS: dict[str, str] = {
|
||||||
|
"weld": "weld button",
|
||||||
|
"input1": "first anvil input slot",
|
||||||
|
"input2": "second anvil input slot",
|
||||||
|
"L": "Light Hit",
|
||||||
|
"M": "Medium Hit",
|
||||||
|
"D": "Heavy Hit",
|
||||||
|
"P": "Draw",
|
||||||
|
"G": "Punch",
|
||||||
|
"Y": "Bend",
|
||||||
|
"O": "Shrink",
|
||||||
|
"R": "Upset",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _recipe_offset_label(key: str) -> str:
|
||||||
|
if key == "rlb":
|
||||||
|
return "recipe panel left scroll / page button"
|
||||||
|
if key == "rrb":
|
||||||
|
return "recipe panel right scroll / page button"
|
||||||
|
return f"recipe slot {key}"
|
||||||
|
|
||||||
|
|
||||||
def apply_top_left_bottom_right(
|
def apply_top_left_bottom_right(
|
||||||
coord_map: dict[str, Any], tl_corner: tuple[int, int], br_corner: tuple[int, int]
|
coord_map: dict[str, Any], tl_corner: tuple[int, int], br_corner: tuple[int, int]
|
||||||
@@ -136,7 +175,7 @@ class _ClickSession:
|
|||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
def run_setup(output_path: Path) -> None:
|
def run_setup(output_path: Path, full: bool) -> None:
|
||||||
anvil = _empty_anvil_map()
|
anvil = _empty_anvil_map()
|
||||||
recipe = _empty_recipe_map()
|
recipe = _empty_recipe_map()
|
||||||
session = _ClickSession()
|
session = _ClickSession()
|
||||||
@@ -145,11 +184,12 @@ def run_setup(output_path: Path) -> None:
|
|||||||
mouse_listener.start()
|
mouse_listener.start()
|
||||||
try:
|
try:
|
||||||
with keyboard.Listener(on_release=session.on_release) as kbd_listener:
|
with keyboard.Listener(on_release=session.on_release) as kbd_listener:
|
||||||
print(
|
intro_anvil = (
|
||||||
"=== Anvil GUI ===\n"
|
"=== Anvil GUI ===\n"
|
||||||
"Open the anvil / forge screen. You will define the full GUI rectangle, then inventory slots.\n",
|
"Open the anvil / forge screen. You will define the full GUI outer rectangle"
|
||||||
flush=True,
|
+ (" then every clickable offset.\n" if full else ".\n")
|
||||||
)
|
)
|
||||||
|
print(intro_anvil, flush=True)
|
||||||
tl_pt = session.wait_click("1/2 Click the TOP-LEFT corner of the anvil GUI (outer box).")
|
tl_pt = session.wait_click("1/2 Click the TOP-LEFT corner of the anvil GUI (outer box).")
|
||||||
br_pt = session.wait_click("2/2 Click the BOTTOM-RIGHT corner of the same GUI box.")
|
br_pt = session.wait_click("2/2 Click the BOTTOM-RIGHT corner of the same GUI box.")
|
||||||
apply_top_left_bottom_right(anvil, tl_pt, br_pt)
|
apply_top_left_bottom_right(anvil, tl_pt, br_pt)
|
||||||
@@ -158,19 +198,36 @@ def run_setup(output_path: Path) -> None:
|
|||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
total = len(ANVIL_INVENTORY_KEYS)
|
if full:
|
||||||
|
ui_total = len(ANVIL_UI_OFFSET_KEYS)
|
||||||
|
for n, key in enumerate(ANVIL_UI_OFFSET_KEYS, start=1):
|
||||||
|
label = _ANVIL_UI_LABELS.get(key, key)
|
||||||
|
xy = session.wait_click(
|
||||||
|
f"Anvil UI {n}/{ui_total} ({key}): click the center of the {label}."
|
||||||
|
)
|
||||||
|
set_offset(anvil, key, xy[0], xy[1])
|
||||||
|
|
||||||
|
inv_total = len(ANVIL_INVENTORY_KEYS)
|
||||||
for n, key in enumerate(ANVIL_INVENTORY_KEYS, start=1):
|
for n, key in enumerate(ANVIL_INVENTORY_KEYS, start=1):
|
||||||
xy = session.wait_click(
|
xy = session.wait_click(
|
||||||
f"Inventory {n}/{total}: click the center of slot {key} "
|
f"Inventory {n}/{inv_total}: click the center of slot {key} "
|
||||||
"(i1–i27: main inventory, usually 9×3 left-to-right, top-to-bottom; then h1–h9: hotbar)."
|
"(i1–i27: main inventory, usually 9×3 left-to-right, top-to-bottom; then h1–h9: hotbar)."
|
||||||
)
|
)
|
||||||
set_offset(anvil, key, xy[0], xy[1])
|
set_offset(anvil, key, xy[0], xy[1])
|
||||||
|
|
||||||
|
session.wait_click("Pickup your ingot to move to the second input slot.")
|
||||||
|
session.wait_click("Move the ingot to the second input slot.")
|
||||||
|
|
||||||
|
plansclick = session.wait_click("Click the PLAN button of the anvil GUI box.")
|
||||||
|
set_offset(anvil, "plans", plansclick[0], plansclick[1])
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"\n=== Recipe GUI ===\n"
|
"\n=== Recipe GUI ===\n"
|
||||||
"Open the recipe selector panel (or the screen where it appears). Same corner convention.\n",
|
"Open the recipe selector panel (or the screen where it appears). Same corner convention.\n",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
if not full:
|
||||||
|
session.wait_click("Click the OPEN button of the recipe GUI box.")
|
||||||
rtl = session.wait_click("1/2 Click the TOP-LEFT corner of the recipe GUI box.")
|
rtl = session.wait_click("1/2 Click the TOP-LEFT corner of the recipe GUI box.")
|
||||||
rbr = session.wait_click("2/2 Click the BOTTOM-RIGHT corner of the recipe GUI box.")
|
rbr = session.wait_click("2/2 Click the BOTTOM-RIGHT corner of the recipe GUI box.")
|
||||||
apply_top_left_bottom_right(recipe, rtl, rbr)
|
apply_top_left_bottom_right(recipe, rtl, rbr)
|
||||||
@@ -178,10 +235,14 @@ def run_setup(output_path: Path) -> None:
|
|||||||
f" Box: tl={recipe['tl']} br={recipe['br']} size={recipe['width']}x{recipe['height']}\n",
|
f" Box: tl={recipe['tl']} br={recipe['br']} size={recipe['width']}x{recipe['height']}\n",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
print(
|
|
||||||
"Recipe controls (rlb, rrb, r1–r18) are left at [0,0] in the file; calibrate them later if needed.\n",
|
if full:
|
||||||
flush=True,
|
rec_total = len(RECIPE_OFFSET_KEYS)
|
||||||
|
for n, key in enumerate(RECIPE_OFFSET_KEYS, start=1):
|
||||||
|
xy = session.wait_click(
|
||||||
|
f"Recipe {n}/{rec_total} ({key}): click the center of the {_recipe_offset_label(key)}."
|
||||||
)
|
)
|
||||||
|
set_offset(recipe, key, xy[0], xy[1])
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"anvil": coord_map_to_json_dict(anvil),
|
"anvil": coord_map_to_json_dict(anvil),
|
||||||
@@ -194,11 +255,29 @@ def run_setup(output_path: Path) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
out = DEFAULT_OUTPUT
|
parser = argparse.ArgumentParser(
|
||||||
if len(sys.argv) > 1:
|
description="Interactive coordinate calibration for TFC forge macro.",
|
||||||
out = Path(sys.argv[1]).expanduser().resolve()
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--full",
|
||||||
|
action="store_true",
|
||||||
|
help=(
|
||||||
|
"Record every offset: anvil UI (plans, weld, inputs, forge steps, inventory, hotbar) "
|
||||||
|
"and recipe UI (rlb, rrb, r1–r18). Default: only the anvil and recipe GUI rectangles."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output",
|
||||||
|
type=Path,
|
||||||
|
default=DEFAULT_OUTPUT,
|
||||||
|
help=f"Path to output coords.json (default: {DEFAULT_OUTPUT}).",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run_setup(out)
|
run_setup(args.output, args.full)
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
print(str(e), flush=True)
|
print(str(e), flush=True)
|
||||||
raise
|
raise
|
||||||
|
|||||||
Reference in New Issue
Block a user