Перейти до вмісту

Content

Цей контент ще не доступний вашою мовою.

A mod can add game content: items, recipes, crafting stations, texts, and any other game resource. This part of the book covers the high-level calls first and the served resources under them last:

  • Items: a new item cloned from a vanilla one, with its own name, icon, tooltip lines and meter;
  • Recipes: single recipes and derived copies of vanilla ones;
  • Workshops: a crafting station with a workshop of its own;
  • Loot containers: reading the corpses and loot bags enemies leave;
  • Random loot: mod items in random loot, each with an exact chance;
  • Served resources: any resource, new or changed, built from bytes;
  • Texts: names and descriptions per language;
  • Data packs: all of the above from TOML files, in a folder with no code or inside a mod, and changes of vanilla values such as stack sizes.

Inventories, which read and change what players and chests hold, have a chapter of their own.

The game reads its resources on both sides: the server decides what a recipe costs and what an item does, the client shows the item and its crafting menu. A mod that adds content runs on both, and server and players must run the same version of it:

fn manifest() -> &'static enshwick::abi::Manifest {
enshwick::manifest!(id: "you.my-mod", side: BOTH, join: true)
}

join: true puts the mod in the mod set the server advertises, so a player without it is warned before joining; see The join check. enshwick-personal-safes and enshwick-game-rates are content mods of this kind.

Every content call is made during init. The loader seals what mods registered once they are initialised, and the game builds each resource the first time it reads it.

Every piece of content a mod adds has a content id, <mod>:<name>: the item, its recipe, the station template, the workshop, each text tag and icon. The mod side is the mod’s own id; the name is 1 to 128 of [a-z0-9_./-], so ids read like paths: you.lockers:locker, you.lockers:locker.name, you.market-stalls:stall/blacksmith. Declare them as constants with Id::new; a malformed id fails the build:

use enshwick::content::{Guid, Id};
const LOCKER: Id = Id::new("you.lockers:locker");
assert_eq!(LOCKER.guid(), Guid::named("you.lockers:locker"));
assert_eq!(LOCKER.guid().to_string(), "67842cb8-d551-542b-8765-fe1af9c28aca");

The game knows content by GUID, so the id turns into one: a version 5 (name-based, SHA-1) UUID of the id’s UTF-8 bytes in Enshwick’s namespace NAMESPACE, e2185889-0e5e-4977-9edd-d6e7bcd1393f. The same id gives the same GUID on every machine, in every release and in any tool: Python’s uuid.uuid5(uuid.UUID("e2185889-0e5e-4977-9edd-d6e7bcd1393f"), "you.lockers:locker") prints the GUID above, and cargo enshwick id you.lockers:locker prints it with its game id. Saves hold these GUIDs and the ids derived from them, so a content id is fixed for good once players have the content; to move content to another id, keep its old one through a migration.

The content calls take an Id for what they create and declare it to the loader, which turns the mod off when the id lies outside the mod’s own namespace or when its game id, a 32-bit hash of the GUID, is another content id’s, naming both ids and both mods:

error [lanterns] off: content id lanterns:lamp of lanterns has the key 0x1a2b3c4d of content id torches:torch of torches; one of them needs another name

Content a mod serves itself with Load::serve is declared with load.declare(id). References to existing content, a vanilla item to clone or another mod’s item for a recipe, stay plain Guids: Guid::parse for a vanilla GUID, Guid::named("you.lockers:locker") for another mod’s content, without depending on that mod. Stat kinds and affixes have <mod>:<name> ids too, and their keys come from the same GUIDs.

Several mods can add items, recipes and workshops at once. The item registry, the recipe registry, the workshop registry and the template list are shared: each mod’s change is a patch, and the loader runs the patches in load order, each on the result of the one before. Two mods that each add an item both end up in the registry.

What does not compose is two mods serving one new resource from different bases, such as two mods adding an item under the same GUID cloned from different vanilla items: the second is refused, and that mod is turned off. Served resources describes the rules in full.

Removing a content mod leaves the world playable:

  • items it added stay in inventories and chests as stand-ins. The game drops a stack whose item id it does not know, so for every item a mod cloned that no mod serves now the loader serves a stand-in under the item’s own GUID: a copy of the vanilla item it was cloned from, named “Item of a removed mod”. The stack keeps its id, count and data, and when the mod is installed again it serves its item and the stack is that item once more;
  • a placed object stays. When its template is one the mod cloned, the loader serves the vanilla template it came from under the clone’s GUID, so a station of a removed mod loads as the vanilla station it was cloned from.

For both the loader records every template and item a mod clones, with its vanilla source, in mod-data/enshwick/clones.toml. Keep the file: the record outlives the mods. The loader logs how many cloned templates read as their vanilla source and how many items stand in. An item renamed by a mod’s migration gets no stand-in, since the game reads it under its new GUID.

Both fallbacks also apply while the mod is installed but turned off. A stand-in works as its vanilla item does, and the count of a stack is held to the vanilla item’s stack size when the world loads.