About Battles

👍

Reminder - Keep Daily Backups

When working on a strategy game with a kit as big as the Complete Kit - always keep a working daily backup! Save yourself the trouble of rolling-back changes and losing work.

What are battles?

In free-to-play strategy games players can not only build their city but also raid other players bases for resources or campaign maps to gain resources to build their base further.

Can I remove battles?

If you're building a city building game without the need for battles you can remove the campaign army menu from the Game scene center anchor, and all of the battle compments will be removed from your gameplay (we've kept battle gameplay separate to make the City Building Kit more versatile for developers to build any sort of game)

How are battles requested?

The central starting point for battles in the City Building Kit is the Army menu located in the Game scene center anchor (element name: army). In this menu, players can initiate a battle against another player's base.

686

Start a war on another player's base. Click to view larger.

Or players can select a campaign map from the scroll view:

686

Campaign Battles. Click to view larger.

When you get to the battle scene, you can deploy troops and begin battles. All of the actions in the battle are controlled by the Helios AI (Scripts/Helios/Helios.cs)

1108

Starting a battle - Pick troops to deploy.

What scripts are involved in the battles?

Script NameDescription
Scripts/Save/SaveLoadBattle.csThis script requests and loads battle maps from the server.
Scripts/Helios/Helios.csThis is the main script that controls all of the battle components.
Scripts/Menu/Army/MenuArmy.csThe deploy troops menu used in battle
Scripts/Multiplayer/LoadTransData.cs and Scripts/Multiplayer/TransData.csTransfers data between the game and the battle scenes

How does a battle start?

Everything starts with the army menu (look in the Game scene Anchors - Anchor Center - army menu). Players can either select a random player base to battle or pick one of the campaign maps in the campaign scrollview. The Scripts/Menus/Army/MenuArmy.cs controls everything that happens on the army menu.

253

Pick a fight button

When they click the "Pick a fight" 250 gold button, LoadMultiplayer0() checks if they have enough units and the gold search fee before initiating the data transfer.

The following excerpt from MenuArmy.cs shows you how this starts:

// MenuArmy.cs
// The following excerpt is from the army go to battle menu
// If you want to learn about campaign
// Please view the Campaign doc page instead
// We're going to look at these code excerpts below 
// based on player battles, not campaign
//
// First, when the player selects the 250 gold battle button:
// This function goes through a few error cases that might prevent
// a battle from starting. Such as:
// no units for battle
// no gold (requires 250 to search)

public void LoadMultiplayer0()
{	
	bool unitsExist = false;

  // Check unit total
	for (int i = 0; i < ((Stats)stats).existingUnits.Length; i++) 
	{
		if(((Stats)stats).existingUnits[i]>0)
		{
			unitsExist = true;
			break;
		}
	}
  
  
  // If units exist and a campaign level number too - load a campaign map (no charge)
  // But for normal player battles, skip this if statement and go further down
	if (unitsExist && ((TransData)transData).campaignLevel != -1) {
		StartCoroutine (LoadMultiplayerMap (0)); 		
    
	}
	else if(!unitsExist)
	{
    // No units exist, show an error message
		((Messenger)statusMsg).DisplayMessage("Train units for battle.");
    
	}
	else if(unitsExist && 
		((((Stats)stats).gold >= 250&&((TransData)transData).campaignLevel==-1)))		
	{
    // A normal multiplayer match was requested
    // go to battle if they have gold
    // If they do not, show an error message
		StartCoroutine(LoadMultiplayerMap(0)); 
   
	}
	else if(!unitsExist)
	{
    // No units exist, show an error
		((Messenger)statusMsg).DisplayMessage("Train units for battle.");
    
	}
	else
	{
    // Not enough gold to pay the 250 search fee - show an error
		((Messenger)statusMsg).DisplayMessage("You need more gold.");
	}

}

If the player has units and 250 gold to pay the search fee, the above reference code starts the coroutine LoadMultiplayerMap(0) which basically sets up the transfer data (the TransData and LoadTransData scripts transfer the data between the player base Game scene and the battle Map01 scene). Then, the game loads the battle scene (Map01 scene) and calls Scripts/Save/SaveLoadBattle.cs to take over with loading the enemy base.

The following excerpt from SaveLoadBattle.cs shows the DownloadBattleMap() function which connects to the server and requests either a random player map:

// Excerpt from SaveLoadBattle.cs
// Second step after the transdata move from
// the player base to the battle map
//
IEnumerator DownloadBattleMap()   
{
  // Get the player's ID for telling the server what map is the player's map
  // This prevents accidentally downloading the player's map as an enemy base
  // (loads a map other than the user map)
	if(CheckServerSaveFile())		
		ReadMyMapID();		
	else 			
		myMapIDCode = nullMapIDCode;

	
  // Get the campaign level from the transfer data
  // If not set, this means it's a player battle requested
  // and not a campaign battle requested
	int campaignLevel = ((TransData)transData).campaignLevel;

  // Normal player versus player battle
  // Requests a random player map that doens't match the player's map ID
  // This connects to the server address and file address
  // Setup in the Map01 scene GameManager > SaveLoadBattle Inspector
  //
	if (campaignLevel == -1)
		w2 = new WWW (serverAddress + filesAddress + "?get_random_map=1&mapid=" + myMapIDCode + "&license=" + license); //mapid with the get_random_map to prevent the user's map from being downloaded by accident
	else 
	{
    // Or if...
    // A campaign map has been requested
    // Sets the battleMapID from the 21 available (0000camp00 - 0000camp20)
		battleMapID = "0000camp" + campaignNo [campaignLevel];
    
    // Requets the battle map contents from the server scripts
    // If you don't have these server scripts yet -- login to
    // CityBuildingKit.com/download to get them or contact us for help
		w2 = new WWW (serverAddress + matchAddress + "?get_user_map=1&mapid=" + battleMapID + "&license=" + license);
	
	}
  // Returns the map data
  yield return w2;
  
// ..... continued below

📘

Want to know what happens on the server end?

Read the Online Server Sync > How Online Game Sync works documentation page to understand more about the server side scripting.

Once the map data is returned, DownloadBattleMap continues to verify the data returned is actually a map file by checking that the first lines match

// .... continued from above

// Check if there's an error connecting to the server and display it
	if(w2.error != null)
	{
		print("Server load error" + w2.error);
		((Messenger)statusMsg).DisplayMessage("Map download error.");
	}
	
	else
	{
    
		//then if the retrieval was successful, 
    // validate its content to ensure the level file integrity is intact
    // we check that the start and end file lines exist which means
    // the entire enemy map file was retrieved successfully
    // 
		if(w2.text != null && w2.text != "")
		{
			if(w2.text.Contains("###StartofFile###") && w2.text.Contains("###EndofFile###"))
			{

        // Reads the second line from the server
        // Which contains the battleMapID
        // Manually replicating this, the first 3 lines of the file
        // would look like:
        // ###StartofFile###
				// demoplayer
				// ###EndofMapid###
        // 
				string[] temp = w2.text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
        
        // Skip if this is a campaign map
				if(campaignLevel==-1)
				{
				battleMapID = temp[1];//skip ###StartofFile### then read ID
				}
        
				// Print out the player file ID
				print ( "Player File "+ battleMapID + " Contents are: \n\n" + w2.text);

        // Save the Battle Map so we can load it into the Map01 scene
				SaveBattleMap();
				sReader = new StreamReader(filePath + battleSaveFile + fileExt);

				StartCoroutine ("LateLoadGame");
				((Messenger)statusMsg).DisplayMessage("Map downloaded.");
			}
			
			else
			{			
        // If there's a problem with the data loading
        // We'll print everything out into the Unity Console
        // So you can see what the server sent back to troubleshoot
        // Also check the Server Sync Datalog page
        // In the adminstration script for details about what the game 
        // requested from the server. For details, see the
        // Online Server Sync > Admin Player Management documentation page
        //
				print ( "Player File is Invalid. Contents are: \n\n" + w2.text);
				//although incorrect, prints the content of the retrieved file
				((Messenger)statusMsg).DisplayMessage("License code not added to GameManager or downloaded file corrupted.\n" +
				"If you have your own save file on server");
				((Messenger)statusMsg).DisplayMessage("delete local files or change your 10 character ID in:\n" +
				    "C:/Users/user/AppData/LocalLow/\n" +
				    "AStarterKits/StrategyKit/MyMapID.txt");
			}
		}
		else
		{
      // Any other problem? Print an error.
			print ( "Player File File is Empty");
			((Messenger)statusMsg).DisplayMessage("Downloaded file empty?");
		}

	}
}

After DownloadBattleMap() retrieves the enemy player base file, we have verified the file is correct and are ready for battle.

Two things happen here:

  1. First, LoadGame() is called (Scripts/Save/SaveLoadBase.cs) which is a centralized function to read player data and fill a map with the objects from the player data. In this case it's easier to centralize one script to do this for both the player or enemy bases. That's what SaveLoadBase.cs does. You'll notice in this script the isBattleMap boolean prevents some objects from loading (such as construction timers or removable terrain options). There are a few differences between player bases and enemy bases - the isBattleMap boolean is how the script knows what to load.

  2. After the map data has been loaded - we're ready to start the Helios battle engine, pathfinding and obstacle avoidance, and battle sound settings (Scripts/SFX/SoundFX.cs) in the BattleMapInstructions() function seen below:

// SaveLoadBattle.cs
// After the SaveLoadBase.cs LoadGame() has loaded the elements
// for battle skipping player items with the iSBattleMap boolean
// we initiate Helios, the GridManager for A*Pathfinding and obstacles
// for preventing pathfinding from walking over buildings and through walls
// BattleMapInstructions() is where all of this setup occurs:

private void BattleMapInstructions()
{
	GameObject tempHelios = GameObject.Find ("Helios");

	tempHelios.GetComponent<Helios>().Buildings = LoadedBuildings;
	//tempHelios.GetComponent<Helios> ().Cannons = LoadedCannons;
	tempHelios.GetComponent<Helios> ().DamageBars = LoadedDamageBars;
	GameObject tempGridManager = GameObject.Find ("GridManager");
	tempGridManager.GetComponent<GridManager>().UpdateObstacles();
	tempHelios.GetComponent<Helios>().NetworkLoadReady();
	((SoundFX)soundFX).BattleMapSpecific ();

}

After this, Helios takes over. The NetworkLoadReady() function initiates a few of the first things like the Chimera corner strategy talking head. Basically, this unit gives advice and can also be used for debugging messages. For more details about Chimera, see the PvP Battle Matchmaking > Chimera documentation page.

Also, the NetworkLoadReady() function starts calculating the total loot of the enemy base with PrepareBuildings() and sets up the automated unit attack AI with the first building target through StartCoroutine("GetFirstBuilding");

See the function reference below:

// Helios.cs
// This function is called from SaveLoadBattle.cs BattleMapInstructions()
// The Chimera PvP strategy talking unit is loaded in the corner
// and the
public void NetworkLoadReady()
{	
  // Activate the HeliosUI
	HeliosUI.SetActive (true);
  // Setup the Chimera corner strategy talking unit with her first words
	Talk ("Chimera online, ready to receive.");
  // Fade Chimera in
	((Dissolve)heliosDiss).FadeFullIn ();

  // Prepare the loot for buildings and calculate total enemy loot
	PrepareBuildings ();
  
  // Automatically prepare the AI's first target for battle
  // Selecting the nearest target
	StartCoroutine("GetFirstBuilding");
	networkLoadReady = true;
}

At this point in time, everything has been loaded, and the Helios script takes over. The battle can begin when the player starts placing their first troops.

How are Units controlled?

For details about how units are loaded and controlled, see the Control Units in Battle documentation page.

Want to learn more about Battle loot?

For details about loot, see the Loot and Resources section of the documentation.

Want to learn more about Chimera?

For details about the talking Chimera head, see Chimera - Live PvP Debugger.

Where can I get details about the server scripts?

Read the Online Server Sync > How Online Game Sync works. Also, to view the server admin management options included with the kit, view the Admin Player Management documentation pages

800

Server management scripts