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

Survivors

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

The game keeps one entity per survivor and asks for it from many of its systems: when a survivor sleeps, roams, gets a bed, or comes to a summoning staff. A mod that keeps several entities of one survivor answers those questions itself through load.on_survivor_lookup. The platform tells the askers apart and reads what each works on; the mod sees a Lookup with the survivor, the game’s own answer and a Purpose:

PurposeWho asksWhat the mod answers
UniquenessThe pass that keeps one entity per survivorentity to keep it, another one to remove it
SpawnA summoning staff used at targetThe entity to move there, or Nobody to spawn
BedOwnerA bed at bed_at binding its sleeperThe entity that may sleep there
AssignBedA player giving a bed with the staffThe entity that gets it
PreviousOccupantThe same assignment, asking who slept thereThat entity; Lookup::bed_of tells
RoamingA survivor’s idle behaviourThe entity whose base it keeps to
BedBindingThe first binding of beds after a loadUsually Nobody while several entities exist
CursorThe cursor’s distance check before assigningNobody skips the measure

Answer::Vanilla keeps the game’s answer. Two engine rules come with the answers: after a Uniqueness answer that keeps another entity than the game’s, the survivor’s record stays on the game’s own entity, and after a Spawn answer the move command reaches only the answered entity for a moment.

use enshwick::survivors::{Answer, Purpose};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
load.on_survivor_lookup(|lookup| match lookup.purpose {
// A staff always spawns a new entity instead of moving the old one.
Purpose::Spawn { .. } => Answer::Nobody,
_ => Answer::Vanilla,
})
}

One mod answers at a time; a second is refused. The lookup runs on the server’s worker threads, in the dedicated server or a hosting client, and for Cursor on a client alone too: keep it quick, never block, and read altars through a handle taken at init.

load.on_survivor_bed_check runs after a survivor’s own check kept its bed; returning false makes the game clear it. A BedCheck, Enshwick’s name, has the survivor’s npc_id, its entity, the bed_id, the survivor’s home cell and the bed’s cell, bed_at.

load.add_summon_item(item, survivor) files a mod item as a summoning staff of the survivor, on both sides, so the game treats it as that survivor’s staff. load.on_summon runs before a staff spawns or moves a survivor and returns a SummonVerdict: Take makes the use take the item, in the same transaction and only when the spawn or move works; Refuse refuses the use, and nothing spawns, moves or is taken; Allow leaves it to the game. load.on_summoned runs after a use that worked.

A Summon names the used item: its id, its slot, its own entity pide and, for an item with a meter, the meter as clients see it; after the use, consumed says whether the game took it. A staff with charges takes the item on its last charge from on_summon and spends one in on_summoned otherwise. Both run inside the game’s step, on the thread of the use: hand world writes to the scheduler.

survivors::WORKSHOP_SURVIVORS lists the game’s workshop survivors, each with its id, its vanilla staff and the template the staff places; WorkshopSurvivor::find("blacksmith") picks one by its short name.

use enshwick::survivors::{SummonVerdict, WorkshopSurvivor};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
const MY_STAFF: enshwick::ItemId = enshwick::ItemId::new(0x5e01);
let smith = WorkshopSurvivor::find("blacksmith")
.ok_or_else(|| enshwick::Off::new("no blacksmith"))?;
load.add_summon_item(MY_STAFF, smith.id())?;
// The last charge takes the staff; the others are spent after the use.
load.on_summon(|summon| {
let last = summon.meter.is_none_or(|charges| charges.current <= 1);
if summon.item == MY_STAFF && last {
SummonVerdict::Take
} else {
SummonVerdict::Allow
}
})?;
let (world, scheduler) = (load.world()?, load.scheduler()?);
load.on_summoned(move |summon| {
if let (MY_STAFF, false, Some(staff)) = (summon.item, summon.consumed, summon.pide) {
scheduler.next_tick(move || {
let _spent = world.change_item_meter(staff, |charges| charges - 1);
});
}
})
}

The client’s survivor menu keeps one record per survivor. With several entities of one survivor, load.on_survivor_status decides what it shows: it gets the survivor and the status byte of each entity, and returns the byte to show, or None for the game’s. Client only; one mod at a time.