본문으로 건너뛰기

효과 VM

clover 문서 목록으로


조커 150종·소모품 52종·바우처 32종·태그 24종·보스 28종·덱 15종의 효과를 시트의 행으로 적습니다. 웹과 유니티는 그 행을 해석하는 두 구현이고, 이 문서가 둘의 정본입니다.

효과가 코드에 있으면 두 구현이 갈라집니다. 데이터에 있으면 갈라질 수 없습니다 — 그것이 이 샘플의 목적이므로, 코드에 남는 것을 세어서 지표로 삼습니다.

무엇어디
변종 전량과 값의 뜻명령 목록
런 상태의 모양상태와 세이브

효과 행 하나의 모양

효과는 소유자에 붙는 멀티 로우입니다. 조커 하나가 효과 행 여러 개를 가집니다.

:field owner order trigger condition.$type ... operation.$type ... scope
무엇
owner이 효과를 가진 것. foreign Joker · foreign Tarot · …
order같은 소유자 안의 순서. 0부터
trigger언제 보는가
condition볼 때 무엇이 참이어야 하는가. 다형
operation무엇을 하는가. 다형
scope누구에게 하는가

조건이 없는 효과는 Always를 적습니다. 빈 칸으로 두지 않습니다 — 빈 칸이면 「조건이 없다」와 「적는 것을 잊었다」가 구분되지 않습니다.

결정론

리플레이 대조가 이 샘플의 판정 기준이므로, 두 구현이 비트 단위로 같은 값을 내야 합니다. 셋을 규격으로 못박습니다.

정수인 배수와 칩

배수는 만분율 int 입니다. ×1.515000이고 +0.252500입니다. 칩은 정수 그대로 입니다.

부동소수를 쓰지 않는 이유는 하나입니다 — X0.1을 30번 곱하는 조커가 있고, 그 누적 오차가 언어와 최적화 수준에 따라 달라집니다. 만분율 정수는 어디서도 같습니다.

곱은 (a * b) / 10000이고 내림입니다. 나눗셈은 0 방향이 아니라 음의 무한 방향으로 내립니다 — 음수 배수가 생기는 조커는 없지만, 규격에 방향이 없으면 두 언어가 다른 답을 낼 자리가 남습니다.

자체 난수

PCG32를 양쪽에 같은 수식으로 구현합니다. 언어 표준 난수를 쓰지 않습니다.

스트림을 분리합니다 — 상점 추첨과 카드 섞기와 조커의 확률 발동이 같은 스트림을 쓰면, 상점에서 리롤 한 번이 득점의 확률을 바꿉니다. 스트림 목록은 RngStream 테이블에 있습니다.

스트림쓰는 곳
shuffle덱 섞기
shop_slot · shop_rarity · shop_voucher상점 구성
pack팩 내용
joker_proc조커의 확률 발동
card_procGlass 파괴 · Lucky 발동
boss보스 추첨
tag태그 추첨
misprintsmudge 의 난수 배수

시드 하나에서 스트림마다 다른 상태를 파생합니다. 파생 방식은 시드 문자열 + 스트림 이름의 해시이고, 해시도 우리 것입니다 — 언어의 hashCode를 쓰지 않습니다.

분수로 적는 확률

1/40.25로 적지 않고 분자 1과 분모 4로 적습니다. 판정은 rng.below(분모) < 분자 입니다.

loaded_dice가 모든 확률을 2배로 하는데, 그것은 분자를 2배로 하는 것입니다. 실수로 두면 0.25 → 0.52/4가 같은 값이지만, 분자를 3배로 하는 것이 붙으면 갈라집니다.

평가 순서

순서가 규칙입니다. 같은 패에서 다른 점수가 나오면 그 자리가 결함입니다.

  1. 족보를 판정합니다. four_knuckles · stepping_stone · smudged_pane · face_pattern이 판정 규칙을 바꿉니다 — 판정 전에 적용됩니다
  2. 족보 레벨로 기본 칩과 기본 배수를 계산합니다. The Flint가 여기서 절반으로 만듭니다
  3. 득점 카드를 왼쪽에서 오른쪽으로 처리합니다. 카드 한 장에 대해
    1. 랭크 칩값 → 강화 → 에디션 → 인장
    2. OnCardScored 효과를 조커 슬롯 왼쪽에서 오른쪽으로
    3. 재발동이 걸려 있으면 1~2를 그 자리에서 반복합니다
  4. 패에 든 카드를 왼쪽에서 오른쪽으로. OnCardHeld 효과가 같은 방식으로 붙습니다
  5. OnHandPlayed 효과를 조커 슬롯 왼쪽에서 오른쪽으로
  6. 칩 × 배수. 여기서 한 번만 곱합니다

재발동이 그 자리에서 즉시인 것이 중요합니다. 카드 3장을 다 처리한 뒤에 재발동하면 누적형 조커의 값이 달라집니다.

규칙 다시 세우기

규칙은 누적이 아닙니다. 기본값에서 시작해 지금 있는 것들을 다시 얹습니다. 누적으로 두면 원인이 생기거나 사라졌을 때 아무도 다시 계산하지 않고, 실제로 셋이 조용히 틀렸습니다 — 상점에서 산 조커의 규칙이 아예 걸리지 않았고, 보스 효과 17개가 아예 걸리지 않았고, 태그가 뽑는 순간 즉시 그리고 영원히 걸렸습니다.

쌓는 차례가 규칙입니다.

순서무엇
1기본값과 스테이크
2한 번 걸리고 남는 것들 — 유령 카드처럼 원인이 이미 없는 것들입니다
3지금 있는 것들의 Passive — 덱 · 바우처 · 보스 · 조커 · 태그
4이번 라운드에만 걸린 것

다시 세우는 자리는 여섯입니다. 런 시작 · 조커를 살 때 · 조커를 팔 때 · 바우처를 살 때 · 라운드가 시작할 때 · 라운드가 끝날 때.

보스는 판을 두는 동안에만 규칙에 들어갑니다. 블라인드만 보면 라운드가 끝나고 상점에 있는 동안에도 남습니다 — 다음 블라인드를 고를 때까지 그 값이 그대로이기 때문입니다.

기간

duration언제까지
PermanentPassive 면 원인이 있는 동안. 아니면 런 끝까지
ThisRound이번 라운드가 끝날 때까지. 상점에서 걸면 다음 라운드가 시작할 때까지
NextRound다음 라운드 한 번

태그

태그는 들고 있는 것입니다. 블라인드를 건너뛰면 하나 얻고, 그 태그가 뜻을 가지는 시점이 올 때 터지고 없어집니다.

trigger언제어느 태그
OnUse뽑는 그 자리에서돈·족보 레벨·팩을 주는 것들, 보스 다시 뽑기, juggle
OnShopEnter다음 상점에 들어갈 때조커와 바우처를 놓는 것들, coupon · d6
OnBossDefeated보스를 잡을 때investment

juggleOnUse 이면서 NextRound 입니다 — 건너뛰는 그 자리에서 걸리지만, 손패가 늘어나는 것은 다음 라운드 한 번뿐입니다.

Custom 탈출구

선언으로 적을 수 없는 효과는 Custom(handler_id)로 두고, 양쪽 구현에 같은 이름의 처리를 씁니다.

이것은 결함의 자리표입니다. Custom 하나는 두 구현이 갈라질 수 있는 자리 하나이고, 개수를 대조표에 적습니다. 지워지면 그 자리가 닫힙니다.

Custom을 쓸 때 규칙 둘을 지킵니다.

  • handler_id는 시트에 적힙니다. 코드가 조커 id로 분기하지 않습니다 — 그러면 데이터를 고쳐도 코드가 따라오지 않습니다
  • 문서에 무엇을 하는지 적습니다. 두 구현이 문서를 보고 각각 작성합니다. 한쪽을 보고 다른 쪽을 옮기면 같은 오해가 두 번 들어갑니다

EOD