규칙 집합 레이어, 번역과 마이그레이션
이 페이지에서는 플레이어가 한 게임에 켜는 변형을 제공하고, 규칙 집합 텍스트를 번역하고, 시트가 바뀔 때 저장된 값을 새 ID로 옮기는 방법을 설명합니다. 규칙 집합 작성자를 위한 페이지입니다. 시작하기 전에 Game Mode 규칙 집합 작성하기를 읽으세요.
레이어: 자체 규칙 집합의 변형
레이어는 Low magic, Hard winter, 더 가혹한 난이도처럼 플레이어가 게임을 만들 때 켜는 규칙 집합의 이름 붙은 변형입니다. 규칙 집합 파일의 선택적 layers 배열 안에 있으므로 함께 이동하며, 사용한 게임에서 사라질 수 없습니다. 마법사는 규칙 집합 아래 토글로 보여 주며, 선택은 규칙 집합 자체와 똑같이 그 게임의 수명 동안 고정됩니다.
"layers": [
{
"id": "hard_winter",
"label": "Hard winter",
"summary": "Cold, hunger and short days. Everything is harder.",
"conflicts": ["mud_season"],
"gm": {
"guidance": "Hard winter is on. Let a failed check cost warmth, food or daylight as well as progress.",
"worldGuidance": "Hard winter is on. Build a world of closed roads, thin stores and rationed settlements."
},
"fields": [{ "id": "calling", "removeValues": ["Sailor"], "default": "Hauler" }],
"difficultyLadder": [{ "label": "Easy", "dc": 7 }],
"catalogs": [{ "id": "knacks", "hide": { "filter": "grit", "above": 0 } }]
},
{
"id": "mud_season",
"label": "Mud season",
"summary": "Thaw, flooded roads and slow going."
}
]레이어가 할 수 있는 것. 목록은 고정되어 있으며 모든 효과는 무언가를 좁히거나 텍스트를 더합니다.
gm.guidance는 작성자 자체 텍스트와 앞선 레이어 뒤에gm.checkGuidance로 덧붙입니다.gm.worldGuidance도 같은 방식으로gm.worldGuidance에 덧붙입니다.fields는 enum 필드에서 값을 제외합니다.removeValues는 필드가 이미 가진 값을 지정하며 최소 하나는 남아야 합니다. 필드의default도 제외 대상이면 레이어가 살아남는default를 대신 지정합니다.difficultyLadder는 자체 해결 유형과 같은 형태의 다른 사다리로 바꿉니다.dice-sum은{label, dc},dice-pool은{label, successes, target?}입니다. 원래 사다리와 정확히 같은 검증을 받습니다. 여러 활성 레이어가 선언하면 마지막 것이 우선합니다.catalogs는 시트 편집기의 선택기에서 항목을 숨깁니다. 각 규칙은 해당 카탈로그가 선언한filters하나와 비교 하나를 지정합니다.number필터는above또는below,text나tags필터는equals또는notIn입니다. 그 필터를 아예 설정하지 않은 항목은 숨기지 않습니다.currencies는 동전을 제외합니다.removeUnits는 개별 동전을,removeFamilies는 계열 전체를 지정합니다. 계열의 가장 작은 동전은 계열과 함께만 제외할 수 있으므로 남은 모든 계열은 여전히 지불하고 거스름돈을 줄 수 있습니다. 레이어가 켜져 있는 동안 아무도 제외된 동전을 벌거나, 지불하거나, 고르거나, 떨어뜨리지 않으며 지갑 줄에도 나오지 않습니다. 그 동전으로 적힌 가격은 같은 가치로 정확히 지불할 수 있는 남은 동전 중 가장 큰 것으로 표시합니다. Gravewatch의 The long night 레이어는 크라운을 제외하므로 3크라운짜리 경비 권총은 15실링입니다. 계열 전체가 사라진 아이템은 가격이 없습니다.items는 어디에서 무엇을 팔고 무엇이 떨어지는지 바꾸며, 무엇이 존재하는지는 바꾸지 않습니다.market.removeSellers는 시장에서 판매자를 제외합니다(항상 최소 하나는 남습니다. 판매자가 없는 시장에서는 누구나 모든 것을 팔 수 있으므로, 두 레이어가 함께 작용하는 경우를 포함해 판매자가 하나도 남지 않게 되는 제외는 적용하지 않습니다).market.unsold는 시장 자체와 같은 필터로 모든 판매자의 판매 물건에서 아이템을 제외합니다(시장의unsold에 더합니다).lootTables는 이미 있는 전리품 표를 ID로 지정하여 같은 형태의 표로 교체하므로, 그 표를 지정한 모든 생물, 위협 단계, 판매자의stock은 교체된 표를 굴립니다. 여러 활성 레이어가 같은 표를 교체하면 마지막 것이 우선합니다. 팔지 않는 아이템도 여전히 아이템입니다. 소지하거나, 사용하거나, 전리품으로 찾거나, Game Master가 나눠 줄 수 있으며, 판매자의stock표에서도 여전히 굴릴 수 있습니다.
"items": {
"market": { "removeSellers": ["magic_dealer", "scribe"], "unsold": [{ "tag": "magical" }] },
"lootTables": [{ "id": "treasure_low", "label": "Pocket treasure", "entries": [{ "coins": "sp", "weight": 1, "count": "4d6" }] }]
}레이어가 할 수 없는 것. 열거형 값, 필드, 기술, 풀, 휴식을 추가하거나, 판매자나 전리품 표를 추가하거나, 해결 유형을 바꾸거나, 실시간 상태나 전투 수치를 건드리거나, 모델 호출을 추가할 수 없습니다. 레이어가 _추가한_ 값은 시트를 읽는 다른 모든 쪽에 알 수 없는 값이 되므로 값은 제외만 합니다. 이 목록을 넘는 것은 규칙 집합 자체의 변경이거나 두 번째 규칙 집합입니다.
충돌. conflicts는 함께 켤 수 없는 레이어를 지정합니다. 쌍의 한쪽에만 지정하면 충분합니다. 마법사는 상대 토글을 비활성화합니다. 저장된 선택에 어떻게든 둘 다 있으면 나중에 선언된 쪽을 버리므로, 같은 두 선택은 항상 같은 규칙을 만듭니다.
제외된 값을 이미 가진 시트는 그대로 보존합니다. 캐릭터를 다시 쓰는 일은 없습니다. 편집기는 값을 더는 제공하지 않으며, 이미 가진 캐릭터는 원래 값을 표시합니다. 새 게임에서 레이어를 끄면 다시 제공됩니다. 숨겨진 카탈로그 항목도 같습니다. 선택기에서만 제외되며, 플레이어가 이미 고른 행은 시트에 남습니다. 레이어가 제외한 동전을 누군가 이미 지니고 있다면 그 동전은 가방에 남습니다.
제한. 규칙 집합당 레이어 12개이며, 레이어마다 두 문자열을 합해 지침 4000자까지입니다. layers 또는 기본 gm.worldGuidance를 선언한 패키지 규칙 집합은 Capability API 1.25가 필요하고, 레이어가 동전을 제외하는 규칙 집합은 1.64, 레이어가 자체 items를 바꾸는 규칙 집합은 1.813이 필요합니다. 가져온 규칙 집합은 읽는 Engine이 검증하므로 이 선언이 필요 없습니다.
다른 사람이 작성한 레이어(직접 쓰지 않은 규칙 집합의 Low Magic 레이어를 별도 파일로 배포하는 것)는 나중에 추가할 기능입니다. 현재 레이어는 자신이 속한 규칙 집합 안에 포함되어 배포됩니다.
번역
번역(Capability API 1.811). 다른 언어로 된 규칙 집합의 표시 문자열은 언어마다 번역 하나로 구성하며, Engine 자체 언어 팩처럼 언어 코드로 이름을 붙입니다(ru, pt-BR, zh-Hans). 공식 에이전트 카탈로그에서는 각 번역이 규칙 집합 파일 옆의 언어 팩입니다(아래 참고). 공유하는 규칙 집합 패키지 파일은 대신 번역을 내부에 담을 수 있습니다. ruleset.json 옆에 언어별로 locales/<language>.json 파일 하나를 두고, 다른 파일과 마찬가지로 매니페스트에 나열합니다. 어느 방식이든 각 번역은 ruleset 섹션이 있는 패키지 현지화 카탈로그입니다. 각 키는 규칙 집합이나 그 카탈로그의 영어 표시 문자열을 그대로 옮긴 것이며, 값은 해당 문자열을 파일의 언어로 번역한 것입니다.
{
"_meta": { "locale": "ru", "direction": "ltr" },
"package": { "name": "5e (SRD 5.1)" },
"ruleset": { "Prone": "Лежащий", "Fire Bolt": "Огненный снаряд", "Goblin": "Гоблин" }
}_meta.locale은 파일 이름이 나타내는 언어여야 하며, 파일에는 $schema, _meta, package, ruleset만 들어가고 모든 값은 텍스트여야 합니다. 빈 문자열은 아직 번역되지 않은 것이므로 영어로 표시합니다. ruleset 섹션이 없는 파일(패키지 자체의 영어 목록인 locales/en.json)은 아무것도 번역하지 않습니다. 번역은 표시에만 적용됩니다. 규칙과 시트 명령은 계속 영어를 기준으로 처리하며, Game Master에게 전달하는 모든 내용도 영어로 유지합니다. 전투 화면(이름, 상태, 행동, 주문, 로그, 턴 순서)은 규칙 집합의 번역에 해당 문자열이 있으면 플레이어의 언어로 표시합니다. 해당 언어 자체의 파일을 먼저 보고, 그다음 상위 언어의 파일(pt-BR의 경우 pt)을 보며, 없으면 영어로 표시합니다. "Goblin 2"는 "Goblin"의 번역에 해당 숫자를 붙여 표시합니다. 번역이 있는 패키지 파일에는 Capability API 1.811이 필요합니다. 가져올 때는 Engine에 설정된 언어(또는 그 상위 언어)의 번역만 보관하고 나머지는 버립니다. 규칙 집합을 다른 언어로 보려면 Engine의 언어를 바꾸고 파일을 다시 가져오세요. 그러면 보관한 번역을 교체합니다. 번역은 버전에 포함되지 않으므로 수정한 번역으로 같은 버전을 다시 가져와도 번역을 교체하며, 번역이 없는 해당 버전의 파일은 기존에 보관한 번역을 그대로 둡니다.
카탈로그 규칙 집합은 파일 안에 locales/<language>.json을 담을 수 없습니다(담은 파일은 다운로드하기 전에 거부함). 번역은 언어 팩입니다(Capability API 1.811). 언어별로 <id>-<version>.<language>.json이라는 JSON 파일 하나를 <id>-<version>.ruleset.json 옆에 게시하며, locales/<language>.json과 정확히 같은 내용을 담습니다(_meta.locale은 파일 이름이 나타내는 언어이며, ruleset 섹션을 포함함). 카탈로그 항목은 artifact 옆에 이를 나열합니다:
"locales": {
"ru": {
"url": "https://raw.githubusercontent.com/Pasta-Devs/Marinara-Agents/main/artifacts/ember-roads-1.0.0.ru.json",
"sha256": "<64 hex>",
"bytes": 812345,
"name": "Угольные дороги",
"description": "<the store card's description in Russian>"
}
}각 팩은 아티팩트와 같은 형식의 https URL이며, 버전과 언어에 따라 이름을 붙이고 sha256과 크기(최대 8 MB)를 함께 기재합니다. 규칙 집합은 최대 40개를 나열하며, en은 나열할 수 없고(영어는 규칙 집합 자체임), 매니페스트는 Capability API 1.811을 선언합니다. name과 description은 해당 언어의 스토어 카드 이름과 설명이므로, 팩을 가져오지 않아도 카드가 Engine의 언어로 표시됩니다. 설치할 때는 규칙 집합 파일을 가져온 다음 Engine에 설정된 언어(또는 그 상위 언어)의 팩만 가져와 크기, 체크섬, 언어를 검사하고 설치된 버전과 함께 보관합니다. Engine의 언어가 바뀔 때마다(그리고 그 언어로 페이지를 열 때마다), 새 언어의 팩을 나열한 설치된 각 카탈로그 규칙 집합은 해당 팩을 가져오고 이전 언어의 팩은 제거합니다. 팩을 가져올 수 없으면(오프라인이거나, 나열된 팩이 없거나, 카탈로그에 나열된 파일과 다르면) 규칙 집합은 영어로 남습니다. 다음 설치나 언어 변경 때 다시 시도합니다. 업데이트는 롤백을 위해 이전 버전을 팩 없이 보관합니다. 롤백 후 다음 언어 변경 때 카탈로그가 여전히 그 버전의 팩을 나열한다면 해당 팩을 다시 가져옵니다.
값을 새 ID로 옮기기: sheet.migrations
추가에는 더 필요한 것이 없습니다. 새 레이아웃이 값을 다른 곳으로 옮길 때(이름이 바뀐 필드, 클래스별 레벨이 된 레벨 하나, 주사위별 풀이 된 히트 다이스 풀 하나) sheet.migrations(Capability API 1.77)에 방법을 적으세요. 그러면 이전 레이아웃으로 만든 모든 캐릭터가 라이브러리, 게임, 이전 저장, 분기와 체크포인트, 가져온 카드에서 숫자를 유지합니다.
"migrations": [
{
"from": 3,
"steps": [
{ "rename": "field", "from": "xp", "to": "experience" },
{
"route": "level",
"by": "class",
"cases": [
{ "words": ["fighter"], "to": "level_fighter" },
{ "words": ["wizard"], "to": "level_wizard" }
],
"else": "level_other",
"default": 1
},
{
"routeLive": "pool",
"from": "hit_dice",
"cases": ["hit_dice_d6", "hit_dice_d8", "hit_dice_d10", "hit_dice_d12"],
"else": "hit_dice_d8"
}
]
}
]- 각 항목은 저장된 시트를 버전
from에서from + 1로 옮기며, 그 버전 이하로 저장된 모든 시트에 대해 순서대로 실행합니다. 한 번 배포한 항목은 절대 지우지 마세요. 오래전에 저장된 시트에는 여전히 필요합니다. 추가만 하는 버전에는 항목이 필요 없습니다. rename(ability,skill,save,field): 값을 새 ID로 복사합니다. 기술이나 내성은 보너스도 함께 가져갑니다. rename은 앞 항목의 rename을 이어 가거나, 나중의route가 보낼 필드에 값을 줄 수 있으며, 가져오기는 그 연결을 끝까지 따라갑니다.route: 한 필드의 값이 다른 필드의 텍스트가 지정하는 필드로 갑니다. 케이스의 단어가 대소문자 구분 없이by의 텍스트에 온전한 단어로 나타나고(글자만 단어를 이루므로 "Fighter3"은 fighter를 지정함) 정확히 한 케이스만 그럴 때 그 케이스가 이깁니다. 하나도 없거나 여럿이면("Fighter 3 / Wizard 2"처럼 묻지 않고는 숫자 하나를 나눌 수 없음)else로 갑니다.default는 그 값을 저장한 적이 없는 시트를 대신합니다.by는 시트에 있는 필드이거나 이전 레이아웃이 이름을 바꿔 옮긴 필드입니다. 대상은 기본값이 없거나 기본값이 0 또는 빈 값인 숫자나 텍스트 필드입니다. 값은 그중 하나에 들어가고 나머지는 모두 기본값을 유지하므로, 다른 기본값은 아무도 준 적 없는 레벨로 읽힐 것이기 때문입니다. 나중의 rename이 옮기는 대상은 그곳까지 따라갑니다.routeLive(실시간 상태의pool또는track): 저장된 값은 마이그레이션한 시트가 보여 주는cases중 하나(최대치가 0보다 큰 풀, 시트가 숨기지 않는 트랙)로, 없으면else로 갑니다. 값이 가는 트랙은 모두 부상 트랙(표시를 옮김)이거나 모두 일반 트랙(숫자를 옮김)이어야 합니다.setColumn(Capability API 1.794):{ "setColumn": { "list": "attacks", "where": { "name": ["Dagger", "Rapier"], "ability": ["str", "dex"] }, "column": "ability", "to": "finesse" } }는 목록에서where의 각 열이 나열된 값 중 하나를 담고 있는 모든 행의 열 하나를 설정합니다(텍스트는 대소문자와 양끝 공백을 무시하고 비교하며, 숫자는 정확히 일치해야 함). 무기의 능력치 선택이 생기기 전에 만든 공격 행처럼, 규칙 집합이 해당 규칙을 제대로 지원하기 전에 만든 행을 고치는 데 사용하세요. 목록과 지정하는 모든 열은 시트에 있어야 합니다.- 빌드 값을 옮기는 다른 모든 단계는 복사합니다. 이전 값은 그 자리에 남으므로 규칙 집합의 이전 버전도 시트를 이전처럼 읽으며, 단계는 대상에 이미 있는 값을 덮어쓰지 않습니다.
setColumn만 덮어쓰는 단계입니다. - 가져오기는
sheet.version이상인 항목, 같은 버전에서 출발하는 두 항목, 시트에 없는 ID에 쓰는 단계, 두 번 나열한 케이스, 시트에 아직 있는 ID에서 읽는rename이나routeLive를 거부합니다(옮겨진 ID는 다시 쓰이지 않으며, 그래서 이 단계들은 다시 실행해도 안전합니다). - 시트는 읽을 때마다 마이그레이션하며 새 레이아웃은 다음 편집 때 저장합니다. 실시간 상태에는 자체 버전이 없으므로
routeLive는 읽을 때마다 실행됩니다. 이전 키는 버리고, 새 키가 아직 없는 동안에만 값을 옮기므로 그 뒤 쓰거나 휴식으로 바뀐 값은 덮어쓰지 않습니다. 값이 간 곳은 업데이트 뒤 첫 쓰기로 정해지므로, 나중에 고친 시트(직접 옮긴 클래스 레벨)에서는 다시 옮기지 않습니다. 새 버전이 도착할 때 이미 진행 중인 전투는 이전처럼 끝까지 진행됩니다.