Game Development Community

dev|Pro Game Development Curriculum

Tactics-Action Hybrid Game Tutorial Part6: Fix Gui Calls

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

Back to Part Five: Energy and Turns

So, we've got all the basic functionality for our playerObject, and now he needs some team members. Before we start on that though, there are a few things regarding the server->client->HUD in stock T3D that needs to be changed. Stock T3D is set up with many clients on different computers in mind, connecting to a main server. Having many "playerObjects" on the same computer will cause the Client's HUD to update anytime an object of the Player or Aiplayer Class requires to update a parameter.

eg: Client shoots an Ai, Ai updates health, Client's own healthHUD GUI is updated, Ai throws ammo, Client's own WeaponHUD AmmoCounter decreases. We're going to stop that and make certain that the only changes to the Client's Head-Up-Display happen with the playerObject directly under his control.

Open up scripts/server/player.cs and make the following alterations:

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

   // Default dynamic armor stats
   %obj.setRechargeRate(0);//(%this.rechargeRate);//yorks out 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 <--- DISABLE NOW!
   %obj.schedule(50, "restoreEnergy");//yorks
}

//...

function Armor::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
//...

   // Update the numerical Health HUD
   if(localClientConnection.player == %obj)//yorks in
		%obj.updateHealth();

//...
}

//...

function Armor::onDisabled(%this, %obj, %state)
{
//...
//yorks add at the bottom   
	if(localClientConnection.player == %obj)
	{
		%obj.setEnergyLevel(0);
		%obj.updateEnergy();
	}
	
	%obj.setShapeName("");
}

//...

function Player::updateHealth(%player)
{
//...

//this fixes an issue with how healthpacks work on a schedule
//so it only displays the changes on the healed playerObject when you switch team members
   if(%player == localClientConnection.player)//<------ yorks insert this here!
      commandToClient(localClientConnection, 'setNumericalHealthHUD', %curHealth);//<--changed yorks
}

//...

function Player::updateEnergy(%player)
{
//...

	if(%player == localClientConnection.player)//<---- yorks add
		commandToClient(localClientConnection, 'setNumericalEnergyHUD', %curEnergy);
}

//...

function Player::restoreEnergy(%player)
{
   %maxEnergy = %player.getDatablock().maxEnergy;
   %player.setEnergyLevel(%maxEnergy);
   
	if(%player == localClientConnection.player)//<------ yorks insert this here!
		commandToClient(localClientConnection, 'setNumericalEnergyHUD', %maxEnergy);
}

//...

Now, if a new playerObject is spawned which is not directly under our Client's control, the on-screen Energy and Health HUD will not update.

Now to sort out the calls to update the weaponHUD. Open scripts/server/weapon.cs and alter the following:

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(%this.ammo !$= "")
   {
      if (%obj.getInventory(%this.ammo))
      {
         %obj.setImageAmmo(%slot, true);
         %currentAmmo = %obj.getInventory(%this.ammo);
      }
      else
         %currentAmmo = 0;

//yorks - changes below		 
		//		%obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle);
		if(localClientConnection.player == %obj)
			commandToClient(localClientConnection, 'RefreshWeaponHud', %currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle);
//yorks changes end
   }
}

//yorks - whole new function to add here!
function WeaponImage::UpdateWeaponHud(%this, %obj, %slot)
{
   if(%this.ammo !$= "")
   {
      if (%obj.getInventory(%this.ammo))
      {
         %obj.setImageAmmo(%slot, true);
         %currentAmmo = %obj.getInventory(%this.ammo);
      }
      else
         %currentAmmo = 0;

		commandToClient(localClientConnection, 'RefreshWeaponHud', %currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle);
	}
}

function WeaponImage::onUnmount(%this, %obj, %slot)
{
//yorks - replace everything inside this function with this
	if(localClientConnection.player == %obj)
		commandToClient(localClientConnection, 'RefreshWeaponHud', %currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle);
}

//...

function Ammo::onInventory(%this, %obj, %amount)
{
   // The ammo inventory state has changed, we need to update any
   // mounted images using this ammo to reflect the new state.
   for (%i = 0; %i < 8; %i++)
   {
      if ((%image = %obj.getMountedImage(%i)) > 0)
         if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId())
         {
            %obj.setImageAmmo(%i, %amount != 0);
            %currentAmmo = %obj.getInventory(%this);
           // %obj.client.setAmmoAmountHud(%currentAmmo);//yorks out

//yorks add these 2 lines below
			if(localClientConnection.player == %obj)
				commandToClient(localclientconnection, 'SetAmmoAmountHud', %currentAmmo);
         }
   }
}

As you might have noticed %obj.client (playerObject's client) will not return anything because we are not controlling the object directly, so we have to make sure the localClientConnection is used.

Now in scripts/server/commands.cs let's finish up the new WeaponImage::UpdateWeaponHud function by adding to our custom serverCmdTurnOver function.

function serverCmdTurnOver(%client)
{
//...

//yorks add to the bottom of the function
   %wpn = %client.player.getmountedimage(%slot);
   %wpn.UpdateWeaponHud(%client.player, %slot);
}

In scripts/client/client.cs alter the following:

function clientCmdSetNumericalHealthHUD(%curHealth)
{
   // Skip if the hud is missing.
   if (!isObject(numericalHealthHUD))
      return;

   // The server has sent us our current health, display it on the HUD
   numericalHealthHUD.setValue(%curHealth);

//yorks out - always display
/*
   // Ensure the HUD is set to visible while we have health / are alive
   if (%curHealth)
      HealthHUD.setVisible(true);
   else
      HealthHUD.setVisible(false);
*/
}

//...

function clientCmdSetAmmoAmountHud(%amount)
{
//yorks replace whole function with this
      AmmoAmount.setText("Ammo: "@%amount);
}

//...

function clientCmdRefreshWeaponHUD(%amount, %preview, %ret, %zoomRet)
{
//yorks start edit at top
 //  if (!%amount)
 //     AmmoAmount.setVisible(false);
//   else
//   {
//      AmmoAmount.setVisible(true);
      AmmoAmount.setText("Ammo: "@ %amount);//yorks leave this!
//   }
//yorks end edit, leave rest below as is

//...
}

That will stop the Healthhud and WeaponHud from vanishing at certain times and instead they will display empty or at zero.

Now we need to change the way items are dealt with when a playerObject is killed and drops them. In the stock game type they disappear quite quickly, but in this slower paced, turnedbased, tactics/action hybrid you really want them to stay - stay but not respawn after use. In scripts/server/item.cs alter:

//yorks - replace this function
function Item::respawn(%this)
{
   // This method is used to respawn static ammo and weapon items
   // and is usually called when the item is picked up.
   // Instant fade...
   %this.startFade(0, 0, true);
   %this.setHidden(true);

   // Shedule a reapearance
	if(%this.noRespawn == true)
	{
		%this.schedule($Item::RespawnTime, "delete");
	}
	else
	{
		%this.schedule($Item::RespawnTime, "setHidden", false);
		%this.schedule($Item::RespawnTime + 100, "startFade", 1000, 0, false);
	}
}

//...

function ItemData::onThrow(%this, %user, %amount)
{

//...

   MissionGroup.add(%obj);
  // %obj.schedulePop();//yorks out! we don't want thrown ammo to timeout
   return %obj;
}


Next, in scripts/server/health.cs edit;

function ShapeBase::tossPatch(%this)
{
//...

   %item.static = false;
   %item.noRespawn = 1;//yorks added!
   MissionCleanup.add(%item);

//...

   %item.setCollisionTimeout(%this);
   //serverPlay3D(%item.getDataBlock().throwSound, %item.getTransform());
 //  %item.schedulePop();//yorks we don't want thrown health packs to timeout

   return %item;
}

Now weapons, ammo and health packs will continue to lie around in game after they have been thrown. Thrown items will not attempt to respawn once they have been picked up.

Whilst we are editing things, let's change the welcome message in scripts/server/gameCore.cs. This is not important and just for fun.

function GameConnection::onConnect(%client, %name)
   {
//...
      // Inform the client we've joined up
      messageClient(%client,
         'MsgClientJoin', '\c2Welcome to Torque Chronicles! The T3D Tactics/Action Hybrid Gameplay Tutorial.',// <--- yorks changed!
         %client.playerName,
//...
}

Now we should have a working logic environment that will not start altering the Client's on-screen GUI values unless it pertains to the playerObject under immediate control. So now we can create our multiple team members, teams, and a specific gameType; "TorqueTactics".

Part Seven: Tactics GameType

#1
05/21/2011 (7:05 pm)
I assume the last snippet should look more like this ? (using 1.1 preview)
// Inform the client we've joined up  
      messageClient(%client,  
         'MsgClientJoin', 'c2Welcome to Torque Chronicles! The T3D Tactics/Action Hybrid Gameplay Tutorial.',// <--- yorks changed!  
         %client.playerName);
... the line had ended abruptly.
#2
05/22/2011 (5:08 am)
Nope.

The full thing is:
// Inform the client we've joined up
      messageClient(%client,
         'MsgClientJoin', '\c2Welcome to Torque Chronicles! The T3D Tactics/Action Hybrid Gameplay Tutorial.',// <--- yorks changed!
         %client.playerName,
         %client,
         %client.sendGuid,
         %client.score,
         %client.kills,
         %client.deaths,
         %client.isAiControlled(),
         %client.isAdmin,
         %client.isSuperAdmin);