Game Development Community

dev|Pro Game Development Curriculum

A Spell System

by Gareth Fouche · 09/02/2005 (5:24 pm) · 35 comments

Download Code File

I created this spell system for my game a little while ago. Basically, my goal was to create something like the item system, whereby you create a new item by filling out details in the datablock and writing a few scripts. The rest should be handled for you behind the scenes (in code).

So here is is. Basically I have created a Spell object in source code. All you do to create a new spell type is fill in a few of the "stub" scripts, such as onBeginCasting and onTick. The spells can do anything you could do with a script, hurl projectiles, heal, teleport, whatever. As long as you have the scripting ability.

To use the spells, add the .cc and .h to your source code and recompile. Then add the spells.cs to your server directory. Make sure you remember to exec it in game.cs. Just after you exec it, put this line :

InitSpells();


Add the Spells folder from the zip into the server directory as well. If you write new spells, put them in there. The InitSpells() call will ensure they are loaded up.

Now, to cast a spell, you simply create a new Spell object, just like you would create a projectile. You create it on the server. Note, the spell itself does not get ghosted across to the client. But any effects you create might be. So for example in the fireball spell, I just create a projectile. It handles ghosting itself across. Likewise, the heal spell restores players health. The player object handles transmitting that data across, there is no need to send the spell object itself.

Here is code that creates a spell object, just add it at the end of player.cs :

function Armor::castSpell(%this, %obj, %spelldata)
{
   %energy = %obj.getEnergyLevel();

   if(%energy > %spelldata.manaCost)
   {
      %obj.setEnergyLevel(%energy - %spelldata.manaCost); 
      
      %spell = new Spell()
      {
         datablock = %spelldata;
         caster = %obj; 
         casterLevel = 10;     
      };

      MissionCleanup.add(%spell);
   }
}

How you call that function is up to you, you could use a Server Command, personally my player stores a "current spell", and clicking the right mouse button calls this function. Note also the hard coded caster level. The Invisibility spell is an example of how to use caster level as a multiplier of the spells effect, in this case duration.

Spells have a casting time, after that time has finished the spell begins "ticking". It ticks for the duration, then ends. If you want effects (eg. particles) to occur while the player is casting, create them in onBeginCasting and clean them up in onEndCasting. Put the actual spell effects in onEndCasting and onTick. If the spell is instantaneous, like Fireball, just put the code in onEndCasting. onDurationEnd is called when the spell ends.

Note, the duration might not be exactly what you set it to be, as the processing occurs once every 32 ms. If the spell is not a multiple of that, it might get an extra "tick".

I have included some example spells in the .rar, Fireball, Heal and Invisibility. Take a look at those to see how its done.

Have fun!
Page «Previous 1 2
#1
09/02/2005 (7:37 pm)
Gareth Fouche :
The link is broken! Can't download!
#2
09/03/2005 (2:40 am)
I can certainly use this when you get the upload fixed =)
#3
09/03/2005 (7:46 am)
Sounds good - please re-upload.
#4
09/04/2005 (11:17 pm)
Sorry about that guys.


Ok, should be fixed now.
#5
09/11/2005 (7:46 am)
Spell.cc(207) : error C2039: 'mCasting' : is not a member of 'Player'

Just simply added like you said, and it came with this. I am using TGE 1.3. Anyone else get this error? Would love a fix, thanks in advance.
#6
09/11/2005 (10:28 am)
"Spell.cc"
..."Spell.cc(207) : error C2039: 'mCasting' : is not a member of 'Player'"
..."/player.h(236) : see declaration of 'Player'"
...."Spell.cc(219) : error C2039: 'mCasting' : is not a member of 'Player'"
..."/player.h(236) : see declaration of 'Player'"

I also get this error.
I really could use this when it is fixed,
Thanks.
#7
09/11/2005 (8:41 pm)
Edit your player.h file and add:

bool mCasting;

To the Players public definitions
#8
09/11/2005 (9:32 pm)
Example implementation:

default.bind.cs:

function fireball(%val)
{
	if(%val)
	{
		commandToServer('CastSpell', 'fireball');	
	}
}

moveMap.bind( keyboard, f, fireball );

server commands.cs

function serverCmdCastSpell(%client, %spellName)
{
	%player = %client.player;
	if(%player)
	{
		%player.castSpell(%player, detag(%spellName)); 	
	}
}
#9
09/12/2005 (11:04 am)
Thanks for that. :)

I still couldn't get the spells to work, as all the console said was that it couldn't get the energy level, or it couldn't call a function, depending on what I changed in the script.

So, after lots of changes, time and help, I decided I couldn't do anything about it, so I left it for a few months.
Then, today I came back to it, after a discussion with Chris Labombard, and he told me to add this code to player.cc (after adding the bool mCasting by Peter Simard), so I did.
ConsoleMethod( Player, getEnergyLevel, const char*, 2, 2, "Return the energy level.")
{
	char* buf = Con::getReturnBuffer(12);
	dSprintf(buf, 12, "%f", object->getEnergyLevel());
	return buf;
}
(Do a Find/Search for ConsoleMethods, and add it after the first one. :))

The spell system still didn't work.

So I changed the serverCmdCastSpell function from the one in the resource, to this one:
function serverCmdCastSpell(%client, %spellName)
{
	echo ("Server Command CastSpell Called...\n");

	%player = %client.player;
	if(%player)
	{
		echo ("Calling '" @ detag(%spellName) @ "' Spell, Player '" @ %player @ "'...\n");
	       	echo ("Casting Spell... '" @ detag(%spellName) @ "'\n");

		%energy = %player.getEnergyLevel();
		
		echo ("Casters Current Mana... '" @ %energy @ "'\n");
		echo ("Spell's Mana Cost... '" @ detag(%spellName).manaCost @ "'\n");


			if(%energy >= detag(%spellName).manaCost)
			{
				      %newEnergyLvl = %energy - detag(%spellName).manaCost; 
				      echo ("New Energy Level: '" @ %newEnergyLvl @ "'\n");

				      %player.setEnergyLevel(%newEnergyLvl); 
      
				      %spell = new Spell()
				      {
				         datablock = detag(%spellName);
				         caster = %player; 
				         casterLevel = 10;     
				      };

      			MissionCleanup.add(%spell);
			} else {
				echo ("Not Enough Mana!\nMana Level:'" @ %energy @ "'\n");
			}
	}
}
(Note: There are some echo's in there you don't need, they were just there to help me figure out what my problems were.)

This elimated the need for the Armor::castSpell function, and everything worked...
It seemed that the problem was to do with the Armor::castSpell function, so, I elimated it. :)


Very nice resource,
Thanks again,
- Tom.

Note: The maxEnergy is 60, in player.cs, if you haven't edited it. So, it makes invisibility impossible to cast without changing something. ;)

Edit: Code clean-up, and correcting echo contents.

Edit: Everything works now. Thanks to Chris. :)
#10
01/06/2006 (4:27 am)
Sorry I left you guys in the lurch there, glad you could get a fix, if anyone has problems in the future please email me directly if I dont post a response here.

I didn't include my player.cc file as it has quite a few other changes in it. In fact, the casting function I wrote up there isn't what I personally use, player has an internal method which casts the active spell (based on the players spellbook object, another dependancy) when you click the right mouse button. Thats probably why it was buggy, I didn't test it properly!

Again, my apologies to anyone who spent a lot of time getting this to work, feel free to mail me any problems in the future.
#11
01/07/2006 (4:22 am)
nothing happens, done everything above
#12
01/07/2006 (4:52 am)
Have you compiled your project with the spell files, and the ConsoleMethod?

If so, have you added all the needed code to your files, and added the Key Binding?

If so, you might want to see into your console.log for any errors, or if you are compiling, you might also have some warnings or errors. (You might want to post the console.log here.)

Hope that helps,
- Tom.
#13
01/07/2006 (7:08 am)
Is it that the code doesnt work or the binding didnt take?

Search for and delete all config.cs and config.cs.dso files. That will allow the binding to take.

Just a note... Instead of moving the castspell code into the sever command, it is better to fix the original function...

It should be Player::castSpell instead of Armor::castSpell
#14
01/18/2006 (1:18 am)
Dammit, even though I have clicked "Notify when new comments are posted", I don't get the comments sent to me. Everywhere else, this works. >:/

I'll look at this when I get the chance and try and upload a fixed version. Sorry again to everyone struggling with it.
#15
01/22/2006 (7:19 pm)
Gareth:

Great resource. I've added the source files (and mods mentioned above), cleaned and rebuilt all my executables without errors.

I added the .cs scripts added the key bindings (including the quotes missing on Peter's binding) and yet still nothing happens. I'm not getting any errors in the console, everything seems fine, which leads me to believe it's still an issue with the key binding...

Any Ideas?
#16
02/03/2006 (12:41 am)
Have you deleted the config.cs and config.cs.dso files in the client directory? Your new bindings wont work unless you delete those. Dont worry, TGE will regenerate new ones when you run it.
#17
02/03/2006 (11:09 am)
to everyone posting 4 me yes i tried that
#18
03/03/2006 (9:21 am)
If using the examples in this thread try changing this one line in player.cs:

function Armor::castSpell(%this, %obj, %spelldata){

to

function Player::castSpell(%this, %obj, %spelldata){

-Sabrecyd
#19
05/09/2006 (5:19 pm)
Excellent resource, thank alot. I having alot fun with it.
#20
07/25/2006 (6:05 pm)
Has anybody tried getting this working with the RTS starter kit? I seem to be encountering some issues in the castSpell call to player.cs

starter.RTS/server/scripts/avatars/player.cs (410): Register object failed for object (null) of class Spell.
Set::add: Object "0" doesn't exist

...upon further investigation, it appears that for some reason, the line
%spell = new Spell(); is failing. I'm not a C++ guru, but I've made sure that the Spell.cc/.h are in the project and compiling though, so any ideas would be appreciated.
Page «Previous 1 2