Custom Scripting

Table of contents

  1. Introduction
  2. Javascript
    1. Environment Variables
    2. Troubleshooting
  3. Script Types
    1. Attack Precondition Check
    2. Attack Damage Script
    3. Callback Functions
    4. Start of Turn Script
    5. End of Turn Script
    6. Action Decision Script
    7. Start of Battle Script
  4. API
    1. BattleModel
    2. Damage Types
    3. Saving Throw versus
    4. AttackResult object
    5. SaveResult object
    6. Conditions
    7. Modifier
    8. AttackModel
    9. ActionModel
    10. SpellModel
    11. CreatureModel

Introduction

When we implement spells, class features, actions and any effect in general, we use an editor to make choices from drop down lists or enter values directly into a text box.

This means that users who are not programmers can create new behaviours or customize existing actions and attacks.

However custom scripting is useful for

  1. Implementing effects as a stop gap that can’t yet be configured with the existing user interface.
  2. Replacing functionality that was originally written in the source code due to its complexity.
  3. Allowing technical users the tools to create custom behaviours and effects in the application.

Javascript

Javascript (tutorial) is a powerful scripting language that allows users to execute changes to the current fight. In our user interface there are various tabs where you can enter a custom script.

Please note, this is not your normal browers javascript engine. It is Rhino 1.7 and consequently there are differences (e.g. don’t use === on strings

Some places require you to write a script that returns true or false e.g.

var success;
if (me.getHitpoints() < 10) {
   success = true;
}
// last line in the script needs to be the return value
success;

Other places may just let you write whatever you need at a specific point in time to affect the environment e.g.

for each (var c in all.toArray()) {
    if (c.directDistance(me) > 30) {
     continue; // too far away
    }
    if (c.isOnSameSideAs(me)) {
        c.heal(10);
    }
}

Environment Variables

In order to be useful javascript needs to be able to reference things in the fight. For example when you write javascript for a website you can access the window which represents an open window in a browser.

Here are the global variables that your script can access, some are only available to specific script types that are executing at a particular time.

me

me is the active Creature in the context doing the move or action etc. So if the script is executing at the start of a creatures turn, then “me” is the creature whose turn it is. For scripts that are checking if an action is valid, then it is the creature who is evaluating the action at that time. You need to check the context notes of the specific script.

target

When the creature is attacking, “target” is the currently selected or about to be selected creature it is attacking. If the action you are doing is harmful it will be the creature being attacked. But for friendly spells like buffs and heals the target may be a friend.

all

All is a list of Creature’s who are in the fight, everyone whether they are alive or dead or a summons.

battle

An object which gives you access to other functions specific to the environment such as adding content to the fight log.

action

The action in the specified context. An action can be a one or more attacks or can also be a spell.

attack

A single specific attack that is in context. Each action/spell can potentially have multiple attacks.

attackResult

The attack roll used dice roll, if it hit etc, a AttackResult object. Not populated for the precondition script.

save

The attack roll used dice roll, if it passed etc

Troubleshooting

battle.log("checking target is sleeping: " + target.isSleeping());

Script types

Attack Precondition Check

Each attack can have a precondition such as Prone, Restrained and you can check that the condition was from you, or that the target does not have the condition. But if you want to check for multiple conditions and apply additional logic then this is the script.

The following variables are passed: me, target, action and attack. The last line on the script should be true or false. If true then the target will have met the precondition requirements. If false then this action will not be used.

Example

Lets say your monster is a hag coven, they have one powerful action that is weighted highly and under normal circumstances would be used in round 1 or 2. However you want to wait until they someone is not only in range, but also next to the cauldron. You could write:

var enemyNextToMe = false;
for each (var c in all.toArray()) {
    if (c.isOnSameSideAs(me)) {
       continue; // ignore hags team
    }
    // the range would already have been checked but 
    // we want it not only in range, but also right next to me
    if (c.directDistance(me) <= 10) {
       enemyNextToMe = true;
    }
}

// last variable in the script is the return value of true/false
// note, do NOT use the return keyword, that will throw an error
enemyNextToMe;

Note that the precondition script is executed a lot, 20 times per round, so don’t use it to make attacks or change the state of something (e.g. add damage or heal)

Attack Damage Script

Each time the attack hits a target the damage script executes.

Out the box without scripting you can define multiple conditions, damage types with various options around saving throws.

However if you have more complex logic to be applied then you can use an Attack Damage script to apply those custom effects.

The following variables are passed: me, target, action, attack, all, save and battle.

Nothing is needed to return and this script is only limited by your imagination as to what kind of effects it could have. It doesn’t need to be a condition. If you leave the save vs blank its really just a damage script (when you hit).

Example

Lets say your monster has a gaze which paralyzes and they have an arm that ends with a spike. You could write:

if (target.isParalyzed() && target.getCurrentHitpoints() < 25) {
   battle.log(me.getName() + ' thrusts its spike into ' + target.getName() + 's chest for a killing blow');
   target.damage(100, me, battle);
}

You will notice that applyDamage requires the battle to be passed, this is because when damage is applied there is a lot going on, and so we need access to the overall fight.

Callback Functions

For non-scripts, if you select a condition of “damage start of targets turn” e.g. Ensnaring Strike, you make your saves but its only on the start of your turn that you start to take damage.

If you select “Damage start of each turn if next to you” then the damage only occurs at the start of the turn obviously.

If you select “Damage start of casters turn using an action” then like Witch Bolt it only executes on the casters turn.

In each of these cases the damage only executes in one spot, and we can only have the damage script execute once.

However if you leave the condition blank and write a damage script which does executes when the attack hits, you can add to that script callback functions which allow you execute additional script code at other points in time.

var startOfTurnScript = new java.lang.Runnable({ 
              run: function () { 
                     battle.log('acid is burning your armor each round!');
                     target.applyDamage(me, "Acid Slime", ReferenceList.ACIDDAMAGE, 3, 6, 0, false, battle);
               } 
        });
// script runs for 10 rounds (unless they save at some point)
target.addConditionScript(10, me, battle, "Acid slime", ReferenceList.RUNSSTARTOFTARGETSTURN, startOfTurnScript, 15, ReferenceList.CONSTITUTIONSAVEVS, true);

So this script, we do whatever we need to do when the attack hits. Then we are creating a function which does stuff each turn as part of the condition applied. The addConditionScript can have 3 different places it can execute

Start of Turn and End of Turn Scripts

In order to change the behaviour of the creatures during their movement phase you need to associate one or more start of turn scripts with the creature.

Go to the New Fight page, add your players and monsters and the click the fighter type icon

Click on the Start of Turn tab and select the scripts you want to add to the creature.

You can click the X button to remove scripts from the creature.

If you do make selections, they will override any scripts already saved on the character sheet.

To create your own start of turn scripts, go to the script tab and click the toolbar button to add a new script.

You have option to publish your scripts for others to find. If you do perhaps put your discord name in the title, but its not required.

There is no target or attack for this script, it is the beginning of the turn. The script has access to

me

all

battle

Note that the script does not execute if the creature is dead, unconcious or otherwise unable to act.

After the script executes, if there is any movement left, the sim will execute default movement as needed.
You can use me.disableDefaultMovement() to prevent the sim from executiing any default movement but if you want to prevent movement used in actions you need to remove the movement left all together.

Sample start of turn script to save movement for end of turn script


battle.log('start of turn stuck');

// disable the sim doing the movement decisions
me.disableDefaultMovement(); 

// store the value on the creature for later
me.saveString("movementLeft", me.getMovementLeft());

// prevent attacks from using movement too
me.setMovementLeft(0);

Remember to use the Reload button on a fight if you are making changes on the fly.

End of turn script to use movement

   me.setMovementLeft(me.getString("movementLeft"));
   battle.log('end of turn movement left ' + me.getMovementLeft());

Attach Scripts to a Creature Permanently

Editing the main properties of a creature has a Custom Scripts tab which works the same as the New Fight tab except that selecting/removing a script is automatically saved to the creature regardless if you press save or cancel.

Example creature specific script



// define a function to call
function heartOfTheDragon() {

    save = me.rerollSaveForCondition(ReferenceList.FRIGHTENEDCONDITION, false, battle);
    if (save.passed) {
        battle.log(me.getName() + ' has a heart of a dragon and is no longer frightened ' + save.getRollDisplay());
         me.removeCondition(ReferenceList.FRIGHTENEDCONDITION);
    } else {
       battle.log(me.getName() + ' had a heart of a dragon');
    }
}


if (me.isParalyzed() || me.isFrightened()) {
    heartOfTheDragon();
}

Action Decision Script

In order to override the default decision on which action to pick you can add an action decision script to a creature.

See Start of Turn Script documentation on how to add this kind of script.

There is no action or attack for this script, it is just before an action is selected but after all the internal weights have been calculated and applied to the existing actions.

The script has access to

me

all

battle

validActions

target

actionType

Note that you don’t actually have a target, however for convenience we find the closet opponent and pass it to the script as target.

validActions is a List see ActionModel

right now we only call the script for the following actionType values

The validAction list is sorted so validActions.get(0) would be the highest weighted action.

In order to override all the other actions you would probably want to add +200 to an actions weight since each starts 20-80 but then gets bonuses up to 60 ish then if determined the very best gets another +50 etc

You would use (whicheveractionyouneed).addWeightForRound(200);

Lets make an example. Supposed you have a 5th level paladin and in the first round the Sim is having Shield of Faith cast but you want Searing Smite setup. You can add a script


  if (battle.getRound() > 1) {
      battle.exit(); // exit script
  }
  if (actionType != ReferenceList.BONUSACTION) {
      battle.exit();
  }
  smite = me.getActionByName("Searing Smite");
  if (smite != null) {
     smite.addWeightForRound(200);
  }
  

Note that this script will execute each time for bonusaction, mainaction and another bonus action after the attack so probably over 600 by the third time it is called without the bonusaction check.
However each action has an actionType and so each actionType is only called once, so its not going to call for Searing Smite more than once.

A simpler method to help out, so you dont have to check for nulls and might as well ignore the actiontype, you can write


   if (battle.getRound() > 1) {
      battle.exit(); // exit script
   }
   me.addActionWeight("Searing Smite", 200);

Note that usually bonus actions are skipped in favor of a main spell for spell casters or round 1 for others. If a bonus action is weighted highly (>100) then it gets back on the list

Start of Battle Script

This script is to simulate actions and spells cast daily in the morning (like Mage Armor) or cast before the battle started (optionally expending a spell slot).

You can write a spell specific script, or you can create a general script to attach to a creature.

Since creature scripts can be attached on the New Fight screen you can also write a script to do custom preparation of a particular battle e.g. reduce hit points, use up spell slots, apply conditions etc.

me (it is not anyones turn, me is the person who has the script attached to them, or attached to a spell/class feature/feat that they have on their character sheet)

all

battle

everything else will be null. battle.getRound() will return 0.

Note to self, don’t attach a script to Spell of status “Not Applicable” or “Future” because those are not actions and will not run.

Lets say you wanted to use up some spell slots and do other things before the battle starts. You can write a single script and attach it to someone on the New Fight screen e.g.


// loop through each person
for each (var c in all.toArray()) {

   // unfortunately this javascript engine really can't use === because of the way it runs inside java
   if (c.hasExactName("Cleric3"))  {
      c.useOneSlot(1); //used up some first level spells
      c.useOneSlot(1);  
      c.useOneSlot(1);  
   }

   if (c.hasExactName("Fighter3"))  {
        c.damage(20, c, battle);   // add some damage
        battle.log('20 dmg ' + c.getCurrentHitpoints());
    }
}

API

BattleModel

/**
 * Get the current round
 * 
 * @return
 */
public int getRound();


/**
 * Log to the fight log under the current creatures turn 
 * 
 * @param msg
 */
public void log(String msg);

    
/** 
 * @return width in squares 
 */
public int getGridWidth();

/**
 * 
 * @return height in squares 
 */
public int getGridHeight();


/**
 * Lookup class level specific values stored as lookups e.g. a Blood Hunters Hemocraft Die
 * 
 * @param classRefId
 * @param lookupNameRefId
 * @param classLevel
 * @return 0 if not found
 */
public int getClassLookup(long classRefId, long lookupNameRefId, int classLevel);

Damage Types

Action Types

Note many of these are never called/associated and trying to get rid of some of these

Saving Throws vs

Classes vs

Feature Types of spells

Skills

AttackResult

This is the object passed along with an attack which uses an attack roll to hit

public class AttackResult {

public int usedRoll = 0;
public int bonus = 0;
public int firstRoll = 0;
public int secondRoll = 0;
public boolean hits = false;
public boolean noRollNeeded = false;
private boolean advantage = false;
private boolean disadvantage = false;
public String advantagereason = "";
public String disadvantagereason = "";
public boolean autohit = false;
public boolean critical = false;

public String getRollDisplay();

}

SaveResult

This is the object you get after calling target.getSavingThrow(…)

public class SaveResult {

public boolean passed = false;
public boolean advantage = false;
public boolean disadvantage = false;
public int firstRoll = 0;
public int secondRoll = 0;
public int usedRoll = 0;
public int bonuses = 0;
public int dc = 0;
public boolean autoPassed = false;

public String getRollDisplay();

}

Roll

eg. var amt = Roll.d(8); // 1d8 var amt = Roll.nd(2,6); // 2d6 var amt = Roll.nd(2,6,3); // 2d6+3 var amt = Roll.nd(2,6,3, true); // 15 since true means max values

Conditions

ModifierModel

public Spell getSrcSpell();

public Action getSrcAction();

public Attack getSrcAttack();

public long getConditionRefId();

public CreatureModel getSrcCreature();

public int getSpellDC();

public long getSaveVs();

public boolean isImmuneTo();

public boolean isImmuneIfSave();

public int getTemporaryHitpoints();

public void setTemporaryHitpoints(int temporaryHitpoints);

public int getRoundStarted();

public boolean isSaveAtEndOfTurn();

public int getAmount();

public void setAmount(int amount);

public int getDuration();

public boolean isDurationEndOnTargetTurn();

public boolean isBuff();

public long getDamageTypeRefId();

AttackModel

 * @param additionalNbrAttacks
 */
void additionalAttacksThisRound(int additionalNbrAttacks);

/**
 * Add to the weapons/attacks damage this round only.  Does not stack
 * 
 * @param 1d8+2 would be 1,8,2
 */
    void additionalDamageThisRound(int nbrDice, int dieType, int bonus);
 

ActionModel

/**
 * 

for each (var a in me.getActions()) { if (a.hasExactName(“Spear”)) { // do something } } * * @param name * @return */ public boolean hasExactName(String name);

/**
 * Melee reach and roll to hit
 * @return
 */
    boolean isMelee();


/**
 * Long range and roll to hit
 * @return
 */
boolean isRangedWeapon();

/**
 * See action effect (it might also check the condition type)
 * @return
 */
boolean isBuff();

/**
 * See action effect (also checks for damage type of heal)
 * @return
 */
boolean isHeal();

/**
 * See action effect
 * 
 * @return
 */
boolean isDisable();

/**
 * See action effect
 * 
 * @return
 */
boolean isEscape();

/**
 * See action effect
 * 
 * @return
 */
boolean isSummons();

/**
 * See action effect
 * 
 * @return
 */
boolean isAreaOfEffect();

/**
 * See action effect
 * 
 * @return
 */
boolean isAttack();

/**
 * See action effect
 * 
 * @return
 */
int getOverallWeight();

/**
 * Adjust the weight for this round by amount
 * In general +200 will ensure it is selected
 * 
 * @param weight
 */
void addWeightForRound(int amount);

/**
 * Gets the first attack found, or an empty attack model if none.
 * Most useful for when you know there is one attack only.
 * 
 * @return
 */
AttackModel getAttack();

/**
 * Get all the attacks
 * 
 * @return
 */
List<AttackModel> getAttacks();

    /**
 * 
 * @return actionName or an empty string or whatever was set
 */
public String getName();

    /**
 * See actionType list e.g. ReferenceList.REACTIONACTIONTYPES
 * 
 * @return  action type entered in Edit Action  
 */
public long getActionType();

    /**
 * Get mellee reach of each attack
 * 
 * @return largest value
 */
public int getMeleeReach();

SpellModel

/**
 * 

for each (var a in me.getActions()) { if (a.hasExactName(“Spear”)) { // do something } } * * @param name * @return */ public boolean hasExactName(String name);

/**
 * See action effect (it might also check the condition type)
 * @return
 */
boolean isBuff();

/**
 * See action effect (also checks for damage type of heal)
 * @return
 */
boolean isHeal();

/**
 * See action effect
 * 
 * @return
 */
boolean isDisable();

/**
 * See action effect
 * 
 * @return
 */
boolean isEscape();

/**
 * See action effect
 * 
 * @return
 */
boolean isSummons();

/**
 * See action effect
 * 
 * @return
 */
boolean isAreaOfEffect();

/**
 * See action effect
 * 
 * @return
 */
boolean isAttack();


/**
 * Gets the first attack found, or an empty attack model if none.
 * Most useful for when you know there is one attack only.
 * 
 * @return
 */
AttackModel getAttack();

/**
 * Get all the attacks
 * 
 * @return
 */
List<AttackModel> getAttacks();

/**
 * 
 * @return actionName or an empty string or whatever was set
 */
public String getName();

/**
 * See actionType list e.g. ReferenceList.MAINACTION
 * 
 * @return  casting time  
 */
public long getActionType();


/**
 * Type of spell it is e.g. ReferenceList.SPELLFEATURE for regular spells
 * @return
 */
public long getFeatureType();

/**
 * Get level of spell where applicable (usually only for regular spells)
 * @return
 */
public int getLevel();

CreatureModel

/**
 * @return
 */
boolean isBlinded();

/**
 * @return true if has the darkness condition
 */
boolean isDarkness();

/**
 * @return
 */
boolean isCharmed();

/**
 * @return
 */
boolean isConfused();

/**
 * @return
 */
boolean isDead();

/**
 * @return
 */
boolean isDeafened();

/**
 * @return
 */
boolean isFeebleminded();

/**
 * @return
 */
boolean isFrightened();


@Override
public boolean isGrappled();

/**
 * @return incapacitated or stunned or petrified
 */
boolean isIncapacitated();

/**
 * @return
 */

boolean isInvisible();

/**
 * @return was originally created as a monster with a CR regardless of which side it is on now
 */
boolean isMonsterType();

/**
 * @return
 */
boolean isParalyzed();

/**
 * @return
 */
boolean isPetrified();

/**
 * @return
 */
boolean isPoisoned();

/**
 * @return
 */
boolean isRaging();

/**
 * @return
 */
boolean isRestrained();

/**
 * @return
 */
boolean isSleeping();

/**
 * @return
 */
boolean isStunned();

/**
 * @return
 */
boolean isSurprised();

/**
 * @return
 */
boolean isUnableToAct();

/**
 * @return
 */
boolean isUnableToMove();

/**
 * @return
 */
boolean isUnconcious();

/**
 * 
 * @return true if added to the players side (even if a monster from the compendium)
 */
boolean isOnPlayerSide();

/**
 * 
 * @return true if added to the players side (even if a monster from the compendium)
 */
boolean isOnMonstersSide();

/**
 * 
 * @param c
 * @return true if on the same side or charmed
 */
boolean isFriendly(CreatureModel c);

/**
 * 
 * @param c another creature
 * @return true if creature c is the same as this 
 */
boolean isSameAs(CreatureModel c);

/**
 * 
 * @param c
 * @return true if on same side originally
 */
boolean isOnSameSideAs(CreatureModel c);


/**
 * @return true if someone is using a help action to distract
 */
public boolean isDistracted();

/**
 * returns true if distracted by someone who was doing for c
 * 
 * @param me
 * @return 
 */
public boolean isDistractedForYou(CreatureModel c);

/**
 * Exact match e.g. 2.Orc
 * 
 * @param name
 * @return
 */
boolean hasExactName(String name);


/**
 * Name contains this string e.g. Cleric1
 * 
 * @param name
 * @return
 */
boolean nameContains(String s);



/**
 * @return base strength
 */
int strength();

/**
 * @return base dexterity
 */
int dexterity();

/**
 * @return base constitution
 */
int constitution();

/**
 * @return base intelligence
 */
int intelligence();

/**
 * @return base wisdom
 */
int wisdom();

/**
 * @return base charisma
 */
int charisma();

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addStrength(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addDexterity(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */

void addConstitution(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addIntelligence(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addWisdom(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * Add a condition that affects an ability, positive nbrs for a buff, negative for a drain
 * 
 * @param amount number of stat points added
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addCharisma(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/**
 * 
 * @return Base strength + modifiers
 */
int getCurrentStrength();

/**
 * 
 * @return Base dexterity + modifiers
 */
int getCurrentDexterity();

/**
 * 
 * @return Base constitution + modifiers
 */
int getCurrentConstitution();

/**
 * 
 * @return Base intelligence + modifiers
 */
int getCurrentIntelligence();

/**
 * 
 * @return Base wisdom + modifiers
 */
int getCurrentWisdom();

/**
 * 
 * @return Base wisdom + modifiers
 */
int getCurrentCharisma();

/**
 * @return 
 */
int passivePerception();

/**
 * @return ability bonus for current value
 */
int getStrengthBonus();

/**
 * @return ability bonus for current value
 */
int getDexterityBonus();

/**
 * @return ability bonus for current value
 */
int getConstitutionBonus();

/**
 * @return ability bonus for current value
 */
int getIntelligenceBonus();

/**
 * @return ability bonus for current value
 */
int getWisdomBonus();

/**
 * @return ability bonus for current value
 */
int getCharismaBonus();

/**
 * 
 * @return armor class after adjustments (dex loss, wild shape etc)
 */
int getArmorClass();

/**
 * Adjust armor class with this condition positive nbrs for a buff, negative for a penalty
 * 
 * @param amount to adjust AC by
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */
void addArmorClass(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);


/**
 * Current hit points / getMaxHitpoints
 */
int getCurrentHitpoints();

/**
 * 
 * @return maximum hitpoints under normal circumstances
 */
int getMaxHitpoints();

/**
 * Get the assigned name which has a sequence prefix if there are multiple e.g.  1.Orc  2.Orc
 */
String getName();

/**
 * @return true if default size is tiny
 */
boolean isTiny();

/**
 * @return true if default size is small
 */
boolean isSmall();

/**
 * @return true if default size is medium
 */
boolean isMedium();

/**
 * @return true if default size is large
 */
boolean isLarge();

/**
 * @return true if default size is huge
 */
boolean isHuge();

/**
 * @return true default if size is huge
 */
boolean isGarguantuan();

/**
 * Get the distance between two creatures counting squares * 5' 
 * 
 * @param c  measure distance to, ignoring obstacles
 * @return distance in feet
 */
int directDistance(CreatureModel c);

/**
 * Reduce hit points directly by source creature e.g. target.damage(Roll.d(10), me, battle);
 * Note that this is direct damage that bypasses any resistances or anything else
 * 
 * @param dmgAmount integer number of damage
 * @param source creature usually "me"
 * @param b
 */
void damage(int dmgAmount, CreatureModel source, BattleModel battle);

/**
 * Get a saving throw result for the specified save parameters
 * 
 * @param saveDC DC of the save required, you can use me.getSpellDC()
 * @param saveVs e.g. ReferenceList.DEXTERITYSAVEVS
 * @param conditionRefId can be zero if no resistance/immunity applies or pick from API e.g. ReferenceList.PARALYZEDCONDITION
 * @param actionName your actions name
 * @param saveWithAdvantage true if you want the saving throw rolled with advantage
 * @param battle
 * @return
 */
SaveResult getSavingThrow(int saveDC, long saveVs, long conditionRefId, String actionName,
        boolean saveWithAdvantage, BattleModel battle);

/**
 * Apply damage that will have resistance and reaction to spells applied.  It writes to the log.
 * e.g.
 *   SaveResult saveResult = target.getSavingThrow(15, ReferenceList.DEXTERITYSAVEVS, 0, "Burning Eye", false, battle ); 
 *   target.applyDamage(me, "Burning Eye", ReferenceList.FIREDAMAGE, 2, 6, 4, false, battle);
 *   
 * will apply 2d6+4 damage, but immunities might kick in etc.
 * 
 * @param source me or target
 * @param actionName your name of the action
 * @param damageTypeRefId see API e.g. ReferenceList.FORCEDAMAGE
 * @param nbrDice for 2d6+4 you would enter 2 for the number of dice
 * @param dieType for 2d6+4 you would enter 6 for the die type
 * @param damageBonus for 2d6+4 you would enter 4.  You can use this to apply a fixed amount of damage.
 * @param saveResult you can pass: new SaveResult() or you can have the target do a saving throw by passing target.getSavingThrow();
 * @param criticalHit true and it will act as if the attack was a natural 20
 * @param battle
 */
void applyDamage(CreatureModel source, String actionName, long damageTypeRefId, int nbrDice, int dieType,
        int damageBonus, SaveResult saveResult, boolean criticalHit, BattleModel battle);

/**
 * Heal lost hit points
 * 
 * @param amount
 * @param battle
 */
void heal(int amount, BattleModel battle);

/**
 * 
 * @param conditionRefId e.g. ReferenceList.PARALYZEDCONDITION
 */
void removeCondition(long conditionRefId);

/**
 * @param conditionRefId e.g. ReferenceList.SLEEPINGCONDITION
 * @param conditionAmount 0 if the condition has [amt] in it, pass the number otherwise zero
 * @param durationInRounds
 * @param source creature usually "me"
 * @param battle
 * @param actionName some name for your action
 * @param isABuff true if targeting friendlies otherwise false
 */
void addCondition(long conditionRefId, int conditionAmount, int durationInRounds, CreatureModel source,
        BattleModel battle, String actionName, boolean isABuff);

/**
 * @param conditionRefId e.g. ReferenceList.SLEEPINGCONDITION
 * @param conditionAmount 0 if the condition has [amt] in it, pass the number otherwise zero
 * @param durationInRounds
 * @param source creature usually "me"
 * @param battle
 * @param actionName some name for your action
 * @param isABuff true if targeting friendlies otherwise false
   @param saveDC leave zero to use the creatures SpellDC or specify a value or leave 0 if no saves to end this
   @param saveVs 0 if there is no saving throw otherwise a savevs e.g. ReferenceList.CONSTITUTIONSAVEVS
   @param saveEachTurn true if you want that saving throw executed each turn to end this condition
 */
void addCondition(long conditionRefId, int conditionAmount, int durationInRounds, CreatureModel source,
        BattleModel battle, String actionName, boolean isABuff, int saveDC, long saveVs, boolean saveEachTurn);

/**
 * 
 * @param durationInRounds needs to be at least 1 
 * @param source usually me
 * @param battle
 * @param actionName name to associate with the action
 * @param runnableWhen when this script runs e.g. ReferenceList.RUNSSTARTOFTARGETSTURN
 * @param runnableScript a runnable function 
 * e.g.
    var startOfTurnScript = new java.lang.Runnable({ 
        run: function () { 
                battle.log('acid is burning your armor');
        } 
    });
    target.addConditionScript(10, me, battle, "Acid slime", ReferenceList.RUNSSTARTOFTARGETSTURN, startOfTurnScript, 15, ReferenceList.CONSTITUTIONSAVEVS, true);
    
   @param saveDC leave zero to use the creatures SpellDC or specify a value or leave 0 if no saves to end this
   @param saveVs 0 if there is no saving throw otherwise a savevs e.g. ReferenceList.CONSTITUTIONSAVEVS
   @param saveEachTurn true if you want that saving throw executed each turn to end this condition
 */
public void addConditionScript(int durationInRounds, CreatureModel source, BattleModel battle, String actionName, int runnableWhen, Runnable runnableScript, 
        int saveDC, long saveVs, boolean saveEachTurn);


/**
 * 
 * @return true if has condition e.g. ReferenceList.SLEEPINGCONDITION
 */
boolean hasCondition(long conditionRefId);

/**
 * Pass the CreatureModel that was the source of the condition.
 * e.g. 
 *   var iGrabbedHim = target.hasConditionBy(ReferenceList.GRAPPLEDCONDITION, me);
 * 
 * @return true if has condition e.g. ReferenceList.SLEEPINGCONDITION
 */
boolean hasConditionBy(long conditionRefId, CreatureModel c);

/**
 * Pass the action name that was the source of the condition.  This could be a 
 * spell name or a name passed to the function addCondition
 * 
 * @return true if has a condition with that source action
 */
boolean hasConditionFromAction(String actionName);

/**
 * 
 * @return default movement each round in feet
 */
int getMovement();

/**
 * @return movement left to use this turn in feet
 */
int getMovementLeft();

/**
 * @return add movement for the rest of this turn only
 */
int addMovement(int movementInFeet);

/**
 * x is >=0 and x is < battle.getGridWidth()
 * 0,0 is the bottom left corner
 * 
 * @return the x in squares for your (x,y) position
 */
int getXpos();

/**
 * y is >=0 and x is < battle.getGridHeight()
 * 0,0 is the bottom left corner
 * 
 * @return the y in squares for your (x,y) position
 */
int getYpos();

/**
 * Add a condition that adds to movement for the specified rounds, buff if amount is positive
 * 
 * @param amount in feet to adjust movement by
 * @param durationInRounds 
 * @param source the creature model e.g. me 
 * @param battle
 * @param actionName some name to associate with the condition
 */

void addMovement(int amount, int durationInRounds, CreatureModel source, BattleModel battle, String actionName);

/** * Get base movement + modifiers * * @return movement in feet */ int getCurrentSpeed();

/**
 * If the creature moved since the start of its turn, this is was the amount of the last move.
 * Could be one of several that turn.
 * 
 * @return movement in feet
 */
int getLastMoveAmount();

/**
 * This will return null if nobody found (not uncommon)
 * Will not target charmed, dominated
 * nor sleeping unless the last enemies are all sleeping
 * 
 * @param c whoever is trying to find an opponent
 * @param battle
 * @return null or a closest creature you can move to 
 */
CreatureModel findClosestMeleeOpponent(BattleModel battle);

/**
 * Uses what movement it has left to move within melee distance of the target.
 * 
 * @param target friend or depending on context
 * @param battle
 */
void moveNextTo(CreatureModel target, BattleModel battle);

/**
 * This will return null if nobody found (not uncommon)
 * Will not target charmed, dominated
 * nor sleeping unless the last enemies are all sleeping
 * 
 * @param c whoever is trying to find an opponent
 * @param battle
 * @return null or a closest creature ignoring obstacles 
 */
CreatureModel findClosestRangedOpponent(CreatureModel c, BattleModel battle);

/**
 * Returns the max nbr of slots available at this level, N/A for cantrips
 * 
 * @param slotLevel (1-9)
 * @return
 */
int maxSlots(int slotLevel);

/**
 * Returns the nbr of slots currently available at this level 
 * 
 * @param slotLevel (1-9)
 * @return
 */
int nbrSlots(int slotLevel);

/**
 * Returns true if you can cast the spell with the specified name e.g. Fireball.
 * Note that the spellname could also be a class feature that uses a lookup like Ki or Channel Divinity etc.
 * 
 * @param slotLevel (1-9)
 * @return false if no match on the spell name
 */
boolean canCast(String spellName);

/**
 * Currently spellDC is not spell specific, we just have one value per character.
 * 
 * @return spellDC from character sheet (could be zero if never added)
 */
int getSpellDC();

/**
 * @return spellModifier from character sheet (could be zero if never added)
 */
int getSpellModifier();

/**
 * @return spellAttackBonus from character sheet (could be zero if never added)
 */
int getSpellAttackBonus();

/**
 * Determine a particular class/racial/spell/trait is on this creature.
 * 
 * @param name the name of a spell or class feature or trait
 * @return true if exists
 */
boolean hasSpellWithName(String name);


/**
 * Get all spells of all types features, traits, feats, spells etc
 * @return
 */
List<SpellModel> getSpells();

/**
 * @return true if main action is available
 */
boolean canUseMainAction();

/**
 * @return true if bonus action is available
 */
boolean canUseBonusAction();

/**
* @return true if reaction is available
*/
boolean canUseReaction();

/**
 * Set bonus action to not being used (even if it was)
 */
void resetBonusAction();

/**
 * Set reaction to not being used (even if it was)
*/
void resetReaction();


/**
 * Resets the spell slots to max with no spells used
 */
public void resetSpellSlots();

/**
 * Resets all actions so creature has not used their main, bonus or reaction etc.
 */
public void resetActions();

/**
 * Return one slot of this level f possible
 * 
 * @param level
 */
public void returnOneSlot(int level);

/**
 * Uses up a slot of this level if possible
 * @param level
 */
public void useOneSlot(int level);

/**
 * 
 * @return true if calculated to prefer melee
 */
boolean isMeleeFighter();

/**
 * 
 * @return true if calculated to prefer melee
 */
boolean isRangedFighter();

/**
 * 
 * @return true if calculated to prefer melee
 */
boolean isSpellCaster();

/**
 * 
 * @return true if calculated to prefer melee
 */
boolean isHealer();

/**
 * @return initiative rolled + bonuses
 */
int initiative();

/**
 * Number of legendary actions left to use this round
 * 
 * @return
 */
int legendaryLeft();

/**
 * Use up a legendary action
 */
void useLegendary();

/**
 * Use up your main action
 */
void useMainAction();

/**
 * Use up your bonus action
 */
void useBonusAction();

/**
 * Use up your reaction
 */
void useReaction();

/**
 * 
 * @return true if a spell was already cast
 */
boolean hasSpellBeenUsed();

/**
 * 
 * @param damageTypeRefId
 * @param magicalSource
 * @return
 */
boolean isDamageResistant(long damageTypeRefId, boolean magicalSource);

/**
 * Check if immune to a condition e.g. 
 * 
 *   var neverscared = target.isImmuneToCondition(ReferenceList.FRIGHTENEDCONDITION, battle);
 * 
 * @return if immune to the specified condition
 */
boolean isImmuneToCondition(long conditionRefId, BattleModel battle);

/**
 * Finds the matching ModifierModel for the specified condition
 * 
 * @param conditionRefId
 * @return null or the Modifier 
 */
public ModifierModel getModifierForCondition(long conditionRefId);

/**
 * Finds the matching ModifierModel for the specified condition, 
 *  will not return a bufff.
 * 
 * @param conditionRefId
 * @return null or the Modifier 
 */
public ModifierModel getModifierForConditionNoBuffs(long conditionRefId);

 
/**
 * Finds the matching ModifierModel 
 * 
 * @param actionName action that was the source
 * @return null or the Modifier 
 */
public ModifierModel getModifierForActionName(String actionName);

/**
 * Checks for an enemy in a square next to you
 * 
 * @param battle
 * @return true if in melee range
 */
public boolean isInMelee(BattleModel battle);   

/**
 * If possible uses a dash to gain more movement
 * 
 * @param battle
 */
public void useDash();


/**
 * Prevent the sim from doing any movement this turn
 */
public void disableDefaultMovement();

/**
 * Sim can do movement (default)
 */
public void enableDefaultMovement();

/**
 * Override and set the movement left
 * 
 * @param moveInFeet
 */
public void setMovementLeft(int moveInFeet);

/**
 * Store a key and a string for its value.  Nulls are not stored.
 * Leading/Trailing spaces are stripped.
 *  
 * @param key
 * @param stringValue
 */
public void saveString(String key, String stringValue); 

/**
 * Return a stored string value for its key
 * 
 * @param key
 * @param stringValue null if not found
 */
public String getString(String key);

/**
 *  Get the last amount the creature moved this turn
 * 
 * @return
 */
public int getLastMovementAmount();


/**
 * x is >=0 and x is < battle.getGridWidth()
 * 0,0 is the bottom left corner
 * y is >=0 and x is < battle.getGridHeight()
 * 0,0 is the bottom left corner
 * 
 * @param xpos
 * @param ypos
 * @param battle
 */
public void moveNextTo(int xpos, int ypos, BattleModel battle);


/**
 * Reroll any saving throws for matching condition
 * get the first saving throw found with a save vs defined
 * 
 * @param conditionRefId
 * @param true/false
 * @param battle
 * @return null if not found
 */
public SaveResult rerollSaveForCondition(long conditionRefId, boolean withAdvantage, BattleModel battle);



/**
 * Get all the attacks
 * 
 * @return
 */
public List<ActionModel> getActions();


/**
 * Returns the action for the name e.g.
 * smite = me.getActionByName("Searing Smite");
 * if (actionModel != null) {
 *    smite.
 * }
 * 
 * 
 * @param actionName
 * 
 * @return null if not found
 */
public ActionModel getActionByName(String actionName);


/**
 * Execute an action, SIM chooses the targets
 * 
 * @param action
 * @param battle
 */
public void executeAnAction(ActionModel action, BattleModel battle);


/**
 * Executes all attacks of the given action against the specified target
 * 
 * @param action
 * @param target  will pick targets if null
 * @param battle
 */
public void executeAttacks(ActionModel action, CreatureModel target, BattleModel battle);


/**
 * No guarantees that the attack will be made as target could end up out of range, or action may no longer be valid.
 * If the attack is a self buff will return null.
 * Note that is the a SINGLE additional attack roll regardless of the NumberOfAttacks/round.
 * 
 * @param action
 * @param preferredTarget
 * @param battle
 * @return AttackResult if an attack is made otherwise null
 */
public AttackResult makeOneAttack(ActionModel action, CreatureModel preferredTarget, BattleModel battle);

/**
 * e.g. cast darkness on yourself as a warlock hexblade
 * 
 *  executeSpell(me, "Darkness", battle);
 * 
 * @param target
 * @param spellName 
 * @param battle
 */
public void executeSpell(CreatureModel target, String spellName, BattleModel battle);



/**
 * When you add a condition script or otherwise add a modifier you can reference it from the actionName passed and remove it
 * 
 * @param actionName e.g. Acid Breath
 */
public void removeConditionByActionName(String actionName);


/**
 * 
 * @return total class level
 */
public int getLevel();


/**
 * 
 * @param classRefId
 * @return
 */
public int getLevel(long classRefId);


/**
 * Person who summoned it
 * 
 * @return null if not a summon
 */
public CreatureModel summonedBy();

/**
 * Summons creatures 100% of the time.  Up to caller to use me.useMainAction() as needed
 * 
 * @param creatureId the character sheet creature id in the URL e.g. 7006 for a brown bear
 * @param petname override creature name (or pass null for default name)
 * @param numberSummoned 0 or less will be ignored
 * @param battle
 * @return the list of creatures summoned
 */
public List<CreatureModel> summonCreatures(long creatureId, String petname, int numberSummoned, BattleModel battle);  


/**
 * 
 * @param battle
 * @return
 */
public boolean inMagicalDarkness(BattleModel battle);

/**
 * Returns true if you cannot see creature due to magical darkness, 
 * or if target is invisible 
 * or if you are blind
 * 
 * @param c
 * @param battle
 * @return false if you have Blindsight
 */
public boolean cannotSee(Creature c, BattleModel battle);

/**
 * Get your main melee weapon
 * 
 * @param battle
 * @return null if not found
 */
public ActionModel getMeleeAction(BattleModel battle);

/**
 * Get your main ranged weapon
 * 
 * @param battle
 * @return null if not found
 */
public ActionModel getRangedAction(BattleModel battle);

/**
 * Determine if your weapon action would have advantage 
 * 
 * @param action
 * @param target
 * @param battle
 * @return
 */
public boolean hasAdvantageAttackRoll(ActionModel action, Creature target, BattleModel battle);


/**
 * There is a conscious opponent next to you
 * 
 * @param battle
 * @return
 */
public boolean inMelee(BattleModel battle); 


/**
 * There is a conscious opponent next to you and they can use their reaction to 
 * opportunity attack
 * 
 * @param battle
 * @return
 */
public boolean inMeleeOpportunity(BattleModel battle);


    
/**
 * Does your main melee attack (might be a polearm) reach
 * 
 * @param target
 * @return false if have no melee attacks
 */
public boolean canMeleeReach(CreatureModel target);

    /**
 * Checks to see if the max nbr of uses has been reached or it is waiting to be recharged.
 * Does not imply anything else.
 * 
 * @param spellId
 * @return true if nbr of uses < max or max is 0 and recharge is 0 or not pending a recharge
 */
public boolean canUseActionOrSpell(ActionModel action);


    
/**
 * Get the calculated total bonus for the skill
 * 
 * @param skillRefId see list of skills e.g. ReferenceList.ATHLETICSSKILLS
 * @return
 */
public int getSkillBonus(long skillRefId);



/**
 * Source can be a creature or just populate with state.xpos/ypos to make it a source position.
 * Will be knocked back in the general opposite direction 
 * 
 * @param src source of the knock back 
 * @param distanceFeet distance moved in roughly the opposite direction to src.
 * @param battle
 */
public void knockback(CreatureModel src, int distanceFeet, BattleModel battle);