Game Development Community

dev|Pro Game Development Curriculum

Weapon Push Back fix - adding new state transitions

by Hedd Roberts · 01/29/2011 (7:55 am) · 6 comments

This resource will show how to add 2 new state transitions to the weapon image called
`StateTransitionOnWall` and `StateTransitionOnNotWall`. These can be used to replace the
weapon pushback problem that annoys everyone. (Me anyway. Lol!). This is not network tested
so if anyone could please test it and let me know if it works. I dont see why it shouldnt!

What I do is play a weapon animation from the state machine, and then call a script from
the state machine to do a %obj.setArmThread(ArmWeaponUp) on the player object.

This is fairly long, so lets get to it!

The first thing is to disable the weapon pushback code! I`ll do it simply here, but you
should think about removing it completely if you want.

In void Player::renderMountedImage(SceneState* state, ShapeImageRenderImage* rimage)

find these lines:

getRenderImageTransform(rimage->mIndex, &mat);
glPushMatrix();

Comment out all the code until this line:

dglMultMatrix(&mat);

Now then.

Open ShapeBase.h

and in:

struct ShapeBaseImageData: public GameBaseData {

after:

S32 wet[2]; ///< wet/notWet

Add:

S32 wall[2]; ///< wall/noWall

After:

const char* stateTransitionNotWet [MaxStates];

Add:

const char* stateTransitionWall [MaxStates];
const char* stateTransitionNotWall [MaxStates];

In class ShapeBase : public GameBase

After:

bool wet; ///< Is the weapon wet?

Add:

bool wall; ///< Is the weapon in a wall?

After:

F32 mWaterCoverage; ///< Percent of this object covered by water

and put this after it:

F32 mInWall; ///< Percent of weapon in the wall

After:

void setImageWetState(U32 imageSlot,bool wet);

Add:

// Hedd - Weapon in wall
void setImageWallState(U32 imageSlot,bool wall);
bool getImageWallState(U32 imageSlot);

Now open ShapeBase.cc

In

ShapeBase::ShapeBase()

After:

mWaterCoverage = 0;

put:

mInWall = 0;

In

void ShapeBase::processTick(const Move* move)

After:

// update wet state
setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered

Put:

// update wall state
setImageWallState(0, mInWall); // more than 0 percent covered

In

void ShapeBase::updateContainer()

put this at the very end.

//Hedd - Weapon In Wall - Is this the right place. In Player.cc, this code has some problems
disableCollision();
if (isMounted())
getObjectMount()->disableCollision();

MatrixF smat;
getEyeTransform(&smat); // Hedd - Cant use the muzzle transform because were going to change the armthread, This could be better.

// See if we are pushed into a wall...
Point3F start, end;
smat.getColumn(3, &start);

smat.getColumn(1, &end);
end *= 1.0f;
end += start;

RayInfo rinfo;
U32 mask = TerrainObjectType | InteriorObjectType | StaticShapeObjectType | StaticObjectType | PlayerObjectType | VehicleObjectType | ShapeBaseObjectType;
if (mContainer->castRay(start, end, mask, &rinfo)) {
if (rinfo.t < 1.0f)
mInWall = 1.0f; //- rinfo.t; // Hedd
else
mInWall = 0.0f;
} else {
mInWall = 0.0f;
}
if (isMounted())
getObjectMount()->enableCollision();
enableCollision();

Just ShapeImage.cc to go:

In ShapeBaseImageData::StateData::StateData()

After:

transition.wet[0] = transition.wet[1] = -1;

Put:

transition.wall[0] = transition.wall[1] = -1;

In ShapeBaseImageData::ShapeBaseImageData()

After:

stateTransitionNotWet[i] = 0;

Add:

stateTransitionWall[i] = 0;
stateTransitionNotWall[i] = 0;

In bool ShapeBaseImageData::onAdd()

After:

s.transition.wet[1] = lookupState(stateTransitionWet[i]);

Add:

s.transition.wall[0] = lookupState(stateTransitionNotWall[i]);
s.transition.wall[1] = lookupState(stateTransitionWall[i]);

In void ShapeBaseImageData::initPersistFields()

After:

addField("stateTransitionOnNotWet", TypeString, Offset(stateTransitionNotWet, ShapeBaseImageData), MaxStates);

Add:

// Hedd - Notice the difference here between the name fields and the c++ internal name fields
addField("stateTransitionOnWall", TypeString, Offset(stateTransitionWall, ShapeBaseImageData), MaxStates);
addField("stateTransitionOnNotWall", TypeString, Offset(stateTransitionNotWall, ShapeBaseImageData), MaxStates);

In void ShapeBaseImageData::packData(BitStream* stream)

After:

stream->writeInt(s.transition.wet[1]+1,NumStateBits);

Add:

stream->writeInt(s.transition.wall[0]+1,NumStateBits);
stream->writeInt(s.transition.wall[1]+1,NumStateBits);

In void ShapeBaseImageData::unpackData(BitStream* stream)

After:

s.transition.wet[1] = stream->readInt(NumStateBits) - 1;

Add:

s.transition.wall[0] = stream->readInt(NumStateBits) - 1;
s.transition.wall[1] = stream->readInt(NumStateBits) - 1;

In ShapeBase::MountedImage::MountedImage()

After:

wet = false;

Add:

wall = false;

In bool ShapeBase::isImageReady(U32 imageSlot,U32 ns,U32 depth)

After:

if ((ns = stateData.transition.wet[image.wet]) != -1)
if (isImageReady(imageSlot,ns,depth))
return true;

Add:

if ((ns = stateData.transition.wall[image.wall]) != -1)
if (isImageReady(imageSlot,ns,depth))
return true;

Then put these two functions after the function void ShapeBase::setImageWallState(U32 imageSlot,bool wall)

void ShapeBase::setImageWallState(U32 imageSlot,bool wall)
{
MountedImage& image = mMountedImageList[imageSlot];
if (image.dataBlock && image.wall != wall) {
setMaskBits(ImageMaskN << imageSlot);
image.wall = wall;
}
}

bool ShapeBase::getImageWallState(U32 imageSlot)
{
MountedImage& image = mMountedImageList[imageSlot];
if (!image.dataBlock)
return false;
return image.wall;
}

Then In void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force)

After:

if ((ns = stateData.transition.wet[image.wet]) != -1) {
setImageState(imageSlot, ns);
return;
}

Add:

if ((ns = stateData.transition.wall[image.wall]) != -1) {
setImageState(imageSlot, ns);
return;
}

In void ShapeBase::updateImageState(U32 imageSlot,F32 dt)

After:

if ((ns = stateData.transition.wet[image.wet]) != -1) {
setImageState(imageSlot,ns);
return;
}

Your not seeing things, same code different function, Add:

if ((ns = stateData.transition.wall[image.wall]) != -1) {
setImageState(imageSlot,ns);
return;
}

Pheewww! That Should be it, recompile!

Now In script:

In your weaponImage file, put this in your ready state, and anywhere else you may need it.

stateTransitionOnWall[No] = "InWall"; // Calls the state called InWall

Where, No, is the number of your ready state.


Put these after you other states, remember to change the numbers to match yours.

stateName[12] = "InWall";
stateScript[12] = "onWeaponIn"; // Script to call
stateSound[12] = "WeaponUseSound";
stateTransitionOnTimeout[12] = "Idle";
stateTimeoutValue[12] = 0.2;
//stateWaitForTimeOut[12] = true;
stateSequence[12] = "inWall";

stateName[13] = "OutWall";
stateScript[13] = "onWeaponOut"; // Script to Call
stateSound[13] = "WeaponUseSound";
stateTransitionOnTimeout[13] = "Ready"; // return to ready state
stateTimeoutValue[13] = 0.2;
//stateWaitForTimeOut[13] = true;
stateSequence[13] = "outWall";

stateName[14] = "Idle";
//stateSound[14] = "WeaponUseSound";
stateTransitionOnTimeout[14] = "Idle";
stateTimeoutValue[14] = 0.1;
//stateWaitForTimeOut[14] = true;
stateTransitionOnTriggerDown[14] = "Idle";
stateTransitionOnNotWall[14] = "OutWall"; // When out of wall go here
//stateSequence[14] = "inWall";

Now the scripts to change the arm thread,

function UmpScopeImage::onWeaponIn(%this, %obj, %slot)
{

// Set the `Weapon in wall` armthread, must be a valid animation, I just use 11 frames all the same

echo("In Wall");

%obj.setArmThread(lookinwall); // You could have this look in the Image datablock for a weapon specific anim

}

function UmpScopeImage::onWeaponOut(%this, %obj, %slot)
{

// You should have a armThread field in your image datablock pointing to a valid animation

echo("Out Wall");

%thread = %obj.getMountedImage(0).armThread;

if (%thread $= "")
%thread = "look"; // Default, if no anim found

%obj.setArmThread(%thread);

}

As you can see I just stop the player from firing by looping the state machine in an
Idle state, but you can do anything you like.

Enjoy.

Thats it! I need a cup of tea now!

Please let me know if there are any problems, Especially networked.

Hedd Roberts

#1
01/29/2011 (10:04 am)
Interesting. Thanks for this.
#2
01/29/2011 (10:53 am)
Cool solution, thanks.

Might I suggest using [ code] [ /code] tags though.
#3
01/29/2011 (11:06 am)
Very useful, thank you very much, Hedd.
#4
01/29/2011 (12:34 pm)
man, I've been waiting for ever for someone to do this.

nice.

EDIT: this will need porting up to T3D, anyone?
#5
01/30/2011 (8:49 am)
Not got T3D yet! :( anyone else is welcome 2 give it a go.
#6
01/30/2011 (11:57 pm)
Good job! Much nicer functionality than having the weapon pushed out of the mount point.