Creating objects from template

The general format to do this is:

var [variable name] = new elements.[object type, Sentence case]([template to make object with]);

So if you want to make a Sword, (making care to replace quotation marks with tildes if manually editing the csv) you would do:

var coolsword = new elements.Equipment("Sword");

Or if you want a dummy fighter (for example Loud Bird):

var dummyenemy = new elements.Fighter("Loud Bird");

And if you want to make and reference a skill from template (for example Meganudge):

var dummyskill = new elements.Skill("Meganudge");

self.equipment and equipment objects

There are four ways to give equipment to the player:

  • giveequipment. This is the easiest way to give equipment, but cannot be used before the start of your turn or after your turn has ended.
  • replacemewith. It's not clear what this does differently to giveequipment aside from work differently for Jester if surrounded by $dollar signs$ in a comment.
  • eq.create. This instantly replaces a targeted equipment with a new equipment you directly create from template using the command. However, you then need to separately track down the equipment you've just spawned before you can do anything with it, e.g. correct its position.
  • Manually declaring an equipment and pushing into self.equipment or replacing an item in self.equipment with it. This is essentially a more robust form of eq.create in that you do not need to track down the equipment to do something to it after you add it - you'll already have it as its own variable. Note however that directly attempting to replace e fails, and you cannot automatically transmute one object into a copy of another, so use self.equipment.indexOf(e) instead. For example, say you want a script that just replaces the equipment it's attached to with Hookshot:
var hookshot = new elements.Equipment("Hookshot");
hookshot.x = e.x;
hookshot.y = e.y;
self.equipment[self.equipment.indexOf(e)] = hookshot;

Since this will be instantaneous, you may want to add hookshot.animate(“flashandshake”) if this happens on-screen. (Note that e.animate(“flashandshake”) will not work because e is no longer in your equipment array.)

Dummy fighters

Dummy fighters can be used to check whether a status is set to alternate or not.

Say you want to check whether freeze is alternate:

var freezeisalt = false;
var dummyfighter = new elements.Fighter("Wisp"); /*enemy is arbitrary as long as it's not immune to freeze*/
dummyfighter.addstatus("ice",1);
for(stat in dummyfighter.status) {
  if(stat.type == "alternate_ice") {
    freezeisalt = true;
  }
}
if(freezeisalt) {
  /*do what you would do if freeze is alternate*/
} else {
  /*do what you would do if freeze is normal*/
}

They can also be used in non-combat environments to call code that normally only works in combat. For example, you could do this in a generator to get a list of all non-special equipment, even though getequipment() only works when called by a fighter:

var testdummy = new elements.Fighter("Wisp");
vat testskill = new elements.Skill("Meganudge");
testskill.script = "self.setvar(\"allequipment\",getequipment());";
testskill.execute(testdummy,testdummy);
var allequipment = testdummy.getvar("allequipment");

Storing code snippets as skills & executing them

Say you have a really long code snippet you need to copy and paste everywhere to get a custom status to work. You can reduce it a bit by making an entry in items.csv with that code in it, then declaring a skill with the entry name and executing it:

var dostatusstuff = new elements.Skill("internal status thing");
dostatusstuff.execute(self,target);

(self,target) means the fighter executing this script is you, and the fighter being targeted by the script is the enemy. This is pretty standard for most scripts.

Also, note that the AI will not be able to correctly handle executing skills. You should wrap the part with the skill declaration/execution in if(!simulation) { }. If the skill handles something that happens to the AI itself on its own turn, you're out of luck, and you'll need to unpack the script.

Note that using e will require a bit of kludgery since skill scripts have no way of telling what e is supposed to be unless a variable is explicitly assigned to e within the script. Have something like this at the start of the script you'll be executing, in this case for a skill named “internal item”:

var e = self.getvar("thisequipment");

And do this when you execute it:

var examplescript = new elements.Skill("internal item");
self.setvar("thisequipment",e);
examplescript.execute(self,target);
self.resetvar("thisequipment");

self.resetvar(“thisequipment”) is required since the game currently crashes if you end a battle while an equipment or array of equipment as one of your selfvars.

Scripts of skills can be manually modified. Here's a script for a card that executes a script of another random card you have, but needs some setup in order for things to work right:

var emulatethiseq = rand(self.equipment);
var testscript = new elements.Skill("Against all odds_old");
testscript.script = emulatethiseq.script;
self.setvar("thisequipment",e);
testscript.script = "var e = self.getvar(\"thisequipment\"); " + testscript.script;
self.resetvar("thisequipment");
testscript.execute(self,target);

Note that if there is sfx associated with the provided skill in equipmentsounds.json, it will play. Against all odds_old is a skill with no sfx that will likely not be removed or be given sfx any time soon.

Reversing the self and target parameters can let you do things like force the target to flee. The following snippet will cause the target to immediately run away, since Jetpack by default just contains “flee();”:

var runaway = new elements.Skill("Jetpack");
runaway.execute(target,self);

Since the game merely throws an error if a skill fails to execute rather than crashing or abandoning the original script, you can use skills as a sort of rudimentary try/catch mechanism. For example:

var trythis = new elements.Skill("code i'm not sure will work");
self.setvar("diditwork","no");
trythis.script = trythis.script + " self.setvar(\"diditwork\",\"yes\");";
trythis.execute(self,target);
if(self.getvar("diditwork") == "yes") {
  /*do what you'd do if it works*/
} else {
  /*do what you'd do if it didn't work*/
}

Script injection

Basically, fighters have their own script hooks for before combat, before start turn, on start turn, end turn, and after combat (last one might not be a thing?). So if the enemy is Sorceress, target.scriptbeforecombat would be the script that sets up her equipment layout. These script hooks can have scripts put in them as strings.

Equipment script hooks (before execute, on execute, on any equipment use, before start turn, etc.) are also accessible and can be modified.

Overall these are all the names you need to know:

  • scriptbeforecombat
  • scriptbeforestartturn
  • scriptonstartturn
  • scriptendturn
  • scriptaftercombat
  • script (just “script”, on execute)
  • scriptbeforeexecute
  • scriptonanyequipmentuse
  • scriptonanycountdownreduce

Todo: go into more detail, look up what “on any countdown reduce” and “on fury” hook names are if they aren't exactly what they'd sound like.

Modifying map nodes

Here's a function:

function specialenemychat(floor) {
	for (node in floor.nodes) {
	  if (node.enemytemplate != null && node.enemytemplate.name == "Rhino Beetle") {
		node.enemytemplate.firstwords = "This is my special|boss-like dialogue!||This is another dialogue box!"; node.enemytemplate.alwayssayfirstwords = true;
		}
	}
};

Add this function to a generator. When you generate each floor, assign it to a variable (e.g. floorx where x is the floor number) the way the last floor is assigned to lastfloor. Then after the floor is generated, call specialenemychat with that floor variable as the argument. This will make Rhino Beetle have special first words, like a boss would, before every fight. (While you can also accomplish this by adding to the “first words” column in Rhino Beetle's .csv, you can not have the first words be picked randomly from a pool, whereas with generator tricks you could do node.enemytemplate.firstwords = rand([“Dialogue 1”,“Dialogue 2”]);)

Here's all the different things an enemy template possesses:

  • name: string
  • health: integer
  • superhealth: integer
  • dice: integer
  • superdice: integer
  • level: integer
  • scriptbeforecombat: string
  • scriptaftercombat: string
  • scriptbeforestartturn: string
  • scriptonstartturn: string
  • scriptendturn: string
  • equipment: array of strings
  • superequipment: array of strings
  • skills: array of strings
  • alwayssayfirstwords: boolean
  • alwayssaylastwords: boolean
  • lastwords1: string
  • lastwords2: string
  • lastwords3: string
  • lastwords_iftheywin: string
  • lastwords_endgame: string
  • lastwordprogress: integer
  • firstwords: string
  • kludge_introfirstwords: boolean
  • description: string
  • innate: array of strings
  • ai: string
  • hassuper: boolean
  • boss: boolean
  • rare: boolean
  • combatAnimation: string
  • overworldAnimation: string
  • vfxoffset: coordinate pair
  • combatanimationoffset: coordinate pair
  • overworldanimationoffset: coordinate pair
  • limit: string
  • alternatelimit: string
  • voice: string
  • chatvoice: string
  • chatstyle: string

Example:

Shops, chests and trading booths also have templates, probably.

Call .toString() on a floor variable and you get some useful info. Todo: test this, actually write stuff.

Jinx syntax

jinx(
	name,
	effect description starting in lowercase,
	effect description starting in uppercase,
	script when activated,
	target,
	user,
	turn count,
	%VAR% value
);

Note that tabs and newlines here are purely for readability, and scripts in a spreadsheet should never use newlines. Jinxes themselves are one of the less complicated features in Dicey Dungeons. This section only exists so people who don't know how to create jinxes aren't alienated by the following section.

Further control over jinxes

Consider the following jinx for a hypothetical equipment “Alternative Medicine”, whose function is to heal and poison you by the inserted dice in 2 turns:

jinx(
	"Alternative Medicine",
	"heal [heal]%VAR%[;] gain [poison]%VAR% poison",
	"Heal [heal]%VAR%[;] gain [poison]%VAR% poison",
	"
		inflict(\"poison\",%VAR%);
		attack(-%VAR%);
		sfx(\"_heal\");
		sfx(\"_poison\");
	",
	self,
	self,
	2,
	d
);

Quite manageable. but say you want the upgrade to Alternative Medicine to have one dice slot determining how much you poison yourself, and one slot determining how much you heal yourself. How? %VAR% is only one variable! (Yes, you could make %VAR% a two-digit number and have the script manually break it down, but hold on.)

The trick is that you can manually substitute values into the script and description strings, so %VAR% isn't actually necessary in any context except for convenience.

jinx(
	"Alternative Medicine+",
	"heal [heal]" + actualdice[0].basevalue + "[;] gain [poison]" + actualdice[1].basevalue + " poison",
	"Heal [heal]" + actualdice[0].basevalue + "[;] gain [poison]" + actualdice[1].basevalue + " poison",
	"
		inflict(\"poison\"," + actualdice[1].basevalue + ");
		attack(-" + actualdice[0].basevalue + ");
		sfx(\"_heal\");
		sfx(\"_poison\");
	",
	self,
	self,
	2,
	d
);

The non-backslashed double quotes in the above script mark interruptions in the string to substitute external values.

Abusing Actuate for real-time effects

Hoo boy where to start with this. Actuators as we know them are exploits regarding the Actuate library and the creation of a SimpleActuator - an object used to handle smooth animation of graphics - which allow for realtime delays before scripts are processed, and scripts that repeatedly activate over an interval of time. If done right, they should look like this:

 if(self.getvar(~myactuator~) + 1 == ~SimpleActuator1~) {
    self.getvar(~myactuator~).stop();
    self.resetvar(~myactuator~);
 }
 var tw = new motion.actuators.SimpleActuator(null|0.1|null);  
 var s = new elements.Skill(~Against all odds_old~);
 s.script = ~
    /*code stuff. quotes here should be backslashed e.g. \~*/
    /*out clauses to stop the actuator should look like this:*/
    if(things) {
       self.getvar(\~myactuator\~).stop();
       self.resetvar(\~myactuator\~);
    }
    /*the actuator should also stop if combat ends in any way. fighter.graphic is the literal graphic displayed by a fighter in battle; it's null if you're not in battle.*/
    if(self.hp <= 0 # self.graphic == null # target == null # target.graphic == null # target.hp <= 0) {
       self.getvar(\~myactuator\~).stop();
       self.resetvar(\~myactuator\~);
    }
    /*if you're doing something like moving the player's equipment aside and giving them a few cards and a dice to make a selection; check whether self.doendturnnow ever turns true, and if so clean up immediately.*/
 ~;
 tw._repeat = -1;
 tw.onRepeat(s.execute|[self|target]);
 self.setvar(~myactuator~|tw);
 tw.move();

The second parameter in new motion.actuators.Simpleactuator is the initial delay and how much time in seconds passes between each repeat of the actuator. tw._repeat just determines how many times the actuator should repeat before it stops automatically. -1 means repeat forever, much like how an equipment with -1 uses is infinitely reuseable. Note tw and s in this case are both arbitrary variable names. If for some reason you're dead-set on elegance or conciseness and want an actuator that only executes a single command (which almost always means it shouldn't have a repeat of -1), the arguments taken by tw.onRepeat are a function and then an array of that function's arguments, so you could do for example:

 tw.onRepeat(attack|[d]);

If you want an actuator that doesn't repeat, and simply acts like a delayed command rather than a while loop, keep in mind tw.onComplete. E.g.

 var tw = new motion.actuators.SimpleActuator(null|0.01|null);
 var s = new elements.Skill(~Against all odds_old~);
 s.script = ~my awesome code~;
 tw.onComplete(s.execute|[self|target]); tw.move();

User Tools