The Psionics mutator adds special powers to ordinary UT2004 game play, making games more varied, tactical and of course EXPLOSIVE. Hurl players off cliffs, track opponents through walls and blow them to bits, the possibilities are endless. Full AI and multiplayer online support. Now has the full source code for your perusal.

Post tutorial Report RSS Making a Psionic Move

This is a tutorial that will help people create their own psionic moves for use with the psionics mutator.

Posted by on - Basic Server Side Coding

Synopsis


This is the first part of four to help people create their own moves. This part details how to set things up and get started on creating basic parts of a functional move. By the end you should be able to create your own basic moves. Note, some of the code requires reformatting due to the 80 character limit on words.

Starting out

  1. Create new folder 'ExtraPsiMoves'
  2. Create 'Classes' folder within that folder
  3. Also create a 'Textures' folder here for later
  4. Create a new 'PsychicMoveHeal.uc' file to represent your new move

Setting Up UDE


Open UDE and your file 'PsychicMoveHeal.uc'
Open EditPackages and add the 'ExtraPsiMoves' class package to it

Starting Structure


Now we will create the barebones structure, firstly we will extend PsychicMove and then set default properties
You can ignore typing the comments for speeds sake

//========

//Heal those who are friendly

//

//Author:

// [Add your name here]

//Date:

// [Add the date here]

//==========

class PsychicMoveHeal extends PsychicMove;

defaultproperties

{

//Some information

thename="Heal"

description="Heal your buddies"

moveon=false

//For the Description tab panel, not all that important

levelDescription[0]="Heal only those who are very close to you who you are looking at"

levelDescription[1]="Heal at a further distance who you are looking at"

levelDescription[2]="Heal at an even further distance who you are looking at"

levelDescription[3]="Heal anyone within a radius of you up to 75% health"

levelDescription[4]="Heal anyone within a radius of you up to full health"

//What it costs to use, if we don't have this much energy the move won't start

// In this case we want it to be free

levelInitialCost[0]=0

levelInitialCost[1]=0

levelInitialCost[2]=0

levelInitialCost[3]=0

levelInitialCost[4]=0

//What it costs to use per second, in this case we only drain energy when it is in use

// so set to 0

levelUsingCost[0]=0

levelUsingCost[1]=0

levelUsingCost[2]=0

levelUsingCost[3]=0

levelUsingCost[4]=0

binstant = false //As this move works over a period of time it is not instant (Blast is instant)

botuseful = true //We will add some bot support to this so set this to true

Icon=Texture'HealIcon' //The icon, we'll set it later

}

 

Icon


Now we will set the icon so that we know which one our move is when selected
Create an icon in your favorite graphics program (i.e. photoshop)
The icon must be 64x64, for best effects use a 32-bit tga (with alpha)
Save this icon into the 'Textures' folder of your package
Add the following line just below the class declaration:
#exec TEXTURE IMPORT NAME=HealIcon FILE=textures\Heal.tga GROUP="HudIcons" MIPS=OFF Flags=2
This will import the texture, notice we set the NAME to the same as the one used in the default properties
Icon=Texture'HealIcon'
The FILE component is obviously the image, just leave the rest the same for any moves

Functionality


Now for some functionality
Add this under the pre-processor statement

function bool startMove(int plevel, PsychicLinkedReplicationInfo _plri)

{

if(Super.startMove(plevel, _plri)) //If the move starts correctly

{

moveon = true; //Turn the move on

return true; //Return all good

}

return false; //Return not good

}

This starts the move if we don't have any problems (such as a backfire)

function moveTick(float DeltaTime)

{

local vector HitLoc, HitNorm, endloc; //Sate the trace function

local Actor HitActor;

local Pawn HitPawn;

bDidAHeal = false;

if(levelUsed < 3) //Line of sight move

{

endloc = user.Pawn.Location + (vector(user.GetViewRotation()) * 500 * (levelUsed+1)); //Set trace end based on level used

HitActor = user.Pawn.Trace(HitLoc, HitNorm, endloc, user.Pawn.Location, true); //Find an actor

HitPawn = Pawn(HitActor); //Cast it to a pawn as we only want to deal with pawns

//Check if pawn is valid and on same team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None


&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{


targetPawn = HitPawn;

//If so heal them and take away energy


if(HitPawn.Health < 95)

{


HitPawn.Health += levelUsed;


plri.energy -= levelUsed;


bDidAHeal = true;

}

}

}

else //Radius move

{

//With upper levels we use a radius based approach

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250, user.Pawn.Location)

{


//Check if pawn is valid and on same team


if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None


&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{


//If so heal them and take away energy


if(HitPawn.Health < 75 + ((levelused - 3)*25)) //Level 4 only goes to 75%

{


HitPawn.Health += levelUsed; //Use dam type blast as I can't be bothered making a new one


plri.energy -= levelUsed/2;

}

}

}

}

//If energy is depleated then turn off the move

if(plri.energy < 0)

{

plri.energy = 0;


plri.endCurMove();

moveon = false;

}

}

This is the meat and potatoes of the move, read the comments as they explain it pretty easily.

function backfire(int level)

{

//If it stuffs up then cause some damage to us

user.Pawn.TakeDamage(level*10, user.Pawn, user.Pawn.Location, vect(0,0,0), class'DamTypeBlast'); //Use dam type blast as I can't be bothered making a new one

}

The backfire function will trigger if we activate when too hot, we just set it to do some damage to us

Testing

  1. Now for a quick test, compile the package (remember if using UDE to refresh package tree, etc.) and make sure it's all good
  2. Now we need to go to the root UT2004 folder and then into system
  3. Open 'MutPsychicPowers.int' in your favorite text editor
  4. Add the following line ot the bottom of the list of similar ones
  5. Object=(Name=ExtraPsiMoves.PsychicMoveHeal, Class=Class,MetaClass=MutPsychicPowers.PsychicMove)
  6. Now run UT2004 and you should be able to select your new move.

Next Up


The next tutorial will add extra code to render to the HUD, just to show how it is done.


Part 2


Now we have our basic move set up we can add in both a secondary function and some rendering just for kicks. Our secondary function will be an all or nothing health dump that increases the targets health by ours, but kills us, only under level 3. The rendering will draw a box around the person we are healing, the colour dependent upon their health.

Secondary Function


The secondary is very easy to implement.

Firstly we need to store the Pawn we are looking at so we don't have to trace again, add this code to the top of your your ‘PsychicMoveHeal.uc' file (just under the pre-processor statement):

var Pawn targetPawn;

 

Now within the moveTick function, inside the if loop that confirms our pawn is valid type the top line:

targetPawn = HitPawn;

//If so heal them and take away energy

if(HitPawn.Health < 95)

 

Now add this function anywhere in the file.

function bool secondaryFunction()

{

//If we have a target

if(targetPawn != None)

{

//Add health to the target


targetPawn.Health += user.Pawn.Health;

//Kill us off


user.Pawn.TakeDamage(user.Pawn.Health*2, user.Pawn, user.Pawn.Location, vect(0,0,0), class'DamTypeBlast');

return true;

}

//Do nothing

return false;

}

Rendering


Okay, now we have a rather pathetic secondary move, we will now draw an even more pathetic box around the player we are healing. For us this means we need to deal with replication (YAAAYYYYY.) Firstly add this variable declaration below the targetPawn one:

var bool bDidAHeal;

 

Now below that type the following:

replication

{

reliable if(Role == ROLE_Authority)

bDidAHeal;

unreliable if(Role == ROLE_Authority)


targetPawn;

}

If you don't understand this part then don't worry about it and skip the rest of this paragraph. If you do understand replication, the reason I added the bDidAHeal Boolean was because I set the targetPawn to unreliable. This is simply helping to save net bandwidth, as sending the pawn reliably is very costly as compared to sending the single byte Boolean. Replicating the pawn properly isn't all that important in the grand scheme of things.

 

Righty'o then, time to add the big cahoona

simulated function moveRender(Canvas C, PlayerController owner)

{

local vector screenloc, tempto;

local color targetcol;


//Log("Target pawn is: " $ targetpawn);

//Don't do if this didn't do a heal or no target or no playercontroller

if(!bDidAHeal || targetpawn == None || owner == None)

return;

//Make sure the player is actually in front of us, stops some stupid bug in unreal

tempto = targetpawn.Location - owner.Pawn.Location;


if(targetpawn.Location Dot vector(owner.GetViewRotation()) < 0)

return;

//Get the player's location on screen based on world, uses a special function in the Interaction class

screenloc = owner.Player.InteractionMaster.GlobalInteractions[ 0].WorldToScreen(targetpawn.Location);

//Choose colour using a simple rule system


if(targetpawn.Health < 30)

targetcol = C.MakeColor(255, 0, 0); //RED

else if(targetpawn.Health < 70)

targetcol = C.MakeColor(255, 255, 0); //YELLOW

else

targetcol = C.MakeColor(0, 255, 0); //GREEN

C.DrawColor = targetcol;

//Set box position to center and draw


C.SetPos(screenloc.X - 25, screenloc.Y - 25);

C.DrawBox(C, 50, 50);

//Set font to small and write the pawns health

C.Font = C.SmallFont;


C.DrawText(targetpawn.Health);

}

GO, GO, GO


Hit your compile button and jump into a game and test your new toy.

Next up


We'll add some client side configuration just for the hell of it.


Part 3


The following will allow you to place configurable variables into the Psionics mutators move configuration menu. This will allow the player to change the way the move works to suit their play style. For this tut we will add the option to turn off HUD rendering and also set how health is drawn. For a more in depth look, check out the Perception and Sight classes as these have configurable values.

Our variables


Add the following variable declarations to the top of the file:

//Config vars

var config string healthdrawtype;

var config bool bDrawOnHud;

 

And it is good practice to set their default values so within the defaultproperties block add:
healthdrawtype="honly"

bDrawOnHud=true

The functionality


Before we get down to how to set config values, lets just add in the functionality quickly. Add the following to the top of the moveRender() function (below the variable declarations):

//Hud Drawing option

if(!bDrawOnHUD)

return;

 

And convert the following part of moveRender() (is at the very bottom)

//Set font to small and write the pawns health

C.Font = C.SmallFont;


C.DrawText(targetPawn.Health);

to

//Set font to small and write the pawns health

C.Font = C.SmallFont;


switch(healthdrawtype)

{

case "none":

break;

case "honly":


C.DrawText(targetpawn.Health);

break;

case "hmax":


C.DrawText(targetpawn.Health $ "/" $ targetpawn.HealthMax);

break;

}

Putting it on the menu


Now it is time to allow our user to change the values. We do this using the FillPlayInfo(PlayInfo PlayInfo) function, which astute readers will note is the exact same as how to add mutator configuration variables. Essentially the system works exactly the same way so check tutorials on how to do mutators for more information on how to do this. Okay now add this function somewhere:

static function FillPlayInfo(PlayInfo PlayInfo)

{

local String Options;


Super.FillPlayInfo(PlayInfo); //Call the super to do some setup (VERY IMPORTANT)

Options = "none;No Health;honly;Health Only;hmax;Health/Max"; //OPtions for the combo box, not semi-colon delimited ;

//Add the options combo box


PlayInfo.AddSetting("PsychicMoveHeal", "healthdrawtype", "Health Draw Type", 0, 0, "Select", Options);

//Add the HUD switch


PlayInfo.AddSetting("PsychicMoveHeal", "bDrawOnHud", "Draw to HUD", 0, 0, "Check");

}

This is where all the information is added to the menu; in this case we are adding a combo box for the type of health and a checkbox to turn on/off HUD rendering. Just to round off the menu system we need to add our description setter function of:

static event string GetDescriptionText(string PropName)

{


switch(PropName)

{

case "healthdrawtype": return "What numbers to display for health";

case "bDrawOnHud": return "Enables/Disables drawing to the HUD";

}

return Super.GetDescriptionText(PropName);

}

This simply sets the tool tip for each variable modifier in the menu.

And move out...

  1. Compile...Run...
  2. Open the client-side mutator config window and select the psionics mutator button
  3. On the smaller move selector, hit the configuration button
  4. Scroll down until you find the heal move
  5. Modify and test

Next up

For the last part we will add some bot support so that our bots can use the move.
 


Part 4


This fourth and final part of the tutorial focuses on allowing bots to use your move in game. The AI implemented in the tutorial will be very basic; it will just serve to highlight what the functions do as opposed to being in depth AI simulation. As a challenge at the end, write the AI to be super smart and useful.

The functions


Before we begin I will quickly explain the purpose of the bot functions:

  • getBotWeight(level, energy) - Return a weight (0.0 - 1.0 generally) of how useful this move would be if used at this particular time. The level is the current level of the user and the energy is the amount of energy they have left.
  • getBestLevel(maxlevel, bplri) - Called if move is to be used, return the best level to use at this time. maxlevel is the maximum level we can use for this move while bplri is a link to the bot replication info so we can access any info in that (energy, level, etc.)
  • botUsing(bplri) - Called when a move is on, return true to turn the move off. bplri is a link to the bot replication info so we can access any info in that (energy, level, etc.)

 

Implementation


Firstly we will write the weighting function. The way I did it was to search all pawns within the affected radius and count the ones that needed healing. The weight is then based on how many need healing:

function float getBotWeight(int level, float energy)

{

local Pawn HitPawn;

local int numwecanheal;

//In order to not get too deep into AI, we will make it easy and only use the radius ones

if(level < 3)

return 0;

//Set our variable to 0

numwecanheal = 0;

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250)

{

//Make sure this person is on our team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None


&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

//If they need healing then update counter


if(HitPawn.Health < 70)

{


numwecanheal++;

}

}

}

//Return a weight, as 1.0 is pretty much the top then any more than

// 4 people need healing will make this a priority

return numwecanheal / 0.25;

}

 

The next function is easy for this move, we just return the highest level we can use:

function int getBestLevel(int maxlevel, BotPsychicLinkedReplicationInfo bplri)

{

//We want the best healing we can get

return maxlevel;

}

 

And the final one does the same checking as the weighting function, but will end the move when less than 2 people need healing (i.e. 0 or 1):

function bool botUsing(BotPsychicLinkedReplicationInfo bplri)

{

local Pawn HitPawn;

local int numwecanheal;

//Set our variable to 0

numwecanheal = 0;

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250)

{

//Make sure this person is on our team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None


&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

//If they need healing then update counter


if(HitPawn.Health < 70)

{


numwecanheal++;

}

}

}

//If less than 2 people need healing then we exit the move


if(numwecanheal < 2)

return true;

//Else stay in the move

return false;

}

 

That's all folks


So there you have it, all the knowledge you need to create your own moves. The next page has a complete code dump of the above example.

Enjoy!!!



//=========================================================

//Heal those who are friendly

//

//Author:

//

//Date:

//

//=========================================================

 

class PsychicMoveHeal extends PsychicMove;

 

#exec TEXTURE IMPORT NAME=HealIcon FILE=textures\Heal.tga GROUP="HudIcons" MIPS=OFF Flags=2

 

var Pawn targetPawn;

var bool bDidAHeal;

 

//Config vars

var config string healthdrawtype;

var config bool bDrawOnHud;

 

replication

{

reliable if(Role == ROLE_Authority)

bDidAHeal;

 

unreliable if(Role == ROLE_Authority)

targetPawn;

}

 

function bool startMove(int plevel, PsychicLinkedReplicationInfo _plri)

{

if(Super.startMove(plevel, _plri)) //If the move starts correctly

{

moveon = true; //Turn the move on

return true; //Return all good

}

 

return false; //Return not good

}

 

function bool secondaryFunction()

{

//If we have a target

if(targetPawn != None)

{

//Add health to the target

targetPawn.Health += user.Pawn.Health;

//Kill us off

user.Pawn.TakeDamage(user.Pawn.Health*2, user.Pawn, user.Pawn.Location, vect(0,0,0), class'DamTypeBlast');

 

return true;

}

 

//Do nothing

return false;

}

 

function moveTick(float DeltaTime)

{

local vector HitLoc, HitNorm, endloc; //Sate the trace function

local Actor HitActor;

local Pawn HitPawn;

 

bDidAHeal = false;

 

if(levelUsed < 3) //Line of sight move

{

endloc = user.Pawn.Location + (vector(user.GetViewRotation()) * 500 * (levelUsed+1)); //Set trace end based on level used

HitActor = user.Pawn.Trace(HitLoc, HitNorm, endloc, user.Pawn.Location, true); //Find an actor

HitPawn = Pawn(HitActor); //Cast it to a pawn as we only want to deal with pawns

//Check if pawn is valid and on same team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None

&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

targetPawn = HitPawn;

//If so heal them and take away energy

if(HitPawn.Health < 95)

{

HitPawn.Health += levelUsed;

plri.energy -= levelUsed;

bDidAHeal = true;

}

}

}

else //Radius move

{

//With upper levels we use a radius based approach

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250, user.Pawn.Location)

{

//Check if pawn is valid and on same team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None

&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

//If so heal them and take away energy

if(HitPawn.Health < 75 + ((levelused - 3)*25)) //Level 4 only goes to 75%

{

HitPawn.Health += levelUsed; //Use dam type blast as I can't be bothered making a new one

plri.energy -= levelUsed/2;

}

}

}

}

 

//If energy is depleated then turn off the move

if(plri.energy < 0)

{

plri.energy = 0;

plri.endCurMove();

moveon = false;

}

}

 

simulated function moveRender(Canvas C, PlayerController owner)

{

local vector screenloc, tempto;

local color targetcol;

 

//Hud Drawing option

if(!bDrawOnHUD)

return;

 

//Don't do if this didn't do a heal or no target or no playercontroller

if(!bDidAHeal || targetpawn == None || owner == None)

return;

 

//Make sure the player is actually in front of us, stops some stupid bug in unreal

tempto = targetpawn.Location - owner.Pawn.Location;

if(targetpawn.Location Dot vector(owner.GetViewRotation()) < 0)

return;

 

//Get the player's location on screen based on world, uses a special function in the Interaction class

screenloc = owner.Player.InteractionMaster.GlobalInteractions[0]. WorldToScreen(targetpawn.Location);

 

//Choose colour using a simple rule system

if(targetpawn.Health < 30)

targetcol = C.MakeColor(255, 0, 0); //RED

else if(targetpawn.Health < 70)

targetcol = C.MakeColor(255, 255, 0); //YELLOW

else

targetcol = C.MakeColor(0, 255, 0); //GREEN

 

C.DrawColor = targetcol;

 

//Set box position to center and draw

C.SetPos(screenloc.X - 25, screenloc.Y - 25);

C.DrawBox(C, 50, 50);

 

//Set font to small and write the pawns health

C.Font = C.SmallFont;

switch(healthdrawtype)

{

case "none":

break;

case "honly":

C.DrawText(targetpawn.Health);

break;

case "hmax":

C.DrawText(targetpawn.Health $ "/" $ targetpawn.HealthMax);

break;

}

}

 

function backfire(int level)

{

//If it stuffs up then cause some damage to us

user.Pawn.TakeDamage(level*10, user.Pawn, user.Pawn.Location, vect(0,0,0), class'DamTypeBlast'); //Use dam type blast as I can't be bothered making a new one

}

 

static function FillPlayInfo(PlayInfo PlayInfo)

{

local String Options;

 

Super.FillPlayInfo(PlayInfo); //Call the super to do some setup (VERY IMPORTANT)

 

Options = "none;No Health;honly;Health Only;hmax;Health/Max"; //OPtions for the combo box, not semi-colon delimited ;

 

//Add the options combo box

PlayInfo.AddSetting("PsychicMoveHeal", "healthdrawtype", "Health Draw Type", 0, 0, "Select", Options);

//Add the HUD switch

PlayInfo.AddSetting("PsychicMoveHeal", "bDrawOnHud", "Draw to HUD", 0, 0, "Check");

}

 

static event string GetDescriptionText(string PropName)

{

switch(PropName)

{

case "healthdrawtype": return "What numbers to display for health";

case "bDrawOnHud": return "Enables/Disables drawing to the HUD";

}

 

return Super.GetDescriptionText(PropName);

}

 

//===============BOT STUFF===============

//Returns a weighting on the moves current usage

//Parameter for maximum level that can be used, max amount of energy

//Return 0 or less if useless

function float getBotWeight(int level, float energy)

{

local Pawn HitPawn;

local int numwecanheal;

 

//In order to not get too deep into AI, we will make it easy and only use the radius ones

if(level < 3)

return 0;

 

//Set our variable to 0

numwecanheal = 0;

 

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250)

{

//Make sure this person is on our team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None

&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

//If they need healing then update counter

if(HitPawn.Health < 70)

{

numwecanheal++;

}

}

}

 

//Return a weight, as 1.0 is pretty much the top then any more than

// 4 people need healing will make this a priority

return numwecanheal / 0.25;

}

 

//Best level to use

function int getBestLevel(int maxlevel, BotPsychicLinkedReplicationInfo bplri)

{

//We want the best healing we can get

return maxlevel;

}

 

//Called during usage to tell the bot what to do

//Returns whether or not to turn the move off

function bool botUsing(BotPsychicLinkedReplicationInfo bplri)

{

local Pawn HitPawn;

local int numwecanheal;

 

//Set our variable to 0

numwecanheal = 0;

 

foreach user.Pawn.RadiusActors(class'Pawn', HitPawn, levelUsed * 250)

{

//Make sure this person is on our team

if(HitPawn != None && HitPawn.PlayerReplicationInfo != None && HitPawn.PlayerReplicationInfo.Team != None

&& HitPawn.PlayerReplicationInfo.Team.TeamIndex == user.PlayerReplicationInfo.Team.TeamIndex)

{

//If they need healing then update counter

if(HitPawn.Health < 70)

{

numwecanheal++;

}

}

}

 

//If less than 2 people need healing then we exit the move

if(numwecanheal < 2)

return true;

 

//Else stay in the move

return false;

}

//===========END BOT STUFF===============

 

defaultproperties

{

//Some information

thename="Heal"

description="Heal your buddies"

moveon=false

 

//Move specifics

targetPawn=None

bDidAHeal=false

healthdrawtype="honly"

bDrawOnHud=true

 

//For the Description tab panel, not all that important

levelDescription[0]="Heal only those who are very close to you who you are looking at"

levelDescription[1]="Heal at a further distance who you are looking at"

levelDescription[2]="Heal at an even further distance who you are looking at"

levelDescription[3]="Heal anyone within a radius of you up to 75% health"

levelDescription[4]="Heal anyone within a radius of you up to full health"

 

//What it costs to use, if we don't have this much energy the move won't start

// In this case we want it to be free

levelInitialCost[0]=0

levelInitialCost[1]=0

levelInitialCost[2]=0

levelInitialCost[3]=0

levelInitialCost[4]=0

 

//What it costs to use per second, in this case we only drain energy when it is in use

// so set to 0

levelUsingCost[0]=0

levelUsingCost[1]=0

levelUsingCost[2]=0

levelUsingCost[3]=0

levelUsingCost[4]=0

 

binstant = false //As this move works over a period of time it is not instant (Blast is instant)

botuseful = true //We will add some bot support to this so set this to true

 

Icon=Texture'HealIcon' //The icon, we'll set it later

}

Post comment Comments
mikejkelley
mikejkelley - - 874 comments

Thnx very much for this, I will definitely put it to good use! :)

Reply Good karma Bad karma+1 vote
Mernom
Mernom - - 718 comments

a video is a goood asistant for that type of stuff...

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: