GUIs: Notify Icon

From Mod Wiki

The Repair Drone Notify Icon

Adding a new notify icon to the HUD.

The Strogg constructor class has an upgrade that allows them to send out a flying repair drone to repair deployables and vehicles. What you have to be careful about while the repair drone is out is to not go too far away or it will stop repairing and try to return to you.

Repair Drone

In this tutorial we will be adding a new notify icon that will display the distance between the player and the repair drone. This is useful for knowing how far away from the repair drone you can go before it tries to return to you.

Notify icon bar:

Notify Icons

Where to Start

The repair drone has been implemented in the script/tools/repair_drone.script script, this is where we should update the GUIs with the distance to the player.

The notify icons are all found in guis/game/hud/hud.gui. We also need to add a global property which is updated from the script and used by the guis, the global properties is found in guis/globals.gui.

Game Script

First let's set up the game script to update a GUI property with the distance to the player in percent, where 0 is at the player position and a value of 1 is the maximum distance before the repair drone has to return.

We find two kind of objects in the repair_drone.script:

  • tool_repair_drone - The script for the repair drone tool the player is holding in his hand, this is not the script we want to work on.
  • repair_drone - This is the object we need to modify.

When the GUI properties need to be updated from the scripts it is often done in a function called UpdateGUI. There is none for the repair_drone object so we add this function. UpdateGUI needs to be called regularly while the repair drone is out. The repair drone has three different states (seeking/repairing/returning) and hence three different while loops we have to add calls to UpdateGUI. The calls are added at the end of the while loops:

repair_drone::SeekTarget:

	DoMove( desiredOrigin );
	UpdateGUI();

	sys.waitFrame();
}

repair_drone::RepairTarget:

	UpdateGUI();

	sys.waitFrame();
}

repair_drone::Return:

	DoMove( newOrigin );
	UpdateGUI();

	sys.waitFrame();
}

repair_drone::UpdateGUI:

void repair_drone::UpdateGUI() {
	entity ownerEntity = getOwnerEntity();
	// Only update the owner of the repair drone
	if ( ownerEntity == sys.getLocalPlayer() ) {
		// signal that the repair drone is active
		sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.repairDroneActive", 1.0f );
		// repairDroneRange is set to the distance in percent to the player
		sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.repairDroneRange", OwnerReturnDistancePct( ownerEntity ) );
	}
}

The distance to the player is a percentage and is calculated in OnwerReturnDistancePct:

float repair_drone::OwnerReturnDistancePct( entity ownerEntity ) {
	// calculate distance to player
	float distance = sys.vecLength( getWorldOrigin() - ownerEntity.getWorldOrigin() );
	// convert to percentage of max distance before it has to return
	float pct = distance / returnDistance;
	return pct;
}

There is one small thing left, once the repair drone has been destroyed we need to set the repairDroneActive GUI property back to 0 to signal the GUIs to destroy the notify icon:

void repair_drone::destroy() {
	if ( cachedOwner == sys.getLocalPlayer() ) {
		sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.repairDroneActive", 0.0f );
	}
}

That's all that is needed for the game scripts.

GUI Script

First let's open guis/globals.gui and add the needed properties. They should be added to the gameHud namespace:

"repairDroneActive"		"float" // repair drone active status
"repairDroneRange"		"float" // range between the repair drone and player in percent of max range

In guis/game/hud/hud.gui we need to cache the notify icon for the repair drone. We will use the same icon as for the repair drone in the weapon menu, but give it a repairdroneicon alias (line 114):

"teleporteritemicon" "guis/assets/icons/weapons/teleportgun"
"repairdroneicon" "guis/assets/icons/weapons/repair_drone"

"constructicon" "hud/icons/missionsystem/obj_build"

The global property gameHud.repairDroneActive we added should be initialized to 0, which we do in the HUD's onActivate event, line 320:

globals.gameHud.teleporterActive = 0;		
globals.gameHud.repairDroneActive = 0;

globals.vehicles.siegeMode = floatToHandle( -1 );

All notify icon types have a unique number associated with them, we add a new one for the repair drone on line 1028:

#define DI_SUPPLIES2 8
#define DI_REPAIRDRONE 9

Now we need a handle to the icon which later can be used to draw the icon (line 1094):

// Teleporter Item
handle	teleporterItemMaterial = gui.cacheMaterial( "teleporterItemIcon", "_st teleporteritemicon" );
handle	teleporterItemIcon;

// Repair drone
handle	repairDroneMaterial = gui.cacheMaterial( "repairDroneIcon", "_st repairdroneicon" );
handle	repairDroneIcon; // handle to the notify icon itself.

This is how the GUI can respond to any changes to the properties changed by the game code. We set up a onPropertyChanged for the gameHud.repairDroneActive property (line 1334):

onPropertyChanged "globals.gameHud.teleporterActive" {
	postNamedEvent( "updateTeleporter" );
}

onPropertyChanged "globals.gameHud.repairDroneActive" {
	// post a named event and do the actual work in updateRepairDrone
	postNamedEvent( "updateRepairDrone" );
}

onPropertyChanged "player.role" {
	...

The actual creation/deletion of the repair drone notify icon happens in the named event updateRepairDrone (line 1282):

onNamedEvent "updateRepairDrone" {
	if( globals.gameHud.repairDroneActive ) {
		// the repair drone is active, see if the notify icon has already been created
		if( isValidHandle( repairDroneIcon ) == false ) {
			// create icon
			repairDroneIcon = addIcon( "nodraw" );
			// set the lastItenAdded handle to the repair drone icon
			lastItemAdded = gui.copyHandle( repairDroneIcon );
			// set DI_REPAIRDRONE for the icon, needed to identify the icon during drawing
			setItemData( repairDroneIcon, DI_REPAIRDRONE );
			// set up the material to the repair drone icon
			repairDroneMaterial = gui.cacheMaterial( "repairDroneIcon", "_st repairdroneicon" );
			// bump the icon, this moves the icon around to get the players attention
			bumpIcon( lastItemAdded, NOTIFY_BUMP_TABLE );
		}
	} else {
		// see if notify icon handle is valid
		if( isValidHandle( repairDroneIcon ) ) {
			// it is valid, remove the icon
			removeIcon( repairDroneIcon );
			// set to an invalid handle
			repairDroneIcon = floatToHandle( -1 );
		}
	}
}

onPropertyChanged "globals.gameHud.supplyCrateHealth0" "globals.gameHud.supplyCrateSupplies0" {
	...

We should also call the named event once at the creation of the GUI in addition to when the gameHud.repairDroneActive property changes (in onCreate, line 1108):

	...
 	postNamedEvent( "updateTeleporter" );
	postNamedEvent( "updateRepairDrone" );
}

Now the final thing missing is the actual drawing of the repair drone icon and the range indicator. An empty notify icon will automatically be drawn, but we want to draw the repair drone icon and range indicator in addition, this is done in onPreDrawIcon (line 1410):

// Repair Drone
if( itemData == DI_REPAIRDRONE ) {
	// we are about to draw the repair drone icon
	// set the rectangle of the repair drone icon we want to draw
	scratchRect.x = itemRect.x - 10,itemRect.y + 6, 64, 16;

	// Set the color we want when drawing
	scratchColor	= COLOR_WEAPON_LIST_ICON_LINE;
	scratchColor.a 	= immediate( scratchColor.a ) * itemColor.a;

	// Draw the repair drone icon
	drawCachedMaterial( repairDroneMaterial, scratchRect, scratchColor );

	// now draw the indicator below the repair drone icon with a white color
	scratchColor	= COLOR_WHITE;
	scratchColor.a 	= immediate( scratchColor.a ) * itemColor.a;

	// x and width of the rectangle depends on the distance to the repair drone
	scratchRect.x = itemRect.x + 2 + ( ( 1.0 - globals.gameHud.repairDroneRange ) * 42 );
	scratchRect.y = itemRect.y + 28;
	scratchRect.w = 42 * ( globals.gameHud.repairDroneRange );
	scratchRect.h = 10;

	// draw the range indicator now
	clipToRect( scratchRect );
	drawCachedMaterial( progress, "itemRect.x + 2, itemRect.y + 28, 42, 10", scratchColor );
	unclipRect();

	return;
}

That's it! Here's how it looks like in-game:

Repair Drone