93 lines
3.8 KiB
GDScript
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 "晴れ: 天候の影響はありません。"
|