Drone Training Script

Here's an example of a chunky script that uses entities to provide a basic single player game with multiple levels.

First off we've got a basic building script file that's used to provide an access point for the player where they can start the minigame:

$kPlayGamePlinthBuildingType = 5

Event( "OSDSelect", "GameOSD:PlaySingle" )
{
	$gPlayerKey[DroneGameMode] = 1
	// Play game music
	*playmusic %PLAYER% http://yyy.com/musicfile.mp3
	*crowspawn %PLAYER% 128 128
	Sleep( 5 )
	
	*gocrow %PLAYER%
	*texteffect %PLAYER%,1,First wave incoming! Ready Up!
	Sleep( 20 )
	CustomEvent( "StartGame" )		// Defined in LevelSpawner.mit
}



Event( "AccessBuilding", "$kPlayGamePlinthBuildingType" )
{
	osdcreate(OSDBUILDING,"GameOSD", "" ) 
	osdadd(EXITBUTTON, 100, 80, 400, 35, "PlaySingle", "Example Single Player Game" )	
	osdactivate() 

}

All pretty standard so far. Now we get on to the good stuff..

//----------------------------------------------------------
//  LevelSpawner.mit
//
//----------------------------------------------------------

$mSpawnPositionType = 1

// -------------------------------
//   Here we're definining a 'kindof' struct thats used to specify the details of each level
//   Each row in our table has 7 values, and we store that in a constant $kSizeOfWavesStruct
//   Each row corresponds to a 'level' in the game, so if we want to access the values for level 3
//   we start with the array value at 3 * $kSizeOfWavesStruct  ( + 1 coz arrays are not zero-indexed _rolls_eyes_)
//---------------------
$kSizeOfWavesStruct = 7

$maEnemyWaves[] = 
{	// num2spawn(1)	min lead(2) max lead(3) min speed(4) max spd(5) min acc(6)   max acc(7)
		2,		20,		50,		1700,		2100,		800,		100,		// Wave 1
		4,		30,		50,		2000,		2700,		1100,		200,		// Wave 2
		4,		50,		70,		2100,		2600,		1400,	1600,			// Wave 3
		5,		60,		70,		2300,		3000,		1100,		1400,		// Wave 4
		6,		80,		100,		2400,		3100,		1200,		1700,		// Wave 5
		7,		90,		100,		2500,		3200,		1300,		1500,		// Wave 6
		8,		90,		100,		2700,		3500,		1300,		1800		// Wave 7
		10,		90,		100,		3000,		4000,		1200,		1900,		// Wave 8
		10,		90,		100,		3100,		4500,		1500,		2000,		// Wave 9
}

//----------------------------------------------------------------------------------------------------

// -------------------------------
//  The table contains a few numbers that define a min--max range for properties of the enemies.
//   This function returns a random number between those ranges. 
//   It uses the $gPlayerKey[WaveNum] to determine which level we're on and then the baseIndex tells us where in 
//   each 'row' we should be looking.
//  (Of course, we could do away with all this nonsense if the script language supported more than just integer arrays, but thats for another time :] )
//---------------------
Function	GetTableMinMaxVal( $baseIndex )
{
	$waveTable = $gPlayerKey[WaveNum] - 1
	if ( $waveTable > 8 )
	{
		$waveTable = 8
	}
	$waveTable *= $kSizeOfWavesStruct
	$min = $maEnemyWaves[$waveTable+$baseIndex]
	$max = $maEnemyWaves[$waveTable+$baseIndex+1]
	$diff = $max - $min
	$min += sysRand( $diff )
	return( $min )
}	


///------------------------------------------------
// The custom StartGame event is triggered from the game access building defined separately
//------------------------------------
Event( "Custom", "StartGame" )
{
	// Delete all entities for the current player
	EntityDeleteAll( $gPlayerID );

	$gPlayerKey[NumEntitiesAlive] = 0
	$gPlayerKey[WaveNum] = 1
	$gPlayerKey[NumKills] = 0

	SpawnMoreBadGuys()
}

///------------------------------------------------
// SpawnMoreBadGuys
//   Called at the start of each 'wave'/level to trigger a new wave
//   of enemies
//---------------------------------------------
Function		SpawnMoreBadGuys( )
{
	$waveNum = $gPlayerKey[WaveNum]
	*notifylarge %PLAYER% Wave $waveNum

	$loop = 0

	$waveTable = $waveNum - 1
	$waveTable *= $kSizeOfWavesStruct
	$numEntitiesToSpawn = $maEnemyWaves[$waveTable+1]

	while ( $loop < $numEntitiesToSpawn )
	{
		SpawnRandomHunter( 15000, 25000 )

		$loop += 1
	}

	$gPlayerKey[NumEntitiesAlive] += $numEntitiesToSpawn
}


//-------------------------------------------------
// SpawnRandomHunter
//   Earlier code has determined we need an extra enemy , and it provides the min/max range where
//   that new enemy should appear. This function converts that range to an actual X,Y world position
//   and then calls the 'SpawnBadGuy' function above to add an entity
//------------------
Function	SpawnRandomHunter( $minrange, $maxrange )
{
	// Spawn somewhere randomnly near the player
	if ( $mSpawnPositionType == 0 )
	{
		$spawnAngle = sysRand( 360 )

		$rangeval = $maxrange - $minrange
		$spawnRange = sysRand( $rangeval )
		$spawnRange += $minrange

		$spawnSin = sysSin( $spawnAngle, $spawnRange )
		$spawnCos = sysCos( $spawnAngle, $spawnRange )

		$xpos = $gPlayerWorldX + $spawnSin
		$ypos = $gPlayerWorldY + $spawnCos
	}
	else	// Spawn on the edges of the Drone Shooter pitch
	{
		$side = sysRand(4)
		if ( $side == 1 )
		{
			$mapPosX = 103
			$mapPosY = sysRand( 50 )
			$mapPosY += 103
		}
		else if ( $side == 2 )
		{
			$mapPosX = 153
			$mapPosY = sysRand( 50 )
			$mapPosY += 103
		}
		else if ( $side == 3 )
		{
			$mapPosX = sysRand( 50 )
			$mapPosX += 103
			$mapPosY = 103
		}
		else if ( $side == 4 )
		{
			$mapPosX = sysRand( 50 )
			$mapPosX += 103
			$mapPosY = 153
		}
		$xpos = sysMapToWorld( $mapPosX )
		$ypos = sysMapToWorld( $mapPosY )
	}

	SpawnBadGuy( $xpos, $ypos )
}



// -------------------------------
//  SpawnBadGuy
// Here we'll have some fun with entities.  The calling function has worked out where to spawn a new enemy,
// So here we calculate some of its properties and then create the actual entity
//---
Function	SpawnBadGuy( $xpos, $ypos )
{
	$waveNum = $gPlayerKey[WaveNum]

	$targetLeadPercentage = GetTableMinMaxVal( 2 )
	$moveSpeed = GetTableMinMaxVal( 4 )
	$moveAcc = GetTableMinMaxVal( 6 )
	
        //  Create an entity that is managed locally (i.e. is effectively just run for the one player)
        //   with a HUNT behaviour. (This means the entity will attempt to hunt down the local player).
        //  Note that the entity does not actually get sent to the player until we add it to the world, later in this function
	$mhTestEntity = EntityCreate( $gPlayerID, "LOCAL", "HUNT"  )

	// Note, currently all entity graphics need to be present in the game's main web content folder on the CDN 
	EntitySetModel( $mhTestEntity, "uniRobocrow2.atm", "uniRobocrow2.jpg" )
	EntitySetPosition( $mhTestEntity, $xpos, $ypos, 0 )
	
	// Behaviour Control flags
      // FaceTarget = Set the entity to always to always look towards the target
	EntitySetValue( $mhTestEntity, "FaceTarget", 1 )   
      // AttackProx = Start attacking when within the ProxRange
	EntitySetValue( $mhTestEntity, "AttackProx", 1 )   
      // DieOnTargetDeath = Entity is destroyed if the target dies
	EntitySetValue( $mhTestEntity, "DieOnTargetDeath", 1 )
		
	// Parameters and values
        // ZOffset = Vertical position offset above the ground
	EntitySetValue( $mhTestEntity, "ZOffset", 800 )
        // ProxRange = Used to determine attack range
	EntitySetValue( $mhTestEntity, "ProxRange", 6000 )
	$targetDist = $waveNum * 200
	$targetDist += 2300
        // Distance from the target the entity will try to maintain
	EntitySetValue( $mhTestEntity, "TargetDist", $targetDist )
        // Who the target is..
	EntitySetValue( $mhTestEntity, "Target", $gPlayerID )
	EntitySetValue( $mhTestEntity, "Weapon", 1 )
        //  Basically this sets how good the entity is at aiming
	EntitySetValue( $mhTestEntity, "TargetLead", $targetLeadPercentage )

	$health = $waveNum * 25
	$health += 100
        // Health = how much damage this entity can take before dieing
	EntitySetValue( $mhTestEntity, "Health", $health )

        // Set Speed and acceleration (Determined by the level settings table)
	EntitySetValue( $mhTestEntity, "MoveSpeed", $moveSpeed )
	EntitySetValue( $mhTestEntity, "MoveAcc", $moveAcc )
	
        // Now all set.. add the entity to the world
	EntityAddToWorld( $mhTestEntity )
}




///------------------------------------------------
// Every time an entity is destroyed we update the player's kill count and decrease the 
//  count of enemies remaining. Once that gets to 0 we start the next wave
//------------------------------------
Event( "EntityDestroyed", "" )
{
	$gPlayerKey[NumKills] += 1
	$gPlayerKey[NumEntitiesAlive] -= 1

	if ( $gPlayerKey[NumEntitiesAlive] == 0 )
	{
		$gPlayerKey[WaveNum] += 1
		SpawnMoreBadGuys()
	}
}

///------------------------------------------------
// GameEnd is triggered from RobocrowDeath Event and displays final score etc

Event ("RobocrowDeath", "" )
{
	if ( $gPlayerKey[DroneGameMode] == 1 )
	{
		*event %PLAYER% GameEnd
	}	
}

//------------------------------------
Event( "Custom", "GameEnd" )
{
	*fademusic %PLAYER% 5
	Sleep(10)

	*texteffect %PLAYER%,1,Game Over
	
	Sleep(20)

	$numKills = $gPlayerKey[NumKills]

	if ( $numKills > $gPlayerKudos )
	{
		*setkudos %PLAYER% $numKills
		*announce %PLAYER% acheived a new personal best of $numKills kills ( Level $gPlayerKey[WaveNum] )
	}
	else
	{
		*announce %PLAYER% reached level $gPlayerKey[WaveNum] with $numKills kills
	}
}