> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nmentities.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Natural Spawns

> Natural spawn rules from code, plus how a custom world generator plugs its biomes into them.

Natural spawn rules are the `type: spawn` entries of `Effects/`: mobs appearing where the world suits them, rather than at a placed [spawner](addon-spawners). This is the surface a **world generator** wants.

## The API

```java theme={null}
NaturalSpawnsAPI spawns = NMEntitiesAPI.naturalSpawns();

spawns.ids();                             // every loaded rule
spawns.get("taiga_alpha");                // the SpawnRule, or null
spawns.all();                             // every rule, in declaration order
spawns.liveCount("taiga_alpha");          // how many it has alive
spawns.ruleOf(entity);                    // which rule spawned this, or null
spawns.replacing("slime");                // rules standing in for natural slimes
spawns.isEnabled("taiga_alpha");

spawns.explain(location, null);           // what /nme spawnrule test prints, as lines
spawns.trigger("taiga_alpha", location);  // spawn it there now, ignoring rate
spawns.setEnabled("taiga_alpha", false);  // as the command does, remembered across restarts
spawns.reset("taiga_alpha", true);        // forget its mobs; true also removes them
```

`explain` is the debugging entry point: it returns the same lines `/nme spawnrule test` prints, so you can show a player why a rule did or didn't apply at a location.

## Integrating a custom world generator

A plugin that invents its own biomes knows better than any config file which of them should have which mobs. Two things make that work, and neither needs changes on our side.

### 1. Custom biomes are matchable by their own namespace

`isBiome{}` compares the full namespaced key, so a generator's biome is distinguishable from a vanilla one of the same name:

```yaml theme={null}
cond: isBiome{myworldgen:frozen_wastes}    # only that biome
cond: isBiome{frozen_wastes}               # any namespace's frozen_wastes
cond: isBiome{minecraft:forest}            # only vanilla forest, not myworldgen:forest
```

A bare name matches any namespace, so existing configs are unaffected. The same rule applies to `isBlock`, `isBlockBelow` and spawner `conditions:`.

### 2. Anything the key can't express, register as a condition

If your world model has concepts that aren't biomes — regions, dimensions, noise layers — [`registerCondition`](extending-effects#custom-conditions) puts them in the same vocabulary, usable in every rule's `cond:` and in ordinary `if` lines:

```java theme={null}
NMEntitiesAPI.effects().registerCondition("isMyRegion", arg -> {
    String want = arg == null ? null : arg.trim();
    if (want == null) return null;                       // null = invalid, line reported
    return (ctx, subject) -> want.equalsIgnoreCase(myRegionAt(ctx.getLocation()));
});
```

<Warning>
  Use `ctx.getLocation()`, not `ctx.getMob()`. A spawn rule evaluates conditions **before** anything is spawned, so there is no caster and `getMob()` throws. `getMobOrNull()` is there for conditions that work either way.
</Warning>

## Shipping bundled rules

Contributed rules merge into the registry and survive `/nme reload`.

```java theme={null}
Condition inFrozenWastes = (ctx, subject) ->
        "myworldgen:frozen_wastes".equals(ctx.getLocation().getBlock().getBiome().key().toString());

NMEntitiesAPI.naturalSpawns().contribute(Map.of(
    "wastes_penguins",
    new SpawnRule("wastes_penguins", "nm_penguin", inFrozenWastes, 0.6,
                  new SpawnCaps(3, 40, 0))));
```

A server owner overrides any of them by declaring the same id in an `Effects/` file: the generator supplies sensible defaults, the owner has the last word. Calling `contribute` again replaces the whole contributed set.

### Constructor shapes

`SpawnRule` takes `(id, mob, condition, rate, caps, pack, replaces, suppressOriginal, level, effects, enabled, source)` and is `@JvmOverloads`, so you may stop after any prefix — `new SpawnRule("id", "mob")` is valid. A `condition` may be a compiled `cond:` string or, as above, your own lambda.

`SpawnCaps(perChunk, perWorld, perPlayer)` is `@JvmOverloads` too. `0` means no ceiling.
