분류 : (+)
목차
1. 개요
2. 변수 체계 및 이벤트 제어
2.1. 변수 스코프별 특성
2.2. 세이브 데이터 호환 및 이벤트 중복 push 방지
2.3. 거점 및 시설의 단계 관리 (eventStep)
3. 파티원 및 조건 판정 스크립트
3.1. 성별 및 특정 캐릭터 유무 판정
3.2. 캐릭터 발화 및 존재 확인
4. 아이템 풀 구성 및 장비 패시브 제어
4.1. 아이템 목록 수집 및 가챠 풀 구성
4.2. 장비 소지 시 상태이상/스탯 부여 패시브
5. 전투 시스템 및 행동 제어
5.1. living.tjs의 행동 결정 메커니즘
5.2. 몬스터 판정 시 객체 비교 오류 방지
6. 게스트 및 신규 캐릭터 제작 규격
6.1. 스탠딩 일러스트 및 표정 차분 네이밍
6.2. 데이터 등록 절차
7. 텍스트 기반 맵 데이터 작성 사양
7.1. 유효 칸과 벽 행 구조
1. 개요 ✎
본 문서는 키리키리(吉里吉里) 및 KAG/TJS 엔진 기반 롤플레잉 게임의 스크립트 개발, 모딩, 시스템 제어 방식에 관한 기술 정보와 개발진 질의응답 내용을 정리한 문서이다. 이벤트 플래그 처리, 파티원 판정, 전투 로직 제어, 게스트 캐릭터 등록, 맵 데이터 작성 사양 등 개발 및 데이터 수정 전반에 필요한 핵심 규격을 다룬다.
2.1. 변수 스코프별 특성 ✎
게임 내에서 사용되는 변수는 수명 주기와 보존 영역에 따라 스코프가 구분된다.
- tf.○○ (임시 변수): 게임 프로세스 종료 시 초기화된다. 일시적인 계산이나 임시 리스트 작성 시 사용된다.
- f.○○ (세이브 데이터 종속 변수): 각 세이브 데이터 파일에 저장 및 연동된다. 특정 세이브 파일 내의 퀘스트 진행도나 이벤트 발생 여부를 판정하는 플래그로 적합하다.
- sf.○○ (전역 시스템 변수): 모든 세이브 데이터가 공유하는 공통 영역에 저장된다. 환경 설정(옵션), 도감 수집 이력, CG 및 회상 개방 등에 사용된다.
- 특정 회차나 세이브 데이터 독립적으로 진행되어야 하는 이벤트에 sf 변수를 사용하면, 한 세이브 데이터에서 열람한 이벤트가 다른 세이브 데이터에서도 이미 발생한 것으로 간주되는 문제가 발생하므로 주의해야 한다.
2.2. 세이브 데이터 호환 및 이벤트 중복 push 방지 ✎
기존 세이브 데이터를 불러올 때 새롭게 추가된 이벤트를 적용하려면 로드 시점에 이벤트를 목록에 주입(push)해야 한다. 단, 조건문 없이 주입할 경우 세이브 데이터를 불러올 때마다 중복으로 push되는 오류가 발생한다.
이를 방지하는 대표적인 처리 패턴은 다음과 같다.
이를 방지하는 대표적인 처리 패턴은 다음과 같다.
- f 변수 플래그 방식: 주입 여부를 세이브 변수에 기록하여 1회만 실행되도록 제어한다.
if (f.slum === void) o.スラム・モスロック.push2(0);
// 모스록의 이벤트 0번 스크립트 내부에서 f.slum = 1 처리
- 더미 이벤트 및 delevent 연계 방식:
- 로드 시점에서 목표 이벤트(예: event0)와 더미 판정용 빈 이벤트(예: event99)의 존재 여부를 동시에 검사한다(`if (!events.has(0) && !events.has(99))`).
- 두 이벤트를 모두 push한 뒤, event0이 시작될 때 둘 중 하나를 삭제(`delevent`)하여 다음 로드 시 중복 실행되지 않도록 차단한다.
2.3. 거점 및 시설의 단계 관리 (eventStep) ✎
- `eventStep`은 라이벌 파티나 특정 개체의 단계 제어에 기본 정의된 변수이다.
- 마을(town)이나 시설(house)에 임의로 `eventStep` 변수를 선언하여 사용할 경우 세이브 시 보존되지 않고 초기화될 수 있다.
- 마을 및 시설에서 단계적 플래그 관리를 영속화하려면 해당 객체(`town`, `house`)에 `var eventStep = 0;`을 명시적으로 선언하고 데이터 사전(`dic`)에 직렬화하여 저장하거나, `f.변수`를 통하여 제어해야 한다.
3.1. 성별 및 특정 캐릭터 유무 판정 ✎
KAG 인라인 식(`exp`)에서 파티원의 구성 상태를 판정할 때 다음 함수를 활용한다.
- 남성/여성 존재 여부 판정:
- `game.party.getFirstGuy() === void`: 파티에 남성 캐릭터가 없음
- `game.party.getFirstHeroine() === void`: 파티에 여성 캐릭터가 없음
- `game.party.getFirstHeroine() !== void`: 파티에 여성 캐릭터가 최소 1명 이상 존재함
- 파티 리더 성별 판정:
- `game.leader.sex == "男"`: 현재 리더의 성별이 남성인지 확인
3.2. 캐릭터 발화 및 존재 확인 ✎
- `TEO` vs `isTEO`의 차이:
- `isTEO`: 현재 발화 대상(`curChara`)이 해당 캐릭터일 때 참을 반환한다.
- `TEO`: 파티 내에 해당 캐릭터가 존재할 경우, 그 캐릭터를 즉시 `curChara`로 전환하여 대사를 출력하게 만든다.
- 비선두 및 파티 멤버 존재 판정:
- `game.leader`는 진형의 선두에 위치한 캐릭터만 가리키므로, 선두가 아닌 파티원을 찾을 때는 적합하지 않다.
- 단순히 특정 캐릭터가 파티 멤버로 등록되어 있는지 확인하려면 `game.party.members.has(...)` 형태를 검사하는 것이 안전하다.
- `o.캐릭터명` 또는 `game.playerChara`를 직접 참조할 경우, 파티에 없더라도 데이터베이스에 등록된 캐릭터 객체 자체가 반환될 수 있으므로 분기 조건 설정 시 유의해야 한다.
4.1. 아이템 목록 수집 및 가챠 풀 구성 ✎
특정 범주의 아이템을 추출하여 보물상자나 가챠 풀을 동적으로 생성할 때의 구현 절차이다.
1. `@call storage="appendix.ks" target=*items_list` (방어구: `*armors_list`, 무기: `*weapons_list`) 매크로를 호출하여 전체 목록 데이터를 메모리에 로드한다.
2. `game.collectionList` 배열을 순회하며 특정 카테고리(예: "링", "장식품")이면서 이벤트 전용 아이템(`event == false`)이 아닌 항목을 임시 목록(`tf.list`)에 수집한다.
3. 랭크(`rank`) 및 제외 목록(`game.closeItems`)을 필터링하여 출현 확률 테이블(`tf.pool`)에 등록한다.
1. `@call storage="appendix.ks" target=*items_list` (방어구: `*armors_list`, 무기: `*weapons_list`) 매크로를 호출하여 전체 목록 데이터를 메모리에 로드한다.
2. `game.collectionList` 배열을 순회하며 특정 카테고리(예: "링", "장식품")이면서 이벤트 전용 아이템(`event == false`)이 아닌 항목을 임시 목록(`tf.list`)에 수집한다.
3. 랭크(`rank`) 및 제외 목록(`game.closeItems`)을 필터링하여 출현 확률 테이블(`tf.pool`)에 등록한다.
- 저랭크 아이템의 출현 빈도를 높이려면 반복문 루프 횟수를 늘려 풀에 동일 아이템 이름을 다수 삽입하는 방식을 취한다.
4.2. 장비 소지 시 상태이상/스탯 부여 패시브 ✎
인벤토리에 특정 아이템을 소지하고 있을 때 자동으로 스탯 보너스나 상태이상을 부여하는 로직이다.
1. `statdata.tjs`: 신규 스테이터스 데이터를 정의하고, `affectedParam` 속성을 통해 증감치를 지정한다. (전투 중에만 적용하려면 `master.inBattle` 조건을 추가)
2. `item.tsv`: 해당 아이템의 기본 데이터를 정의한다.
3. `party.tjs`: 이동 시 상태를 점검하는 `checkStatusByMoving()` 함수의 시작 부분에서 `findItem` 판정을 수행한다. 소지 중(`true`)이면 `setStatus`로 해당 상태이상을 부여하고, 미소지(`false`) 상태면 상태이상을 제거하도록 처리한다.
1. `statdata.tjs`: 신규 스테이터스 데이터를 정의하고, `affectedParam` 속성을 통해 증감치를 지정한다. (전투 중에만 적용하려면 `master.inBattle` 조건을 추가)
2. `item.tsv`: 해당 아이템의 기본 데이터를 정의한다.
3. `party.tjs`: 이동 시 상태를 점검하는 `checkStatusByMoving()` 함수의 시작 부분에서 `findItem` 판정을 수행한다. 소지 중(`true`)이면 `setStatus`로 해당 상태이상을 부여하고, 미소지(`false`) 상태면 상태이상을 제거하도록 처리한다.
5.1. living.tjs의 행동 결정 메커니즘 ✎
NPC의 자동 행동(`.auto == true`)은 `living.tjs`의 `getUsableActions()` 함수에서 추첨된다.
- `cond`와 `determined`의 차이:
- `cond`: 스킬 사용의 전제 조건이다. 조건을 만족하면 행동 후보 목록에 등록된다.
- `determined`: `true`로 평가되는 순간, 후속 후보를 탐색하지 않고 해당 스킬로 행동을 즉시 확정한다. 특정 턴이나 특정 상황에서 반드시 특정 행동을 강제하고자 할 때 사용된다.
- 주의사항: `cond` 판정이 `determined`보다 먼저 실행된다. `determined`를 부적절하게 구성하면 매 턴 동일한 행동만 반복하는 현상이 발생할 수 있다.
5.2. 몬스터 판정 시 객체 비교 오류 방지 ✎
전투 중 특정 몬스터를 판정할 때 단일 객체 인스턴스 비교(`m.몬스터명`)를 사용하면 오작동할 수 있다.
- 몬스터는 필드에 동일한 종류가 여러 마리 출현할 수 있으므로, 단일 객체 포인터(`m[...]`)는 하나만 유지된다.
- 따라서 특정 인덱스의 적을 식별할 때는 객체 직접 비교가 아닌 문자열 이름(`enemy.members[i].name == "몬스터명"`)을 비교해야 정상적으로 인식된다.
6.1. 스탠딩 일러스트 및 표정 차분 네이밍 ✎
기본 스탠딩 CG 이미지 이름 접두사(`img`)를 기준으로 접미사를 조합하여 작성한다.
- 의상 차분 접미사:
- `_default`: 기본 스탠딩 일러스트 (평상복)
- `_topless`: 상하의 속옷 상태
- `_under`: 팬티만 착용한 상태
- `_swim`: 수영복
- `_nude`: 전라
- `_bigbelly`: 전라 임신 상태
- `_sweat`: 전라 땀 상태
- `_fight`: 무기를 소지한 전투 스탠딩 (누락 시 로딩 에러 발생 원인이 되므로 필수 제작 필요)
- 표정 차분: 접두사 뒤에 `_00`, `_24` 등의 번호로 분리 저장
- MOD 추가 특수 의상:
- `[이름]_special.png` ~ `special8.png` 형식으로 명명
- 미에로 장비(노출도 중간)와 에로 장비(전라에 준하는 레벨)로 구분
- 최근 패치 규격에서는 PNG 대신 WebP 포맷을 사용하며, XP3로 패킹하여 배치
6.2. 데이터 등록 절차 ✎
1. monster.csv 등록: 몬스터 및 게스트의 기본 스탯, 속성, 기술, 직업, 진형 데이터를 등록한다.
- 게스트 캐릭터는 `mLV` 값을 `-2`로 지정한다.
[macro name="캐릭터명"][swname * n=&'mp.tagname' key=&'mp.tagname' buf=고유번호][endmacro]
- `buf` 번호는 기존에 사용되지 않은 고유 번호를 순차적으로 부여한다.
- `face`: 대화 시 표정 오버레이가 배치될 좌상단 좌표 `[x, y]`
- `trim`: 대화창 컷인 잘라내기 시작 위치 `[x, y]`
- `figureTop`: 스탠딩 CG의 화면 높이 오프셋
- `threeSize`: 신체 치수 `[B, W, H]`
- `isNPC`: 동료화 이전 정보 표시 여부 (`true` 시 NPC로 표기)
7.1. 유효 칸과 벽 행 구조 ✎
텍스트 에디터 상에서 맵을 구성할 때 지나갈 수 있는 타일과 벽/문 타일의 좌표 체계를 분리해야 한다.
- 시스템상 벽과 문은 시각적으로는 얇은 경계선이지만, 내부 데이터 배열에서는 독립된 1행 전체를 점유한다.
- 행 계산 규칙:
- 실제 필요한 데이터 행 수 = `(이동 가능한 유효 칸의 행 수) + (유효 칸의 행 수 + 1)`
- 예시: 3×3 크기의 방을 만들 경우 총 7행의 데이터가 필요하다.
- 1행: 외벽
- 2행: 유효 이동 칸 1
- 3행: 내벽
- 4행: 유효 이동 칸 2
- 5행: 내벽
- 6행: 유효 이동 칸 3
- 7행: 하단 외벽 및 문
- 배치 시 주의사항:
- 보물상자, 계단, 진입 이벤트 등은 반드시 '유효 이동 칸(바닥)' 행에 배치해야 한다. 벽 행에 배치하면 트리거가 소실된다.
- 벽이나 문 오브젝트를 '유효 이동 칸'에 배치하면 판정이 충돌하거나 증발하므로 정해진 벽 전용 행에만 배치해야 한다.
- 텍스트 맵 작업 시 MS Gothic 등 고정폭(등폭) 폰트 환경의 Excel 또는 VS Code를 활용하면 열과 행 정렬 오류를 줄일 수 있다.

