// Handles fire controls for squad members.
// Worthless Bums 2012-2018.


function lockFireControls ()
{
	$fireControlsLocked = true;
}
function unlockFireControls ()
{
	$fireControlsLocked = false;
}
function clearTargetIndicators (%fireFlag)
{//echo("clearTargetIndicators()");
	deleteFireControlFloatingText();
	if (isObject($targetIndicators))
	{//echo("$targetIndicators.getCount() =" SPC $targetIndicators.getCount());
		for (%i = 0; %i < $targetIndicators.getCount(); %i++)
		{//echo("delete" SPC %i);
			($targetIndicators.getObject(%i)).safeDelete();
		}
	}
	// Unfogs tiles.
	if (isObject($tilesToBeFogged))
	{
		for (%i = 0; %i < $tilesToBeFogged.getCount(); %i++)
		{
			%toBeFoggedTile = $tilesToBeFogged.getObject(%i);
			%xIndex = (%toBeFoggedTile).xIndex;
			%yIndex = (%toBeFoggedTile).yIndex;
			$fogOfWar.fadeInFogTile(%xIndex, %yIndex);
			if (isObject(%toBeFoggedTile))
			{
				//%toBeFoggedTile.safeDelete();	// Get "Unknown command safeDelete"!
			}
		}
	}
	// Clear the list of tiles to be fogged.
	$tilesToBeFogged = new SimSet();
	//undoAllTempFog();		// Removed since we no longer have toggled fire controls!
	if (%fireFlag)
	{
		engageCharacterActionLock();
		sceneWindow2D.schedule($fireActionDelay, "dismount");
		sceneWindow2D.schedule($fireActionDelay, "mount", $playerSquad[$activeCharacter], $sceneWindow2DxOffset SPC $sceneWindow2DyOffset, $cameraMountForce, false);
		schedule($fireActionDelay, 0, "releaseCharacterActionLock");
		MonsterTargetPanel.setVisible(false);
	}
	$fogOfWar.showFogOfWar();
}
function temporaryUnfog (%xIndex, %yIndex)
{//echo("temporaryUnfog()");
	$fogOfWar.uncoverFogTile(%xIndex, %yIndex);
}
function undoAllTempFog ()
{//echo("undoAllTempFog()");
	$fogOfWar.showFogOfWar();
}
function setAoeTargetIndicators (%x, %y)
{//echo("setAoeTargetIndicators()");
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x - 1, %y - 1));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x, %y - 1));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x + 1, %y - 1));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x - 1, %y));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x + 1, %y));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x - 1, %y + 1));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x, %y + 1));
	$targetIndicators.add(%targetIndicatorAnimation);
	
	%targetIndicatorAnimation = createTargetIndicatorAnimation();
	%targetIndicatorAnimation.setPosition(getTileShiftedPositionFromIndices(%x + 1, %y + 1));
	$targetIndicators.add(%targetIndicatorAnimation);
}
function autoTargetEnemyUnit (%delay)
{//echo("autoTargetEnemyUnit()");
	if (isEventPending($autoTargetEvent))
	{
		cancel($autoTargetEvent);
	}
	else
	{
		$autoTargetEvent = schedule(%delay, 0, "setAutoTargetPending");
	}
}
function setAutoTargetPending ()
{//echo("setAutoTargetPending()");
	// Auto-Target any enemy unit in range/sight!
	%targetIndices = getAutoTarget($activeCharacter);
	if (isObject($autoTargetAnimation)	&&
		($autoTargetAnimation.xIndex != getWord(%targetIndices, 0)	&&
		 $autoTargetAnimation.yIndex != getWord(%targetIndices, 1)))
	{
		$autoTargetAnimation.safeDelete();
		MonsterTargetPanel.setVisible(false);
		return;
	}
	if (!setAutoTarget($activeCharacter))
	{
		MonsterTargetPanel.setVisible(false);
		if ($AutoLock)
		{
			sceneWindow2D.dismount();
			sceneWindow2D.mount($playerSquad[$activeCharacter], $sceneWindow2DxOffset SPC $sceneWindow2DyOffset, $cameraMountForce, false);
		}
	}
	else
	{
		MonsterTargetPanel.setVisible(true);
	}
}
function setAutoTarget (%characterIndex)
{//echo("setAutoTarget()");
	%autoTarget = getAutoTarget(%characterIndex);
	if (%autoTarget !$= "-9999 -9999")
	{
		if ($generateTutorialLevel)
		{
			if ($tutorialSection == 3)
			{
				tutorialMessage4();
			}
		}
		createAutoTargetAnimation(getWord(%autoTarget, 0), getWord(%autoTarget, 1));
		return true;
	}
	return false;
}
function getAutoTarget (%characterIndex)
{//echo("getAutoTarget()");
	$targetIndicators = new SimSet();
	%xIndex = $playerSquad[%characterIndex].xIndex;
	%yIndex = $playerSquad[%characterIndex].yIndex;
	%range = ($playerSquad[%characterIndex].weapon).range;
	if (isLargeUnit($playerSquad[%characterIndex]))
	{
		%range = $HulkFlamethrowerRange;
	}
	%monster = null;
	for (%i = 0; %i < %range; %i++)
	{
		switch ($playerSquad[%characterIndex].facing)
		{
			case $northDirection:	%yIndex--;
			case $southDirection:	%yIndex++;
			case $eastDirection:	%xIndex++;
			case $westDirection:	%xIndex--;
		}
		if (canFireThrough(%xIndex, %yIndex))
		{
			%monsterIndex = monsterAtPosition(%xIndex, %yIndex);
			if (%monsterIndex != -1	&&
				$fogOfWar.isClear(%xIndex, %yIndex))
			{
				//setIndividualDialoguePopup($playerSquad[%characterIndex], "Enemy in range.", "dialogue");
				%i = %range;
				%monster = $monsterList.getObject(%monsterIndex);
				%monster.hasBeenSpotted = true;
				%monster.isActive = true;
			}
		}
		else
		{
			return "-9999 -9999";
		}
	}
	if (isObject(%monster))
	{
		fireControlFloatingText(%monster);
		if ($AutoLock)
		{
			sceneWindow2D.dismount();
			sceneWindow2D.mount(%monster, $sceneWindow2DxOffset SPC $sceneWindow2DyOffset, $autoTargetCameraMountForce, false);
		}
		setMonsterTargetPanelInfo(%monster);
		MonsterTargetPanel.setVisible(true);
		return %monster.xIndex SPC %monster.yIndex;
	}
	return "-9999 -9999";
}
function updateMonsterTargetPanelInfo ()
{//echo("updateMonsterTargetPanelInfo()");
	%characterIndex = $activeCharacter;
	%xIndex = $playerSquad[%characterIndex].xIndex;
	%yIndex = $playerSquad[%characterIndex].yIndex;
	%range = ($playerSquad[%characterIndex].weapon).range;
	if (isLargeUnit($playerSquad[%characterIndex]))
	{
		%range = $HulkFlamethrowerRange;
	}
	%monster = null;
	for (%i = 0; %i < %range; %i++)
	{
		switch ($playerSquad[%characterIndex].facing)
		{
			case $northDirection:	%yIndex--;
			case $southDirection:	%yIndex++;
			case $eastDirection:	%xIndex++;
			case $westDirection:	%xIndex--;
		}
		if (canFireThrough(%xIndex, %yIndex))
		{
			%monsterIndex = monsterAtPosition(%xIndex, %yIndex);
			if (%monsterIndex != -1	&&
				$fogOfWar.isClear(%xIndex, %yIndex))
			{//setIndividualDialoguePopup($playerSquad[%characterIndex], "Enemy in range.", "dialogue");
				%i = %range;
				%monster = $monsterList.getObject(%monsterIndex);
				%monster.hasBeenSpotted = true;
				%monster.isActive = true;
			}
		}
	}
	if (isObject(%monster))
	{
		fireControlFloatingText(%monster);
		setMonsterTargetPanelInfo(%monster);
		MonsterTargetPanel.setVisible(true);
	}
}
function setMonsterTargetPanelInfo (%monster)
{//echo("setMonsterTargetPanelInfo()");
return;		// Disabled for now!
	MonsterTargetSidePortrait.setBitmap("");
	MonsterTargetHealthText.setText(%monster.currentHealth @ "/" @ %monster.maxHealth);
	%healthBarHeight = 13;
	%healthBarWidth = %monster.currentHealth / %monster.maxHealth;
	%healthBarWidth = mFloor(%healthBarWidth * 108);
	MonsterTargetHealthBar.extent = %healthBarWidth SPC %healthBarHeight;
	for (%i = 1; %i <= 7; %i++)
	{
		if (%i <= %monster.currentActionPoints)
		{
			(MonsterTargetActionBlock @ %i).setBitmap("../../../Sprites/1/assets/images/UI/explore/actionBlock");
			(MonsterTargetActionBlock @ %i).setVisible(true);
		}
		else if (%i > %monster.maxActionPoints)
		{
			(MonsterTargetActionBlock @ %i).setVisible(false);
		}
		else
		{
			(MonsterTargetActionBlock @ %i).setBitmap("../../../Sprites/1/assets/images/UI/explore/actionBlockEmpty");
			(MonsterTargetActionBlock @ %i).setVisible(true);
		}
	}
	MonsterTargetPanel.position = "16 514";
}
function setTargetIndicators (%characterIndex)
{//echo("setTargetIndicators()");//echo("%characterIndex =" SPC %characterIndex);
	fireControlFloatingText();
	//clearTargetIndicators(false);
	%character = $playerSquad[%characterIndex];
	if (!isObject(%character))
	{
		return false;
	}
	if (isObject($targetIndicators))
	{
		clearTargetIndicators(false);
	}
	else
	{
		$targetIndicators = new SimSet();
	}
	pruneLevelSceneGraphOfTargetIndicators();
	%xIndex = %character.xIndex;
	%yIndex = %character.yIndex;
	%x = %xIndex;
	%y = %yIndex;
	%range = (%character.weapon).range;
	if (isLargeUnit(%character))
	{
		%range = $HulkFlamethrowerRange;
	}
	switch (%character.facing)
	{
		case $northDirection:
			// Determine closest North target, if any.
			for (%i = 0; %i < %range; %i++)
			{
				// Move the y index up (north) one tile.
				%y--;
				
				// Bound check.
				if (%y >= 0)
				{
					// Make sure the tile is not an obstacle.
					if (canFireThrough(%x, %y))
					{//return;	// GOOD TILL THIS POINT!
						// Check if a monster is on this tile position.
						%monsterIndex = monsterAtPosition(%x, %y);
						// Check if the monster is at the current indices.
						if (%monsterIndex != -1)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);//(%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
							// Found the first target - leave this loop.
							%i = %range;
				
							// Check if this character is a Grenadier class.
							if (%character.characterClass == 2	&&
								!isLargeUnit(%character))
							{
								// Set aoe target indicators.
								setAoeTargetIndicators(%x, %y);
							}
							
							// Grab monster.
							%monster = $monsterList.getObject(%monsterIndex);
							// Flag monster as spotted.
							%monster.hasBeenSpotted = true;
							// Flag monster as active
							%monster.isActive = true;
							// Show monster through fog of war.
							$fogOfWar.schedule($showAndTellWait, "showAndTellMonsters");
							
							// Set fire control floating text.
							fireControlFloatingText(%monster);
						}
						else
						{
							%freeIndicator = null;
							// Create a static sprite target indicator at this tile position.
							if (%character.characterClass == 2	&&
								%i == %range - 1				&&
								!isLargeUnit(%character))
							{
								%freeIndicator = createTargetIndicatorAnimation();
								setAoeTargetIndicators(%x, %y);
							}
							else
							{
								%freeIndicator = createFreeIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%freeIndicator, getTileShiftedPositionFromIndices(%x, %y), (%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%freeIndicator);
						}
					}
					else
					{//echo("NORTH obstacle detected!");
					//echo("$mazeRoom[" @ %x @ ", " @ %y @ "].status =" SPC $mazeRoom[%x, %y].status);
						// Grenadier class weapons can destroy obstacles!
						if (%character.characterClass == 2)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
						}
						// Obstacle! Stop tracing fire path.
						%i = %range;
					}
				//return; // GOOD TILL THIS POINT!
					// Check if fog of war is on this tile.
					if ($fogOfWar.tileHasFog(%x, %y))
					{
						// Temporarily clear fog-of-war on this tile!
						temporaryUnfog(%x, %y);
						// Tag tile as being fogged later.
						$tilesToBeFogged.add(new ScriptObject(){xIndex = %x; yIndex = %y;});
					}
				}
			}
			// Reset the y index.
			%y = %yIndex;
			//echo("%x =" SPC %x);
			//echo("%y =" SPC %y);
	
		case $southDirection:
			// Determine closest South target, if any.
			for (%i = 0; %i < %range; %i++)
			{
				// Move the y index down (south) one tile.
				%y++;
				
				// Bound check.
				if (%y < $mazeDimension)
				{
					// Make sure the tile is not an obstacle.
					if (canFireThrough(%x, %y))
					{
						// Check if a monster is on this tile position.
						%monsterIndex = monsterAtPosition(%x, %y);
							
						// Check if the monster is at the current indices.
						if (%monsterIndex != -1)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);//(%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
							// Found the first target - leave this loop.
							%i = %range;
				
							// Check if this character is a Grenadier class.
							if (%character.characterClass == 2	&&
								!isLargeUnit(%character))
							{
								// Set aoe target indicators.
								setAoeTargetIndicators(%x, %y);
							}				
							
							// Grab monster.
							%monster = $monsterList.getObject(%monsterIndex);
							// Flag monster as spotted.
							%monster.hasBeenSpotted = true;
							// Flag monster as active
							%monster.isActive = true;
							// Show monster through fog of war.
							$fogOfWar.schedule($showAndTellWait, "showAndTellMonsters");
							
							// Set fire control floating text.
							fireControlFloatingText(%monster);
						}
						else
						{
							%freeIndicator = null;
							// Create a static sprite target indicator at this tile position.
							if (%character.characterClass == 2	&&
								%i == %range - 1				&&
								!isLargeUnit(%character))
							{
								%freeIndicator = createTargetIndicatorAnimation();
								setAoeTargetIndicators(%x, %y);
							}
							else
							{
								%freeIndicator = createFreeIndicatorAnimation();
							}
							if (%freeIndicator != null)
							{
								// Schedule moving this animation into position.
								scheduleObjectMove(%freeIndicator, getTileShiftedPositionFromIndices(%x, %y), (%i + 1) * $targetDelay);
								// Add the target sprite to the target indicators sim set.
								$targetIndicators.add(%freeIndicator);
							}
						}
					}
					else
					{//echo("SOUTH obstacle detected!");
					//echo("$mazeRoom[" @ %x @ ", " @ %y @ "].status =" SPC $mazeRoom[%x, %y].status);
						// Grenadier class weapons can destroy obstacles!
						if (%character.characterClass == 2)
						{
							// Create target indicator animation.
							%targetIndicatorAnimation = createTargetIndicatorAnimation();
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
						}
						// Obstacle! Stop tracing fire path.
						%i = %range;
					}
					
					// Check if fog of war is on this tile.
					if ($fogOfWar.tileHasFog(%x, %y))
					{
						// Temporarily clear fog-of-war on this tile!
						temporaryUnfog(%x, %y);
						
						// Tag tile as being fogged later.
						$tilesToBeFogged.add(new ScriptObject(){xIndex = %x; yIndex = %y;});
					}
				}
			}
			// Reset the y index.
			%y = %yIndex;
			//echo("%x =" SPC %x);
			//echo("%y =" SPC %y);
	
		case $westDirection:
			// Determine closest West target, if any.
			for (%i = 0; %i < %range; %i++)
			{
				// Move the x index left (west) one tile.
				%x--;
				
				// Bound check.
				if (%x >= 0)
				{
					// Make sure the tile is not an obstacle.
					if (canFireThrough(%x, %y))
					{
						// Check if a monster is on this tile position.
						%monsterIndex = monsterAtPosition(%x, %y);
							
						// Check if the monster is at the current indices.
						if (%monsterIndex != -1)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);//(%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
							// Found the first target - leave this loop.
							%i = %range;
				
							// Check if this character is a Grenadier class.
							if (%character.characterClass == 2	&&
								!isLargeUnit(%character))
							{
								// Set aoe target indicators.
								setAoeTargetIndicators(%x, %y);
							}				
							
							// Grab monster.
							%monster = $monsterList.getObject(%monsterIndex);
							// Flag monster as spotted.
							%monster.hasBeenSpotted = true;
							// Flag monster as active
							%monster.isActive = true;
							// Show monster through fog of war.
							$fogOfWar.schedule($showAndTellWait, "showAndTellMonsters");
							
							// Set fire control floating text.
							fireControlFloatingText(%monster);
						}
						else
						{
							%freeIndicator = null;
							// Create a static sprite target indicator at this tile position.
							if (%character.characterClass == 2	&&
								%i == %range - 1				&&
								!isLargeUnit(%character))
							{
								%freeIndicator = createTargetIndicatorAnimation();
								setAoeTargetIndicators(%x, %y);
							}
							else
							{
								%freeIndicator = createFreeIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%freeIndicator, getTileShiftedPositionFromIndices(%x, %y), (%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%freeIndicator);
						}
					}
					else
					{//echo("WEST obstacle detected!");
					//echo("$mazeRoom[" @ %x @ ", " @ %y @ "].status =" SPC $mazeRoom[%x, %y].status);
						// Grenadier class weapons can destroy obstacles!
						if (%character.characterClass == 2)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
						}
						// Obstacle! Stop tracing fire path.
						%i = %range;
					}
					
					// Check if fog of war is on this tile.
					if ($fogOfWar.tileHasFog(%x, %y))
					{
						// Temporarily clear fog-of-war on this tile!
						temporaryUnfog(%x, %y);
						// Tag tile as being fogged later.
						$tilesToBeFogged.add(new ScriptObject(){xIndex = %x; yIndex = %y;});
					}
				}
			}
			// Reset the x index.
			%x = %xIndex;
			//echo("%x =" SPC %x);
			//echo("%y =" SPC %y);
	
		case $eastDirection:
			// Determine closest East target, if any.
			for (%i = 0; %i < %range; %i++)
			{
				// Move the x index right (east) one tile.
				%x++;
				
				// Bound check.
				if (%x < $mazeDimension)
				{
					// Make sure the tile is not an obstacle.
					if (canFireThrough(%x, %y))
					{
						// Check if a monster is on this tile position.
						%monsterIndex = monsterAtPosition(%x, %y);
							
						// Check if the monster is at the current indices.
						if (%monsterIndex != -1)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);//(%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
							// Found the first target - leave this loop.
							%i = %range;
				
							// Check if this character is a Grenadier class.
							if (%character.characterClass == 2	&&
								!isLargeUnit(%character))
							{
								// Set aoe target indicators.
								setAoeTargetIndicators(%x, %y);
							}				
							
							// Grab monster.
							%monster = $monsterList.getObject(%monsterIndex);
							// Flag monster as spotted.
							%monster.hasBeenSpotted = true;
							// Flag monster as active
							%monster.isActive = true;
							// Show monster through fog of war.
							$fogOfWar.schedule($showAndTellWait, "showAndTellMonsters");
							
							// Set fire control floating text.
							fireControlFloatingText(%monster);
						}
						else
						{
							%freeIndicator = null;
							// Create a static sprite target indicator at this tile position.
							if (%character.characterClass == 2	&&
								%i == %range - 1				&&
								!isLargeUnit(%character))
							{
								%freeIndicator = createTargetIndicatorAnimation();
								setAoeTargetIndicators(%x, %y);
							}
							else
							{
								%freeIndicator = createFreeIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%freeIndicator, getTileShiftedPositionFromIndices(%x, %y), (%i + 1) * $targetDelay);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%freeIndicator);
						}
					}
					else
					{//echo("EAST obstacle detected!");
					//echo("$mazeRoom[" @ %x @ ", " @ %y @ "].status =" SPC $mazeRoom[%x, %y].status);
						// Grenadier class weapons can destroy obstacles!
						if (%character.characterClass == 2)
						{
							// Create target indicator animation.
							if (%i == %range - 1)
							{
								%targetIndicatorAnimation = createMaxRangeIndicatorAnimation();
							}
							else
							{
								%targetIndicatorAnimation = createTargetIndicatorAnimation();
							}
							// Schedule moving this animation into position.
							scheduleObjectMove(%targetIndicatorAnimation, getTileShiftedPositionFromIndices(%x, %y), 0);
							// Add the target sprite to the target indicators sim set.
							$targetIndicators.add(%targetIndicatorAnimation);
						}
						// Obstacle! Stop tracing fire path.
						%i = %range;
					}
					
					// Check if fog of war is on this tile.
					if ($fogOfWar.tileHasFog(%x, %y))
					{
						// Temporarily clear fog-of-war on this tile!
						temporaryUnfog(%x, %y);
						// Tag tile as being fogged later.
						$tilesToBeFogged.add(new ScriptObject(){xIndex = %x; yIndex = %y;});
					}
				}
			}
	}

	// If we found a monster above...
	if (isObject(%monster))
	{
		// ... mount the camera to the monster.
		if ($AutoLock)
		{
			sceneWindow2D.dismount();
			sceneWindow2D.mount(%monster, $sceneWindow2DxOffset SPC $sceneWindow2DyOffset, $cameraMountForce, false);
		}
		// Set Monster Target Panel.
		/*MonsterTargetSidePortrait.setBitmap("");
		MonsterTargetHealthText.setText(%monster.currentHealth @ "/" @ %monster.maxHealth);
		// Set health bar.
		%healthBarHeight = 13;
		%healthBarWidth = %monster.currentHealth / %monster.maxHealth;
		%healthBarWidth = mFloor(%healthBarWidth * 108);
		MonsterTargetHealthBar.extent = %healthBarWidth SPC %healthBarHeight;
		// Set action blocks' bitmaps.
		for (%i = 1; %i <= 7; %i++)
		{
			if (%i <= %monster.currentActionPoints)
			{//echo("set ap" SPC %i);
				(MonsterTargetActionBlock @ %i).setBitmap("../../../Sprites/1/assets/images/UI/explore/actionBlock");
				(MonsterTargetActionBlock @ %i).setVisible(true);
			}
			else if (%i > %monster.maxActionPoints)
			{
				(MonsterTargetActionBlock @ %i).setVisible(false);
			}
			else
			{
				(MonsterTargetActionBlock @ %i).setBitmap("../../../Sprites/1/assets/images/UI/explore/actionBlockEmpty");
				(MonsterTargetActionBlock @ %i).setVisible(true);
			}
		}
		%bitmap = "../../../Sprites/1/assets/images/UI/Explore/Portraits/Enemy-Portraits-" @ %monster.name;
		MonsterTargetSidePortrait.setBitmap(%bitmap);*/
		MonsterTargetPanel.position = "16 514";
		MonsterTargetPanel.setVisible(true);
	}
}
function getValidNeighborTargets (%x1, %y1)
{//echo("getValidNeighborTargets()");
	// Check monster list.
	for (%i = 0; %i < $monsterList.getCount(); %i++)
	{//echo("%i =" SPC %i);
		%monster = $monsterList.getObject(%i);
		if (isObject(%monster)		&&
			%x1 == %monster.xIndex	&&
			%y1 == %monster.yIndex)
		{
			$validNeighboringTargets.add(%monster);
			//$validNeighboringTargetsIndex.add(%index);
			// Get out - monsters are never stacked!
			%i = $monsterList.getCount();
		}
	}
	// Check squad members.
	for (%i = 0; %i < $maxSquadSize; %i++)
	{//echo("%i =" SPC %i);
		%squadMember = $playerSquad[%i];
		if (isObject(%squadMember)		&&
			%x1 == %squadMember.xIndex	&&
			%y1 == %squadMember.yIndex)
		{
			$validNeighboringTargets.add(%squadMember);
			// Get out - squad members are never stacked!
			%i = $maxSquadSize;
		}
	}
}
function scoutFirePathDamage (%monsterIndex, %direction)			// Need the direction for Scout penetration shots!
{//echo("scoutFirePathDamage()");
	// Grab the squad member who fired.
	%squadMember = $playerSquad[$activeCharacter];
	
	// Grab the monster taking damage.
	%monster = $monsterList.getObject(%monsterIndex);
	// For Ricochet talent below!
	%originalMonster = %monster;
	
	// Scout secondary and tertiary penetration targets take less damage and have less chance to be hit.
	// Scout penetration attacks still do no harm friendlies.
	//%damage = getRandom(4, 10);		// 4-10 damage roll.
	//%attackRating = 75;				// 80% chance to hit.
	%weapon = %squadMember.weapon;
	%calculatedDamageAndAccuracy = calculateDamageAndAccuracy($activeCharacter, %monster);
	%damage = getWord(%calculatedDamageAndAccuracy, 0);
	%attackRating = getWord(%calculatedDamageAndAccuracy, 1);
	
	// Grab the monster tile indeices.
	%xIndex = %monster.xIndex;
	%yIndex = %monster.yIndex;
	%secondTarget = null;
	%thirdTarget = null;
	
	// Compose secondary and tertiary tiles based on direction.
	switch (%direction)
	{
		case $northDirection:	// Up.
			// Second target.
			%xIndex2 = %xIndex;
			%yIndex2 = %yIndex - 1;
			// Bounds check.
			if (%xIndex2 >= 0				&&
				%xIndex2 < $mazeDimension	&&
				%yIndex2 >= 0				&&
				%yIndex2 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex2 == %monster.xIndex	&&
						%yIndex2 == %monster.yIndex)
					{
						%secondTarget = %monster;
						%secondIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
			// Third target.
			%xIndex3 = %xIndex2;
			%yIndex3 = %yIndex2 - 1;
			// Bounds check.
			if (%xIndex3 >= 0				&&
				%xIndex3 < $mazeDimension	&&
				%yIndex3 >= 0				&&
				%yIndex3 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex3 == %monster.xIndex	&&
						%yIndex3 == %monster.yIndex)
					{
						%thirdTarget = %monster;
						%thirdIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
		
		case $southDirection:	// Down.
			// Second target.
			%xIndex2 = %xIndex;
			%yIndex2 = %yIndex + 1;
			// Bounds check.
			if (%xIndex2 >= 0				&&
				%xIndex2 < $mazeDimension	&&
				%yIndex2 >= 0				&&
				%yIndex2 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex2 == %monster.xIndex	&&
						%yIndex2 == %monster.yIndex)
					{
						%secondTarget = %monster;
						%secondIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
			// Third target.
			%xIndex3 = %xIndex2;
			%yIndex3 = %yIndex2 + 1;
			// Bounds check.
			if (%xIndex3 >= 0				&&
				%xIndex3 < $mazeDimension	&&
				%yIndex3 >= 0				&&
				%yIndex3 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex3 == %monster.xIndex	&&
						%yIndex3 == %monster.yIndex)
					{
						%thirdTarget = %monster;
						%thirdIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
		
		case $westDirection:	// Left.
			// Second target.
			%xIndex2 = %xIndex - 1;
			%yIndex2 = %yIndex;
			// Bounds check.
			if (%xIndex2 >= 0				&&
				%xIndex2 < $mazeDimension	&&
				%yIndex2 >= 0				&&
				%yIndex2 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex2 == %monster.xIndex	&&
						%yIndex2 == %monster.yIndex)
					{
						%secondTarget = %monster;
						%secondIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
			// Third target.
			%xIndex3 = %xIndex2 - 1;
			%yIndex3 = %yIndex2;
			// Bounds check.
			if (%xIndex3 >= 0				&&
				%xIndex3 < $mazeDimension	&&
				%yIndex3 >= 0				&&
				%yIndex3 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex3 == %monster.xIndex	&&
						%yIndex3 == %monster.yIndex)
					{
						%thirdTarget = %monster;
						%thirdIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
		
		case $eastDirection:	// Right.
			// Second target.
			%xIndex2 = %xIndex + 1;
			%yIndex2 = %yIndex;
			// Bounds check.
			if (%xIndex2 >= 0				&&
				%xIndex2 < $mazeDimension	&&
				%yIndex2 >= 0				&&
				%yIndex2 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex2 == %monster.xIndex	&&
						%yIndex2 == %monster.yIndex)
					{
						%secondTarget = %monster;
						%secondIndex = %i;
						// Get out - monsters are never stacked!
						%monsterIndex = %i;
						%i = $monsterList.getCount();
					}
				}
			}
			// Third target.
			%xIndex3 = %xIndex2 + 1;
			%yIndex3 = %yIndex2;
			// Bounds check.
			if (%xIndex3 >= 0				&&
				%xIndex3 < $mazeDimension	&&
				%yIndex3 >= 0				&&
				%yIndex3 < $mazeDimension)
			{
				// Check monster list.
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{//echo("%i =" SPC %i);
					%monster = $monsterList.getObject(%i);
					if (isObject(%monster)		&&
						%xIndex3 == %monster.xIndex	&&
						%yIndex3 == %monster.yIndex)
					{
						%thirdTarget = %monster;
						%thirdIndex = %i;
						// Get out - monsters are never stacked!
						%i = $monsterList.getCount();
					}
				}
			}
	}
	
	// Ricochet the shot!
	/*if ($squadRicochet)
	{
		// Set Ricochet to false.
		//$squadRicochet = false;
		// NOTE: function continueTraceFirePath() parameter list outdated!
		//continueTraceFirePath(%monster, %originalMonster.xIndex, %originalMonster.yIndex, $northDirection, ($playerSquad[1].weapon).range);
		//continueTraceFirePath(%monster, %originalMonster.xIndex, %originalMonster.yIndex, $southDirection, ($playerSquad[1].weapon).range);
		//continueTraceFirePath(%monster, %originalMonster.xIndex, %originalMonster.yIndex, $eastDirection, ($playerSquad[1].weapon).range);
		//continueTraceFirePath(%monster, %originalMonster.xIndex, %originalMonster.yIndex, $westDirection, ($playerSquad[1].weapon).range);
	}*/
	
	// Attack second target.
	if (isObject(%secondTarget))
	{
		%monster = %secondTarget;
		
		// Roll a random hit number.
		%hitRoll = getRandom(1, 100);
		
		// Hit chance.
		if (%hitRoll <= %attackRating)
		{
			// Return false - a monster is blocking the way!
			if (%monster.stasis)
			{
				// Update event log.
				updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots at " @ $monsterColor @ %monster.name @ $defaultColor @ "!");
				updateEventText($whiteColor @ "Unit is in stasis!");
				// Play invalid action sound effect.
				playAudio("Audio:invalidAction", EffectsAudioVolume.value);
				// Leave - a stasis unit is blocking the way!
				return;
			}
			
			// Update event log.
			updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots" SPC $monsterColor @ %monster.name SPC $defaultColor @ "for" SPC $damageColor @ %damage SPC "damage" @ $defaultColor @ "!");
			
			// Deal 6 damage to the monster!
			if (%monster.currentHealth <= %damage)
			{
				// Set new monster health.
				%monster.currentHealth = 0;
			}
			else
			{
				// Set new monster health.
				%monster.currentHealth -= %damage;
			}
			if (%monster.facing == %direction)
			{//echo("facing same direction - CRIT!");
				%damage *= 2;
			}
			// Blood splatter when monsters get hit.
			monsterHit(%secondIndex, %damage, %direction, getWeaponDamageType($activeCharacter));
		}
		// Missed!
		else
		{
			// Handle floating text.
			scrollFloatingText(%monster, "miss", "miss");
		
			// Wake the monster upon being fired at!
			monsterAttackedReaction($monsterList.getObject(%monsterIndex));
			
			// Update event log.
			updateEventText($squadColor @ %squadMember.name SPC $greyColor @ "misses" SPC $monsterColor @ %monster.name @ $defaultColor @ "!");
		}
	}
	else
	{
		// Leave - there will also be no third target.
		return;
	}
	
	// Attack third target.
	if (isObject(%thirdTarget))
	{
		%monster = %thirdTarget;
		
		// Roll a random hit number.
		%hitRoll = getRandom(1, 100);
		
		// Hit chance.
		if (%hitRoll <= %attackRating)
		{
			// Return false - a monster is blocking the way!
			if (%monster.stasis)
			{
				// Update event log.
				updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots at " @ $monsterColor @ %monster.name @ $defaultColor @ "!");
				updateEventText($whiteColor @ "Unit is in stasis!");
				// Play invalid action sound effect.
				playAudio("Audio:invalidAction", EffectsAudioVolume.value);
				// Leave - a stasis unit is blocking the way!
				return;
			}
			
			// Update event log.
			updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots" SPC $monsterColor @ %monster.name SPC $defaultColor @ "for" SPC $damageColor @ %damage SPC "damage" @ $defaultColor @ "!");
			
			if (%monster.facing == %direction)
			{//echo("facing same direction - CRIT!");
				%damage *= 2;
			}
			// Deal 6 damage to the monster!
			if (%monster.currentHealth <= %damage)
			{
				// Set new monster health.
				%monster.currentHealth = 0;
			}
			else
			{
				// Set new monster health.
				%monster.currentHealth -= %damage;
			}
			
			// Blood splatter when monsters get hit.
			monsterHit(%thirdIndex, %damage, %direction, getWeaponDamageType($activeCharacter));
		}
		// Missed!
		else
		{
			// Handle floating text.
			scrollFloatingText(%monster, "miss", "miss");
			
			// Wake the monster upon being fired at!
			monsterAttackedReaction($monsterList.getObject(%monsterIndex));
			
			// Update event log.
			updateEventText($squadColor @ %squadMember.name SPC $greyColor @ "misses" SPC $monsterColor @ %monster.name @ $defaultColor @ "!");
		}
	}
	else
	{
		// Leave - there will also be no third target.
		return;
	}
}
function gunshipDestroyTerrain (%xIndex, %yIndex)
{//echo("gunshipDestroyTerrain()");
	%wallsDestroyed = 0;
	if (isObject($mazeRoom[%xIndex, %yIndex - 1])	&&		// North.
		%xIndex		!= $stairsXIndex				&&
		%yIndex - 1 != $stairsYIndex)
	{
		destroyTerrainTile(%xIndex, %yIndex - 1);
		%wallsDestroyed++;
	}
	if (isObject($mazeRoom[%xIndex, %yIndex + 1])	&&		// South.
		%xIndex		!= $stairsXIndex				&&
		%yIndex + 1 != $stairsYIndex)
	{
		destroyTerrainTile(%xIndex, %yIndex + 1);
		%wallsDestroyed++;
	}
	if (isObject($mazeRoom[%xIndex + 1, %yIndex])	&&		// East.
		%xIndex + 1	!= $stairsXIndex				&&
		%yIndex 	!= $stairsYIndex)
	{
		destroyTerrainTile(%xIndex + 1, %yIndex);
		%wallsDestroyed++;
	}
	if (isObject($mazeRoom[%xIndex - 1, %yIndex])	&&		// West.
		%xIndex - 1	!= $stairsXIndex				&&
		%yIndex 	!= $stairsYIndex)
	{
		destroyTerrainTile(%xIndex - 1, %yIndex);
		%wallsDestroyed++;
	}
	return %wallsDestroyed;
}
function grenadierDestroyTerrain (%xIndex, %yIndex)
{//echo("grenadierDestroyTerrain()");
	if (!isObject($mazeRoom[%xIndex, %yIndex]))
	{
		return;
	}
	if (%xIndex == $stairsXIndex	&&
		%yIndex == $stairsYIndex)
	{
		return;
	}
	%roomPositionStatus = $mazeRoom[%xIndex, %yIndex].status;	// 0 and -2 are smashable walls.
	%wallDestruction = 0;
	if (%roomPositionStatus == 0)			// Destructible.
	{
		%wallDestruction = 100;
	}
	else if	(%roomPositionStatus == -2	||		// "Unsmashable" but destructible!
			 %roomPositionStatus == -3	||
			 %roomPositionStatus == -4)
	{
		%wallDestruction = $grenadeDestroyDoorChance;
	}
	if (%wallDestruction > 0)
	{
		if (getRandom(1, 100) <= %wallDestruction)
		{
			destroyTerrainTile(%xIndex, %yIndex);
			updateEventText($squadColor @ $playerSquad[$activeCharacter].name @ $friendlyActionColor @ " destroyed " @ $greyColor @ "wall" @ $defaultColor @ ".");
		}
	}
}
function grenadierFirePathDamage (%monsterIndex, %direction, %character)
{//echo("grenadierFirePathDamage()");
	// Grab the squad member who fired.
	%squadMember = %character;
	%characterIndex = getCharacterIndex(%squadMember);
	
	// Grab the monster taking damage.
	%monster = $monsterList.getObject(%monsterIndex);
	
	// Grenadier inputs.
	%calculatedDamageAndAccuracy = calculateDamageAndAccuracy(%characterIndex, %monster);
	%damage = getWord(%calculatedDamageAndAccuracy, 0);
	%attackRating = getWord(%calculatedDamageAndAccuracy, 1);
	
	// Roll a random hit number.
	%hitRoll = getRandom(1, 100);
	
	// If the character is a Grenadier with the Minesweeper talent.
	if (%squadMember.characterClass == 2	&&
		getWord(%squadMember.talents, 3) == 1)
	{
		// Destroy nearby landmines.
		%radius = 1;
		destroyMinesInRadius(%monster.xIndex, %monster.yIndex, %radius);
	}
	
	// Eight (8) neighboring tiles that the Grenadier's aoe attack can hit.
	// x5, x5 is the middle (initial) attack target.
	%x5 = %monster.xIndex;
	%y5 = %monster.yIndex;
	
	// Compose the other positions to check.
	$validNeighboringTargets = new SimSet();
	
	// Up left.
	%x1 = %x5 - 1;
	%y1 = %y5 - 1;
	// Bounds check.
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x1, %y1);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x1, %y1);
	}
	// Up middle.
	%x2 = %x5;
	%y2 = %y5 - 1;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x2, %y2);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x2, %y2);
	}
	// Up right.
	%x3 = %x5 + 1;
	%y3 = %y5 - 1;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x3, %y3);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x3, %y3);
	}
	
	// Middle left.
	%x4 = %x5 - 1;
	%y4 = %y5;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x4, %y4);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x4, %y4);
	}
	// Middle right.
	%x6 = %x5 + 1;
	%y6 = %y5;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x6, %y6);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x6, %y6);
	}
	
	// Down left.
	%x7 = %x5 - 1;
	%y7 = %y5 + 1;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x7, %y7);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x7, %y7);
	}
	// Down middle.
	%x8 = %x5;
	%y8 = %y5 + 1;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x8, %y8);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x8, %y8);
	}
	// Down right.
	%x9 = %x5 + 1;
	%y9 = %y5 + 1;
	if (%x1 >= 0				&&
		%x1 < $mazeDimension	&&
		%y1 >= 0				&&
		%y1 < $mazeDimension)
	{
		// Get all valid neighboring targets.
		getValidNeighborTargets(%x9, %y9);
		
		// Perform Grenadier terrain destruction action.
		grenadierDestroyTerrain(%x9, %y9);
	}
	
	for (%i = 0; %i < $validNeighboringTargets.getCount(); %i++)
	{//echo("$validNeighboringTargets.getCount() =" SPC $validNeighboringTargets.getCount());
		// Get the target in the neighbor list.
		%monster = $validNeighboringTargets.getObject(%i);
		//echo("%damage =" SPC %damage);
		
		// If the character is a Grenadier with the Smart Bombs talent.
		if (%squadMember.characterClass == 2		&&
			getWord(%squadMember.talents, 7) == 1	&&
			isSquadMember(%monster))
		{//echo("Grenadier has Smart Bombs and this target is a squad member! Removing aoe target!");
			$validNeighboringTargets.remove(%monster);
			%i--;
		}
		else
		{
			// Chance to hit.
			if (%hitRoll <= %attackRating)
			{
				if (%monster.facing == %direction)
				{//echo("facing same direction - CRIT!");
					%damage *= 2;
				}
				updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "damages" SPC $monsterColor @ %monster.name SPC $defaultColor @ "for" SPC $damageColor @ %damage SPC "damage" @ $defaultColor @ "!");
				// Make sure aoe splash damage does not kill secondary targets!
				if (%monster.currentHealth <= %damage)
				{
					// Check for Grenadier class talent Annihilation.
					if (%squadMember.characterClass == 2	&&
						getWord(%squadMember.talents, 1) == 1)
					{//echo("Annihilation detected!");
						%monster.currentHealth = 0;
						updateEventText($squadColor @ %squadMember.name SPC $damageColor @ "kills" SPC $monsterColor @ %monster.name @ $defaultColor @ "!");
					}
					else
					{
						%monster.currentHealth = 1;			// AoE attacks cannot outright kill peripheral targets!
					}
				}
				else
				{
					%monster.currentHealth -= %damage;
				}
				monsterHit(getMonsterIndex(%monster), %damage, %direction, getWeaponDamageType(%characterIndex));
				if (isSquadMember(%monster))
				{
					objectHit(%monster, %damage);
					if (%monster.currentHealth <= 0)
					{
						%monster.currentHealth = 1;
					}
				}
			}
			else
			{
				scrollFloatingText(%monster, "miss", "miss");
				updateEventText($squadColor @ %squadMember.name SPC $greyColor @ "misses" SPC $monsterColor @ %monster.name @ $defaultColor @ "!");
			}
			monsterAttackedReaction(%monster);
		}
	}
}

// Damage monster(s) in fire path.
function firePathDamage (%currentCharacter, %monsterIndex, %direction)			// May need the direction in the future!
{//echo("firePathDamage()");
	%squadMember = $playerSquad[%currentCharacter];
	%monster = $monsterList.getObject(%monsterIndex);
	%weapon = $playerSquad[%currentCharacter].weapon;
	%damage = %weapon.damage * %weapon.ammo;
	%attackRating = %weapon.accuracy;
	if (%damage < 0)
	{
		%damage = 0;
	}
	%calculatedDamageAndAccuracy = calculateDamageAndAccuracy(%currentCharacter, %monster);
	%damage = getWord(%calculatedDamageAndAccuracy, 0);
	%attackRating = getWord(%calculatedDamageAndAccuracy, 1);
	if (%monster.name $= "Bruton"	&&
		%monster.facing == getReverseDirection(%direction))
	{
		%damage = 0;
		playAudio("Audio:deflection", EffectsAudioVolume.value);
	}
	%hitRoll = getRandom(1, 100);
	if (%hitRoll <= %attackRating)
	{
		if (%monster.stasis)
		{
			updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots at " @ $monsterColor @ %monster.name @ $defaultColor @ "!");
			updateEventText($whiteColor @ "Unit is in stasis!");
			playAudio("Audio:invalidAction", EffectsAudioVolume.value);
			return;
		}
		updateEventText($squadColor @ %squadMember.name SPC $friendlyActionColor @ "shoots" SPC $monsterColor @ %monster.name SPC $defaultColor @ "for" SPC $damageColor @ %damage SPC "damage" @ $defaultColor @ "!");
		applyPassiveTalentOnHitEffects(%currentCharacter, %monster);
		if (%monster.facing == %direction)
		{//echo("facing same direction - CRIT!");
			%damage *= 2;
		}
		if (%monster.currentHealth <= %damage)
		{
			%monster.currentHealth = 0;
		}
		else
		{
			%monster.currentHealth -= %damage;
		}
		if ($playerSquad[%currentCharacter].characterClass == 0)
		{
			//knockBackUnit(%monster, %direction);	// Shifted upward incorrectly!
			knockBackUnit(%monster, %squadMember.facing);
		}
		monsterHit(%monsterIndex, %damage, %direction, getWeaponDamageType(%currentCharacter));
	}
	else
	{
		scrollFloatingText(%monster, "miss", "miss");
		monsterAttackedReaction($monsterList.getObject(%monsterIndex));
		if ($playerSquad[%currentCharacter].weapon.projectileType == $weaponProjectileBallisticType)
		{//echo("ballistic projectile!");
			if ($playerSquad[%currentCharacter].characterClass != 2)		// Not for Grenadier class!
			{
				if ($playerSquad[%currentCharacter].characterClass == 3)
				{
					schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
					schedule($bulletImpactDelay + 75, 0, "playBulletImpactSfx");
					schedule($bulletImpactDelay + 150, 0, "playBulletImpactSfx");
				}
				else
				{
					schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
				}
			}
		}
		updateEventText($squadColor @ %squadMember.name SPC $greyColor @ "misses" SPC $monsterColor @ %monster.name @ $defaultColor @ "!");
	}
	// The Scout fire has a penetration effect, so if the active character is the Grenadier run special code.
	if ($playerSquad[%currentCharacter].characterClass == 1)
	{
		scoutFirePathDamage(%monsterIndex, %direction);
	}
	// The Grenadier has an aoe attack, so if the active character is the Grenadier run special code.
	else if ($playerSquad[%currentCharacter].characterClass == 2)
	{
		grenadierFirePathDamage(%monsterIndex, %direction, $playerSquad[%currentCharacter]);
		createGrenadierExplosion(($monsterList.getObject(%monsterIndex)).xIndex, ($monsterList.getObject(%monsterIndex)).yIndex, $playerSquad[%currentCharacter]);
	}
}
// Returns the index of the monster at the specified coordinates, -1 otherwise.
function monsterAtPosition (%x, %y)
{//echo("monsterAtPosition()");
	for (%i = 0; %i < $monsterList.getCount(); %i++)
	{//echo("Checking monster #" @ %i @ "!");
		%monster = $monsterList.getObject(%i);
		if (%x == %monster.xIndex &&
			%y == %monster.yIndex)
		{//echo("returning" SPC %i);
			return %i;
		}
	}
	return -1;
}
// Traces a fire path from a source to a blocked tile.
// Returns the first blocked tile encountered.
function traceFirePath (%unit, %tileSourceX, %tileSourceY, %direction, %range)
{//echo("traceFirePath()");echo("getCharacterIndex(%unit) =" SPC getCharacterIndex(%unit));echo("%tileSourceX SPC %tileSourceY =" SPC %tileSourceX SPC %tileSourceY);
	// Play rifle muzzle flash animation
	// NOTE: We should be playing the animation based on what weapon the current character is using!
	$rifleMuzzleFlashEvent = rifleMuzzleFlash(%direction);
	return continueTraceFirePath(%unit, %tileSourceX, %tileSourceY, %direction, %range);
}

// Returns true iff the tile can be fired/traced through!
function canFireThrough (%xIndex, %yIndex)
{//echo("canFireThrough()");echo(%xIndex SPC %yIndex);
	if (isObject($mazeRoom[%xIndex, %yIndex]))	// Is an object!
	{//echo("is object!");
		if ($mazeRoom[%xIndex, %yIndex].status == -4	||	// Open door!
			isTileStairs(%xIndex, %yIndex)				||	// Stairs tile!
			$mazeRoom[%xIndex, %yIndex].porthole		||	// Porthole!
			isSpaceTile(%xIndex, %yIndex))					// Space tile.
		{//echo("tile is open door, stairs, or space tile!");
			return true;
		}
		else
		{//echo("tile is obstacle!");
			return false;
		}
	}//echo("tile is not an object! can fire through!");
	return true;
}

// Waits on fire animation to complete process.
// NOTE: Currently never called except in itself!
//		 function continueTraceFirePath() needs the unit firing as the first parameter!
/*function waitFireAnimation (%tileSourceX, %tileSourceY, %direction, %range)
{//echo("waitFireAnimation()");
	if (isEventPending($rifleMuzzleFlashEvent))
	{
		schedule(100, 0, "waitFireAnimation", %tileSourceX, %tileSourceY, %direction, %range);
	}
	else
	{//echo("CONTINUE");
		schedule(100, 0, "continueTraceFirePath", %tileSourceX, %tileSourceY, %direction, %range);
	}
}*/

//--------------------------------------
// Class weapon projectile animations.
//--------------------------------------
function createShotAnimation (%characterIndex, %srcXIndex, %srcYIndex, %dstXIndex, %dstYIndex, %direction, %range, %projectileType)
{//echo("createShotAnimation()");echo("%characterIndex =" SPC %characterIndex);
	%shotAnimation = createFireProjectileAnimation(%characterIndex, %direction, %projectileType, $playerSquad[%characterIndex].characterClass);
	%speed = 60.0;
	switch ($playerSquad[%characterIndex].characterClass)
	{
		case 0:	%speed =  60.0;		// Leader.
		case 1:	%speed = 120.0;		// Scout.
		case 2:	%speed = 100.0;		// Grenadier.
		case 3:	%speed =  90.0;		// Support.
		case 4:	%speed =  60.0;		// Engineer.
	}
	%position = getTilePositionFromIndices(%srcXIndex, %srcYIndex);
	%xPos = getWord(%position, 0);
	%yPos = getWord(%position, 1);
	%effectiveRange = 0;
	if (%dstXIndex == -9999	&&
		%dstYIndex == -9999)
	{
		%effectiveRange = %range;
	}
	else
	{
		%effectiveRange = mRound(VectorDist(%srcXIndex SPC %srcYIndex, %dstXIndex SPC %dstYIndex));
	}
	%tileMagnitude = 4.0;
	switch (%direction)
	{
		case $northDirection:	%shotAnimation.moveTo(%xPos, %yPos - %effectiveRange * %tileMagnitude, %speed, true, false);
		case $southDirection:	%shotAnimation.moveTo(%xPos, %yPos + %effectiveRange * %tileMagnitude, %speed, true, false);
		case $eastDirection:	%shotAnimation.moveTo(%xPos + %effectiveRange * %tileMagnitude, %yPos, %speed, true, false);
		case $westDirection:	%shotAnimation.moveTo(%xPos - %effectiveRange * %tileMagnitude, %yPos, %speed, true, false);
	}
	%lifetimeModifier = 0.10;
	switch ($playerSquad[%characterIndex].characterClass)
	{
		case 0:	%lifetimeModifier = 0.10;
		case 1:	%lifetimeModifier = 0.07;
		case 2:	%lifetimeModifier = 0.11;
		case 3:	%lifetimeModifier = 0.10;
		case 4:	%lifetimeModifier = 0.10;
	}
	%shotAnimation.setLifetime(%lifetimeModifier * %effectiveRange);
	
	// Add shot sound effect.
	switch ($playerSquad[%characterIndex].characterClass)
	{
		case 0:	playShotGunSfx(%projectileType);
		case 1:	playSniperRifleSfx(%projectileType);
		case 2:	playGrenadeLauncherSfx(%projectileType);
		case 3:	playMachineGunSfx(%projectileType);
		case 4:	playSubMachineGunSfx(%projectileType);
	}
	//playAudio("Audio:LaserBeam", EffectsAudioVolume.value);
}

// Fires weapon particle effects.
function fireWeaponParticleEffects (%characterIndex, %srcXIndex, %srcYIndex, %dstXIndex, %dstYIndex, %direction, %range)
{//echo("fireWeaponParticleEffects()");echo("%characterIndex =" SPC %characterIndex);echo("actual marine indices =" SPC $playerSquad[%characterIndex].xIndex SPC $playerSquad[%characterIndex].yIndex);echo("%srcXIndex SPC %srcYIndex =" SPC %srcXIndex SPC %srcYIndex);
	for (%i = 1; %i <= ($playerSquad[%characterIndex].weapon).ammo; %i++)
	{
		schedule(%i * 50, 0, "createShotAnimation", %characterIndex, %srcXIndex, %srcYIndex, %dstXIndex, %dstYIndex, %direction, %range, $playerSquad[%characterIndex].weapon.projectileType);
	}
}

// Continues tracing fire path (after fire animation has finished).
function continueTraceFirePath (%unit, %tileSourceX, %tileSourceY, %direction, %range)
{//echo("continueTraceFirePath()");echo("%tileSourceX SPC %tileSourceY =" SPC %tileSourceX SPC %tileSourceY);
	// Infinite bullets!
	//schedule(300, 0, "continueTraceFirePath", %unit, %tileSourceX, %tileSourceY, %direction, %range);
	
	// Get the actual current character firing!
	%currentCharacter = getCharacterIndex(%unit);
	// For Guard Mode perform checks to determine who is actually firing!
	if ($monsterTurnOngoing	&&
		$playerSquad[$guardModeCharacter].guardMode)
	{//echo("$guardModeCharacter =" SPC $guardModeCharacter);
		%currentCharacter = $guardModeCharacter;
	}
	
	// Check for weapon stat boosting effects!
	// NOTE: Should his be a separate function?
	if ($squadHailMary	&&
		$playerSquad[%currentCharacter].characterClass == 3)
	{
		%attackRating += $hailMaryAccuracyBoost;
		%range += $hailMaryRangeBoost;
	}
	
	// Get tile source.
	%x = %tileSourceX;
	%y = %tileSourceY;
		
	// Make sure we only check within the fire range.
	for (%i = 0; %i < %range; %i++)
	{
		// Compose the closest tile.
		switch (%direction)
		{
			case $northDirection:	%y = %y - 1;	// Up is negative y-axis.
			case $southDirection:	%y = %y + 1;	// Down is negative y-axis.
			case $eastDirection:	%x = %x + 1;	// Right is positive x-axis.
			case $westDirection:	%x = %x - 1;	// Left is negative x-axis.
		}
		
		// Bounds check.
		if (%x < 0					||
			%y < 0					||
			%x > $mazeDimension - 1	||
			%y > $mazeDimension - 1)
		{//echo("FAILURE!");
			// Failure! Return tile source as a position.
			return %tileSourceX SPC %tileSourceY;
		}
		// Bounds check passed, check the tile.
		else
		{
			// Check if this tile is a wall (not walkable).
			// NOTE: Be careful we don't use this for something like lava (if lava is made unwalkable)!
			if (!canFireThrough(%x, %y))
			{
				if ($playerSquad[%currentCharacter].weapon.projectileType == $weaponProjectileBallisticType)
				{
					// Switch based on character class.
					switch ($playerSquad[%currentCharacter].characterClass)
					{
						case 0:
							// Play bullet impact sound effects!
							schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
							
						case 1:
							// Play bullet impact sound effects!
							schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
							
						case 2:
							// Destroy this tile!
							grenadierDestroyTile(%x, %y);
							
						case 3:
							schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
							schedule($bulletImpactDelay + 75, 0, "playBulletImpactSfx");
							schedule($bulletImpactDelay + 150, 0, "playBulletImpactSfx");
							
						case 4:
							schedule($bulletImpactDelay, 0, "playBulletImpactSfx");
							schedule($bulletImpactDelay + 75, 0, "playBulletImpactSfx");
							schedule($bulletImpactDelay + 150, 0, "playBulletImpactSfx");
							schedule($bulletImpactDelay + 225, 0, "playBulletImpactSfx");
					}
				}
				
				// Fire particle effects!
				fireWeaponParticleEffects(%currentCharacter,
										  $playerSquad[%currentCharacter].xIndex, $playerSquad[%currentCharacter].yIndex,
										  %x, %y,
										  %direction, %range);
				
				// Blow up on wall tiles/obstructions.
				if ($playerSquad[%currentCharacter].characterClass == 2)
				{
					// NOTE: Check if a squad member is on the tile since the above function canFireThrough() will not detect that!
					grenadeClassEmptyAoePathDamage(%x, %y, %direction, isObject(getCharacterOnTile(%x, %y)), $playerSquad[%currentCharacter]);
					createGrenadierExplosion(%x, %y, $playerSquad[%currentCharacter]);
					grenadierDestroyTile(%x, %y);		// Redundant? Or causing issues?
				}
				
				// Return this tile position by index.
				return %x SPC %y + 1;					// Return the tile BEFORE the obstacle!
			}
			// Check if a monster is on this tile position.
			else
			{//echo("checking monster position...");
				// Monster index to fire path to.
				%monsterIndex = monsterAtPosition(%x, %y);
				//echo("%monsterIndex =" SPC %monsterIndex);
				// Check if a monster is in this tile position.
				if (%monsterIndex != -1)
				{//echo("monster at position!");
					// Check for Grenadier class talent "Ambush" here!
					if ($squadAmbush			&&
						$playerSquad[%currentCharacter].characterClass == 2	&&
						isObject(($monsterList.getObject(%monsterIndex)).sleepAnimation))
					{//echo("AMBUSH INSTA-KILL!");
						// Kill monster after slight delay!
						($monsterList.getObject(%monsterIndex)).schedule(150, "safeDelete");
						// Update event log.
						updateEventText($whiteColor @ "Ambush succeeded!" @ $defaultColor @ "!");
					}
					if ($squadGutShot			&&
						$playerSquad[%currentCharacter].characterClass == 2	&&
						($monsterList.getObject(%monsterIndex)).currentActionPoints <= 0)
					{//echo("GUT SHOT INSTA-KILL!");
						// Kill monster after slight delay!
						($monsterList.getObject(%monsterIndex)).schedule(150, "safeDelete");
						// Update event log.
						updateEventText($whiteColor @ "Gut Shot succeeded!" @ $defaultColor @ "!");
					}
					// Grab monster.
					%monster = $monsterList.getObject(%monsterIndex);
					// Fire particle effects!
					fireWeaponParticleEffects(%currentCharacter,
											  $playerSquad[%currentCharacter].xIndex, $playerSquad[%currentCharacter].yIndex,
											  %monster.xIndex, %monster.yIndex,
											  %direction, %range);
					// Calculate number of tiles between current character and the monster.
					%distance = VectorDist($playerSquad[%currentCharacter].getPosition(), %monster.getPosition());
					%tiles = mRound(%distance / 4.0);
					%damageDelay = %tiles * $damageDelay;
					schedule(%damageDelay, 0, "firePathDamage", %currentCharacter, %monsterIndex, %direction);
					// Return this tile position.
					return %x SPC %y;
				}
			}
		}
	}
	
	if ($playerSquad[%currentCharacter].characterClass == 2)
	{
		grenadeClassEmptyAoePathDamage(%x, %y, %direction, isObject(getCharacterOnTile(%x, %y)), $playerSquad[%currentCharacter]);
		createGrenadierExplosion(%x, %y, $playerSquad[%currentCharacter]);
		// Return this tile position.
		return %x SPC %y;
	}
	
	// Clear line of fire through max range, no valid targets or walls or obstructions or anything!
	fireWeaponParticleEffects(%currentCharacter,
							  $playerSquad[%currentCharacter].xIndex, $playerSquad[%currentCharacter].yIndex,
							  -9999, -9999,
							  %direction, %range);
}
function initiateFireControls ()
{//echo("initiateFireControls()");
	if ($squadReposition)
	{
		updateEventText($whiteColor @ "Squad is Repositioning - cannot attack" @ $defaultColor @ "!");
		return false;
	}
	if (!weaponHasRequiredAmmo($activeCharacter))
	{
		updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $greyColor @ "does not have enough ammunition to fire" @ $defaultColor @ "!");
		setNoAmmoDialogue($playerSquad[$activeCharacter]);
		return false;
	}
	if (isLargeUnit($playerSquad[$activeCharacter]))
	{
		// Special Hulk ranged attack!
		useHulkRangedWeapon($activeCharacter, false);
	}
	else
	{
		clearTargetIndicators(false);

		/*
		// If the cursor is on the active marine when firing, set target indicators!
		%mouseSquadMember = mouseOverSquadMember();
		if (%mouseSquadMember != -1	&&
			$activeCharacter != %mouseSquadMember)
		{
			//setTargetIndicators($activeCharacter);
			clearTargetIndicators(false);
		}
		*/

		fireWeaponInDirection($playerSquad[$activeCharacter].facing);
	}
	return true;
}
function deductAmmo ()
{//echo("deductAmmo()");
	%currentCharacter = $activeCharacter;
	// For Guard Mode perform checks to determine who is actually firing!
	if ($monsterTurnOngoing	&&
		$playerSquad[$guardModeCharacter].guardMode)
	{//echo("$guardModeCharacter =" SPC $guardModeCharacter);
		%currentCharacter = $guardModeCharacter;
	}
	deductAmmoFromCharacter(%currentCharacter);
}
function deductAmmoFromCharacter (%characterIndex)
{//echo("deductAmmoFromCharacter()");
	// Increment total Ammunition Expended!
	$GlobalStats.totalAmmunitionExpended += ($playerSquad[%characterIndex].weapon).ammo;
	// Increment total Ammunition Mass Expended!
	%ammoMass = 0.1;	// In kilograms!
	switch ($playerSquad[%characterIndex].characterClass)
	{
		case 0:	%ammoMass = 0.07;
		case 1:	%ammoMass = 0.09;
		case 2:	%ammoMass = 0.50;
		case 3:	%ammoMass = 0.05;
		case 4:	%ammoMass = 0.05;
	}
	$GlobalStats.totalAmmunitionMassExpended += ($playerSquad[%characterIndex].weapon).ammo * %ammoMass;
	// Deduct ammunition use from active character.
	($playerSquad[%characterIndex].weapon).currentAmmo -= ($playerSquad[%characterIndex].weapon).ammo;
	// Sanity check!
	if (($playerSquad[%characterIndex].weapon).currentAmmo < 0)
	{
		($playerSquad[%characterIndex].weapon).currentAmmo = 0;
	}
	// Set current ammo in weapon's weaponType!
	($playerSquad[%characterIndex].weapon).weaponType = getWord(($playerSquad[%characterIndex].weapon).weaponType,  0)	SPC		// Name.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  1)	SPC		// Squad Member.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  2)	SPC		// Damage.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  3)	SPC		// Range.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  4)	SPC		// Accuracy.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  5)	SPC		// Imagemap.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  6)	SPC		// Ammo/Magazine Capacity.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  7)	SPC		// Capacity/Max Ammo.
														getWord(($playerSquad[%characterIndex].weapon).weaponType,  8)	SPC		// Damage Range.
														($playerSquad[%characterIndex].weapon).currentAmmo				SPC		// Current Ammo/In Magazine.
														getWord(($playerSquad[%characterIndex].weapon).weaponType, 10)	SPC		// Reload/AP Cost.
														getWord(($playerSquad[%characterIndex].weapon).weaponType, 11)	SPC		// Fire Cost.
														getWord(($playerSquad[%characterIndex].weapon).weaponType, 12);			// Projectile Type.
	scrollFloatingText($playerSquad[%characterIndex], ($playerSquad[%characterIndex].weapon).currentAmmo, "ammoCounter", "ammoCounter");
}
function getTotalFireRange (%characterIndex)
{//echo("getTotalFireRange()");
	return ($playerSquad[%characterIndex].weapon).range;
}
function getTotalFireAccuracy (%characterIndex)
{//echo("getTotalFireAccuracy()");
	return ($playerSquad[%characterIndex].weapon).accuracy + $playerSquad[%characterIndex].arms;
}
function fireWeaponInDirection (%direction)
{//echo("fireWeaponInDirection()");
	if (!$playerSquad[$activeCharacter].guardMode)
	{
		%actionPointCost = ($playerSquad[$activeCharacter].weapon).fireCost;
		if (%actionPointCost > $playerSquad[$activeCharacter].currentActionPoints)
		{
			return;
		}
		setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
	}
	else
	{
		return;
	}
	lockFireControls();
	deductAmmo();
	playMarineFireAnimation($activeCharacter);
	dispatchSquadMemberCameraShake();
	updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $friendlyActionColor @ "takes a shot" @ $defaultColor @ ".");
	%range = getTotalFireRange($activeCharacter);
	%fireTile = traceFirePath($playerSquad[$activeCharacter], $playerSquad[$activeCharacter].xIndex, $playerSquad[$activeCharacter].yIndex, %direction, %range);
	initializeReadout();
	scheduleMonsterTurn();
	schedule(150, 0, "unlockFireControls");
	updateFirearmReloadReadout();
}
function characterFire (%characterIndex)
{//echo("characterFire()");echo("%characterIndex =" SPC %characterIndex);echo("$playerSquad[%characterIndex].xIndex SPC $playerSquad[%characterIndex].yIndex =" SPC $playerSquad[%characterIndex].xIndex SPC $playerSquad[%characterIndex].yIndex);
	if (!$playerSquad[%characterIndex].guardMode)
	{
		%actionPointCost = ($playerSquad[%characterIndex].weapon).fireCost;
		if (%actionPointCost > $playerSquad[%characterIndex].currentActionPoints)
		{
			return;
		}
		setCurrentActionPoints(%characterIndex, $playerSquad[%characterIndex].currentActionPoints - %actionPointCost);
	}
	deductAmmo();
	playMarineFireAnimation(%characterIndex);
	dispatchSquadMemberCameraShake();
	%range = getTotalFireRange(%characterIndex);
	%fireTile = traceFirePath($playerSquad[%characterIndex], $playerSquad[%characterIndex].xIndex, $playerSquad[%characterIndex].yIndex, $playerSquad[%characterIndex].facing, %range);
	initializeReadout();
	scheduleMonsterTurn();
	unlockFireControls();
	updateFirearmReloadReadout();
}
function clearFireControl ()
{//echo("clearFireControl()");
	if ($fireControlInitiated)
	{//echo("$fireControlInitiated =" SPC $fireControlInitiated);
		$fireControlInitiated = false;
		clearTargetIndicators(false);
	}
}
function updateFirearmReloadReadout ()
{//echo("updateFirearmReloadReadout()");
	// Hook to updating the Monster Target Panel!
	schedule(100, 0, "autoTargetEnemyUnit");
	// Updated Firearm Reload Readout.
	switch ($playerSquad[$activeCharacter].characterClass)
	{
		case 0:
			if ($playerSquad[$activeCharacter].weapon.currentAmmo >= $playerSquad[$activeCharacter].weapon.capacity * 0.75)
			{//echo("full");
				LeaderFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Leader/LeaderFirearmReloadFull");
			}
			else if ($playerSquad[$activeCharacter].weapon.currentAmmo <= 0)
			{//echo("empty");
				LeaderFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Leader/LeaderFirearmReloadEmpty");
			}
			else
			{//echo("half");
				LeaderFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Leader/LeaderFirearmReloadHalf");
			}
			
			LeaderFirearmReadout.visible = true;
			ScoutFirearmReadout.visible = false;
			GrenadierFirearmReadout.visible = false;
			SupportFirearmReadout.visible = false;
			EngineerFirearmReadout.visible = false;
			LeaderFirearmReadoutAmmoCounter.setText($playerSquad[$activeCharacter].weapon.currentAmmo @ "/" @ $playerSquad[$activeCharacter].ammo);
		
		case 1:
			if ($playerSquad[$activeCharacter].weapon.currentAmmo >= $playerSquad[$activeCharacter].weapon.capacity * 0.75)
			{//echo("full");
				ScoutFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Scout/LeaderFirearmReloadFull");
			}
			else if ($playerSquad[$activeCharacter].weapon.currentAmmo <= 0)
			{//echo("empty");
				ScoutFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Scout/LeaderFirearmReloadEmpty");
			}
			else
			{//echo("half");
				ScoutFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Scout/LeaderFirearmReloadHalf");
			}
			
			LeaderFirearmReadout.visible = false;
			ScoutFirearmReadout.visible = true;
			GrenadierFirearmReadout.visible = false;
			SupportFirearmReadout.visible = false;
			EngineerFirearmReadout.visible = false;
			ScoutFirearmReadoutAmmoCounter.setText($playerSquad[$activeCharacter].weapon.currentAmmo @ "/" @ $playerSquad[$activeCharacter].ammo);
		
		case 2:
			if ($playerSquad[$activeCharacter].weapon.currentAmmo >= $playerSquad[$activeCharacter].weapon.capacity * 0.75)
			{//echo("full");
				GrenadierFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Grenadier/LeaderFirearmReloadFull");
			}
			else if ($playerSquad[$activeCharacter].weapon.currentAmmo <= 0)
			{//echo("empty");
				GrenadierFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Grenadier/LeaderFirearmReloadEmpty");
			}
			else
			{//echo("half");
				GrenadierFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Grenadier/LeaderFirearmReloadHalf");
			}
			
			LeaderFirearmReadout.visible = false;
			ScoutFirearmReadout.visible = false;
			GrenadierFirearmReadout.visible = true;
			SupportFirearmReadout.visible = false;
			EngineerFirearmReadout.visible = false;
			GrenadierFirearmReadoutAmmoCounter.setText($playerSquad[$activeCharacter].weapon.currentAmmo @ "/" @ $playerSquad[$activeCharacter].ammo);
		
		case 3:
			if ($playerSquad[$activeCharacter].weapon.currentAmmo >= $playerSquad[$activeCharacter].weapon.capacity * 0.75)
			{//echo("full");
				SupportFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Support/LeaderFirearmReloadFull");
			}
			else if ($playerSquad[$activeCharacter].weapon.currentAmmo <= 0)
			{//echo("empty");
				SupportFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Support/LeaderFirearmReloadEmpty");
			}
			else
			{//echo("half");
				SupportFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Support/LeaderFirearmReloadHalf");
			}
			
			LeaderFirearmReadout.visible = false;
			ScoutFirearmReadout.visible = false;
			GrenadierFirearmReadout.visible = false;
			SupportFirearmReadout.visible = true;
			EngineerFirearmReadout.visible = false;
			SupportFirearmReadoutAmmoCounter.setText($playerSquad[$activeCharacter].weapon.currentAmmo @ "/" @ $playerSquad[$activeCharacter].ammo);
		
		case 4:
			if ($playerSquad[$activeCharacter].weapon.currentAmmo >= $playerSquad[$activeCharacter].weapon.capacity * 0.75)
			{//echo("full");
				EngineerFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Engineer/LeaderFirearmReloadFull");
			}
			else if ($playerSquad[$activeCharacter].weapon.currentAmmo <= 0)
			{//echo("empty");
				EngineerFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Engineer/LeaderFirearmReloadEmpty");
			}
			else
			{//echo("half");
				EngineerFirearmReadoutReload.setBitmap("../../../Sprites/1/assets/images/UI/Firearm UI/Engineer/LeaderFirearmReloadHalf");
			}
			
			LeaderFirearmReadout.visible = false;
			ScoutFirearmReadout.visible = false;
			GrenadierFirearmReadout.visible = false;
			SupportFirearmReadout.visible = false;
			EngineerFirearmReadout.visible = true;
			EngineerFirearmReadoutAmmoCounter.setText($playerSquad[$activeCharacter].weapon.currentAmmo @ "/" @ $playerSquad[$activeCharacter].ammo);
	}
}