STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/slow-monster/core/drawlist.tpz
Topaz · 17998 B · Original file
1
// 상태에서 그리기 목록을 만듭니다. 토파즈는 그리지 않고 무엇을 그릴지만 적고, 페이지가 목록을 읽어 160x144 캔버스에 칠합니다.2
// 목록은 DL1 머리 뒤에 한 줄에 지시 하나입니다. 공통 PAL, 필드 BG TILES SPR WARP ENT BOX TXT PROMPT DRIFT OBJECTIVE, 압력전 BATTLEBG CSPR ALLYSPR BOX TXT HPBAR DRIFT PRESSURE BEATFX, 파티 PTITLE PROW DRIFT PCURSOR, 진화 EVOLVE EPARENT ECAND EMENU, 도감 DEXSTAT DEXPATH DEXWIN FHEAD FROW DEXVIEW DEXEDGE DEXNODE DEXCURSOR DEXDETAIL DEXSIBLING, 이야기 STORY 입니다.3
// 타일은 16 픽셀이라 화면에 10x9 칸이 보이고 카메라는 플레이어를 가운데 두되 맵 경계에서 멈춥니다. 상태의 순수 함수라 같은 상태는 늘 같은 목록입니다.4
// 상태를 바꾸지 않고 픽셀을 칠하지 않습니다. 진화 모핑과 깜빡임 같은 움직임은 페이지가 그립니다.5
// Draw-list builder: describes each scene as one directive per line for the page to paint on a 160x144 canvas.7
import data.maps { 맵너비, 맵높이, 맵타일들, 타일에서, 워프목록, 워프 }8
import data.palettes { 연못팔레트 }9
import data.species10
import data.entities11
import data.types { 속성이름 }12
import core.creature13
import core.encounter14
import core.genome15
import core.evolution16
import core.lineagedex17
import core.auto { 자동목표 }18
import core.story { 최하위비트, 비트내레이션, 켜진비트인가, 비트_첫포획, 비트_첫심층, 비트_정복 }19
import core.clash { 적게놈유도 }20
import core.state21
import core.util { 범위가두기 }23
let 타일크기 = 1624
let 가시너비 = 1025
let 가시높이 = 926
let 보스아이디 = 1428
// 지시의 켜짐 칸은 1 과 0 입니다.29
function 표시값(켜짐: bool) -> int {30
if 켜짐 { 1 } else { 0 }31
}33
// 페이지가 드리프트 막대와 표현형으로 블롭의 외형을 조금 흔듭니다. 주인은 party:<i>, ally, field 입니다.34
function 드리프트줄(주인: string, 전투원: creature.전투원) -> string {35
let 표현 = genome.표현형(전투원.게놈, 전투원.계통시드, 전투원.드리프트)36
"DRIFT|{주인}|{전투원.드리프트}|{genome.드리프트퍼센트(전투원.드리프트)}|{표시값(genome.진화준비(전투원.드리프트))}|{genome.게놈요약(전투원.게놈)}|{genome.게놈요약(표현)}"37
}39
function 카메라x(플레이어x: int) -> int {40
범위가두기(플레이어x - 가시너비 / 2, 0, 맵너비 - 가시너비)41
}43
function 카메라y(플레이어y: int) -> int {44
범위가두기(플레이어y - 가시높이 / 2, 0, 맵높이 - 가시높이)45
}47
function 화면안인가(x: int, y: int, 왼쪽: int, 위: int) -> bool {48
x >= 왼쪽 && x < 왼쪽 + 가시너비 && y >= 위 && y < 위 + 가시높이49
}51
// 맵 밖은 타일에서가 나무 벽으로 메웁니다.52
function 타일윈도(타일들: Array<int>, 왼쪽: int, 위: int) -> string {53
[for 행 in 0..<가시높이 for 열 in 0..<가시너비 => "{타일에서(타일들, 왼쪽 + 열, 위 + 행)}"].join(",")54
}56
// 인물의 한마디는 가장 멀리 간 이야기 이정표를 따라 바뀌고, 이정표 전에는 배치된 대사를 씁니다.57
function 인물대사(현재: state.상태, 앞: entities.엔티티) -> string {58
if 켜진비트인가(현재.스토리.본것, 비트_정복) {59
"숲이 다시 숨을 쉽니다. 당신이 천천히 지켜낸 결과입니다. 푹 쉬어 가십시오."60
} else if 켜진비트인가(현재.스토리.본것, 비트_첫심층) {61
"깊은 곳을 보셨군요. 그곳은 적의 땅이 아니라, 멈춰 버린 생명입니다. 회복하고 다시 내려가십시오."62
} else if 켜진비트인가(현재.스토리.본것, 비트_첫포획) {63
"한 가닥의 생명을 돌보기 시작하셨군요. 서두르지 마십시오. A로 회복합니다. 깊은 곳일수록 더 강한 미생물이 기다립니다."64
} else {65
match 앞.종류 {66
case 인물(대사) => 대사67
case 없음 | 야생(_, _) => ""68
}69
}70
}72
function 엔티티스프라이트(대상: entities.엔티티) -> int {73
match 대상.종류 {74
case 야생(종아이디, _) => species.종찾기(종아이디).스프라이트75
case 없음 | 인물(_) => 076
}77
}79
// 처치한 야생은 맵에서 사라집니다. 인물을 마주 보면 대화 상자가, 야생을 마주 보면 A 프롬프트가 그 칸 위에 뜹니다.80
function 필드줄들(현재: state.상태, 팔레트: string) -> Array<string> {81
let 플레이어값 = 현재.플레이어82
let 왼쪽 = 카메라x(플레이어값.x)83
let 위 = 카메라y(플레이어값.y)84
let 머리줄들 = [85
"DL1",86
"PAL|0|{팔레트}",87
"BG|{현재.맵아이디}|{왼쪽}|{위}",88
"TILES|{가시너비}|{가시높이}|{타일윈도(맵타일들(현재.맵아이디), 왼쪽, 위)}",89
"SPR|0|{state.방향번호(플레이어값.방향)}|{(플레이어값.x - 왼쪽) * 타일크기}|{(플레이어값.y - 위) * 타일크기}|0|0|10",90
]91
let 워프줄들 = [92
for 대상 in 워프목록(현재.맵아이디) if 화면안인가(대상.x, 대상.y, 왼쪽, 위) => "WARP|{(대상.x - 왼쪽) * 타일크기}|{(대상.y - 위) * 타일크기}"93
]94
let 엔티티줄들 = [95
for 대상 in entities.엔티티목록(현재.맵아이디) if 화면안인가(대상.x, 대상.y, 왼쪽, 위) && !entities.처치된것인가(현재.처치된것들, 대상.아이디) => "ENT|{entities.종류코드(대상)}|{엔티티스프라이트(대상)}|{(대상.x - 왼쪽) * 타일크기}|{(대상.y - 위) * 타일크기}"96
]97
let 앞x = state.마주x(플레이어값)98
let 앞y = state.마주y(플레이어값)99
let 앞 = entities.활성엔티티에서(현재.맵아이디, 앞x, 앞y, 현재.처치된것들)100
let 대화줄들 = if entities.인물인가(앞) { ["BOX|6|104|148|34|0", "TXT|12|112|136|{인물대사(현재, 앞)}"] } else { [] }101
let 프롬프트줄들 = if entities.야생인가(앞) { ["PROMPT|{(앞x - 왼쪽) * 타일크기}|{(앞y - 위) * 타일크기}"] } else { [] }102
let 드리프트줄들 = if 현재.파티.length > 0 { [드리프트줄("field", state.초점전투원(현재))] } else { [] }103
[for 묶음 in [머리줄들, 워프줄들, 엔티티줄들, 대화줄들, 프롬프트줄들, 드리프트줄들, ["OBJECTIVE|{자동목표(현재)}"]] for 줄 in 묶음 => 줄]104
}106
// 적은 오른쪽 위, 내 세포는 왼쪽 아래에 그리고 체력 막대를 안정도 막대로 씁니다. 야생에는 게놈이 없어 적은 종 색 블롭입니다.107
// 이번 비트의 두 사건은 " · " 로 나뉘어 아래 상자에 두 줄로 들어갑니다.108
function 배틀드로우(현재: state.상태, 팔레트: string) -> string {109
let 야생 = 현재.야생110
let 파티 = state.초점전투원(현재)111
let 배틀값 = 현재.배틀112
let 적최대 = creature.최대체력(species.종찾기(야생.종아이디), 야생.레벨)113
let 내형질 = evolution.지배트레이트(genome.표현형(파티.게놈, 파티.계통시드, 파티.드리프트))114
let 적형질 = evolution.지배트레이트(적게놈유도(야생.종아이디, 야생.레벨, 현재.월드시드))115
let 사건들 = 배틀값.메시지.split(" · ")116
let 줄들 = [117
"DL1",118
"PAL|0|{팔레트}",119
"BATTLEBG|0",120
"CSPR|{야생.스프라이트}|104|22|0|5",121
"ALLYSPR|{파티.스프라이트}|18|72",122
"BOX|4|4|92|30|1",123
"TXT|8|8|84|{야생.이름}",124
"TXT|8|17|84|Lv {야생.레벨} 안정도",125
"HPBAR|8|27|80|{배틀값.적체력}|{적최대}",126
"BOX|64|62|92|30|1",127
"TXT|68|66|84|{species.종찾기(파티.종아이디).이름}",128
"TXT|68|75|84|Lv {파티.레벨} 안정도",129
"HPBAR|68|85|68|{파티.현재체력}|{파티.최대체력}",130
드리프트줄("ally", 파티),131
"PRESSURE|{배틀값.메뉴커서}",132
"BEATFX|{내형질}|{적형질}",133
"BOX|0|104|160|40|0",134
]135
let 본문줄들 = map(0..<사건들.length, i => "TXT|8|{114 + i * 12}|148|{사건들[i]}")136
[for 묶음 in [줄들, 본문줄들] for 줄 in 묶음 => 줄].join("\n")137
}139
function 파티행(현재: state.상태, i: int) -> string {140
let 원 = 현재.파티[i]141
let 행줄 = "PROW|{i}|{표시값(i == 현재.파티커서)}|{원.스프라이트}|{species.종찾기(원.종아이디).이름}|{원.레벨}|{원.현재체력}|{원.최대체력}|{속성이름(원.속성)}|{표시값(i == 현재.초점)}"142
"{행줄}\n{드리프트줄("party:{i}", 원)}"143
}145
function 파티드로우(현재: state.상태, 팔레트: string) -> string {146
let 머리 = "DL1\nPAL|0|{팔레트}\nBOX|0|0|160|144|0\nPTITLE|파티 ({현재.파티.length}/{state.파티최대}) 보관함 {현재.보관함.length}"147
let 행들 = map(0..<현재.파티.length, i => "\n{파티행(현재, i)}").join("")148
"{머리}{행들}\nPCURSOR|{현재.파티커서}\nTXT|8|132|144|A 키우기(초점) B/START 닫기"149
}151
// 고르기 단계는 분기진화와 같은 함수로 후보를 다시 유도하므로 메뉴에서 본 후보가 곧 고정될 후보입니다. 이름은 보여 주되 정확한 외형은 모핑에서 드러납니다.152
// 모핑 단계는 EVOLVE 한 줄과 확정 문구를 내고 느린 모핑 장면은 페이지가 재생합니다.153
function 진화드로우(현재: state.상태, 팔레트: string) -> string {154
let 진 = 현재.진화155
let 활성 = state.초점전투원(현재)156
let 머리 = "DL1\nPAL|0|{팔레트}\nBATTLEBG|0"157
match 진.단계 {158
case "morph" => {159
let 줄들 = [160
머리,161
"EVOLVE|{진.부모형태}|{진.결과형태}|{genome.게놈요약(진.부모게놈)}|{genome.게놈요약(진.결과게놈)}|{진.잠긴분기}|{활성.스프라이트}",162
"CSPR|{활성.스프라이트}|72|48|0|5|{genome.게놈요약(활성.게놈)}",163
"BOX|0|104|160|40|0",164
"TXT|8|112|144|{진.결과형태}(으)로 진화했습니다",165
"TXT|8|124|144|세대 {활성.세대} 게놈 {genome.게놈요약(활성.게놈)}",166
]167
줄들.join("\n")168
}169
case "choose" | "none" => {170
let 후보들 = evolution.후보들유도(진.부모게놈, evolution.계통고유번호(진.부모계통시드), 진.부모계통시드, 진.부모진화카운터)171
let 머리줄들 = [172
머리,173
"BOX|0|0|160|30|0",174
"TXT|8|6|150|진화의 갈림길",175
"TXT|8|18|150|{진.부모형태} 게놈 {genome.게놈요약(진.부모게놈)}",176
"EPARENT|{genome.게놈요약(진.부모게놈)}",177
]178
let 후보줄들 = map(0..<후보들.length, i => "ECAND|{후보들[i].분기}|{표시값(i == 진.선택커서)}|{활성.스프라이트}|{후보들[i].형태이름}|{genome.게놈요약(후보들[i].게놈)}|{후보들[i].스탯부스트}|{evolution.후보힌트(후보들[i])}")179
let 끝줄들 = ["EMENU|{진.선택커서}|{후보들.length}", "TXT|8|132|150|←→ 선택 A 진화 확정"]180
[for 묶음 in [머리줄들, 후보줄들, 끝줄들] for 줄 in 묶음 => 줄].join("\n")181
}182
}183
}185
function 분기문자(분기: int) -> string {186
match 분기 {187
case 0 => "A"188
case 1 => "B"189
case 2 => "C"190
case _ => "?"191
}192
}194
// 같은 고유번호가 여럿이면 옛 동작대로 마지막 노드를 씁니다.195
function 부모노드(노드들: Array<evolution.계통노드>, 자식: evolution.계통노드) -> Option<evolution.계통노드> {196
let 일치들 = 노드들.filter(후보 => 후보.고유번호 == 자식.부모고유번호)197
if 일치들.length == 0 { None } else { Some(일치들[일치들.length - 1]) }198
}200
// 가장 깊은 노드에서 부모를 따라 올라가며 진화마다 고른 분기를 모아 뿌리부터 "A-C-B" 로 잇습니다. 같은 시드와 선택이면 같은 코드라 비교하고 공유할 수 있습니다.201
// 조작된 목록의 순환도 백 걸음에서 멈춥니다.202
function 계통경로코드(노드들: Array<evolution.계통노드>) -> string {203
if 노드들.length == 0 { return "" }204
let mut 지금노드 = 노드들.reduce(노드들[0], (깊은, 노드) => if 노드.세대 > 깊은.세대 { 노드 } else { 깊은 })205
let mut 글자들: Array<string> = []206
while 지금노드.세대 > 0 && 글자들.length < 100 {207
match 부모노드(노드들, 지금노드) {208
case Some(부모) => {209
글자들.push(분기문자(evolution.노드분기(genome.게놈요약파싱(부모.게놈요약), 부모.시드지문, 부모.세대, 지금노드.게놈요약)))210
지금노드 = 부모211
}212
case None => { break }213
}214
}215
[for i in (글자들.length - 1)..0 by -1 => 글자들[i]].join("-")216
}218
// 뿌리에서 첫 자식을 따라 내려가며 분기 글자를 모읍니다. 진화하지 않았으면 "." 이고 분기를 찾지 못하면 추측하지 않고 "?" 입니다.219
function 뿌리경로(노드들: Array<evolution.계통노드>, 뿌리: evolution.계통노드) -> string {220
let mut 지금노드 = 뿌리221
let mut 글자들: Array<string> = []222
while 글자들.length < 100 {223
let 자식들 = 노드들.filter(후보 => 후보.부모고유번호 == 지금노드.고유번호 && 후보.고유번호 != 지금노드.고유번호)224
if 자식들.length == 0 { break }225
let 자식 = 자식들[0]226
let 분기 = evolution.노드분기(genome.게놈요약파싱(지금노드.게놈요약), 지금노드.시드지문, 지금노드.세대, 자식.게놈요약)227
글자들.push(if 분기 >= 0 { 분기문자(분기) } else { "?" })228
지금노드 = 자식229
}230
if 글자들.length == 0 { "." } else { 글자들.join("-") }231
}233
// 숲 공유 코드는 발견 순서의 뿌리마다 한 줄이라 같은 시드와 선택이면 같은 코드입니다.234
function 숲줄들(현재: state.상태) -> Array<string> {235
let 뿌리줄들 = [236
for 노드 in 현재.계통노드들 if 노드.세대 == 0 => "FROW|{genome.출처키(노드.시드지문)}|{뿌리경로(현재.계통노드들, 노드)}"237
]238
[for 묶음 in [["FHEAD|F1|{현재.월드시드}|{표시값(entities.처치된것인가(현재.처치된것들, 보스아이디))}"], 뿌리줄들] for 줄 in 묶음 => 줄]239
}241
// 고른 노드가 진화한 노드면 부모에서 후보를 다시 유도해 가지 않은 길의 힌트를 붙입니다.242
function 형제줄들(노드들: Array<evolution.계통노드>, 선택노드: evolution.계통노드) -> Array<string> {243
if 선택노드.세대 <= 0 { return [] }244
match 부모노드(노드들, 선택노드) {245
case Some(부모) => {246
let 힌트들 = evolution.형제힌트들(genome.게놈요약파싱(부모.게놈요약), 부모.시드지문, 부모.세대, 선택노드.게놈요약)247
if 힌트들 == "" { [] } else { ["DEXSIBLING|{힌트들}"] }248
}249
case None => []250
}251
}253
// 선은 노드 아래에 깔리도록 노드보다 먼저 냅니다. 상세의 시드 지문이 이 시드가 이 나무를 키웠다는 공유의 열쇠입니다.254
function 도감드로우(현재: state.상태, 팔레트: string) -> string {255
let 노드들 = 현재.계통노드들256
let 레이아웃 = lineagedex.레이아웃계산(노드들)257
let 배치들 = 레이아웃.노드들258
let 커서 = if 배치들.length > 0 { 범위가두기(현재.도감커서, 0, 배치들.length - 1) } else { 0 }259
let 선택인덱스 = if 배치들.length > 0 { 배치들[커서].인덱스 } else { -1 }260
let 최고세대 = 노드들.reduce(0, (최고, 노드) => if 노드.세대 > 최고 { 노드.세대 } else { 최고 })261
let 경로코드 = 계통경로코드(노드들)262
let 머리줄들 = ["DL1", "PAL|0|{팔레트}", "BOX|0|0|160|18|0", "TXT|6|5|90|계통도감", "DEXSTAT|{최고세대}|{노드들.length}|{현재.월드시드}"]263
let 경로줄들 = if 경로코드 != "" { ["DEXPATH|{경로코드}|{현재.월드시드}"] } else { [] }264
let 승리줄들 = if entities.처치된것인가(현재.처치된것들, 보스아이디) { ["DEXWIN|1"] } else { [] }265
let 선줄들 = [266
for 노드 in 배치들 if 노드.부모인덱스 >= 0 && 노드.부모인덱스 < 배치들.length => "DEXEDGE|{배치들[노드.부모인덱스].x}|{배치들[노드.부모인덱스].y}|{노드.x}|{노드.y}"267
]268
let 노드줄들 = [269
for 노드 in 배치들 => "DEXNODE|{노드.인덱스}|{노드.부모인덱스}|{노드.x}|{노드.y}|{노드.게놈요약}|{노드.형태이름}|{노드.세대}|{표시값(노드.인덱스 == 선택인덱스)}"270
]271
let 상세줄 = if 배치들.length > 0 {272
let 선택 = 배치들[커서]273
"DEXDETAIL|{선택.형태이름}|{선택.세대}|{선택.게놈요약}|{선택.시드지문}|{genome.출처(선택.시드지문)}"274
} else {275
"TXT|8|72|144|아직 계통이 없습니다"276
}277
let 형제들 = if 배치들.length > 0 { 형제줄들(노드들, 노드들[선택인덱스]) } else { [] }278
let 묶음들 = [279
머리줄들,280
경로줄들,281
승리줄들,282
숲줄들(현재),283
["DEXVIEW|{레이아웃.너비}|{레이아웃.높이}|{배치들.length}"],284
선줄들,285
노드줄들,286
["DEXCURSOR|{커서}", 상세줄],287
형제들,288
["TXT|6|136|150|←→↑↓ 선택 B/SEL 닫기"],289
]290
[for 묶음 in 묶음들 for 줄 in 묶음 => 줄].join("\n")291
}293
// 내레이션의 줄바꿈은 | 로 바꿔 지시 한 줄로 유지합니다. 페이지는 목록을 줄 단위로 읽습니다.294
function 스토리드로우(현재: state.상태, 팔레트: string) -> string {295
let 비트 = 최하위비트(현재.스토리.대기)296
"{필드줄들(현재, 팔레트).join("\n")}\nSTORY|{비트}|{비트내레이션(비트).replace("\n", "|")}"297
}299
export function 드로우리스트(현재: state.상태) -> string {300
let 팔레트 = 연못팔레트()301
match 현재.장면 {302
case "battle" => 배틀드로우(현재, 팔레트)303
case "party" => 파티드로우(현재, 팔레트)304
case "evolve" => 진화드로우(현재, 팔레트)305
case "dex" => 도감드로우(현재, 팔레트)306
case "story" => 스토리드로우(현재, 팔레트)307
case "field" => 필드줄들(현재, 팔레트).join("\n")308
}309
}SHA256 3a1e53a4f2a12a016e83703b104e30f64a949d58e6b234b4f4d048b628254add