API Reference
Technical reference for plugin developers. Provides programmatic access to the Citizen and Dialog systems through the unified CoreAPI facade.
Module
The CoreAPI is provided by kyuubisoft-core.jar. Ensure the Core mod is loaded before calling any API methods.
Getting the API Instance
import com.kyuubisoft.core.api.CoreAPI;
// Availability check (recommended)
if (CoreAPI.isAvailable()) {
CoreAPI api = CoreAPI.getInstance();
// ...
}
// Or direct (returns null if Core is not loaded)
CoreAPI api = CoreAPI.getInstance();
if (api == null) {
// Core plugin not loaded
return;
}
Citizen Methods
| Method | Return Type | Description |
|---|---|---|
getCitizen(String id) | CitizenData | Get CitizenData by ID, or null if not found |
getAllCitizens() | Collection<CitizenData> | Get all registered citizens (never null) |
getCitizensByGroup(String group) | List<CitizenData> | Get citizens matching a group (never null) |
addCitizenListener(CitizenListener) | void | Add a citizen lifecycle listener |
removeCitizenListener(CitizenListener) | void | Remove a citizen lifecycle listener |
addDialogInterceptor(CitizenDialogInterceptor) | void | Add a dialog interceptor (checked before default dialogs) |
removeDialogInterceptor(CitizenDialogInterceptor) | void | Remove a dialog interceptor |
dispatchCitizenInteract(Player, String citizenId) | void | Trigger a citizen interaction event programmatically |
pauseCitizenMovement(CitizenData, Player) | void | Pause NPC movement (e.g., during dialog) |
resumeCitizenMovement(String citizenId) | void | Resume NPC movement after interaction |
isCitizenPaused(String citizenId) | boolean | Check if an NPC is currently paused |
Dialog Methods
| Method | Return Type | Description |
|---|---|---|
openDialog(Player, PlayerRef, Ref, Store, String dialogId, String citizenId) | void | Open a dialog by its ID |
openDialogFromTree(Player, PlayerRef, Ref, Store, DialogTree, String citizenId) | void | Open a dialog from a pre-built DialogTree object |
addDialogConditionProvider(DialogConditionProvider) | void | Register a custom condition provider for dialog branching |
removeDialogConditionProvider(DialogConditionProvider) | void | Remove a condition provider |
Interfaces
CitizenListener
Implement this interface to receive citizen lifecycle events. All methods have default no-op implementations, so you only need to override what you need.
import com.kyuubisoft.core.citizen.CitizenListener;
public interface CitizenListener {
/** Player interacts with an NPC (before dialog opens) */
default void onCitizenInteract(Player player, String citizenId) {}
/** Player selects a dialog choice */
default void onDialogChoice(Player player, String dialogId, int choiceIndex, String choiceText) {}
/** Dialog chain is fully completed */
default void onDialogComplete(Player player, String dialogId) {}
/** Player enters text input during a dialog */
default void onDialogInput(Player player, String dialogId, String input) {}
}
CitizenDialogInterceptor
Implement this interface to intercept NPC interactions before the default dialog system runs. If your interceptor returns true, no further dialog processing occurs.
import com.kyuubisoft.core.citizen.CitizenDialogInterceptor;
public interface CitizenDialogInterceptor {
/**
* Attempt to intercept the dialog for a citizen.
*
* @return true if this interceptor handled the interaction (no further dialog will open)
*/
boolean interceptDialog(Player player, PlayerRef playerRef,
Ref<EntityStore> ref, Store<EntityStore> store,
String citizenId);
}
DialogConditionProvider
Implement this interface to provide custom conditions for dialog branching. Used by the Quest mod, Achievement mod, and others to enable conditional dialog flows.
import com.kyuubisoft.core.dialog.DialogConditionProvider;
public interface DialogConditionProvider {
/** Whether this provider handles the given condition type. */
boolean handles(String conditionType);
/** Evaluate the condition for a player. */
boolean evaluate(Player player, String conditionType, String value, boolean negate);
}
Usage Examples
Get Citizen Data
CoreAPI api = CoreAPI.getInstance();
CitizenData citizen = api.getCitizen("blacksmith_npc");
if (citizen != null) {
String name = citizen.getDisplayName();
boolean hasShop = citizen.shopId != null;
LOGGER.info("Citizen: " + name + ", shop: " + hasShop);
}
// Get all citizens in a group
List<CitizenData> guards = api.getCitizensByGroup("town_guards");
LOGGER.info("Found " + guards.size() + " town guards");
React to Citizen Interactions (Interceptor)
CoreAPI api = CoreAPI.getInstance();
api.addDialogInterceptor((player, playerRef, ref, store, citizenId) -> {
if ("quest_giver".equals(citizenId)) {
// Open custom quest UI instead of default dialog
openQuestUI(player, playerRef, ref, store);
return true; // Intercepted — no default dialog
}
return false; // Let default dialog system handle it
});
Open a Dialog Programmatically
CoreAPI api = CoreAPI.getInstance();
// Open a specific dialog for a citizen
// (typically called from within a command or event handler that has access to PlayerRef/Ref/Store)
api.openDialog(player, playerRef, ref, store, "greeting_dialog", "merchant_npc");
Pause and Resume NPC Movement
CoreAPI api = CoreAPI.getInstance();
CitizenData npc = api.getCitizen("patrol_guard");
if (npc != null && !api.isCitizenPaused("patrol_guard")) {
// Pause the NPC while player interacts
api.pauseCitizenMovement(npc, player);
// ... do interaction ...
// Resume movement when done
api.resumeCitizenMovement("patrol_guard");
}
Register a Dialog Condition Provider
CoreAPI api = CoreAPI.getInstance();
api.addDialogConditionProvider(new DialogConditionProvider() {
@Override
public boolean handles(String type) {
return "quest_active".equals(type) || "quest_completed".equals(type);
}
@Override
public boolean evaluate(Player player, String type, String value, boolean negate) {
boolean result = switch (type) {
case "quest_active" -> questService.isQuestActive(player, value);
case "quest_completed" -> questService.isQuestCompleted(player, value);
default -> false;
};
return negate ? !result : result;
}
});
Thread Safety
- The API uses
CopyOnWriteArrayListfor all listener and interceptor lists (thread-safe registration/removal) - All query methods (
getCitizen,getAllCitizens,getCitizensByGroup,isCitizenPaused) can be called from any thread pauseCitizenMovementandresumeCitizenMovementinvolve entity operations and should be called withinworld.execute()when possible- Dialog opening methods (
openDialog,openDialogFromTree) involve UI operations and should be called from the main thread or withinworld.execute()
Java Package Structure
com.kyuubisoft.core.citizen/
├── CitizenData.java - NPC data model
├── CitizenService.java - Citizen lifecycle management
├── CitizenListener.java - Lifecycle event interface
├── CitizenDialogInterceptor.java - Dialog intercept interface
com.kyuubisoft.core.dialog/
├── DialogService.java - Dialog management
├── DialogTree.java - Dialog tree model
├── DialogNode.java - Dialog node model
├── DialogConditionProvider.java - Condition provider interface