Modding Resources: Installing Mods | Creating Mods | Basic Code Examples | Creating a Character | Audio and Visual Effects | The Automator | User Interface | Graph Editor

Creating a Character

Characters are made up of several different components that can work together. Some of the things a character can have are a Level Graph (aka Skill Tree), Skills, Traits, Stats, and Buffs, and we will be looking at some of the ways these can be implemented and manipulated here.

See ExampleCharacterMain.as in the Clicker Heroes 2\mods\examples folder for a mod made using the code described here.

Basic Character Creation and Info

Note: At the moment the character name must be exactly the same as the mod name defined in the MOD_INFO object.

Adding new art assets is currently not supported, however for the time being you can use Cid's artwork to create a new character by setting your character's assetGroupName to "HelpfulAdventurer" in the onStartup function.

Creating a character requires creating a new Character object, filling out the basic information for this new Character, and then adding it to Characters' startingDefaultInstances.

To do this, you will need to import models.Characters in your mod. Then create a new Character in a public variable in your main class, outside of the other functions, so that any function can access the new character you're creating:

	
	public var newCharacter:Character = new Character();
	

Here is a template for setting up a new character that uses Cid's ("HelpfulAdventurer"'s) art assets, it should go in the onStartup function:

	
	newCharacter.assetGroupName = "HelpfulAdventurer";
	newCharacter.name = "Example Character";
	newCharacter.flavorName = "Newbie";
	newCharacter.flavorClass = "New Character";
	newCharacter.flavor = "A character who is new.";
	newCharacter.availableForCreation = true;
	newCharacter.visibleOnCharacterSelect = true;
	newCharacter.startingSkills = [ ];
	newCharacter.upgradeableStats = Character.DEFAULT_UPGRADEABLE_STATS;
	
	Characters.startingDefaultInstances[newCharacter.name] = newCharacter;
	

You will also want to add a mod dependency so that saves created with your new character will indicate that this mod is required to be installed. See the Mod Dependencies section for more information.

Creating a Level Graph (aka Skill Tree)

To create a level graph you will need to define the node types that will be in the graph, then set the location of the nodes and the edges that will connect them in the levelGraphObject, and finally have your character load the levelGraphObject you created.

First you will need to import heroclickerlib.LevelGraph. Here is the basic outline for creating a level graph for the character newCharacter that was defined in the previous example, this is all done from within the onStartup function:

	
	newCharacter.levelGraphNodeTypes = { }

	newCharacter.levelGraphObject = { }

	newCharacter.levelGraph = LevelGraph.loadGraph(newCharacter.levelGraphObject, newCharacter);

	
Level Graph Nodes

A character has a levelGraphNodeTypes object that contains information about all of the nodes that are available to purchase in that character's skill tree. Template for creating new nodes:

	
	newCharacter.levelGraphNodeTypes = { 
		"N1": { 
			"name": "Example Node",
			"tooltip": "" ,
			"flavorText": null,
			"alwaysAvailable": true,
			"setupFunction": function() {},
			"purchaseFunction": function() {},
			"icon": ""
		    },
		"N2": {
			"name": "Another Node",
			"tooltip": "",
			"flavorText": null,
			"alwaysAvailable": false,
			"setupFunction": function() {},        
			"purchaseFunction": function() {},
			"icon": ""
		    }
	}
	

N1 and N2 are IDs that you can use to reference the individual nodes. What you put in the purchaseFunction field is what happens when a player purchases that node in the skill tree. Some examples are to give the player a new skill, a trait, or to increase their stats, all of which will be detailed below.

The setupFunction is for code that needs to run when the character is created, before the node in purchased. For an example of how the setup function can be used, see The Automator section.

Level Graph Object

The level graph object defines the location where each node will be in the graph, and which nodes are connected to each other. Each edge (connecting line) connects two nodes, and each node has an x-coordinate and a y-coordinate for where it is located within the Skill Tree panel. Here is an example level graph object that places and connects the nodes N1 and N2 from the previous example:

	
	newCharacter.levelGraphObject = {"edges":[{"1":[1,2]}], "nodes":[{"1":{"val":"N1", "x": 0, "y": 0}},{"2":{"val":"N2","x":75,"y":0}}]};
	

The above line of code has one edge,1, that connects nodes 1 and 2. The first node, 1, is set to have the value of N1 at the coordinates (0,0) and the second node, 2, has the value N2 at the coordinates (75, 0).

You can use the same nodes multiple times within the level graph, for example, you can have two or more nodes with the "val" of N2, so characters can get the same effect multiple times. This can be useful to use with the addTrait function, which is explained in the Creating and Using Traits section.

To help with the visual layout of your graph you can create or edit level graph objects using the Graph Editor tool. See the Graph Editor section for how to use this.

Adding Skills

First you will need to have a skill created. You can use any skill that already exists and has been added to to Character.staticSkillInstances, or you can create a new skill, as outlined here: Creating a Skill

Once you have a skill you want to use, there are a couple of ways to add it to a character. One way is through the skill tree, by adding it through the purchaseFunction field of a node:

	
	"purchaseFunction": function() {
		var skill:Skill = CH2.currentCharacter.getStaticSkill("Example Skill");
		CH2.currentCharacter.activateSkill(skill.uid);

		CH2.currentCharacter.hasPurchasedFirstSkill = true;
	}
	

The above code will give the skill "Example Skill", which was created in the Creating a Skill section, to a character when they purchase this node from the skill tree. If this skill can be one of the first skills your character can purchase from the level graph, you will want to set hasPurchasedFirstSkill to true, otherwise you can leave this part out.

Another way to add the skill to your character is to list it in your character's startingSkills array:

	
	newCharacter.startingSkills = ["Example Skill"];
	

With the above code, your character will start out with this skill available, without the need to have a node in the skill tree for it.

Note: If you are using a pre-existing skill that has a sound effect you will need to load that sound for your character as explained in Adding Sounds.

Creating and Using Traits

Traits are simple attributes that a character can have that can be used in multiple ways. A trait only has two parts, a name and a numerical value. You create a trait as you add it to a character by using one of the following functions:

	
	CH2.currentCharacter.addTrait("ExampleTrait", 1);

	CH2.currentCharacter.setTrait("ExampleTrait",1);
	

addTrait will add the value you input (in this case 1) to the current value of the trait (which will be 0 if this is the first time the character is being assigned this trait), and setTrait will overwrite whatever the current trait value is and set it to the value you input.

You can access a trait your character might have, either to see if they have it, or to use the numerical value assigned to it with the getTrait function:

	
	CH2.currentCharacter.getTrait("ExampleTrait");
	

Example use:

In levelGraphNodeTypes we can use the addTrait function in the Node's purchaseFunction field to add a trait when a that node is purchased:

	
	"purchaseFunction": function() { CH2.currentCharacter.addTrait("ExampleTrait", 1); }
	

Now that the character has the trait, one way we can use it is to affect skills. For example, we can edit the exampleEffect we created in the Creating a Skill section to do more damage if the character has this particular trait:

	
	public function exampleEffect():void 
	{
    	var monster:Monster =  CH2.world.getNextMonster();        
            
    	if (monster) 
	{                
    		var attackData:AttackData = new AttackData();         
                
                
    		var damage:Number;
    		if (CH2.currentCharacter.getTrait("ExampleTrait")) 
		{
			damage = 100 * (1 + CH2.currentCharacter.getTrait("ExampleTrait"));
		}
		else 
		{
        		damage = 100;
    		}
                
    		var damageNumber:BigNumber = new BigNumber(damage);
                
    		attackData.damage = damageNumber;
    		CH2.currentCharacter.attack(attackData);
		}
	}
	

This example uses the getTrait function twice, once to see if the current character has had the trait added with the statement if (CH2.currentCharacter.getTrait("ExampleTrait")), and again with the expression 100 * (1 + CH2.currentCharacter.getTrait), which uses the value we assigned to the trait, in this case 1, and thus setting the damage variable to 200 (or 100 * (1 + 1)). If we wanted, we could create more nodes in the level graph that use the same trait name, and have them add to the number, so you can do things like have the skill do more and more damage as a player gets more nodes in the skill tree.

For example, if we create another node with the purchaseFunction value of CH2.currentCharacter.addTrait("ExampleTrait", 2), the value of 2 would be added to the previous ExampleTrait value of 1, assuming that node was previously purchased, so that the getTrait function will return a value of 3. It would be the same if the value 2 trait node were purchased first, so then purchasing the value 1 node after it would add to the value, again coming to a total of 3. If we instead used the setTrait function here however, it would completely replace any previous data, and purchasing this node would then mean that the value of ExampleTrait would be set to 2, regardless of what other ExampleTrait nodes the player may have purchased previously. If you want to get rid of a trait entirely, you can use the setTrait function to set the value to 0.

Levelling Character Stats

One way you can increase a character's stats is by using the character's levelUpStat function:

	
	CH2.currentCharacter.levelUpStat(CH2.STAT_CRIT_CHANCE);
	

If you put the above line of code in a node's purchaseFunction, it will level up the Crit Chance stat for the current character when they purchase that node. By default it will increase the stat's level by 1, but if you want to level it up more you can specify a value, for example, the following will level up Crit Chance 2 times:

	
	CH2.currentCharacter.levelUpStat(CH2.STAT_CRIT_CHANCE, 2);
	

What happens when you level up a stat is determined by a character's statValueFunctions and the specific stat's calculationType (whether it's an additive or multiplicative stat). A character has a default function for each stat which has a default value that will be used to determine how much to increase that stat's value by when it is levelled up. You can replace the default statValueFunctions and their values for a character in the onStartup function:

	
	newCharacter.statValueFunctions[CH2.STAT_CRIT_CHANCE] = Character.linear(0.05);
	

The above will make it so that 5% crit chance is added to our example character every time their crit chance stat is levelled up. The linear function is used to get the value of 0.05 times the level of the stat, and because Crit Chance is an additive stat, that total will be added to the base value of the stat (the starting value for the character). For other stats we might want to use the exponentialMultiplier function:

	
	newCharacter.statValueFunctions[CH2.STAT_CRIT_DAMAGE] = Character.exponentialMultiplier(1.5);
	

The exponentialMultiplier function will be used to multiply 1.5 to itself once for each level of the stat. In other words, the current stat level will be the power that the value you give to this function is raised to. So at stat level 0, 1.5^0 = 1; at level 1, 1.5^1 = 1.5; at level 2, 1.5^2 = 2.25; and so on. Then, because crit damage is a multiplicative stat, that total will be multiplied to the character's base value of that stat.

The base value of a stat that our character starts out with can be modified in a similar way:

	
	newCharacter.statBaseValues[CH2.STAT_CRIT_CHANCE] = 0.1;
	

The above will set the base value of our example character's crit chance to 10%.

These are all of the current stats and their default Value Functions:

	
	statValueFunctions[CH2.STAT_GOLD] = exponentialMultiplier(1.1);
	statValueFunctions[CH2.STAT_MOVEMENT_SPEED] = exponentialMultiplier(1.05);
	statValueFunctions[CH2.STAT_CRIT_CHANCE] = linear(0.02);
	statValueFunctions[CH2.STAT_CRIT_DAMAGE] = exponentialMultiplier(1.20);
	statValueFunctions[CH2.STAT_HASTE] = exponentialMultiplier(1.05);
	statValueFunctions[CH2.STAT_MANA_REGEN] = exponentialMultiplier(1.1);
	statValueFunctions[CH2.STAT_IDLE_GOLD] = exponentialMultiplier(1.25);
	statValueFunctions[CH2.STAT_IDLE_DAMAGE] = exponentialMultiplier(1.25);
	statValueFunctions[CH2.STAT_CLICKABLE_GOLD] = exponentialMultiplier(1.5);
	statValueFunctions[CH2.STAT_CLICK_DAMAGE] = exponentialMultiplier(1.1);
	statValueFunctions[CH2.STAT_TREASURE_CHEST_CHANCE] = linear(0.02);
	statValueFunctions[CH2.STAT_MONSTER_GOLD] = exponentialMultiplier(1.12);
	statValueFunctions[CH2.STAT_ITEM_COST_REDUCTION] = exponentialMultiplier(0.92);
	statValueFunctions[CH2.STAT_TOTAL_MANA] = linear(25);
	statValueFunctions[CH2.STAT_TOTAL_ENERGY] = linear(25);
	statValueFunctions[CH2.STAT_CLICKABLE_CHANCE] = linear(0.1);
	statValueFunctions[CH2.STAT_BONUS_GOLD_CHANCE] = linear(0.01);
	statValueFunctions[CH2.STAT_TREASURE_CHEST_GOLD] = exponentialMultiplier(1.25);
	statValueFunctions[CH2.STAT_PIERCE_CHANCE] = linear(0.01);
	statValueFunctions[CH2.STAT_ENERGY_REGEN] = linear(0.00);
	statValueFunctions[CH2.STAT_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ENERGY_COST_REDUCTION] = linear(0);
	statValueFunctions[CH2.STAT_ITEM_WEAPON_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_HEAD_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_CHEST_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_RING_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_LEGS_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_HANDS_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_FEET_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_ITEM_BACK_DAMAGE] = exponentialMultiplier(1.50);
	statValueFunctions[CH2.STAT_AUTOMATOR_SPEED] = exponentialMultiplier(1.25);
	

These are all of the current stats and their default Base Values:

	
	statBaseValues[CH2.STAT_GOLD] = 1;
	statBaseValues[CH2.STAT_MOVEMENT_SPEED] = 1;
	statBaseValues[CH2.STAT_CRIT_CHANCE] = 0.05;
	statBaseValues[CH2.STAT_CRIT_DAMAGE] = 3;
	statBaseValues[CH2.STAT_HASTE] = 1;
	statBaseValues[CH2.STAT_MANA_REGEN] = 1;
	statBaseValues[CH2.STAT_IDLE_GOLD] = 1;
	statBaseValues[CH2.STAT_IDLE_DAMAGE] = 1;
	statBaseValues[CH2.STAT_CLICKABLE_GOLD] = 1;
	statBaseValues[CH2.STAT_CLICK_DAMAGE] = 1;
	statBaseValues[CH2.STAT_TREASURE_CHEST_CHANCE] = 0.02;
	statBaseValues[CH2.STAT_MONSTER_GOLD] = 1;
	statBaseValues[CH2.STAT_ITEM_COST_REDUCTION] = 1;
	statBaseValues[CH2.STAT_TOTAL_MANA] = 100;
	statBaseValues[CH2.STAT_TOTAL_ENERGY] = 100;
	statBaseValues[CH2.STAT_CLICKABLE_CHANCE] = 0.00;
	statBaseValues[CH2.STAT_BONUS_GOLD_CHANCE] = 0;
	statBaseValues[CH2.STAT_TREASURE_CHEST_GOLD] = 1;
	statBaseValues[CH2.STAT_PIERCE_CHANCE] = 0;
	statBaseValues[CH2.STAT_ENERGY_REGEN] = 0;
	statBaseValues[CH2.STAT_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ENERGY_COST_REDUCTION] = 0;
	statBaseValues[CH2.STAT_ITEM_WEAPON_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_HEAD_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_CHEST_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_RING_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_LEGS_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_HANDS_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_FEET_DAMAGE] = 1;
	statBaseValues[CH2.STAT_ITEM_BACK_DAMAGE] = 1;
	statBaseValues[CH2.STAT_AUTOMATOR_SPEED] = 1;
	

These are all of the current stats and their default calculation types:

	
	STAT_GOLD = MULTIPLICATIVE
	STAT_MOVEMENT_SPEED = MULTIPLICATIVE
	STAT_CRIT_CHANCE = ADDITIVE
	STAT_CRIT_DAMAGE = MULTIPLICATIVE
	STAT_HASTE = MULTIPLICATIVE
	STAT_MANA_REGEN = MULTIPLICATIVE
	STAT_IDLE_GOLD = MULTIPLICATIVE
	STAT_IDLE_DAMAGE = MULTIPLICATIVE
	STAT_CLICKABLE_GOLD = MULTIPLICATIVE
	STAT_CLICK_DAMAGE = MULTIPLICATIVE
	STAT_TREASURE_CHEST_CHANCE = ADDITIVE
	STAT_MONSTER_GOLD = MULTIPLICATIVE
	STAT_ITEM_COST_REDUCTION = MULTIPLICATIVE
	STAT_TOTAL_MANA = ADDITIVE
	STAT_TOTAL_ENERGY = ADDITIVE
	STAT_CLICKABLE_CHANCE = ADDITIVE
	STAT_BONUS_GOLD_CHANCE = ADDITIVE
	STAT_TREASURE_CHEST_GOLD = MULTIPLICATIVE
	STAT_PIERCE_CHANCE = ADDITIVE
	STAT_ENERGY_REGEN = ADDITIVE
	STAT_DAMAGE = MULTIPLICATIVE
	STAT_ENERGY_COST_REDUCTION = ADDITIVE
	STAT_ITEM_WEAPON_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_HEAD_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_CHEST_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_RING_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_LEGS_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_HANDS_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_FEET_DAMAGE = MULTIPLICATIVE
	STAT_ITEM_BACK_DAMAGE = MULTIPLICATIVE
	STAT_AUTOMATOR_SPEED = MULTIPLICATIVE
	

Creating and Using Buffs

Buffs are a versatile feature that can be used to affect many aspects of a character. One way to apply a buff to a character is through skills, by creating the buff within a function that a skill's effectFunction references (see the Creating a Skill section for information about the effectFunction). You will need to create a buff object, fill out information about the buff, including what you want the buff to do, and then add the buff to the list of Character's buffs.

First you will need to import models.buff. Here is a simple example of a function that creates a buff:

	
	public function buffExampleEffect():void 
	{
		var buff:Buff = new Buff();

		buff.name = "Example Buff";
		buff.iconId = 200;
		buff.duration = 5000;
		buff.tickRate = 1000;
		buff.tickFunction = function() {
			CH2.currentCharacter.addEnergy(5);
		}
		buff.tooltipFunction = function() {
			return {
				"header": "Example Buff",
				"body": "Restoring " + (5 * CH2.currentCharacter.hasteRating).toFixed(2) + " energy per second."
			};
		}

		CH2.currentCharacter.buffs.addBuff(buff);
	}

	

The tickFunction is what happens whenever a buff "ticks". The tickRate is how often it ticks, ie. how often the tickFunction is run while the buff is active. The above code will add 5 energy to the current character every 1000 milliseconds for the next 5000 milliseconds.

By default, a buff's tickRate is affected by how much haste a character has, which is why the tooltip description is coded to take hasteRating into account. The toFixed function simply limits how many decimal places of a number will be shown.

Other attributes you can use when creating a buff:

	
	buff.isFinished = true;

	buff.onFinish();
	

The above boolean and function can be used together to end a buff before the set duration runs out, for example, if some other condition is met.

	
	buff.isUntimedBuff

	buff.stacks

	buff.maximumStacks
	

If isUntimedBuff is set to true you can create a buff that does not have a set time duration. With stacks and maximumStacks you can create a buff that has a set number of charges.

	
	buff.timeLeft

	buff.timeSinceActivated:Number

	buff.buffStat(id, buffModifier)
	

The buffStat function works similarly to the levelUpStat function, but the effect only lasts while the buff is active. It requires one of the stat IDs listed in the Levelling Character Stats section, and a Number for the amount you want the buff to modify the stat.

	
	buff.attackFunction = function(attackDatas:Array) { }
	

The attackFunction allows a buff to run code when a character attacks, and can be used in place of, or in addition to, the tickFunction.

	
	buff.finishFunction = function() { }
	

In the finishFunction you can add code that runs when a buff ends.

You can check whether or not a character currently has a buff using the hasBuffByName function, which returns a boolean value (true or false):

	
	CH2.currentCharacter.buffs.hasBuffByName("Example Buff");
	

Overriding Character Functions

There is basic functionality for all characters that is pre-set in the game that determines what happens when a character does basic things like attack, use a skill, or kill a monster. This means if you like how these things work by default you don’t have to touch them and they will just work, however if you want your character to work differently you can override many of these functions. This is also useful if you generally want things to work like default, but want to add traits to your character that change how things work in certain circumstances.

In order to override one of these functions you first need to have your mod tell the game that it’s going to be handling that function in the onCharacterCreated function. You do this by setting the specific Handler variable for the function you’re overriding (See a list of all Handler variables here). Here is an example that tells the game our Example Character mod will be handling the attack function:

	
	public function onCharacterCreated(characterInstance:Character):void 
	{
		if (characterInstance.name == "Example Character")
		{
		    characterInstance.attackHandler = this;
		}  
	}
	

After you’ve told the game your mod will be handling it, you will need to create an override function for what the game should do instead. The function you create must have a specific name and requires a certain parameter list and return type. You can see a list of all of those here. To continue our example, here is what it would look like to override the attack function:

	
	public function attackOverride(attackData:AttackData):void 
	{                
		var character:Character = CH2.currentCharacter;
		if (character.getTrait("Example Trait"))
		{
			attackData.damage.timesEqualsN(2);
		}
		    
		character.attackDefault(attackData);
	}
	

In this example, whenever the character attacks it first checks to see if they have the “Example Trait” that was created in the Creating and Using Traits section. If they do, their damage gets doubled before being sent to the default attack function, so that other than the increased damage everything proceeds exactly the same as the default. If they do not have the trait, nothing gets changed and the character continues to attack as normal. This is how it's possible to take advantage of the default code without having to completely rewrite it, while still making certain changes specific to a certain character.

List of Overridable Character Functions