Companion Dogs: 모딩 설명서
애드온을 직접 만드는 법. 새 품종 하나, 또는 종 전체를, Companion Dogs에 물리는 별도의 워크샵 모드로 만듭니다.
이 설명서는 모드를 만드는 사람을 위한 것입니다. 플레이만 할 생각이라면 나머지 두 권을 읽으세요. 개가 무엇을 하는지는 기능, 품종은 견종입니다.
Lua를 읽을 줄 알고, 리그와 애니메이션이 붙은 동물 모델이 있다고 가정합니다. 아트 파이프라인은 여기서 다루지 않습니다. 모델 파일이 만족해야 하는 조건은 2절에 있습니다.
이 설명서에 이름이 나오는 것은 모두 공개 계약이며, 모드 바깥에서 호출하라고 있는 것입니다. 여기에 이름이 없는 것은 내부용이고, 어떤 버전에서든 예고 없이 바뀝니다.
1. 애드온이란 무엇인가
애드온은 require=CompanionDogs를 선언하고 로드 시점에 함수 하나를 호출하는 평범한 워크샵 모드입니다. 품종 하나(몸을 공유한다면 여럿)를 추가하고, 그 외에는 아무것도 하지 않습니다.
베이스가 가진 것:
- 애니메이션 상태 기계, 애님셋, 스켈레톤
- 따라오기, 길찾기, 전투, 사냥, 가축 돌보기, 보초, 욕구, 유지 관리
- 인터페이스 전체. 개 창, 방사형 메뉴, 상황 메뉴, 견사, 지도 표시
- 길들이기, 유대, 번식, 강아지, 잡종
- 샌드박스 옵션과 멀티플레이 동기화
여러분의 애드온이 가진 것:
- 여러분의 동물을 믹스와 다르게 만드는 숫자들
- 몸 모델, 텍스처, 초상화, 그리고 있다면 무들 아이콘
- 세계 어디에서 발견되는지
- 이름과 설명을, 함께 배포하는 모든 언어로
행동이 똑같은 두 색깔도 베이스에게는 서로 다른 두 품종입니다(이름 둘, 견사 항목 둘). 공유 테이블 하나에서 둘을 뽑아 쓰면 되고, CD: Cats가 다섯 가지 털색으로 그렇게 합니다.
2. 시작하기 전에 필요한 것
동물에게는 media/models_X/Skinned/ 아래에 자기 스킨드 .glb가 필요하고, 그 안에 Rac_* 애니메이션 클립 21개가 전부 들어 있어야 합니다. 클립은 모델 파일끼리 상속되지 않습니다. 클립이 열다섯 개뿐인 모델은 나머지 여섯 개 중 하나가 요구되는 순간 얼어붙는 동물이 됩니다. 모델은 또한 본 60개 아래여야 하고 최상위 노드가 항등이어야 합니다. 아니면 엔진이 프레임마다 예외를 던지고 동물은 검은 얼룩으로 그려집니다. 선택 클립이 둘 더 있습니다. Rac_WalkLimpFront와 Rac_WalkLimpBack(절뚝이는 걷기 사이클)인데, 품종 정의에서 limpAnim = true를 켤 때만 의미가 있습니다.
아직 모델이 없다면, CD.applyDogModel을 베이스의 몸 중 하나로 겨누어 놓고 나머지 전부를 쓰고 시험할 수 있습니다. 동물은 믹스처럼 보이지만, 이 설명서의 모든 시스템이 돌아갑니다.
몸에 쓸 텍스처와 견사 창에 쓸 초상화도 필요합니다.
코드를 쓰기 전에 여러분의 동물이 품종인지 종인지부터 정하세요. 품종은 숫자가 다른 개입니다. 종은 어떤 일거리 자체가 없는 동물입니다. 이 선택이 무슨 필드를 쓰는지를 바꿉니다. 6절이 다룹니다.
3. 애드온의 파일 구성
공개된 애드온 중 가장 작은 CD: Pug입니다. 중요한 부분만 추렸습니다.
CompanionDogsPug/
42/
mod.info
icon.png
poster.png
media/
models_X/Skinned/Pug_Body.glb
scripts/models_pug.txt
textures/Body/Pug.png
textures/CDPortrait_pug.png
lua/shared/CompanionDogsPug_Breed.lua
lua/shared/Definitions/animal/PugDefinitions.lua
lua/shared/Definitions/animal/CompanionDogsPug_Parts.lua
lua/client/CompanionDogsPug_Moodle.lua
lua/shared/Translate/EN/IG_UI.json
lua/shared/Translate/PTBR/IG_UI.json
common/
그리고 mod.info입니다.
id=CompanionDogsPug
name=CD: Pug
description=Pugs for Companion Dogs. REQUIRES the Companion Dogs mod (0.6.8 or newer).
poster=poster.png
icon=icon.png
require=CompanionDogs
modversion=0.1.0
pzversion=42.19.0
versionMin=42.18.0
중요한 줄은 require=CompanionDogs입니다. 이 줄이 베이스 Lua 전부가 여러분 것보다 먼저 돌았음을 보장합니다. shared, client, server 모든 단계에서 그렇습니다. 이 줄이 없으면 여러분 파일이 먼저 로드될 수 있고, 그러면 모든 CD. 호출이 nil 인덱싱입니다.
versionMin으로는 "Companion Dogs 0.6.8 이상이 필요하다"를 표현할 수 없습니다. 그 필드는 게임 빌드만 막고, 의존 모드의 버전을 적을 mod.info 필드는 없습니다. 베이스 하한선은 다음 절의 가드가 강제하고, 플레이어에게는 여러분의 설명으로 알립니다. 거기에 말로 적으세요. 아니면 플레이어가 겪는 증상은 끝내 나타나지 않는 동물 하나뿐입니다.
4. 버전 가드
애드온의 모든 Lua 파일 맨 위에, 애드온이 필요로 하는 숫자와 함께 이것을 넣으세요.
local CD = CompanionDogs
if not (CD and CD.registerBreed and (CD.API_VERSION or 0) >= 3) then return end
베이스가 없거나 너무 낡았으면 파일은 둘째 줄에서 반환되고 애드온은 아무것도 하지 않습니다. 품종도, 스폰도, 무들도, 로그 한 줄도 없습니다.
가드는 파일마다 걸고, 파일마다 자기가 쓰는 것으로 겁니다. PugDefinitions.lua는 모델 헬퍼가 필요하므로 그것으로 겁니다.
if not (CompanionDogs and CompanionDogs.applyDogModel and CompanionDogs.DOG_SOUNDS) then return end
전역을 만들지 마세요. 애드온 안의 CompanionDogs = CompanionDogs or {}는 없는 베이스를 반쯤 만들어진 테이블로 바꿔 놓고, 그 뒤의 모든 가드가 통과하면 안 될 때 통과합니다.
5. 품종 등록
shared 파일에서 호출 하나면 됩니다.
CD.registerBreed({
key = "pug",
engineBreed = "pug",
typePrefix = "pug",
nameKey = "IGUI_PD_Breed_pug",
litter = { 1, 2 },
xpMult = { scent = 1.5, combat = 1, obedience = 2.0, hunt = 1.2, herding = 1.0 },
combatPower = 0.15,
lethalityCurve = { min = 0.40, max = 1.3 },
canKill = false,
canKnockdown = false,
combatStressMult = 0.9,
panicThreshold = 0.70,
bagMult = 0.6,
sentinelMult = 1.35,
barkNoiseMult = 1.5,
alertModeLocked = true,
loyaltyDecayMult = 0,
geneRange = {
strength = { 0.00, 0.10 },
aggressiveness = { 0.05, 0.20 },
resistance = { 0.00, 0.15 },
stress = { 0.55, 0.85 },
},
spawns = {
{ id = "pughouse", class = "house", suffix = "|pg", breed = "pug",
chance = function() return CD.strayChancePerHouse() / CD.PUG_HOUSE_RARITY end },
},
})
이 호출은 품종을 집어넣고, 동물 타입 세 개를 등록하고, 여러분의 동물이 잡종 교배에 끼도록 번식 순서를 다시 만들고, 스폰 단계를 등록합니다.
정의가 잘못되어 있으면 호출은 예외를 던지지 않고 nil을 반환하므로 로드는 계속됩니다. 거부될 때마다 로그에 이름이 붙은 줄이 찍힙니다.
필수 필드
| 필드 | 무엇인가 |
|---|---|
key |
품종 식별자. 모든 모드를 통틀어 유일해야 합니다. 출시한 뒤에는 바꾸지 마세요. |
typePrefix |
동물 타입 세 개의 접두사. <prefix>pup, <prefix>female, <prefix>male. 몸 메시를 고릅니다. |
nameKey |
표시 이름의 번역 키. |
xpMult |
스킬별 학습 속도. scent, combat, obedience, hunt, herding. 1.0이 보통입니다. |
combatPower |
때리는 세기. 믹스가 0.20이고, 싸움용 품종은 1.0을 넘습니다. |
lethalityCurve |
{ min, max }. 전투 0에서 전투 10까지 피해가 자라는 모양입니다. |
geneRange |
유전자 네 개의 출생 범위. strength, aggressiveness, resistance, stress. 각각 0에서 1 사이의 { low, high }입니다. |
선택 필드
| 필드 | 기본값 | 하는 일 |
|---|---|---|
engineBreed |
key와 같음 |
엔진에 넘기는 품종 이름. 텍스처를 고릅니다. 품종마다 유일해야 합니다. |
descKey |
IGUI_PD_BreedDesc_<key> |
설명 키. 여러 품종이 키 하나를 같이 쓸 수 있습니다. |
litter |
베이스 값 | 한 배에 나오는 강아지 수 { min, max }. |
canKill |
true | false면 좀비를 깎기만 하고 마지막 일격은 넣지 않습니다. |
canKnockdown |
false | true면 좀비를 넘어뜨릴 수 있습니다. |
combatStressMult |
1 | 싸움 한 번이 스트레스를 얼마나 먹는지. |
panicThreshold |
베이스 값 | 싸움을 그만두는 스트레스 수위. panicImmune = true면 절대 그만두지 않습니다. |
bagMult |
1 | 안장가방 용량 배수. |
puppySize |
0.6 | 강아지의 절대 시각 크기. 작은 품종은 반드시 넣어야 하고, 없으면 강아지가 성체 크기로 태어납니다. |
sentinelMult |
1 | 최종 보초 반경에 곱합니다. |
barkNoiseMult |
1 | 경보 짖음의 반경과 음량을 조절합니다. 샌드박스 소음 옵션이 꺼져 있으면 값이 무엇이든 어떤 품종도 짖어서 좀비를 끌지 않습니다. |
loyaltyDecayMult |
1 | 하루치 충성도 감소에 곱합니다. 0이면 유대가 절대 식지 않습니다. |
alertModeLocked |
false | 동물이 항상 완전 경계 상태입니다. 주인이 어디서도 조용히나 침묵으로 바꿀 수 없습니다. |
huntFetchLevel |
6 | 사냥감을 주인에게 물어다 주기 시작하는 사냥 레벨. |
huntDeliverTimeoutMin |
5 | 배달을 포기하기까지 계속 시도하는 게임 내 분. |
distract |
꺼짐 | { <kind> = { chance = 0..1, ... } }. 품종이 모드도 레벨도 필요 없이 스스로 무언가를 쫓습니다. |
idleAnimMs |
베이스 값 | 대기 애니메이션 구간의 길이, 밀리초. 클립이 개의 것보다 짧을 때 넣으세요. 안 넣으면 루프가 다시 시작하면서 동작을 중간에 자릅니다. |
restAnim |
true | false면 대기와 경비에서 엎드리지 않고 서 있습니다. |
restPoses |
엎드리기 | 동물이 자리를 잡을 때마다 뽑는 휴식 자세 목록. 들어가고 나오는 전환과 대기 변주를 선택적으로 붙일 수 있습니다. 클립과 애니메이션 노드를 직접 만들어야 합니다. |
limpAnim |
false | true면 다친 동물이 걸을 때 절뚝입니다. 모델에 선택 클립 Rac_WalkLimpFront와 Rac_WalkLimpBack이 있을 때만 켜세요(Rac_Walk와 같은 루트 모션을 가진 1.0초 걷기 사이클). 클립이 없으면 동물이 제자리에 굳은 채 걷습니다. |
bandSkin |
꺼짐 | { base, front, back, cutFront, cutBack }, 몸 텍스처 이름 다섯 개. 다친 발에는 상처에서 피가 나는 동안 베인 변형이, 붕대를 감은 동안 붕대 변형이 입혀집니다. 네 가지 변형은 _dogrig/forge/_paw_band.py로 만드세요. 없어도 동물은 똑같이 다치고, 자국만 보이지 않습니다. |
voices |
개 소리 | { bark, growl, idle, wildbark, pet, whine, eat, drink }. |
diet |
개의 목록 | 동물이 먹으면 안 되는 것, 동물에게 고기로 치는 것, 여물통에서 스스로 먹는 것. |
maleChance |
0.5 | 수컷으로 태어나는 비율, 0에서 1까지. |
sterileMale |
false | true면 그 품종의 수컷은 번식에서 빠집니다. |
species |
"dog" |
번식은 종 안에서만 이루어집니다. 6절을 보세요. |
skills, canBreed, huntMaxPrey |
전부 켜짐 | 구조를 정하는 블록들. 6절을 보세요. |
정의에 넣은 숫자와 플래그는 CD.getBreedDef(animal).field가 아니라 CD.breedNumber(animal, field)와 CD.breedFlag(animal, field)로 다시 읽으세요. 이 둘은 없는 필드를 알아서 처리하고, 베이스가 바뀌어도 계속 돌아갑니다.
본능: distract
distract = {
prey = { chance = 0.25 },
},
이것을 선언한 품종은 이따금 무언가를 발견하고 하던 일을 버린 채 쫓아갑니다. 따라오기에서도 하고 대기에서도 합니다. 그동안 명령은 순종 판정이 이미 쓰고 있는 distracted 메시지와 함께 거부됩니다. 유일하게 통하는 명령은 이리 오기이고, 그것이 산만함을 끝냅니다.
모든 종류가 같은 선택 손잡이를 받습니다. 빼먹은 것은 모드의 기본값을 씁니다.
| 필드 | 하는 일 |
|---|---|
chance |
0에서 1 사이. 그 종류의 방아쇠가 무언가를 찾았을 때만 굴립니다. "25%"를 뜻하려고 25라고 쓰면 로그에 이름이 붙은 줄과 함께 거부됩니다 |
radius |
방아쇠가 훑는 타일 수. 그 종류가 훑기를 할 때만 |
durationMin |
창이 저절로 닫히기까지의 게임 내 분 |
cooldownMin |
같은 동물이 다시 산만해지기까지의 게임 내 분 |
prey 종류
베이스 모드가 싣고 나오는 유일한 종류입니다. 동물이 야생 동물을 쫓고, 어떤 것이 대상인지는 classes가 고릅니다.
distract = {
prey = { chance = 0.25, radius = 6, classes = { tiny = true, small = true } },
},
무엇을 선언하든 한계 두 가지가 걸립니다.
- 여러분의
huntMaxPrey를 절대 넘지 않습니다.small에서 잘린 품종은large를 선언해도 사슴을 쫓지 않습니다. - 레벨 관문은
tiny에서만 생략됩니다. 갓 길들인 사냥 0짜리 동물도 설치류는 이미 잡습니다.small을 선언하면 품종이 레벨과 무관하게 토끼를 쫓지만, 잡는 데는 래브라도가 필요로 하는 사냥 레벨이 똑같이 필요합니다.
샌드박스 사냥 스위치는 잡는 것에만 걸립니다. 동물은 여전히 쫓지만 사냥감이 달아납니다.
이렇게 잡은 것도 평범한 회수 절차를 타므로, 몸을 주인에게 물어다 줄지는 품종의 huntFetchLevel이 정합니다. 0이면 첫날부터 배달하고, 고양이가 죽은 쥐를 물어다 주는 것이 그것입니다. 배달은 산만함의 창보다 오래 갑니다. 사냥감이 죽는 순간 동물은 다시 명령을 받고, 돌아오는 길은 자기 huntDeliverTimeoutMin으로 돕니다.
직접 종류 만들기
distract는 디스패처입니다. 여러분 것을 등록하세요.
CD.registerDistraction("butterfly", {
gate = function(animal, cfg) return animal:isOutside() end, -- optional, cheap pre-check
find = function(animal, cfg) return findButterflyNear(animal, cfg.radius) end,
drive = function(animal, owner, d, cfg) return chaseIt(animal, d) end,
stop = function(animal, d) end, -- optional, cleanup
})
그런 다음 다른 종류와 똑같이 품종에 선언합니다. distract = { butterfly = { chance = 0.10 } }.
gate는 모드의 전역 훑기 예산보다 먼저 돕니다. find는 예산 뒤에 돌면서 비싼 탐색을 합니다. drive는 아직 동물을 몰고 있으면 true를, 끝났으면 false를 반환하고, false가 창을 닫습니다. 여러분의 핸들러는 자기 pcall 안에서 돕니다. 예외를 던지면 모드가 이름이 붙은 줄을 로그에 남기고 그 세션 동안 그 종류를 등록 해제하며, 동반자의 나머지는 그대로 돌아갑니다.
앞으로 베이스가 쓸 예약된 이름은 drink, eat, play입니다. 오늘 그 이름으로 종류를 등록할 수는 있지만, 베이스가 자기 것을 내면 이름을 가져갑니다.
휴식 자세
쉬는 동물은 엎드립니다. restPoses는 그 하나뿐인 자세를, 동물이 자리를 잡을 때마다 뽑는 목록으로 바꿉니다.
restPoses = {
"cdRest", -- the base lie-down
{ var = "cdSit", enter = "cdSitIn", exit = "cdSitOut",
variation = { "cdSitGroom", "cdSitGroom2" } },
},
각 항목은 애니메이션 변수의 이름이거나 테이블입니다. var는 쉬는 내내 켜져 있는 불리언입니다. enter와 exit는 자리를 잡을 때와 일어날 때 한 번만 재생되는 펄스입니다. variation은 자세를 유지하는 동안 이따금 재생되는 한 번짜리 펄스 목록이고, 대기 변주와 같은 시계로 돕니다. 필수인 것은 var뿐입니다.
추첨은 균등하고 휴식에 들어갈 때마다 일어납니다. 항목이 둘이면 각각 50%이고, 같은 것이 두 번 연달아 나올 수도 있습니다. 번갈아 나오는 것이 아닙니다.
각 펄스는 idleAnimMs가 그 변수에 정한 만큼 이어집니다. 그러니 테이블 형태로 넘기고, 이미 쓰고 있던 대기 변수까지 포함해 모든 변수에 길이를 주세요. 클립의 실제 길이를 넣으세요.
idleAnimMs = { cdIdle2 = 3800, cdIdle3 = 3800, cdSitIn = 1375, cdSitOut = 1417,
cdSitGroom = 19833, cdSitGroom2 = 25167 },
클립과 노드는 베이스가 아니라 여러분의 것입니다. 변수마다 애드온의 media/AnimSets/raccoon/idle/ 아래에 노드 파일이 있어야 하고, 그 m_AnimName이 가리키는 클립이 여러분의 모델에 있어야 합니다. 모델에 없는 클립을 가리키는 노드는 조용히 실패하고, 동물은 베이스의 대기 자세로 그냥 서 있습니다. 자세 노드에는 베이스 것들처럼 m_ConditionPriority를 10으로 주고, 그 위에서 재생되어야 하는 것에는 더 큰 수를 주세요. 변주는 11, 두 전환은 12입니다. 노드는 따로 말하지 않으면 클립을 반복 재생하므로, 한 번만 재생되어야 하는 노드, 즉 두 전환과 모든 변주에는 <m_Looped>false</m_Looped>가 필요합니다. 없으면 클립이 끝나자마자 다시 시작되고, 변수는 다음 서버 틱에야 꺼지기 때문에 동물이 눈에 띄게 되감겨서 같은 동작을 반복합니다. 전부 idle/에만 두세요. pathfind/에 있는 자세 노드는 루트 모션 없는 클립을 재생하고 동물을 그 자리에 얼려 버립니다.
목소리
voices가 없으면 모든 품종이 개 소리를 쓰므로 고양이가 짖습니다. 먼저 여러분의 소리를 등록하세요.
CD.registerVoices({
CDCatMeow = "bark", CDCatGrowl = "bark", CDCatHiss = "bark", CDCatPurr = "bark",
CDCatPet = "fx", CDCatMeowAmbient = "ambient",
})
그다음 품종이 그것을 가리키게 합니다.
voices = { bark = "CDCatMeow", growl = "CDCatGrowl", idle = "CDCatPurr",
wildbark = "CDCatMeowAmbient", pet = "CDCatPet", whine = "CDCatHiss" },
CD.registerVoices 없이도 소리는 나지만, 플레이어의 분류별 슬라이더와 게임의 효과음 음량을 무시하고, 로그에는 그것을 가리키는 것이 아무것도 없습니다. voices에서 빼먹은 키는 개 소리로 떨어집니다.
whine은 동물이 다치거나 병에 걸렸을 때 내는 소리입니다. eat와 drink는 먹고 마시는 효과음입니다. 개의 것은 루프라서 동물이 다 먹으면 베이스가 멈춥니다. 여러분의 것도 루프라면 CD.SOUND_LOOPED에 넣으세요. 그러면 듣는 사람이 범위 밖으로 나갈 때도 베이스가 멈춥니다. API 11보다 오래된 베이스는 이 세 키를 무시하고 개 소리를 냅니다.
세 번째 인자는 각 소리가 들리는 거리로, 타일 단위입니다. 여러분의 사운드 스크립트에 쓴 distanceMax와 같아야 합니다.
CD.registerVoices(map, nil, { CDCatMeow = 22, CDCatGrowl = 10 })
서버에서 베이스는 그 거리 안의 플레이어에게만 소리를 보냅니다. 거리를 빼면 목소리는 분류별 일반 거리를 받습니다. 열 타일 나가는 하악질이 서른 타일 안 모두에게 나가는 식입니다. distanceMax를 넘어가면 FMOD의 역제곱 감쇠가 무음으로 가지 않고 감쇠를 멈추므로, 그냥 놓아둔 .ogg는 아무리 멀어도 제 음량의 distanceMin / distanceMax로 계속 납니다. 같은 이유로 큰 목소리에는 distanceMin을 작게 두세요.
식단
diet가 없으면 모든 품종이 개처럼 먹습니다. 개를 병들게 하는 음식이 같고, 고기로 치는 음식이 같고, 여물통에서 뜯어 먹는 음식이 같습니다. 이 필드는 그 목록 셋을 여러분의 품종에만 덮어씁니다.
diet = {
bad = { Fruits = true }, -- fruit now makes it sick
badParts = { onion = false }, -- onion no longer does
protein = { Cheese = true }, -- cheese now counts as meat
proteinParts = { tuna = true }, -- and so does anything with "tuna" in its type
trough = { Vegetables = false }, -- it will not graze vegetables
troughItems = { ["Base.CatFoodOpen"] = true },
},
형태가 둘인 이유는 게임 자체의 분류가 거기까지만 닿기 때문입니다. Cheese는 진짜 FoodType이라 키 하나로 게임 안의 모든 치즈와 그 분류를 재사용하는 음식 모드의 치즈까지 덮습니다. 참치는 아닙니다. 딴 생선 전부와 함께 딴 통조림이 FoodType = Fish이고, 안 딴 통조림은 음식 타입을 아예 선언하지 않습니다. badParts와 proteinParts가 그런 것에 닿는 방법입니다.
모든 필드는 <key> = true(추가) 또는 <key> = false(베이스에 있던 것 제거) 테이블입니다. true도 false도 아닌 값은 로그에 이름이 붙은 줄을 남기고 버려집니다.
| 필드 | 키가 무엇인가 |
|---|---|
bad |
동물을 병들게 하고, 충성도를 깎고, 먹이 메뉴에서 빨간 항목이 되는 음식의 FoodType |
badParts |
아이템 타입의 조각. 소문자로, 어디에 있든 걸립니다 |
protein |
동물의 고기 빚을 갚는 FoodType. 쇠약 무들이 지켜보는 그것입니다 |
proteinParts |
아이템 타입의 조각. badParts와 같은 방식으로 걸립니다 |
nonProtein |
고기가 아님을 여러분이 아는 FoodType. 처음 보는 음식 타입에 베이스가 남기는 로그 줄을 잠재우기만 합니다 |
trough |
동물이 여물통에서 스스로 먹는 FoodType과 AnimalFeedType |
troughItems |
여물통에 든 아이템 중 자기 사료 타입이 없는 것을 위한 전체 아이템 타입 |
replace = true는 개의 목록이 아니라 빈 목록에서 시작합니다. 여러분의 동물이 개와 공유하는 것이 거의 없을 때 쓰세요. 고양이는 보통 몇 개의 추가와 제거로 쓰는 편이 더 쉽습니다.
목록 셋은 동물을 따라다니므로, 같은 여물통 앞에 선 개와 고양이는 거기서 서로 다른 것을 먹습니다. 모드 자체의 밥그릇은 예외입니다. 밥그릇은 채운 아이템이 아니라 음식 점수를 저장하므로, 채우는 판정은 누구에게나 베이스 목록으로 갑니다. 어차피 밥그릇에는 해로운 것을 넣을 수 없습니다.
태어날 때의 성별
동물은 나타날 때 수컷이나 암컷으로 뽑히고, 기본은 반반입니다. maleChance는 수컷으로 태어나는 비율이고 0에서 1까지이며, sterileMale은 그 품종의 수컷을 번식에서 뺍니다.
maleChance = 0.00033,
sterileMale = true,
이 한 쌍이 바로 삼색 고양이입니다. 주황과 검정의 모자이크는 X 염색체에 실려 있어서 삼색은 거의 항상 암컷이고, 가끔 나오는 수컷은 XXY로 3000마리에 한 마리쯤이며 새끼를 보지 못합니다. sterileMale 품종의 수컷은 짝으로 뽑히지 않고, 상호작용 메뉴에서 번식 항목이 사라집니다. 암컷은 평소대로 번식합니다.
그런 품종의 수컷은 게임이 성별을 보여주는 모든 곳에 "불임" 표시가 붙습니다. 상태창, 상호작용 메뉴, 떠돌이 조사, 견사 어디서나 붙으니 플레이어가 왜 번식하지 않는지 알 수 있습니다.
두 항목 모두 품종 정의에서 읽으므로 동물에는 아무것도 기록되지 않고, 기존 세이브에도 마이그레이션이 필요 없습니다. 같은 추첨이 새끼가 태어날 때의 성별도 정하므로, 월드에서 암컷인 품종은 새끼에서도 암컷으로 남습니다.
6. 시스템 하나를 통째로 끄기
스킬 다섯 개가 곧 시스템 다섯 개입니다.
| 스킬 | 어떤 시스템인가 |
|---|---|
scent |
보초. 좀비를 알아채고 여러분에게 알립니다 |
combat |
싸움, 경비 모드, 자기 방어 |
obedience |
재주 계통 |
hunt |
사냥, 그리고 주인에게 주는 채집 보너스 |
herding |
울타리 안 가축 돌보기 |
그래서 종은 자기에게 없는 것을 선언합니다.
skills = { combat = false, herding = false },
canBreed = false,
huntMaxPrey = "tiny",
없거나 true면 동물이 그 스킬을 가집니다.
스킬을 끄면 그 경험치도 쌓이지 않고, 인터페이스에서 그 막대와 명령도 사라집니다.
canBreed = false가 테이블 밖에 따로 있는 것은 번식에 막대도 경험치도 없기 때문입니다. 그 동물은 새끼를 배지 않고, 짝으로 뽑히지도 않으며, 제 동족과도 번식하지 않습니다.
종
개가 아니라 다른 동물을 넣는 애드온은 이렇게 선언합니다.
CD.registerSpecies({ key = "cat",
nounKey = "IGUI_PD_SpeciesNoun_cat",
youngKey = "IGUI_PD_Young_cat" })
CD.registerBreed({ ..., species = "cat" })
지금까지 나온 품종에는 이 필드가 하나도 없고, 없으면 "dog"입니다.
번식은 종 안에서만 이루어집니다. 명령에서도, 시간마다 도는 수동 판정에서도, 디버그 도구에서도, 양쪽이 같은 종일 때만 새끼를 뱁니다. 그리고 인터페이스가 여러분의 동물을 개라고 부르기를 그만둡니다. nounKey는 "이 %1는 이미 다른 생존자의 소유입니다." 같은 문장을 채우는 맨 명사이고, youngKey는 여러분 종의 새끼가 창과 상황 메뉴와 견사에서 다는 이름표로, 개라면 강아지라고 나오는 자리입니다.
이 명사 둘은 %1을 두고 지은 문장만 덮습니다. 개라는 단어가 박혀 있는 다른 모든 문장("개 살펴보기", "개가 배고파합니다", "개가 일부러 짖어")은 API 7부터 종별 키를 받습니다. 번역 파일에 IGUI_PD_ScanStray_cat을 실으면, 화면에 있는 동물이 고양이일 때 베이스가 IGUI_PD_ScanStray 대신 그것을 보여줍니다. 싣지 않은 키는 개의 문구로 떨어집니다. 접미사를 받는 키는 이렇습니다. ScanStray, ScanTitle, PauseGrowthTip, BadFood, AlertFullDesc, AlertQuietDesc, AlertLockedTip, HuntModeDesc, 모든 Trick*Desc, TrickDistractDone, TrickNeedsBag, TrickGotoPick, TrickSniffNoItem, TrickFetchNoItem, AlertGroup, DogLost, 모든 Refused_*, KennelStatusNoData, 그리고 돌봄 무들(질병, 쇠약, 배고픔, 갈증, 휴식, 상실)의 Moodle_*_desc. 화면에 동물이 없는 문장(등록부 탭, "먼저 동물과 친해지세요.")은 베이스에서 이미 종 중립입니다. 고양이는 그 전부를 열네 개 언어로 싣고 나오니, 그 IG_UI.json을 본으로 삼아 베끼세요.
동물의 group은 디버그 스폰 메뉴와 클립 뷰어만 IGUI_Animal_Group_<group>을 통해 읽습니다. 행동 헬퍼 다음에 t.group = "cat"을 넣고 그 키를 실으세요. 안 그러면 거기서 여러분의 종이 개 이름으로 나옵니다.
species를 등록하지 않고 선언하는 것은 치명적이지 않습니다. 관문이 문자열을 그대로 비교하므로 동물은 여전히 제 종 밖과는 번식하지 않습니다. 명사 둘만 개의 것으로 떨어지고, 로그가 그 사실을 이름을 붙여 말합니다.
API 6보다 낡은 베이스에서는 이 필드가 로그 한 줄도 없이 무시되고, 여러분의 동물은 개로 번식합니다. 개와 절대 섞이면 안 된다면 API 6으로 가드를 걸거나, 베이스가 낡은 동안은 canBreed = false로 두세요. 고양이가 그렇게 합니다.
huntMaxPrey는 여전히 존재하는 사냥 안에서 사냥감 크기에 상한을 씌우는 것이지, hunt = false를 대신하지 않습니다. "tiny"(생쥐, 쥐, 다람쥐), "small"(토끼, 너구리), "large"(사슴. 기본값이고 상한 없음)를 받습니다. 상한 위의 사냥감은 여러분의 동물이 쫓지도 가리키지도 않습니다. 그 사다리 밖의 값은 "large"로 떨어지고 로그에 이름이 붙은 줄을 찍습니다.
막힌 선택지는 방사형 메뉴와 상황 메뉴와 창에서 사라지고, 서버는 메시지 없이 거부합니다. 이 필드들은 번역 키를 하나도 만들지 않으니, 한계는 품종 설명에 적어 두세요.
7. 동물 정의
엔진은 메시를 품종이 아니라 동물 타입에 묶으므로, 여러분의 품종에는 자기 타입 세 개가 필요합니다. 가장 빠른 길은 기존 애드온의 파일을 복사해 이름을 바꾸는 것입니다.
세 조각이고, Definitions/animal/ 아래 shared 파일 하나에 들어갑니다.
AnimalDefinitions.stages["pug"] = {}
AnimalDefinitions.stages["pug"].stages = {}
AnimalDefinitions.stages["pug"].stages["pugpup"] = {}
AnimalDefinitions.stages["pug"].stages["pugpup"].ageToGrow = 3 * 30
AnimalDefinitions.stages["pug"].stages["pugpup"].nextStage = "pugfemale"
AnimalDefinitions.stages["pug"].stages["pugpup"].nextStageMale = "pugmale"
AnimalDefinitions.breeds["pug"].breeds["pug"].texture = "Pug"
AnimalDefinitions.breeds["pug"].breeds["pug"].textureMale = "Pug"
AnimalDefinitions.breeds["pug"].breeds["pug"].rottenTexture = "Raccoon_Rotting"
AnimalDefinitions.breeds["pug"].breeds["pug"].invIconMale = "CDDogPaw_64"
AnimalDefinitions.breeds["pug"].breeds["pug"].invIconMaleDead = "CDDogPawDead_64"
local pugfemale = {}
CD.applyDogModel(pugfemale, "Pug_Body")
CD.applyDogBehaviour(pugfemale)
pugfemale.female = true
pugfemale.babyType = "pugpup"
pugfemale.minSize = 1.5
pugfemale.maxSize = 2.0
pugfemale.minWeight = 5
pugfemale.maxWeight = 8
pugfemale.wildFleeTimeUntilDeadTimer = CD.STRAY_NO_BLEEDOUT
pugfemale.breeds = AnimalDefinitions.breeds["pug"].breeds
pugfemale.stages = AnimalDefinitions.stages["pug"].stages
pugfemale.genes = AnimalDefinitions.genome["dog"].genes
AnimalDefinitions.animals["pugfemale"] = pugfemale
베이스가 주는 헬퍼입니다.
| 헬퍼 | 무엇을 채워 주는가 |
|---|---|
CD.applyCompanionModel(t, "Body_Model") |
몸 모델, 스켈레톤, 해체 텍스처, 애님셋 |
CD.applyCompanionBehaviour(t) |
동반자에게 필요한 엔진 행동 플래그 전부 |
CD.applyCompanionAvatar(t) |
동물 창의 초상화 카메라 |
CD.COMPANION_SOUNDS |
공유 발소리와 폴리 테이블 |
CD.defineGenome("species") |
표준 유전자 목록을 갖춘 새 종의 게놈(API 7) |
CD.STRAY_NO_BLEEDOUT |
겁먹은 떠돌이가 출혈로 죽지 않게 하는 값 |
CD.defineCompanionParts(typePrefix, engineBreed, meat) |
세 단계 전부의 해체 부위. meat는 선택이고 { item, minNb, maxNb, pupMinNb, pupMaxNb } 형태이며, 없으면 동물은 베이스의 개고기를 냅니다. 엔진이 부위마다 개수와 허기를 사체 크기로 곱하므로, 크기가 큰 종(고양이는 시각 크기로 2.5에서 3.5를 씁니다)은 기본 허기가 작고 개수가 적은 자기 아이템이 필요합니다 |
종 중립 이름은 API 7에 왔습니다. 옛 이름인 CD.applyDogModel, CD.applyDogBehaviour, CD.applyDogAvatar, CD.DOG_SOUNDS, CD.defineDogParts, CD.DogMoodles는 같은 함수와 테이블의 첫 이름입니다. 0.7.3보다 낡은 베이스에서도 로드되고 싶은 애드온은 옛 이름으로 가드를 걸고 새 이름이 있으면 그것을 고릅니다. local applyModel = CD.applyCompanionModel or CD.applyDogModel.
애님셋은 "raccoon"으로 두어야 합니다. 갈라낸 애님셋은 자기 상태 기계를 로드하지 않고, 동물은 제자리에 굳어 섭니다.
게놈은 재사용하세요. 개 품종은 베이스의 목록을 가리킵니다. AnimalDefinitions.genome["dog"].genes. 새 종은 CD.defineGenome("cat")을 한 번 부르고 그것이 돌려준 테이블을 모든 단계에 줍니다. 엔진은 각 단계의 genes 목록만 읽고 게놈 키는 관례일 뿐이므로, 여러분의 종은 자기 유전자 목록 없이도 자기 항목을 갖습니다.
mate는 선언하지 마세요. 그것이 있으면 엔진이 동물 구역 안에서 자기 짝짓기를 돌리는데, 그것은 모드의 번식 블록과 샌드박스 옵션을 무시하고 유대 없는 강아지를 만듭니다. 베이스가 부팅 때 이 필드를 지우기는 하지만, 거기에 기대지 마세요.
부위는 필수입니다. 자기 파일에 세 줄이면 됩니다.
if CompanionDogs and CompanionDogs.defineDogParts then
CompanionDogs.defineDogParts("pug", "pug")
end
이것이 없으면 여러분의 동물을 해체할 때 바닐라 코드가 죽습니다. 부위 정의를 타입과 품종으로 인덱싱하다 nil을 만나기 때문입니다.
CD.applyDogAvatar의 초상화 줌은 개에 맞춰 잡혀 있고, 아바타 줌은 배율입니다. 동물이 작을수록 숫자가 커집니다. 개 대비 크기 비율로 줌을 나누고 오프셋에는 곱하세요. 안 그러면 여러분의 동물이 제 초상화 바닥에 조그맣게 앉아 있습니다.
8. 모델 스크립트
media/scripts/ 아래 파일 하나가 여러분의 모델이 있다고 게임에 알립니다.
module Base
{
model Pug_Body
{
mesh = Skinned/Pug_Body,
shader = animalEffect,
static = false,
animationsMesh = PugAnim,
attachment head
{
offset = -0.0061 0.1671 0.0507,
rotate = -179.0 -5.0 95.0,
bone = Dummy01,
}
}
animationsMesh PugAnim
{
meshFile = Skinned/Pug_Body,
keepMeshAnimations = true,
}
}
keepMeshAnimations = true는 모델 파일 안의 클립을 계속 쓸 수 있게 합니다. 이것이 없으면 모델은 로드되고 영영 움직이지 않습니다.
head 어태치먼트는 모자 같은 것이 앉는 자리입니다. 안장가방을 여러분의 동물에 맞추고 싶으면 saddlebags_l, saddlebags_r, saddlebags_c를 같은 방식으로 척추 본에 붙이세요. 그 숫자는 게임 안에서 동물을 보며 조금씩 밀어 찾으세요. 모델에서 재어 나온 값은 가방을 메시의 기하학적 중심에 놓는데, 몸에서 보기 좋은 자리는 거기가 아닙니다.
9. 동물이 나타나는 곳
떠돌이는 건물마다, 청크마다, 그 청크가 처음 로드될 때 굴립니다. 여러분은 그 굴림을 단계 목록으로 적습니다. 품종 정의 안에 spawns로 넣거나, CD.registerStraySpawns(list)로 넣습니다.
spawns = {
{ id = "pughouse", class = "house", suffix = "|pg", breed = "pug", indoor = 85,
chance = function() return CD.strayChancePerHouse() / CD.PUG_HOUSE_RARITY end },
{ id = "pugpetvet", class = "petvet", suffix = "|pv", breed = "pug",
chance = function() return CD.PUG_PETVET_CHANCE * CD.dogSpawnMultiplier() end },
},
| 키 | 무엇인가 |
|---|---|
id |
단계의 이름. 여러분 자신의 로그를 위한 것입니다 |
class |
굴림이 걸리는 건물 분류 |
chance |
백분율을 돌려주는 함수. 굴리는 시점에 샌드박스 배수를 읽을 수 있습니다. |
suffix |
영구 저장소의 키. 모든 모드를 통틀어 유일해야 하고, 절대 바뀌면 안 됩니다. |
breed |
스폰할 품종 키 |
gate |
불리언을 돌려주는 선택 함수. 굴리기 전에 검사합니다. 허스키가 CD.isWinter를 씁니다. |
indoor |
거리가 아니라 건물 안에서 태어날 백분율 확률. 기본값 35. |
여러분의 확률에 CD.dogSpawnMultiplier()를 곱하거나, CD.strayChancePerHouse()를 여러분이 정한 희귀도로 나누세요. 둘 다 플레이어의 샌드박스 설정을 살려 둡니다.
indoor는 선호일 뿐입니다. 그 청크에 빈 실내 타일이 없는 건물은 바깥 타일로 떨어집니다.
건물 분류
베이스가 네 개를 등록합니다.
| 분류 | 걸리는 것 |
|---|---|
house |
상점이 아닌 주거 건물 |
police |
경찰서 같은 곳 |
petvet |
애완동물 가게와 동물병원 |
farm |
농장 건물. 도시 밖에서도 허용됩니다 |
어느 것도 맞지 않으면 여러분 것을 등록하세요.
CD.registerBuildingClass("mineshaft", function(def) return def:isX() end,
{ skipUrbanGate = true })
판정 함수는 건물 정의를 받고 보호 호출 안에서 도므로, 거기서 난 오류가 청크 로드를 멈추지 않습니다. skipUrbanGate는 도시가 아닌 청크에서도 그 분류가 굴게 하며, 농장이나 숲속 오두막에는 그것이 필요합니다. exclusive는 켜지 마세요. 베이스 분류들은 자기들끼리 배타적이고, 애드온 분류는 그 위에 겹쳐서 걸립니다.
이미 쓰이는 접미사
접미사는 "이 건물은 이미 굴렸다"를 저장하는 키의 일부입니다. 같은 접미사를 쓰는 두 모드는 서로의 굴림을 망가뜨립니다.
접미사는 모두 세로 막대로 시작합니다. 이미 쓰이고 있는 것들입니다.
- 베이스: 빈 접미사, 그리고
g,h,hv,bc,gh,hh,bh - 로트와일러:
rw,r - 도베르만:
db,dm,dh - 래브라도:
lb,lk - 퍼그:
pg,pv - 말리노이즈:
ml,mm,mh - 고양이:
ct,cv또는cs로 시작하는 모든 접미사(털색마다 한 세트)
10. 품종 무들
무들은 클라이언트 파일입니다. CD.DogMoodles에 테이블 하나를 덧붙이면 베이스가 그것을 그리고, 추적하고, 치웁니다.
CD.DogMoodles[#CD.DogMoodles + 1] = {
id = "shadow",
breed = "pug",
nameKey = "IGUI_PD_Moodle_Shadow",
descKey = "IGUI_PD_Moodle_Shadow_desc",
icon = "CD_Moodle_Shadow",
fg = "CD_MoodShadowFG",
tintR = 0.92, tintG = 0.52, tintB = 0.68,
condition = function(player, dog)
if not dog or dog:isDead() then return 0 end
if CD.getBreed(dog) ~= "pug" then return 0 end
if CD.isDisloyal(dog) then return 0 end
return 1
end,
apply = function(player, dog, elapsedMin)
CD.relieveMood(player, CD.moodleReliefPerMin() * elapsedMin)
end,
}
condition은 단계를 돌려주고, 안 보일 때는 0을 돌려줍니다. apply는 지난 호출 이후의 게임 내 분을 받으므로, 효과에 elapsedMin을 곱하세요.
아이콘은 32픽셀부터 128픽셀까지 여섯 크기로 실으세요. 인터페이스가 플레이어의 배율에 따라 크기를 고르고, 없는 크기는 빈 사각형으로 나옵니다. 아이콘은 바닐라 무들 사이가 아니라 모드 자체의 줄에 나타납니다.
11. 훅
베이스가 서버에서 호출하는 함수 목록 둘입니다. shared 파일에서 덧붙이세요. 각 핸들러는 자기 보호 호출 안에서 도므로, 애드온 하나에서 난 오류가 베이스를 멈추지 않습니다.
API 2부터 있는 CD.onHuntDelivered는 동물이 사냥감을 주인 발치에 내려놓은 직후에 호출됩니다.
CD.onHuntDelivered[#CD.onHuntDelivered + 1] = function(animal, owner)
-- server side, so this is the right place to write mod data
end
API 3부터 있는 CD.onUpkeepStress는 유지 관리 주기마다, 베이스가 채우지 못한 욕구에서 나온 스트레스를 더한 뒤 그것을 쓰기 전에 호출됩니다. 숫자를 돌려주면 합계에 더해집니다.
CD.onUpkeepStress[#CD.onUpkeepStress + 1] = function(animal, needsStress)
return CD.pugHeatRatio(animal) * CD.PUG_HEAT_STRESS_MAX
end
유지 관리는 주기마다 값을 다시 계산해 덮어쓰므로, 바깥에서 쓴 것은 다음 틱에 사라집니다. 더위나 추위나 비처럼 환경에서 오는 조건에는 이 훅을 쓰세요.
멀티플레이에서 클라이언트는 모드 데이터를 절대 쓰지 않습니다. 클라이언트 쪽에서 쓴 값은 다음 동기화에 서버의 사본을 덮어쓰고, 증상은 몇 분 뒤 상관없는 곳에서 나타납니다. 그 일은 서버 파일에서 하거나 이 훅 안에서 하세요.
SERVER TUNING 패널의 숫자들은 평범한 CD.* 상수이고, 애드온은 로드 시점에 shared나 server 파일에서 하나를 대입할 수 있습니다. API 9부터 패널은 서버가 로드를 마친 시점에 유효한 값을 그 조절값의 기본값으로 삼습니다. 관리자가 덮어쓸 때까지 유지되고, 리셋 버튼은 그 값으로 돌아가며, 패널도 그것을 기본값으로 보여 줍니다. 더 오래된 베이스에서는 패널이 부팅할 때마다 출고값을 다시 써 넣었습니다. 로드 시점에 대입하고 나중의 이벤트에서는 하지 마세요. 그러지 않으면 어느 조절값이든 다음에 바뀌는 순간 출고값으로 돌아갑니다.
12. 번역 키
언어 폴더마다 IG_UI.json 하나씩을 media/lua/shared/Translate/<LANG>/ 아래에 실으세요. 베이스는 열네 개를 싣습니다. EN, PTBR, CH, CN, DE, ES, FR, IT, JP, KO, RU, TH, UA, VI.
품종에 필요한 키입니다.
| 키 | 어디에 나오는가 |
|---|---|
IGUI_PD_Breed_<key> |
품종 이름. 모드 인터페이스 전체 |
IGUI_PD_BreedDesc_<key> |
동물 카드의 설명 |
IGUI_Breed_<engineBreed> |
게임 자체 화면에 나오는 품종 이름 |
IGUI_AnimalType_<type> |
여러분의 타입 세 개마다 하나씩 |
| 여러분의 종 키 | CD.registerSpecies에 넘긴 nounKey와 youngKey |
| 여러분의 무들 키 | 이름과 설명 |
{
"IGUI_Breed_pug": "Pug",
"IGUI_AnimalType_pugmale": "Pug (male)",
"IGUI_AnimalType_pugfemale": "Pug (female)",
"IGUI_AnimalType_pugpup": "Pug (puppy)",
"IGUI_PD_Breed_pug": "Pug",
"IGUI_PD_BreedDesc_pug": "The house dog: the first companion whose worth is not fighting."
}
번역 문자열 안의 퍼센트 기호 하나가 그것을 보여주는 화면을 죽입니다. 포매터가 그것을 자리표시자로 읽기 때문입니다. 바이트 순서 표시가 붙은 언어 파일은 컴파일되지 않고, 오류는 망가진 파일이 아닌 다른 파일의 이름을 댑니다.
13. 세이브가 기억하는 것
여러분의 식별자 셋은 세이브 파일에 쓰이고 영원히 다시 읽힙니다.
key. 여러분 동물 하나하나에 저장되는 품종 식별자typePrefix. 엔진이 저장하는 동물 타입을 이루는 것suffix. "이미 굴렸다" 저장소의 키. 스폰 단계마다 하나
셋 중 무엇도 출시 뒤에는 이름을 바꿀 수 없습니다. key를 바꾸면 그 품종의 살아 있는 동물이 전부 미아가 됩니다. suffix를 바꾸면 기존 세이브의 모든 건물이 다시 굴려지고, 몇 달을 플레이한 세계가 여러분의 동물로 가득 찹니다.
새 접미사는 이미 돌아가고 있는 세계에 새 품종이 닿는 방법이기도 합니다. CD: Cats는 새 털색 넷에 새 접미사를 주었고, 그래서 기존 세이브에도 나타나기 시작했습니다. 원래의 검은 고양이는 옛 접미사를 유지해 다시 굴려지지 않았습니다.
그 품종의 동물이 살아 있는 세이브에서 애드온을 빼면 그 동물들을 잃습니다. 엔진은 자기가 더 이상 모르는 타입의 동물을 버립니다. 베이스는 죽지 않고, 유대와 견사는 깔끔하게 정리됩니다. 이 사실을 워크샵 페이지에 적으세요.
여러분의 애드온이 없으면 베이스는 물어보는 모든 것에 기본 품종으로 답하므로, 여러분의 모드 없이 연 세이브도 읽힙니다.
14. 공개하기 전에
- 가드가 모든 Lua 파일에 있고, 실제로 쓰는 API를 댄다.
- 베이스 버전 하한선이 설명에 말로 적혀 있다.
versionMin으로는 그것을 말할 수 없다. - 첫 로드에서 로그를 읽었다. 그러지 않으면
CD.registerBreed는 조용히 거부하며, 거부마다 이름이 붙은 줄이 찍힌다. CD.defineDogParts를 부른다. 동물 하나를 해체해 죽지 않는지 본다.key와typePrefix와 스폰 접미사가 유일하고, 앞으로 바꿀 일이 없다.- 작은 품종이면
puppySize를 넣었고, 강아지를 직접 보고 확인했다. - 다른 종이면
CD.registerVoices를 부르고whine까지 넣어voices를 채웠다. 안 그러면 개처럼 짖고 낑낑댄다. - 무들 아이콘이 여섯 크기 전부 있다.
- 함께 싣는 언어 폴더마다 모든 키가 있다. 빠진 키는 화면에 키 문자열 그대로 나온다.
- 싱글플레이만이 아니라 멀티플레이에서도 시험했다. 위치와 소유권과 모드 데이터는 거기서 다르게 굴러간다.
- 워크샵 페이지가 살아 있는 동물이 있는 세이브에서 애드온을 빼지 말라고 경고한다.
15. 흔한 실수
| 증상 | 무엇인가 |
|---|---|
| "동물이 아예 나타나지 않는다." | 거의 항상 가드입니다. 애드온이 요구하는 API가 설치된 베이스에 없어서 일찍 반환하고 아무것도 등록하지 않은 것입니다. |
| "나타나기는 하는데 굳어 있다." | 애님셋이 "raccoon"이 아니거나, 모델에 클립이 빠져 있습니다. |
| "검은 얼룩으로 그려지고 로그가 넘친다." | 모델의 본이 60개를 넘습니다. |
| "해체하면 게임이 죽는다." | CD.defineDogParts를 부르지 않았습니다. |
| "강아지가 성체 크기로 태어난다." | 작은 품종이 puppySize를 넣지 않았습니다. 이 값은 절대값이고 훑을 때마다 다시 적용되므로, 타입의 크기 범위로는 덮이지 않습니다. |
| "내 품종 둘이 자꾸 서로로 변한다." | engineBreed를 같이 쓰고 있습니다. 접두사는 메시를 고르므로 한 계열이 공유해도 됩니다. engineBreed는 텍스처를 고르고 그 계열 안에서 품종을 구별하므로, 품종마다 유일해야 합니다. |
| "소리는 나는데 음량 슬라이더를 무시한다." | 그 소리에 CD.registerVoices를 부르지 않았습니다. |
| "싱글플레이에서는 되는데 서버에서 이상하게 논다." | 무언가가 클라이언트에서 모드 데이터를 쓰고 있습니다. 서버로 옮기세요. |
| "전투를 껐는데도 좀비와 싸웠다." | 돌아가고 있는 베이스가 API 5보다 낡았습니다. 그 베이스는 이 필드를 몰라서 무시합니다. 가드가 필드 없이 로드되게 두지 말고 그런 베이스에서 막아야 합니다. |
이 설명서에 틀린 것이 있거나 빠진 것이 있나요? 디스코드에서 알려 주세요.