[
 {
  "name": "status",
  "category": "meta",
  "status": "verified",
  "mechanism": "월드 블록 + 고속 레인 + 클레임 테이블",
  "latency": "로컬 계산",
  "signature": "status() -> 'dict'",
  "doc": "연결 상태: pid, 월드 발행(주기, 수집 소요 시간), 고속 레인 카운트."
 },
 {
  "name": "snapshot",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 월드 블록 Local\\War3World_<pid>(런타임이 50 ms마다 푸시, seqlock)",
  "latency": "푸시 스냅샷",
  "signature": "snapshot(max_age: 'float' = 0.05)",
  "doc": "맵 전체의 완전한 상태(WorldState): .units .players .items .clock .me. max_age초 안에 다시 호출하면 같은 스냅샷을 반환합니다.\n⚠ 금광에 들어간 일꾼은 목록에 없습니다. 기본값은 맵 전체가 보이는 상태입니다(락스텝 모델이라 로컬에 모든 정보가 있음). Game(fair=True)일 때만 시야로 필터링합니다."
 },
 {
  "name": "last_seen",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷의 visibleTo (스냅샷을 갱신할 때마다 보이는 적/크립 유닛을 기록)",
  "latency": "푸시 스냅샷",
  "signature": "last_seen(owner: 'str | int' = 'enemy', max_age: 'float | None' = None) -> 'list'",
  "doc": "마지막으로 본 적(또는 'creep' 크립, 또는 특정 플레이어 번호) 유닛: [(그때의 유닛 상태, 그때의 게임 시계, 지난 초)], 최신순.\n죽는 것을 보면 목록에서 지웁니다. 공정 모드와 일반 모드 모두 \"지금 아군이 볼 수 있는가\"를 기준으로 기록합니다 — 플레이어 머릿속에 있는 바로 그 지도입니다:\n정찰한 병력, 상대 영웅을 마지막으로 본 위치, 상대가 확장 기지를 언제 가져갔는지. max_age를 주면 그 게임 초 이내의 기록만 반환합니다."
 },
 {
  "name": "map",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 맵 블록 Local\\War3Map_<pid>(게임 시작 후 런타임이 나눠서 계산, IsTerrainPathable 이동/건설)",
  "latency": "푸시 스냅샷",
  "signature": "map()",
  "doc": "이번 게임의 지형 테이블 MapInfo: .walkable(x,y) .buildable(x,y) .at(x,y) .bounds (플레이 가능 영역) .starts (시작 지점) .cells (bit0 이동 불가, bit1 건설 불가).\n게임 시작 후 계산이 끝나기까지 몇 초 걸리며, 그 전에는 None을 반환합니다. 나무는 포함되지 않습니다(trees() 사용)."
 },
 {
  "name": "me",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 헤더",
  "latency": "푸시 스냅샷",
  "signature": "me() -> 'int | None'",
  "doc": "내가 몇 번 플레이어인지(0~11)."
 },
 {
  "name": "resources",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 players[16]",
  "latency": "푸시 스냅샷",
  "signature": "resources(player: 'int | None' = None) -> 'dict | None'",
  "doc": "{'gold','lumber','food_used','food_cap','gold_gathered','lumber_gathered'}. player 기본값은 아군이며, 모든 플레이어를 읽을 수 있습니다.\n읽지 못하면 None을 반환합니다. 0으로 취급하지 마세요."
 },
 {
  "name": "players",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 players[16]",
  "latency": "푸시 스냅샷",
  "signature": "players() -> 'list'",
  "doc": "16개 플레이어 슬롯 전체: Player(id, gold, lumber, food_used, food_cap, gold_gathered, lumber_gathered, race, known)."
 },
 {
  "name": "units",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 units[]",
  "latency": "푸시 스냅샷",
  "signature": "units(owner: 'str | int' = 'all', types=None, alive: 'bool' = True) -> 'list'",
  "doc": "소유자/종류로 유닛을 필터링합니다. owner: 'me' / 'enemy' / 'creep' / 'all' / 플레이어 번호. types: 4자 코드 집합."
 },
 {
  "name": "unit",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 by_handle",
  "latency": "푸시 스냅샷",
  "signature": "unit(handle) -> 'object | None'",
  "doc": "핸들 쌍 (lo, hi)로 유닛을 찾습니다(오더 대상, 작업 대상, 이벤트가 주는 값은 모두 핸들 쌍)."
 },
 {
  "name": "is_building",
  "category": "observe",
  "status": "verified",
  "mechanism": "스냅샷 + units.json (spd==0 = 건물)",
  "latency": "푸시 스냅샷",
  "signature": "is_building(u) -> 'bool'",
  "doc": "건물인지 여부(타워 포함). 유닛 테이블의 이동 속도가 0인지로 판정합니다. 언데드 본진 건물은 점유 면적이 0이므로 점유 면적으로 판정하지 마세요."
 },
 {
  "name": "my_workers",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷",
  "latency": "푸시 스냅샷",
  "signature": "my_workers() -> 'list'",
  "doc": "아군 일꾼(농부 / Peon / 시종 / 위습)."
 },
 {
  "name": "idle_workers",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(오더 슬롯 + 작업 슬롯)",
  "latency": "푸시 스냅샷",
  "signature": "idle_workers() -> 'list'",
  "doc": "할 일이 없는 일꾼: 오더도 없고 작업도 없는 일꾼(이번 틱에 방금 일을 맡긴 일꾼은 제외).\n⚠ 작업 중인 일꾼에게 채집 명령을 다시 내리면 채집 주기가 끊깁니다(수입이 0이 됨)."
 },
 {
  "name": "my_heroes",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷",
  "latency": "푸시 스냅샷",
  "signature": "my_heroes() -> 'list'",
  "doc": "살아 있는 아군 영웅(죽은 영웅은 제단의 부활 목록에 있음, revive 참고)."
 },
 {
  "name": "my_army",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷 + units.json",
  "latency": "푸시 스냅샷",
  "signature": "my_army() -> 'list'",
  "doc": "아군 전투 유닛: 일꾼도 건물도 아닌 유닛."
 },
 {
  "name": "my_buildings",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷",
  "latency": "푸시 스냅샷",
  "signature": "my_buildings(types=None) -> 'list'",
  "doc": "아군 건물(타워, 건설 중인 기초 포함). types로 특정 종류만 고를 수 있습니다. 예: {'hbar'}."
 },
 {
  "name": "is_constructing",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(오더 = 건물 4자 코드, 또는 건설/수리 오더)",
  "latency": "푸시 스냅샷",
  "signature": "is_constructing(worker) -> 'bool'",
  "doc": "이 일꾼이 건물을 짓고 있는지(지으러 가는 중 / 수리를 돕는 중 포함, 이번 틱에 방금 맡긴 것도 포함). 건설 일꾼을 고를 때 이 일꾼은 건너뛰어야 합니다. 그렇지 않으면 이전 기초 공사가 멈춥니다."
 },
 {
  "name": "under_construction",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(기초의 체력이 아주 낮은 값에서 최대치까지 오름)",
  "latency": "푸시 스냅샷",
  "signature": "under_construction(building) -> 'bool'",
  "doc": "이 건물이 아직 완성되지 않았는지(체력이 가득 차지 않음). ⚠ 공격받아 손상된 건물도 체력이 가득 차 있지 않습니다 — 초반 판단에는 충분하지만, 교전이 시작된 뒤에는 시간과 함께 판단하세요."
 },
 {
  "name": "gold_mines",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(ngol/egol/ugol)",
  "latency": "푸시 스냅샷",
  "signature": "gold_mines() -> 'list'",
  "doc": "맵 위의 금광. ⚠ 나이트 엘프가 휘감은(Entangle) 금광과 중립 금광은 같은 좌표에 유닛이 하나씩 있습니다. 채집은 자기 쪽 금광으로 보내야 합니다."
 },
 {
  "name": "enemies",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷",
  "latency": "푸시 스냅샷",
  "signature": "enemies(fighters_only: 'bool' = False) -> 'list'",
  "doc": "적 플레이어의 유닛(크립 제외). fighters_only: 일꾼과 건물을 제외합니다."
 },
 {
  "name": "creeps",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(owner 12 = 중립 적대)",
  "latency": "푸시 스냅샷",
  "signature": "creeps() -> 'list'",
  "doc": "크립(중립 적대). ⚠ 밤에는 시야가 짧아져, 먼 캠프가 안개 속으로 들어가면 그 대상에게 내리는 명령이 거부됩니다(원인 코드 1001)."
 },
 {
  "name": "nearest",
  "category": "meta",
  "status": "verified",
  "mechanism": "순수 계산",
  "latency": "로컬 계산",
  "signature": "nearest(candidates, to)",
  "doc": "to (유닛 또는 (x,y))에 가장 가까운 하나. 후보가 없으면 None을 반환합니다."
 },
 {
  "name": "life_mana",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 유닛 hp/hpMax/mana/manaMax",
  "latency": "푸시 스냅샷",
  "signature": "life_mana(u) -> 'dict | None'",
  "doc": "{'hp','hp_max','mana','mana_max'}(부동소수점, 엔진 원값). u는 스냅샷에서 얻은 유닛이면 됩니다(최신 값으로 바꿔 읽음)."
 },
 {
  "name": "hero_info",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 유닛 level/xp/skillPoints",
  "latency": "푸시 스냅샷",
  "signature": "hero_info(hero) -> 'dict | None'",
  "doc": "{'level','xp','skill_points'}."
 },
 {
  "name": "abilities",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 상세: 스킬(코드 / 레벨 / 플래그 / 남은 쿨다운)",
  "latency": "푸시 스냅샷",
  "signature": "abilities(u) -> 'list'",
  "doc": "[{code, level, cooldown, flags}]. 버프는 buffs(u)에 있습니다. \"상세 정보\"가 있는 유닛에만 있습니다(영웅 > 플레이어 유닛 > 크립, 최대 256개)."
 },
 {
  "name": "buffs",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 상세: B로 시작하는 스킬 객체",
  "latency": "푸시 스냅샷",
  "signature": "buffs(u) -> 'list'",
  "doc": "유닛에 걸린 버프 코드(예: 'BHds' Divine Shield, 'Bslo' 감속). 코드별 효과는 data/game/buffs.json 참고."
 },
 {
  "name": "cooldown",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 상세: 스킬 남은 쿨다운(스킬 타이머)",
  "latency": "푸시 스냅샷",
  "signature": "cooldown(u, ability: 'str') -> 'float | None'",
  "doc": "이 스킬의 남은 쿨다운(게임 초). 0 = 사용 가능. 이 스킬이 없으면(또는 이 유닛에 상세 정보가 없으면) None을 반환합니다."
 },
 {
  "name": "inventory",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 상세: 인벤토리 6칸",
  "latency": "푸시 스냅샷",
  "signature": "inventory(hero) -> 'list | None'",
  "doc": "6칸 아이템의 4자 코드(빈칸은 None). 인벤토리가 없으면 None을 반환합니다."
 },
 {
  "name": "current_order",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 유닛 오더 / 오더 대상 / 오더 대상 지점",
  "latency": "푸시 스냅샷",
  "signature": "current_order(u) -> 'dict | None'",
  "doc": "{'order','target','x','y'}: 유닛이 지금 수행 중인 오더(order는 0x000D00xx 또는 건물 4자 코드, 0 = 대기).\ntarget은 핸들 쌍이며, g.unit(target)으로 유닛으로 바꿉니다."
 },
 {
  "name": "current_target",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 유닛 작업 대상",
  "latency": "푸시 스냅샷",
  "signature": "current_target(u)",
  "doc": "유닛이 **실제로 공격하거나 쫓고 있는** 유닛(없으면 None).\n⚠ 공격 명령을 내리면 오더 슬롯은 금방 비고 공격은 작업(task)에 걸립니다 — \"누구를 공격 중인지\"는 current_order가 아니라 이것으로 판단하세요."
 },
 {
  "name": "clock",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 헤더 clockMs (엔진 게임 시계)",
  "latency": "푸시 스냅샷",
  "signature": "clock() -> 'float | None'",
  "doc": "엔진 게임 시계(게임 초, 로딩 중에는 0). 배속에서는 실제 시간보다 빠르게 흐릅니다."
 },
 {
  "name": "production",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 생산 테이블(Aque/ABnP/AUnP 스킬 객체 + 런타임이 추적한 경과 시간, 실측 오차 < 0.2 게임 초)",
  "latency": "푸시 스냅샷",
  "signature": "production(building)",
  "doc": "이 건물이 지금 무엇을 만들고 있는지: Production(kind, queue, duration, elapsed, blocked, progress, remaining…). 아무것도 안 하면 None을 반환합니다.\n  kind 'queue'(훈련/연구/영웅, queue는 최대 7칸, [0]이 진행 중) / 'construction'(건설 중) / 'upgrade'(본진 건물/타워 업그레이드);\n  blocked = 대기열은 있지만 시작하지 못함(대개 인구 부족 — 농장을 지을 때입니다); progress 0..1.\n상대 건물도 볼 수 있습니다(공정 모드에서는 보이는 건물만)."
 },
 {
  "name": "queue",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 생산 테이블",
  "latency": "푸시 스냅샷",
  "signature": "queue(building) -> 'list'",
  "doc": "훈련/연구 대기열의 4자 코드([0]이 진행 중). 대기 중이거나 생산 건물이 아니면 []."
 },
 {
  "name": "all_production",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 생산 테이블",
  "latency": "푸시 스냅샷",
  "signature": "all_production(owner: 'str | int' = 'me') -> 'list'",
  "doc": "진행 중인 모든 생산 [(건물, Production)]. owner는 units()와 같습니다: 'me' / 'enemy' / 플레이어 번호 / 'all'.\n프로의 활용법: 상대가 어떤 유닛을 뽑는지, 어떤 기술을 연구하는지, 언제 테크 업을 하는지 봅니다(건물을 정찰했을 때)."
 },
 {
  "name": "path_distance",
  "category": "observe",
  "status": "verified",
  "mechanism": "맵 블록(엔진 IsTerrainPathable) + 트리 블록 + 건물 점유 면적, SDK 측 A*(128당 한 칸)",
  "latency": "푸시 스냅샷 + 로컬 계산",
  "signature": "path_distance(a, b) -> 'float | None'",
  "doc": "지상 유닛이 a에서 b까지 걸어가는 거리(a, b는 유닛 또는 (x,y)). 갈 수 없으면 None. 섬 맵에서 \"이 크립 캠프/확장 기지에 지상으로 갈 수 있는가\"를 판단할 때 쓰세요.\n직선 거리보다 믿을 만합니다(숲, 절벽, 건물을 돌아감). 정밀도는 한 칸 128이며, 한 칸보다 좁은 틈은 막힌 것으로 판정합니다."
 },
 {
  "name": "reachable",
  "category": "observe",
  "status": "verified",
  "mechanism": "위와 같음",
  "latency": "푸시 스냅샷 + 로컬 계산",
  "signature": "reachable(a, b) -> 'bool | None'",
  "doc": "지상으로 갈 수 있는지 여부(맵 블록 계산 전 = None)."
 },
 {
  "name": "walk_path",
  "category": "observe",
  "status": "verified",
  "mechanism": "위와 같음",
  "latency": "푸시 스냅샷 + 로컬 계산",
  "signature": "walk_path(a, b) -> 'list | None'",
  "doc": "경로의 꺾이는 지점 [(x,y)...](마지막 점이 b). path(units, 지점 목록)와 함께 쓰면 부대가 이 경로를 따라 이동합니다(타워를 피하고 샛길로)."
 },
 {
  "name": "upkeep",
  "category": "observe",
  "status": "inferred",
  "mechanism": "1.27 고정 규칙: 인구 0~50은 유지비 없음, 51~80은 수입 ×0.7, 81~100은 ×0.4",
  "latency": "푸시 스냅샷",
  "signature": "upkeep(player: 'int | None' = None) -> 'dict | None'",
  "doc": "유지비 단계: {'level': 'none'/'low'/'high', 'income': 1.0/0.7/0.4, 'next_at': 다음 단계의 인구(없으면 None)}.\n프로의 상식: 3티어 테크 업이나 공격/방어 업그레이드 중에는 인구 50에서 멈추고, 결전 직전에야 80까지 늘립니다."
 },
 {
  "name": "xp_to_next",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 level/xp + MiscGame NeedHeroXP 공식",
  "latency": "푸시 스냅샷",
  "signature": "xp_to_next(hero) -> 'int | None'",
  "doc": "영웅이 다음 레벨까지 필요한 경험치(10레벨 = 0)."
 },
 {
  "name": "creep_camps",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷(크립을 600 거리 기준으로 한 무리로 묶음) + units.json 레벨",
  "latency": "푸시 스냅샷",
  "signature": "creep_camps(link: 'float' = 600.0) -> 'list'",
  "doc": "필드의 (보이는) 크립을 캠프로 묶습니다: [{'x','y','units','level','hp','max_level'}], 아군 본진에서 가까운 순.\nlevel = 캠프 총 레벨(사냥 난이도를 흔히 재는 기준), hp = 총 체력. time_to_kill / path_distance와 함께 사냥할 곳을 고르세요."
 },
 {
  "name": "buff_info",
  "category": "observe",
  "status": "verified",
  "mechanism": "data/game/buffs.json (AbilityData.slk의 BuffID -> 스킬/효과/지속 시간)",
  "latency": "로컬 데이터",
  "signature": "buff_info(code: 'str') -> 'dict | None'",
  "doc": "버프 코드가 무엇인지: {'ability','effect','dur','hero_dur','targets'}(예: 'Bslo' -> 감속). 한 코드에 여러 행이 있으면 첫 행을 반환합니다."
 },
 {
  "name": "stats",
  "category": "observe",
  "status": "verified",
  "mechanism": "데이터 테이블(UnitBalance/UnitWeapons/UpgradeData/MiscGame) + 실시간 기술 레벨 + 영웅 레벨",
  "latency": "푸시 스냅샷 + 고속 레인(5초마다 한 배치)",
  "signature": "stats(u, player: 'int | None' = None)",
  "doc": "유닛의 전투 속성 combat.UnitStats: 체력/마나 최대치, 방어력(공격/방어 업그레이드, 영웅 민첩 포함), 방어 타입, 이동 속도, 낮/밤 시야,\n무기(공격 가능 대상, 사거리, 공격 간격, 피해량 범위, 공격 타입, 스플래시). u는 유닛(소유자의 기술과 영웅 레벨을 자동 적용) 또는 4자 코드(player 기본값은 아군).\n.dps_vs(상대) / .hits_to_kill(상대) / combat.time_to_kill(무리, 상대)와 함께 쓰세요. ⚠ 아이템, 오라, 버프는 반영하지 않습니다."
 },
 {
  "name": "time_to_kill",
  "category": "observe",
  "status": "verified",
  "mechanism": "stats() + 실시간 체력",
  "latency": "푸시 스냅샷",
  "signature": "time_to_kill(attackers, target) -> 'float | None'",
  "doc": "이 유닛 무리가 함께 target을 공격해 죽이는 데 걸리는 게임 초(target의 현재 체력 기준. 상성, 방어력, 공격/방어 업그레이드는 반영하고 무빙, 스플래시, 치유는 반영하지 않음).\n프로의 활용법: 점사는 가장 가까운 적이 아니라 \"가장 빨리 죽일 수 있는\" 적(time_to_kill이 가장 작은 적)부터. 공격할 수 없으면 None."
 },
 {
  "name": "time_of_day",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 확장 영역: GetFloatGameState(GAME_STATE_TIME_OF_DAY)",
  "latency": "푸시 스냅샷",
  "signature": "time_of_day() -> 'float | None'",
  "doc": "게임 내 시각(시, 0~24). 시작은 오전 8시이며, 하루 = 480 게임 초(낮과 밤 각 240초, 낮밤 진행 속도에 따라 조정).\n읽지 못하면(구버전 런타임 / 게임 중이 아님) None을 반환합니다."
 },
 {
  "name": "is_night",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 확장 영역(6~18시가 낮)",
  "latency": "푸시 스냅샷",
  "signature": "is_night() -> 'bool | None'",
  "doc": "지금이 밤인지(18:00~6:00). 프로의 운영: 밤에는 크립이 잠들고(먼저 공격해도 포위당하지 않음), 모든 유닛의 시야가 짧아지며(기습하기 좋은 때),\n나이트 엘프의 보초와 유닛은 밤에 나무 곁에서 은신합니다. 읽지 못하면 None."
 },
 {
  "name": "seconds_until",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 확장 영역 + 하루 480초(실측 1시간 = 20 게임 초)",
  "latency": "푸시 스냅샷",
  "signature": "seconds_until(hour: 'float') -> 'float | None'",
  "doc": "게임 내 시각 hour시까지 남은 게임 초(예: seconds_until(18) = 해가 지기까지 남은 시간, 밤 사냥을 계획할 때 사용)."
 },
 {
  "name": "items_on_ground",
  "category": "observe",
  "status": "verified",
  "mechanism": "월드 블록 items[] (바닥에 있는 것만: 소지자 핸들이 모두 FF)",
  "latency": "푸시 스냅샷",
  "signature": "items_on_ground() -> 'list'",
  "doc": "바닥에 있는 아이템 [Item(addr, handle_lo, handle_hi, type, x, y, life)]. 주워 가거나 사용되면 item.removed 이벤트가 발생합니다."
 },
 {
  "name": "trees",
  "category": "observe",
  "status": "verified",
  "mechanism": "트리 블록 Local\\War3Trees_<pid>(2초마다 갱신)",
  "latency": "푸시 스냅샷",
  "signature": "trees(x: 'float | None' = None, y: 'float | None' = None, limit: 'int' = 60) -> 'list'",
  "doc": "살아 있는 나무(DestructableData에서 targType에 tree가 포함된 것). (x,y)를 주면 가까운 순으로, 최대 limit그루.\n각각은 Tree(addr, handle_lo, handle_hi, type, x, y, life)이며, 그대로 gather에 넘겨 벌목할 수 있습니다."
 },
 {
  "name": "events",
  "category": "observe",
  "status": "verified",
  "mechanism": "이벤트 링 Local\\War3Events_<pid>(발행 간 비교 + 런타임이 포착한 피해 이벤트)",
  "latency": "푸시 스냅샷",
  "signature": "events() -> 'list'",
  "doc": "마지막 호출 이후에 일어난 일: unit.appeared / unit.died / unit.removed / unit.damaged / order.changed /\nhero.levelup / owner.changed / item.appeared / item.removed / game.started (발행 간 비교로 얻음, 정밀도 = 발행 주기 50 ms),\n그리고 엔진 수준의 damage / killed (런타임이 게임 스레드에서 발생 즉시 기록, **타격마다** 발생):\n    damage: handle = 맞은 유닛, .source_addr = 때린 유닛(snapshot().unit_by_addr로 유닛으로 변환), .value = 실제로 깎인 체력,\n            .raw_damage = 방어력 적용 전 피해, .attack_type(normal/pierce/siege/magic/chaos/hero/spell), .damage_type\n    killed: 이 타격으로 죽음, .source_addr = 처치한 유닛\n그리고 런타임이 생산 테이블을 추적해 얻는 production.done (정밀도 = 발행 주기): 유닛 = 건물, .done_code = 완료된 4자 코드,\n    .done_kind = 'training'(유닛/영웅/부활) / 'research' / 'construction'(건물 완공) / 'upgrade'(테크 업/타워 업그레이드), .value = 걸린 게임 초\n09-25 보강:\n    spell.cast: 유닛 = 시전자, .spell 스킬 4자 코드, b 레벨, value 쿨다운 초, x,y 시전 지점(스킬 쿨다운이 시작될 때 인식, 정밀도 = 발행 주기)\n    player.left: .player 나가거나 패배 판정으로 제거된 플레이어 번호; game.ended: 게임에서 나감\n    selection.changed: 로컬 플레이어의 선택이 바뀜(g.selection()으로 유닛을 가져옴)\n    message: 화면 메시지 영역의 한 줄(게임 안내, 채팅, 시스템): .text 전체 텍스트, .frame 메시지 영역 번호,\n             .chat = {'channel', 'sender', 'text'}(채팅일 때. 플레이어가 채팅창에 입력한 내용은 여기서 읽음)\n    ui.click / ui.hover / hotkey / mouse.world: UI와 입력(g.ui), .key는 캔버스 key / 단축키 표기\n각 항목은 Event(seq, kind, clock, addr, handle, type, owner, a, b, x, y, value, extra)입니다.\n공정 모드(fair=True)에서는 다음만 제공합니다: 자기 유닛의 이벤트, 지금 보이는(또는 1초 전까지 보였던) 유닛의 이벤트, 아군이 받거나 가한 피해,\n그리고 로컬 UI / 메시지 / 게임 진행 관련 이벤트."
 },
 {
  "name": "selection",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 월드 블록 확장 영역 selAddrs(런타임이 발행할 때마다 로컬 플레이어의 선택을 함께 담음)",
  "latency": "푸시 스냅샷",
  "signature": "selection() -> 'list'",
  "doc": "로컬 플레이어가 지금 선택한 유닛(주 유닛이 맨 앞, 최대 12개). 선택이 바뀌면 selection.changed 이벤트가 발생합니다."
 },
 {
  "name": "messages",
  "category": "observe",
  "status": "verified",
  "mechanism": "공유 메모리 Local\\War3Msgs_<pid>(런타임이 포착한 화면 메시지)",
  "latency": "푸시 스냅샷",
  "signature": "messages() -> 'list'",
  "doc": "마지막 호출 이후 화면 메시지 영역에 새로 나온 메시지: [{'text', 'frame', 'repeat', 'seq', 'game_ms'}].\n게임 안내(\"농장이 더 필요합니다\", \"그곳에는 건설할 수 없습니다\"), 채팅, 시스템 메시지가 모두 여기에 있으며, frame으로 어느 메시지 영역인지 구분합니다.\n이벤트 스트림의 message 이벤트와 같은 메시지입니다(커서는 각자 따로)."
 },
 {
  "name": "ui",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P 74 input_enable + 공유 메모리 Local\\War3Input_<pid>(런타임이 창 입력을 받음)",
  "latency": "공유 메모리",
  "signature": "ui()",
  "doc": "UI와 입력(openwar3.ui.UI): 클릭할 수 있는 버튼과 선택 카드, 단축키, 지면을 클릭해 위치 고르기, 마우스가 가리키는 곳.\n버튼 위를 누른 클릭은 게임에 전달되지 않습니다. 순수한 로컬 입력 + 로컬 드로잉이므로 멀티플레이 게임에서도 안전합니다."
 },
 {
  "name": "tech",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_tech (엔진의 플레이어 기술 카운트)",
  "latency": "고속 레인",
  "signature": "tech(code: 'str', player: 'int | None' = None) -> 'int | None'",
  "doc": "연구 레벨 / 완성된 건물 수(업그레이드 체인 포함: 성도 htow로 셈). player 기본값은 아군이며, 모든 플레이어를 조회할 수 있습니다."
 },
 {
  "name": "can_do",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_feasible (엔진 실행 가능성 검사)",
  "latency": "고속 레인",
  "signature": "can_do(u, code: 'str') -> 'int | None'",
  "doc": "엔진의 실행 가능성 판정: 0/220 가능, 3 인구 부족, 8 금 부족, 9 목재 부족, 32 대기열 가득 참, 183 선행 조건 부족, 185 제단에서 부활 중, 221 해당 항목 없음/건설 중.\n⚠ 일꾼의 건물 건설에는 항상 221이므로 건설 위치 판정에는 쓸 수 없습니다(build_near 사용)."
 },
 {
  "name": "can_do_many",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_feasible × N, 한 배치로 제출",
  "latency": "고속 레인 × 1",
  "signature": "can_do_many(pairs) -> 'list'",
  "doc": "can_do를 한 번에 여러 개 묻습니다: pairs = [(유닛, 4자 코드), ...], 같은 순서의 판정 코드 목록을 반환합니다(조회하지 못한 것은 None).\n한 틱에 무엇을 짓고 뽑을지 계획할 때 먼저 한꺼번에 물어보면 can_do를 하나씩 부르는 것보다 N배 빠릅니다(레퍼런스 브레인 09-23: 건설 계획 76 -> 25 ms)."
 },
 {
  "name": "tech_many",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_tech × N, 한 배치로 제출",
  "latency": "고속 레인 × 1",
  "signature": "tech_many(codes, player: 'int | None' = None) -> 'dict'",
  "doc": "기술/건물 카운트를 한 번에 여러 개 조회합니다: {4자 코드: 개수 또는 None}."
 },
 {
  "name": "visible",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_visible (보임 / 안개 / 검은 영역)",
  "latency": "고속 레인",
  "signature": "visible(x: 'float', y: 'float') -> 'bool | None'",
  "doc": "이 지점이 지금 아군에게 보이는지(안개/검은 영역이 아님). 공정 모드 봇은 보이는 적만 사용해야 합니다."
 },
 {
  "name": "gold_left",
  "category": "observe",
  "status": "inferred",
  "mechanism": "W3P 쿼리 q_mine_gold (엔진의 금광 잔량)",
  "latency": "고속 레인",
  "signature": "gold_left(mine) -> 'int | None'",
  "doc": "금광에 남은 금."
 },
 {
  "name": "enemy_ai_plan",
  "category": "observe",
  "status": "verified",
  "mechanism": "W3P 쿼리 q_captain (적 병력이 따르는 컴퓨터 대장)",
  "latency": "고속 레인",
  "signature": "enemy_ai_plan(enemy_unit) -> 'dict | None'",
  "doc": "컴퓨터 AI의 대장: 병력을 이끌고 어디로 가려는지(출발 전부터 기지의 어디를 칠지 알 수 있음). 컴퓨터 상대에게만 유효하며, 대장을 따르지 않으면 None을 반환합니다."
 },
 {
  "name": "batch",
  "category": "command",
  "status": "verified",
  "mechanism": "블록 안의 명령을 한 배치로 모아 블록이 끝날 때 한 번에 제출(같은 프레임에 실행, 게임 스레드를 한 번만 기다림)",
  "latency": "고속 레인 × 1",
  "signature": "batch() -> 'Batch'",
  "doc": "한 틱의 명령을 한 배치로 묶습니다:\n\n    with g.batch() as b:\n        g.attack(archers, target)          # Pending 반환, 블록이 끝난 뒤 회신이 됨\n        g.move(wounded, *home)\n        g.cast(hero, \"thunderclap\")\n    print(b.sent, b.wait_ms, [r.reason for r in b.receipts])\n\n명령을 하나씩 보내면 매번 게임 스레드가 한 번 처리할 때까지 기다려야 합니다(약 10 ms). 배치는 한 번만 기다립니다 — 레퍼런스 브레인 09-23은 이것으로 한 라운드를 48 -> 26 ms로 줄였습니다.\n* 중재는 여전히 명령별로 거칩니다(점유된 유닛은 즉시 held 회신을 받고 배치에 들어가지 않음);\n* 블록 안의 명령은 Pending을 반환합니다: 블록이 끝나기 전에 .ok를 읽으면 예외가 발생하고(회신이 아직 없음), 블록이 끝난 뒤에는 Receipt처럼 씁니다;\n* 블록 안에서 예외가 발생하면 = 배치 전체 취소(status 97 cancelled), 점유했던 유닛은 반환됩니다;\n* 쿼리(can_do / tech / visible …)와 build_near, buy는 배치에 들어가지 않고 그 자리에서 바로 묻습니다 — 결과를 즉시 써야 하기 때문입니다;\n  여러 개를 한 번에 물으려면 can_do_many / tech_many를 쓰세요;\n* 중첩된 with g.batch()는 가장 바깥 배치에 합쳐집니다. 16개를 넘으면 런타임이 자동으로 여러 구간으로 나눕니다(구간마다 한 번 대기)."
 },
 {
  "name": "order_of",
  "category": "observe",
  "status": "verified",
  "mechanism": "스냅샷 오더 + 이 프로세스에서 방금 수락된 명령(회신)",
  "latency": "푸시 스냅샷",
  "signature": "order_of(u) -> 'int | None'",
  "doc": "유닛의 현재 오더. **이번 틱에 방금 내린 것도 포함**합니다(스냅샷이 아직 따라잡지 못했으면 회신의 새 오더를 사용).\n⚠ 09-23 실전: hello_bot이 농부를 농장 건설에 막 보냈는데, 같은 틱에 rush_bot이 스냅샷에서 그 농부를 \"대기 중\"으로 보고 병영 건설에 또 보내서, 농장이 번번이 중간에 포기되었습니다.\n  \"놀고 있는/건물을 짓고 있지 않은\" 유닛을 고를 때는 u.order 대신 이것을 쓰세요."
 },
 {
  "name": "move",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: move (extra 비트 = 대기열 방식)",
  "latency": "고속 레인",
  "signature": "move(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "(x,y)로 이동하며, 가는 길에 공격하지 않습니다(후퇴할 때 사용). 유닛 하나 또는 목록을 넘길 수 있습니다(같은 프레임에 함께 명령).\nqueue='after': 지금 하는 일을 끝낸 뒤 이동(현재 오더 뒤에 삽입). 회신 values[0] = 명령 후 이 유닛에 쌓인 오더 수(진행 중인 것 포함)."
 },
 {
  "name": "attack_move",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: 지점 대상 attack",
  "latency": "고속 레인",
  "signature": "attack_move(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "공격 이동(A 지면 클릭): 가는 길에 적을 만나면 공격합니다. queue는 move와 같습니다."
 },
 {
  "name": "attack",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: 대상 명령(우클릭 smart)",
  "latency": "고속 레인",
  "signature": "attack(units, target, force: 'bool' = False, queue: 'str | None' = None)",
  "doc": "target을 공격합니다. 기본은 우클릭입니다(적에게 = 이 대상 하나를 공격, 09-23 실측 결과 오더 대상/작업 대상 모두 그 대상).\n⚠ 대상은 반드시 시야 안에 있어야 하며, 보이지 않으면 거부됩니다(원인 코드 1001).\nforce=True는 공격 오더 0x0F를 사용합니다(아군/중립 동물을 공격할 때 필요) — 실측 결과 공격 오더만 바꾸고 대상을 기억하지 않아,\n근처의 다른 적을 공격하러 갑니다. 특정 대상을 공격할 때는 쓰지 마세요."
 },
 {
  "name": "stop",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: stop",
  "latency": "고속 레인",
  "signature": "stop(units)",
  "doc": "하던 일을 모두 멈춥니다(오더 ID 0x000D0004). 대기 중인 오더도 지웁니다."
 },
 {
  "name": "hold",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: holdposition",
  "latency": "고속 레인",
  "signature": "hold(units, queue: 'str | None' = None)",
  "doc": "위치 사수(쫓아가지 않고 사거리 안의 적만 공격)."
 },
 {
  "name": "patrol",
  "category": "command",
  "status": "inferred",
  "mechanism": "W3P point: patrol",
  "latency": "고속 레인",
  "signature": "patrol(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "현재 위치와 (x,y) 사이를 순찰합니다."
 },
 {
  "name": "attack_ground",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: attackground (공성 유닛 / 박격포 / 투석기)",
  "latency": "고속 레인",
  "signature": "attack_ground(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "지면 공격: 포가 한 지점에 포격합니다(은신 유닛, 숲 뒤의 적 공격, 길목 봉쇄). 지면 공격이 가능한 유닛만 받아들입니다."
 },
 {
  "name": "cancel",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: cancel",
  "latency": "고속 레인",
  "signature": "cancel(building)",
  "doc": "취소: 훈련/연구 대기열의 마지막 칸(환불), 건설 중인 건물(75% 환불), 업그레이드 중인 본진 건물."
 },
 {
  "name": "path",
  "category": "command",
  "status": "verified",
  "mechanism": "한 배치: 첫 구간은 즉시 실행, 나머지는 역순으로 queue='after'로 삽입(엔진은 현재 오더 뒤 삽입만 지원)",
  "latency": "고속 레인 × 1",
  "signature": "path(units, points, attack: 'bool' = False)",
  "doc": "일련의 지점을 순서대로 지나갑니다(Shift 연속 클릭: 경유지, 타워 우회, 정찰 경로). attack=True면 각 구간이 공격 이동입니다.\n한 번에 제출하며, 회신은 지점마다 하나씩(points 순서)입니다."
 },
 {
  "name": "gather",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: harvest (금광 또는 나무)",
  "latency": "고속 레인",
  "signature": "gather(workers, target, queue: 'str | None' = None)",
  "doc": "금 채집/벌목(target은 금광 또는 trees()의 나무). ⚠ 놀고 있는 일꾼(idle_workers)에게만 맡기세요: 작업 중인 일꾼에게 다시 명령하면 채집 주기가 끊깁니다.\n프로의 활용법: 건물을 다 지은 뒤 채광으로 복귀 = build(...) 다음에 gather(worker, mine, queue='after')."
 },
 {
  "name": "repair",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: repair",
  "latency": "고속 레인",
  "signature": "repair(workers, building, queue: 'str | None' = None)",
  "doc": "수리 / 건설 돕기(휴먼과 오크의 건설 현장은 짓는 일꾼이 없으면 공사가 멈춤)."
 },
 {
  "name": "build",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P build: 건설 오더, 같은 프레임에 일꾼 오더를 다시 읽어 확인",
  "latency": "고속 레인",
  "signature": "build(worker, code: 'str', x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "일꾼이 (x,y)에 code를 짓게 합니다(좌표는 32 단위로 정렬). 회신 수락 = 일꾼의 오더가 이미 이 건물(또는 착공 오더)이 됨.\nqueue='after'일 때 = 일꾼의 오더 대기열에 들어감(회신 values[0]은 대기 수).\n⚠ 수락 ≠ 건설 성공: 숲 속 지점도 엔진은 즉시 수락하고, 일꾼이 도착해서야 실패합니다(09-23 실측). 돈이 다른 곳에 쓰여도 기초가 생기지 않습니다.\n어디에 지을 수 있는지 모르면 build_near를 쓰세요(결과를 추적하고 실패한 지점을 블랙리스트에 올림). 여러 채를 연달아 지을 때는 build_queue."
 },
 {
  "name": "build_queue",
  "category": "command",
  "status": "verified",
  "mechanism": "한 배치: 첫 건물은 즉시, 나머지는 역순으로 queue='after'",
  "latency": "고속 레인 × 1",
  "signature": "build_queue(worker, plan)",
  "doc": "일꾼 한 명이 여러 건물을 순서대로 연달아 짓습니다(Shift 연속 건설): plan = [(4자 코드, x, y), ...]. 한 번에 제출하며, 회신은 plan 순서입니다.\n⚠ 비용은 착공할 때 차감됩니다(대기열에 넣을 때는 차감 안 됨) — 3채를 예약했는데 돈이 1채분뿐이면, 나머지 두 채는 일꾼이 도착했을 때 실패합니다."
 },
 {
  "name": "build_near",
  "category": "command",
  "status": "verified",
  "mechanism": "지점마다 build + 추적(기초 생성 = 성공, 일꾼이 오더를 포기했는데 기초가 없음 = 그 지점 블랙리스트)",
  "latency": "고속 레인 × 시도한 지점 수",
  "signature": "build_near(worker, code: 'str', x: 'float', y: 'float', min_r: 'float' = 450, max_r: 'float' = 1500, max_tries: 'int' = 24)",
  "doc": "(x,y) 주변에서 가까운 곳부터 차례로 지을 수 있는 지점을 찾아 code를 짓습니다. **블로킹하지 않으므로** 매 틱 호출해도 됩니다:\n  * 이 건물의 시도가 아직 진행 중(일꾼이 이동 중) -> 그 지점을 반환하고 명령을 반복하지 않음;\n  * 이전 시도가 성공(기초가 생김) -> 이번에는 필요하면 새 지점을 찾음;\n  * 이전 시도가 실패(일꾼이 도착해서야 지을 수 없음을 알고, 엔진이 오더를 취소했으며 기초가 없음) -> 그 지점을 45초간 블랙리스트에 올리고 다음 지점으로;\n  * 돈이 부족 -> 바로 None 반환(시도하지 않고 블랙리스트에도 올리지 않음); 모든 지점을 시도했으면 None 반환.\n⚠ 추적이 필요한 이유: 09-23 실전에서 숲 속 지점을 엔진이 **즉시 수락**하고 일꾼이 도착해서야 실패했습니다(같은 프레임의 회신으로는 판별 불가);\n  게다가 엔진의 배치 위치 검사는 일꾼의 건물 건설에 대해 항상 221을 반환하므로, 먼저 \"조회\"하고 지을 수도 없습니다. 명백히 점유된 지점(본진 건물 한가운데)만 즉시 거부됩니다."
 },
 {
  "name": "can_afford",
  "category": "observe",
  "status": "verified",
  "mechanism": "푸시 스냅샷의 아군 자원 + units.json의 가격",
  "latency": "푸시 스냅샷",
  "signature": "can_afford(code: 'str') -> 'bool'",
  "doc": "지금 금/목재로 code (유닛, 건물)를 살 수 있는지(units.json의 가격 기준). 가격표에 없는 것은 모두 살 수 있는 것으로 취급합니다.\n⚠ 테크 업 4자 코드는 표에 누적 가격으로 되어 있어 여기서는 보수적으로 판단됩니다. 최종 판단은 엔진의 회신을 따르세요."
 },
 {
  "name": "train",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: 4자 코드, 거부 시 실행 가능성 원인 코드 포함",
  "latency": "고속 레인",
  "signature": "train(building, code: 'str')",
  "doc": "유닛 훈련 / 기술 연구 / 본진 건물 업그레이드(테크 업 = 본진 건물 자체에 목표 본진 건물의 4자 코드를 명령, 예: 'hkee').\n거부되면 회신의 reason에 이유가 적힙니다(인구 부족, 금 부족, 목재 부족, 대기열 가득 참, 선행 조건 부족……)."
 },
 {
  "name": "learn",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P learn: 스킬 포인트가 줄어야 습득으로 인정",
  "latency": "고속 레인",
  "signature": "learn(hero, ability: 'str')",
  "doc": "영웅이 스킬을 배웁니다(4자 코드, 예: 'AHbz' 눈보라)."
 },
 {
  "name": "cast",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target / point / immediate (인자에 따라 선택)",
  "latency": "고속 레인",
  "signature": "cast(u, spell, target=None, x: 'float | None' = None, y: 'float | None' = None)",
  "doc": "스킬을 사용합니다. spell은 오더 문자열('thunderbolt' 폭풍 망치, 'blizzard', 'holybolt' Holy Light…, data/order-ids.txt 참고) 또는 오더 ID.\ntarget을 주면 = 유닛 대상, x,y를 주면 = 지면 대상, 둘 다 없으면 = 대상 없음(Thunder Clap, Divine Shield, 물의 정령 소환).\n회신 수락은 엔진이 받아들였다는 뜻일 뿐입니다. 실제로 시전됐는지는 cooldown()이 쿨다운에 들어갔는지, buffs()에 버프가 나타났는지로 확인하세요."
 },
 {
  "name": "rally",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P rally",
  "latency": "고속 레인",
  "signature": "rally(building, x: 'float | None' = None, y: 'float | None' = None, target=None)",
  "doc": "집결 지점을 설정합니다(지점, 또는 유닛/금광 대상)."
 },
 {
  "name": "revive",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P revive: 사망 영웅 목록 -> 제단이 죽은 영웅에게 부활 시전",
  "latency": "고속 레인",
  "signature": "revive(altar, hero=None)",
  "doc": "제단에서 죽은 영웅을 부활시킵니다(hero를 주지 않으면 목록의 첫 번째를 부활).\n흔한 거부 원인(회신 reason에 적힘): 인구 부족(영웅도 인구를 차지함), 돈 부족, 죽은 지 얼마 안 됨(사망 후 약 3 게임 초가 지나야 부활 가능),\n이미 부활 진행 중(수락될 때 엔진이 그 슬롯을 즉시 비움)."
 },
 {
  "name": "pick_up",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: 아이템 우클릭",
  "latency": "고속 레인",
  "signature": "pick_up(hero, item)",
  "doc": "영웅이 바닥의 아이템을 주우러 갑니다(item은 items_on_ground에서). 주우면 인벤토리에 나타나고, 바닥 쪽에는 item.removed 이벤트가 발생합니다."
 },
 {
  "name": "use_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P use_item (칸 번호 기준)",
  "latency": "고속 레인",
  "signature": "use_item(hero, slot: 'int', target=None, x: 'float | None' = None, y: 'float | None' = None)",
  "doc": "인벤토리 slot번째 칸(0~5)의 아이템을 사용합니다. 대상 유닛이나 대상 지점을 지정할 수 있습니다.\n⚠ 지점 대상 아이템(예: Ivory Tower)은 엔진이 성공해도 0을 반환하므로 회신은 항상 수락으로 처리됩니다 — 인벤토리 그 칸이 비었는지 확인하세요."
 },
 {
  "name": "drop_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P item_drop (JASS UnitDropItemPoint를 그대로 따름: dropitem 0xD0021 지점 대상 + 아이템 즉시 대상)",
  "latency": "고속 레인",
  "signature": "drop_item(hero, slot: 'int', x: 'float', y: 'float')",
  "doc": "인벤토리 slot번째 칸의 아이템을 (x,y)에 내려놓습니다(영웅이 걸어가서 내려놓음)."
 },
 {
  "name": "give_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P item_drop (JASS UnitDropItemTarget을 그대로 따름: 유닛 대상 dropitem)",
  "latency": "고속 레인",
  "signature": "give_item(hero, slot: 'int', to)",
  "doc": "인벤토리 slot번째 칸의 아이템을 to (다른 영웅 / 유닛)에게 줍니다(걸어가서 건네줌). 상점에 주면 = 판매(sell_item 참고)."
 },
 {
  "name": "sell_item",
  "category": "command",
  "status": "verified",
  "mechanism": "give_item과 같으며 대상이 상점(실측: Staff of Sanctuary를 125 금에 판매)",
  "latency": "고속 레인",
  "signature": "sell_item(hero, slot: 'int', shop)",
  "doc": "인벤토리 slot번째 칸의 아이템을 상점에 팝니다(영웅이 상점 옆까지 가야 함. 판매 가능한 아이템만 받으며, 가격의 절반을 돌려줌)."
 },
 {
  "name": "move_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: 오더 0xD0022+칸 번호, 대상 = 아이템(JASS UnitDropItemSlot을 그대로 따름)",
  "latency": "고속 레인",
  "signature": "move_item(hero, slot: 'int', to_slot: 'int')",
  "doc": "인벤토리 안에서 칸을 옮깁니다(slot번째 칸을 to_slot번째 칸으로. 두 칸 모두 아이템이 있으면 서로 바꿈). 단축키 배치를 정리할 때 사용합니다."
 },
 {
  "name": "buy",
  "category": "command",
  "status": "inferred",
  "mechanism": "W3P buy: 상점이 옆에 있는 영웅에게 판매",
  "latency": "고속 레인",
  "signature": "buy(shop, item_code: 'str')",
  "doc": "상점에서 아이템을 삽니다(상점 옆에 서 있는 영웅에게). 기술 선행 조건이 부족하면 엔진이 0을 반환하고 돈을 차감하지 않습니다."
 },
 {
  "name": "call_to_arms",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: townbellon/off",
  "latency": "고속 레인",
  "signature": "call_to_arms(hall, on: 'bool' = True)",
  "doc": "휴먼 Call to Arms: 농부가 민병대로 변합니다(1티어 마을 회관에는 이 능력이 없고, 성채/성에서만 유효)."
 },
 {
  "name": "set_speed",
  "category": "control",
  "status": "verified",
  "mechanism": "액션 47(25~800%)",
  "latency": "제어 채널",
  "signature": "set_speed(percent: 'int') -> 'bool'",
  "doc": "게임 배속(100 = 기본 속도)."
 },
 {
  "name": "pause",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P pause",
  "latency": "고속 레인",
  "signature": "pause(on: 'bool' = True)",
  "doc": "게임 일시정지 / 재개. 일시정지 중에는 엔진 시계가 멈추지만, 고속 레인으로는 평소처럼 명령을 내릴 수 있습니다(이벤트 디스패치는 계속 돌아감)."
 },
 {
  "name": "set_publish_period",
  "category": "control",
  "status": "verified",
  "mechanism": "월드 블록 requestedPeriodMs",
  "latency": "푸시 스냅샷",
  "signature": "set_publish_period(ms: 'int') -> 'None'",
  "doc": "월드 상태의 발행 주기(16~1000밀리초, 기본값 50). 한 번 수집에 약 0.5 ms가 들므로 33 ms도 문제없습니다. 기기 전체가 하나의 값을 공유하며, 마지막에 쓴 값이 적용됩니다."
 },
 {
  "name": "say",
  "category": "control",
  "status": "verified",
  "mechanism": "액션 56",
  "latency": "제어 채널",
  "signature": "say(u, text: 'str', seconds: 'float' = 4.0) -> 'bool'",
  "doc": "유닛 머리 위에 채팅 말풍선을 띄웁니다(방송/디버깅용, 게임에 영향 없음). 말풍선이 뜨지 않으면 False를 반환하고, 이유는 g.last_say_error에 있습니다."
 },
 {
  "name": "message",
  "category": "control",
  "status": "inferred",
  "mechanism": "액션 45",
  "latency": "제어 채널",
  "signature": "message(text: 'str') -> 'bool'",
  "doc": "게임 왼쪽 아래 메시지 영역에 한 줄을 출력합니다(이 PC에서만 보임). 게임이 먼저 알림을 한 번 띄운 뒤에야 동작합니다(DLL이 그때 메시지 창을 잡음)."
 },
 {
  "name": "end_game",
  "category": "control",
  "status": "verified",
  "mechanism": "액션 22",
  "latency": "제어 채널",
  "signature": "end_game() -> 'bool'",
  "doc": "이 게임 프로세스를 종료합니다(farm.py --keep은 next_game.json에 따라 다음 게임을 자동으로 시작)."
 },
 {
  "name": "canvas",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P 73 canvas_enable + 공유 메모리 Local\\War3Canvas_<pid>(런타임이 매 프레임 게임이 포인터를 그리기 직전에 그림. 포인터가 그 위를 덮음)",
  "latency": "공유 메모리",
  "signature": "canvas()",
  "doc": "캔버스: 게임 화면 위에 텍스트 상자, 패널, 진행 바, 이미지, 지면의 원과 경로를 그립니다(openwar3.canvas.Canvas).\n런타임이 직접 그리므로 게임 핸들을 만들지 않고 게임 상태도 바꾸지 않습니다 — 멀티플레이 게임에서도 안전합니다. 스타일은 자유롭습니다(CJK 텍스트, 둥근 모서리, 반투명)."
 },
 {
  "name": "press_to_continue",
  "category": "control",
  "status": "verified",
  "mechanism": "PostMessage WM_KEYDOWN/UP 스페이스를 게임 창에 전송(포커스를 빼앗지 않음)",
  "latency": "창 메시지",
  "signature": "press_to_continue() -> 'bool'",
  "doc": "“계속하려면 아무 키나 누르세요” 로딩 화면에서 스페이스를 한 번 누릅니다. 많은 RPG / 스토리 맵은 로딩이 끝난 뒤 키를 눌러야 시작합니다(09-24 WarChasers 실측:\n누르지 않으면 로딩 화면에 계속 머물고, 게임 시계는 0, 고속 레인도 비워지지 않음). openwar3.run 등은 게임에 들어갈 때 알아서 누르므로 보통 직접 호출할 필요가 없습니다."
 },
 {
  "name": "map_data",
  "category": "observe",
  "status": "verified",
  "mechanism": "맵 파일(런처 --map 경로): w3u/w3t/w3a + wts, 보호된 맵은 맵 안의 TXT를 읽음",
  "latency": "파일 읽기(첫 회 약 0.1초)",
  "signature": "map_data()",
  "doc": "지금 플레이 중인 맵의 데이터(openwar3.mapdata.MapData): name_of('HC07') 커스텀 유닛/아이템/스킬의 이름, hero_names, tooltip.\nRPG 맵의 유닛은 대부분 맵이 직접 만든 것이라 내장 이름 테이블에 없습니다. 런처로 시작한 게임이 아니면(맵 파일을 찾을 수 없으면) None을 반환합니다."
 },
 {
  "name": "jass",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "W3P 70 jass(런타임이 이름으로 native 테이블을 조회, 1291개)",
  "latency": "고속 레인",
  "signature": "jass()",
  "doc": "임의의 JASS native를 이름으로 호출합니다: g.jass.CreateUnit(g.jass.Player(1), \"Hpal\", x, y, 270.0).\n인수 I/R/B/S/H는 자동으로 변환되며(유닛/아이템 객체는 그대로 전달), 멀티플레이 게임에서는 읽기 전용 native만 호출할 수 있습니다. 자세한 내용은 openwar3/jass.py와 docs/COMPANION_ZH.md를 참고하세요."
 },
 {
  "name": "player_slots",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS GetPlayerController / GetPlayerSlotState / IsPlayerAlly",
  "latency": "고속 레인",
  "signature": "player_slots() -> 'list[dict]'",
  "doc": "플레이어 슬롯 16개: controller(user 사람 / computer / neutral…), state(empty / playing / left), human, me, ally(나와 동맹인지).\nRPG 맵에서 동료를 둘 빈 슬롯을 찾거나, 싱글플레이 게임인지 판단할 때 씁니다."
 },
 {
  "name": "spawn",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS CreateUnit + W3P 72 핸들 -> 유닛",
  "latency": "고속 레인",
  "signature": "spawn(code: 'str', x: 'float', y: 'float', player: 'int | None' = None, facing: 'float' = 270.0)",
  "doc": "(x,y)에 유닛 하나를 생성하고(player 기본값은 로컬 플레이어) 스냅샷 속 유닛을 반환합니다(다음 월드 발행까지 대기, 약 50 ms). 생성하지 못하면 None을 반환합니다.\n반환된 유닛에는 jass_handle 속성이 하나 더 있습니다. ⚠ 싱글플레이 게임에서만 사용할 수 있습니다(멀티플레이에서는 동기화가 어긋남)."
 },
 {
  "name": "set_alliance",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS SetPlayerAlliance",
  "latency": "고속 레인",
  "signature": "set_alliance(a: 'int', b: 'int', allied: 'bool' = True, vision: 'bool' = True, control: 'bool' = False, xp: 'bool' = False, both: 'bool' = True) -> 'None'",
  "doc": "플레이어 a가 b에 대해 갖는 동맹 관계를 설정합니다: allied = 서로 공격하지 않음 + 서로 지원 요청, vision 시야 공유, control 유닛 제어 공유(b가 a의 유닛을 지휘 가능),\nxp 경험치 공유. both=True이면 양방향을 함께 설정합니다(control은 a -> b만)."
 },
 {
  "name": "set_player_name",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS SetPlayerName",
  "latency": "고속 레인",
  "signature": "set_player_name(player: 'int', name: 'str') -> 'None'",
  "doc": "플레이어 이름을 바꿉니다(점수판, 채팅, 동맹 패널에 표시되는 이름). 동료에게 이름을 붙일 때 씁니다."
 },
 {
  "name": "show_text",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS DisplayTimedTextToPlayer",
  "latency": "고속 레인",
  "signature": "show_text(text: 'str', seconds: 'float' = 6.0, player: 'int | None' = None) -> 'None'",
  "doc": "화면 왼쪽 아래에 텍스트 한 줄을 표시합니다(맵 트리거가 쓰는 바로 그 텍스트). 기본적으로 로컬 플레이어에게만 보입니다. |cffRRGGBB 색상 코드를 지원합니다."
 }
]