ServerSide Melee System Update for TGE 1.5.2
by Infinitum3D · in Torque Game Engine · 11/07/2008 (7:02 am) · 20 replies
Oops, looks like I somehow lost my post... Let me try again.
I'm going to try to update the Server Side Melee System resource by Josh Moore (Mar 19, 2004). It looks like the resource was written for TGE1.1 when Realm Wars was being developed. I have the Realm Wars source and according to Jeff Loveless it can be merged into TGE 1.4 using WinMerge.
Since I'm not familiar with WinMerge (although I WILL look into it), I'm going to try to "hand merge" it into a stock TGE 1.5.2.
Wish me luck!
I'm going to need a lot of help, so I'll post all my progress here. Once I get it working I'll upload it as a resource.
OK One disclaimer! I'm not a programmer.
Another disclaimer! It.s not stock TGE1.5.2. I've already added the RPGDialog resource by Gonsalves. I don't think it will interfere though.
Here we go...
Tony
I3D
I'm going to try to update the Server Side Melee System resource by Josh Moore (Mar 19, 2004). It looks like the resource was written for TGE1.1 when Realm Wars was being developed. I have the Realm Wars source and according to Jeff Loveless it can be merged into TGE 1.4 using WinMerge.
Since I'm not familiar with WinMerge (although I WILL look into it), I'm going to try to "hand merge" it into a stock TGE 1.5.2.
Wish me luck!
I'm going to need a lot of help, so I'll post all my progress here. Once I get it working I'll upload it as a resource.
OK One disclaimer! I'm not a programmer.
Another disclaimer! It.s not stock TGE1.5.2. I've already added the RPGDialog resource by Gonsalves. I don't think it will interfere though.
Here we go...
Tony
I3D
#2
Personally, I would use WinMerge. It will save loads of time, and I'm pretty sure it's free.
11/07/2008 (9:08 am)
Tony, I've gotten the server side melee resource integrated into TGE 1.5.2 in the past. I no longer have that build, but let me know if you have any troubles.Personally, I would use WinMerge. It will save loads of time, and I'm pretty sure it's free.
#3
First note! Always close your brackets :) (sheesh)
Seriously though,
I'm getting an error
shapebase.cc(2887) : error C2065: 'ServerIDMask' : undeclared identifier
I thought I was declaring it in shapeBase.h
enum ShapeBaseMasks {
ServerIdMask = Parent::NextFreeMask << 8,
Is it case sensitive?
Any ideas?
Thanks!
Tony
I3D
11/07/2008 (11:29 am)
Thanks Edward and J.P.!First note! Always close your brackets :) (sheesh)
Seriously though,
I'm getting an error
shapebase.cc(2887) : error C2065: 'ServerIDMask' : undeclared identifier
I thought I was declaring it in shapeBase.h
enum ShapeBaseMasks {
ServerIdMask = Parent::NextFreeMask << 8,
Is it case sensitive?
Any ideas?
Thanks!
Tony
I3D
#5
It Works! MUAHAHAHahahahaha (laughing maniacally!!!)
At least it built without errors!
Now to run it through the debugger.
Tony
11/07/2008 (12:09 pm)
Hah! Thank you!!!!!It Works! MUAHAHAHahahahaha (laughing maniacally!!!)
At least it built without errors!
Now to run it through the debugger.
Tony
#6
Make sure you have a sword.dts file and that its located in the shapes/sword/ folder
Remember to add the h1slice.dsq (and the rest) animation files to the shapes/player folder
Remember to load the files by adding the lines
sequence30 = "./player_h1slice.dsq h1slice";
etc...
and delete your .dsq's
everything works. There were only a couple minor changes from Josh's original resource.
Very nicely done, Josh!
I'll upload my experience soon.
Thanks again to everyone!
Tony
11/07/2008 (1:30 pm)
OK, things to remeember;Make sure you have a sword.dts file and that its located in the shapes/sword/ folder
Remember to add the h1slice.dsq (and the rest) animation files to the shapes/player folder
Remember to load the files by adding the lines
sequence30 = "./player_h1slice.dsq h1slice";
etc...
and delete your .dsq's
everything works. There were only a couple minor changes from Josh's original resource.
Very nicely done, Josh!
I'll upload my experience soon.
Thanks again to everyone!
Tony
#7
After all the Audio Profile stuff,
about halfway down weapon.cs, between
//-----------------------------------------------------------------------------
// Weapon Image Class
//-----------------------------------------------------------------------------
and
function WeaponImage::onMount(%this,%obj,%slot)
add this:
// phdana hth ->
// a 'hand to hand attack' is a sequence that gets played
// as a "play once look anim". Hand to Hand weapons, such
// as an axe, can have one or more 'hand to hand attacks'
// that they can play
datablock GameBaseData(OneHandedAttackSwing)
{
seqName = "h1swing";
timeScale = 1.5;
damageAmount = 30;
//startDamage = 0.2;
//endDamage = 0.6;
startDamage = 0.2;
endDamage = 1.3;
};
datablock GameBaseData(OneHandedAttackSlice)
{
seqName = "h1slice";
timeScale = 1.0;
damageAmount = 30;
//startDamage = 0.3;
//endDamage = 0.7;
startDamage = 0.1;
endDamage = 0.9;
};
datablock GameBaseData(OneHandedAttackThrust)
{
seqName = "h1thrust";
timeScale = 1.0;
damageAmount = 30;
//startDamage = 0.4;
//endDamage = 0.8;
startDamage = 0.1;
endDamage = 0.9;
};
//datablock GameBaseData(OneHandedJumpAttack)
//{
//seqName = "h1jumpattack";
//timeScale = 1.0;
//damageAmount = 30;
//startDamage = 0.4;
//endDamage = 0.8;
//startDamage = 0.1;
//endDamage = 0.9;
//};
// this is the default function to call when firing a hand-to-hand weapon
function WeaponImage::onFireHandToHand(%this, %obj, %slot)
{
if(%obj.hthStun) //|| %obj.shielded)
return;
// there was code here for special attacks
%action = "Normal";
switch$(%action)
{
//case "JumpAttack":
//%attack = %this.jumpAttack;
case "Normal":
// for now we randomly choose an attack
%index = mFloor(getRandom()*(%this.hthNumAttacks-0.0001));
if (%index > (%this.hthNumAttacks-1))
%index = (%this.hthNumAttacks-1);
%attack = %this.hthAttack[%index];
}
// setup the "play once look anim"
%obj.hthDamageAttack = %attack;
%obj.hthDamageSeqPlaying = 1;
%obj.hthDamageStartMS = $sim::Time;
%obj.hthDamageLastId = -1;
if (!%obj.setArmThreadPlayOnce(%attack.seqName))
echo("ERROR in setArmThreadPlayOnce()");
return;
}
// default weapon intersect
function WeaponImage::onImageIntersect(%this,%player,%slot,%startvec,%endvec)
{
// if damage sequence is not playing then dont do damage
if (!%player.hthDamageSeqPlaying || %player.getState() $= "Dead")
return;
// determine if damage is active or if we can say the seq is done playing
// based on current server time
%offset = $sim::Time - %player.hthDamageStartMS;
// depending on which attack is playing...
%attack = %player.hthDamageAttack;
%startOffset = %attack.startDamage;
%endOffset = %attack.endDamage;
// how long until the last damage is done
// at which point we can say the seq has "Stopped playing"
if (%offset > %endOffset)
{
%player.hthDamageSeqPlaying = 0;
%player.hthDamageActive = 0;
// echo("seq stopping (all damage done) %offset = " @ %offset);
return;
}
// how long it takes for damage to start...for now we just
// have one interval and damage is active all during that interval
if (%offset >%startOffset)
%player.hthDamageActive = 1;
// no damage yet?
if (!%player.hthDamageActive)
{
// echo("seq playing (no damage) %offset = " @ %offset);
return;
}
// search for just players to damage
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType;
// search for objects within the damage rays that fit the masks above
%scanTarg = ContainerRayCast(%startvec, %endvec, %searchMasks, %slot);
if(%scanTarg && (%scanTarg.getType() & $TypeMasks::PlayerObjectType))
{
// a target in range was found
%target = firstWord(%scanTarg);
// store end point from raycast return buffer
%pos = getWords(%scanTarg, 1, 3);
// if we have hit this person already...apply no more damage
if (%target == %player.hthDamageLastId)
return;
// save who we last damaged
%player.hthDamageLastId = %target;
// Apply damage targetted object
// Works for all shapebase objects.
if (%target.getState() !$= "Dead" && %target.getId() !$= %player.getId())
{
%damage = %attack.damageAmount;
%damageType = %this.item; // example: Axe / Sword etc
%damLoc = firstWord(%target.getDamageLocation(%pos));
// you can use this to add limited loacational damage, but the head is whats hit the most - TF
//if(%damLoc $= "head")
//error("object sliced on head");
//else if(%damLoc $= "torso")
//error("object sliced on torso");
//else if(%damLoc $= "legs")
//error("object sliced on legs");
// code ripped from Armor::damgae
%target.applyDamage(%damage);
// this is in the Armor::damage
//%location = "Body";
// Deal with client callbacks here because we don't have this
// information in the onDamage or onDisable methods
%client = %target.client;
%sourceObject = %this;
%sourceClient = %sourceObject ? %sourceObject.client : 0;
if (%target.getState() $= "Dead")
{
if(%client)
%client.onDeath(%sourceObject, %sourceClient, %damageType, %pos);
}
// phdana stun ->
else
{
stunPlayer(%target,%attack);
pushPlayerBack(%target,%pos,%player,%attack);
}
// <- phdana stun
}
}
else if(%scanTarg && (%scanTarg.getType() & $TypeMasks::StaticShapeObjectType))
{
%damage = %attack.damageAmount;
%object = firstWord(%scanTarg);
%object.applyDamage(%damage);
}
}
11/07/2008 (1:40 pm)
First step: Modify weapon.csAfter all the Audio Profile stuff,
about halfway down weapon.cs, between
//-----------------------------------------------------------------------------
// Weapon Image Class
//-----------------------------------------------------------------------------
and
function WeaponImage::onMount(%this,%obj,%slot)
add this:
// phdana hth ->
// a 'hand to hand attack' is a sequence that gets played
// as a "play once look anim". Hand to Hand weapons, such
// as an axe, can have one or more 'hand to hand attacks'
// that they can play
datablock GameBaseData(OneHandedAttackSwing)
{
seqName = "h1swing";
timeScale = 1.5;
damageAmount = 30;
//startDamage = 0.2;
//endDamage = 0.6;
startDamage = 0.2;
endDamage = 1.3;
};
datablock GameBaseData(OneHandedAttackSlice)
{
seqName = "h1slice";
timeScale = 1.0;
damageAmount = 30;
//startDamage = 0.3;
//endDamage = 0.7;
startDamage = 0.1;
endDamage = 0.9;
};
datablock GameBaseData(OneHandedAttackThrust)
{
seqName = "h1thrust";
timeScale = 1.0;
damageAmount = 30;
//startDamage = 0.4;
//endDamage = 0.8;
startDamage = 0.1;
endDamage = 0.9;
};
//datablock GameBaseData(OneHandedJumpAttack)
//{
//seqName = "h1jumpattack";
//timeScale = 1.0;
//damageAmount = 30;
//startDamage = 0.4;
//endDamage = 0.8;
//startDamage = 0.1;
//endDamage = 0.9;
//};
// this is the default function to call when firing a hand-to-hand weapon
function WeaponImage::onFireHandToHand(%this, %obj, %slot)
{
if(%obj.hthStun) //|| %obj.shielded)
return;
// there was code here for special attacks
%action = "Normal";
switch$(%action)
{
//case "JumpAttack":
//%attack = %this.jumpAttack;
case "Normal":
// for now we randomly choose an attack
%index = mFloor(getRandom()*(%this.hthNumAttacks-0.0001));
if (%index > (%this.hthNumAttacks-1))
%index = (%this.hthNumAttacks-1);
%attack = %this.hthAttack[%index];
}
// setup the "play once look anim"
%obj.hthDamageAttack = %attack;
%obj.hthDamageSeqPlaying = 1;
%obj.hthDamageStartMS = $sim::Time;
%obj.hthDamageLastId = -1;
if (!%obj.setArmThreadPlayOnce(%attack.seqName))
echo("ERROR in setArmThreadPlayOnce()");
return;
}
// default weapon intersect
function WeaponImage::onImageIntersect(%this,%player,%slot,%startvec,%endvec)
{
// if damage sequence is not playing then dont do damage
if (!%player.hthDamageSeqPlaying || %player.getState() $= "Dead")
return;
// determine if damage is active or if we can say the seq is done playing
// based on current server time
%offset = $sim::Time - %player.hthDamageStartMS;
// depending on which attack is playing...
%attack = %player.hthDamageAttack;
%startOffset = %attack.startDamage;
%endOffset = %attack.endDamage;
// how long until the last damage is done
// at which point we can say the seq has "Stopped playing"
if (%offset > %endOffset)
{
%player.hthDamageSeqPlaying = 0;
%player.hthDamageActive = 0;
// echo("seq stopping (all damage done) %offset = " @ %offset);
return;
}
// how long it takes for damage to start...for now we just
// have one interval and damage is active all during that interval
if (%offset >%startOffset)
%player.hthDamageActive = 1;
// no damage yet?
if (!%player.hthDamageActive)
{
// echo("seq playing (no damage) %offset = " @ %offset);
return;
}
// search for just players to damage
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType;
// search for objects within the damage rays that fit the masks above
%scanTarg = ContainerRayCast(%startvec, %endvec, %searchMasks, %slot);
if(%scanTarg && (%scanTarg.getType() & $TypeMasks::PlayerObjectType))
{
// a target in range was found
%target = firstWord(%scanTarg);
// store end point from raycast return buffer
%pos = getWords(%scanTarg, 1, 3);
// if we have hit this person already...apply no more damage
if (%target == %player.hthDamageLastId)
return;
// save who we last damaged
%player.hthDamageLastId = %target;
// Apply damage targetted object
// Works for all shapebase objects.
if (%target.getState() !$= "Dead" && %target.getId() !$= %player.getId())
{
%damage = %attack.damageAmount;
%damageType = %this.item; // example: Axe / Sword etc
%damLoc = firstWord(%target.getDamageLocation(%pos));
// you can use this to add limited loacational damage, but the head is whats hit the most - TF
//if(%damLoc $= "head")
//error("object sliced on head");
//else if(%damLoc $= "torso")
//error("object sliced on torso");
//else if(%damLoc $= "legs")
//error("object sliced on legs");
// code ripped from Armor::damgae
%target.applyDamage(%damage);
// this is in the Armor::damage
//%location = "Body";
// Deal with client callbacks here because we don't have this
// information in the onDamage or onDisable methods
%client = %target.client;
%sourceObject = %this;
%sourceClient = %sourceObject ? %sourceObject.client : 0;
if (%target.getState() $= "Dead")
{
if(%client)
%client.onDeath(%sourceObject, %sourceClient, %damageType, %pos);
}
// phdana stun ->
else
{
stunPlayer(%target,%attack);
pushPlayerBack(%target,%pos,%player,%attack);
}
// <- phdana stun
}
}
else if(%scanTarg && (%scanTarg.getType() & $TypeMasks::StaticShapeObjectType))
{
%damage = %attack.damageAmount;
%object = firstWord(%scanTarg);
%object.applyDamage(%damage);
}
}
#8
// call when %victim is hit with %attack but does not die
function stunPlayer(%vplayer, %attack)
{
// for now we stun every time...
// get the player for this object
//if (!%victim.client || !%victim.client.player)
if(!%vplayer.getType() & $TypeMasks::PlayerObjectType)
{
error("ATTEMPTING to STUN a non-player");
return;
}
// if this player is in the middle of a hth swing themself, then
// their swing is aborted. firstly we have to make sure they dont
// do any damage, secondly we must blend their swing anim into
// the stun anim
if (%vplayer.hthDamageSeqPlaying)
{
// make sure they wont do damage....
%vplayer.hthDamageSeqPlaying = false;
// blend into the stun animation
//error("STUN: victim: " @ %vplayer @ " DOING transition once...");
%vplayer.setArmThreadTransitionOnce("h1stun");
}
else
{
// just start the stun animation
//error("STUN: victim: " @ %vplayer @ " only doing a playonce...");
if(%vplayer.shielded)// not while stuned!
%vplayer.setImageTrigger(1,false);
%vplayer.setArmThreadPlayOnce("h1stun");
}
// the victim is now in a stun state
//%vplayer.hthStunSequencePlaying = true;
%vplayer.hthStun = true;
schedule(1000, %vplayer, "resetStun", %vplayer);
//%vplayer.hthStunStartMS = $sim::Time;
}
function resetStun(%obj)
{
%obj.hthStun = false;
}
function pushPlayerBack(%victim, %pos, %attacker, %attack)
{
// the push back is relative to the attacker
// a straight push back would be along the attackers
// Y axis....
// right now we always push the victim at his center
// we could explore what happnes if we push at the
// point of contact instead (might turn or do something intersting)
// get the usual direction to push...we could get the Y axis of
// the attacker with getTransform() then grabbing the rotation part
// and passing that to VectorOrthoBasis() and then using column 1
// whichi would be words 3,4,5 (couting from 0)...but that's overkill
// for something that can be approximated pretty good by a line drawn
// from attacker to victim...so let's use that instead
%vpos = %victim.getWorldBoxCenter();
%pushDirection = VectorSub(%vpos,%attacker.getWorldBoxCenter());
%pushDirection = VectorNormalize(%pushDirection);
// hardoded impluse
%impulse = 15.0;
// ok apply impulse to victim's center
%mass = %victim.getDataBlock().mass;
%pushVec = VectorScale(%pushDirection,%impulse * %mass);
//error("Applying, to player " @ %victim @ " of mass " @ %mass @ ", an impulseVec: " @ %pushVec);
%victim.applyImpulse(%vpos, %pushVec);
}
// <- phdana hth
Next, (still in weapon.cs) look for function WeaponImage::onMount(%this,%obj,%slot)
after %obj.setImageAmmo(%slot,true); add this
//---
if (%this.customLookAnim !$= "")
{
%obj.setArmThread(%this.customLookAnim);
}
else
{
%obj.setArmThread("look");
}
//---
It should now look like this
function WeaponImage::onMount(%this,%obj,%slot)
{
// Images assume a false ammo state on load. We need to
// set the state according to the current inventory.
if (%obj.getInventory(%this.ammo))
%obj.setImageAmmo(%slot,true);
//---
if (%this.customLookAnim !$= "")
{
%obj.setArmThread(%this.customLookAnim);
}
else
{
%obj.setArmThread("look");
}
}
//---
}
save weapon.cs
11/07/2008 (1:40 pm)
// phdana stun ->// call when %victim is hit with %attack but does not die
function stunPlayer(%vplayer, %attack)
{
// for now we stun every time...
// get the player for this object
//if (!%victim.client || !%victim.client.player)
if(!%vplayer.getType() & $TypeMasks::PlayerObjectType)
{
error("ATTEMPTING to STUN a non-player");
return;
}
// if this player is in the middle of a hth swing themself, then
// their swing is aborted. firstly we have to make sure they dont
// do any damage, secondly we must blend their swing anim into
// the stun anim
if (%vplayer.hthDamageSeqPlaying)
{
// make sure they wont do damage....
%vplayer.hthDamageSeqPlaying = false;
// blend into the stun animation
//error("STUN: victim: " @ %vplayer @ " DOING transition once...");
%vplayer.setArmThreadTransitionOnce("h1stun");
}
else
{
// just start the stun animation
//error("STUN: victim: " @ %vplayer @ " only doing a playonce...");
if(%vplayer.shielded)// not while stuned!
%vplayer.setImageTrigger(1,false);
%vplayer.setArmThreadPlayOnce("h1stun");
}
// the victim is now in a stun state
//%vplayer.hthStunSequencePlaying = true;
%vplayer.hthStun = true;
schedule(1000, %vplayer, "resetStun", %vplayer);
//%vplayer.hthStunStartMS = $sim::Time;
}
function resetStun(%obj)
{
%obj.hthStun = false;
}
function pushPlayerBack(%victim, %pos, %attacker, %attack)
{
// the push back is relative to the attacker
// a straight push back would be along the attackers
// Y axis....
// right now we always push the victim at his center
// we could explore what happnes if we push at the
// point of contact instead (might turn or do something intersting)
// get the usual direction to push...we could get the Y axis of
// the attacker with getTransform() then grabbing the rotation part
// and passing that to VectorOrthoBasis() and then using column 1
// whichi would be words 3,4,5 (couting from 0)...but that's overkill
// for something that can be approximated pretty good by a line drawn
// from attacker to victim...so let's use that instead
%vpos = %victim.getWorldBoxCenter();
%pushDirection = VectorSub(%vpos,%attacker.getWorldBoxCenter());
%pushDirection = VectorNormalize(%pushDirection);
// hardoded impluse
%impulse = 15.0;
// ok apply impulse to victim's center
%mass = %victim.getDataBlock().mass;
%pushVec = VectorScale(%pushDirection,%impulse * %mass);
//error("Applying, to player " @ %victim @ " of mass " @ %mass @ ", an impulseVec: " @ %pushVec);
%victim.applyImpulse(%vpos, %pushVec);
}
// <- phdana hth
Next, (still in weapon.cs) look for function WeaponImage::onMount(%this,%obj,%slot)
after %obj.setImageAmmo(%slot,true); add this
//---
if (%this.customLookAnim !$= "")
{
%obj.setArmThread(%this.customLookAnim);
}
else
{
%obj.setArmThread("look");
}
//---
It should now look like this
function WeaponImage::onMount(%this,%obj,%slot)
{
// Images assume a false ammo state on load. We need to
// set the state according to the current inventory.
if (%obj.getInventory(%this.ammo))
%obj.setImageAmmo(%slot,true);
//---
if (%this.customLookAnim !$= "")
{
%obj.setArmThread(%this.customLookAnim);
}
else
{
%obj.setArmThread("look");
}
}
//---
}
save weapon.cs
#9
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
// Weapon Item. This is the item that exists in the world, i.e. when it's
// been dropped, thrown or is acting as re-spawnable item. When the weapon
// is mounted onto a shape, the CrossbowImage is used.
datablock ItemData(Sword)
{
// Mission editor category
category = "Weapon";
// Hook into Item Weapon class hierarchy. The weapon namespace
// provides common weapon handling functions in addition to hooks
// into the inventory system.
className = "Weapon";
// Basic Item properties
shapeFile = "~/data/shapes/sword/rune_blade01.dts";
mass = 1;
elasticity = 0.2;
friction = 0.6;
emap = true;
// Dynamic properties defined by the scripts
pickUpName = "a sword";
image = SwordImage;
itemType="melee";
trayIcon = "sword01.png";
};
//--------------------------------------------------------------------------
// Crossbow image which does all the work. Images do not normally exist in
// the world, they can only be mounted on ShapeBase objects.
// phdana hth ->
datablock ShapeBaseImageData(SwordImage)
{
// Basic Item properties
shapeFile = "~/data/shapes/sword/rune_blade01.dts";
emap = true;
// Specify mount point & offset for 3rd person, and eye offset
// for first person rendering.
mountPoint = 0;
eyeOffset = "0.1 0.4 -0.6";
// When firing from a point offset from the eye, muzzle correction
// will adjust the muzzle vector to point to the eye LOS point.
// Since this weapon doesn't actually fire from the muzzle point,
// we need to turn this off.
correctMuzzleVector = false;
// Add the WeaponImage namespace as a parent, WeaponImage namespace
// provides some hooks into the inventory system.
className = "WeaponImage";
// Projectile && Ammo.
item = Sword;
ammo = CrossbowAmmo;
projectile = CrossbowProjectile;
projectileType = Projectile;
// we are a HAND TO HAND weapon so we have a custom look anim
//customLookAnim = "h1root"; // as a test
customLookAnim = "looknw";
// Here are the Attacks we support
hthNumAttacks = 3;
hthAttack[0] = OneHandedAttackSwing;
hthAttack[1] = OneHandedAttackSlice;
hthAttack[2] = OneHandedAttackThrust;
//jumpAttack = OneHandedJumpAttack;
// Images have a state system which controls how the animations
// are run, which sounds are played, script callbacks, etc. This
// state system is downloaded to the client so that clients can
// predict state changes and animate accordingly. The following
// system supports basic ready->fire->reload transitions as
// well as a no-ammo->dryfire idle state. In this case we are a
// HAND to HAND weapon and there is no ammo but we can use the
// reload time to limit how often the weapon can be fired
// Initial start up state
stateName[0] = "Preactivate";
stateTransitionOnLoaded[0] = "Activate";
// Activating the gun. Called when the weapon is first mounted
stateName[1] = "Activate";
stateTransitionOnTimeout[1] = "Ready";
stateTimeoutValue[1] = 0.6;
//stateSequence[1] = "Activate";
// Ready to fire, just waiting for the trigger
stateName[2] = "Ready";
stateTransitionOnTriggerDown[2] = "Fire";
// Fire the weapon. Calls the fire script which does the actual work.
stateName[3] = "Fire";
stateTransitionOnTimeout[3] = "Reload";
stateTimeoutValue[3] = 0.2;
stateFire[3] = true;
stateAllowImageChange[3] = false;
//stateSequence[3] = "Fire";
stateScript[3] = "onFire";
//stateSound[3] = CrossbowFireSound;
// Play the relead animation, and transition into
stateName[4] = "Reload";
stateTransitionOnTimeout[4] = "Ready";
stateTimeoutValue[4] = 0.8;
stateAllowImageChange[4] = false;
//stateSequence[4] = "Reload";
stateEjectShell[4] = false;
//stateSound[4] = CrossbowReloadSound;
};
11/07/2008 (1:41 pm)
STEP 2 - direct from Josh Moore's tutorial - make a sword.cs file with this code//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
// Weapon Item. This is the item that exists in the world, i.e. when it's
// been dropped, thrown or is acting as re-spawnable item. When the weapon
// is mounted onto a shape, the CrossbowImage is used.
datablock ItemData(Sword)
{
// Mission editor category
category = "Weapon";
// Hook into Item Weapon class hierarchy. The weapon namespace
// provides common weapon handling functions in addition to hooks
// into the inventory system.
className = "Weapon";
// Basic Item properties
shapeFile = "~/data/shapes/sword/rune_blade01.dts";
mass = 1;
elasticity = 0.2;
friction = 0.6;
emap = true;
// Dynamic properties defined by the scripts
pickUpName = "a sword";
image = SwordImage;
itemType="melee";
trayIcon = "sword01.png";
};
//--------------------------------------------------------------------------
// Crossbow image which does all the work. Images do not normally exist in
// the world, they can only be mounted on ShapeBase objects.
// phdana hth ->
datablock ShapeBaseImageData(SwordImage)
{
// Basic Item properties
shapeFile = "~/data/shapes/sword/rune_blade01.dts";
emap = true;
// Specify mount point & offset for 3rd person, and eye offset
// for first person rendering.
mountPoint = 0;
eyeOffset = "0.1 0.4 -0.6";
// When firing from a point offset from the eye, muzzle correction
// will adjust the muzzle vector to point to the eye LOS point.
// Since this weapon doesn't actually fire from the muzzle point,
// we need to turn this off.
correctMuzzleVector = false;
// Add the WeaponImage namespace as a parent, WeaponImage namespace
// provides some hooks into the inventory system.
className = "WeaponImage";
// Projectile && Ammo.
item = Sword;
ammo = CrossbowAmmo;
projectile = CrossbowProjectile;
projectileType = Projectile;
// we are a HAND TO HAND weapon so we have a custom look anim
//customLookAnim = "h1root"; // as a test
customLookAnim = "looknw";
// Here are the Attacks we support
hthNumAttacks = 3;
hthAttack[0] = OneHandedAttackSwing;
hthAttack[1] = OneHandedAttackSlice;
hthAttack[2] = OneHandedAttackThrust;
//jumpAttack = OneHandedJumpAttack;
// Images have a state system which controls how the animations
// are run, which sounds are played, script callbacks, etc. This
// state system is downloaded to the client so that clients can
// predict state changes and animate accordingly. The following
// system supports basic ready->fire->reload transitions as
// well as a no-ammo->dryfire idle state. In this case we are a
// HAND to HAND weapon and there is no ammo but we can use the
// reload time to limit how often the weapon can be fired
// Initial start up state
stateName[0] = "Preactivate";
stateTransitionOnLoaded[0] = "Activate";
// Activating the gun. Called when the weapon is first mounted
stateName[1] = "Activate";
stateTransitionOnTimeout[1] = "Ready";
stateTimeoutValue[1] = 0.6;
//stateSequence[1] = "Activate";
// Ready to fire, just waiting for the trigger
stateName[2] = "Ready";
stateTransitionOnTriggerDown[2] = "Fire";
// Fire the weapon. Calls the fire script which does the actual work.
stateName[3] = "Fire";
stateTransitionOnTimeout[3] = "Reload";
stateTimeoutValue[3] = 0.2;
stateFire[3] = true;
stateAllowImageChange[3] = false;
//stateSequence[3] = "Fire";
stateScript[3] = "onFire";
//stateSound[3] = CrossbowFireSound;
// Play the relead animation, and transition into
stateName[4] = "Reload";
stateTransitionOnTimeout[4] = "Ready";
stateTimeoutValue[4] = 0.8;
stateAllowImageChange[4] = false;
//stateSequence[4] = "Reload";
stateEjectShell[4] = false;
//stateSound[4] = CrossbowReloadSound;
};
#10
function SwordImage::onFire(%this, %obj, %slot)
{
// default hand to hand weapon code
WeaponImage::onFireHandToHand(%this, %obj, %slot);
return;
}
// <- phdana hth
//---
//---
Save sword.cs and remember to exec it. Open server/scripts/game.cs, and under
// Load up all datablocks, objects etc. This function is called when
// a server is constructed.
look for exec("./crossbow.cs");
paste this just under it exec("./sword.cs");
11/07/2008 (1:42 pm)
//-----------------------------------------------------------------------------function SwordImage::onFire(%this, %obj, %slot)
{
// default hand to hand weapon code
WeaponImage::onFireHandToHand(%this, %obj, %slot);
return;
}
// <- phdana hth
//---
//---
Save sword.cs and remember to exec it. Open server/scripts/game.cs, and under
// Load up all datablocks, objects etc. This function is called when
// a server is constructed.
look for exec("./crossbow.cs");
paste this just under it exec("./sword.cs");
#11
Around line 751 is Player::Player()
at the END of this, just after mMountPending = 0; add
mArmThreadPlayOnce = false;
Make sure you put it BEFORE the closing bracket }
Next find Player::updateLookAnimation() around line 1777
Replace this:
if (mArmAnimation.thread) {
// TG: Adjust arm position to avoid collision.
F32 tp = mControlObject? 0.5f :
(mHead.x - mArmRange.min) / mArmRange.delta;
mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp, 0.0f, 1.0f));
}
With this:
if (!mArmThreadPlayOnce)
{
// if we are doing play once anim then we dont adjust arm thread based on head tilt...
if (mArmAnimation.thread) {
// TG: Adjust arm position to avoid collision.
F32 tp = mControlObject? 0.5:
(mHead.x - mArmRange.min) / mArmRange.delta;
mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp,0,1));
}
MAKE SURE YOU GET YOUR CLOSING BRACKETS RIGHT!!!
Next, AFTER the closing bracket at the end of Player::SetActionThread (around line 2063)
but BEFORE void Player::updateActionThread()
add this:
// phdana hth play once arm thread->
void Player::startPlayOnce(F32 timeScale)
{
mShapeInstance->setTimeScale(mArmAnimation.thread,timeScale);
}
void Player::stopPlayOnce()
{
mShapeInstance->setTimeScale(mArmAnimation.thread,0.0);
// only do this if on server
if (!isGhost())
setArmThread(mArmThreadSavedAction);
}
// called on server
bool Player::setArmThreadPlayOnce(const char* sequence)
{
// if we are already playing arm thread...ignore
if (mArmThreadPlayOnce)
return true;
// save old sequence
mArmThreadSavedAction = mArmAnimation.action;
if (!setArmThread(sequence))
return false;
// flag that we are playing once!
mArmThreadPlayOnce = true;
// melee
//for (int i = 0; i < MaxMountedImages; i++)
//if (mMountedImageList[i].dataBlock)
//UpdateImageRaycastDamage(i, TickSec);
// melee
startPlayOnce(1.0);
return true;
}
// phdana stun->
// called on server
bool Player::setArmThreadTransitionOnce(const char* sequence)
{
// generally this will only be called when we are ALREADY
// playing a play once arm thread...but in case we
// are not..then just play normally
if (!mArmThreadPlayOnce)
return setArmThreadPlayOnce(sequence);
S32 seq = mDataBlock->shape->findSequence("h1stun");
F32 time = 0.25;
// otherwise lets transition to the new sequence
F32 pos = mShapeInstance->getPos(mArmAnimation.thread);
mShapeInstance->transitionToSequence(mArmAnimation.thread, seq, pos, time, true);
return true;
}
// <- phdana stun
// advance time on a play once arm thread...if we have
// reached the end of time on this animation then
// clear the play once status and restore old thread
// NOTE: only call this method when mArmThreadPlayOnce is true
void Player::advancePlayOnceTime(F32 dt)
{
// advance time
mShapeInstance->advanceTime(dt,mArmAnimation.thread);
// if we reached end...
if (mShapeInstance->getPos(mArmAnimation.thread) == 1.0)
{
// phdana hth twitch fix ->
if (!isGhost())
{
stopPlayOnce();
mArmThreadPlayOnce = false;
}
// <- phdana hth twitch fix
}
}
// <- phdana hth play once arm thread
NEXT in void Player::updateAnimation(F32 dt)
AFTER THIS
if ((isGhost() || mActionAnimation.animateOnServer) && mActionAnimation.thread)
mShapeInstance->advanceTime(dt,mActionAnimation.thread);
if (mRecoilThread)
mShapeInstance->advanceTime(dt,mRecoilThread);
BUT BEFORE THIS
// If we are the client's player on this machine, then we need
// to make sure the transforms are up to date as they are used
// to setup the camera.
if (isGhost())
{
ADD THIS
// phdana hth play once arm thread ->
if (mArmThreadPlayOnce)
{
advancePlayOnceTime(dt);
// if we are doing a "play once" for the purpose of hand-to-hand
// then we must also ensure that mounted images get updated correctly
// when doing this on the server
if (!isGhost())
mShapeInstance->animate();
}
The accuracy of the next part is crucial!
NEXT in U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream) (around line 3634)
AFTER
stream->writeInt(mArmAnimation.action,PlayerData::ActionAnimBits);
BUT BEFORE THE CLOSING BRACKET
add this:
// phdana hth play once arm thread
stream->writeFlag(mArmThreadPlayOnce);
// <- phdana hth play once arm thread->
NOTE: LOOK FOR mArmAnimation.action, NOT mActionAnimation.action
11/07/2008 (1:42 pm)
STEP 3 Open Player.ccAround line 751 is Player::Player()
at the END of this, just after mMountPending = 0; add
mArmThreadPlayOnce = false;
Make sure you put it BEFORE the closing bracket }
Next find Player::updateLookAnimation() around line 1777
Replace this:
if (mArmAnimation.thread) {
// TG: Adjust arm position to avoid collision.
F32 tp = mControlObject? 0.5f :
(mHead.x - mArmRange.min) / mArmRange.delta;
mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp, 0.0f, 1.0f));
}
With this:
if (!mArmThreadPlayOnce)
{
// if we are doing play once anim then we dont adjust arm thread based on head tilt...
if (mArmAnimation.thread) {
// TG: Adjust arm position to avoid collision.
F32 tp = mControlObject? 0.5:
(mHead.x - mArmRange.min) / mArmRange.delta;
mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp,0,1));
}
MAKE SURE YOU GET YOUR CLOSING BRACKETS RIGHT!!!
Next, AFTER the closing bracket at the end of Player::SetActionThread (around line 2063)
but BEFORE void Player::updateActionThread()
add this:
// phdana hth play once arm thread->
void Player::startPlayOnce(F32 timeScale)
{
mShapeInstance->setTimeScale(mArmAnimation.thread,timeScale);
}
void Player::stopPlayOnce()
{
mShapeInstance->setTimeScale(mArmAnimation.thread,0.0);
// only do this if on server
if (!isGhost())
setArmThread(mArmThreadSavedAction);
}
// called on server
bool Player::setArmThreadPlayOnce(const char* sequence)
{
// if we are already playing arm thread...ignore
if (mArmThreadPlayOnce)
return true;
// save old sequence
mArmThreadSavedAction = mArmAnimation.action;
if (!setArmThread(sequence))
return false;
// flag that we are playing once!
mArmThreadPlayOnce = true;
// melee
//for (int i = 0; i < MaxMountedImages; i++)
//if (mMountedImageList[i].dataBlock)
//UpdateImageRaycastDamage(i, TickSec);
// melee
startPlayOnce(1.0);
return true;
}
// phdana stun->
// called on server
bool Player::setArmThreadTransitionOnce(const char* sequence)
{
// generally this will only be called when we are ALREADY
// playing a play once arm thread...but in case we
// are not..then just play normally
if (!mArmThreadPlayOnce)
return setArmThreadPlayOnce(sequence);
S32 seq = mDataBlock->shape->findSequence("h1stun");
F32 time = 0.25;
// otherwise lets transition to the new sequence
F32 pos = mShapeInstance->getPos(mArmAnimation.thread);
mShapeInstance->transitionToSequence(mArmAnimation.thread, seq, pos, time, true);
return true;
}
// <- phdana stun
// advance time on a play once arm thread...if we have
// reached the end of time on this animation then
// clear the play once status and restore old thread
// NOTE: only call this method when mArmThreadPlayOnce is true
void Player::advancePlayOnceTime(F32 dt)
{
// advance time
mShapeInstance->advanceTime(dt,mArmAnimation.thread);
// if we reached end...
if (mShapeInstance->getPos(mArmAnimation.thread) == 1.0)
{
// phdana hth twitch fix ->
if (!isGhost())
{
stopPlayOnce();
mArmThreadPlayOnce = false;
}
// <- phdana hth twitch fix
}
}
// <- phdana hth play once arm thread
NEXT in void Player::updateAnimation(F32 dt)
AFTER THIS
if ((isGhost() || mActionAnimation.animateOnServer) && mActionAnimation.thread)
mShapeInstance->advanceTime(dt,mActionAnimation.thread);
if (mRecoilThread)
mShapeInstance->advanceTime(dt,mRecoilThread);
BUT BEFORE THIS
// If we are the client's player on this machine, then we need
// to make sure the transforms are up to date as they are used
// to setup the camera.
if (isGhost())
{
ADD THIS
// phdana hth play once arm thread ->
if (mArmThreadPlayOnce)
{
advancePlayOnceTime(dt);
// if we are doing a "play once" for the purpose of hand-to-hand
// then we must also ensure that mounted images get updated correctly
// when doing this on the server
if (!isGhost())
mShapeInstance->animate();
}
The accuracy of the next part is crucial!
NEXT in U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream) (around line 3634)
AFTER
stream->writeInt(mArmAnimation.action,PlayerData::ActionAnimBits);
BUT BEFORE THE CLOSING BRACKET
add this:
// phdana hth play once arm thread
stream->writeFlag(mArmThreadPlayOnce);
// <- phdana hth play once arm thread->
NOTE: LOOK FOR mArmAnimation.action, NOT mActionAnimation.action
#12
In player::unpackupdate
AFTER
else
mArmAnimation.action = action;
But BEFORE the closing bracket
add this:
//-------
// phdana hth play once arm thread
mArmThreadPlayOnce = stream->readFlag();
if (mArmThreadPlayOnce)
startPlayOnce(1.0);
// phdana hth twitch fix ->
else
stopPlayOnce();
// <- phdana hth twitch fix
// <- phdana hth play once arm thread->
//-------
LAST STEP IN PLAYER.CC
(around line 3847) AFTER the whole UnPackUpdate section, but
BEFORE THIS
ConsoleMethod( Player, getState, const char*, 2, 2, "Return the current state name.")
{
return object->getStateName();
}
ADD THESE TWO CONSOLE METHODS
// phdana hth play once arm thread ->
ConsoleMethod( Player, setArmThreadPlayOnce, bool, 3, 3, "(string sequenceName)")
{
return object->setArmThreadPlayOnce(argv[2]);
}
ConsoleMethod( Player, SetArmThreadTransitionOnce, bool, 3, 3, "(string sequenceName)")
{
return object->setArmThreadTransitionOnce(argv[2]);
}
// phdana hth <<----
//----
SAVE PLAYER.CC
//----
OK, STILL A LONG WAY TO GO...
11/07/2008 (1:42 pm)
Same with this part! Pack and Unpack MUST MATCH!In player::unpackupdate
AFTER
else
mArmAnimation.action = action;
But BEFORE the closing bracket
add this:
//-------
// phdana hth play once arm thread
mArmThreadPlayOnce = stream->readFlag();
if (mArmThreadPlayOnce)
startPlayOnce(1.0);
// phdana hth twitch fix ->
else
stopPlayOnce();
// <- phdana hth twitch fix
// <- phdana hth play once arm thread->
//-------
LAST STEP IN PLAYER.CC
(around line 3847) AFTER the whole UnPackUpdate section, but
BEFORE THIS
ConsoleMethod( Player, getState, const char*, 2, 2, "Return the current state name.")
{
return object->getStateName();
}
ADD THESE TWO CONSOLE METHODS
// phdana hth play once arm thread ->
ConsoleMethod( Player, setArmThreadPlayOnce, bool, 3, 3, "(string sequenceName)")
{
return object->setArmThreadPlayOnce(argv[2]);
}
ConsoleMethod( Player, SetArmThreadTransitionOnce, bool, 3, 3, "(string sequenceName)")
{
return object->setArmThreadTransitionOnce(argv[2]);
}
// phdana hth <<----
//----
SAVE PLAYER.CC
//----
OK, STILL A LONG WAY TO GO...
#13
(around line 337)
// in call Player: Public, after:
TSThread* mArmThread;
but BEFORE
TSThread* mHeadVThread;
add this
// phdana hth play once arm thread->
bool mArmThreadPlayOnce; // if true then special case where we actaully PLAY arm thread
U32 mArmThreadSavedAction; // arm thread that was active prior to play once thread
// <- phdana hth play once arm thread
NEXT
(about line 386)
AFTER
// New collision
public:
OrthoBoxConvex mConvex;
Box3F mWorkingQueryBox;
BUT BEFORE
protected:
ADD THIS
/ phdana hth play once arm thread->
void startPlayOnce(F32 timeScale);
void stopPlayOnce();
bool setArmThreadPlayOnce(const char* sequence);
// phdana stun ->
bool setArmThreadTransitionOnce(const char* sequence);
// <- phdana stun
void advancePlayOnceTime(F32 dt);
// <- phdana hth play once arm thread
//----
Save Player.h
//----
Step Five: Open shapeBase.h
(line 320)
// in struct ShapeBaseImageData: public, after;
S32 fireState; ///< The ID of the fire state.
ADD THIS
// phdana
S32 damageStartNode; // PC: the node which is used to start the damage raycast
S32 damageEndNode; // the end node to raycast to.. anything between start and end
// is damaged.
// phdana
NEXT
// Then in class ShapeBase : public GameBase, After (line 948);
virtual void onImpact(VectorF vec);
add this
virtual void UpdateImageRaycastDamage(F32 dt, U32 imageSlot );
NEXT (line 1005)
after:
const char* getSkinName();
add:
// PC server id for this ghost? should be a base method somewehere.
// phdana
S32 mShapeServerId;
void setShapeServerId(S32 Id);
S32 getShapeServerId(){ return mShapeServerId; };
// phdana
OK, HERE'S A CHANGE FROM JOSH'S RESOURCE
(line 971)
The order of Masks is very important!
This line gets changed first.
SoundMaskN = Parent::NextFreeMask << 8, ///< Extends + MaxSoundThreads bits
It needs the 8 changed to 9
SoundMaskN = Parent::NextFreeMask << 9, ///< Extends + MaxSoundThreads bits
SoundMaskN, ThreadMaskN, and ImageMaskN MUST be the last three Masks. I think it's
because of the enum BaseMaskConstants that follows it.
Anyways, change the 8 to 9, then between these two lines:
SkinMask = Parent::NextFreeMask << 7,
SoundMaskN = Parent::NextFreeMask << 9,
ADD THIS LINE
ServerIdMask = Parent::NextFreeMask << 8,
So it should look like this:
SkinMask = Parent::NextFreeMask << 7,
ServerIdMask = Parent::NextFreeMask << 8,
SoundMaskN = Parent::NextFreeMask << 9,
///< Extends + MaxSoundThreads bits
ThreadMaskN = SoundMaskN << MaxSoundThreads,
///< Extends + MaxScriptThreads bits
ImageMaskN = ThreadMaskN << MaxScriptThreads,
///< Extends + MaxMountedImage bits
NextFreeMask = ImageMaskN << MaxMountedImages
};
//-------
SAVE shapeBase.h and open shapeBase.cc
//-------
11/07/2008 (1:43 pm)
Step 4 Player.h(around line 337)
// in call Player: Public, after:
TSThread* mArmThread;
but BEFORE
TSThread* mHeadVThread;
add this
// phdana hth play once arm thread->
bool mArmThreadPlayOnce; // if true then special case where we actaully PLAY arm thread
U32 mArmThreadSavedAction; // arm thread that was active prior to play once thread
// <- phdana hth play once arm thread
NEXT
(about line 386)
AFTER
// New collision
public:
OrthoBoxConvex mConvex;
Box3F mWorkingQueryBox;
BUT BEFORE
protected:
ADD THIS
/ phdana hth play once arm thread->
void startPlayOnce(F32 timeScale);
void stopPlayOnce();
bool setArmThreadPlayOnce(const char* sequence);
// phdana stun ->
bool setArmThreadTransitionOnce(const char* sequence);
// <- phdana stun
void advancePlayOnceTime(F32 dt);
// <- phdana hth play once arm thread
//----
Save Player.h
//----
Step Five: Open shapeBase.h
(line 320)
// in struct ShapeBaseImageData: public, after;
S32 fireState; ///< The ID of the fire state.
ADD THIS
// phdana
S32 damageStartNode; // PC: the node which is used to start the damage raycast
S32 damageEndNode; // the end node to raycast to.. anything between start and end
// is damaged.
// phdana
NEXT
// Then in class ShapeBase : public GameBase, After (line 948);
virtual void onImpact(VectorF vec);
add this
virtual void UpdateImageRaycastDamage(F32 dt, U32 imageSlot );
NEXT (line 1005)
after:
const char* getSkinName();
add:
// PC server id for this ghost? should be a base method somewehere.
// phdana
S32 mShapeServerId;
void setShapeServerId(S32 Id);
S32 getShapeServerId(){ return mShapeServerId; };
// phdana
OK, HERE'S A CHANGE FROM JOSH'S RESOURCE
(line 971)
The order of Masks is very important!
This line gets changed first.
SoundMaskN = Parent::NextFreeMask << 8, ///< Extends + MaxSoundThreads bits
It needs the 8 changed to 9
SoundMaskN = Parent::NextFreeMask << 9, ///< Extends + MaxSoundThreads bits
SoundMaskN, ThreadMaskN, and ImageMaskN MUST be the last three Masks. I think it's
because of the enum BaseMaskConstants that follows it.
Anyways, change the 8 to 9, then between these two lines:
SkinMask = Parent::NextFreeMask << 7,
SoundMaskN = Parent::NextFreeMask << 9,
ADD THIS LINE
ServerIdMask = Parent::NextFreeMask << 8,
So it should look like this:
SkinMask = Parent::NextFreeMask << 7,
ServerIdMask = Parent::NextFreeMask << 8,
SoundMaskN = Parent::NextFreeMask << 9,
///< Extends + MaxSoundThreads bits
ThreadMaskN = SoundMaskN << MaxSoundThreads,
///< Extends + MaxScriptThreads bits
ImageMaskN = ThreadMaskN << MaxScriptThreads,
///< Extends + MaxMountedImage bits
NextFreeMask = ImageMaskN << MaxMountedImages
};
//-------
SAVE shapeBase.h and open shapeBase.cc
//-------
#14
in shapebase::shapebase(), after (line 1298):
mAppliedForce.set(0, 0, 0);
add:
mShapeServerId = 0;
NEXT
// in U32 ShapeBase::pack update(NetConnection
(line 2878)
after ShieldMask | SkinMask
ADD | ServerIDMask
so it looks like this
if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask |
ShieldMask | SkinMask | ServerIDMask)))
return retMask;
NEXT
(line 2929)
Where it says // Group some of the uncommon stuff together.
add | ServerIDMask
So that it looks like this
// Group some of the uncommon stuff together.
if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask |
ServerIDMask))) {
if (stream->writeFlag(mask & CloakMask)) {
In that same area (line 2934), after:
// piggyback control update
stream->writeFlag(bool(getControllingClient()));
// add:
// PC: added ServerIdMask and support code to send ServerId value.
if (stream->writeFlag(mask & ServerIdMask)) {
stream->writeInt(mShapeServerId,32);
}
NEXT
(line 3109)
in unpackupdate, after:
setCloakedState(stream->readFlag());
mIsControlled = stream->readFlag();
add:
// PC: read team id value (8 bits signed integer)
if(stream->readFlag())
{
mShapeServerId = stream->readInt(32);
}
The section should look like this
if (stream->readFlag())
{
if(stream->readFlag()) // Cloaked and control
{
setCloakedState(stream->readFlag());
mIsControlled = stream->readFlag();
// PC: read team id value (8 bits signed integer)
if(stream->readFlag())
{
mShapeServerId = stream->readInt(32);
}
if (( mFading = stream->readFlag()) == true) {
mFadeOut = stream->readFlag();
if(mFadeOut)
mFadeVal = 1.0f;
else
mFadeVal = 0;
stream->read(&mFadeTime);
mFadeDelay = 0;
mFadeElapsedTime = 0;
}
else
mFadeVal = F32(stream->readFlag());
}
11/07/2008 (1:43 pm)
Step Six: shapeBase.ccin shapebase::shapebase(), after (line 1298):
mAppliedForce.set(0, 0, 0);
add:
mShapeServerId = 0;
NEXT
// in U32 ShapeBase::pack update(NetConnection
(line 2878)
after ShieldMask | SkinMask
ADD | ServerIDMask
so it looks like this
if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask |
ShieldMask | SkinMask | ServerIDMask)))
return retMask;
NEXT
(line 2929)
Where it says // Group some of the uncommon stuff together.
add | ServerIDMask
So that it looks like this
// Group some of the uncommon stuff together.
if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask |
ServerIDMask))) {
if (stream->writeFlag(mask & CloakMask)) {
In that same area (line 2934), after:
// piggyback control update
stream->writeFlag(bool(getControllingClient()));
// add:
// PC: added ServerIdMask and support code to send ServerId value.
if (stream->writeFlag(mask & ServerIdMask)) {
stream->writeInt(mShapeServerId,32);
}
NEXT
(line 3109)
in unpackupdate, after:
setCloakedState(stream->readFlag());
mIsControlled = stream->readFlag();
add:
// PC: read team id value (8 bits signed integer)
if(stream->readFlag())
{
mShapeServerId = stream->readInt(32);
}
The section should look like this
if (stream->readFlag())
{
if(stream->readFlag()) // Cloaked and control
{
setCloakedState(stream->readFlag());
mIsControlled = stream->readFlag();
// PC: read team id value (8 bits signed integer)
if(stream->readFlag())
{
mShapeServerId = stream->readInt(32);
}
if (( mFading = stream->readFlag()) == true) {
mFadeOut = stream->readFlag();
if(mFadeOut)
mFadeVal = 1.0f;
else
mFadeVal = 0;
stream->read(&mFadeTime);
mFadeDelay = 0;
mFadeElapsedTime = 0;
}
else
mFadeVal = F32(stream->readFlag());
}
#15
AFTER
ConsoleMethod( ShapeBase, getSkinName, const char*, 2, 2, "")
{
return object->getSkinName();
}
BUT BEFORE
//----------------------------------------------------------------------------
void ShapeBase::consoleInit()
{
Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec);
Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec);
Con::addVariable("pref::environmentMaps", TypeBool, &gRenderEnvMaps);
}
ADD THIS
// PC: shape id thats valid on the client for any server ident needed.
void ShapeBase::setShapeServerId(S32 Id)
{
if (!isGhost())
{
mShapeServerId = Id;
// dirty the team mask so the team id gets updated
setMaskBits(ServerIdMask);
}
}
ConsoleMethod( ShapeBase, setShapeServerId, void, 3, 3, "(id)")
{
if (object->isServerObject())
object->setShapeServerId(dAtoi(argv[2]));
}
LAST THING IN SHAPEBASE.CC
// in shapebase::processTick,
after:
updateServerAudio(); (line 943)
before // update wet state
ADD
// phdana hth server side axe ->
// this function was previously only called on client side
for (int i = 0; i < MaxMountedImages; i++)
if (mMountedImageList[i].dataBlock)
UpdateImageRaycastDamage( TickSec, i );
// <- phdana hth server side axe
SAVE SHAPEBASE.CC AND OPEN SHAPEIMAGE.CC
11/07/2008 (1:43 pm)
(line 4075)AFTER
ConsoleMethod( ShapeBase, getSkinName, const char*, 2, 2, "")
{
return object->getSkinName();
}
BUT BEFORE
//----------------------------------------------------------------------------
void ShapeBase::consoleInit()
{
Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec);
Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec);
Con::addVariable("pref::environmentMaps", TypeBool, &gRenderEnvMaps);
}
ADD THIS
// PC: shape id thats valid on the client for any server ident needed.
void ShapeBase::setShapeServerId(S32 Id)
{
if (!isGhost())
{
mShapeServerId = Id;
// dirty the team mask so the team id gets updated
setMaskBits(ServerIdMask);
}
}
ConsoleMethod( ShapeBase, setShapeServerId, void, 3, 3, "(id)")
{
if (object->isServerObject())
object->setShapeServerId(dAtoi(argv[2]));
}
LAST THING IN SHAPEBASE.CC
// in shapebase::processTick,
after:
updateServerAudio(); (line 943)
before // update wet state
ADD
// phdana hth server side axe ->
// this function was previously only called on client side
for (int i = 0; i < MaxMountedImages; i++)
if (mMountedImageList[i].dataBlock)
UpdateImageRaycastDamage( TickSec, i );
// <- phdana hth server side axe
SAVE SHAPEBASE.CC AND OPEN SHAPEIMAGE.CC
#16
in ShapeImage.cc (line 222)
bool ShapeBaseImageData::preload(bool server, char errorBuffer[256])
After
if(computeCRC)
{
Con::printf("Validation required for shape: %s", shapeName);
if(server)
mCRC = shape.getCRC();
else if(mCRC != shape.getCRC())
{
dSprintf(errorBuffer, 256, "Shape \"%s\" does not match version on server.",shapeName);
return false;
}
}
BUT BEFORE(line 264) // Resolve nodes & build mount transform
ADD
// PC: find our damage location nodes..
damageStartNode = shape->findNode("damageStart");
damageEndNode = shape->findNode("damageEnd");
// RW ----------------------
AND FINALLY, ADD THIS AT THE VERY BOTTOM OF SHAPEIMAGE.CC
// Add this functions to the bottom of the file. (after line 1931 or whatever is LAST!)
void ShapeBase::UpdateImageRaycastDamage( F32 dt,U32 imageSlot)
{
MountedImage& image = mMountedImageList[imageSlot];
ShapeBaseImageData* imageData = image.dataBlock;
if (!image.dataBlock) return;
// test if our mount nodes are available.
if (imageData->damageStartNode == -1) return;
if (imageData->damageEndNode == -1) return;
// temporarily say this is false so we get
// the image transforms from the MOUNT points
ShapeBaseImageData& data = *image.dataBlock;
bool oldUse = data.useEyeOffset;
data.useEyeOffset = false;
// if we have our mount nodes, transform them to world space
MatrixF startTrans;
getImageTransform( imageSlot, imageData->damageStartNode, &startTrans );
VectorF vecStart = startTrans.getPosition();
MatrixF endTrans;
getImageTransform( imageSlot, imageData->damageEndNode, &endTrans );
VectorF vecEnd = endTrans.getPosition();
// restore
data.useEyeOffset = oldUse;
// then call the raycast function in script to do the damage. lets call it onImageIntersect
char buff1[32];
char buff2[100];
char buff3[100];
dSprintf(buff1,sizeof(buff1),"%d",getShapeServerId());
dSprintf(buff2,sizeof(buff2),"%f %f %f",vecStart.x, vecStart.y, vecStart.z);
dSprintf(buff3,sizeof(buff3),"%f %f %f",vecEnd.x, vecEnd.y, vecEnd.z);
// call the script function!
Con::executef(image.dataBlock, 5, "onImageIntersect",scriptThis(),buff1,buff2,buff3);
}
SAVE SHAPEIMAGE.CC, AND BUILD YOUR SOLUTION
Delete .dso's, cross your fingers, and run the debugger!
11/07/2008 (1:44 pm)
OPEN SHAPEIMAGE.CC[/b]in ShapeImage.cc (line 222)
bool ShapeBaseImageData::preload(bool server, char errorBuffer[256])
After
if(computeCRC)
{
Con::printf("Validation required for shape: %s", shapeName);
if(server)
mCRC = shape.getCRC();
else if(mCRC != shape.getCRC())
{
dSprintf(errorBuffer, 256, "Shape \"%s\" does not match version on server.",shapeName);
return false;
}
}
BUT BEFORE(line 264) // Resolve nodes & build mount transform
ADD
// PC: find our damage location nodes..
damageStartNode = shape->findNode("damageStart");
damageEndNode = shape->findNode("damageEnd");
// RW ----------------------
AND FINALLY, ADD THIS AT THE VERY BOTTOM OF SHAPEIMAGE.CC
// Add this functions to the bottom of the file. (after line 1931 or whatever is LAST!)
void ShapeBase::UpdateImageRaycastDamage( F32 dt,U32 imageSlot)
{
MountedImage& image = mMountedImageList[imageSlot];
ShapeBaseImageData* imageData = image.dataBlock;
if (!image.dataBlock) return;
// test if our mount nodes are available.
if (imageData->damageStartNode == -1) return;
if (imageData->damageEndNode == -1) return;
// temporarily say this is false so we get
// the image transforms from the MOUNT points
ShapeBaseImageData& data = *image.dataBlock;
bool oldUse = data.useEyeOffset;
data.useEyeOffset = false;
// if we have our mount nodes, transform them to world space
MatrixF startTrans;
getImageTransform( imageSlot, imageData->damageStartNode, &startTrans );
VectorF vecStart = startTrans.getPosition();
MatrixF endTrans;
getImageTransform( imageSlot, imageData->damageEndNode, &endTrans );
VectorF vecEnd = endTrans.getPosition();
// restore
data.useEyeOffset = oldUse;
// then call the raycast function in script to do the damage. lets call it onImageIntersect
char buff1[32];
char buff2[100];
char buff3[100];
dSprintf(buff1,sizeof(buff1),"%d",getShapeServerId());
dSprintf(buff2,sizeof(buff2),"%f %f %f",vecStart.x, vecStart.y, vecStart.z);
dSprintf(buff3,sizeof(buff3),"%f %f %f",vecEnd.x, vecEnd.y, vecEnd.z);
// call the script function!
Con::executef(image.dataBlock, 5, "onImageIntersect",scriptThis(),buff1,buff2,buff3);
}
SAVE SHAPEIMAGE.CC, AND BUILD YOUR SOLUTION
Delete .dso's, cross your fingers, and run the debugger!
#17
13>c:\torque\engine\game\shapebase.cc(2887) : error C2065: 'ServerIDMask' : undeclared identifier
OK, It's case sensitive!!! Make sure ALL of your words match case!
ServerIdMask is different than ServerIDMask
It builds without errors!
The debugger is throwing an error in weapon.cs though. Gotta check that out.
Ah, forgot to spawn with a sword.
In Player.cs, under //Allowable Inventory Items add
maxInv[Sword] = 1;
and in game.cs change this:
// Starting equipment
%player.setInventory(Crossbow,1);
%player.setInventory(CrossbowAmmo,100);
%player.mountImage(CrossbowImage,0);
to this:
// Starting equipment
%player.setInventory(Sword,1);
%player.setInventory(CrossbowAmmo,100);
%player.mountImage(SwordImage,0);
add the .dsq sequences to shapes/player/player.cs
sequence30 = "./player_h1jumpattack.dsq h1jumpattack";
sequence31 = "./player_h1root.dsq h1root";
sequence32 = "./player_h1slice.dsq h1slice";
sequence33 = "./player_h1stunde.dsq h1stunde";
sequence34 = "./player_h1swing.dsq h1swing";
sequence35 = "./player_h1thrust.dsq h1thrust";
make sure you have a sword (ie. rune_blade01.dts) in the shapes/sword folder.
delete .dso's and debug.
It works! My framerate seems poor, the animation looks choppy, but it works!
THANKS TO JOSH MOORE FOR THE CODE!!!
Good luck!
Tony
11/07/2008 (1:45 pm)
Getting an error on my build13>c:\torque\engine\game\shapebase.cc(2887) : error C2065: 'ServerIDMask' : undeclared identifier
OK, It's case sensitive!!! Make sure ALL of your words match case!
ServerIdMask is different than ServerIDMask
It builds without errors!
The debugger is throwing an error in weapon.cs though. Gotta check that out.
Ah, forgot to spawn with a sword.
In Player.cs, under //Allowable Inventory Items add
maxInv[Sword] = 1;
and in game.cs change this:
// Starting equipment
%player.setInventory(Crossbow,1);
%player.setInventory(CrossbowAmmo,100);
%player.mountImage(CrossbowImage,0);
to this:
// Starting equipment
%player.setInventory(Sword,1);
%player.setInventory(CrossbowAmmo,100);
%player.mountImage(SwordImage,0);
add the .dsq sequences to shapes/player/player.cs
sequence30 = "./player_h1jumpattack.dsq h1jumpattack";
sequence31 = "./player_h1root.dsq h1root";
sequence32 = "./player_h1slice.dsq h1slice";
sequence33 = "./player_h1stunde.dsq h1stunde";
sequence34 = "./player_h1swing.dsq h1swing";
sequence35 = "./player_h1thrust.dsq h1thrust";
make sure you have a sword (ie. rune_blade01.dts) in the shapes/sword folder.
delete .dso's and debug.
It works! My framerate seems poor, the animation looks choppy, but it works!
THANKS TO JOSH MOORE FOR THE CODE!!!
Good luck!
Tony
#19
Tony
11/08/2008 (9:00 am)
I'll upload it as a new resource for 1.5.2, but I realy only made a couple changes. Josh's code was excellent.Tony
#20
Tony
11/08/2008 (9:31 am)
I'll upload it as a new resource for 1.5.2, but I realy only made a couple changes. Josh's code was excellent.Tony
Torque 3D Owner Edward