Spawn
Spawn adds one person for the rest of the scene. When it can’t, it returns 0 and does not throw. Anything that goes wrong inside the game is written to the log once, and nothing half-built is left in the scene.
Things to know
Spawncopies a person from the game. ThesourceCharacterIdis a character number fromCharacters.All(), and the player and other mod NPCs can’t be a source.- The copy keeps the source’s name data, outfit and appearance data, and it is built on a 3D body (see below).
- The person stands on the ground below
position, not in the air. The game drops the spot onto the first surface within 4 units below it, starting 1 unit above it. (the ground is the first collider within 4 units below the position, with a 1 unit lift) - The game moves the person aside if something blocks the spot. A scene with nothing there refuses the spawn.
nameandlastNamebecome the person’s name. The display name is the two joined by a space, and it is the name shown when the player looks at the person.- The body decides what the person looks like in 3D.
bodyCharacterIdpicks the character whose 3D model (the prefab, the game’s ready-made template of that model) is used. The version ofSpawnwithout it passes0. 0uses the source’s own 3D model only when that model has the current rig, a model with the sliders the framework uses for looks. Otherwise it uses a default body. (the source’s own model is used only when its prefab has anAppearancecomponent in its children, the current slider rig; otherwise the default body is the non-player character with the lowest number inCharacters.All()that has a model with a slider rig (anAppearancecomponent) and anNPCInfocomponent, and that is not a mod NPC; when there is none, the spawn returns0and the framework logs it once)ModNpcInfo.BodyCharacterIdis the body that was used, or0when the source’s own model was used. A0that fell back to a default body reports that body’s number.- By default the person can’t be talked to. The game shows “Can’t talk”. The source’s conversation would run the source’s own quest and variable lines, which is not what you want for a new person.
keepSourceDialogue = truelets the person use the source’s conversation. The lines are the source’s, under the new name. (the copy keeps the source’s own conversation only when the body that was used is the source itself, which needsbodyCharacterId0and a source whose own model has the current rig, or whenbodyCharacterIdis the source’s own number)- A new person does nothing on its own. It has no timetable and no game record, so it stands still. The movement, action and appearance pages can address it by
CharacterId. See mod NPC routines for walks, wandering, patrols and daily timetables. (the person keeps the source’s AI components and is found by the game’s NPC lookup, soNpcMovement.MoveTo,NpcActionsandNpcAppearancework on it) List(modId)lists the people your mod has added right now.CharacterIdOfgives one person’s character number, or0when there is no such person.
Example
This mod adds a visitor who lasts until the scene changes, and a keeper who comes back after every load of the same scene. Both are built from the first character in the game who is not the player, three metres in front of the player. Each call returns 0 when it refuses, so the mod writes a warning and goes on. The limits below list the reasons.
using ObenseuerCore;
using UnityEngine;
using UnityEngine.SceneManagement;
[ModInfo("example.shopkeeper", "Shopkeeper", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class Shopkeeper : FrameworkMod
{
protected override void OnLoad()
{
// Adding a person touches the world, so it waits for a loaded save.
Events.WhenReady(() =>
{
int sourceId = 0;
foreach (CharacterInfo character in Characters.All())
if (!character.IsPlayer) { sourceId = character.Id; break; }
Vector3 spot = PlayerState.Position + Vector3.forward * 3f;
int visitor = ModNpcs.Spawn(Mod.Id, "visitor", sourceId, "Val", "Visitor", spot, 0f);
if (visitor == 0)
Log.Warn("The visitor was refused. See the limits below.");
string scene = SceneManager.GetActiveScene().name;
int keeper = ModNpcs.SpawnSaved(Mod.Id, "keeper", sourceId, "Sam", "Stone", spot + Vector3.right, 90f, scene, false);
if (keeper == 0)
Log.Warn("The keeper was refused. See the limits below.");
Log.Info("Visitor " + visitor + ", keeper " + keeper + ".");
});
}
}
Spawn is the visitor’s call. SpawnSaved stores the request for the keeper, so the keeper is added again with the same character number after a load, and it keeps its look, because both calls use the same npcId every time. See SpawnSaved for what is stored.
Limits
Spawnreturns0for a few plain reasons: the game is loading or saving, no scene with people in it is loaded, the source character is unknown, the body has no usable 3D model, there is no ground under the spot, or the samemodIdandnpcIdis already added.- Missing ground is also written to the log as a warning. Read the log to see which reason it was.
- Any other body number uses that character’s model. The player, a mod NPC, an unknown number, a negative number, or a character with no model returns
0. A negative number is written to the log once. - Whether the person can walk depends on the scene having a navigation graph (the map the game uses for finding paths).
- Most named people in the game have no 3D model of their own, so their look may not carry over. They are placed straight into a scene. The copy gets the source’s name and appearance data, but whether the look carries over depends on where the game stores that person’s look, so the copy may come out in the body’s default look.
- Pass the body number yourself, for example the gym person’s body, rather than relying on the default.
- NPC appearance can only switch parts that the body already has. If the body has none of the source’s parts, the copy shows the body’s own look and appearance changes do not show on it. If it has some of them, the body’s other parts are hidden.
keepSourceDialogue = truechanges the player’s save. Every quest and variable change that conversation makes is a change for the source person, and it is written to the player’s save. Use it only when that is what you want.keepSourceDialogue = trueonly works when the copy uses the source’s own 3D model. With any other body, talk stays off and the framework writes one line to the log. A source whose own model has no current rig gets a default body, so its conversation is off as well. Passing the source’s own character number asbodyCharacterIdkeeps the source’s own model and its conversation, even when that model has no current rig.- New dialogue lines are not part of this API. Runtime conversations come from dialogue authoring.