@adatechnology/game-weather 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-EODWEOJG.js +1301 -0
- package/dist/chunk-EODWEOJG.js.map +1 -0
- package/dist/index.d.ts +4 -452
- package/dist/index.global.js +239 -124
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +51 -1248
- package/dist/index.js.map +1 -1
- package/dist/testing-B4pjKlOJ.d.ts +453 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +9 -0
- package/dist/testing.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { Vec3, AudioSink, EnvironmentBus, EnvironmentState, TestableModule, ModuleLayer, ModuleHost } from '@adatechnology/game-contracts';
|
|
2
|
+
|
|
3
|
+
/** Every number of a weather state; these are the values that blend between two states. */
|
|
4
|
+
type StateValues = {
|
|
5
|
+
/** Rain in mm/h. */
|
|
6
|
+
readonly rain: number;
|
|
7
|
+
/** Drops the renderer keeps in the air. */
|
|
8
|
+
readonly drops: number;
|
|
9
|
+
/** 0..1: rain sheets (curtains of water) passing. */
|
|
10
|
+
readonly sheets: number;
|
|
11
|
+
/** Wind in m/s along x and z (the rain slants by windX). */
|
|
12
|
+
readonly windX: number;
|
|
13
|
+
readonly windZ: number;
|
|
14
|
+
/** Strongest gust in m/s. */
|
|
15
|
+
readonly gust: number;
|
|
16
|
+
/** Linear fog far distance in metres (render). */
|
|
17
|
+
readonly fogFar: number;
|
|
18
|
+
/** Exponential-squared fog density; 0 means linear fog. */
|
|
19
|
+
readonly fogDensity: number;
|
|
20
|
+
/** Multiplies the sky colour. */
|
|
21
|
+
readonly skyScale: number;
|
|
22
|
+
/** How far one sees for gameplay, in metres (not the render fog). */
|
|
23
|
+
readonly visibility: number;
|
|
24
|
+
/** Relative humidity, 0..1, before the climate offset. */
|
|
25
|
+
readonly humidity: number;
|
|
26
|
+
/** Added to the climate's temperature of the moment. */
|
|
27
|
+
readonly temperatureDeltaC: number;
|
|
28
|
+
/** Ground wetness this state wets up to, 0..1. */
|
|
29
|
+
readonly wetness: number;
|
|
30
|
+
/** Multiplies the radius at which listeners hear (rain masks). */
|
|
31
|
+
readonly hearingScale: number;
|
|
32
|
+
/** Multiplies the noise footsteps make. */
|
|
33
|
+
readonly footstepScale: number;
|
|
34
|
+
/** Multiplies how fast wet ground dries. */
|
|
35
|
+
readonly dryingScale: number;
|
|
36
|
+
/** Acceleration in m/s² that wind puts on loose debris. */
|
|
37
|
+
readonly debrisPush: number;
|
|
38
|
+
/** 0..1: heat haze. */
|
|
39
|
+
readonly haze: number;
|
|
40
|
+
readonly mistPerSecond: number;
|
|
41
|
+
readonly ashPerSecond: number;
|
|
42
|
+
};
|
|
43
|
+
type StateValueKey = keyof StateValues;
|
|
44
|
+
/** Strikes of a state: seconds between them, seconds to the first one, how far they land and how long a flash reveals the scene. */
|
|
45
|
+
type LightningConfig = {
|
|
46
|
+
readonly interval: readonly [min: number, max: number];
|
|
47
|
+
readonly first: readonly [min: number, max: number];
|
|
48
|
+
readonly distance: readonly [min: number, max: number];
|
|
49
|
+
readonly reveal: number;
|
|
50
|
+
};
|
|
51
|
+
type WeatherState = StateValues & {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
/** Where each number comes from: a team source and line, or the design decision that set it. */
|
|
54
|
+
readonly sources: readonly string[];
|
|
55
|
+
readonly lightning?: LightningConfig;
|
|
56
|
+
};
|
|
57
|
+
type Climate = {
|
|
58
|
+
readonly id: string;
|
|
59
|
+
readonly sources: readonly string[];
|
|
60
|
+
readonly start: string;
|
|
61
|
+
/** Game hours a state lasts, drawn uniformly. */
|
|
62
|
+
readonly dwellHours: readonly [min: number, max: number];
|
|
63
|
+
/** Game minutes a change takes to blend. */
|
|
64
|
+
readonly blendMinutes: number;
|
|
65
|
+
/** Where fronts travel to: [1, 0] is toward +x, so they arrive from the west. */
|
|
66
|
+
readonly frontDirection: readonly [x: number, z: number];
|
|
67
|
+
readonly temperatureC: {
|
|
68
|
+
readonly mean: number;
|
|
69
|
+
readonly swing: number;
|
|
70
|
+
};
|
|
71
|
+
readonly humidityOffset: number;
|
|
72
|
+
/** From state to the weights of the next one. A state that is not here, or has nothing reachable, stays. */
|
|
73
|
+
readonly transitions: Readonly<Record<string, Readonly<Record<string, number>>>>;
|
|
74
|
+
};
|
|
75
|
+
type DeepPartial<TValue> = {
|
|
76
|
+
readonly [Key in keyof TValue]?: NonNullable<TValue[Key]> extends readonly unknown[] ? TValue[Key] : NonNullable<TValue[Key]> extends object ? DeepPartial<NonNullable<TValue[Key]>> : TValue[Key];
|
|
77
|
+
};
|
|
78
|
+
type WeatherEffects = {
|
|
79
|
+
/** Gameplay visibility in metres. */
|
|
80
|
+
readonly visibility: number;
|
|
81
|
+
/** Multiplies how far something sees (`perc[0]` of the game): weather times `1 - 0.4 × nightFactor`. */
|
|
82
|
+
readonly sightScale: number;
|
|
83
|
+
/** Multiplies how far something hears (rain masks sound). */
|
|
84
|
+
readonly hearingScale: number;
|
|
85
|
+
/** Multiplies the noise a footstep makes. */
|
|
86
|
+
readonly footstepScale: number;
|
|
87
|
+
readonly heatIndexC: number;
|
|
88
|
+
/** 0..1 on the NOAA bands 27, 32, 41 and 54 °C. */
|
|
89
|
+
readonly heatStress: number;
|
|
90
|
+
/** Seconds a flash lights the scene (0 where there is no lightning). */
|
|
91
|
+
readonly lightningReveal: number;
|
|
92
|
+
readonly dryingScale: number;
|
|
93
|
+
readonly debrisPush: number;
|
|
94
|
+
};
|
|
95
|
+
type Strike = {
|
|
96
|
+
readonly id: number;
|
|
97
|
+
/** Simulation seconds since the weather started. */
|
|
98
|
+
readonly time: number;
|
|
99
|
+
/** Game seconds since the weather started. */
|
|
100
|
+
readonly gameSeconds: number;
|
|
101
|
+
/** Metres from the listener. */
|
|
102
|
+
readonly distance: number;
|
|
103
|
+
/** Degrees clockwise from north. */
|
|
104
|
+
readonly bearing: number;
|
|
105
|
+
/** Seconds from the flash to the thunder: distance / 343. */
|
|
106
|
+
readonly thunderDelay: number;
|
|
107
|
+
};
|
|
108
|
+
type WeatherSample = StateValues & {
|
|
109
|
+
readonly gameSeconds: number;
|
|
110
|
+
/** 0 is midnight, 0.5 is noon. */
|
|
111
|
+
readonly dayFraction: number;
|
|
112
|
+
readonly hour: number;
|
|
113
|
+
readonly nightFactor: number;
|
|
114
|
+
/** 0..1 sun height (the game's `s`). */
|
|
115
|
+
readonly sun: number;
|
|
116
|
+
/** The state being blended to (or settled in), and the one it is leaving while the blend runs. */
|
|
117
|
+
readonly state: string;
|
|
118
|
+
readonly previous: string | undefined;
|
|
119
|
+
/** 0..1: how far the blend has come; 1 is settled. */
|
|
120
|
+
readonly blend: number;
|
|
121
|
+
readonly front: {
|
|
122
|
+
readonly direction: readonly [x: number, z: number];
|
|
123
|
+
readonly progress: number;
|
|
124
|
+
};
|
|
125
|
+
readonly windSpeed: number;
|
|
126
|
+
readonly temperatureC: number;
|
|
127
|
+
/** Relative humidity in percent, with the climate offset. */
|
|
128
|
+
readonly humidityPercent: number;
|
|
129
|
+
/** Ground wetness now, 0..1 (rises with rain, dries 180 s after it stops). */
|
|
130
|
+
readonly groundWetness: number;
|
|
131
|
+
/** 0..1 flash of the latest strike (the Workshop's double flash). */
|
|
132
|
+
readonly flash: number;
|
|
133
|
+
readonly effects: WeatherEffects;
|
|
134
|
+
};
|
|
135
|
+
type WeatherEventMap = {
|
|
136
|
+
readonly transition: {
|
|
137
|
+
readonly from: string;
|
|
138
|
+
readonly to: string;
|
|
139
|
+
readonly gameSeconds: number;
|
|
140
|
+
};
|
|
141
|
+
readonly strike: Strike;
|
|
142
|
+
readonly thunder: Strike;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
declare const WEATHER_STATES: readonly WeatherState[];
|
|
146
|
+
declare const CLIMATES: readonly Climate[];
|
|
147
|
+
type RegistryOptions = {
|
|
148
|
+
/** Added to the shipped states or, when the id exists, replacing it. */
|
|
149
|
+
readonly states?: readonly WeatherState[];
|
|
150
|
+
readonly climates?: readonly Climate[];
|
|
151
|
+
/** Merged into a shipped or added entry by id; a patch for an id that does not exist is ignored. */
|
|
152
|
+
readonly patch?: {
|
|
153
|
+
readonly states?: Readonly<Record<string, DeepPartial<WeatherState>>>;
|
|
154
|
+
readonly climates?: Readonly<Record<string, DeepPartial<Climate>>>;
|
|
155
|
+
};
|
|
156
|
+
readonly remove?: {
|
|
157
|
+
readonly states?: readonly string[];
|
|
158
|
+
readonly climates?: readonly string[];
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
type WeatherRegistry = {
|
|
162
|
+
state(id: string): WeatherState | undefined;
|
|
163
|
+
climate(id: string): Climate | undefined;
|
|
164
|
+
stateIds(): string[];
|
|
165
|
+
climateIds(): string[];
|
|
166
|
+
registerState(state: WeatherState): void;
|
|
167
|
+
registerClimate(climate: Climate): void;
|
|
168
|
+
patchState(id: string, patch: DeepPartial<WeatherState>): void;
|
|
169
|
+
patchClimate(id: string, patch: DeepPartial<Climate>): void;
|
|
170
|
+
removeState(id: string): void;
|
|
171
|
+
removeClimate(id: string): void;
|
|
172
|
+
};
|
|
173
|
+
/** The shipped states and climates with yours added, patched or removed; change it later with the same verbs. */
|
|
174
|
+
declare function createWeatherRegistry(options?: RegistryOptions): WeatherRegistry;
|
|
175
|
+
|
|
176
|
+
declare const DEFAULT_WET_RISE_SECONDS = 120;
|
|
177
|
+
/** The Workshop sheet: the water goes back to the colour of the ground 3 minutes after the rain stops. */
|
|
178
|
+
declare const DEFAULT_DRY_OUT_SECONDS = 180;
|
|
179
|
+
type WetnessOptions = {
|
|
180
|
+
/** Seconds of rain to wet the ground fully. */
|
|
181
|
+
readonly riseSeconds?: number;
|
|
182
|
+
/** Seconds from the end of the rain until the ground is dry. */
|
|
183
|
+
readonly dryOutSeconds?: number;
|
|
184
|
+
};
|
|
185
|
+
type Wetness = {
|
|
186
|
+
value(): number;
|
|
187
|
+
/** Moves `seconds` toward `target` (the wetness the weather asks for); `dryingScale` speeds the dry-out up. Returns the new value. */
|
|
188
|
+
update(seconds: number, target: number, dryingScale?: number): number;
|
|
189
|
+
reset(value?: number): void;
|
|
190
|
+
};
|
|
191
|
+
/** Ground wetness: rises while the weather asks for more, then falls in a straight line that ends `dryOutSeconds` after the start of the fall. */
|
|
192
|
+
declare function createWetness(options?: WetnessOptions): Wetness;
|
|
193
|
+
|
|
194
|
+
declare const DEFAULT_START_HOUR = 8;
|
|
195
|
+
type WeatherOptions = RegistryOptions & {
|
|
196
|
+
/** Seed of the schedule and of the lightning (each has its own stream). Default 1. */
|
|
197
|
+
readonly seed?: number;
|
|
198
|
+
/** Climate id. Default `temperate`. */
|
|
199
|
+
readonly climate?: string;
|
|
200
|
+
/** First state; default the climate's `start`, which is `clear`: the normal world comes first. */
|
|
201
|
+
readonly start?: string;
|
|
202
|
+
/** Game seconds per real second. Default 72 (the 20-minute day). */
|
|
203
|
+
readonly timeScale?: number;
|
|
204
|
+
/** Hour of the day the world starts at. Default 8. */
|
|
205
|
+
readonly startHour?: number;
|
|
206
|
+
/** `false` keeps the state until `force` changes it. Default true. */
|
|
207
|
+
readonly autoSchedule?: boolean;
|
|
208
|
+
readonly wetness?: WetnessOptions;
|
|
209
|
+
/** An existing registry, instead of building one from the registry options. */
|
|
210
|
+
readonly registry?: WeatherRegistry;
|
|
211
|
+
};
|
|
212
|
+
type WeatherTimeline = {
|
|
213
|
+
readonly seed: number;
|
|
214
|
+
readonly climate: string;
|
|
215
|
+
readonly start: string;
|
|
216
|
+
readonly hours: number;
|
|
217
|
+
readonly sampleMinutes: number;
|
|
218
|
+
readonly transitions: ReadonlyArray<{
|
|
219
|
+
readonly gameSeconds: number;
|
|
220
|
+
readonly from: string;
|
|
221
|
+
readonly to: string;
|
|
222
|
+
}>;
|
|
223
|
+
readonly strikes: readonly Strike[];
|
|
224
|
+
readonly samples: ReadonlyArray<Readonly<Record<string, number | string>>>;
|
|
225
|
+
};
|
|
226
|
+
type Weather = {
|
|
227
|
+
readonly registry: WeatherRegistry;
|
|
228
|
+
/** Moves the world forward by `dt` real seconds (game time moves `timeScale` times as fast). Events fire in order as they are crossed. */
|
|
229
|
+
advance(dt: number): void;
|
|
230
|
+
sample(): WeatherSample;
|
|
231
|
+
/** The state being blended to, or settled in. */
|
|
232
|
+
state(): string;
|
|
233
|
+
/** Changes state now, blending over `blendSeconds` real seconds (0 is at once); the new state then lasts a normal dwell. */
|
|
234
|
+
force(id: string, blendSeconds?: number): void;
|
|
235
|
+
setTimeScale(timeScale: number): void;
|
|
236
|
+
setAutoSchedule(isOn: boolean): void;
|
|
237
|
+
/** What a fresh world with these options does over `hours` game hours; the live world is not touched. */
|
|
238
|
+
timeline(hours: number, sampleMinutes?: number): WeatherTimeline;
|
|
239
|
+
on<TEvent extends keyof WeatherEventMap>(event: TEvent, listener: (payload: WeatherEventMap[TEvent]) => void): () => void;
|
|
240
|
+
};
|
|
241
|
+
declare function createWeather(options?: WeatherOptions): Weather;
|
|
242
|
+
type TimelineOptions = WeatherOptions & {
|
|
243
|
+
readonly hours: number;
|
|
244
|
+
/** Game minutes between samples. Default 10. */
|
|
245
|
+
readonly sampleMinutes?: number;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* What a fresh world does over `hours` game hours, stepping one game minute at a time: the state changes, the strikes (time, distance, bearing) and
|
|
249
|
+
* a sample every `sampleMinutes`. Plain JSON, rounded to 6 decimals, so it can be a golden.
|
|
250
|
+
*/
|
|
251
|
+
declare function createTimeline(options: TimelineOptions): WeatherTimeline;
|
|
252
|
+
|
|
253
|
+
type WeatherSoundStyle = 'art_scene' | 'game';
|
|
254
|
+
type WeatherSoundOptions = {
|
|
255
|
+
/** Which team's rain: `art_scene` (default, the kit mix) or `game` (a highpass hiss). The wind and the thunder are the same. */
|
|
256
|
+
readonly style?: WeatherSoundStyle;
|
|
257
|
+
/**
|
|
258
|
+
* Level of the rain loop. It is `light` at `lightAt` mm/h (the game's rain, 0.035) and `heavy` from `heavyAt` mm/h (the art scene's, 0.06), a straight
|
|
259
|
+
* line between, and 0 when it does not rain.
|
|
260
|
+
*/
|
|
261
|
+
readonly rain?: {
|
|
262
|
+
readonly light?: number;
|
|
263
|
+
readonly heavy?: number;
|
|
264
|
+
readonly lightAt?: number;
|
|
265
|
+
readonly heavyAt?: number;
|
|
266
|
+
};
|
|
267
|
+
/** Level of the wind loop: `base` (the art scene's 0.05) at `referenceSpeed` m/s, in proportion to the speed, at most `max`. */
|
|
268
|
+
readonly wind?: {
|
|
269
|
+
readonly base?: number;
|
|
270
|
+
readonly referenceSpeed?: number;
|
|
271
|
+
readonly max?: number;
|
|
272
|
+
};
|
|
273
|
+
/** Where the listener is, to place the thunder around it (default the origin). */
|
|
274
|
+
readonly listener?: () => Vec3;
|
|
275
|
+
/** Level of the thunder (default 1). */
|
|
276
|
+
readonly thunderVolume?: number;
|
|
277
|
+
/** `false` keeps the sink's bank as it is (the host registered `WEATHER_BANK` itself). */
|
|
278
|
+
readonly register?: boolean;
|
|
279
|
+
};
|
|
280
|
+
declare const DEFAULT_RAIN_SOUND: {
|
|
281
|
+
readonly light: 0.035;
|
|
282
|
+
readonly heavy: 0.06;
|
|
283
|
+
readonly lightAt: 2.5;
|
|
284
|
+
readonly heavyAt: 15;
|
|
285
|
+
};
|
|
286
|
+
/** The art scene opens its wind at 0.05 with the breeze of its day (3.8 m/s, the `clear` state's wind): the level follows the speed from there. */
|
|
287
|
+
declare const DEFAULT_WIND_SOUND: {
|
|
288
|
+
readonly base: 0.05;
|
|
289
|
+
readonly referenceSpeed: 3.8;
|
|
290
|
+
readonly max: 0.12;
|
|
291
|
+
};
|
|
292
|
+
/** The level of the rain loop for `rain` mm/h. */
|
|
293
|
+
declare function rainVolume(rain: number, options?: WeatherSoundOptions['rain']): number;
|
|
294
|
+
/** The level of the wind loop for a wind of `speed` m/s. */
|
|
295
|
+
declare function windVolume(speed: number, options?: WeatherSoundOptions['wind']): number;
|
|
296
|
+
/** Where a strike is, around `origin`: `bearing` is degrees clockwise from north, and north is -z. */
|
|
297
|
+
declare function strikePosition(strike: Pick<Strike, 'distance' | 'bearing'>, origin?: Vec3): Vec3;
|
|
298
|
+
type WeatherSound = {
|
|
299
|
+
/** Sets the level of the loops from the weather of now (opens a loop the first time it is above 0). */
|
|
300
|
+
update(sample: Pick<WeatherSample, 'rain' | 'windSpeed'>): void;
|
|
301
|
+
/** Plays a clap of thunder at the strike (call it when the thunder arrives: the weather delays it by `distance / 343`). */
|
|
302
|
+
thunder(strike: Pick<Strike, 'distance' | 'bearing'>): void;
|
|
303
|
+
dispose(): void;
|
|
304
|
+
};
|
|
305
|
+
/** The sound of the weather on the host's `audio`: the rain loop, the wind loop and the thunder. Without a sink (or one without loops) it does nothing. */
|
|
306
|
+
declare function createWeatherSound(sink: AudioSink | undefined, options?: WeatherSoundOptions): WeatherSound;
|
|
307
|
+
|
|
308
|
+
/** The shape of the film of `game-fluids-three` (`FilmBridge`) this needs: no import, so the weather does not depend on it. */
|
|
309
|
+
type FilmTarget = {
|
|
310
|
+
setWeather(weather: {
|
|
311
|
+
rain: number;
|
|
312
|
+
drying?: number;
|
|
313
|
+
}): void;
|
|
314
|
+
wash?(maxDryness?: number): number;
|
|
315
|
+
};
|
|
316
|
+
/** The interactables world (`game-interactables`): its water takes the rain, the soft volumes the wind. */
|
|
317
|
+
type InteractablesTarget = {
|
|
318
|
+
water: {
|
|
319
|
+
setRain(mmPerHour: number): void;
|
|
320
|
+
};
|
|
321
|
+
setWind(x: number, z: number): void;
|
|
322
|
+
};
|
|
323
|
+
/** The vegetation renderer (`game-vegetation`). `direction` is normalised by the renderer, `strength` and `gust` are 0..1. */
|
|
324
|
+
type VegetationTarget = {
|
|
325
|
+
setWind(wind: {
|
|
326
|
+
direction?: [number, number];
|
|
327
|
+
strength?: number;
|
|
328
|
+
gust?: number;
|
|
329
|
+
}): void;
|
|
330
|
+
};
|
|
331
|
+
/** The effects world (`game-vfx`'s `VfxWorld`): smoke and dust drift with its wind. */
|
|
332
|
+
type VfxTarget = {
|
|
333
|
+
setWind(wind: readonly [number, number, number]): void;
|
|
334
|
+
};
|
|
335
|
+
type WeatherTargets = {
|
|
336
|
+
readonly film?: FilmTarget;
|
|
337
|
+
readonly interactables?: InteractablesTarget;
|
|
338
|
+
readonly vegetation?: VegetationTarget;
|
|
339
|
+
readonly vfx?: VfxTarget;
|
|
340
|
+
/** The host's environment bus (`ModuleHost.environment`). */
|
|
341
|
+
readonly environment?: Pick<EnvironmentBus, 'set'>;
|
|
342
|
+
/** The host's sound (`ModuleHost.audio`): the rain and wind loops, and the thunder. */
|
|
343
|
+
readonly audio?: AudioSink;
|
|
344
|
+
};
|
|
345
|
+
/** The rules the bridges apply; every number can be replaced. */
|
|
346
|
+
type WeatherBridgeRules = {
|
|
347
|
+
/** Rain in mm/h at which the film counts as fully rained on (it stops drying, spreads 1.5 times faster). Default 2.5, the light rain. */
|
|
348
|
+
readonly filmRainFullAt?: number;
|
|
349
|
+
/** Rain in mm/h from which the rain washes the fresh stain (Workshop and visor: heavy rain and stronger). Default 15; light rain does not wash. */
|
|
350
|
+
readonly washFrom?: number;
|
|
351
|
+
/** Stains less dry than this are washed (visor L13494). Default 0.5. */
|
|
352
|
+
readonly washBelow?: number;
|
|
353
|
+
/** Seconds between two washes while it pours. Default 1. */
|
|
354
|
+
readonly washEvery?: number;
|
|
355
|
+
/** Wind speed in m/s that is 1 for the vegetation (`strength = min(1, speed / this)`). Default 20. */
|
|
356
|
+
readonly windFull?: number;
|
|
357
|
+
};
|
|
358
|
+
type WeatherBridgeOptions = WeatherBridgeRules & {
|
|
359
|
+
readonly sound?: WeatherSoundOptions | false;
|
|
360
|
+
};
|
|
361
|
+
declare const DEFAULT_BRIDGE_RULES: {
|
|
362
|
+
readonly filmRainFullAt: 2.5;
|
|
363
|
+
readonly washFrom: 15;
|
|
364
|
+
readonly washBelow: 0.5;
|
|
365
|
+
readonly washEvery: 1;
|
|
366
|
+
readonly windFull: 20;
|
|
367
|
+
};
|
|
368
|
+
/** The weather as the environment bus holds it. */
|
|
369
|
+
declare function environmentOf(sample: WeatherSample): Partial<EnvironmentState>;
|
|
370
|
+
type WeatherBridges = {
|
|
371
|
+
/** Follows this weather (the one before is let go) and pushes its values at once. */
|
|
372
|
+
bind(weather: Weather): void;
|
|
373
|
+
/** Pushes the weather of now to every target; call it each frame after `weather.advance(dt)`. */
|
|
374
|
+
update(dt: number): void;
|
|
375
|
+
dispose(): void;
|
|
376
|
+
};
|
|
377
|
+
/**
|
|
378
|
+
* Pushes one weather to the packages that react to it, each through a small interface of its own, so the weather depends on none of them and every
|
|
379
|
+
* target is optional:
|
|
380
|
+
* - fluids film: `setWeather({ rain: min(1, rain / filmRainFullAt), drying: dryingScale })`, and `wash()` every `washEvery` seconds while the rain is at
|
|
381
|
+
* `washFrom` or stronger (light rain stops the drying and does not wash);
|
|
382
|
+
* - interactables: `water.setRain(mm/h)` and `setWind(x, z)`;
|
|
383
|
+
* - vegetation: `setWind({ direction: [x, z], strength: min(1, speed / windFull), gust: min(1, gust / windFull) })`;
|
|
384
|
+
* - vfx: `setWind([x, 0, z])`;
|
|
385
|
+
* - the environment bus: every value of `environmentOf`;
|
|
386
|
+
* - sound: the rain and wind loops follow the weather, and each thunder that arrives plays at its bearing.
|
|
387
|
+
*/
|
|
388
|
+
declare function createWeatherBridges(targets: WeatherTargets, options?: WeatherBridgeOptions): WeatherBridges;
|
|
389
|
+
|
|
390
|
+
/** What a renderer of the weather (game-weather-three) gives the bench: its layers, a step per frame and the switch of each layer. */
|
|
391
|
+
type WeatherVisual = {
|
|
392
|
+
update(dt: number): void;
|
|
393
|
+
setLayer(id: string, on: boolean): void;
|
|
394
|
+
dispose(): void;
|
|
395
|
+
};
|
|
396
|
+
/** The bench hands a visual the weather it shows (always the live one, even after the bench restarts it) and what the room lends. */
|
|
397
|
+
type WeatherVisualContext = {
|
|
398
|
+
readonly host: ModuleHost;
|
|
399
|
+
readonly locale: string;
|
|
400
|
+
readonly weather: {
|
|
401
|
+
sample(): WeatherSample;
|
|
402
|
+
};
|
|
403
|
+
};
|
|
404
|
+
/** The bench declares its layers before the room lends anything, so the factory tells them first (`fog` is the bench's own layer and reaches the visual through `setLayer` too). */
|
|
405
|
+
type WeatherVisualFactory = {
|
|
406
|
+
layers(locale: string): readonly ModuleLayer[];
|
|
407
|
+
create(context: WeatherVisualContext): WeatherVisual;
|
|
408
|
+
};
|
|
409
|
+
type WeatherTestModuleOptions = WeatherOptions & {
|
|
410
|
+
locale?: string;
|
|
411
|
+
/** Text of a state id in the game's words; without it the bench uses its own names, then the id. */
|
|
412
|
+
translate?: (key: string, locale: string) => string | undefined;
|
|
413
|
+
/** The weather to test (default a new one built from the other options). */
|
|
414
|
+
weather?: Weather;
|
|
415
|
+
/** Whether the scene fog follows the weather at first (default true; the layer can be switched either way). A room that is not about weather starts it off. */
|
|
416
|
+
fog?: boolean;
|
|
417
|
+
/** The packages the weather drives besides the host's environment bus and sound (fluids film, interactables, vegetation, vfx), from the example. */
|
|
418
|
+
targets?: Omit<WeatherTargets, 'environment' | 'audio'>;
|
|
419
|
+
/** The rain, wind and thunder on the host's `audio`; `false` keeps the area silent. */
|
|
420
|
+
sound?: WeatherSoundOptions | false;
|
|
421
|
+
/** The picture of the weather (rain, fog, sky, lightning, headlights, haze), from `@adatechnology/game-weather-three`. With it the bench leaves the scene fog to the visual. */
|
|
422
|
+
visual?: WeatherVisualFactory;
|
|
423
|
+
};
|
|
424
|
+
/** What the scenarios use of the context the test room passes. */
|
|
425
|
+
interface WeatherScenarioLike {
|
|
426
|
+
expect(condition: boolean, label: string, data?: Record<string, unknown>): void;
|
|
427
|
+
}
|
|
428
|
+
type WeatherTestModule = TestableModule<WeatherScenarioLike> & {
|
|
429
|
+
/** The weather in use (replaced when the bench changes the seed or the hour). */
|
|
430
|
+
readonly weather: Weather;
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* The weather area of a test room, written against the generic contracts only: it drives a `createWeather` world, writes the result into the host's
|
|
434
|
+
* environment bus (`host.environment`) every frame and shows the numbers a game reads from it. It also sets the scene fog from the weather's render fog.
|
|
435
|
+
* Pure: no three.js beyond the host's own `Fog`, and the sky, rain, lightning and sound wait for the packages that draw and play them.
|
|
436
|
+
*/
|
|
437
|
+
declare function createWeatherTestModule(options?: WeatherTestModuleOptions): WeatherTestModule;
|
|
438
|
+
|
|
439
|
+
type WeatherBenchText = {
|
|
440
|
+
readonly group: string;
|
|
441
|
+
readonly name: string;
|
|
442
|
+
readonly controls: Readonly<Record<'state' | 'transition' | 'time_of_day' | 'auto_schedule' | 'seed', string>>;
|
|
443
|
+
readonly hints: Readonly<Record<'transition' | 'time_of_day' | 'auto_schedule' | 'seed', string>>;
|
|
444
|
+
readonly options: Readonly<Record<'instant' | 'ten_seconds', string>>;
|
|
445
|
+
readonly layers: Readonly<Record<'environment' | 'fog', string>>;
|
|
446
|
+
readonly metrics: Readonly<Record<'state' | 'hour' | 'rain' | 'visibility' | 'sight' | 'hearing' | 'wind' | 'heat_index' | 'wetness' | 'strikes' | 'blend' | 'temperature', string>>;
|
|
447
|
+
readonly scenarios: Readonly<Record<'storm' | 'blend' | 'determinism' | 'dry_ground', string>>;
|
|
448
|
+
/** Names of the states and climates by id; the bench falls back to the id. */
|
|
449
|
+
readonly states: Readonly<Record<string, string>>;
|
|
450
|
+
};
|
|
451
|
+
declare const weatherBenchTextOf: (locale: string) => WeatherBenchText;
|
|
452
|
+
|
|
453
|
+
export { strikePosition as $, type WeatherTestModule as A, type WeatherTestModuleOptions as B, type Climate as C, DEFAULT_BRIDGE_RULES as D, type WeatherTimeline as E, type FilmTarget as F, type WeatherVisual as G, type WeatherVisualContext as H, type InteractablesTarget as I, type WeatherVisualFactory as J, type Wetness as K, type LightningConfig as L, type WetnessOptions as M, createTimeline as N, createWeather as O, createWeatherBridges as P, createWeatherRegistry as Q, type RegistryOptions as R, type StateValueKey as S, type TimelineOptions as T, createWeatherSound as U, type VegetationTarget as V, type WeatherState as W, createWeatherTestModule as X, createWetness as Y, environmentOf as Z, rainVolume as _, type StateValues as a, weatherBenchTextOf as a0, windVolume as a1, type WeatherEffects as b, CLIMATES as c, DEFAULT_DRY_OUT_SECONDS as d, DEFAULT_RAIN_SOUND as e, DEFAULT_START_HOUR as f, DEFAULT_WET_RISE_SECONDS as g, DEFAULT_WIND_SOUND as h, type DeepPartial as i, type Strike as j, type VfxTarget as k, WEATHER_STATES as l, type Weather as m, type WeatherBenchText as n, type WeatherBridgeOptions as o, type WeatherBridgeRules as p, type WeatherBridges as q, type WeatherEventMap as r, type WeatherOptions as s, type WeatherRegistry as t, type WeatherSample as u, type WeatherScenarioLike as v, type WeatherSound as w, type WeatherSoundOptions as x, type WeatherSoundStyle as y, type WeatherTargets as z };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { n as WeatherBenchText, v as WeatherScenarioLike, A as WeatherTestModule, B as WeatherTestModuleOptions, G as WeatherVisual, H as WeatherVisualContext, J as WeatherVisualFactory, X as createWeatherTestModule, a0 as weatherBenchTextOf } from './testing-B4pjKlOJ.js';
|
|
2
|
+
import '@adatechnology/game-contracts';
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adatechnology/game-weather",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Pure, seeded weather for games: states (clear to typhoon), climates with a Markov scheduler in game time, fronts from the west, lightning with thunder delay, ground wetness, gameplay effects, heat index and the day-night formula. Knows nothing about any game.",
|
|
6
6
|
"keywords": [
|
|
@@ -32,14 +32,18 @@
|
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"import": "./dist/index.js"
|
|
34
34
|
},
|
|
35
|
+
"./testing": {
|
|
36
|
+
"types": "./dist/testing.d.ts",
|
|
37
|
+
"import": "./dist/testing.js"
|
|
38
|
+
},
|
|
35
39
|
"./global": "./dist/index.global.js"
|
|
36
40
|
},
|
|
37
41
|
"files": [
|
|
38
42
|
"dist"
|
|
39
43
|
],
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"@adatechnology/game-audio": "^0.
|
|
42
|
-
"@adatechnology/game-contracts": "^0.
|
|
45
|
+
"@adatechnology/game-audio": "^0.4.0",
|
|
46
|
+
"@adatechnology/game-contracts": "^0.22.0"
|
|
43
47
|
},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"@types/node": "^22.10.7",
|