NPC Placement and Alignment

From DragonMineZ Wiki

NPC Placement and Alignment covers the files that decide where master and trainer NPCs stand and how a player's alignment changes the way each NPC treats them. Two related per-world JSON files do this, both under <world save>/dragonminez/npcs/, plus a datapack format for NPCs that live inside structures.

File What it controls
placements.json Where master and trainer NPCs spawn (position, dimension, initial alignment or relation).
alignment_rules.json How a player's alignment affects each NPC's hostility, friendliness and availability for interaction.

Both files load at server start and reload with /dmzreload config (or /dmzreload all); see Reloading and Updating Changes. A reload also applies the placements again to every loaded dimension.

Old worlds keep their old files Both files are only written with DragonMineZ's defaults when they are missing. A world created with an older DragonMineZ version keeps its old files, so it does not get new default entries (for example the v2.2 Otherworld NPCs and their new coordinates). To get the current defaults, delete the file (back it up first if you edited it) and run /dmzreload config.

NPC placement

placements.json lists every NPC DragonMineZ places and where.

Source: src/main/java/com/dragonminez/server/world/npc/NPCPlacementManager.java.

{
  "schema": 1,
  "placements": [
    {
      "id": "master_roshi",
      "entity": "dragonminez:master_roshi",
      "dimension": "minecraft:overworld",
      "structure": "roshi_house",
      "x": 0.0,
      "y": 0.0,
      "z": 0.0,
      "yaw": 225.0,
      "pitch": 0.0,
      "surface": true,
      "relative_to_spawn": false,
      "enabled": true,
      "override": false
    }
  ]
}
Field Type What it does
id String Unique placement id. It is tagged onto the spawned entity so DragonMineZ can find and update it later. If two placements share an id, the last one in the file wins (a warning is logged).
entity String Registry id of the entity type.
dimension String Defaults to minecraft:overworld.
npc_id String Only meaningful for dragonminez:quest_npc entities: links to the quest system's NPC id.
model / texture String Only used for quest_npc entities.
structure String If set, x, y and z become an offset from that structure's origin instead of world coordinates. Valid values: roshi_house, goku_house, elder_guru, timechamber, kamilookout, gero_lab. An unrecognized value falls back to treating x, y and z as raw world coordinates (with a warning in the log).
x, y, z Number World coordinates, or the offset from the structure (see above). Defaults: 0.5, 64.0, 0.5.
yaw, pitch Number Facing direction in degrees.
surface Boolean If true, snaps Y to the terrain surface at load time instead of using the given Y.
relative_to_spawn Boolean If true, adds the world's spawn point to x and z (applied after any structure offset).
enabled Boolean If false, this placement is skipped entirely.
override Boolean If true, the entity is always respawned, even if one already exists for this id. Otherwise an existing entity is just updated in place.
alignment Integer (0–100) Optional alignment for this specific NPC: above 60 it is friendly, from 41 to 60 neutral, and 40 or less hostile (unless an alignment rule makes it hostile first, see below).
relation String Optional override for this instance (HOSTILE, NEUTRAL or FRIENDLY), stored on the entity itself instead of being looked up from the alignment rules.

Unknown keys are reported in the JSON load report.

Default placements

The default file actively spawns these NPCs:

Placement id Entity Where
master_roshi dragonminez:master_roshi Roshi's house
master_goku dragonminez:master_goku Goku's house
master_karin, master_dende, master_popo dragonminez:master_karin, master_dende, master_popo Kami's Lookout
master_gero dragonminez:master_gero Gero's lab
master_guru dragonminez:master_guru Elder Guru's house (Namek)
master_kaiosama dragonminez:master_kaiosama Otherworld, 54.5 299 1031.5
master_toribot dragonminez:master_toribot Otherworld, 50.5 298 1030.5
master_enma dragonminez:master_enma Otherworld, 0.5 150 17.5
master_baba dragonminez:master_uranai Otherworld, 9.5 150 3.5
master_grandkai dragonminez:master_grandkai Otherworld, -30.5 150 12.5
npc_hell_ogre dragonminez:quest_npc (npc_id hell_ogre) Otherworld, 9.5 150 16.5
master_otherworld_announcer dragonminez:master_otherworld_announcer Otherworld Tournament grounds, -25.5 352 -1164.5 (only placed once the tournament grounds have been built)

Quest NPCs and master NPCs

Quest NPCs (Bulma, Krillin, Yamcha, Tien, Piccolo, Gohan, Vegeta, Trunks, Chi-Chi, Videl, Shin and the Namek Elder) appear in the default file too, but with "enabled": false: they are no longer spawned at runtime through this system. They are baked directly into the structure NBT, so they live at fixed, hand-placed spots instead of piling up at the world spawn. Their entries here are for reference only. NPCs added to a structure after its NBT was made come from structure NPC files instead (Chi-Chi in Goku's House is one).

The Otherworld NPCs (master_kaiosama, master_enma, master_baba, master_toribot, master_grandkai, master_otherworld_announcer and npc_hell_ogre) always force override: true internally, whatever the file says, since they need to exist reliably in a generated dimension. See Otherworld.

Structure NPCs

Structure NPC files place NPCs inside a DragonMineZ structure without editing its .nbt file. Unlike the files above, these are datapack files: data/<namespace>/structure_npcs/*.json, one per structure, loaded with the datapacks (and by /reload).

{
  "structure": "dragonminez:goku_house",
  "npcs": [
    {
      "id": "npc_chi_chi",
      "entity": "dragonminez:quest_npc",
      "npc_id": "chi_chi",
      "model": "saga_videl",
      "texture": "saga_chichi",
      "pos": [67.5, 5.0, 50.5],
      "yaw": 90.0
    }
  ]
}
Field Required What it does
structure Yes Id of the structure the NPCs belong to.
id Yes Unique id of this NPC inside the structure. It is also the tracking key, so don't rename it once worlds use it.
entity Yes Entity type to spawn (for example dragonminez:quest_npc or a master's entity id).
npc_id No Quest NPC id, for dragonminez:quest_npc.
model, texture No Model and texture names for quest NPCs.
template No Which template of the structure the position is relative to, for structures made of several pieces.
pos Yes Position relative to the structure's template, so it stays right whatever rotation the structure spawned with.
yaw No Facing, also relative to the template (default 0).
alignment, relation No Initial alignment and relation, as in placements.json.

Each NPC spawns once per generated structure when its chunk loads, including structures that were generated before the file existed, and the spawn is remembered: an NPC that dies doesn't come back.

To get the coordinates, stand where the NPC should be and run /dmzstructure here: it prints the structure, its template and your position and yaw relative to the template, with a Copy NPC JSON button that copies a ready-made entry. /dmzstructure npcs lists the NPCs of the structure you are in and whether each one has spawned yet. Both commands need the dragonminez.dmzstructure permission (see Permissions).

NPC alignment rules

See also: Alignment, Masters

alignment_rules.json decides how each NPC reacts to the player's alignment.

Source: src/main/java/com/dragonminez/common/alignment/NpcAlignmentRules.java.

{
  "npcs": {
    "goku": {
      "default_relation": "FRIENDLY",
      "interaction": { "min_alignment": 61 },
      "hostile_below": 25
    },
    "gero": {
      "default_relation": "NEUTRAL",
      "interaction": { "max_alignment": 60 }
    }
  }
}

The keys in npcs are NPC ids with any namespace stripped (dragonminez:roshi becomes the key roshi).

Field Type What it does
default_relation HOSTILE / NEUTRAL / FRIENDLY Base relation when none of the thresholds below overrides it. Defaults to NEUTRAL.
hostile_below Integer If the player's alignment is below this value, the NPC turns hostile regardless of default_relation.
hostile_above Integer The same, but for an alignment above this value.
interaction.min_alignment Integer Below this alignment the NPC still talks to the player, but greets them coldly, and its side quests show as locked (saga quests, quests with their own alignment condition and quests already in progress stay available). It can also be written directly on the rule, without the interaction wrapper.
interaction.max_alignment Integer The same, but as an upper bound.

All four threshold fields are optional: leave out whichever don't apply. Reading the goku entry above as an example: below 25 alignment Goku is hostile; from 25 to 60 he still talks to you, but coldly, and his side quests stay locked (the alignment is under the min_alignment of 61); at 61 or more he is friendly and everything is open.

NpcDispositionService.getRelation() checks the relation in this order:

  1. A relation set on the placement (placements.json) wins.
  2. The rule's hostile_below and hostile_above thresholds.
  3. An alignment set on the placement.
  4. The rule's default_relation.

Master NPCs

For master and trainer NPCs the rule file decides hostility, but their alignment gate is a fixed rule instead of interaction: masters can always be talked to, but Goku, Gohan, King Kai, Old Kai, Roshi and Krillin only unlock their Skills, Train and new quests at an alignment of at least 61, and Cell and Frieza at 40 or less. Every other master has no alignment gate. See Masters.

NPC ids covered by default

The default file covers goku, roshi, karin, guru, dende, popo, kingkai, gero, enma, baba, toribot, grandkai, otherworld_announcer, bulma, krillin, yamcha, tien, chiaotzu, gohan, trunks, chi_chi, videl, namek_elder, shin, piccolo and vegeta.

  • Most default to FRIENDLY with min_alignment: 41 (or 61 for Goku, Guru, Dende, King Kai and the Namek Elder) and hostile_below: 25.
  • gero defaults to NEUTRAL with max_alignment: 60.
  • enma, baba, toribot, grandkai, otherworld_announcer and vegeta default to NEUTRAL with no thresholds.
  • piccolo defaults to NEUTRAL with its own thresholds (min_alignment: 41, hostile_below: 20).

Reload and validate

  1. Edit the files under <world save>/dragonminez/npcs/.
  2. Validate the JSON syntax (see How to JSON).
  3. Run /dmzreload config (or /dmzreload all); see Reloading and Updating Changes.
  4. Check that the NPC spawned or moved as expected, and that hostility and interaction based on alignment behave as configured (test at a couple of alignment values if you can).