Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ElectroGamesDev/HyCitizens/llms.txt

Use this file to discover all available pages before exploring further.

ScheduleManager drives the daily routine system for HyCitizens. It evaluates every Citizen with scheduleConfig.enabled = true once per second, moving each one toward the appropriate schedule location and switching its NPC role to match the configured activity. Access the instance via CitizensManager.getScheduleManager().
ScheduleManager scheduleManager = HyCitizensPlugin.get().getCitizensManager().getScheduleManager();

How It Works

ScheduleManager runs a fixed-rate scheduler with a 1 000 ms tick interval. On each tick it iterates all Citizens and, for each one that has scheduling enabled, reads the current in-game time from the world’s WorldTimeResource, selects the highest-priority ScheduleEntry whose time window contains the current time, and transitions the Citizen through the following states:
  1. If the Citizen is not yet within arrivalRadius of the entry’s location, it is set to TRAVELING — the NPC role switches to a travel variant and a move-target marker is placed at the destination.
  2. Once the Citizen arrives it transitions to ACTIVE — movement stops, the role switches to the activity variant, and an optional arrival animation plays.
  3. When no entry is active the fallback mode determines behavior (see Fallback Modes below).
All world-entity operations are dispatched onto the correct world thread via World.execute().

Key Methods

refreshCitizen(CitizenData citizen)

Force-reprocesses a Citizen’s schedule on the next available world tick, bypassing the 1 000 ms cooldown. Call this immediately after updating a Citizen’s ScheduleConfig programmatically to apply the change without waiting for the next automatic tick.
scheduleManager.refreshCitizen(citizen);

clearCitizen(String citizenId)

Removes all schedule session state for a Citizen. This is called automatically when a Citizen is removed via CitizensManager.removeCitizen(). You can call it manually if you need to reset a Citizen’s scheduling state without removing it.
scheduleManager.clearCitizen("merchant-01");

getDesiredRoleName(CitizenData citizen)

Returns the NPC role name that the schedule system currently wants this Citizen to use. If no schedule session is active, it falls back to the base role from RoleGenerator. This method is primarily used internally but can be useful for debugging schedule state.
String roleName = scheduleManager.getDesiredRoleName(citizen);
System.out.println("Desired role: " + roleName);

shutdown()

Stops the internal scheduler and clears all session state. Called automatically by CitizensManager.shutdown() during plugin shutdown — you should not need to call this manually.

Schedule Runtime State

Each Citizen exposes its current scheduling state through CitizenData.getCurrentScheduleRuntimeState(), which returns a ScheduleRuntimeState enum value:
StateDescription
INACTIVESchedule is disabled or no entry is active; the Citizen follows its base movement behavior.
TRAVELINGThe Citizen is walking toward the target schedule location.
ACTIVEThe Citizen has arrived and is performing the scheduled activity.
FALLBACKNo active entry matches the current time and the fallback mode is in effect.
BLOCKEDA configuration error prevents scheduling (missing world, location world mismatch, etc.).
Additional transient fields on CitizenData expose further detail at runtime:
FieldTypeDescription
getCurrentScheduleEntryId()StringID of the currently active ScheduleEntry, or empty if none.
getCurrentScheduleRoleName()StringNPC role name currently applied by the schedule.
getCurrentScheduleStatusText()StringHuman-readable status string (e.g. "Traveling to Market Stall for Morning Work").

Fallback Modes

When no ScheduleEntry is active for the current time, ScheduleConfig.fallbackMode controls Citizen behavior:
ModeBehavior
USE_BASE_BEHAVIORThe Citizen returns to its configured movement behavior (wander, patrol, idle, etc.).
HOLD_LAST_SCHEDULE_STATEThe Citizen stays in its last schedule state without moving.
GO_TO_DEFAULT_LOCATION_IDLEThe Citizen travels to the location specified by ScheduleConfig.defaultLocationId and idles there.

Activity Types

ScheduleActivityType is an enum with the following values:
ValueDescription
IDLEThe Citizen stands still at the schedule location.
WANDERThe Citizen wanders within wanderRadius of the location.
PATROLThe Citizen patrols the path named in ScheduleEntry.patrolPathName.
FOLLOW_CITIZENThe Citizen follows another Citizen specified by ScheduleEntry.followCitizenId.

Configuring Schedules Programmatically

Build a ScheduleConfig with two schedule locations and two entries, then apply it to a Citizen:
import com.electro.hycitizens.models.*;
import java.util.*;

// Create locations
ScheduleLocation morningSpot = new ScheduleLocation(
    "morning",           // id
    "Market Stall",      // display name
    worldUUID,
    new Vector3d(100, 64, 200),
    new Vector3f(0, 0, 0)
);

ScheduleLocation eveningSpot = new ScheduleLocation(
    "evening", "Tavern", worldUUID,
    new Vector3d(120, 64, 210), new Vector3f(0, 0, 0)
);

// Create entries
ScheduleEntry morning = new ScheduleEntry();
morning.setId("morning-entry");
morning.setName("Morning Work");
morning.setEnabled(true);
morning.setStartTime24(8.0);   // 8:00 AM
morning.setEndTime24(17.0);    // 5:00 PM
morning.setLocationId("morning");
morning.setActivityType(ScheduleActivityType.IDLE);
morning.setTravelSpeed(10.0f);
morning.setArrivalRadius(1.5f);

ScheduleEntry evening = new ScheduleEntry();
evening.setId("evening-entry");
evening.setName("Evening Rest");
evening.setEnabled(true);
evening.setStartTime24(18.0);
evening.setEndTime24(22.0);
evening.setLocationId("evening");
evening.setActivityType(ScheduleActivityType.WANDER);
evening.setWanderRadius(5.0f);
evening.setTravelSpeed(10.0f);
evening.setArrivalRadius(1.5f);

// Build and apply the config
ScheduleConfig config = new ScheduleConfig();
config.setEnabled(true);
config.setLocations(List.of(morningSpot, eveningSpot));
config.setEntries(List.of(morning, evening));
config.setFallbackMode(ScheduleFallbackMode.USE_BASE_BEHAVIOR);

citizen.setScheduleConfig(config);
HyCitizensPlugin.get().getCitizensManager().updateCitizen(citizen, true);
After updating a Citizen’s ScheduleConfig, call scheduleManager.refreshCitizen(citizen) to apply the change immediately without waiting for the next automatic tick.
Schedule entries use 24-hour float notation. Midnight is 0.0, noon is 12.0, and 2:30 PM is 14.5. Values are clamped to the range [0.0, 24.0] by the setter. If startTime24 == endTime24 the entry is considered always active.

ScheduleEntry Field Reference

FieldTypeDefaultDescription
idString""Unique identifier for this entry within the schedule.
nameString""Human-readable name shown in status text.
enabledbooleantrueWhether this entry participates in selection.
startTime24double8.0Start of the active window in 24-hour float time.
endTime24double17.0End of the active window in 24-hour float time.
locationIdString""ID of a ScheduleLocation in the parent ScheduleConfig.
activityTypeScheduleActivityTypeIDLEWhat the Citizen does once it arrives.
arrivalRadiusfloat1.5fDistance in blocks at which the Citizen is considered to have arrived.
travelSpeedfloat10.0fMovement speed while traveling (clamped to ≥ 1.0).
wanderRadiusfloat5.0fWander area radius when activityType = WANDER (clamped to ≥ 1.0).
patrolPathNameString""Patrol path name when activityType = PATROL.
followCitizenIdString""Target Citizen ID when activityType = FOLLOW_CITIZEN.
followDistancefloat2.0fFollow distance in blocks (clamped to ≥ 0.1).
arrivalAnimationNameString""Animation to play once upon arrival (empty = none).
arrivalAnimationSlotint0Animation slot for the arrival animation.
priorityint0When multiple entries are active simultaneously, the one with the highest priority wins.

Build docs developers (and LLMs) love