Creatures
A Creature is a prefab representing an enemy, animal or NPC in Valheim. Creatures can be used in various spawning classes where certain conditions have to be fulfilled to instantiate those prefabs into the game. Jötunn provides an API for validating and adding custom creature prefabs to the game via the CustomCreature class using the CreatureManager. Basic spawn conditions can be defined by adding spawn configurations to those custom creature prefabs as well as drop lists using drop configs.
Note: The Unity screens and code snippets are taken from our example mod.
Creating Creature Prefabs
A prefab has to match certain criteria and has to follow a certain layout to be recognized as a creature prefab by Valheim. Those requirements are absoultely mandatory as part of the game code. A minimal creature prefab layout looks like this:
The base prefab consists of the prefab root GameObject and at least two child objects for the eye position and the visual part of the prefab (the armature and model).
Base Components
The root GameObject of the prefab has several mandatory components attached:
Instead of using the Character
and AnimalAI
components you can also use a combination of Humanoid
and MonsterAI
.
The first combination is used for "passive mobs" like deers or other animals.
The latter combination is used for mobs which can attack the player like Boars or Skeletons.
EyePos
The first child GO of the creature prefab is EyePos
and does not have any mandatory components itself but is a mandatory reference used in the Character
component:
Visual
The second child GO Visual
also has some mandatory components.
Those are normally not attached at this level of the prefab structure as the Visual
part is usually holding the armature and model of a prefab which each consist of further GOs when using more complex creature prefabs.
To keep it simple for this minimal example, the mandatory components as well as the model components are attached to this single child GO as follows:
When validating the creature prefab the game looks for the Animator
and CharacterAnimEvent
component at any level of the prefab's GO hierarchy.
Additional Considerations
There are some "gotcha"'s when creating more complex creatures.
- Certain child GameObjects have to be added to the
character
layer for the game's collision and attack calculations (and probably more use cases). Jötunn will set the base GO and the first level of child GOs to this layer automatically for you. There is also the static CreatureManager.CharacterLayer field for mods to assign this layer in code. - Creatures wanting to use Valheim's
Tameable
orProcreation
components must haveHumanoid
andMonsterAI
components attached.
Adding Custom Creature Prefabs
After preparing the creature prefab using the minimal requirements, we will load and import the prefab as a CustomCreature. This can be done as early as the mods Awake() call. All loaded creature prefabs added to the CreatureManager are kept between world loads. The most basic use case is to only add a prefab using as a custom creature and define if Jötunn should resolve mock references. In our example we opted to have more advanced uses to show all of Jötunn's possibilities. This example does:
- Create a new custom item to be used as the drop and consume item (see the item tutorial for more information on how to do that)
- Load an "animal" creature and add it to the game using drop and spawn configs by providing a CreatureConfig
- Load an "enemy" creature and add it to the game using consume and spawn configs by providing a CreatureConfig.
- Add localization for all added entities (see the localization tutorial for more information on that topic)
private void Awake()
{
// Create custom creatures and spawns
AddCustomCreaturesAndSpawns();
}
// Add custom made creatures using world spawns and drop lists
private void AddCustomCreaturesAndSpawns()
{
AssetBundle creaturesAssetBundle = AssetUtils.LoadAssetBundleFromResources("creatures");
try
{
// Load LulzCube test texture and sprite
var lulztex = AssetUtils.LoadTexture("JotunnModExample/Assets/test_tex.jpg");
var lulzsprite = Sprite.Create(lulztex, new Rect(0f, 0f, lulztex.width, lulztex.height), Vector2.zero);
// Create an optional drop/consume item for this creature
CreateDropConsumeItem(lulzsprite, lulztex);
// Load and create a custom animal creature
CreateAnimalCreature(creaturesAssetBundle, lulztex);
// Load and create a custom monster creature
CreateMonsterCreature(creaturesAssetBundle, lulztex);
// Add localization for all stuff added
Localization.AddTranslation("English", new Dictionary<string, string>
{
{"item_lulzanimalparts", "Parts of a Lulz Animal"},
{"item_lulzanimalparts_desc", "Remains of a LulzAnimal. It still giggles when touched."},
{"creature_lulzanimal", "Lulz Animal"},
{"creature_lulzmonster", "Lulz Monster"}
});
}
catch (Exception ex)
{
Logger.LogWarning($"Exception caught while adding custom creatures: {ex}");
}
finally
{
creaturesAssetBundle.Unload(false);
}
}
private void CreateDropConsumeItem(Sprite lulzsprite, Texture2D lulztex)
{
// Create a little lulz cube as the drop and consume item for both creatures
ItemConfig lulzCubeConfig = new ItemConfig();
lulzCubeConfig.Name = "$item_lulzanimalparts";
lulzCubeConfig.Description = "$item_lulzanimalparts_desc";
lulzCubeConfig.Icon = lulzsprite;
var lulzItem = new CustomItem("item_lul", true, lulzCubeConfig);
lulzItem.ItemDrop.m_itemData.m_shared.m_maxStackSize = 20;
lulzItem.ItemPrefab.AddComponent<Rigidbody>();
// Set our lulzcube test texture on the first material found
lulzItem.ItemPrefab.GetComponentInChildren<MeshRenderer>().material.mainTexture = lulztex;
// Make it smol
lulzItem.ItemPrefab.GetComponent<ZNetView>().m_syncInitialScale = true;
lulzItem.ItemPrefab.transform.localScale = new Vector3(0.33f, 0.33f, 0.33f);
// Add to the ItemManager
ItemManager.Instance.AddItem(lulzItem);
}
private void CreateAnimalCreature(AssetBundle creaturesAssetBundle, Texture2D lulztex)
{
// Load creature prefab from AssetBundle
var lulzAnimalPrefab = creaturesAssetBundle.LoadAsset<GameObject>("LulzAnimal");
// Set our lulzcube test texture on the first material found
lulzAnimalPrefab.GetComponentInChildren<MeshRenderer>().material.mainTexture = lulztex;
// Create a custom creature using our drop item and spawn configs
var lulzAnimalConfig = new CreatureConfig();
lulzAnimalConfig.Name = "$creature_lulzanimal";
lulzAnimalConfig.Faction = Character.Faction.AnimalsVeg;
lulzAnimalConfig.AddDropConfig(new DropConfig
{
Item = "item_lul",
Chance = 100f,
LevelMultiplier = false,
MinAmount = 1,
MaxAmount = 3,
//OnePerPlayer = true
});
lulzAnimalConfig.AddSpawnConfig(new SpawnConfig
{
Name = "Jotunn_LulzAnimalSpawn1",
SpawnChance = 100f,
SpawnInterval = 1f,
SpawnDistance = 1f,
MaxSpawned = 10,
Biome = Heightmap.Biome.Meadows
});
lulzAnimalConfig.AddSpawnConfig(new SpawnConfig
{
Name = "Jotunn_LulzAnimalSpawn2",
SpawnChance = 50f,
SpawnInterval = 2f,
SpawnDistance = 2f,
MaxSpawned = 5,
Biome = ZoneManager.AnyBiomeOf(Heightmap.Biome.BlackForest, Heightmap.Biome.Plains)
});
// Add it to the manager
CreatureManager.Instance.AddCreature(new CustomCreature(lulzAnimalPrefab, false, lulzAnimalConfig));
}
private void CreateMonsterCreature(AssetBundle creaturesAssetBundle, Texture2D lulztex)
{
// Load creature prefab from AssetBundle
var lulzMonsterPrefab = creaturesAssetBundle.LoadAsset<GameObject>("LulzMonster");
// Set our lulzcube test texture on the first material found
lulzMonsterPrefab.GetComponentInChildren<MeshRenderer>().material.mainTexture = lulztex;
// Create a custom creature using our consume item and spawn configs
var lulzMonsterConfig = new CreatureConfig();
lulzMonsterConfig.Name = "$creature_lulzmonster";
lulzMonsterConfig.Faction = Character.Faction.ForestMonsters;
lulzMonsterConfig.UseCumulativeLevelEffects = true;
lulzMonsterConfig.AddConsumable("item_lul");
lulzMonsterConfig.AddSpawnConfig(new SpawnConfig
{
Name = "Jotunn_LulzMonsterSpawn1",
SpawnChance = 100f,
MaxSpawned = 1,
Biome = Heightmap.Biome.Meadows
});
lulzMonsterConfig.AddSpawnConfig(new SpawnConfig
{
Name = "Jotunn_LulzMonsterSpawn2",
SpawnChance = 50f,
MaxSpawned = 1,
Biome = ZoneManager.AnyBiomeOf(Heightmap.Biome.BlackForest, Heightmap.Biome.Plains)
});
// Add it to the manager
CreatureManager.Instance.AddCreature(new CustomCreature(lulzMonsterPrefab, true, lulzMonsterConfig));
}
Modifying and Cloning Vanilla Creatures
You can get vanilla creatures and either clone them or modify the original to change any aspect of that creature you want. To make sure all vanilla creature prefabs are loaded, use the provided event OnVanillaCreaturesAvailable.
Cloned creatures keep all of their vanilla components except for the spawn configurations. This means unless you provide new spawn configurations, a cloned creature does not spawn at all. If you don't provide a new DropConfig for the cloned creature, all vanilla drops are kept. Providing a new DropConfig completely overrides all vanilla drops.
Since creature prefabs are loaded globally and Jötunn keeps all added creatures for the game session after adding them once, you can unsubscribe from the event after its first execution. Keep in mind that other mods could alter vanilla cratures, too, so it might be required to modify vanilla creatures on every event execution.
private void Awake()
{
// Hook creature manager to get access to vanilla creature prefabs
CreatureManager.OnVanillaCreaturesAvailable += ModifyAndCloneVanillaCreatures;
}
// Modify and clone vanilla creatures
private void ModifyAndCloneVanillaCreatures()
{
// Clone a vanilla creature with and add new spawn information
var lulzetonConfig = new CreatureConfig();
lulzetonConfig.AddSpawnConfig(new SpawnConfig
{
Name = "Jotunn_SkelSpawn1",
SpawnChance = 100,
SpawnInterval = 20f,
SpawnDistance = 1f,
Biome = Heightmap.Biome.Meadows,
MinLevel = 3
});
var lulzeton = new CustomCreature("Lulzeton", "Skeleton_NoArcher", lulzetonConfig);
var lulzoid = lulzeton.Prefab.GetComponent<Humanoid>();
lulzoid.m_walkSpeed = 0.1f;
CreatureManager.Instance.AddCreature(lulzeton);
// Get a vanilla creature prefab and change some values
var skeleton = CreatureManager.Instance.GetCreaturePrefab("Skeleton_NoArcher");
var humanoid = skeleton.GetComponent<Humanoid>();
humanoid.m_walkSpeed = 2;
// Unregister the hook, modified and cloned creatures are kept over the whole game session
CreatureManager.OnVanillaCreaturesAvailable -= ModifyAndCloneVanillaCreatures;
}
Drop Configurations
You can add one or more Drop Configurations to your custom creature. Drops are items a creature leaves behind on its death. You must provide a prefab name for the drop as a string, Jötunn resolves that prefab at runtime for you.
There are also additional properties to further the drops for the creature:
Property | Effect | Default |
---|---|---|
MinAmount MaxAmount |
How many of the drop items should be spawned. | 1 |
Chance | The chance of this drop in percent. | 100 |
OnePerPlayer | If true, one instance per active player will be dropped regardless of min/max amount. | false |
LevelMultiplier | Should the drop amount be multiplied by the creature level. | true |
Spawn Configurations
While adding creatures with Jötunn you can define one or more basic Spawn Configurations. Those configurations are added to the world spawn list so your creatures spawn into the world automatically. You must at least provide an unique name for your spawn configuration and a spawning biome.
There are plenty of properties to refine your spawn configuration:
Property | Effect | Default |
---|---|---|
WorldSpawnEnabled | If set to false, Jötunn creates the SpawnData for this config but disables the actual spawn. | true |
Biome | Heightmap.Biome for your creature to spawn in. Multiple biomes per spawn config are possible. To get the correct value for a multi-biome spawn, you can use ZoneManager.AnyBiomeOf. |
|
BiomeArea | Uses the Heightmap.BiomeArea enum to define if the spawn should be in the middle, on the edges or everywhere on the spawning biomes. | Heightmap.BiomeArea.Everywhere |
MaxSpawned | How many instances of this creature can be active at the same time (excluding event instances). | 1 |
SpawnInterval | Seconds between new spawn checks. | 4f |
SpawnChance | Spawn chance each spawn interval in percent. | 100f |
SpawnDistance | Minimum distance to another instance. | 10f |
MinSpawnRadius MaxSpawnRadius |
Min/max distance from player to spawn at. A specific player is chosen as a target, this setting basically creates a ring around the player, in which a spawn point can be chosen |
0 (equals the global default of 40) 0 (equals the global default of 80) |
MinLevel MaxLevel |
Min/max level the creature spawns with. Level is assigned by rolling levelup-chance for each level from min, until max is reached. |
1 1 |
RequiredGlobalKey | Only spawn if this key is set. See GameConstants.GlobalKey for constant values |
|
RequiredEnvironments | Only spawn if one of this environments is active. See GameConstants.Weather for constant values |
|
MinGroupSize MaxGroupSize |
Min/Max number of entities to attempt to spawn at a time. | 1 1 |
GroupRadius | Radius of circle, in which to spawn a pack of entities Eg., when group size is 3, all 3 spawns will happen inside a circle indicated by this radius. |
3f |
SpawnAtDay | Can spawn during day. Note: If not true, creatures with MonsterAI will attempt to despawn at day |
true |
SpawnAtNight | Can spawn during night | true |
MinAltitude MaxAltitude |
The min/max altitude (distance to water surface) for the creature to spawn | -1000f 1000f |
MinTilt MaxTilt |
The min/max tilt of terrain required to spawn. Tested multiple times to decide where to spawn entity. | 35f |
MinOceanDepth MaxOceanDepth |
The min/max ocean depth for the creature to spawn. Ignored if min == max. | 0 0 |
SpawnInForest | Spawn can happen in forest areas. | true |
SpawnOutsideForest | Spawn can happen outside of forest areas. | true |
HuntPlayer | Set true to let the AI hunt the player on spawn. | false |
GroundOffset | Offset to the ground the creature spawns on | 0.5f |
Jötunn's spawn configuration covers all of the game's default spawning options. To have much tighter control of your creature spawns, we recommend using the SpawnThat! mod by A Sharp Pen.
Consumable Items
Creatures using the MonsterAI
component can have ItemDrop
references attached to define which items this creature can consume when it encounters it in the world.
This list is also used to define the items needed to tame the creature (if the Tameable
component is also attached).
You can add prefab strings to the CreatureConfig.Consumables array and Jötunn will try to resolve the ItemDrop
component for you at runtime.
Cumulative Level Effects
When using the LevelEffects
component on a creature prefab, the game only enables the effects for the actual creature level upon spawning a creature with a level higher than 0.
Besides some material and transform alteration, those level effects also include an additional GameObject
you can set per level to be enabled.
Using Jötunn, you can set CreatureConfig.UseCumulativeLevelEffects for any added custom creature to true
.
This will enable all previous levels GamneObjects
as well as the object of your current level upon spawning the creature.
If you spawn a 3-star creature for example, the objects for level 2 and 1 will also be enabled.