Game Development Community

dev|Pro Game Development Curriculum

Plastic Gem #33: Damaged/Bloody Weapons

by Anthony Rosenbaum · 07/25/2008 (6:15 am) · 5 comments


i936.photobucket.com/albums/ad202/vincismurf/banner.jpg


IFL Skin on ShapeBaseImages

Have you ever noticed that threads on ShapeBaseImages can only be played via its state machine? Have you ever wanted to play a thread on that ShapeBaseImages weapon to show a change in its appearance such as showing damage or blood? Well using ifls to change the texture of the ShapeBaseImage is the focus of this resource. There will be C changes, art requirements, and script examples so follow closely. That being said, this resource could be used as a stepping off point for playing other animations on the ShapeBaseImage separate from the state machine.

This code can be implemented in TGEA 1.7 and TGE 1.5.2

1) Add an blend IFL animation to your .dts labeled "damage_skin"


Ok the Art requirements are done, be sure you test it in the show tool and before going on to the C side.

We'll need a place to store that sequence in the ShapeBaseImageData


2) In ShapeBase.h in ShapeBaseImageData class before
S32 spinSequence; ///< ID of the spin animation sequence. ADD


// > pg damage blood
   S32 sequenceDamage;           ///< Blood Damage thread sequence.
   //< pg damage blood


Now we'll need a variable to store the current position of the thread.


3) In the ShapeBase class within the MountedImage Struct after
bool wet; ///< Is the weapon wet? ADD


// > pg damage blood
	  F32	damagePos; 
// < pg damage blood

We'll need a new thread on the MountedImage Struct to play so


4) After TSThread *spinThread; Add


// > pg damage blood
	  TSThread *damageThread;
	  // < pg damage blood

5) Now it is time to declare an interface, also within Shapebase after
void getMuzzlePoint(U32 imageSlot,Point3F* pos); ADD


// > pg damage blood
   	void setDamageBloodPos(U32 imageSlot, F32 pos);
// < pg damage blood

Time for a quick stop in ShapeBase.cc to transmit the sequence position over the network


6) In U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream) AFTER
con->packStringHandleU(stream, image.skinNameHandle); ADD

// > pg damage blood
stream->write(image.damagePos);
// < pg damage blood


7) In void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) AFTER
StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream); ADD


// > pg damage blood
stream->read(&image.damagePos);
setDamageBloodPos(i, image.damagePos);
// < pg damage blood
Notice how we set the position each time it is transmitted, this might seem like over kill right now but setDamageBloodPos() does some checking to reduce the transmittions.


Moving on to ShapeImage.cc, we'll need to associate the sequence to the datablock.


8) In bool ShapeBaseImageData::preload(bool server, char errorBuffer[256])
After
spinSequence = shape->findSequence("spin"); ADD


// > pg damage blood
	sequenceDamage = shape->findSequence("damage_skin");
// < pg damage blood
Time to initalize our thread when the image is set on the shapebase


9) In void ShapeBase::setImage()
AFTER
image.spinThread = 0; ADD


// > pg damage blood
   image.damageThread = 0;
// < pg damage blood

And not to far below that code, if the sequenceDamage actually exsist on the datablock we will associate the sequence to the thread



10) In ShapeBase::SetImage() after the
if (image.dataBlock->spinSequence != -1) {} Block ADD


// > pg damage blood
if (image.dataBlock->sequenceDamage != -1) {
    image.damageThread = image.shapeInstance->addThread();
    image.shapeInstance->setTimeScale(image.damageThread,1);
    image.shapeInstance->setSequence(image.damageThread,
                                   image.dataBlock->sequenceDamage,0);
      }
// < pg damage blood

Our final C change is defineing the setBloodDamagePos() function and its console interface. Notice how the bits are only set if it is a server object and if the position has changed, this reduces network traffic. This same function is also reused by the console interface.



11) At the bottom of the file add


// > pg damage blood
void ShapeBase::setDamageBloodPos(U32 imageSlot,F32 pos)
{
	
	if (isServerObject())
	{
		MountedImage& image = mMountedImageList[imageSlot];
		if (image.dataBlock)
		{
			if (pos == image.damagePos)
				return; // nothing changed...

			setMaskBits(ImageMaskN << imageSlot);
			image.damagePos = pos;
		}
	}
	else
	{
		// put client crap here...
		MountedImage& image = mMountedImageList[imageSlot];
		if (image.dataBlock != NULL && image.dataBlock->sequenceDamage != -1) 	
			image.shapeInstance->setPos(image.damageThread,pos);
	}
}

ConsoleMethod( ShapeBase, setDamageBloodPos, void, 4, 4, "(int slot, float pos)")
{
	int imageSlot = dAtoi(argv[2]);
	F32 pos = dAtof(argv[3]);
	if (imageSlot >= 0 && imageSlot < ShapeBase::MaxMountedImages && pos >= 0.0)
		object->setDamageBloodPos(imageSlot, pos); 
}

// < pg damage blood


Now you are set up to see the ShapeBaseImage change it's ifl as you determine.

Here is an example of some script to cheat and see the texture change. Be aware that each time to remount the image it will reset the ifl position to 0 so you'll need to maintian the current position and update the positiion as you see fit.



12) In YOURGAMENAME/client/scripts/default.bind.cs Add

// > pg damage blood
MoveMap.bind(keyboard, "ctrl-alt 1", CheatDamageBlood);

function CheatDamageBlood(%val)
{
   if(%val)
      commandToServer('CheatDamageBlood');
}
// < pg damage blood


13) In YOURGAMENAME/server/scripts/commands.cs ADD

// < pg damage blood

function serverCmdCheatDamageBlood(%client)
{
   %player = %client.player;
   %weapon = %player.getMountedImage($WeaponSlot);
      
   %BloodPos = %player.BloodPos;
   if(%BloodPos $= "")
      %BloodPos = 0;
   %BloodPos = %BloodPos + 0.25;

   if(%BloodPos > 1)
      %BloodPos = 1;
   echo("blood pos" SPC %BloodPos SPC %slot);      
   %player.setDamageBloodPos($WeaponSlot, %BloodPos);
   %player.BloodPos = %BloodPos;  
}
// < pg damage blood

Now mount the ShapeBaseImage and hit "ctrl + alt 1" to watch the texture change, of course, in your game you probably want to tie this code into the Armor::onDamage() function, but that is your call.

#1
07/25/2008 (1:02 pm)
Wow, this one opens up immense opportunities. I'd never thought much on how powerful an ifl can be used properly. Thanks!
#2
08/03/2008 (7:26 pm)
Will it work with TGE ?
#3
08/05/2008 (8:23 pm)
Charin - yes this works with both TGE and TGEA
#4
09/30/2008 (12:33 pm)
Excellet idea, and perfect for what I want to do :) Also gives me some info on adding new sequences to images, which is probably something I'll be doing in the future...
#5
03/09/2009 (5:15 am)
Does this work in TGEA 1.8.1?