# JASS 채널

> 맵 제작자가 쓸 수 있는 JASS 함수 1291개를 이제 게임 밖에서 이름으로 바로 호출할 수 있습니다. 유닛 생성, 속성 변경, 특수 효과, 패널, 대화 상자, 사운드, 카메라, 전장의 안개…… Farsight 콘솔, 명령줄, HTTP, Python 네 가지 방법으로 쓸 수 있습니다.

출처: https://war3ai.com/ko/docs/jass/

맵 제작자가 맵 스크립트에서 쓸 수 있는 **JASS native 1291개**를 이제 모두 게임 밖에서 이름으로 바로 호출할 수 있습니다. 유닛 생성, 속성 변경, 특수 효과 그리기, 패널과 대화 상자 띄우기, 사운드 재생, 카메라 이동, 안개 변경…… 게임을 한층 더 커스터마이즈하는 데 쓰세요 — RPG 보조, [AI 동료](https://war3ai.com/ko/docs/companion/), 직접 만든 미니 게임, 디버깅 도구.

| 사용법 | 적합한 경우 | 진입점 |
|---|---|---|
| **Farsight "JASS 콘솔" 페이지** | 직접 시험해 보기, 보면서 고치기 | 왼쪽 사이드바 "시스템 → JASS 콘솔": 스크립트를 쓰고 실행을 누름. 오른쪽에서 분류별로 함수를 찾고, 클릭하면 스크립트에 삽입 |
| **명령줄** | 직접 시험해 보기, 또는 스크립트 파일로 만들어 반복 실행 | `python -m openwar3 jass --inst 20`(대화형), `-e "코드"`, `my_script.j`, `--list 키워드` |
| **HTTP** | 어떤 언어로 만든 외부 프로그램이든 | `POST /api/instances/{n}/jass` 등(아래 참고). Farsight 백엔드는 로컬에서만 수신 대기 |
| **Python** | 스킴, 동료, 도구 작성 | `g.jass.함수이름(...)`. 자주 쓰는 화면·상호작용 기능은 `openwar3.visual`에 묶여 있음 |

> **주의**
>
> 경계는 세 가지이며, 모두 메커니즘상 정해진 것입니다.
> 
> - **싱글플레이 게임**(이 컴퓨터에서 컴퓨터 상대)에서만 월드를 바꿀 수 있습니다. 이 컴퓨터가 일방적으로 오브젝트를 만들거나 유닛을 바꾸면 멀티플레이 게임에서 다른 플레이어와 동기화가 깨집니다 — 멀티플레이 게임에서는 읽기 전용 함수(`Get*`, `Is*`, `Count*`……)만 허용합니다.
> - 이 컴퓨터의 자체 도구 전용입니다. 플레이어 신분으로 연결(`Game(player=N)`)했거나 공정 모드에서 호출하면 거부됩니다.
> - 싱글플레이 또는 LAN에서 직접 만든 게임에서만 사용하세요.
> 
> 멀티플레이 게임에서 화면에 무언가를 더하려면 [캔버스](https://war3ai.com/ko/docs/canvas/)를 쓰세요. 런타임이 직접 그리며 게임 상태를 바꾸지 않습니다.

## 스크립트 작성법

콘솔, 명령줄, HTTP는 같은 스크립트를 씁니다. 한 줄에 한 문장이며, **JASS를 그대로 붙여 넣을 수도 있고**(`call` / `set` / `local`, `true` / `false` / `null`, `'Hpal'` 4자 코드, `//` 주석) Python 식으로 쓸 수도 있습니다.

```text
set h = hero()                                   // 내장: 아군 주 영웅
local texttag t = CreateTextTag()
call SetTextTagText(t, "|cffffcc00+128 치명타!|r", 0.024)
call SetTextTagPosUnit(t, h, 60)
call SetTextTagVelocity(t, 0, 0.03)
call SetTextTagPermanent(t, false)
call SetTextTagLifespan(t, 4)
call SetTextTagVisibility(t, true)
call PingMinimapEx(h.x, h.y + 300, 3, 255, 0, 0, false)
set u = CreateUnit(Player(0), 'hfoo', h.x + 200, h.y, 270)
print("생성함", u, "영웅 레벨", GetHeroLevel(h))
```

- **변수는 계속 기억됩니다**: 같은 인스턴스, 같은 게임 안에서는 이번 블록에서 `set`한 변수를 다음 블록에서 이어서 쓸 수 있습니다. 게임이 바뀌면 자동으로 비워지며, 수동으로 비울 수도 있습니다.
- **내장 함수**: `hero()` 아군 주 영웅, `me()` 로컬 플레이어, `unit('hfoo')` 유닛 하나 찾기, `unit_at(x, y)`, `wait(초)`, `print(...)`. 유닛에서는 `.x`, `.y`, `.hp`, `.hp_max`, `.mana`, `.type`, `.owner`, `.level`을 읽을 수 있고, 사칙연산과 비교를 지원합니다.
- `if`, `loop`, `function`은 **지원하지 않습니다** — 로직을 쓰려면 Python의 `g.jass`(평범한 함수 호출입니다)를 쓰거나 [스킴](https://war3ai.com/ko/docs/schemes/)으로 만드세요.
- 오류가 나면 몇 번째 줄에서 왜 났는지 알려 줍니다(그런 함수가 없음, 인자 개수가 틀림, 정의되지 않은 변수……). 오류 이전의 문장은 이미 적용되어 있습니다.

인자와 반환값:

| 시그니처 | 전달할 값 | 설명 |
|---|---|---|
| 정수 | 숫자. `'Hpal'` 4자 코드는 자동 변환 | |
| 실수 | 숫자 | 런타임이 엔진이 요구하는 형식으로 변환 |
| 불리언 | `true` / `false` | |
| 문자열 | `"..."` | 중국어 등 비ASCII 문자와 게임 색상 코드 지원. 게임이 보관하는 문자열(떠다니는 텍스트, 패널, 버튼, 채팅 명령)은 모두 그 자리에서 복사해 두므로 안전 |
| 핸들 | 변수에 담긴 핸들, 또는 유닛(`hero()` 같은 것은 자동으로 핸들로 변환) | |
| 함수(code) | `null`만 가능 | 밖에서는 JASS 함수를 넘길 수 없음. `TimerStart(t, 60, false, null)` 같은 것은 가능 |
| 문자열 반환 | — | 엔진이 반환하는 것은 문자열 테이블 번호라서 텍스트를 읽어 올 수 없음. 유닛 이름은 `g.map_data.name_of` 사용 |

## 분류

함수는 이름으로 분류되어 있으며, 콘솔 오른쪽과 `--list` 모두 이 분류를 따릅니다.

| 분류 | 개수 | 예 |
|---|---|---|
| 화면 효과 | 80 | 떠다니는 텍스트, 번개 연결선, 특수 효과, 지면 이미지, 지면 표식, 유닛 색 변경 / 크기 조절 / 애니메이션 재생 |
| UI 패널 | 146 | 멀티보드, 리더보드, 타이머 창, 대화 상자, 퀘스트, 화면 텍스트, 미니맵 핑, 초상화 대사, 전체 화면 필터 |
| 카메라 | 44 | 카메라 필드, 팬 이동, 카메라 흔들기 |
| 사운드·음악 | 50 | 사운드 생성과 재생, 음악 재생 |
| 안개·시야 | 25 | 가시 영역, 안개 켜기/끄기 |
| 아이템 / 영웅 / 유닛 | 63 / 32 / 161 | 아이템 생성, 영웅 레벨 설정, 소유자 변경, 스킬 추가 |
| 플레이어 / 동맹 / 자원 | 71 | 동맹 설정, 금·목재 변경 |
| 트리거 / 이벤트 / 타이머 | 62 | 트리거 생성, 이벤트 등록, 타이머 |
| 지형 / 날씨 / 파괴 가능 오브젝트 | 45 | 날씨 효과, 지형 변경, 파괴 가능 오브젝트 생성 |
| 게임 진행 | 57 | 게임 속도, 일시정지, 낮밤 시각 |
| 기타 | …… | 유닛 그룹과 영역, 저장소, 컴퓨터 AI 스크립트, 형 변환과 수학, 이벤트 응답…… |

2026-09-24에 하나씩 실제 게임에서 호출해 효과를 눈으로 확인한 것은 **94개**입니다. 나머지도 같은 경로를 거치지만, 하나하나 효과를 확인하지는 않았습니다.

> **참고**
>
> "이벤트 응답" 계열 함수(`GetTriggerUnit`, `GetClickedButton`……)는 트리거가 실행되는 그 순간에만 값이 있어서, 밖에서 호출하면 0이나 빈 값을 받습니다. "일어났는지 여부"를 알고 싶다면 아래의 이벤트 카운트를 쓰세요.

## HTTP

Farsight 백엔드(기본 `127.0.0.1:8866`, 로컬에서만 수신 대기):

```http
GET  /api/jass/natives?q=TextTag&cat=visual
POST /api/instances/20/jass        {"code": "set h = hero()\ncall PingMinimapEx(h.x, h.y, 3, 255, 0, 0, false)"}
     -> {"ok": true, "rows": [...], "printed": [...], "vars": {...}}
     -> 오류: {"ok": false, "error": "第 2 行：...", "line": 2}      ("第 2 行" = 2번째 줄)
POST /api/instances/20/jass/call   {"name": "SetUnitScale", "args": [{"unit": 599669636}, 1.4, 1.4, 1.4]}
POST /api/instances/20/jass/reset  기억된 변수 지우기
```

유닛 인자는 `{"unit": 주소}`로 쓰며, 주소는 스냅샷에 있는 유닛의 `addr`입니다. 실측 요청 1회에 60 ~ 90 ms.

## Python: g.jass와 openwar3.visual

```python
j = g.jass
t = j.CreateTextTag()
j.SetTextTagText(t, "안녕", 0.024)     # 인자 규칙은 스크립트와 같음. 스냅샷의 유닛, 아이템 객체를 그대로 넘겨도 됨
j.signature("CreateImage")             # 시그니처 조회
```

`openwar3.visual.Visual(g)`는 실측으로 확인한 자주 쓰는 화면 효과를 한 줄에 하나씩 쓸 수 있게 묶어 둔 것입니다(틱마다 `v.tick()`을 한 번 호출하면 만료된 것은 지우고, 유닛을 따라가는 선과 원은 옮겨 줌. `v.clear()`는 전부 삭제).

| 메서드 | 효과 |
|---|---|
| `float_text(텍스트, 유닛 또는 지점, ...)` | 떠다니는 텍스트: 피해 숫자, 머리 위 알림. 비ASCII 문자와 색상 모두 가능 |
| `link(a, b, kind)` | 두 유닛 사이의 선, 유닛을 따라감: 견인 / 영혼 연결 / 생명력 흡수 / 치유의 물결 |
| `effect(모델, 유닛 또는 지점, ...)` | 특수 효과 모델: 머리 위, 발밑, 또는 한 번 재생(폭발, 빛기둥) |
| `ring(유닛 또는 지점, 반지름, color)` | 지면의 범위 원: 스킬 범위, 위험 지역, 집결 지점. 유닛을 따라가게 할 수 있음 |
| `ping(지점, color)` | 미니맵 핑 |
| `board(제목, 줄...)` | 오른쪽 위 멀티보드(아이콘 포함). 칸 단위로 수정 가능 |
| `countdown(제목, 초)` | 오른쪽 위 타이머 창. 게임이 알아서 초를 셈 |
| `scene(이름, 대사, portrait)` | 초상화 대사: 하단 초상화가 말하는 유닛으로 바뀌고, 화면에 "이름: 대사" 자막이 나옴 |
| `screen_tint(color, alpha)` | 전체 화면 필터(기본값은 가장자리가 붉게 물듦: 체력 경고) |
| `sound(경로)` / `reveal(지점, 반지름, 초)` / `look(유닛, ...)` | 사운드 재생 / 안개 일부 걷기 / 유닛 색 변경, 확대, 애니메이션 재생, 번쩍임 |

## 상호작용: JASS 함수를 쓰지 않고도 플레이어가 한 일 알아내기

JASS에서 플레이어에게 반응하려면 트리거 함수를 써야 하는데, 밖에서는 함수를 넘겨줄 수 없습니다. 해법은 이렇습니다. **조건도 동작도 없는 빈 트리거를 만들어 이벤트만 등록하고, 그 트리거가 몇 번 실행됐는지 셉니다.** 실측 결과 빈 트리거도 실행 횟수가 그대로 집계됩니다.

| 메서드 | 용도 |
|---|---|
| `chat_commands(["-follow", "-stay"])` → `.poll()` | 플레이어가 채팅창에 입력한 명령(정확히 일치, 또는 앞부분 일치) |
| `menu(제목, [버튼...])` → `.clicked()` | 화면 가운데 버튼 메뉴에서 어느 것을 눌렀는지 |
| `hotkeys(("left", "right", "up", "down", "esc"))` → `.poll()` | 방향키, Esc가 몇 번 눌렸는지 |
| `on("TriggerRegister...Event", 인자...)` → `.poll()` | 임의의 JASS 이벤트가 몇 번 발생했는지: 유닛 사망, 영역 진입, 피해, 타이머…… |

한계는 "몇 번 일어났는지"만 알 뿐 "누가, 무슨 글자를 입력했는지"는 모른다는 것입니다. 누구인지 구분하려면 대상마다 카운터를 따로 만드세요. [AI 동료](https://war3ai.com/ko/docs/companion/)의 채팅 명령도 이렇게 연결했습니다.

## 주의

- **만든 것은 직접 지워야 합니다**: 떠다니는 텍스트, 연결선, 이미지, 패널, 트리거…… 지우지 않으면 계속 남습니다(`Visual.clear()`는 자신이 만든 것을 지웁니다). 게임에는 떠다니는 텍스트를 동시에 최대 약 100개까지 둘 수 있습니다.
- **BJ 함수는 native가 아닙니다**: `CreateTextTagUnitBJ` 같은 함수는 맵 스크립트에서 native를 조합해 만든 것이라 여기에는 없습니다 — 그 구현을 보고 native를 호출하세요.
- **어떤 상수는 먼저 변환해야 합니다**: 예를 들어 `ConvertPlayerColor(1)`, `ConvertFogState(4)`(값은 common.j 참고).
- 호출 1회에 약 13 ms(핸들 변환 포함). 프로토콜 계층은 W3P 연산 코드 70 ~ 72이며, [W3P 프로토콜](https://war3ai.com/ko/docs/protocol/)을 참고하세요.
