STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/slow-monster/core/auto.tpz
Topaz · 8687 B · Original file
1
// 자동 진행 정책입니다. 상태를 보고 다음 입력 하나를 정하고, 같은 스텝 엔진이 그 입력을 적용합니다.2
// 필드의 우선순위는 위험하면 인물에게 회복, 아니면 가장 가까운 보스 아닌 야생, 다 비우면 연못은 워프로 내려가고 심층은 보스입니다. 이동은 목표 칸에서 퍼지는 너비 우선 거리장으로 정합니다.3
// 이웃 순서를 아래, 위, 왼쪽, 오른쪽으로 고정해 같은 상태는 늘 같은 입력입니다.4
// 분기 진화는 기본으로 멈춰 플레이어가 고르게 하고, 자동 고르기는 공개된 후보 수만 씁니다. 페이지는 타이머로 auto 를 보내고 그 사이를 그리기만 합니다.5
// Auto-play policy: picks the next ordinary input from the state; branch evolution pauses for the player by default.7
import data.maps { 맵너비, 맵높이, 맵타일들, 타일에서, 워프목록, 워프, 타일_물, 타일_나무 }8
import data.entities9
import core.state10
import core.creature11
import core.evolution { 후보수, 계통고유번호, 진화시작 }12
import core.rng { 난수범위 }13
import core.util { 범위가두기 }15
// 보스는 보통 야생 목표에서 빼고 다른 야생을 다 비운 뒤의 마지막 상대로만 다룹니다.16
let 보스아이디 = 1418
// 아래, 위, 왼쪽, 오른쪽입니다. 이 순서가 같은 거리의 이웃 중 무엇을 고를지 정합니다.19
let 이웃x차 = [0, 0, -1, 1]20
let 이웃y차 = [1, -1, 0, 0]21
let 이웃방향 = ["down", "up", "left", "right"]23
// 목표 칸은 막혀 있어도 거리장의 출발점으로 따로 넣습니다.24
function 통행가능한가(타일들: Array<int>, 맵아이디: string, x: int, y: int, 처치된것들: Array<int>) -> bool {25
let 타일 = 타일에서(타일들, x, y)26
타일 != 타일_물 && 타일 != 타일_나무 && !entities.활성엔티티칸인가(맵아이디, x, y, 처치된것들)27
}29
function 맵안인가(x: int, y: int) -> bool {30
x >= 0 && y >= 0 && x < 맵너비 && y < 맵높이31
}33
// 목표 칸이 0 이고 닿지 못하는 칸은 -1 입니다.34
function 거리장(목표칸들: Array<int>, 타일들: Array<int>, 맵아이디: string, 처치된것들: Array<int>) -> Array<int> {35
let 칸수 = 맵너비 * 맵높이36
let mut 거리 = [for i in 0..<칸수 => -1]37
let mut 대기열: Array<int> = []38
for 칸 in 목표칸들 {39
if 칸 >= 0 && 칸 < 칸수 && 거리[칸] == -1 {40
거리[칸] = 041
대기열.push(칸)42
}43
}44
let mut 머리 = 045
while 머리 < 대기열.length {46
let 칸 = 대기열[머리]47
머리 += 148
for k in 0..<4 {49
let x = 칸 % 맵너비 + 이웃x차[k]50
let y = 칸 / 맵너비 + 이웃y차[k]51
if 맵안인가(x, y) && 거리[y * 맵너비 + x] == -1 && 통행가능한가(타일들, 맵아이디, x, y, 처치된것들) {52
거리[y * 맵너비 + x] = 거리[칸] + 153
대기열.push(y * 맵너비 + x)54
}55
}56
}57
거리58
}60
// 지금 칸보다 확실히 가까운 이웃 중 고정 순서의 첫째로 갑니다. 그런 이웃이 없으면 "" 입니다.61
function 다가가기방향(거리: Array<int>, x: int, y: int) -> string {62
let 내칸 = y * 맵너비 + x63
let mut 최선거리 = if 내칸 >= 0 && 내칸 < 거리.length { 거리[내칸] } else { -1 }64
let mut 최선방향 = ""65
for k in 0..<4 {66
let 이웃x = x + 이웃x차[k]67
let 이웃y = y + 이웃y차[k]68
if 맵안인가(이웃x, 이웃y) {69
let 이웃거리 = 거리[이웃y * 맵너비 + 이웃x]70
if 이웃거리 >= 0 && (최선거리 < 0 || 이웃거리 < 최선거리) {71
최선거리 = 이웃거리72
최선방향 = 이웃방향[k]73
}74
}75
}76
최선방향77
}79
function 엔티티칸(대상: entities.엔티티) -> int {80
대상.y * 맵너비 + 대상.x81
}83
function 야생칸들(맵아이디: string, 처치된것들: Array<int>) -> Array<int> {84
entities.엔티티목록(맵아이디).filter(대상 => entities.야생인가(대상) && 대상.아이디 != 보스아이디 && !entities.처치된것인가(처치된것들, 대상.아이디)).map(엔티티칸)85
}87
function 보스칸(맵아이디: string, 처치된것들: Array<int>) -> Array<int> {88
entities.엔티티목록(맵아이디).filter(대상 => 대상.아이디 == 보스아이디 && !entities.처치된것인가(처치된것들, 대상.아이디)).map(엔티티칸)89
}91
function 인물칸들(맵아이디: string) -> Array<int> {92
entities.엔티티목록(맵아이디).filter(entities.인물인가).map(엔티티칸)93
}95
function 워프칸들(맵아이디: string) -> Array<int> {96
워프목록(맵아이디).map(대상 => 대상.y * 맵너비 + 대상.x)97
}99
function 위험한가(활성: creature.전투원) -> bool {100
활성.현재체력 * 3 < 활성.최대체력101
}103
// 의도 0 은 멈춰 플레이어가 고르고, 1 은 첫 가지, 그 밖은 진화 난수 띠에서 뽑은 가지입니다. 커서를 그 가지로 옮긴 뒤 잠급니다.104
function 분기고르기(진: state.진화상태, 의도: int) -> string {105
if 의도 == 0 { return "" }106
let 엔티티 = 계통고유번호(진.부모계통시드)107
let 수 = 후보수(엔티티, 진.부모계통시드, 진.부모진화카운터)108
if 수 <= 1 { return "a" }109
let 뽑은분기 = if 의도 == 1 { 0 } else { 난수범위("auto_evolve", 엔티티, 진화시작 + 진.부모진화카운터, 진.부모계통시드, 0, 수 - 1) }110
let 목표분기 = 범위가두기(뽑은분기, 0, 수 - 1)111
if 진.선택커서 < 목표분기 { "right" } else if 진.선택커서 > 목표분기 { "left" } else { "a" }112
}114
function 진화자동(현재: state.상태, 의도: int) -> string {115
match 현재.진화.단계 {116
case "morph" => "a"117
case "none" => ""118
case "choose" => 분기고르기(현재.진화, 의도)119
}120
}122
// 지나가다 보스를 일찍 들이받지 않도록 보스는 다른 야생을 다 비운 뒤에만 A 로 맞붙습니다.123
// 목표가 막힌 엔티티면 옆 칸에서 마주 본 뒤 다음 틱에 A 를 누르고, 워프면 그 칸으로 들어갑니다.124
function 필드결정(현재: state.상태) -> string {125
let 위험 = 위험한가(state.초점전투원(현재))126
let 보통야생들 = 야생칸들(현재.맵아이디, 현재.처치된것들)127
let 앞 = entities.활성엔티티에서(현재.맵아이디, state.마주x(현재.플레이어), state.마주y(현재.플레이어), 현재.처치된것들)128
if entities.야생인가(앞) && 앞.아이디 != 보스아이디 { return "a" }129
if 앞.아이디 == 보스아이디 && 보통야생들.length == 0 { return "a" }130
if entities.인물인가(앞) && 위험 { return "a" }131
let 목표 = if 위험 {132
인물칸들(현재.맵아이디)133
} else if 보통야생들.length > 0 {134
보통야생들135
} else if 현재.맵아이디 == "deep" {136
보스칸(현재.맵아이디, 현재.처치된것들)137
} else {138
워프칸들(현재.맵아이디)139
}140
if 목표.length == 0 { return "" }141
let 거리 = 거리장(목표, 맵타일들(현재.맵아이디), 현재.맵아이디, 현재.처치된것들)142
다가가기방향(거리, 현재.플레이어.x, 현재.플레이어.y)143
}145
// 전투는 메뉴 커서 기본값이 싸우기라 A 만으로 끝까지 갑니다. 이야기 카드는 페이지가 읽을 시간을 두고 넘깁니다.146
// "" 는 멈춤이며 수동 진화 갈림길처럼 플레이어의 몫입니다.147
export function 자동결정(현재: state.상태, 의도: int) -> string {148
match 현재.장면 {149
case "battle" | "story" => "a"150
case "evolve" => 진화자동(현재, 의도)151
case "party" => "start"152
case "dex" => "select"153
case "field" => 필드결정(현재)154
}155
}157
// 자동결정과 같은 우선순위를 글로만 읽어 HUD 가 실제 행동과 어긋나지 않게 합니다.158
function 필드목표(현재: state.상태) -> string {159
if 위험한가(state.초점전투원(현재)) { return "회복하러 가는 중" }160
if 야생칸들(현재.맵아이디, 현재.처치된것들).length > 0 { return "야생을 찾아가는 중" }161
if 현재.맵아이디 == "deep" {162
return if 보스칸(현재.맵아이디, 현재.처치된것들).length > 0 { "심연군주에게 가는 중" } else { "정복 완료" }163
}164
if 워프칸들(현재.맵아이디).length > 0 { "더 깊은 곳으로 가는 중" } else { "둘러보는 중" }165
}167
export function 자동목표(현재: state.상태) -> string {168
match 현재.장면 {169
case "battle" => "야생과 싸우는 중"170
case "evolve" => "진화의 갈림길"171
case "party" => "파티를 보는 중"172
case "dex" => "계통을 보는 중"173
case "story" | "field" => 필드목표(현재)174
}175
}SHA256 879c5a53f338bd45233f0a21d688dbf902c462c05ef75d1305acf09b774000ad