Back to: Content Creation

Table of Contents:

Getting Started

Every turret needs both types of scripts, but a lot of effort has been put in to allow non-scripters to copy and past 99% of the scripts to get their turrets working.
This tutorial is meant to give you the broadest over view of creating the most basic turret. If you have done absolutely no programming or scripting ever, this may be a bit over your head still. But what do I know, maybe you'll take to it like a creep to a path (god that was lame, even by my standards).
So lets take a look at the matter. Step 1, We need to create our script file, this is just a simple text file with a .as extension.
You should name the file something like MyTurret.as and put it in the same directory as the turrets XML file and images.

Ok, the first thing you should know is that Turrets are Classes in Network Defense. If you don't know what classes are, look up Object Oriented Programming. Its a topic too deep for this tutorial.
Still here? Then I can be a bit more specific, Turrets, as far as Network Defense knows, are just a common interface:
ITurret.as
/**
* This is the common interface all Turrets must implement
*/

interface ITurret {
	void tick();
	bool fire();
	void upgrade();
	void setWorld( World@ w );
}
					
Every single Turret Class must implement every one of these methods. However, not every turret NEEDS all of these methods. So to simplify your job, I provide a base implementation that your turrets can inherit from:
TurretBase.as
#include "/data/common/turrets/ITurret.as"

class TurretBase : ITurret
{
	Turret@	m_turret;
	World@	m_world;
	
	TurretBase( Turret@ turret )
	{
		@m_turret = @turret;
	}
	
	void tick()
	{
	}
	
	bool fire()
	{
		return true;
	}
	
	void upgrade()
	{
	}
	
	void setWorld( World@ w )
	{
		@m_world = @w;
	}
}
					
One thing to note, the include directive can take absolute paths by using a leading slash. In terms of scripts, absolute paths refers to a path relative to the Network Defense executable.
Relative paths in the scripts have no leading slash, and are relative to the location of that actual script file.
Absolute paths should be used to refrence anything in the common directory. And relative paths should be used to access anything in your current directory.

Lets take a look at the different methods implemented here, any of which can of coure be over ridden inside your deriving class:
  • TurretBase( Turret@ turret ): The constructor for our base class, it takes in a handle to the application side turret object which stores basic information about our turret

  • void tick(): The application will call this method once every frame allowing the turret to process any logic it wishes

  • bool fire(): The application will call this method when the turret is ready to fire. The script can then decide which creeps if any it wants to fire on. Any damage or other effects to the creeps should be handled here. Then if the turret does indeed fire on a creep, it should return true to let the application know, other wise, it should return false. This method will keep being called as long as the turret is ready to fire, and this method continues to return false

  • void upgrade(): The application will call this method once for every upgrade level as the turret is upgrade. A level is never skipped. The script can use this call to manage any changes that result from the upgrade.

  • void setWorld( World@ w ): After a Turret is constructed, it will be added to the game world. When this happens, the application will call this method, pass in a handle to the world it was added to.
Now all that is required is to derive from this base class to make your own! Here is a simple implementation of a turret that deals 5 damage when it fires:
#include "/data/common/turrets/TurretBase.as"

class MyTurret : TurretBase
{
}
					
Lets break it down, first you see we include the TurretBase Class, and then we declare our own class, and have it inherit from BaseTurret.

Next, we need the proper constructor to comply with the ITurret interface:
#include "/data/common/turrets/TurretBase.as"

class MyTurret : TurretBase
{
	MyTurret( Turret@ turret )
	{
		super( turret );
	}
}
					
As you can see, I've added a few things here, firstly, I added the constructor which takes in a handle to the application side Turret.

Then the first thing I do in the constructor is call our parent constructor from TurretBase by using the super keyword. This is importaint because this allows us to use the m_turret member later.

Lets make one small addition before we contiue. To make it easy to tweak the ammount of damage our turret does, lets make it a member varriable:
#include "/data/common/turrets/TurretBase.as"

class MyTurret : TurretBase
{
	int16 m_damage;
	
	MyTurret( Turret@ turret )
	{
		super( turret );
		
		m_damage = 5;
	}
}
					
Ok, now the last thing we need to do, is over ride the bool fire() method to allow us to apply the damage to the creeps:
#include "/data/common/turrets/TurretBase.as"

class MyTurret : TurretBase
{
	int16 m_damage;
	
	MyTurret( Turret@ turret )
	{
		super( turret );
		
		m_damage = 5;
	}
	
	bool fire()
	{
		
	}
}
					
Ok, lets build this function up peice by peice, first lets just get our return value setup:
	bool fire()
	{
		bool fired = false;
		
		return fired;
	}
					
Remember that we want to return false if we did NOT fire, and true if we DID fire. So by default, we will return false, unless we decide we want to fire.

The world member that our TurretBase stores, provides different ways of query for creeps to fire on. The easiest one to use is: getOptimalCreep( Turret@ ). This method will return a handle to a creep which is inside this turrets range, and it will pick the creep which is closest to the end of its path.

In short, it will pick the creep that most Tower Defense type games would pick for a turret.

Here getOptimalCreep( Turret@ ) will return null if no Creep met the parameters. However it is importaint to note that, you should always always ALWAYS check to see if a Creep handle is null before using it. If you do this, you will always be safe :)
	bool fire()
	{
		bool fired = false;

		Creep@ c = m_world.getOptimalCreep( m_turret );
		
		if( c !is null ) {

		}
		
		return fired;
	}
					
Now that we have the creep we want to fire on, lets deal some damage to it:
	bool fire()
	{
		bool fired = false;

		Creep@ c = m_world.getOptimalCreep( m_turret );
		
		if( c !is null ) {
			c.health( c.health() - m_damage );
			
			if( c.health() <= 0 )
			{
				m_world.killCreep( m_turret, c );
			}
			
			fired = true;
		}
		
		return fired;
	}
					
The first thing we do here is subtract our damage from the creeps health.

Then we check to see if the creep is dead by checking if it's health is at or below zero. If so, we tell the world to kill the creep. This is importaint because it ensures the player gets paid for the kill!

The very last thing we do, is flip our return value fired to true, because by this point we have fired, and we need to let the application know that.

And thats it! We have a simple turret that deals 5 damage to creeps with in it's range. The possibilities are limitless though, check into the Scripting API to see all of the tools provided.

Now the last thing before we can use our turret, is we must create a client script!

A note about storing handles to Creeps. Creeps can be removed from the world for any number of reasons at any time. To make things easier on Scripters, the application will scan all script objects for handles to the creep being removed, and will null out the handle as to never leave dangling handles. So all you have to do as a scripter to be safe, is always ALWAYS check your Creep handles for null before using them!