Files

93 lines
3.8 KiB
GDScript

extends RefCounted
## Approved initial weather values A13 (P07-01 SPEC sections 2 and 4). The
## timeline stays a pure state model; this file owns the draw and the effects.
const DAY_MINUTES := 1440
const DRAW_MINUTE_OF_DAY := 360
const FIRST_START_MINUTE_OF_DAY := 360
const START_SLOT_MINUTES := 30
const START_SLOTS := 29
## Rolls 0..99: clear 0-59, rain 60-84, mana fog 85-94, storm 95-99.
const CLEAR_BELOW := 60
const RAIN_BELOW := 85
const FOG_BELOW := 95
const DURATION_MINUTES := {&"rain": 360, &"mana_fog": 240, &"storm": 120}
const RAIN_ARRIVAL_PERCENT := 50
const RAIN_HOLD_MAX_MINUTES := 240
const FOG_MANA_NUMERATOR := 12
const FOG_MANA_DENOMINATOR := 10
const FOG_INTIMACY_NUMERATOR := 3
const FOG_INTIMACY_DENOMINATOR := 2
static func is_draw_minute(absolute_minute: int) -> bool:
return absolute_minute >= 0 and posmod(absolute_minute, DAY_MINUTES) == DRAW_MINUTE_OF_DAY
static func day_of(absolute_minute: int) -> int:
return int(floor(float(absolute_minute) / DAY_MINUTES)) if absolute_minute >= 0 else -1
## One draw per day from the saved weather stream. A clear day consumes one
## roll; other days also consume one start-slot roll.
static func draw(rng: DeterministicRng, day: int) -> Dictionary:
var roll := rng.randi_range(0, 99)
var kind: StringName = &"clear"
if roll >= FOG_BELOW:
kind = &"storm"
elif roll >= RAIN_BELOW:
kind = &"mana_fog"
elif roll >= CLEAR_BELOW:
kind = &"rain"
if kind == &"clear":
return {&"day": day, &"kind": kind, &"roll": roll, &"slot": -1, &"start": -1, &"end": -1}
var slot := rng.randi_range(0, START_SLOTS - 1)
var start := day * DAY_MINUTES + FIRST_START_MINUTE_OF_DAY + slot * START_SLOT_MINUTES
return {&"day": day, &"kind": kind, &"roll": roll, &"slot": slot,
&"start": start, &"end": start + int(DURATION_MINUTES[kind])}
## A saved day's draw must be this rule's own result (SPEC section 5.3): the roll
## picks the kind, a clear day has no slot, and any other day starts on its slot
## and lasts its kind's duration. An empty draw (no day drawn yet) is consistent.
static func is_consistent_draw(draw: Dictionary) -> bool:
if draw.is_empty():
return true
var roll := int(draw[&"roll"])
var kind := StringName(draw[&"kind"])
var expected: StringName = &"clear"
if roll >= FOG_BELOW:
expected = &"storm"
elif roll >= RAIN_BELOW:
expected = &"mana_fog"
elif roll >= CLEAR_BELOW:
expected = &"rain"
if kind != expected:
return false
if kind == &"clear":
return int(draw[&"slot"]) == -1 and int(draw[&"start"]) == -1 and int(draw[&"end"]) == -1
var slot := int(draw[&"slot"])
var start := int(draw[&"day"]) * DAY_MINUTES + FIRST_START_MINUTE_OF_DAY + slot * START_SLOT_MINUTES
return slot >= 0 and slot < START_SLOTS and int(draw[&"start"]) == start \
and int(draw[&"end"]) == start + int(DURATION_MINUTES[kind])
static func arrival_percent(kind: StringName) -> int:
return RAIN_ARRIVAL_PERCENT if kind == &"rain" else 100
static func mana_yield(base_amount: int, kind: StringName) -> int:
if kind != &"mana_fog":
return base_amount
return int(floor(float(base_amount * FOG_MANA_NUMERATOR) / FOG_MANA_DENOMINATOR))
static func fog_intimacy_increase(increase: int) -> int:
if increase <= 0:
return increase
return int(floor(float(increase * FOG_INTIMACY_NUMERATOR) / FOG_INTIMACY_DENOMINATOR))
## Japanese HUD guidance for the current kind (SPEC section 4.6).
static func effect_text(kind: StringName) -> String:
match kind:
&"rain":
return "雨: 新規来客が半分になり、帰る予定の客は雨が止むまで(最大4時間)残ります。"
&"storm":
return "嵐: 新規来客はありません。館内の客は残ります。"
&"mana_fog":
return "マナ霧: 世界樹のマナ産出が1.2倍、成人確認済みの客の性欲の増え方が1.5倍になります。"
return "晴れ: 天候の影響はありません。"