STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/slow-monster/core/state.tpz
Topaz · 26171 B · Original file
1
// 게임 상태 레코드와 STATE 문자열의 직렬화, 복원을 둡니다. 게임 전체가 월드시드와 입력 순서의 순수 함수입니다.2
// STATE 는 파이프로 나눈 한 줄입니다. v8 순서는 버전, 시드, 스텝, x, y, 방향, 맵, 장면, 야생 여섯 칸, 스포어볼, 포획카운터, 파티(카운트 + 11칸씩), 보관함, 파티커서, 도감커서, 처치 목록(카운트 접두), 초점, "본것:대기", 계통 노드(카운트 + 7칸씩), 진화 11칸, 배틀 9칸이고 배틀 메시지가 맨 끝입니다.3
// 시계, 파일, 시드 없는 난수를 쓰지 않아 같은 입력은 늘 같은 상태입니다. 복원한 상태는 늘 v8 로 다시 직렬화됩니다.4
// v7 부터 v0 까지 옛 저장을 읽고, 손상되거나 조작된 값은 가두거나 기본값으로 되돌려 아래쪽 정수 계산이 넘치지 않게 합니다. 빈 문자열과 일곱 칸보다 짧은 저장은 오류로 돌려 진입점이 기본 상태로 닫습니다.5
// Game state record and the pipe-separated STATE string: serialize, and restore every save version from v0 to v8 with clamping.7
import core.encounter { 야생크리처 }8
import core.creature9
import core.battle10
import core.evolution11
import core.genome12
import core.util { 범위가두기, 복사추가 }13
import data.species14
import data.items { 시작스포어볼 }16
// 차면 잡은 야생은 보관함으로 갑니다.17
export let 파티최대 = 619
// 정상 플레이는 닿지 않는 상한이라 범위 안의 값은 그대로 지나갑니다.20
// 좌표 9999 는 24x24 맵을 한참 넘고, 그리기의 (x - 카메라) * 16 과 이동의 ±1 이 넘치지 않습니다.21
let 좌표상한 = 999922
// 스텝, 경험, 포획과 전투 카운터, 체력의 상한입니다. 하류의 +1 과 + 적레벨 * 14 가 넘치지 않습니다.23
let 카운터상한 = 100000000025
function 좌표가두기(값: int) -> int {26
범위가두기(값, 0, 좌표상한)27
}29
function 카운터가두기(값: int) -> int {30
범위가두기(값, 0, 카운터상한)31
}33
// 비트 연산이 없어 산술 비트셋이고 열여섯 비트로 가둡니다. 토파즈 과제 '비트 연산' 의 우회입니다.34
export function 비트가두기(값: int) -> int {35
범위가두기(값, 0, 65535)36
}38
export type 방향 = "아래" | "위" | "왼쪽" | "오른쪽"40
// 저장 문자열과 그리기 목록의 방향 번호는 0 아래, 1 위, 2 왼쪽, 3 오른쪽입니다. 그 밖의 번호는 아래로 읽습니다.41
function 방향읽기(번호: int) -> 방향 {42
match 번호 {43
case 1 => "위"44
case 2 => "왼쪽"45
case 3 => "오른쪽"46
case _ => "아래"47
}48
}50
export function 방향번호(방향값: 방향) -> int {51
match 방향값 {52
case "아래" => 053
case "위" => 154
case "왼쪽" => 255
case "오른쪽" => 356
}57
}59
export type 플레이어 = { x: int, y: int, 방향: 방향 }61
// 플레이어가 마주 본 칸입니다.62
export function 마주x(대상: 플레이어) -> int {63
match 대상.방향 {64
case "왼쪽" => 대상.x - 165
case "오른쪽" => 대상.x + 166
case "아래" | "위" => 대상.x67
}68
}70
export function 마주y(대상: 플레이어) -> int {71
match 대상.방향 {72
case "위" => 대상.y - 173
case "아래" => 대상.y + 174
case "왼쪽" | "오른쪽" => 대상.y75
}76
}78
export type 장면 = "field" | "battle" | "party" | "dex" | "story" | "evolve"80
function 장면파싱(토큰: string) -> 장면 {81
match 토큰 {82
case "battle" => "battle"83
case "party" => "party"84
case "dex" => "dex"85
case "story" => "story"86
case "evolve" => "evolve"87
case _ => "field"88
}89
}91
// 후보는 저장하지 않고 부모 스냅샷에서 다시 유도합니다. 모핑은 고정 직후 한 프레임이며 페이지가 장면을 재생합니다.92
export type 진화단계 = "none" | "choose" | "morph"94
function 진화단계파싱(토큰: string) -> 진화단계 {95
match 토큰 {96
case "choose" => "choose"97
case "morph" => "morph"98
case _ => "none"99
}100
}102
export type 진화상태 = {103
단계: 진화단계,104
부모형태: string,105
부모게놈: genome.게놈,106
부모계통시드: string,107
부모진화카운터: int,108
부모세대: int,109
부모고유번호: int,110
선택커서: int,111
잠긴분기: int,112
결과형태: string,113
결과게놈: genome.게놈,114
}116
// 본것은 한 번씩만 띄우는 이정표, 대기는 보여 줄 카드 큐이고 둘 다 비트셋입니다.117
export type 스토리상태 = { 본것: int, 대기: int }119
// 배틀, 드리프트, 진화는 파티[초점] 한 마리만 합니다. 잡은 동료마다 제 계통의 뿌리라 도감이 숲이 됩니다.120
export type 상태 = {121
버전: int,122
월드시드: string,123
스텝: int,124
플레이어: 플레이어,125
맵아이디: string,126
장면: 장면,127
야생: 야생크리처,128
스포어볼: int,129
포획카운터: int,130
파티: Array<creature.전투원>,131
보관함: Array<creature.전투원>,132
파티커서: int,133
도감커서: int,134
처치된것들: Array<int>,135
초점: int,136
스토리: 스토리상태,137
계통노드들: Array<evolution.계통노드>,138
진화: 진화상태,139
배틀: battle.배틀상태,140
}142
// 초점은 복원할 때 가두지만 한 번 더 가둡니다.143
export function 초점전투원(현재: 상태) -> creature.전투원 {144
현재.파티[범위가두기(현재.초점, 0, 현재.파티.length - 1)]145
}147
export function 빈진화() -> 진화상태 {148
let 영게놈 = genome.게놈만들기(0, 0, 0, 0, 0)149
{150
단계: "none",151
부모형태: "",152
부모게놈: 영게놈,153
부모계통시드: "",154
부모진화카운터: 0,155
부모세대: 0,156
부모고유번호: 0,157
선택커서: 0,158
잠긴분기: 0,159
결과형태: "",160
결과게놈: 영게놈,161
}162
}164
export function 빈야생() -> 야생크리처 {165
야생크리처 { 종아이디: 0, 이름: "", 레벨: 0, 체력: 0, 스프라이트: 0, 속성: 0 }166
}168
export type 추가결과 = { 파티: Array<creature.전투원>, 보관함: Array<creature.전투원> }170
// 파티가 차 있으면 보관함에 붙이고 둘이 함께 늘지는 않습니다.171
export function 파티추가(파티: Array<creature.전투원>, 보관함: Array<creature.전투원>, 잡은것: creature.전투원) -> 추가결과 {172
if 파티.length >= 파티최대 {173
{ 파티: 파티, 보관함: 복사추가(보관함, 잡은것) }174
} else {175
{ 파티: 복사추가(파티, 잡은것), 보관함: 보관함 }176
}177
}179
// 계통수의 뿌리 노드입니다. 미진화면 종 이름을 형태이름으로 씁니다.180
export function 시작노드(전투원: creature.전투원, 스텝: int) -> evolution.계통노드 {181
let 이름 = if 전투원.형태이름 != "" { 전투원.형태이름 } else { species.종찾기(전투원.종아이디).이름 }182
evolution.노드생성(evolution.계통고유번호(전투원.계통시드), 0, 이름, genome.게놈요약(전투원.게놈), 전투원.세대, 스텝, 전투원.계통시드)183
}185
// 같은 고유번호는 다시 붙이지 않아 여러 번 적용해도 같습니다.186
export function 노드추가(원본: Array<evolution.계통노드>, 노드: evolution.계통노드) -> Array<evolution.계통노드> {187
if 노드.고유번호 in 원본.map(항목 => 항목.고유번호) { 원본.slice(0, 원본.length) } else { 복사추가(원본, 노드) }188
}190
// 시작종은 "시드#starter" 계통시드를 받아 게놈이 월드시드의 함수가 되고, 계통 노드는 그 뿌리 하나로 시작합니다.191
export function 기본상태(시드: string) -> 상태 {192
let 시작전투원 = creature.종에서전투원(species.시작종아이디(), 5, genome.시작계통시드(시드))193
{194
버전: 8,195
월드시드: 시드,196
스텝: 0,197
플레이어: { x: 3, y: 5, 방향: "아래" },198
맵아이디: "pond",199
장면: "field",200
야생: 빈야생(),201
스포어볼: 시작스포어볼(),202
포획카운터: 0,203
처치된것들: [],204
초점: 0,205
스토리: { 본것: 0, 대기: 0 },206
파티: [시작전투원],207
보관함: [],208
파티커서: 0,209
도감커서: 0,210
계통노드들: [시작노드(시작전투원, 0)],211
진화: 빈진화(),212
배틀: battle.빈배틀(),213
}214
}216
export function 처치추가(목록: Array<int>, 아이디: int) -> Array<int> {217
if 아이디 in 목록 { 목록.slice(0, 목록.length) } else { 복사추가(목록, 아이디) }218
}220
// 인물 앞에서 쉬면 파티 전체를 채워 긴 세션이 체력 소모로 무너지지 않게 합니다.221
export function 파티회복(현재: 상태) -> 상태 {222
현재{ 파티: 현재.파티.map(creature.회복) }223
}225
// 콜론으로 이어 파이프 없는 한 칸에 담습니다.226
function 게놈직렬(대상: genome.게놈) -> string {227
"{대상.색소}:{대상.막}:{대상.크기}:{대상.대사}:{대상.촉수}"228
}230
function 게놈역직렬(토큰: string) -> genome.게놈 {231
let 칸들 = 토큰.split(":")232
let 값들 = map(0..<5, i => if 칸들.length > i { toInt(칸들[i]) ?? 0 } else { 0 })233
genome.게놈만들기(값들[0], 값들[1], 값들[2], 값들[3], 값들[4])234
}236
// 계통시드와 형태이름은 파이프가 없는 문자열이고 형태이름 "" 은 미진화입니다.237
function 전투원직렬(전투원: creature.전투원) -> string {238
"{전투원.종아이디}|{전투원.레벨}|{전투원.경험}|{전투원.현재체력}|{전투원.계통시드}|{전투원.드리프트}|{게놈직렬(전투원.게놈)}|{전투원.세대}|{전투원.진화카운터}|{전투원.형태이름}|{전투원.스탯부스트}"239
}241
function 노드직렬(노드: evolution.계통노드) -> string {242
"{노드.고유번호}|{노드.부모고유번호}|{노드.형태이름}|{노드.게놈요약}|{노드.세대}|{노드.발견스텝}|{노드.시드지문}"243
}245
// 개수를 앞에 두어 가변 길이 구획이 스스로 끝을 알립니다.246
function 카운트접두(조각들: Array<string>) -> string {247
if 조각들.length == 0 { "0" } else { "{조각들.length}|{조각들.join("|")}" }248
}250
function 진화직렬(진: 진화상태) -> string {251
"{진.단계}|{진.부모형태}|{게놈직렬(진.부모게놈)}|{진.부모계통시드}|{진.부모진화카운터}|{진.부모세대}|{진.부모고유번호}|{진.선택커서}|{진.잠긴분기}|{진.결과형태}|{게놈직렬(진.결과게놈)}"252
}254
// 메시지에는 파이프가 없어 맨 끝에 두면 앞의 가변 구획 길이와 상관없이 읽힙니다.255
export function 직렬화(현재: 상태) -> string {256
let 야생 = 현재.야생257
let 배틀값 = 현재.배틀258
let 앞부분 = "v{현재.버전}|{현재.월드시드}|{현재.스텝}|{현재.플레이어.x}|{현재.플레이어.y}|{방향번호(현재.플레이어.방향)}|{현재.맵아이디}|{현재.장면}"259
let 야생부 = "{야생.종아이디}|{야생.레벨}|{야생.체력}|{야생.스프라이트}|{야생.속성}|{야생.이름}"260
let 파티부 = "{현재.스포어볼}|{현재.포획카운터}|{카운트접두(현재.파티.map(전투원직렬))}|{카운트접두(현재.보관함.map(전투원직렬))}|{현재.파티커서}|{현재.도감커서}"261
let 진행부 = "{카운트접두(현재.처치된것들.map(아이디 => "{아이디}"))}|{현재.초점}|{현재.스토리.본것}:{현재.스토리.대기}|{카운트접두(현재.계통노드들.map(노드직렬))}|{진화직렬(현재.진화)}"262
let 배틀부 = "{배틀값.단계}|{배틀값.메뉴커서}|{배틀값.기술커서}|{배틀값.전투카운터}|{배틀값.적체력}|{배틀값.메시지커서}|{배틀값.다음단계}|{배틀값.결과}|{배틀값.메시지}"263
"{앞부분}|{야생부}|{파티부}|{진행부}|{배틀부}"264
}266
function 버전파싱(토큰: string) -> int {267
toInt(토큰.replace("v", "")) ?? 3268
}270
// 보폭 11 은 v5 이후, 6 은 v4, 4 는 v3 입니다. 보폭이 11 보다 작으면 미진화로 옮기고 v3 은 계통시드를 마이그레이션시드로 받습니다.271
// 세대 0 이면 전투원가두기가 게놈을 계통시드에서 다시 유도하므로 옛 저장의 게놈 인자는 쓰이지 않습니다.272
function 전투원파싱(칸들: Array<string>, 시작: int, 보폭: int, 마이그레이션시드: string) -> creature.전투원 {273
let 종 = toInt(칸들[시작]) ?? species.시작종아이디()274
let 레벨 = toInt(칸들[시작 + 1]) ?? 5275
// 경험적용의 경험 + 얻은경험이 넘치지 않게 가둡니다. 나머지는 전투원가두기가 가둡니다.276
let 경험 = 카운터가두기(toInt(칸들[시작 + 2]) ?? 0)277
let 체력 = toInt(칸들[시작 + 3]) ?? 0278
let 계통시드 = if 보폭 >= 6 { 칸들[시작 + 4] } else { 마이그레이션시드 }279
let 드리프트 = if 보폭 >= 6 { toInt(칸들[시작 + 5]) ?? 0 } else { 0 }280
let 세대 = if 보폭 >= 11 { toInt(칸들[시작 + 7]) ?? 0 } else { 0 }281
let 진화카운터 = if 보폭 >= 11 { toInt(칸들[시작 + 8]) ?? 0 } else { 0 }282
let 형태이름 = if 보폭 >= 11 { 칸들[시작 + 9] } else { "" }283
let 스탯부스트 = if 보폭 >= 11 { toInt(칸들[시작 + 10]) ?? 0 } else { 0 }284
let 기준게놈 = if 보폭 >= 11 { 게놈역직렬(칸들[시작 + 6]) } else { genome.게놈유도(계통시드) }285
creature.전투원가두기(creature.빈전투원(){286
종아이디: 종,287
레벨: 레벨,288
경험: 경험,289
현재체력: 체력,290
계통시드: 계통시드,291
드리프트: 드리프트,292
세대: 세대,293
진화카운터: 진화카운터,294
형태이름: 형태이름,295
스탯부스트: 스탯부스트,296
게놈: 기준게놈,297
})298
}300
type 배열파싱결과 = { 목록: Array<creature.전투원>, 다음: int }302
// 잘린 저장에서 파티가 칸을 다 쓰면 보관함의 카운트 자리가 끝 너머라 빈 목록으로 돌아갑니다.303
// 카운트는 실제로 남은 칸이 받쳐 주는 항목 수로 가두고, 파티는 비면 시작종 한 마리를 받습니다.304
function 배열파싱(칸들: Array<string>, 시작: int, 최소항목수: int, 보폭: int, 월드시드: string, 마이그레이션접두: string) -> 배열파싱결과 {305
if 시작 < 0 || 시작 >= 칸들.length { return { 목록: [], 다음: 시작 } }306
let 가용 = ((칸들.length - (시작 + 1)) / 보폭).atLeast(0)307
let 카운트 = (toInt(칸들[시작]) ?? 0).atLeast(0).atMost(가용)308
let 읽은목록 = map(0..<카운트, i => 전투원파싱(칸들, 시작 + 1 + i * 보폭, 보폭, "{월드시드}#legacy:{마이그레이션접두}:{i}"))309
let 목록 = if 읽은목록.length < 최소항목수 { [creature.종에서전투원(species.시작종아이디(), 5, genome.시작계통시드(월드시드))] } else { 읽은목록 }310
{ 목록: 목록, 다음: 시작 + 1 + 카운트 * 보폭 }311
}313
type 노드배열파싱결과 = { 목록: Array<evolution.계통노드>, 다음: int }315
// 도감커서가 끼어든 v5 인지 판정합니다. 계통 구획이 진화 블록 시작에서 정확히 끝나면 도감커서가 있는 배치입니다.316
function 계통섹션끝일치(칸들: Array<string>, 시작: int, 예상끝: int) -> bool {317
if 시작 < 0 || 시작 >= 칸들.length { return false }318
let 카운트 = toInt(칸들[시작]) ?? -1319
if 카운트 < 0 { return false }320
시작 + 1 + 카운트 * 7 == 예상끝321
}323
// 배치는 노드마다 목록 전체를 훑어 제곱 시간이므로 여기서 128 개로 묶습니다. 실제 계통수는 끝까지 가도 수십 개입니다.324
function 노드배열파싱(칸들: Array<string>, 시작: int, 상한배제: int) -> 노드배열파싱결과 {325
if 시작 < 0 || 시작 >= 칸들.length { return { 목록: [], 다음: 시작 } }326
let 가용 = ((상한배제 - (시작 + 1)) / 7).atLeast(0)327
let 카운트 = (toInt(칸들[시작]) ?? 0).atLeast(0).atMost(가용).atMost(128)328
let 목록 = map(0..<카운트, i => 노드파싱(칸들, 시작 + 1 + i * 7))329
{ 목록: 목록, 다음: 시작 + 1 + 카운트 * 7 }330
}332
function 노드파싱(칸들: Array<string>, 시작: int) -> evolution.계통노드 {333
let 세대 = creature.세대가두기(toInt(칸들[시작 + 4]) ?? 0)334
evolution.노드생성(toInt(칸들[시작]) ?? 0, toInt(칸들[시작 + 1]) ?? 0, 칸들[시작 + 2], 칸들[시작 + 3], 세대, toInt(칸들[시작 + 5]) ?? 0, 칸들[시작 + 6])335
}337
type 처치파싱결과 = { 목록: Array<int>, 다음: int }339
// 카운트를 엔티티 수보다 한참 큰 64 로 가두고 범위 밖 아이디는 버립니다.340
function 처치파싱(칸들: Array<string>, 시작: int, 상한: int) -> 처치파싱결과 {341
if 시작 < 0 || 시작 >= 칸들.length { return { 목록: [], 다음: 시작 } }342
let 카운트 = (toInt(칸들[시작]) ?? 0).atLeast(0).atMost(64)343
// 끝에서 센 위치에 진화 구획이 없으면 잘린 저장이므로 실제 남은 칸까지 살립니다.344
let 진화있음 = 상한 > 시작 && 상한 < 칸들.length - 10 && (칸들[상한] == "none" || 칸들[상한] == "choose" || 칸들[상한] == "morph")345
let 경계 = if 진화있음 { 상한 } else { 칸들.length }346
let 끝 = (시작 + 1 + 카운트).atMost(경계)347
let 아이디들 = map((시작 + 1)..<끝, i => toInt(칸들[i]) ?? -1)348
{ 목록: 아이디들.filter(아이디 => 아이디 >= 0 && 아이디 <= 100000), 다음: 끝 }349
}351
// 진화 블록 11 칸이 다 있을 때만 읽습니다.352
function 진화블록파싱(칸들: Array<string>, 시작: int) -> 진화상태 {353
if 시작 < 0 || 시작 + 10 >= 칸들.length { return 빈진화() }354
{355
단계: 진화단계파싱(칸들[시작]),356
부모형태: 칸들[시작 + 1],357
부모게놈: 게놈역직렬(칸들[시작 + 2]),358
부모계통시드: 칸들[시작 + 3],359
부모진화카운터: creature.진화카운터가두기(toInt(칸들[시작 + 4]) ?? 0),360
부모세대: creature.세대가두기(toInt(칸들[시작 + 5]) ?? 0),361
부모고유번호: toInt(칸들[시작 + 6]) ?? 0,362
선택커서: toInt(칸들[시작 + 7]) ?? 0,363
잠긴분기: toInt(칸들[시작 + 8]) ?? 0,364
결과형태: 칸들[시작 + 9],365
결과게놈: 게놈역직렬(칸들[시작 + 10]),366
}367
}369
function 칸정수(칸들: Array<string>, 위치: int) -> int {370
if 위치 >= 0 && 위치 < 칸들.length { toInt(칸들[위치]) ?? 0 } else { 0 }371
}373
// 파티커서 뒤의 진행 구획입니다. 진화 11 칸과 배틀 9 칸은 끝에 고정되고 계통 노드는 진화 블록 앞까지만 읽습니다.374
type 진행구획 = {375
도감커서: int,376
처치된것들: Array<int>,377
초점: int,378
스토리: 스토리상태,379
계통노드들: Array<evolution.계통노드>,380
진화: 진화상태,381
}383
function 계통상한(진화시작: int, 계통시작: int, 배틀끝: int) -> int {384
if 진화시작 >= 계통시작 { 진화시작 } else { 배틀끝 }385
}387
// v6 은 도감커서, 처치 목록, 계통이 차례로 오고 v7 은 처치 목록 뒤에 초점, v8 은 그 뒤에 "본것:대기" 를 더합니다.388
function 진행구획v6(칸들: Array<string>, 버전: int, 커서위치: int, 진화시작: int) -> 진행구획 {389
let 처치결과 = 처치파싱(칸들, 커서위치 + 2, 진화시작)390
let 스토리위치 = 처치결과.다음 + 1391
let 스토리조각들 = if 버전 >= 8 && 스토리위치 >= 0 && 스토리위치 < 칸들.length { 칸들[스토리위치].split(":") } else { [] }392
let 스토리값 = if 스토리조각들.length >= 2 { { 본것: 비트가두기(toInt(스토리조각들[0]) ?? 0), 대기: 비트가두기(toInt(스토리조각들[1]) ?? 0) } } else { { 본것: 0, 대기: 0 } }393
let 계통시작 = if 버전 >= 8 { 처치결과.다음 + 2 } else if 버전 >= 7 { 처치결과.다음 + 1 } else { 처치결과.다음 }394
{395
도감커서: 칸정수(칸들, 커서위치 + 1).atLeast(0),396
처치된것들: 처치결과.목록,397
초점: if 버전 >= 7 { 칸정수(칸들, 처치결과.다음) } else { 0 },398
스토리: 스토리값,399
계통노드들: 노드배열파싱(칸들, 계통시작, 계통상한(진화시작, 계통시작, 칸들.length)).목록,400
진화: 진화블록파싱(칸들, 진화시작),401
}402
}404
// 이전 v5 는 도감커서 없이 파티커서 뒤에 바로 계통 카운트가 옵니다.405
function 진행구획v5(칸들: Array<string>, 커서위치: int, 진화시작: int) -> 진행구획 {406
let 도감있음 = 커서위치 + 1 < 칸들.length && 계통섹션끝일치(칸들, 커서위치 + 2, 진화시작)407
let 계통시작 = if 도감있음 { 커서위치 + 2 } else { 커서위치 + 1 }408
{409
도감커서: if 도감있음 { 칸정수(칸들, 커서위치 + 1).atLeast(0) } else { 0 },410
처치된것들: [],411
초점: 0,412
스토리: { 본것: 0, 대기: 0 },413
계통노드들: 노드배열파싱(칸들, 계통시작, 계통상한(진화시작, 계통시작, 칸들.length)).목록,414
진화: 진화블록파싱(칸들, 진화시작),415
}416
}418
function 빈진행구획() -> 진행구획 {419
{ 도감커서: 0, 처치된것들: [], 초점: 0, 스토리: { 본것: 0, 대기: 0 }, 계통노드들: [], 진화: 빈진화() }420
}422
type 본문 = {423
스포어볼: int,424
포획카운터: int,425
파티: Array<creature.전투원>,426
보관함: Array<creature.전투원>,427
파티커서: int,428
진행: 진행구획,429
}431
// v3 부터는 가변 길이 파티와 보관함이 오고, v2 는 [14..17] 의 한 마리 파티, v1 과 v0 은 파티가 없어 시작종을 줍니다.432
function 본문파싱(칸들: Array<string>, 버전: int, 시드: string, 배틀있음: bool) -> 본문 {433
if 버전 >= 3 && 칸들.length >= 17 {434
let 보폭 = if 버전 >= 5 { 11 } else if 버전 >= 4 { 6 } else { 4 }435
let 파티결과 = 배열파싱(칸들, 16, 1, 보폭, 시드, "party")436
let 보관결과 = 배열파싱(칸들, 파티결과.다음, 0, 보폭, 시드, "box")437
// 파티커서는 보관함 바로 뒤를 앞에서부터 셉니다. v5 이후의 계통 구획이 가변 길이라 끝에서 셀 수 없습니다.438
let 커서위치 = 보관결과.다음439
let 진화시작 = if 배틀있음 { 칸들.length - 9 - 11 } else { 칸들.length - 11 }440
let 진행 = if 버전 >= 6 { 진행구획v6(칸들, 버전, 커서위치, 진화시작) } else if 버전 >= 5 { 진행구획v5(칸들, 커서위치, 진화시작) } else { 빈진행구획() }441
return {442
스포어볼: (toInt(칸들[14]) ?? 시작스포어볼()).atLeast(0),443
포획카운터: 카운터가두기(toInt(칸들[15]) ?? 0),444
파티: 파티결과.목록,445
보관함: 보관결과.목록,446
파티커서: 범위가두기(칸정수(칸들, 커서위치), 0, 파티결과.목록.length - 1),447
진행: 진행,448
}449
}450
let 파티 = if 칸들.length >= 18 { [전투원파싱(칸들, 14, 4, "{시드}#legacy:party:0")] } else { [creature.종에서전투원(species.시작종아이디(), 5, genome.시작계통시드(시드))] }451
{ 스포어볼: 시작스포어볼(), 포획카운터: 0, 파티: 파티, 보관함: [], 파티커서: 0, 진행: 빈진행구획() }452
}454
// 적 레벨 0 은 야생 없음이고 99 로 가둬 피해 계산이 넘치지 않게 합니다.455
function 야생파싱(칸들: Array<string>) -> 야생크리처 {456
if 칸들.length < 14 { return 빈야생() }457
야생크리처 { 종아이디: toInt(칸들[8]) ?? 0, 이름: 칸들[13], 레벨: (toInt(칸들[9]) ?? 0).atLeast(0).atMost(99), 체력: 카운터가두기(toInt(칸들[10]) ?? 0), 스프라이트: toInt(칸들[11]) ?? 0, 속성: toInt(칸들[12]) ?? 0 }458
}460
// 배틀은 v2 이후 늘 끝 9 칸입니다.461
function 배틀파싱(칸들: Array<string>) -> battle.배틀상태 {462
if 칸들.length < 23 { return battle.빈배틀() }463
let 시작 = 칸들.length - 9464
{465
단계: battle.단계파싱(칸들[시작]),466
메뉴커서: toInt(칸들[시작 + 1]) ?? 0,467
기술커서: toInt(칸들[시작 + 2]) ?? 0,468
전투카운터: 카운터가두기(toInt(칸들[시작 + 3]) ?? 0),469
적체력: 카운터가두기(toInt(칸들[시작 + 4]) ?? 0),470
메시지커서: toInt(칸들[시작 + 5]) ?? 0,471
다음단계: battle.다음단계파싱(칸들[시작 + 6]),472
결과: battle.결과파싱(칸들[시작 + 7]),473
메시지: 칸들[시작 + 8],474
}475
}477
export type 파싱오류 = "empty" | "short"479
// 계통 구획이 없던 옛 저장은 파티와 보관함의 원형 노드로 도감의 뿌리를 채웁니다.480
// 복원한 상태는 늘 v8 이라 옮겨 온 옛 저장도 v8 머리와 v8 본문으로 다시 저장됩니다.481
export function 역직렬화(문자열: string) -> Result<상태, 파싱오류> {482
let 다듬은문자열 = 문자열.trim()483
if 다듬은문자열 == "" { return Err("empty") }484
let 칸들 = 다듬은문자열.split("|")485
if 칸들.length < 7 { return Err("short") }486
let 버전 = 버전파싱(칸들[0])487
let 시드 = 칸들[1]488
let 장면값: 장면 = if 칸들.length >= 8 { 장면파싱(칸들[7]) } else { "field" }489
let 야생 = 야생파싱(칸들)490
let 배틀값 = 배틀파싱(칸들)491
let 읽은본문 = 본문파싱(칸들, 버전, 시드, 칸들.length >= 23)492
let 진행 = 읽은본문.진행493
let 스텝값 = 카운터가두기(toInt(칸들[2]) ?? 0)494
let 노드들 = if 진행.계통노드들.length == 0 {495
[for 묶음 in [읽은본문.파티, 읽은본문.보관함] for 원 in 묶음 => 시작노드(원, 스텝값)]496
} else {497
진행.계통노드들498
}499
Ok({500
버전: 8,501
월드시드: 시드,502
스텝: 스텝값,503
플레이어: {504
x: 좌표가두기(toInt(칸들[3]) ?? 3),505
y: 좌표가두기(toInt(칸들[4]) ?? 5),506
방향: 방향읽기(toInt(칸들[5]) ?? 0),507
},508
맵아이디: 칸들[6],509
장면: 장면값,510
야생: 야생,511
스포어볼: 읽은본문.스포어볼,512
포획카운터: 읽은본문.포획카운터,513
파티: 읽은본문.파티,514
보관함: 읽은본문.보관함,515
파티커서: 읽은본문.파티커서,516
도감커서: if 노드들.length > 0 { 범위가두기(진행.도감커서, 0, 노드들.length - 1) } else { 0 },517
처치된것들: 진행.처치된것들,518
초점: 범위가두기(진행.초점, 0, 읽은본문.파티.length - 1),519
스토리: 진행.스토리,520
계통노드들: 노드들,521
진화: 진행.진화,522
배틀: 배틀값,523
})524
}SHA256 cd2b5198ea6ed56621bb44f7d664cd6322145dc7df14257435d3c27d35dbc60a