tarinai/js/weather_system.js
2026-07-18 22:15:26 +09:00

93 lines
3.3 KiB
JavaScript

"use strict";
(function (global) {
const DomainIds = global.TarinaiDomainIds;
if (!DomainIds) throw new Error("TarinaiDomainIds must be loaded before weather_system.js");
const WEATHER_IDS = DomainIds.WEATHER_IDS;
function canonicalWeather(value = "sunny") {
return DomainIds.canonicalWeatherId(value);
}
function chooseNext(current = "sunny") {
const id = canonicalWeather(current);
const choices = id === "light_rain"
? WEATHER_IDS.filter(weather => weather !== "light_rain")
: WEATHER_IDS;
return pick(choices);
}
function resetTimer() {
return rand(CONFIG.weatherChangeMin, CONFIG.weatherChangeMax);
}
function fixedWeatherFor(worldRef) {
const fixed = global.TarinaiGround.fixedWeather(worldRef?.groundType || "soil") || "";
return fixed || null;
}
function enforceFixedWeather(worldRef, options = {}) {
const fixed = fixedWeatherFor(worldRef);
if (!fixed || !worldRef) return false;
const previous = canonicalWeather(worldRef.weather || "sunny");
worldRef.weather = canonicalWeather(fixed);
worldRef.nextWeatherChange = resetTimer();
if (previous !== worldRef.weather && options.silentLog !== true) {
worldRef.log?.(`\u5929\u6C17: ${weatherLabel(worldRef.weather)}`);
}
return true;
}
function shouldDropRainWater(worldRef, dt = 0) {
return Boolean(
worldRef?.weather === "light_rain" &&
Math.random() < Math.max(0, Number(dt) || 0) * 0.24
);
}
function createRainWaterItem(worldRef, opts = {}) {
const px = Number(opts?.x);
const py = Number(opts?.y);
const hasX = Number.isFinite(px);
const hasY = Number.isFinite(py);
const x = hasX ? px : rand(58, worldRef.w - 58);
const y = hasY ? py : rand(58, worldRef.h - 58);
const drop = new Item("water", x, y);
drop.rainDrop = opts?.rainDrop === true || (!hasX && !hasY);
const amount = Number(opts?.amount);
drop.amount = Number.isFinite(amount) ? amount : rand(24, 58);
// Rain drops, placed water drops, and water-balloon splashes all use the
// same Item("water") object. Optional velocity only gives that same water
// item a short ballistic motion; it is not a separate splash item type.
const vx = Number(opts?.vx);
const vy = Number(opts?.vy);
if (Number.isFinite(vx) || Number.isFinite(vy)) {
drop.prevX = drop.x;
drop.prevY = drop.y;
drop.vx = Number.isFinite(vx) ? vx : 0;
drop.vy = Number.isFinite(vy) ? vy : 0;
}
const flight = Number(opts?.splashFlightTimer ?? opts?.flightTimer);
if (Number.isFinite(flight) && flight > 0) drop.splashFlightTimer = flight;
return drop;
}
function applyNextWeather(worldRef) {
if (!worldRef) return null;
if (enforceFixedWeather(worldRef)) return worldRef.weather || "sunny";
const previous = canonicalWeather(worldRef.weather || "sunny");
const next = chooseNext(previous);
worldRef.weather = canonicalWeather(next);
worldRef.nextWeatherChange = resetTimer();
if (next !== previous) {
worldRef.log?.(`\u5929\u6C17: ${weatherLabel(next)}`);
}
return next;
}
global.TarinaiWeatherSystem = Object.freeze({
enforceFixedWeather,
shouldDropRainWater,
createRainWaterItem,
applyNextWeather,
});
})(typeof window !== "undefined" ? window : globalThis);