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:
- 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.
- Once the Citizen arrives it transitions to
ACTIVE — movement stops, the role switches to the activity variant, and an optional arrival animation plays.
- 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:
| State | Description |
|---|
INACTIVE | Schedule is disabled or no entry is active; the Citizen follows its base movement behavior. |
TRAVELING | The Citizen is walking toward the target schedule location. |
ACTIVE | The Citizen has arrived and is performing the scheduled activity. |
FALLBACK | No active entry matches the current time and the fallback mode is in effect. |
BLOCKED | A configuration error prevents scheduling (missing world, location world mismatch, etc.). |
Additional transient fields on CitizenData expose further detail at runtime:
| Field | Type | Description |
|---|
getCurrentScheduleEntryId() | String | ID of the currently active ScheduleEntry, or empty if none. |
getCurrentScheduleRoleName() | String | NPC role name currently applied by the schedule. |
getCurrentScheduleStatusText() | String | Human-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:
| Mode | Behavior |
|---|
USE_BASE_BEHAVIOR | The Citizen returns to its configured movement behavior (wander, patrol, idle, etc.). |
HOLD_LAST_SCHEDULE_STATE | The Citizen stays in its last schedule state without moving. |
GO_TO_DEFAULT_LOCATION_IDLE | The Citizen travels to the location specified by ScheduleConfig.defaultLocationId and idles there. |
Activity Types
ScheduleActivityType is an enum with the following values:
| Value | Description |
|---|
IDLE | The Citizen stands still at the schedule location. |
WANDER | The Citizen wanders within wanderRadius of the location. |
PATROL | The Citizen patrols the path named in ScheduleEntry.patrolPathName. |
FOLLOW_CITIZEN | The 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
| Field | Type | Default | Description |
|---|
id | String | "" | Unique identifier for this entry within the schedule. |
name | String | "" | Human-readable name shown in status text. |
enabled | boolean | true | Whether this entry participates in selection. |
startTime24 | double | 8.0 | Start of the active window in 24-hour float time. |
endTime24 | double | 17.0 | End of the active window in 24-hour float time. |
locationId | String | "" | ID of a ScheduleLocation in the parent ScheduleConfig. |
activityType | ScheduleActivityType | IDLE | What the Citizen does once it arrives. |
arrivalRadius | float | 1.5f | Distance in blocks at which the Citizen is considered to have arrived. |
travelSpeed | float | 10.0f | Movement speed while traveling (clamped to ≥ 1.0). |
wanderRadius | float | 5.0f | Wander area radius when activityType = WANDER (clamped to ≥ 1.0). |
patrolPathName | String | "" | Patrol path name when activityType = PATROL. |
followCitizenId | String | "" | Target Citizen ID when activityType = FOLLOW_CITIZEN. |
followDistance | float | 2.0f | Follow distance in blocks (clamped to ≥ 0.1). |
arrivalAnimationName | String | "" | Animation to play once upon arrival (empty = none). |
arrivalAnimationSlot | int | 0 | Animation slot for the arrival animation. |
priority | int | 0 | When multiple entries are active simultaneously, the one with the highest priority wins. |