Denizen/0.7/Assignments

From Citizens Wiki

Assignments

Still reading? Great! You're on your way to writing some sweet scripts. Let's start out with the basics: Interact Scripts and Assignments. Denizen needs two things to be functional. A script, and an assignment. Scripts, obviously, hold instructions that are carried out when triggered. Assignments hold information on which scripts should trigger, and when. Bear with me, and keep reading!

General layout of an Interact Script

This small 'outline' below shows the general layout of a Denizen Assignment. Remember: SPACING is INCREDIBLY important with YML. If you are receiving a SnakeYML error in your console when working with any YML file, 99% of the time it is because of bad spacing or a missing ':' colon. YAML tutorials can be intimidating, but this small tutorial on the Essentials wiki is pretty useful for basic YAML, which is the extent of which Denizens uses.

Denizens:            # Required only once, at the top of the document.
  'Name of Denizen': # Name node
    Interact Scripts:
    - .. ...
    - .. ...
    Scheduled Activities:
    - ..:.. ...
    - ..:.. ...
    Texts:
      ....: ...
      ....: ...

For information on what goes in Interact Scripts, Scheduled Activities, etc., keep reading. What is show above is just a an outline of the correct spacing, not a valid assignment! Let's take a look at the basics, below.


A basic interact script

In order to sufficiently explain an assignment, we first need a script. The great thing about a Denizen script is that it can be simple or advanced, short or long, thanks to a wide array of commands and functions at your disposal. Scripts can work with, extend, and rely on one another to create rich, interactive gameplay. Each script has at least 2 parts, Requirements and Steps. For now, let's take this very simple script as an example. We'll get more detailed later on.

Remember: Scripts are defined in plugins/Denizen/scripts/ folder. They can be put into any YML file contained in that directory. You may need to create the directory yourself if no scripts yet exist.

'Daytime in the City':   
  Type: Interact
  Requirements:
    Mode: All
    List:
    - TIME Day
  Steps:
    1:
      Click Trigger:
        Script:
        - CHAT "Welcome to the city! Isn't it beautiful in the daytime?"

So what just happened there? A quick literal translation: This script is named 'Daytime in the City'. This name is used when assigning Denizen NPCs their scripts. When the script is called by a trigger, in this case a 'Click Trigger', for it to activate the Requirements set must be met, more specifically ALL of the requirements set. In this example, the only requirement is TIME DAY which requires the current time in the world to be daytime. If the Requirements are met, the script defined in the 'Click Trigger' is sent to be run. It will have the Denizen NPC chat to the player 'Welcome to the city! Isn't it beautiful in the daytime?'.

But before the script can be triggered, it needs to be assigned.

A basic assignment

In order for a Denizen to be associated with a script, he needs to be assigned to it. The following shows a simple assignment format.

Assignments are defined in the Denizens node found in plugins/Denizen/assignments.yml and must be done by hand. Once you edit the config.yml, immediately save and use /denizen reload in-game.

Denizens:                         
  'Steve':                        
    Interact Scripts:                      
    - 10 Daytime in the City  
    Texts:
      No Click Trigger: 'Come back during the day!'

Let's break this down again. This assignement will work for Denizens named 'Steve'. Denizen NPC names are case-sensitive! Triggers that involve this NPC, such as a 'Click Trigger' will call the script named 'Daytime in the City'. The 10 before the name is the script priority, useful when assigning multiple scripts, but we'll get into that next. If no scripts meet requirements (in this case -- if it's NOT daytime when clicking on Steve), the text defined in the Texts: node called 'No Click Trigger' will trigger instead. So to reiterate, if the player clicks on Steve during the daytime, the player will get a chat containing 'Welcome to the city! Isn't it beautiful in the daytime?' If the player clicks during the nighttime, since the script requirements are not met and therefore no Click Trigger is found, the player will see a chat containing 'Come back during the day!'. Note that the default No Click Trigger text can be changed in the config.yml. Texts will be covered shortly.

Multiple Script Assignments

The key to Denizens being dynamic is their ability to handle multiple scripts in different situations. This is done by assigning a priority to script assignments. Let's take this example configs below to see how script priority is handled. The below code references assignments.yml and a script.yml.

---- assignments.yml ----

Denizens:                         
  'Steve':                        
    Interact Scripts:                      
    - 0 Regular Joe
    - 10 Joe the Builder  

---- script.yml ----

'Regular Joe':
  Type: Trigger   
  Requirements:
    Mode: None
  Steps:
    1:
      Click Trigger:
        Script:
        - CHAT "Hello <PLAYER>! I supply builders only!"

'Joe the Builder':
  Type: Trigger   
  Requirements:
    Mode: All
    List:
    - PERMISSION modifyworld.*
  Steps:
    1:
      Click Trigger:
        Script:
        - ENGAGE
        - CHAT "Hello <PLAYER> the Builder! Take this shovel!"
        - GIVE WOOD_SPADE QTY:1
        - FINISH
        - DISENGAGE

Denizen Steve has been assigned two scripts. Upon player interaction, each script is checked for met requirements. If only one script has their requirements met, it's clear what script will be returned. But what if both meet requirements? That's where the number before each script assigned comes in. Higher priority always wins. To break down this specific situation, if a player with the 'modifyworld.*' permission clicks on Steve, both scripts meet the requirements, but since the script 'Joe the Builder' has a priority of 10, and 'Regular Joe' only has a priority of 0, 'Joe the Builder' sends its script. 'Regular Joe' is simply ignored. Denizens can be issued as many scripts as you want. If two scripts have the same priority, the first in the list will be the one to trigger, if both scripts meet their requirements.

An Overlay Interact Script Assignment

Advanced usage! Overlay Script Assignments are new in 0.7 and open a wide array of new possibilities with multiple script interaction. This is completely optional, and probably should be ignored if you are a first time user and instead be revisited when you start to master Denizen Scripting.

A normal setup with multiple scripts is 'take this script', 'or take this script' kind of behavior. Interaction is pulled from whichever script has the highest priority, regardless of whether or not an actual trigger exists. If no trigger, default Text is pulled alerting the Player. With an Overlay Assignment, it will only honor the script if the actual trigger exists. If no trigger exists, the script triggering will become to the next script in the list -- the script with the second highest priority. Let's see an example. I should also note that to assign a script as an 'Overlay', use a '^' directly in front of the script name.

---- assignments.yml ----

Denizens:                         
  'Steve':                        
    Interact Scripts:                      
    - 0 Proximity Joe
    - 10 ^Regular Joe
    - 20 ^Joe the Builder  

---- script.yml ----
'Proximity Joe':
  Type: Trigger
  Requirements:
    Mode: None
  Steps:
    1:
      Proximity Trigger:
        Script:
        - CHAT "Nice to see you, <PLAYER>!"

'Regular Joe':
  Type: Trigger   
  Requirements:
    Mode: None
  Steps:
    1:
      Click Trigger:
        Script:
        - CHAT "Hello <PLAYER>! I supply builders only!"

'Joe the Builder':
  Type: Trigger   
  Requirements:
    Mode: All
    List:
    - PERMISSION modifyworld.*
  Steps:
    1:
      Click Trigger:
        Script:
        - ENGAGE
        - CHAT "Hello <PLAYER> the Builder! Take this shovel!"
        - GIVE WOOD_SPADE QTY:1
        - FINISH
        - DISENGAGE

Do you see now the possibilities of the Denizen Script Engine? I love it! Let's see what happens in a few different scenarios.

First Scenario Let's say a Player clicks, doesn't have the permissions 'modifyworld.*', so therefore only two scripts meet requirements: 'Regular Joe' and 'Proximity Joe'. It should be pretty obvious what happens. 'Regular Joe' has higher priority, so a Click Trigger will use that script. The Player will see "Hello Player! I supply builders only!". That's the only trigger, right? Wrong. This leads us into the next scenario.

Second Scenario The first thing to trigger the Denizen NPC would actually be the Proximity Trigger. In a regular assignment situation, with the same permissions situation as the first scenario, 'Regular Joe' would beat out 'Proximity Joe' and since there is no Proximity Trigger in 'Regular Joe', no script would trigger. But this is an Overlay Assignment. Since there is no Proximity Trigger in the script 'Regular Joe', the next script available, 'Proximity Joe', would attempt to trigger instead. And what do we have here? A Proximity Trigger! The Player would see "Nice to see you, Player!".

Third Scenario Player triggers a Proximity Trigger. Player has permission 'modifyworld.*', so the highest matching script is 'Joe the Builder' since it has a priority of 30. But there is no Proximity Trigger for this script, so the next script in line is checked. 'Regular Joe', of course, has no Proximity Trigger either! And since both 'Joe the Builder' and 'Regular Joe' are Overlay Assignments, we're back to 'Proximity Joe' triggering, alerting the player "Nice to see you, Player!".


Simple, right? To get your teeth wet, take a look at this additional example situation which uses FLAGS and magic :)

# ASSIGNMENTS.YML ----

Denizens:
  Maggie:
    Interact Scripts:
    - 0 Magic Shop
    - 10 ^Magic Shop Not Enough Money
    - 20 ^Magic Shop Feathers
    - 20 ^Magic Shop Dust
    
# MAGIC SHOP.YML ----
    
'Magic Shop':
  Type: Interact
  Requirements:
    Mode: None
  Steps:
    1:
      Proximity Trigger:
        Script:
        - CHAT 'Welcome to my shop! Have a look around!'

      Click Trigger:
        Script:
        - CHAT 'What would you like to buy?'
        - HINT

      Chat Trigger:
        1:
          Trigger: I would like to buy some /feathers/.
          Script:
          - CHAT 'Great! Feathers are 20 coins.'
          - NARRATE 'Right click to purchase some feathers.'
          - FLAG 'MAGICSHOPITEM:FEATHER' 'DURATION:30'
        2:
          Trigger: I would like to buy some /glowstone dust/.
          Script:
          - CHAT 'Great! Glowstone dust is 50 coins.'
          - NARRATE 'Right click to purchase some glowstone dust.'
          - FLAG 'MAGICSHOPITEM:DUST' 'DURATION:30'
        3:
          Trigger: I would like to buy some /*/.
          Script:
          - CHAT "Ah! Sorry, I don't sell any of that!"
          
'Magic Shop Not Enough Money':
  Type: Interact
  Requirements:
    Mode: All
    List:
    - FLAGGED 'MAGICSHOPITEM'
  Steps:
    1:
      Click Trigger:
        Script:
        - CHAT "Ah, sorry! You don't have enough money!"
        - RESET 'FLAG:MAGICSHOPITEM'
        
'Magic Shop Feathers':
  Type: Interact
  Requirements:
    Mode: All
    List:
    - FLAGGED 'MAGICSHOPITEM:FEATHER'
    - MONEY 20
  Steps:
    1:
      Click Trigger:
        Script:
        - TAKE MONEY QTY:20
        - GIVE FEATHER QTY:10
        - CHAT 'Thanks! Here are your feathers.'  
        
'Magic Shop Dust':
  Type: Interact
  Requirements:
    Mode: All
    List:
    - FLAGGED 'MAGICSHOPITEM:DUST'
    - MONEY 50
  Steps:
    1:
      Click Trigger:
        Script:
        - TAKE MONEY QTY:50
        - GIVE GLOWSTONE_DUST QTY:10
        - CHAT "Thanks! Here's your dust."

For a good exercise, I'll leave figuring out the flow of this script up to you!

Texts

In addition to handling the script assignments, there are also a few other things that can be declared in the Denizens node of assignments.yml. Under the text node you can define single chat lines that will be displayed for each of 3 core trigger types if there is not matching script found. Click, Damage and Chat. Additionally a message can be defined for when the Denizen is Engaged or its interaction cooldown isn't finished.

Settings here override the default setting found in Denizen/config.yml for this Denizen.

Denizens: 
  Castle Guard: 
    Interact Scripts: 
      - 10 Guard_Script
    Texts: 
      No Click Trigger: Don't poke, me, <PLAYER>!
      No Damage Trigger: Ouch! you lookin' to get smacked?
      No Chat Trigger: Eh, what's that?
      Denizen Unavailable: I'm busy right now.

Other Things to Note

Denizens with the same name use the same script assignments. The plus to this is the ability to quickly make 'generic NPCs' such as a 'Townsman' or 'Miner'. You can also assign to NPC IDs instead of name. This is covered in Advanced Usage.

All the scripts and configuration nodes are in YAML. Remember! Spacing is CRITICAL when dealing with YAML files. Each parent node and sibling node should follow the spacing and formatting guidelines set forth by the YAML 1.0 standard. Generally, your indents should remain consistant. In all the examples used in this guide, 2 spaces are used for indenting. Generally, whatever you choose should be maintained through your YAML document. Remember: NO TABS are allowed in YAML files. If you are using notepad++ to edit your scripts, be sure to change the tab->space setting in Preferences>LanguageMenu/Tab Settings.

In all the denizen YAML files, nodes are case sensitive! For example, in a script.yml, 'Scripts:' will work. 'scripts:' will not! All nodes are first letter capital, rest of the word lowercase. Scripts names follow this rule too! Also: Commands/Requirements are generally ALL UPPERCASE, but we'll get to that.

YAML tutorials can be intimidating, but this small tutorial on the Essentials wiki is pretty useful for basic YAML, which is the extent of which Denizens uses.