API: Difference between revisions
Line 36: | Line 36: | ||
==Creating a Trait== | ==Creating a Trait== | ||
Traits are persistent, attachable objects that are linked to an NPC and provide specific functionality. This can be anything from a full-blown dynamic villager AI to a simple talking trait. | Traits are persistent, attachable objects that are linked to an NPC and provide specific functionality. This can be anything from a full-blown dynamic villager AI to a simple talking trait. | ||
If using Maven, Citizens' Maven repo is available at http://repo.citizensnpcs.com | |||
To register a trait, we use the '''TraitFactory''' class. This controls registration for your custom traits. | To register a trait, we use the '''TraitFactory''' class. This controls registration for your custom traits. | ||
{{codebox|height=300px|width=100%|Example registration and simple trait|<syntaxhighlight lang="java5"> | {{codebox|height=300px|width=100%|Example registration and simple trait|<syntaxhighlight lang="java5"> | ||
//This is your trait that will be applied to a npc using the /trait mytraitname command. Each NPC gets its own instance of this class. | //This is your trait that will be applied to a npc using the /trait mytraitname command. Each NPC gets its own instance of this class. | ||
//the Trait class has a reference to the attached NPC class through 'npc' or getNPC(). | //the Trait class has a reference to the attached NPC class through the protected field 'npc' or getNPC(). | ||
//The Trait class also implements Listener so you can add EventHandlers directly to your trait. | //The Trait class also implements Listener so you can add EventHandlers directly to your trait. | ||
public class MyTrait extends Trait { | public class MyTrait extends Trait { | ||
Line 52: | Line 54: | ||
boolean SomeSetting = false; | boolean SomeSetting = false; | ||
// see the 'Persistence API' section | |||
@Persist("mysettingname") boolean automaticallyPersistedSetting = false; | |||
//Here you should load up any values you have previously saved. | // Here you should load up any values you have previously saved (optional). | ||
//This does NOT get called when applying the trait for the first time, only loading onto an existing npc at server start. | // This does NOT get called when applying the trait for the first time, only loading onto an existing npc at server start. | ||
//This is called AFTER onAttach so you can load defaults in onAttach and they will be overridden here. | // This is called AFTER onAttach so you can load defaults in onAttach and they will be overridden here. | ||
//This is called | // This is called AFTER onSpawn (2.0.4+), but npc.getBukkitEntity() may or may not be null. | ||
public void load(DataKey key) { | public void load(DataKey key) { | ||
SomeSetting = key.getBoolean("SomeSetting", false); | SomeSetting = key.getBoolean("SomeSetting", false); | ||
} | } | ||
//Save settings for this NPC. These values will be | // Save settings for this NPC (optional). These values will be persisted to the Citizens saves file | ||
public void save(DataKey key) { | public void save(DataKey key) { | ||
key.setBoolean("SomeSetting",SomeSetting); | key.setBoolean("SomeSetting",SomeSetting); | ||
} | } | ||
// An example event handler. All traits will be registered automatically as Bukkit Listeners. | |||
@EventHandler | @EventHandler | ||
public void click(net.citizensnpcs.api.event.NPCClickEvent event){ | public void click(net.citizensnpcs.api.event.NPCClickEvent event){ | ||
Line 72: | Line 78: | ||
} | } | ||
// Called every tick | |||
@Override | |||
public void run() { | |||
} | |||
//Run code when your trait is attached to a NPC. | //Run code when your trait is attached to a NPC. | ||
//This is called | //This is called AFTER onSpawn, but npc.getBukkitEntity() may or may not be null | ||
//This would be a good place to load configurable defaults for new NPCs. | //This would be a good place to load configurable defaults for new NPCs. | ||
@Override | @Override | ||
Line 87: | Line 97: | ||
} | } | ||
//Run code when the NPC is spawned. Note that npc.getBukkitEntity() | //Run code when the NPC is spawned. Note that npc.getBukkitEntity() may or may not be null until this method is called. | ||
//This is called AFTER onAttach and AFTER Load when the server is started. | //This is called AFTER onAttach and AFTER Load when the server is started. | ||
@Override | @Override | ||
Line 130: | Line 140: | ||
* Check npc.isSpawned() before using npc.getBukkitEntity() | * Check npc.isSpawned() before using npc.getBukkitEntity() | ||
* Check npc.isSpawned() before using npc.getNavigator() | * Check npc.isSpawned() before using npc.getNavigator() | ||
* Create a separate singleton Listener class if you expect there to be many instances of this trait running. This may help performance. | * Create a separate singleton Listener class if you expect there to be many instances of this trait running. This may help performance with frequently called events. | ||
* Honor npc.data().get(NPC.DEFAULT_PROTECTED_METADATA) If this is true the NPC should be 'invulnerable' to normal damaging effects. | * Honor npc.data().get(NPC.DEFAULT_PROTECTED_METADATA) If this is true the NPC should be 'invulnerable' to normal damaging effects. | ||
* use CitizensAPI.getNPCRegistry().isNPC() to check if an entity is a NPC. Real players and player-type NPCs will both return true for instanceof Player. | * use CitizensAPI.getNPCRegistry().isNPC() to check if an entity is a NPC. Real players and player-type NPCs will both return true for instanceof Player. | ||
'''{{color|red|white|DON'T}}''' | '''{{color|red|white|DON'T}}''' | ||
* Attempt to access npc.getBukkitEntity() from within traits until onSpawn() has been called. | * Attempt to access npc.getBukkitEntity() from within traits until onSpawn() has been called or npc.isSpawned() returns true. | ||
* Change anything in npc.getNavigator.getDefaultParams() unless you're sure you want a global change. Use the localParams() instead after setting a navigation target. | * Change anything in npc.getNavigator.getDefaultParams() unless you're sure you want a global change. Use the localParams() instead ''after'' setting a navigation target. | ||
* Assume a NPC is a player-type. Mob types have some important differences | * Assume a NPC is a player-type. Mob types have some important differences. | ||
===Download an example=== | ===Download an example=== |
Revision as of 12:55, 30 October 2012
|
Citizens has an extensive API that can be used for making your plugins work with NPCs or even for adding a brand new character that can be attached to an NPC. Make sure you always are using an up-to-date build of the CitizensAPI to ensure that your plugin works with the latest release of Citizens.
Javadocs can be found at http://jd.citizensnpcs.com
Hooking Into Citizens
Hooking into Citizens is as simple as creating a basic plugin and adding the line depend: [Citizens] into your plugin.yml. From here, a common basic entry point is the CitizensAPI class. This gives you access to the NPCRegistry for NPC lookup, as well as the TraitFactory which allows trait registration.
Checking if an entity is a Citizens NPC
Citizens NPCs will have the "NPC" metadata set to true. Eg.
boolean isCitizensNPC = entity.hasMetadata("NPC");
Creating a Trait
Traits are persistent, attachable objects that are linked to an NPC and provide specific functionality. This can be anything from a full-blown dynamic villager AI to a simple talking trait.
If using Maven, Citizens' Maven repo is available at http://repo.citizensnpcs.com
To register a trait, we use the TraitFactory class. This controls registration for your custom traits.
Code: Example registration and simple trait |
Dos and Don'ts
DO
- Check npc.isSpawned() before using npc.getBukkitEntity()
- Check npc.isSpawned() before using npc.getNavigator()
- Create a separate singleton Listener class if you expect there to be many instances of this trait running. This may help performance with frequently called events.
- Honor npc.data().get(NPC.DEFAULT_PROTECTED_METADATA) If this is true the NPC should be 'invulnerable' to normal damaging effects.
- use CitizensAPI.getNPCRegistry().isNPC() to check if an entity is a NPC. Real players and player-type NPCs will both return true for instanceof Player.
DON'T
- Attempt to access npc.getBukkitEntity() from within traits until onSpawn() has been called or npc.isSpawned() returns true.
- Change anything in npc.getNavigator.getDefaultParams() unless you're sure you want a global change. Use the localParams() instead after setting a navigation target.
- Assume a NPC is a player-type. Mob types have some important differences.
Download an example
This is a link to a an example trait. It is similar to the code above, with some additional code for better handling commands, default configuration, and a plugin.yml
You will need to build against CitizensAPI.jar,Citizens.jar (although this is not always required), and Bukkit.jar.
NPC Events
Citizens implements its own Listeners and will call new NPC-specific versions of many common events. This saves Trait developers the trouble of finding their npcs from the normal event entities. The event object these events provide are just like their Bukkit counterparts with the addition of the getNPC() method. Citizens currently provides the following:
- EntityTargetNPCEvent
- NPCClickEvent
- NPCCollisionEvent
- NPCCombustByBlockEvent
- NPCCombustByEntityEvent
- NPCCombustEvent
- NPCDamageByBlockEvent
- NPCDamageByEntityEvent
- NPCDamageEvent
- NPCDespawnEvent
- NPCEvent
- NPCLeftClickEvent
- NPCPushEvent
- NPCRemoveEvent
- NPCRightClickEvent
- NPCSelectEvent
- NPCSpawnEvent
See the [Javadocs] for details.
Using the AI API
The AI API of Citizens can be broken down into two parts - GoalController and Navigator.
A Goal is a repeatable, abstract unit of work that can be performed by an NPC. It can be registered with a GoalController with a priority (higher is more important). The highest priority goal which can be executed will be prioritised. NPC contains getDefaultGoalController() for this purpose.
The GoalSelector allows a great deal of flexibility within goal implementations. It allows firstly the dynamic selection of sub-goals and the concurrent execution of many sub-goals, and can stop execution at any time.
Code: Example |
{{{2}}} |
The second concept is the Navigator. This controls the pathfinding aspects of the NPC. The Navigator can have one target at a time, and will call events to notify of completion/cancellation.
The pathfinding range of the Navigator is the maximum range it will search when attempting to find a path to the target. This is usually set by the server admin. The speed of the Navigator is the movement speed of the NPC while moving to the target. The default speed is around 0.3.
See Also
Quick Navigation | |
---|---|
Usage | Installation · Frequently Asked Questions · Commands · Editors · Characters · API · |
Configuration | Configuration · Text Syntax · Permissions · Waypoints · Data Storage |