@jsprismarine/spawning 0.0.0 → 0.16.0-unstable-20261029140140
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/CHANGELOG.md +80 -0
- package/LICENSE +373 -0
- package/README.md +207 -0
- package/dist/conditions.cjs.cjs +190 -0
- package/dist/conditions.d.ts +81 -0
- package/dist/conditions.d.ts.map +1 -0
- package/dist/conditions.es.js +186 -0
- package/dist/constants.cjs.cjs +29 -0
- package/dist/constants.d.ts +73 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.es.js +26 -0
- package/dist/despawn.cjs.cjs +25 -0
- package/dist/despawn.d.ts +33 -0
- package/dist/despawn.d.ts.map +1 -0
- package/dist/despawn.es.js +23 -0
- package/dist/engineRules.cjs.cjs +145 -0
- package/dist/engineRules.d.ts +86 -0
- package/dist/engineRules.d.ts.map +1 -0
- package/dist/engineRules.es.js +138 -0
- package/dist/index.cjs.cjs +32 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +8 -0
- package/dist/rules.cjs.cjs +282 -0
- package/dist/rules.d.ts +152 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.es.js +277 -0
- package/dist/spawner.cjs.cjs +280 -0
- package/dist/spawner.d.ts +105 -0
- package/dist/spawner.d.ts.map +1 -0
- package/dist/spawner.es.js +278 -0
- package/dist/tables/spawnConstants.cjs.cjs +111 -0
- package/dist/tables/spawnConstants.d.ts +2 -0
- package/dist/tables/spawnConstants.es.js +89 -0
- package/dist/tables/spawnConstants.json.d.ts +2 -0
- package/dist/world.cjs.cjs +51 -0
- package/dist/world.d.ts +111 -0
- package/dist/world.d.ts.map +1 -0
- package/dist/world.es.js +45 -0
- package/package.json +68 -5
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# @jsprismarine/spawning
|
|
2
|
+
|
|
3
|
+
[](https://discord.gg/6w8JWhy)
|
|
4
|
+
[](https://www.npmjs.com/package/@jsprismarine/spawning)
|
|
5
|
+
|
|
6
|
+
Bedrock natural spawning: the `spawn_rules/*.json` grammar, the condition evaluator, the population
|
|
7
|
+
pools and the spawn loop. It builds on `@jsprismarine/packs` for the filter grammar, on
|
|
8
|
+
`@jsprismarine/entities` for the filter evaluator that spawn rules, entity definitions and trade
|
|
9
|
+
tables all share, and on `@jsprismarine/predicates` for the injected random source.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Spawner, parseSpawnRule } from '@jsprismarine/spawning';
|
|
13
|
+
import { seededRandom } from '@jsprismarine/predicates';
|
|
14
|
+
|
|
15
|
+
const rules = files.map((file) => parseSpawnRule(file.data, { pack: file.pack, path: file.path }));
|
|
16
|
+
const spawner = new Spawner({ rules: rules.filter((rule) => rule !== undefined), world, random: seededRandom(seed) });
|
|
17
|
+
|
|
18
|
+
for (const request of spawner.tick(tick)) {
|
|
19
|
+
world.create(request.identifier, request.position, request.spawnEvent);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## The grammar
|
|
24
|
+
|
|
25
|
+
A file is `{"format_version": "<semver>", "minecraft:spawn_rules": {description, conditions}}`.
|
|
26
|
+
`description` carries `identifier` and `population_control`; `conditions` is a list of alternatives,
|
|
27
|
+
and a rule allows a position when any one of them does.
|
|
28
|
+
|
|
29
|
+
`parseSpawnRule` reads all 24 components `Spawn Rules.json` declares and reports an unknown one
|
|
30
|
+
rather than refusing the file, so a pack written against a newer game still loads. Vanilla writes 21
|
|
31
|
+
of the 24. The three it never writes, `minecraft:spawns_above_block_filter`,
|
|
32
|
+
`minecraft:is_persistent` and `minecraft:is_experimental`, parse into the condition and keep their
|
|
33
|
+
meaning for the caller, because Mojang documents none of them.
|
|
34
|
+
|
|
35
|
+
Three components are written in more than one shape, and all of them read:
|
|
36
|
+
|
|
37
|
+
- `minecraft:spawns_on_block_filter` as one name, as a list of names, or as a list of `{name}`
|
|
38
|
+
objects.
|
|
39
|
+
- `minecraft:herd` as one object or as a list of alternatives, which is how the tropical fish picks
|
|
40
|
+
a color event.
|
|
41
|
+
- `minecraft:biome_filter` as one test, as a bare array (an implicit `all_of`), or as a group map.
|
|
42
|
+
`@jsprismarine/packs` parses it, including the integer `subject` and `operator` encoding and the
|
|
43
|
+
`AND`, `OR` and `NOT` aliases.
|
|
44
|
+
|
|
45
|
+
`minecraft:permute_type` entries read both spellings of their third field: the schema's
|
|
46
|
+
`min_guaranteed` and the documentation page's `guaranteed_count`.
|
|
47
|
+
|
|
48
|
+
## What a condition decides
|
|
49
|
+
|
|
50
|
+
`evaluateSpawnCondition` runs the components cheapest first and names the first one that rejected
|
|
51
|
+
the position, so a server can explain an empty attempt and a test can name the component it covers.
|
|
52
|
+
|
|
53
|
+
| Component | What it compares |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `minecraft:spawns_on_surface`, `minecraft:spawns_underground` | The pass the position is in. Naming both takes either; naming neither takes any. |
|
|
56
|
+
| `minecraft:spawns_underwater`, `minecraft:spawns_lava` | The medium. Naming neither refuses a position inside either liquid. |
|
|
57
|
+
| `minecraft:disallow_spawns_in_bubble` | A bubble column at the position. |
|
|
58
|
+
| `minecraft:height_filter` | The position's Y against the inclusive range. |
|
|
59
|
+
| `minecraft:spawns_on_block_filter`, `minecraft:spawns_on_block_prevented_filter` | The block one below the position. |
|
|
60
|
+
| `minecraft:spawns_above_block_filter` | The `distance` blocks below the position. |
|
|
61
|
+
| `minecraft:brightness_filter` | The light the world reports, lowered by the weather when `adjust_for_weather` is set. |
|
|
62
|
+
| `minecraft:difficulty_filter` | The world difficulty against the inclusive range. |
|
|
63
|
+
| `minecraft:world_age_filter` | The world age in ticks; a maximum of -1 is no upper bound. |
|
|
64
|
+
| `minecraft:mob_event_filter` | Whether the named mob event is running. |
|
|
65
|
+
| `minecraft:distance_filter` | The distance to the nearest player. |
|
|
66
|
+
| `minecraft:player_in_village_filter` | A player within `distance` who is within `village_border_tolerance` of a village border. |
|
|
67
|
+
| `minecraft:biome_filter` | The shared filter grammar, through the `@jsprismarine/entities` evaluator. |
|
|
68
|
+
| `minecraft:density_limit` | How many entities of this type stand within `densityRadius`, per pass. |
|
|
69
|
+
| `minecraft:delay_filter` | A cooldown keyed by the filter's `identifier`, then a `spawn_chance` roll. |
|
|
70
|
+
|
|
71
|
+
The remaining six components are not gates. `minecraft:weight` and its `rarity` pick between the
|
|
72
|
+
conditions that passed, `minecraft:herd` and `minecraft:permute_type` build the group, and
|
|
73
|
+
`minecraft:spawn_event`, `minecraft:is_persistent` and `minecraft:is_experimental` describe what
|
|
74
|
+
spawns.
|
|
75
|
+
|
|
76
|
+
Three readings in that table are this project's, because Mojang states the component and not the
|
|
77
|
+
rule. A condition that names neither liquid refuses a position inside one, which is what keeps cows
|
|
78
|
+
out of the ocean. `minecraft:spawns_above_block_filter` scans downward, because an entity "above a
|
|
79
|
+
block" stands on it. `rarity` is read as a one-in-N roll on top of the weight, which is the only
|
|
80
|
+
reading under which a rarity of 3 makes a spawn rarer.
|
|
81
|
+
|
|
82
|
+
## The spawn loop
|
|
83
|
+
|
|
84
|
+
Mojang's spawn cycle diagram names six steps, "check spawn conditions, find valid spawn locations,
|
|
85
|
+
check population caps, evaluate spawn weight, spawn entity, and run spawn event".
|
|
86
|
+
`Spawner.tick(tick)` runs them for every pool whose cadence falls on that tick, and
|
|
87
|
+
`Spawner.cycle(pool, tick)` runs one pool on demand.
|
|
88
|
+
|
|
89
|
+
One cycle of one pool:
|
|
90
|
+
|
|
91
|
+
1. Read the pool's cap. A `per_player` cap multiplies by the players in the region; a `per_region`
|
|
92
|
+
cap does not. A pool with no cap, which is the pillager pool, skips the check.
|
|
93
|
+
2. For each player, sample `chunksPerCycle` chunks inside `spawnRadiusChunks`.
|
|
94
|
+
3. In each chunk, try `positionsPerChunk` positions for the surface pass, where Y is
|
|
95
|
+
`SpawnWorld.surfaceY`, and the same number for the underground pass, where Y is drawn between
|
|
96
|
+
`SpawnWorld.minY` and the surface.
|
|
97
|
+
4. At each position, collect every condition of every rule in the pool that passes, draw one
|
|
98
|
+
weighted by `minecraft:weight.default`, then apply its `rarity` roll.
|
|
99
|
+
5. Draw a herd size inside the chosen `minecraft:herd` alternative, place the first member at the
|
|
100
|
+
position and the rest within `herdRadius`, following the terrain on the surface pass.
|
|
101
|
+
6. Give each member its entity type and spawn event. `minecraft:permute_type` swaps the type, taking
|
|
102
|
+
its `min_guaranteed` members first and drawing the rest by weight, and the
|
|
103
|
+
`minecraft:pillager<minecraft:spawn_as_patrol_follower>` form splits into a type and an event.
|
|
104
|
+
The event is `initial_event` for the first `initial_event_count` members, then `event` for every
|
|
105
|
+
member past `event_skip_count`, then the permutation's own event, then
|
|
106
|
+
`minecraft:spawn_event`.
|
|
107
|
+
7. Stop as soon as the pool reaches its cap, mid-herd if that is where the cap falls.
|
|
108
|
+
|
|
109
|
+
`despawnReason` carries the two rules that belong to the population rather than to an entity: the
|
|
110
|
+
published Y floor of -128, and the edge of the simulated region. The per-entity policy is
|
|
111
|
+
`minecraft:despawn` in `@jsprismarine/entities`, which this package does not duplicate.
|
|
112
|
+
|
|
113
|
+
## The injected seams
|
|
114
|
+
|
|
115
|
+
`SpawnWorld` is the one interface the loop reads the world through, so a test drives the whole
|
|
116
|
+
algorithm with a typed double and a server implements it once.
|
|
117
|
+
|
|
118
|
+
- `site(flag, at)` answers the six site facts, each named by the component or filter test that
|
|
119
|
+
reads it: `surface`, `underground`, `underwater`, `lava`, `bubble_column` and `snow_covered`.
|
|
120
|
+
- `block(at)`, `light(at)`, `biomeTags(at)` and `biome(at)` answer one position.
|
|
121
|
+
- `surfaceY(x, z)` and `minY()` give the two passes their Y.
|
|
122
|
+
- `players()`, `poolCount(pool)` and `typeCount(identifier, at, radius)` answer the caps and the
|
|
123
|
+
density limit.
|
|
124
|
+
- `difficulty()`, `weather()`, `age()`, `mobEventActive(event)` and `villageBorderDistance(at)`
|
|
125
|
+
answer the rest.
|
|
126
|
+
|
|
127
|
+
`RandomSource` from `@jsprismarine/predicates` is the only source of randomness, so one seed
|
|
128
|
+
reproduces one set of spawns. `SpawnDelays` holds the `minecraft:delay_filter` cooldowns and a
|
|
129
|
+
caller can persist it across a restart. `SpawnConstants` replaces the numbers below.
|
|
130
|
+
|
|
131
|
+
## Which numbers Mojang publishes
|
|
132
|
+
|
|
133
|
+
**None of the ones the algorithm needs.** Mojang documents the pool names and every component's
|
|
134
|
+
parameters. Mojang publishes no spawn attempt rate, no cap value, no spawn radius, no herd placement
|
|
135
|
+
rule, no density radius, and no statement of which light value `minecraft:brightness_filter`
|
|
136
|
+
compares or what `adjust_for_weather` does to it. The research note
|
|
137
|
+
[`docs/research/bedrock-entities-spawning-loot-trading.md`](../../docs/research/bedrock-entities-spawning-loot-trading.md)
|
|
138
|
+
section 4 lists the gaps one by one.
|
|
139
|
+
|
|
140
|
+
The one place numbers appear is the "Typical Cap" column of `EntitySpawningDeepDive.md`, and every
|
|
141
|
+
entry there is hedged with a tilde: `animal` "~10 per player", `monster` "~70 in loaded chunks",
|
|
142
|
+
`water_animal` "~5 per water body", `water_ambient` "~20 per water body", `ambient` "~15 in caves",
|
|
143
|
+
`pillager` "Limited by patrols".
|
|
144
|
+
|
|
145
|
+
So every number this package uses is this project's choice. They live in
|
|
146
|
+
[`src/tables/spawnConstants.json`](src/tables/spawnConstants.json) rather than in code, each with a
|
|
147
|
+
`source` string, so a reader can judge them and a host can replace the whole table through
|
|
148
|
+
`SpawnerOptions.constants`.
|
|
149
|
+
|
|
150
|
+
| Constant | Value | Where it came from |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `pools.animal.cap` | 10 per player | The tilde-hedged "~10 per player". |
|
|
153
|
+
| `pools.water_animal.cap` | 5 per player | "~5 per water body". A water body is not a quantity this server tracks, so the cap counts per player. |
|
|
154
|
+
| `pools.water_ambient.cap` | 20 per player | "~20 per water body". No vanilla rule names this pool. |
|
|
155
|
+
| `pools.ambient.cap` | 15 per player | "~15 in caves". Only the bat uses this pool. |
|
|
156
|
+
| `pools.monster.cap` | 70 per region | "~70 in loaded chunks", so it covers the region rather than scaling with players. |
|
|
157
|
+
| `pools.cat.cap` | 5 per region | Mojang says only that cat limits are "based upon the amount within a village" and gives no number. |
|
|
158
|
+
| `pools.pillager.cap` | none | "Limited by patrols", so `minecraft:delay_filter` and `minecraft:mob_event_filter` gate it instead. |
|
|
159
|
+
| `pools.*.cycleTicks` | 400 for the calm pools, 20 for monsters and pillagers, 1200 for cats | Mojang publishes no rate. Java's animal pass runs every 400 ticks and its monster pass every tick; 20 is this project's cheaper monster cadence. |
|
|
160
|
+
| `spawnRadiusChunks` | 4 | Bedrock's documented default simulation distance, because the ticking area documentation says spawning happens "in a radius centered on a player". |
|
|
161
|
+
| `chunksPerCycle`, `positionsPerChunk` | 8 and 3 | This project's budget, picked so one player in an empty plains biome reaches the animal cap within a few cycles. |
|
|
162
|
+
| `herdRadius` | 5 blocks | Java's horizontal pack spread. |
|
|
163
|
+
| `densityRadius` | 16 blocks | One chunk's width, as this project's reading of the "certain locations" the density limit talks about. |
|
|
164
|
+
| `rainDarkening`, `thunderDarkening` | 5 and 10 | Java's `Level.getSkyDarken` subtracts these under rain and thunder. |
|
|
165
|
+
| `despawnRadiusChunks` | 8 | The 128 blocks of `minecraft:despawn.despawn_from_distance.max_distance`, the one published spawning distance. |
|
|
166
|
+
| `despawnFloorY` | -128 | Published, and the same in both editions. |
|
|
167
|
+
|
|
168
|
+
The project chose Bedrock's model over Java's deliberately.
|
|
169
|
+
[`docs/plans/data-driven-vanilla.md`](../../docs/plans/data-driven-vanilla.md) section 12 decision 3
|
|
170
|
+
records why: Java's model is implementable and invisible to a Bedrock client, and it would break
|
|
171
|
+
every farm a Bedrock player has built.
|
|
172
|
+
|
|
173
|
+
## Measured coverage
|
|
174
|
+
|
|
175
|
+
Against the composed vanilla behavior pack of Bedrock Dedicated Server 1.26.51.1, through the corpus
|
|
176
|
+
test in `src/vanillaSpawnRules.test.ts`:
|
|
177
|
+
|
|
178
|
+
| Measure | Result |
|
|
179
|
+
| --- | --- |
|
|
180
|
+
| Spawn rule files composed | 60 |
|
|
181
|
+
| Files that parse with no diagnostic | 60 |
|
|
182
|
+
| Condition components the schema declares | 24, all parsed |
|
|
183
|
+
| Condition components vanilla writes | 21, all evaluated |
|
|
184
|
+
| Population pools in vanilla | `animal` 27, `monster` 24, `water_animal` 5, `pillager` 2, `cat` 1, `ambient` 1 |
|
|
185
|
+
| Rules with no conditions | 4: blaze, cat, pillager, wither skeleton |
|
|
186
|
+
| Biome filter tests left unanswered by the spawn filter context | 0 |
|
|
187
|
+
|
|
188
|
+
The corpus test needs the sources that `pnpm --filter @jsprismarine/bedrock-data run fetch:sources`
|
|
189
|
+
caches and runs only with `JSP_VANILLA_SPAWN_RULES` set:
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
JSP_VANILLA_SPAWN_RULES=1 pnpm --filter @jsprismarine/spawning exec vitest run src/vanillaSpawnRules.test.ts
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## What a later agent adds
|
|
196
|
+
|
|
197
|
+
- **The four rules with no conditions.** Blaze, cat, pillager and wither skeleton register a pool
|
|
198
|
+
and leave placement to engine code: fortress spawns, village cats and patrol placement. This
|
|
199
|
+
package spawns them wherever the pool cycle looks, which is wrong for all four.
|
|
200
|
+
- **Structure-based spawn selection.** When the world supplies Java structure data, the spawner
|
|
201
|
+
suppresses a rule candidate if Java reserves that entity type exclusively for structure
|
|
202
|
+
overrides and the candidate is outside every matching structure. This keeps guardians out of
|
|
203
|
+
ordinary ocean water while leaving entities also listed in Java biome spawn tables eligible
|
|
204
|
+
outside structures. Structure overrides do not yet create spawn attempts or replace the biome
|
|
205
|
+
category's weighted selection.
|
|
206
|
+
- **Measuring the constants.** Every number in the table above is replaceable, and a capture against
|
|
207
|
+
a running BDS would turn the guesses into measurements.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_world = require("./world.cjs.cjs");
|
|
4
|
+
const require_constants = require("./constants.cjs.cjs");
|
|
5
|
+
const require_rules = require("./rules.cjs.cjs");
|
|
6
|
+
let _jsprismarine_entities = require("@jsprismarine/entities");
|
|
7
|
+
// src/conditions.ts
|
|
8
|
+
/**
|
|
9
|
+
* The cooldowns `minecraft:delay_filter` keeps.
|
|
10
|
+
*
|
|
11
|
+
* The three pillager patrol conditions each name their own identifier, which
|
|
12
|
+
* is how one per-difficulty cooldown exists per world rather than one per
|
|
13
|
+
* condition.
|
|
14
|
+
*/
|
|
15
|
+
var SpawnDelays = class {
|
|
16
|
+
ready = /* @__PURE__ */ new Map();
|
|
17
|
+
/** The tick the named cooldown next allows a spawn, or undefined when it has never run. */
|
|
18
|
+
readyAt(identifier) {
|
|
19
|
+
return this.ready.get(identifier);
|
|
20
|
+
}
|
|
21
|
+
/** Sets the tick at which the named cooldown allows the next spawn. */
|
|
22
|
+
schedule(identifier, tick) {
|
|
23
|
+
this.ready.set(identifier, tick);
|
|
24
|
+
}
|
|
25
|
+
/** Draws a delay inside the filter's range and schedules it from `tick`. */
|
|
26
|
+
scheduleFrom(delay, tick, random) {
|
|
27
|
+
if (delay.identifier === void 0) return;
|
|
28
|
+
const span = Math.max(0, delay.max - delay.min);
|
|
29
|
+
this.schedule(delay.identifier, tick + delay.min + Math.floor(random.nextFloat() * (span + 1)));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var rejected = (component) => ({
|
|
33
|
+
passed: false,
|
|
34
|
+
rejectedBy: component
|
|
35
|
+
});
|
|
36
|
+
var PASSED = { passed: true };
|
|
37
|
+
/**
|
|
38
|
+
* Builds the filter context a `minecraft:biome_filter` reads.
|
|
39
|
+
*
|
|
40
|
+
* A spawn rule has no entity yet, so the `self` subject is the position: its
|
|
41
|
+
* biome tags answer `has_biome_tag`, its biome answers `is_biome`, and its
|
|
42
|
+
* site flags answer `is_snow_covered`, `is_underground`, `is_underwater` and
|
|
43
|
+
* `in_lava`. Every other test the shared grammar allows goes unanswered and is
|
|
44
|
+
* reported, which is what the engine does for a fact it cannot establish.
|
|
45
|
+
*/
|
|
46
|
+
var spawnFilterContext = (world, at) => {
|
|
47
|
+
return {
|
|
48
|
+
self: {
|
|
49
|
+
flag: (name) => {
|
|
50
|
+
switch (name) {
|
|
51
|
+
case "is_snow_covered": return world.site("snow_covered", at);
|
|
52
|
+
case "is_underground": return world.site("underground", at);
|
|
53
|
+
case "is_underwater":
|
|
54
|
+
case "in_water": return world.site("underwater", at);
|
|
55
|
+
case "in_lava": return world.site("lava", at);
|
|
56
|
+
default: return;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
names: (name) => {
|
|
60
|
+
if (name === "has_biome_tag") return world.biomeTags(at);
|
|
61
|
+
if (name === "is_biome") {
|
|
62
|
+
const biome = world.biome?.(at);
|
|
63
|
+
return biome === void 0 ? void 0 : [biome];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
world: {
|
|
68
|
+
difficulty: () => world.difficulty(),
|
|
69
|
+
weather: () => world.weather()
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
/** The light a brightness filter compares, after the darkening weather applies to it. */
|
|
74
|
+
var brightnessAt = (world, at, adjustForWeather, constants) => {
|
|
75
|
+
const light = world.light(at);
|
|
76
|
+
if (!adjustForWeather) return light;
|
|
77
|
+
const weather = world.weather();
|
|
78
|
+
if (weather === "rain") return Math.max(0, light - constants.rainDarkening);
|
|
79
|
+
if (weather === "thunder") return Math.max(0, light - constants.thunderDarkening);
|
|
80
|
+
return light;
|
|
81
|
+
};
|
|
82
|
+
var difficultyRank = (difficulty) => require_rules.DIFFICULTIES.indexOf(difficulty);
|
|
83
|
+
/** The distance from a position to the nearest player, or undefined when no player is loaded. */
|
|
84
|
+
var nearestPlayerDistance = (world, at) => {
|
|
85
|
+
let best;
|
|
86
|
+
for (const player of world.players()) {
|
|
87
|
+
const distance = require_world.distanceSquared(player.position, at);
|
|
88
|
+
if (best === void 0 || distance < best) best = distance;
|
|
89
|
+
}
|
|
90
|
+
return best === void 0 ? void 0 : Math.sqrt(best);
|
|
91
|
+
};
|
|
92
|
+
var matchesBlock = (identifier, names) => identifier !== void 0 && names.includes(identifier);
|
|
93
|
+
/**
|
|
94
|
+
* Decides whether one condition allows a spawn at a position.
|
|
95
|
+
*
|
|
96
|
+
* The components run cheapest first, and the first one that rejects names
|
|
97
|
+
* itself in the result. A condition that declares neither
|
|
98
|
+
* `minecraft:spawns_underwater` nor `minecraft:spawns_lava` refuses a position
|
|
99
|
+
* inside either liquid, which is the reading that keeps land mobs out of the
|
|
100
|
+
* ocean; Mojang states the components and not that rule.
|
|
101
|
+
*
|
|
102
|
+
* @param requestedPass - The pass the spawn loop is evaluating, or absent to
|
|
103
|
+
* derive it from the world's site facts.
|
|
104
|
+
*/
|
|
105
|
+
var evaluateSpawnCondition = (condition, at, context, requestedPass) => {
|
|
106
|
+
const { world } = context;
|
|
107
|
+
const constants = context.constants ?? require_constants.SPAWN_CONSTANTS;
|
|
108
|
+
const pass = requestedPass ?? require_world.passAt(world, at);
|
|
109
|
+
if (condition.spawnsOnSurface || condition.spawnsUnderground) {
|
|
110
|
+
if (pass === "surface" && !condition.spawnsOnSurface) return rejected("minecraft:spawns_underground");
|
|
111
|
+
if (pass === "underground" && !condition.spawnsUnderground) return rejected("minecraft:spawns_on_surface");
|
|
112
|
+
}
|
|
113
|
+
const inWater = world.site("underwater", at);
|
|
114
|
+
const inLava = world.site("lava", at);
|
|
115
|
+
if (condition.spawnsUnderwater && !inWater) return rejected("minecraft:spawns_underwater");
|
|
116
|
+
if (condition.spawnsLava && !inLava) return rejected("minecraft:spawns_lava");
|
|
117
|
+
if (!condition.spawnsUnderwater && inWater) return rejected("minecraft:spawns_underwater");
|
|
118
|
+
if (!condition.spawnsLava && inLava) return rejected("minecraft:spawns_lava");
|
|
119
|
+
if (condition.disallowSpawnsInBubble && world.site("bubble_column", at)) return rejected("minecraft:disallow_spawns_in_bubble");
|
|
120
|
+
if (condition.height !== void 0 && (at.y < condition.height.min || at.y > condition.height.max)) return rejected("minecraft:height_filter");
|
|
121
|
+
if (condition.blocks !== void 0) {
|
|
122
|
+
if (!matchesBlock(world.block({
|
|
123
|
+
x: at.x,
|
|
124
|
+
y: at.y - 1,
|
|
125
|
+
z: at.z
|
|
126
|
+
}), condition.blocks)) return rejected("minecraft:spawns_on_block_filter");
|
|
127
|
+
}
|
|
128
|
+
if (condition.preventedBlocks !== void 0) {
|
|
129
|
+
if (matchesBlock(world.block({
|
|
130
|
+
x: at.x,
|
|
131
|
+
y: at.y - 1,
|
|
132
|
+
z: at.z
|
|
133
|
+
}), condition.preventedBlocks)) return rejected("minecraft:spawns_on_block_prevented_filter");
|
|
134
|
+
}
|
|
135
|
+
if (condition.aboveBlocks !== void 0) {
|
|
136
|
+
let found = false;
|
|
137
|
+
for (let depth = 1; depth <= condition.aboveBlocks.distance && !found; depth += 1) found = matchesBlock(world.block({
|
|
138
|
+
x: at.x,
|
|
139
|
+
y: at.y - depth,
|
|
140
|
+
z: at.z
|
|
141
|
+
}), condition.aboveBlocks.blocks);
|
|
142
|
+
if (!found) return rejected("minecraft:spawns_above_block_filter");
|
|
143
|
+
}
|
|
144
|
+
if (condition.brightness !== void 0) {
|
|
145
|
+
const light = brightnessAt(world, at, condition.brightness.adjustForWeather, constants);
|
|
146
|
+
if (light < condition.brightness.min || light > condition.brightness.max) return rejected("minecraft:brightness_filter");
|
|
147
|
+
}
|
|
148
|
+
if (condition.difficulty !== void 0) {
|
|
149
|
+
const rank = difficultyRank(world.difficulty());
|
|
150
|
+
if (rank < difficultyRank(condition.difficulty.min) || rank > difficultyRank(condition.difficulty.max)) return rejected("minecraft:difficulty_filter");
|
|
151
|
+
}
|
|
152
|
+
if (condition.worldAge !== void 0) {
|
|
153
|
+
const age = world.age();
|
|
154
|
+
if (age < condition.worldAge.min) return rejected("minecraft:world_age_filter");
|
|
155
|
+
if (condition.worldAge.max >= 0 && age > condition.worldAge.max) return rejected("minecraft:world_age_filter");
|
|
156
|
+
}
|
|
157
|
+
if (condition.mobEvent !== void 0 && !world.mobEventActive(condition.mobEvent)) return rejected("minecraft:mob_event_filter");
|
|
158
|
+
if (condition.distance !== void 0) {
|
|
159
|
+
const distance = nearestPlayerDistance(world, at);
|
|
160
|
+
if (distance === void 0 || distance < condition.distance.min || distance > condition.distance.max) return rejected("minecraft:distance_filter");
|
|
161
|
+
}
|
|
162
|
+
if (condition.playerInVillage !== void 0) {
|
|
163
|
+
const { distance, villageBorderTolerance } = condition.playerInVillage;
|
|
164
|
+
if (!world.players().filter((player) => require_world.distanceSquared(player.position, at) <= distance * distance).some((player) => {
|
|
165
|
+
const border = world.villageBorderDistance?.(player.position);
|
|
166
|
+
return border !== void 0 && border <= villageBorderTolerance;
|
|
167
|
+
})) return rejected("minecraft:player_in_village_filter");
|
|
168
|
+
}
|
|
169
|
+
if (condition.biomeFilter !== void 0) {
|
|
170
|
+
const options = {};
|
|
171
|
+
if (context.onUnanswered !== void 0) options.onUnanswered = context.onUnanswered;
|
|
172
|
+
if (!(0, _jsprismarine_entities.evaluateFilter)(condition.biomeFilter, spawnFilterContext(world, at), options)) return rejected("minecraft:biome_filter");
|
|
173
|
+
}
|
|
174
|
+
if (condition.density !== void 0) {
|
|
175
|
+
const limit = pass === "surface" ? condition.density.surface : condition.density.underground;
|
|
176
|
+
if (limit >= 0 && world.typeCount(context.identifier, at, constants.densityRadius) >= limit) return rejected("minecraft:density_limit");
|
|
177
|
+
}
|
|
178
|
+
if (condition.delay !== void 0) {
|
|
179
|
+
const identifier = condition.delay.identifier;
|
|
180
|
+
const readyAt = identifier === void 0 ? void 0 : context.delays?.readyAt(identifier);
|
|
181
|
+
if (readyAt !== void 0 && context.tick < readyAt) return rejected("minecraft:delay_filter");
|
|
182
|
+
if (context.random.nextFloat() * 100 >= condition.delay.spawnChance) return rejected("minecraft:delay_filter");
|
|
183
|
+
}
|
|
184
|
+
return PASSED;
|
|
185
|
+
};
|
|
186
|
+
exports.SpawnDelays = SpawnDelays;
|
|
187
|
+
exports.evaluateSpawnCondition = evaluateSpawnCondition;
|
|
188
|
+
exports.spawnFilterContext = spawnFilterContext;
|
|
189
|
+
|
|
190
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZGl0aW9ucy5janMuY2pzIiwibmFtZXMiOlsiRElGRklDVUxUSUVTIiwiZGlzdGFuY2VTcXVhcmVkIiwiU1BBV05fQ09OU1RBTlRTIiwicGFzc0F0IiwiZXZhbHVhdGVGaWx0ZXIiXSwic291cmNlcyI6WyIuLi9zcmMvY29uZGl0aW9ucy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IEV2YWx1YXRlRmlsdGVyT3B0aW9ucywgRmlsdGVyQ29udGV4dCwgRmlsdGVyRW50aXR5VmlldyB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvZW50aXRpZXMnO1xuaW1wb3J0IHsgZXZhbHVhdGVGaWx0ZXIgfSBmcm9tICdAanNwcmlzbWFyaW5lL2VudGl0aWVzJztcbmltcG9ydCB0eXBlIHsgUmFuZG9tU291cmNlIH0gZnJvbSAnQGpzcHJpc21hcmluZS9wcmVkaWNhdGVzJztcblxuaW1wb3J0IHR5cGUgeyBTcGF3bkNvbnN0YW50cyB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCB7IFNQQVdOX0NPTlNUQU5UUyB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCB0eXBlIHsgRGlmZmljdWx0eSwgU3Bhd25Db25kaXRpb24sIFNwYXduRGVsYXksIFNwYXduUnVsZUNvbXBvbmVudCB9IGZyb20gJy4vcnVsZXMnO1xuaW1wb3J0IHsgRElGRklDVUxUSUVTIH0gZnJvbSAnLi9ydWxlcyc7XG5pbXBvcnQgdHlwZSB7IFNwYXduUGFzcywgU3Bhd25Qb3NpdGlvbiwgU3Bhd25Xb3JsZCB9IGZyb20gJy4vd29ybGQnO1xuaW1wb3J0IHsgZGlzdGFuY2VTcXVhcmVkLCBwYXNzQXQgfSBmcm9tICcuL3dvcmxkJztcblxuLyoqXG4gKiBEZWNpZGluZyB3aGV0aGVyIG9uZSBjb25kaXRpb24gb2YgYSBzcGF3biBydWxlIGFsbG93cyBhIHBvc2l0aW9uLlxuICpcbiAqIEEgcnVsZSdzIGBjb25kaXRpb25zYCBhcmUgYWx0ZXJuYXRpdmVzOiB0aGUgcnVsZSBhbGxvd3MgYSBwb3NpdGlvbiB3aGVuIGFueVxuICogb25lIGNvbmRpdGlvbiBkb2VzLiBFYWNoIGNvbmRpdGlvbiBpcyBhIGNvbmp1bmN0aW9uIG9mIGl0cyBjb21wb25lbnRzLCBhbmRcbiAqIHRoaXMgbW9kdWxlIHJlcG9ydHMgd2hpY2ggY29tcG9uZW50IHJlamVjdGVkIGEgcG9zaXRpb24gc28gYSBzZXJ2ZXIgY2FuXG4gKiBleHBsYWluIGFuIGVtcHR5IHNwYXduIGF0dGVtcHQgYW5kIGEgdGVzdCBjYW4gbmFtZSB0aGUgY29tcG9uZW50IGl0IGNvdmVycy5cbiAqXG4gKiBgbWluZWNyYWZ0OmJpb21lX2ZpbHRlcmAgZ29lcyB0aHJvdWdoIHRoZSBmaWx0ZXIgZXZhbHVhdG9yIG9mXG4gKiBgQGpzcHJpc21hcmluZS9lbnRpdGllc2AsIGJlY2F1c2Ugc3Bhd24gcnVsZXMsIGVudGl0eSBkZWZpbml0aW9ucyBhbmQgdHJhZGVcbiAqIHRhYmxlcyBzaGFyZSBvbmUgZmlsdGVyIGdyYW1tYXIgYW5kIG9uZSBldmFsdWF0b3IuXG4gKi9cblxuLyoqIFdoYXQgb25lIGNvbmRpdGlvbiBldmFsdWF0aW9uIHByb2R1Y2VkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTcGF3bkNvbmRpdGlvblJlc3VsdCB7XG4gICAgcmVhZG9ubHkgcGFzc2VkOiBib29sZWFuO1xuICAgIC8qKiBUaGUgZmlyc3QgY29tcG9uZW50IHRoYXQgcmVqZWN0ZWQgdGhlIHBvc2l0aW9uLCBhYnNlbnQgd2hlbiB0aGUgY29uZGl0aW9uIHBhc3NlZC4gKi9cbiAgICByZWFkb25seSByZWplY3RlZEJ5PzogU3Bhd25SdWxlQ29tcG9uZW50O1xufVxuXG4vKiogUmVhZHMgdGhlIGNvb2xkb3ducyBgbWluZWNyYWZ0OmRlbGF5X2ZpbHRlcmAga2VlcHMsIGtleWVkIGJ5IGl0cyBgaWRlbnRpZmllcmAuICovXG5leHBvcnQgaW50ZXJmYWNlIFNwYXduRGVsYXlWaWV3IHtcbiAgICAvKiogVGhlIHRpY2sgdGhlIG5hbWVkIGNvb2xkb3duIG5leHQgYWxsb3dzIGEgc3Bhd24sIG9yIHVuZGVmaW5lZCB3aGVuIGl0IGhhcyBuZXZlciBydW4uICovXG4gICAgcmVhZHlBdChpZGVudGlmaWVyOiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogVGhlIGNvb2xkb3ducyBgbWluZWNyYWZ0OmRlbGF5X2ZpbHRlcmAga2VlcHMuXG4gKlxuICogVGhlIHRocmVlIHBpbGxhZ2VyIHBhdHJvbCBjb25kaXRpb25zIGVhY2ggbmFtZSB0aGVpciBvd24gaWRlbnRpZmllciwgd2hpY2hcbiAqIGlzIGhvdyBvbmUgcGVyLWRpZmZpY3VsdHkgY29vbGRvd24gZXhpc3RzIHBlciB3b3JsZCByYXRoZXIgdGhhbiBvbmUgcGVyXG4gKiBjb25kaXRpb24uXG4gKi9cbmV4cG9ydCBjbGFzcyBTcGF3bkRlbGF5cyBpbXBsZW1lbnRzIFNwYXduRGVsYXlWaWV3IHtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJlYWR5ID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcblxuICAgIC8qKiBUaGUgdGljayB0aGUgbmFtZWQgY29vbGRvd24gbmV4dCBhbGxvd3MgYSBzcGF3biwgb3IgdW5kZWZpbmVkIHdoZW4gaXQgaGFzIG5ldmVyIHJ1bi4gKi9cbiAgICBwdWJsaWMgcmVhZHlBdChpZGVudGlmaWVyOiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICAgICAgICByZXR1cm4gdGhpcy5yZWFkeS5nZXQoaWRlbnRpZmllcik7XG4gICAgfVxuXG4gICAgLyoqIFNldHMgdGhlIHRpY2sgYXQgd2hpY2ggdGhlIG5hbWVkIGNvb2xkb3duIGFsbG93cyB0aGUgbmV4dCBzcGF3bi4gKi9cbiAgICBwdWJsaWMgc2NoZWR1bGUoaWRlbnRpZmllcjogc3RyaW5nLCB0aWNrOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5yZWFkeS5zZXQoaWRlbnRpZmllciwgdGljayk7XG4gICAgfVxuXG4gICAgLyoqIERyYXdzIGEgZGVsYXkgaW5zaWRlIHRoZSBmaWx0ZXIncyByYW5nZSBhbmQgc2NoZWR1bGVzIGl0IGZyb20gYHRpY2tgLiAqL1xuICAgIHB1YmxpYyBzY2hlZHVsZUZyb20oZGVsYXk6IFNwYXduRGVsYXksIHRpY2s6IG51bWJlciwgcmFuZG9tOiBSYW5kb21Tb3VyY2UpOiB2b2lkIHtcbiAgICAgICAgaWYgKGRlbGF5LmlkZW50aWZpZXIgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgICAgICBjb25zdCBzcGFuID0gTWF0aC5tYXgoMCwgZGVsYXkubWF4IC0gZGVsYXkubWluKTtcbiAgICAgICAgdGhpcy5zY2hlZHVsZShkZWxheS5pZGVudGlmaWVyLCB0aWNrICsgZGVsYXkubWluICsgTWF0aC5mbG9vcihyYW5kb20ubmV4dEZsb2F0KCkgKiAoc3BhbiArIDEpKSk7XG4gICAgfVxufVxuXG4vKiogRXZlcnl0aGluZyBvbmUgY29uZGl0aW9uIGV2YWx1YXRpb24gbmVlZHMgYmV5b25kIHRoZSBjb25kaXRpb24gYW5kIHRoZSBwb3NpdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3Bhd25Db25kaXRpb25Db250ZXh0IHtcbiAgICByZWFkb25seSB3b3JsZDogU3Bhd25Xb3JsZDtcbiAgICAvKiogVGhlIGVudGl0eSB0aGUgcnVsZSBzcGF3bnMsIHdoaWNoIGBtaW5lY3JhZnQ6ZGVuc2l0eV9saW1pdGAgY291bnRzLiAqL1xuICAgIHJlYWRvbmx5IGlkZW50aWZpZXI6IHN0cmluZztcbiAgICByZWFkb25seSByYW5kb206IFJhbmRvbVNvdXJjZTtcbiAgICAvKiogVGhlIGN1cnJlbnQgd29ybGQgdGljaywgd2hpY2ggYG1pbmVjcmFmdDpkZWxheV9maWx0ZXJgIGNvbXBhcmVzLiAqL1xuICAgIHJlYWRvbmx5IHRpY2s6IG51bWJlcjtcbiAgICByZWFkb25seSBjb25zdGFudHM/OiBTcGF3bkNvbnN0YW50cztcbiAgICByZWFkb25seSBkZWxheXM/OiBTcGF3bkRlbGF5VmlldztcbiAgICAvKiogUmVjZWl2ZXMgZXZlcnkgYmlvbWUgZmlsdGVyIHRlc3QgdGhlIGV2YWx1YXRvciBjb3VsZCBub3QgYW5zd2VyLiAqL1xuICAgIHJlYWRvbmx5IG9uVW5hbnN3ZXJlZD86IEV2YWx1YXRlRmlsdGVyT3B0aW9uc1snb25VbmFuc3dlcmVkJ107XG59XG5cbmNvbnN0IHJlamVjdGVkID0gKGNvbXBvbmVudDogU3Bhd25SdWxlQ29tcG9uZW50KTogU3Bhd25Db25kaXRpb25SZXN1bHQgPT4gKHsgcGFzc2VkOiBmYWxzZSwgcmVqZWN0ZWRCeTogY29tcG9uZW50IH0pO1xuXG5jb25zdCBQQVNTRUQ6IFNwYXduQ29uZGl0aW9uUmVzdWx0ID0geyBwYXNzZWQ6IHRydWUgfTtcblxuLyoqXG4gKiBCdWlsZHMgdGhlIGZpbHRlciBjb250ZXh0IGEgYG1pbmVjcmFmdDpiaW9tZV9maWx0ZXJgIHJlYWRzLlxuICpcbiAqIEEgc3Bhd24gcnVsZSBoYXMgbm8gZW50aXR5IHlldCwgc28gdGhlIGBzZWxmYCBzdWJqZWN0IGlzIHRoZSBwb3NpdGlvbjogaXRzXG4gKiBiaW9tZSB0YWdzIGFuc3dlciBgaGFzX2Jpb21lX3RhZ2AsIGl0cyBiaW9tZSBhbnN3ZXJzIGBpc19iaW9tZWAsIGFuZCBpdHNcbiAqIHNpdGUgZmxhZ3MgYW5zd2VyIGBpc19zbm93X2NvdmVyZWRgLCBgaXNfdW5kZXJncm91bmRgLCBgaXNfdW5kZXJ3YXRlcmAgYW5kXG4gKiBgaW5fbGF2YWAuIEV2ZXJ5IG90aGVyIHRlc3QgdGhlIHNoYXJlZCBncmFtbWFyIGFsbG93cyBnb2VzIHVuYW5zd2VyZWQgYW5kIGlzXG4gKiByZXBvcnRlZCwgd2hpY2ggaXMgd2hhdCB0aGUgZW5naW5lIGRvZXMgZm9yIGEgZmFjdCBpdCBjYW5ub3QgZXN0YWJsaXNoLlxuICovXG5leHBvcnQgY29uc3Qgc3Bhd25GaWx0ZXJDb250ZXh0ID0gKHdvcmxkOiBTcGF3bldvcmxkLCBhdDogU3Bhd25Qb3NpdGlvbik6IEZpbHRlckNvbnRleHQgPT4ge1xuICAgIGNvbnN0IHNlbGY6IEZpbHRlckVudGl0eVZpZXcgPSB7XG4gICAgICAgIGZsYWc6IChuYW1lKSA9PiB7XG4gICAgICAgICAgICBzd2l0Y2ggKG5hbWUpIHtcbiAgICAgICAgICAgICAgICBjYXNlICdpc19zbm93X2NvdmVyZWQnOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gd29ybGQuc2l0ZSgnc25vd19jb3ZlcmVkJywgYXQpO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2lzX3VuZGVyZ3JvdW5kJzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHdvcmxkLnNpdGUoJ3VuZGVyZ3JvdW5kJywgYXQpO1xuICAgICAgICAgICAgICAgIGNhc2UgJ2lzX3VuZGVyd2F0ZXInOlxuICAgICAgICAgICAgICAgIGNhc2UgJ2luX3dhdGVyJzpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHdvcmxkLnNpdGUoJ3VuZGVyd2F0ZXInLCBhdCk7XG4gICAgICAgICAgICAgICAgY2FzZSAnaW5fbGF2YSc6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB3b3JsZC5zaXRlKCdsYXZhJywgYXQpO1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIG5hbWVzOiAobmFtZSkgPT4ge1xuICAgICAgICAgICAgaWYgKG5hbWUgPT09ICdoYXNfYmlvbWVfdGFnJykgcmV0dXJuIHdvcmxkLmJpb21lVGFncyhhdCk7XG4gICAgICAgICAgICBpZiAobmFtZSA9PT0gJ2lzX2Jpb21lJykge1xuICAgICAgICAgICAgICAgIGNvbnN0IGJpb21lID0gd29ybGQuYmlvbWU/LihhdCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGJpb21lID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiBbYmlvbWVdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4ge1xuICAgICAgICBzZWxmLFxuICAgICAgICB3b3JsZDogeyBkaWZmaWN1bHR5OiAoKSA9PiB3b3JsZC5kaWZmaWN1bHR5KCksIHdlYXRoZXI6ICgpID0+IHdvcmxkLndlYXRoZXIoKSB9XG4gICAgfTtcbn07XG5cbi8qKiBUaGUgbGlnaHQgYSBicmlnaHRuZXNzIGZpbHRlciBjb21wYXJlcywgYWZ0ZXIgdGhlIGRhcmtlbmluZyB3ZWF0aGVyIGFwcGxpZXMgdG8gaXQuICovXG5jb25zdCBicmlnaHRuZXNzQXQgPSAoXG4gICAgd29ybGQ6IFNwYXduV29ybGQsXG4gICAgYXQ6IFNwYXduUG9zaXRpb24sXG4gICAgYWRqdXN0Rm9yV2VhdGhlcjogYm9vbGVhbixcbiAgICBjb25zdGFudHM6IFNwYXduQ29uc3RhbnRzXG4pOiBudW1iZXIgPT4ge1xuICAgIGNvbnN0IGxpZ2h0ID0gd29ybGQubGlnaHQoYXQpO1xuICAgIGlmICghYWRqdXN0Rm9yV2VhdGhlcikgcmV0dXJuIGxpZ2h0O1xuICAgIGNvbnN0IHdlYXRoZXIgPSB3b3JsZC53ZWF0aGVyKCk7XG4gICAgaWYgKHdlYXRoZXIgPT09ICdyYWluJykgcmV0dXJuIE1hdGgubWF4KDAsIGxpZ2h0IC0gY29uc3RhbnRzLnJhaW5EYXJrZW5pbmcpO1xuICAgIGlmICh3ZWF0aGVyID09PSAndGh1bmRlcicpIHJldHVybiBNYXRoLm1heCgwLCBsaWdodCAtIGNvbnN0YW50cy50aHVuZGVyRGFya2VuaW5nKTtcbiAgICByZXR1cm4gbGlnaHQ7XG59O1xuXG5jb25zdCBkaWZmaWN1bHR5UmFuayA9IChkaWZmaWN1bHR5OiBEaWZmaWN1bHR5KTogbnVtYmVyID0+IERJRkZJQ1VMVElFUy5pbmRleE9mKGRpZmZpY3VsdHkpO1xuXG4vKiogVGhlIGRpc3RhbmNlIGZyb20gYSBwb3NpdGlvbiB0byB0aGUgbmVhcmVzdCBwbGF5ZXIsIG9yIHVuZGVmaW5lZCB3aGVuIG5vIHBsYXllciBpcyBsb2FkZWQuICovXG5jb25zdCBuZWFyZXN0UGxheWVyRGlzdGFuY2UgPSAod29ybGQ6IFNwYXduV29ybGQsIGF0OiBTcGF3blBvc2l0aW9uKTogbnVtYmVyIHwgdW5kZWZpbmVkID0+IHtcbiAgICBsZXQgYmVzdDogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICAgIGZvciAoY29uc3QgcGxheWVyIG9mIHdvcmxkLnBsYXllcnMoKSkge1xuICAgICAgICBjb25zdCBkaXN0YW5jZSA9IGRpc3RhbmNlU3F1YXJlZChwbGF5ZXIucG9zaXRpb24sIGF0KTtcbiAgICAgICAgaWYgKGJlc3QgPT09IHVuZGVmaW5lZCB8fCBkaXN0YW5jZSA8IGJlc3QpIGJlc3QgPSBkaXN0YW5jZTtcbiAgICB9XG4gICAgcmV0dXJuIGJlc3QgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IE1hdGguc3FydChiZXN0KTtcbn07XG5cbmNvbnN0IG1hdGNoZXNCbG9jayA9IChpZGVudGlmaWVyOiBzdHJpbmcgfCB1bmRlZmluZWQsIG5hbWVzOiByZWFkb25seSBzdHJpbmdbXSk6IGJvb2xlYW4gPT5cbiAgICBpZGVudGlmaWVyICE9PSB1bmRlZmluZWQgJiYgbmFtZXMuaW5jbHVkZXMoaWRlbnRpZmllcik7XG5cbi8qKlxuICogRGVjaWRlcyB3aGV0aGVyIG9uZSBjb25kaXRpb24gYWxsb3dzIGEgc3Bhd24gYXQgYSBwb3NpdGlvbi5cbiAqXG4gKiBUaGUgY29tcG9uZW50cyBydW4gY2hlYXBlc3QgZmlyc3QsIGFuZCB0aGUgZmlyc3Qgb25lIHRoYXQgcmVqZWN0cyBuYW1lc1xuICogaXRzZWxmIGluIHRoZSByZXN1bHQuIEEgY29uZGl0aW9uIHRoYXQgZGVjbGFyZXMgbmVpdGhlclxuICogYG1pbmVjcmFmdDpzcGF3bnNfdW5kZXJ3YXRlcmAgbm9yIGBtaW5lY3JhZnQ6c3Bhd25zX2xhdmFgIHJlZnVzZXMgYSBwb3NpdGlvblxuICogaW5zaWRlIGVpdGhlciBsaXF1aWQsIHdoaWNoIGlzIHRoZSByZWFkaW5nIHRoYXQga2VlcHMgbGFuZCBtb2JzIG91dCBvZiB0aGVcbiAqIG9jZWFuOyBNb2phbmcgc3RhdGVzIHRoZSBjb21wb25lbnRzIGFuZCBub3QgdGhhdCBydWxlLlxuICpcbiAqIEBwYXJhbSByZXF1ZXN0ZWRQYXNzIC0gVGhlIHBhc3MgdGhlIHNwYXduIGxvb3AgaXMgZXZhbHVhdGluZywgb3IgYWJzZW50IHRvXG4gKiAgIGRlcml2ZSBpdCBmcm9tIHRoZSB3b3JsZCdzIHNpdGUgZmFjdHMuXG4gKi9cbmV4cG9ydCBjb25zdCBldmFsdWF0ZVNwYXduQ29uZGl0aW9uID0gKFxuICAgIGNvbmRpdGlvbjogU3Bhd25Db25kaXRpb24sXG4gICAgYXQ6IFNwYXduUG9zaXRpb24sXG4gICAgY29udGV4dDogU3Bhd25Db25kaXRpb25Db250ZXh0LFxuICAgIHJlcXVlc3RlZFBhc3M/OiBTcGF3blBhc3Ncbik6IFNwYXduQ29uZGl0aW9uUmVzdWx0ID0+IHtcbiAgICBjb25zdCB7IHdvcmxkIH0gPSBjb250ZXh0O1xuICAgIGNvbnN0IGNvbnN0YW50cyA9IGNvbnRleHQuY29uc3RhbnRzID8/IFNQQVdOX0NPTlNUQU5UUztcbiAgICBjb25zdCBwYXNzID0gcmVxdWVzdGVkUGFzcyA/PyBwYXNzQXQod29ybGQsIGF0KTtcblxuICAgIGlmIChjb25kaXRpb24uc3Bhd25zT25TdXJmYWNlIHx8IGNvbmRpdGlvbi5zcGF3bnNVbmRlcmdyb3VuZCkge1xuICAgICAgICBpZiAocGFzcyA9PT0gJ3N1cmZhY2UnICYmICFjb25kaXRpb24uc3Bhd25zT25TdXJmYWNlKSByZXR1cm4gcmVqZWN0ZWQoJ21pbmVjcmFmdDpzcGF3bnNfdW5kZXJncm91bmQnKTtcbiAgICAgICAgaWYgKHBhc3MgPT09ICd1bmRlcmdyb3VuZCcgJiYgIWNvbmRpdGlvbi5zcGF3bnNVbmRlcmdyb3VuZCkgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6c3Bhd25zX29uX3N1cmZhY2UnKTtcbiAgICB9XG5cbiAgICBjb25zdCBpbldhdGVyID0gd29ybGQuc2l0ZSgndW5kZXJ3YXRlcicsIGF0KTtcbiAgICBjb25zdCBpbkxhdmEgPSB3b3JsZC5zaXRlKCdsYXZhJywgYXQpO1xuICAgIGlmIChjb25kaXRpb24uc3Bhd25zVW5kZXJ3YXRlciAmJiAhaW5XYXRlcikgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6c3Bhd25zX3VuZGVyd2F0ZXInKTtcbiAgICBpZiAoY29uZGl0aW9uLnNwYXduc0xhdmEgJiYgIWluTGF2YSkgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6c3Bhd25zX2xhdmEnKTtcbiAgICBpZiAoIWNvbmRpdGlvbi5zcGF3bnNVbmRlcndhdGVyICYmIGluV2F0ZXIpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OnNwYXduc191bmRlcndhdGVyJyk7XG4gICAgaWYgKCFjb25kaXRpb24uc3Bhd25zTGF2YSAmJiBpbkxhdmEpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OnNwYXduc19sYXZhJyk7XG5cbiAgICBpZiAoY29uZGl0aW9uLmRpc2FsbG93U3Bhd25zSW5CdWJibGUgJiYgd29ybGQuc2l0ZSgnYnViYmxlX2NvbHVtbicsIGF0KSlcbiAgICAgICAgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6ZGlzYWxsb3dfc3Bhd25zX2luX2J1YmJsZScpO1xuXG4gICAgaWYgKGNvbmRpdGlvbi5oZWlnaHQgIT09IHVuZGVmaW5lZCAmJiAoYXQueSA8IGNvbmRpdGlvbi5oZWlnaHQubWluIHx8IGF0LnkgPiBjb25kaXRpb24uaGVpZ2h0Lm1heCkpXG4gICAgICAgIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OmhlaWdodF9maWx0ZXInKTtcblxuICAgIGlmIChjb25kaXRpb24uYmxvY2tzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3QgYmVsb3cgPSB3b3JsZC5ibG9jayh7IHg6IGF0LngsIHk6IGF0LnkgLSAxLCB6OiBhdC56IH0pO1xuICAgICAgICBpZiAoIW1hdGNoZXNCbG9jayhiZWxvdywgY29uZGl0aW9uLmJsb2NrcykpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OnNwYXduc19vbl9ibG9ja19maWx0ZXInKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZGl0aW9uLnByZXZlbnRlZEJsb2NrcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnN0IGJlbG93ID0gd29ybGQuYmxvY2soeyB4OiBhdC54LCB5OiBhdC55IC0gMSwgejogYXQueiB9KTtcbiAgICAgICAgaWYgKG1hdGNoZXNCbG9jayhiZWxvdywgY29uZGl0aW9uLnByZXZlbnRlZEJsb2NrcykpXG4gICAgICAgICAgICByZXR1cm4gcmVqZWN0ZWQoJ21pbmVjcmFmdDpzcGF3bnNfb25fYmxvY2tfcHJldmVudGVkX2ZpbHRlcicpO1xuICAgIH1cblxuICAgIGlmIChjb25kaXRpb24uYWJvdmVCbG9ja3MgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAvLyBUaGUgY29tcG9uZW50IG5hbWVzIG5vIGRpcmVjdGlvbi4gXCJTcGF3bnMgYWJvdmUgYmxvY2tcIiByZWFkcyBhcyB0aGVcbiAgICAgICAgLy8gZW50aXR5IHN0YW5kaW5nIGFib3ZlIG9uZSBvZiB0aGUgYmxvY2tzLCBzbyB0aGUgc2NhbiBydW5zIGRvd253YXJkXG4gICAgICAgIC8vIGZyb20gdGhlIHBvc2l0aW9uIGZvciBgZGlzdGFuY2VgIGJsb2Nrcy5cbiAgICAgICAgbGV0IGZvdW5kID0gZmFsc2U7XG4gICAgICAgIGZvciAobGV0IGRlcHRoID0gMTsgZGVwdGggPD0gY29uZGl0aW9uLmFib3ZlQmxvY2tzLmRpc3RhbmNlICYmICFmb3VuZDsgZGVwdGggKz0gMSkge1xuICAgICAgICAgICAgZm91bmQgPSBtYXRjaGVzQmxvY2sod29ybGQuYmxvY2soeyB4OiBhdC54LCB5OiBhdC55IC0gZGVwdGgsIHo6IGF0LnogfSksIGNvbmRpdGlvbi5hYm92ZUJsb2Nrcy5ibG9ja3MpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZm91bmQpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OnNwYXduc19hYm92ZV9ibG9ja19maWx0ZXInKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZGl0aW9uLmJyaWdodG5lc3MgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zdCBsaWdodCA9IGJyaWdodG5lc3NBdCh3b3JsZCwgYXQsIGNvbmRpdGlvbi5icmlnaHRuZXNzLmFkanVzdEZvcldlYXRoZXIsIGNvbnN0YW50cyk7XG4gICAgICAgIGlmIChsaWdodCA8IGNvbmRpdGlvbi5icmlnaHRuZXNzLm1pbiB8fCBsaWdodCA+IGNvbmRpdGlvbi5icmlnaHRuZXNzLm1heClcbiAgICAgICAgICAgIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OmJyaWdodG5lc3NfZmlsdGVyJyk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbi5kaWZmaWN1bHR5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3QgcmFuayA9IGRpZmZpY3VsdHlSYW5rKHdvcmxkLmRpZmZpY3VsdHkoKSk7XG4gICAgICAgIGlmIChyYW5rIDwgZGlmZmljdWx0eVJhbmsoY29uZGl0aW9uLmRpZmZpY3VsdHkubWluKSB8fCByYW5rID4gZGlmZmljdWx0eVJhbmsoY29uZGl0aW9uLmRpZmZpY3VsdHkubWF4KSlcbiAgICAgICAgICAgIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OmRpZmZpY3VsdHlfZmlsdGVyJyk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbi53b3JsZEFnZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnN0IGFnZSA9IHdvcmxkLmFnZSgpO1xuICAgICAgICBpZiAoYWdlIDwgY29uZGl0aW9uLndvcmxkQWdlLm1pbikgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6d29ybGRfYWdlX2ZpbHRlcicpO1xuICAgICAgICBpZiAoY29uZGl0aW9uLndvcmxkQWdlLm1heCA+PSAwICYmIGFnZSA+IGNvbmRpdGlvbi53b3JsZEFnZS5tYXgpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OndvcmxkX2FnZV9maWx0ZXInKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZGl0aW9uLm1vYkV2ZW50ICE9PSB1bmRlZmluZWQgJiYgIXdvcmxkLm1vYkV2ZW50QWN0aXZlKGNvbmRpdGlvbi5tb2JFdmVudCkpXG4gICAgICAgIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0Om1vYl9ldmVudF9maWx0ZXInKTtcblxuICAgIGlmIChjb25kaXRpb24uZGlzdGFuY2UgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zdCBkaXN0YW5jZSA9IG5lYXJlc3RQbGF5ZXJEaXN0YW5jZSh3b3JsZCwgYXQpO1xuICAgICAgICBpZiAoZGlzdGFuY2UgPT09IHVuZGVmaW5lZCB8fCBkaXN0YW5jZSA8IGNvbmRpdGlvbi5kaXN0YW5jZS5taW4gfHwgZGlzdGFuY2UgPiBjb25kaXRpb24uZGlzdGFuY2UubWF4KVxuICAgICAgICAgICAgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6ZGlzdGFuY2VfZmlsdGVyJyk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbi5wbGF5ZXJJblZpbGxhZ2UgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zdCB7IGRpc3RhbmNlLCB2aWxsYWdlQm9yZGVyVG9sZXJhbmNlIH0gPSBjb25kaXRpb24ucGxheWVySW5WaWxsYWdlO1xuICAgICAgICBjb25zdCBpblJhbmdlID0gd29ybGQucGxheWVycygpLmZpbHRlcigocGxheWVyKSA9PiBkaXN0YW5jZVNxdWFyZWQocGxheWVyLnBvc2l0aW9uLCBhdCkgPD0gZGlzdGFuY2UgKiBkaXN0YW5jZSk7XG4gICAgICAgIGNvbnN0IG5lYXJWaWxsYWdlID0gaW5SYW5nZS5zb21lKChwbGF5ZXIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGJvcmRlciA9IHdvcmxkLnZpbGxhZ2VCb3JkZXJEaXN0YW5jZT8uKHBsYXllci5wb3NpdGlvbik7XG4gICAgICAgICAgICByZXR1cm4gYm9yZGVyICE9PSB1bmRlZmluZWQgJiYgYm9yZGVyIDw9IHZpbGxhZ2VCb3JkZXJUb2xlcmFuY2U7XG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoIW5lYXJWaWxsYWdlKSByZXR1cm4gcmVqZWN0ZWQoJ21pbmVjcmFmdDpwbGF5ZXJfaW5fdmlsbGFnZV9maWx0ZXInKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZGl0aW9uLmJpb21lRmlsdGVyICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3Qgb3B0aW9uczogRXZhbHVhdGVGaWx0ZXJPcHRpb25zID0ge307XG4gICAgICAgIGlmIChjb250ZXh0Lm9uVW5hbnN3ZXJlZCAhPT0gdW5kZWZpbmVkKSBvcHRpb25zLm9uVW5hbnN3ZXJlZCA9IGNvbnRleHQub25VbmFuc3dlcmVkO1xuICAgICAgICBpZiAoIWV2YWx1YXRlRmlsdGVyKGNvbmRpdGlvbi5iaW9tZUZpbHRlciwgc3Bhd25GaWx0ZXJDb250ZXh0KHdvcmxkLCBhdCksIG9wdGlvbnMpKVxuICAgICAgICAgICAgcmV0dXJuIHJlamVjdGVkKCdtaW5lY3JhZnQ6YmlvbWVfZmlsdGVyJyk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbi5kZW5zaXR5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3QgbGltaXQgPSBwYXNzID09PSAnc3VyZmFjZScgPyBjb25kaXRpb24uZGVuc2l0eS5zdXJmYWNlIDogY29uZGl0aW9uLmRlbnNpdHkudW5kZXJncm91bmQ7XG4gICAgICAgIGlmIChsaW1pdCA+PSAwICYmIHdvcmxkLnR5cGVDb3VudChjb250ZXh0LmlkZW50aWZpZXIsIGF0LCBjb25zdGFudHMuZGVuc2l0eVJhZGl1cykgPj0gbGltaXQpXG4gICAgICAgICAgICByZXR1cm4gcmVqZWN0ZWQoJ21pbmVjcmFmdDpkZW5zaXR5X2xpbWl0Jyk7XG4gICAgfVxuXG4gICAgaWYgKGNvbmRpdGlvbi5kZWxheSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnN0IGlkZW50aWZpZXIgPSBjb25kaXRpb24uZGVsYXkuaWRlbnRpZmllcjtcbiAgICAgICAgY29uc3QgcmVhZHlBdCA9IGlkZW50aWZpZXIgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IGNvbnRleHQuZGVsYXlzPy5yZWFkeUF0KGlkZW50aWZpZXIpO1xuICAgICAgICBpZiAocmVhZHlBdCAhPT0gdW5kZWZpbmVkICYmIGNvbnRleHQudGljayA8IHJlYWR5QXQpIHJldHVybiByZWplY3RlZCgnbWluZWNyYWZ0OmRlbGF5X2ZpbHRlcicpO1xuICAgICAgICBpZiAoY29udGV4dC5yYW5kb20ubmV4dEZsb2F0KCkgKiAxMDAgPj0gY29uZGl0aW9uLmRlbGF5LnNwYXduQ2hhbmNlKSByZXR1cm4gcmVqZWN0ZWQoJ21pbmVjcmFmdDpkZWxheV9maWx0ZXInKTtcbiAgICB9XG5cbiAgICByZXR1cm4gUEFTU0VEO1xufTtcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUE0Q0EsSUFBYSxjQUFiLE1BQW1EO0NBQy9DLHdCQUF5QixJQUFJLElBQW9COztDQUdqRCxRQUFlLFlBQXdDO0VBQ25ELE9BQU8sS0FBSyxNQUFNLElBQUksVUFBVTtDQUNwQzs7Q0FHQSxTQUFnQixZQUFvQixNQUFvQjtFQUNwRCxLQUFLLE1BQU0sSUFBSSxZQUFZLElBQUk7Q0FDbkM7O0NBR0EsYUFBb0IsT0FBbUIsTUFBYyxRQUE0QjtFQUM3RSxJQUFJLE1BQU0sZUFBZSxLQUFBLEdBQVc7RUFDcEMsTUFBTSxPQUFPLEtBQUssSUFBSSxHQUFHLE1BQU0sTUFBTSxNQUFNLEdBQUc7RUFDOUMsS0FBSyxTQUFTLE1BQU0sWUFBWSxPQUFPLE1BQU0sTUFBTSxLQUFLLE1BQU0sT0FBTyxVQUFVLEtBQUssT0FBTyxFQUFFLENBQUM7Q0FDbEc7QUFDSjtBQWdCQSxJQUFNLFlBQVksZUFBeUQ7Q0FBRSxRQUFRO0NBQU8sWUFBWTtBQUFVO0FBRWxILElBQU0sU0FBK0IsRUFBRSxRQUFRLEtBQUs7Ozs7Ozs7Ozs7QUFXcEQsSUFBYSxzQkFBc0IsT0FBbUIsT0FBcUM7Q0EyQnZGLE9BQU87RUFDSCxNQUFBO0dBMUJBLE9BQU8sU0FBUztJQUNaLFFBQVEsTUFBUjtLQUNJLEtBQUssbUJBQ0QsT0FBTyxNQUFNLEtBQUssZ0JBQWdCLEVBQUU7S0FDeEMsS0FB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
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { EvaluateFilterOptions, FilterContext } from '@jsprismarine/entities';
|
|
2
|
+
import type { RandomSource } from '@jsprismarine/predicates';
|
|
3
|
+
import type { SpawnConstants } from './constants';
|
|
4
|
+
import type { SpawnCondition, SpawnDelay, SpawnRuleComponent } from './rules';
|
|
5
|
+
import type { SpawnPass, SpawnPosition, SpawnWorld } from './world';
|
|
6
|
+
/**
|
|
7
|
+
* Deciding whether one condition of a spawn rule allows a position.
|
|
8
|
+
*
|
|
9
|
+
* A rule's `conditions` are alternatives: the rule allows a position when any
|
|
10
|
+
* one condition does. Each condition is a conjunction of its components, and
|
|
11
|
+
* this module reports which component rejected a position so a server can
|
|
12
|
+
* explain an empty spawn attempt and a test can name the component it covers.
|
|
13
|
+
*
|
|
14
|
+
* `minecraft:biome_filter` goes through the filter evaluator of
|
|
15
|
+
* `@jsprismarine/entities`, because spawn rules, entity definitions and trade
|
|
16
|
+
* tables share one filter grammar and one evaluator.
|
|
17
|
+
*/
|
|
18
|
+
/** What one condition evaluation produced. */
|
|
19
|
+
export interface SpawnConditionResult {
|
|
20
|
+
readonly passed: boolean;
|
|
21
|
+
/** The first component that rejected the position, absent when the condition passed. */
|
|
22
|
+
readonly rejectedBy?: SpawnRuleComponent;
|
|
23
|
+
}
|
|
24
|
+
/** Reads the cooldowns `minecraft:delay_filter` keeps, keyed by its `identifier`. */
|
|
25
|
+
export interface SpawnDelayView {
|
|
26
|
+
/** The tick the named cooldown next allows a spawn, or undefined when it has never run. */
|
|
27
|
+
readyAt(identifier: string): number | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The cooldowns `minecraft:delay_filter` keeps.
|
|
31
|
+
*
|
|
32
|
+
* The three pillager patrol conditions each name their own identifier, which
|
|
33
|
+
* is how one per-difficulty cooldown exists per world rather than one per
|
|
34
|
+
* condition.
|
|
35
|
+
*/
|
|
36
|
+
export declare class SpawnDelays implements SpawnDelayView {
|
|
37
|
+
private readonly ready;
|
|
38
|
+
/** The tick the named cooldown next allows a spawn, or undefined when it has never run. */
|
|
39
|
+
readyAt(identifier: string): number | undefined;
|
|
40
|
+
/** Sets the tick at which the named cooldown allows the next spawn. */
|
|
41
|
+
schedule(identifier: string, tick: number): void;
|
|
42
|
+
/** Draws a delay inside the filter's range and schedules it from `tick`. */
|
|
43
|
+
scheduleFrom(delay: SpawnDelay, tick: number, random: RandomSource): void;
|
|
44
|
+
}
|
|
45
|
+
/** Everything one condition evaluation needs beyond the condition and the position. */
|
|
46
|
+
export interface SpawnConditionContext {
|
|
47
|
+
readonly world: SpawnWorld;
|
|
48
|
+
/** The entity the rule spawns, which `minecraft:density_limit` counts. */
|
|
49
|
+
readonly identifier: string;
|
|
50
|
+
readonly random: RandomSource;
|
|
51
|
+
/** The current world tick, which `minecraft:delay_filter` compares. */
|
|
52
|
+
readonly tick: number;
|
|
53
|
+
readonly constants?: SpawnConstants;
|
|
54
|
+
readonly delays?: SpawnDelayView;
|
|
55
|
+
/** Receives every biome filter test the evaluator could not answer. */
|
|
56
|
+
readonly onUnanswered?: EvaluateFilterOptions['onUnanswered'];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds the filter context a `minecraft:biome_filter` reads.
|
|
60
|
+
*
|
|
61
|
+
* A spawn rule has no entity yet, so the `self` subject is the position: its
|
|
62
|
+
* biome tags answer `has_biome_tag`, its biome answers `is_biome`, and its
|
|
63
|
+
* site flags answer `is_snow_covered`, `is_underground`, `is_underwater` and
|
|
64
|
+
* `in_lava`. Every other test the shared grammar allows goes unanswered and is
|
|
65
|
+
* reported, which is what the engine does for a fact it cannot establish.
|
|
66
|
+
*/
|
|
67
|
+
export declare const spawnFilterContext: (world: SpawnWorld, at: SpawnPosition) => FilterContext;
|
|
68
|
+
/**
|
|
69
|
+
* Decides whether one condition allows a spawn at a position.
|
|
70
|
+
*
|
|
71
|
+
* The components run cheapest first, and the first one that rejects names
|
|
72
|
+
* itself in the result. A condition that declares neither
|
|
73
|
+
* `minecraft:spawns_underwater` nor `minecraft:spawns_lava` refuses a position
|
|
74
|
+
* inside either liquid, which is the reading that keeps land mobs out of the
|
|
75
|
+
* ocean; Mojang states the components and not that rule.
|
|
76
|
+
*
|
|
77
|
+
* @param requestedPass - The pass the spawn loop is evaluating, or absent to
|
|
78
|
+
* derive it from the world's site facts.
|
|
79
|
+
*/
|
|
80
|
+
export declare const evaluateSpawnCondition: (condition: SpawnCondition, at: SpawnPosition, context: SpawnConditionContext, requestedPass?: SpawnPass) => SpawnConditionResult;
|
|
81
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAoB,MAAM,wBAAwB,CAAC;AAErG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAc,cAAc,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE1F,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpE;;;;;;;;;;;GAWG;AAEH,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CAC5C;AAED,qFAAqF;AACrF,MAAM,WAAW,cAAc;IAC3B,2FAA2F;IAC3F,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,cAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IAEnD,2FAA2F;IACpF,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErD;IAED,uEAAuE;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEtD;IAED,4EAA4E;IACrE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAI/E;CACJ;AAED,uFAAuF;AACvF,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,CAAC,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC;CACjE;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,UAAW,UAAU,MAAM,aAAa,KAAG,aA+BzE,CAAC;AAgCF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,cACpB,cAAc,MACrB,aAAa,WACR,qBAAqB,kBACd,SAAS,KAC1B,oBAuGF,CAAC"}
|