Game Development Community

dev|Pro Game Development Curriculum

Tactics-Action Hybrid Game Tutorial Part5: Energy and Turns

by Steve Acaster · 05/08/2011 (12:11 pm) · 2 comments

Back to Part Four: Player Actions

So, we have a playerObject that can run around, and shoot once per turn - but the playerObject's Energy/ActionPoints needs to drop when it moves, and we need to create a Turnbased system to then restore that energy and allow the playerObject to shoot again in the next turn.

Firstly, Energy.

The player Class, and thus the AiPlayer Class which derives from it and is the Class which we are using for our playerObjects, already has a fully functioning "Energy" system which works in a similar method to the Player's "Damage" system.

In scripts/server/player.cs add:
function Player::updateEnergy(%player)
{
   //echo("\c4Player::updateEnergy() -> Player Energy changed, updating HUD!");

   %energyLevel = %player.getEnergyLevel();
   %curEnergy = mceil(%energyLevel);

   // Send the player object's current health level to the client, where it
   // will Update the numericalEnergy HUD.
   
		commandToClient(localClientConnection, 'setNumericalEnergyHUD', %curEnergy);
}

function Player::restoreEnergy(%player)
{
   %maxEnergy = %player.getDatablock().maxEnergy;
   %player.setEnergyLevel(%maxEnergy);
   commandToClient(localClientConnection, 'setNumericalEnergyHUD', %maxEnergy);
}

function Player::decEnergy(%player)
{

	if(%player.getVelocity() !$="0 0 0")
	{
		%curEnergy = %player.getEnergyLevel() -1;
		%player.setEnergyLevel(%curEnergy);
		%player.updateEnergy();
		
		if(%curEnergy < 1)
		{
			%player.stop();
			messageClient(localclientconnection, 'MsgPlayer', '\c0%1 - Cannot Move - Out Of Energy!', %player.getName());
			
			if( %player.decal > -1 )
				decalManagerRemoveDecal( %player.decal );
			return;
		}
		
		%player.schedule(250, "decEnergy");
	}
}

And we also need to alter the "onAdd" function for the "Armor" (armor = playerObject/avatar rather than the Player Class). We need to add a schedule for restoreEnergy(). Energy starts off at zero and slowly recharges, but we want it to start at full. We also set the RechargeRate for player energy to zero.

function Armor::onAdd(%this, %obj)
{
   // Vehicle timeout
   %obj.mountVehicle = true;

   // Default dynamic armor stats
   %obj.setRechargeRate(0);//(%this.rechargeRate);//yorks changed! to prevent auto recharging
   %obj.setRepairRate(0);

   // Set the numerical Health HUD
   //%obj.updateHealth();

   // Calling updateHealth() must be delayed now... for some reason
   %obj.schedule(50, "updateHealth");//yorks - remember this, we'll disable it later
   %obj.schedule(50, "restoreEnergy");//yorks in!
}

Now we have 3 new functions. decEnergy() is called by the moveAction in playGui and works whilst the player is moving, decrementing the playerObject's energy. When this reaches zero the playerObject is forcibly stopped and cannot move again until it receives more energy (eg: at the start of a new turn) when restoreEnergy() is called. The third function is updateEnergy which in turns calls the player's Gui HUD to display the change in energy on screen.

In scripts/client/client.cs we need to add the function to receive this call:

function clientCmdSetNumericalEnergyHUD(%curEnergy)
{
   // Skip if the hud is missing.
   if (!isObject(numericalEnergyHUD))
      return;

   // The server has sent us our current energy, display it on the HUD
   numericalEnergyHUD.setValue(%curEnergy);
   
}

Now we need to make this GUI exist in the playGui so that it can be displayed on screen.

Boot up T3D, go into level, and open up the Gui Editor again.

Firstly, select "weaponHUD" and move it to "114 693". Now select "healthHUD" and change it's extent to "112 38" and it's position to "6 726". Adjust the bitmap background to fit (extent = "96 22"). Change "numericalHealthHUD" position to "48 4" and extent to "64 32". Create a new GuiTextCtrl and make it a child of "healthHud". Call it "HPtitle" and give it position "16 11", extent "64 16" and text = "Health".

Now select and then copy "healthHud", select playGui and paste. You should now have a new "healthHUD1", change this to "energyHud". Change "HPtitle1" to "Energytitle" and "numericalHealthHUD1" to "numericalEnergyHUD". Change text = "Health" to text = "Energy". To finalize this new Hud element, reposition it above the "healthHUD" at position = "6 693". Save.

Now your playerObject's energy will have someone to display and report changes when he moves. Whilst we are in the GUI Editor, let's add the final button for now, the "End Turn Button".

Create a new "library->images->GuiBitmapBorderCtrl" to keep our buttons inside. Call it "turnHud" and give it horizSizing = "left" and vertSizing = "bottom". Give it the control->Profile = "chatHudBorderProfile" and check "isContainer". Give it the position = "938 8" and extent = "80 48".

N.B. When resizing screen, to keep position the vert/horizSizing should be the OPPOSITE of their nearest edge. We want this gui element to be at the bottom right of the screen so we use "bottom left" for their vert/horizSizing.

Next create a background (library->Images->BitmapGuiCtrl with bitmap "core/art/gui/images/hudfill.png") for this and make sure it is a child of "turnHUD". Position = "8 8", extent = "64 32", sizings are "width" and "height".

Finally we need to make a button (library->buttons->GuibuttonCtrl) and call it "EndTurn", make it a child of "turnHUD" and set position = "8 8", extent = "64 32" with sizings are "width" and "height". Finally check it is a buttonType = "pushButton", and text = "End Turn".

Save, return to the game, and hopefully the GUI setup on your HUD looks like this:

yorks.deta.in/TorqueTacticsTutorial/TorqueTacticsTut1.jpg

Quit T3D and back in your favoured script editor let's make that "EndTurn" button work. In art/gui/playGui.gui add the new function to control this button's action.

function EndTurn::onAction(%this)
{
	commandToServer('TurnOver');

	tacticsMove.pressed = 0;
	tacticsShoot.pressed = 0;

	tacticsMove.setStateOn(false);
	tacticsShoot.setStateOn(false);
}

When the "EndTurn" action is triggered, it will reset the 2 action toggleButtons and their variable, and inform the server the turn is over.

In scripts/server/commands.cs add:

function serverCmdTurnOver(%client)
{
   if(%client.player.getVelocity() !$="0 0 0")//this should never happen but safety first ;)
   {
      %client.player.stop();
		
      if( %client.player.decal > -1 )//just checking
         decalManagerRemoveDecal( %client.player.decal );
   }

   //reset everything for the player!	
   %client.player.action = 0;
   %client.player.hasFired = 0;
   %client.player.restoreEnergy();
}

And now, we should have a fully functioning system which allows our playerObject to run around, updating the HUD as his energy changes, and stopping him when he runs out of energy. He can fire once a turn, and to restore his energy and ability to shoot we start a new turn.

Next, we need some team members, and functions to toggle Client control between them. This will also involve a fair bit of cleaning up the way the server->client system works in stock T3D.
Part Six: Fix GUI Calls

#1
05/13/2011 (9:42 am)
This is great, I was wondering if you know how to implement a clock that counted down so I could give the turn a time limit too?
#2
05/15/2011 (8:30 am)
There's a stock clock in T3D which counts gameTime "up". It could probably be adapted to count "down". Or just make a schedule that counts down, and updates a gui as it goes, and reset it at the start of each turn.