Add some Magick to T3D
by Richard Marrevee · 04/19/2010 (1:44 am) · 3 comments
This engine modification allows you to give a player or an AI magick-points, which then can be used for casting spells and other "magick"al things. This resource contains 2 parts: the engine modification and a scripted example on how to use it.
Added functionality:
object.getMagickLevel();
object.setMagickLevel(float value);
object.getMagickRate();
object.setMagickRate(float value);
object.getMagickPercent();
object.isMagick();
guiHealthBarHud: displayMagick(bool);
Note:
The line numbers I mention are approximate. Make sure that you find the corresponding code to be certain you have the right part.
Remarks:
The code works for a single-player game. I don't know if it works in a networked version. I haven't tested it because it is beyond my needs.
Thanks to:
For the magickal push I have used the following resource from Shane 'Tibius' Parker. Thanks Shane.
http://www.torquepowered.com/community/resources/view/17751
Now let's get started.
First open the source code of your project.
Now find the folder called project DLL, where project is the name of your project.
In this folder go to the "source files/engine/T3D" folder.
Open shapeBase.h
Now find this:
Hereafter add this:
Now find
around line 743 and add hereafter
These are the variables the engine will use.
Now declare the various functions.
First find
and add after that the following new functiondeclarations.
That is it for shapeBase.h. Save the file (it is useful to make a backup of the old files before you do so).
Now open shapeBase.cpp
Find the following piece of code:
and add after this:
Now find:
around line 133
and add here:
Below this you will find:
Now it is getting a bit difficult.
Find the following function
and after
add:
This will display the 3 fields in the datablock(editor), so you can change this.
Now find the function:
In this function you find:
around line 583 and add this:
Find this function:
In here find:
and add:
Now goto:
at line 749 and add:
Still here? We are halfway the engine mod.
Find function:
around line 902
In this function find the next lines of code:
And add:
These are just some defaults when a new datablock is created.
Now let us have the engine restoring used magick-points.
Find the function:
In here find this piece of code:
and add:
Now we are ready to add some new functions.
First you have to find this:
After this code block you add the new functions:
Now find this function and change them as mentioned:
Let's do the same for the reading part:
Now make sure we can use the new functionality from the console so add some consolemethods.
Let us find the function:
Hereafter add the following new consolemethods:
Now we are done with shapeBase.cpp
Now open player.cpp
In PlayerData::PlayerData() line 148 find
Now goto
Now the last part.
Goto
That's it for player.cpp
Now you are done with the engine modification. The following additions are not really necessary, but add some icing to the cake.
We are going to modificate the guiHealthBarHud so it is able to display the magick-points.
Open guiHealthBarHud.cpp in the fps folder.
Find the following code and make the changes as mentioned.
Now we have to add the fields so it can be used in the guiEditor.
find the next piece of code and add the line:
find function
And change it like this
These are all the engine changes, so now you can rebuild the engine.
Make sure that when you add this control to your HUD, you don't check both displayEnergy and displayMagick.
Now we are ready to do some scripting to show the new functionality.
In scripts/server create a file magickcommands.cs
and add:
Now create a file also in scripts/server call it push.cs
and add
Now make sure these scripts are executed so open sripts/server/scriptExec.cs
and add the scripts.
Find and add:
Now let's assign a key to the push. I used the m key but feel free to use another. Open scripts/clients/default.bind.cs and add:
The first 10 is the distance from the player, the second is the force applied. Change this to your needs.
Make sure you delete config.cs from the same folder or these changes are lost (if there is a config.cs).
Pfweh.... we are finished.
I hope you like it.
Added functionality:
object.getMagickLevel();
object.setMagickLevel(float value);
object.getMagickRate();
object.setMagickRate(float value);
object.getMagickPercent();
object.isMagick();
guiHealthBarHud: displayMagick(bool);
Note:
The line numbers I mention are approximate. Make sure that you find the corresponding code to be certain you have the right part.
Remarks:
The code works for a single-player game. I don't know if it works in a networked version. I haven't tested it because it is beyond my needs.
Thanks to:
For the magickal push I have used the following resource from Shane 'Tibius' Parker. Thanks Shane.
http://www.torquepowered.com/community/resources/view/17751
Now let's get started.
First open the source code of your project.
Now find the folder called project DLL, where project is the name of your project.
In this folder go to the "source files/engine/T3D" folder.
Open shapeBase.h
Now find this:
struct ShapeBaseData : public GameBaseData {Should be somewhere around line 386 and goto :F32 maxEnergy; F32 maxDamage; F32 repairRate; ///< Rate per tick.somewhere around line 433
Hereafter add this:
F32 maxMagick; ///< Maximum magick level. F32 magickRate; ///< Rate at which used Magick-points are restored per tick. bool hasMagick; ///< has or has no Magick abilities.
Now find
F32 mEnergy; ///< Current enery level. F32 mRechargeRate; ///< Energy recharge rate (in units/tick).
around line 743 and add hereafter
F32 mMagick; ///< Current Magick level. F32 mMagickRate; ///< Magick recharge rate (in units/tick).
These are the variables the engine will use.
Now declare the various functions.
First find
/// Sets the rate at which the energy replentishes itself
/// @param rate Rate at which energy restores
void setRechargeRate(F32 rate) { mRechargeRate = rate; }
/// Returns the amount of energy in the object
F32 getEnergyLevel();
/// Returns the percentage of energy, 0.0 - 1.0
F32 getEnergyValue();
/// Returns the recharge rate
F32 getRechargeRate() { return mRechargeRate; }around line 1120and add after that the following new functiondeclarations.
virtual void setMagickLevel(F32 magick);
void setMagickRate (F32 rate) { mMagickRate = rate; }
/// Returns the amount of Magick in the object
F32 getMagickLevel();
/// Returns the percentage of Magick, 0.0 - 1.0
F32 getMagickValue();
F32 getMagickRate() { return mMagickRate; }
bool isMagick();That is it for shapeBase.h. Save the file (it is useful to make a backup of the old files before you do so).
Now open shapeBase.cpp
Find the following piece of code:
ShapeBaseData::ShapeBaseData()
: shadowEnable( false ),
shadowSize( 128 ),
shadowMaxVisibleDistance( 80.0f ),
shadowProjectionDistance( 10.0f ),
shadowSphereAdjust( 1.0f ),
shapeName( StringTable->insert("") ),
cloakTexName( StringTable->insert("") ),
mass( 1.0f ),
drag( 0.0f ),
density( 1.0f ),
maxEnergy( 0.0f ),
maxDamage( 1.0f ),
disabledLevel( 1.0f ),
destroyedLevel( 1.0f ),
repairRate( 0.0033f ),and add after this:
maxMagick( 0.0f ), magickRate( 0.0 ), hasMagick( false ),
Now find:
struct ShapeBaseDataProto
{
F32 mass;
F32 drag;
F32 density;
F32 maxEnergy;around line 133
and add here:
F32 maxMagick;
Below this you will find:
ShapeBaseDataProto()
{
mass = 1;
drag = 0;
density = 1;
maxEnergy = 0;
maxMagick = 0; ///< add this line for magick
cameraMaxDist = 0;
cameraMinDist = 0.2f;
cameraDefaultFov = 75.f;
cameraMinFov = 5.0f;
cameraMaxFov = 120.f;
}
};and add the mentioned line of code.Now it is getting a bit difficult.
Find the following function
void ShapeBaseData::initPersistFields()around line 376.
and after
endGroup( "Damage/Energy" );
add:
addGroup( "Magick" );
addField( "maxMagick", TypeF32, Offset(maxMagick, ShapeBaseData) );
addField( "magickRate", TypeF32, Offset(magickRate, ShapeBaseData) );
addField( "hasMagick", TypeBool, Offset(hasMagick, ShapeBaseData) );
endGroup( "Magick" );This will display the 3 fields in the datablock(editor), so you can change this.
Now find the function:
void ShapeBaseData::packData(BitStream* stream)Somewhere around line 561
In this function you find:
if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy))
stream->write(maxEnergy);around line 583 and add this:
if(stream->writeFlag(maxMagick != gShapeBaseDataProto.maxMagick))
stream->write(maxMagick);
stream->writeFlag(hasMagick);Find this function:
void ShapeBaseData::unpackData(BitStream* stream)at line 635
In here find:
if(stream->readFlag())
stream->read(&maxEnergy);
else
maxEnergy = gShapeBaseDataProto.maxEnergy;and add:
if(stream->readFlag())
stream->read(&maxMagick);
else
maxMagick = gShapeBaseDataProto.maxMagick;
hasMagick = stream->readFlag();Now goto:
ShapeBase::ShapeBase() : mDrag( 0.0f ), mBuoyancy( 0.0f ), mWaterCoverage( 0.0f ), mLiquidHeight( 0.0f ), mControllingObject( NULL ), mGravityMod( 1.0f ), mAppliedForce( Point3F::Zero ), mTimeoutList( NULL ), mDataBlock( NULL ), mShapeInstance( NULL ), mEnergy( 0.0f ), mRechargeRate( 0.0f ), mDamage( 0.0f ), mRepairRate( 0.0f ), mRepairReserve( 0.0f ), mDamageState( Enabled ), mDamageThread( NULL ),
at line 749 and add:
mMagick( 0.0f ), mMagickRate( 0.0f ),
Still here? We are halfway the engine mod.
Find function:
bool ShapeBase::onNewDataBlock( GameBaseData *dptr )
around line 902
In this function find the next lines of code:
if( isGhost() )
reSkin();
//
mEnergy = 0;
mDamage = 0;And add:
mMagick = 0;
mMagickRate = 0;
if (mDataBlock->hasMagick) {
mMagick = mDataBlock->maxMagick;
mMagickRate = mDataBlock->magickRate;
if (mMagickRate == 0) mMagickRate = 0.001f;
}These are just some defaults when a new datablock is created.
Now let us have the engine restoring used magick-points.
Find the function:
void ShapeBase::processTick(const Move* move)at line 1053
In here find this piece of code:
// Virtual setEnergyLevel is used here by some derived classes to
// decide whether they really want to set the energy mask bit.
if (mEnergy != store)
setEnergyLevel(mEnergy);
}and add:
// Magick management
if (mDataBlock->hasMagick) {
mMagick += mMagickRate;
if (mMagick > mDataBlock->maxMagick)
mMagick = mDataBlock->maxMagick;
else
if (mMagick < 0)
mMagick = 0;
}Now we are ready to add some new functions.
First you have to find this:
void ShapeBase::setDamageLevel(F32 damage)
{
if (!mDataBlock->isInvincible) {
F32 store = mDamage;
mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage);
if (store != mDamage) {
updateDamageLevel();
if (isServerObject()) {
setMaskBits(DamageMask);
char delta[100];
dSprintf(delta,sizeof(delta),"%g",mDamage - store);
Con::executef(mDataBlock, "onDamage",scriptThis(),delta);
}
}
}
}After this code block you add the new functions:
F32 ShapeBase::getMagickLevel()
{
if (mDataBlock->hasMagick)
return mMagick;
return 0.0f;
}
F32 ShapeBase::getMagickValue()
{
if (mDataBlock->hasMagick)
{
F32 maxMagick = mDataBlock->maxMagick;
if ( maxMagick > 0.f )
return (mMagick / mDataBlock->maxMagick);
}
return 0.0f;
}
void ShapeBase::setMagickLevel(F32 magick)
{
if (mDataBlock->hasMagick) {
mMagick = (magick > mDataBlock->maxMagick)?
mDataBlock->maxMagick: (magick < 0)? 0: magick;
}
}
bool ShapeBase::isMagick()
{
return mDataBlock->hasMagick;
}Now find this function and change them as mentioned:
void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream)
{
Parent::writePacketData(connection, stream);
stream->write(getEnergyLevel());
stream->write(mRechargeRate);
stream->write(getMagickLevel()); ///< Add this line
stream->write(mMagickRate); ///< And this line
}Somewhere around line 2832Let's do the same for the reading part:
void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream)
{
Parent::readPacketData(connection, stream);
F32 energy;
F32 magick; ///< Add this line
stream->read(&energy);
setEnergyLevel(energy);
stream->read(&mRechargeRate);
stream->read(&magick); ///< And add this line
setMagickLevel(magick); ///< this line
stream->read(&mMagickRate); ///< and this line
}Now make sure we can use the new functionality from the console so add some consolemethods.
Let us find the function:
ConsoleMethod( ShapeBase, getEnergyPercent, F32, 2, 2, "")
{
return object->getEnergyValue();
}around line 3899Hereafter add the following new consolemethods:
ConsoleMethod( ShapeBase, setMagickLevel, void, 3, 3, "Set the Magick (float level)")
{
object->setMagickLevel(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, getMagickLevel, F32, 2, 2, "Get Magick Level")
{
return object->getMagickLevel();
}
ConsoleMethod( ShapeBase, getMagickPercent, F32, 2, 2, "Get Percentage Magick")
{
return object->getMagickValue();
}
ConsoleMethod( ShapeBase, setMagickRate, void, 3, 3, "Set rate magick restores (float rate)")
{
object->setMagickRate(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, getMagickRate, F32, 2, 2, "Get the Magick Recharge Rate")
{
return object->getMagickRate();
}
ConsoleMethod( ShapeBase, isMagick, bool, 2, 2, "Has this object Magick abilities?")
{
return object->isMagick();
}Now we are done with shapeBase.cpp
Now open player.cpp
In PlayerData::PlayerData() line 148 find
mass = 9.0f; // from ShapeBase maxEnergy = 60.0f; // from ShapeBaseand add
maxMagick = 0.0f;
Now goto
void PlayerData::packData(BitStream* stream)at line 657 and find
stream->write(mass); stream->write(maxEnergy);Add:
stream->write(maxMagick);
Now the last part.
Goto
void PlayerData::unpackData(BitStream* stream)at line 804 and find:
stream->read(&mass); stream->read(&maxEnergy);Add:
stream->read(&maxMagick);
That's it for player.cpp
Now you are done with the engine modification. The following additions are not really necessary, but add some icing to the cake.
We are going to modificate the guiHealthBarHud so it is able to display the magick-points.
Open guiHealthBarHud.cpp in the fps folder.
Find the following code and make the changes as mentioned.
class GuiHealthBarHud : public GuiControl
{
typedef GuiControl Parent;
bool mShowFrame;
bool mShowFill;
bool mDisplayEnergy;
bool mDisplayMagick; ///< Add this line
ColorF mFillColor;
ColorF mFrameColor;
ColorF mDamageFillColor;
S32 mPulseRate;
F32 mPulseThreshold;
F32 mValue;Now we have to add the fields so it can be used in the guiEditor.
find the next piece of code and add the line:
addGroup("Misc");
addField( "showFill", TypeBool, Offset( mShowFill, GuiHealthBarHud ) );
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiHealthBarHud ) );
addField( "displayEnergy", TypeBool, Offset( mDisplayEnergy, GuiHealthBarHud ) );
addField( "displayMagick", TypeBool, Offset( mDisplayMagick, GuiHealthBarHud ) ); ///< Add this new line
endGroup("Misc");find function
void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect)
And change it like this
ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
if (!control || !(control->getType() & PlayerObjectType))
return;
//start changes
if(mDisplayEnergy)
{
mValue = control->getEnergyValue();
}
else if (mDisplayMagick)
{
mValue = control->getMagickValue();
}
else
{
// We'll just grab the damage right off the control object.
// Damage value 0 = no damage.
mValue = 1 - control->getDamageValue();
}
//end changes
// Background firstThese are all the engine changes, so now you can rebuild the engine.
Make sure that when you add this control to your HUD, you don't check both displayEnergy and displayMagick.
Now we are ready to do some scripting to show the new functionality.
In scripts/server create a file magickcommands.cs
and add:
// Magickal Weapons
function serverCmdForcePush(%client,%distance,%force)
{
messageClient(%client,'MsgSpellCast', 'c0You are casting the Telekinetic Push');
%player = %client.player;
if(%player.isMagick()){
%handle = Eye_Distance_HandleRay(%player, %distance, $TypeMasks::PlayerObjectType | $TypeMasks::StaticTSObjectType );
if(%handle)
PushBack(%handle, %player, %force);
else
echo("Nothing to push");
}
else
echo ("No Magick");
}Now create a file also in scripts/server call it push.cs
and add
function Eye_Distance_HandleRay(%player, %distance, %typeMasks)
{
echo("**************Commencing Eye_Distance_HandleRay**************");
echo("--------------------Checking For Objects--------------------");
%magick=%player.getMagickLevel(); ///< The casting of this spell uses 20 magick-points and 10 energy
%energy=%player.getEnergyLevel(); ///< change it to your needs
if(%magick<=20) return false;
if(%energy<=10) return false;
%player.setMagickLevel(%magick-20);
%player.setEnergyLevel(%energy-10);
%eye = %player.getEyeVector();
%vec = vectorScale(%eye, %distance);
%start = %player.getEyeTransform();
%end = VectorAdd(%start, %vec);
echo("Eye Vec: "@ %eye); // Remove this Line When Script is Complete
echo("Vec Scl: "@ %vec); // Remove this Line When Script is Complete
echo("Vec Str: "@ %start); // Remove this Line When Script is Complete
echo("Vec End: "@ %end); // Remove this Line When Script is Complete
%found = ContainerRayCast (%start, %end, %typeMasks, %player);
if(%found)
{
echo("======Raycast has found an Object within the Specified TypeMask.======");//Parse out the Found Object's ID Handle
%handle = getWord(%found, 0);
echo("Object has been found. ID: "@ %handle); // Remove this Line When Script is Complete
return %handle;
}
else
{
echo("======Raycast did not encounter any Object within the Specified TypeMask.======");
}
echo("--------------------Raycast Complete--------------------");
}
// This Function applies the physical force to the object.
// %this = Object ID
// %vec = Vector Force is to Follow
// %force = Ammount of Force Applied
function PushBack(%this, %player, %force)
{
%damage=getRandom(0,100); ///< The push applies a random damage to the target from 0-100
%vec=%player.getEyeVector(); ///< Change it to your specific needs.
if (%this.getType()&$TypeMasks::StaticTSObjectType)
{
echo("Static Object");
}
if (%this.getType()&$TypeMasks::PlayerObjectType)
{
%objectMass = %this.getDatablock().mass;
echo("Object's Mass:"@ %objectMass);
echo("Force Vector:"@ %vec);
echo("Force Applied:"@ %force);
echo("Damage:"@ %damage);
%impulseVector = vectorScale(%vec, %objectMass * %force);
%this.applyImpulse( %this.getWorldBoxCenter(), %impulseVector);
%this.damage(%player,"Body",%damage,"MagickDamage");
}
}This is the modified resource from Shane 'Tibius' Parker.Now make sure these scripts are executed so open sripts/server/scriptExec.cs
and add the scripts.
Find and add:
// Load our gametypes
exec("./gameCore.cs"); // This is the 'core' of the gametype functionality.
exec("./gameDM.cs"); // Overrides GameCore with DeathMatch functionality.
exec("./push.cs"); ///< Add this line for magick.
exec("./magickcommands.cs"); ///< And this line.Now let's assign a key to the push. I used the m key but feel free to use another. Open scripts/clients/default.bind.cs and add:
moveMap.bindCmd(keyboard, "m", "commandToServer('ForcePush',10,10);", "");The first 10 is the distance from the player, the second is the force applied. Change this to your needs.
Make sure you delete config.cs from the same folder or these changes are lost (if there is a config.cs).
Pfweh.... we are finished.
I hope you like it.
About the author
Started programming in 1984 on an Oric, when time progressed switched to MSX, Amiga and finally the Windows PC with T3D. Now developing an epic fantasy game: The Master's Eye. Creator of the DoorClass pack and VolumetricFog pack @ richardsgamestudio.com
#2
Thanks. For certain is it possible to add these things to the player datablock in scripting. The idea behind my approach is:
1 - my game depends heavy on scenes with many high-detail objects, so I need all the performance gains I can get.
2 - I have added this functionality to the shapebase datablock (which I couldn't find in script, I even don't know if it is available in script), so it is also available on al the derived classes like vehicle, staticshape and items and not only to the player, which is a need for my game.
Maybe this explains why I modded the source.
But you're right. you can add it to the player datablock in script.
04/22/2010 (2:35 am)
@Marc:Thanks. For certain is it possible to add these things to the player datablock in scripting. The idea behind my approach is:
1 - my game depends heavy on scenes with many high-detail objects, so I need all the performance gains I can get.
2 - I have added this functionality to the shapebase datablock (which I couldn't find in script, I even don't know if it is available in script), so it is also available on al the derived classes like vehicle, staticshape and items and not only to the player, which is a need for my game.
Maybe this explains why I modded the source.
But you're right. you can add it to the player datablock in script.
#3
btw I am using T3D 1.1b1
09/20/2010 (5:45 am)
Heyo I tried compiling this and it compiled and stuff but when I hit the play button it crashes is there a particular reason for this or what?btw I am using T3D 1.1b1

Torque Owner Marc B
Marc