// Ranged Monster AI.
// Worthless Bums 2012.


// Checks if a monster can range a squad member.
function monsterCanRange (%monster, %characterIndex)
{//echo("monsterCanRange()");
	%canRange = false;
	%distance = VectorDist(%monster.getPosition(), $playerSquad[%characterIndex].getPosition());
	if (%distance <= %monster.range)
	{
		if (%monster.xIndex == $playerSquad[%characterIndex].xIndex	||
			%monster.yIndex == $playerSquad[%characterIndex].yIndex)
		{
			%canRange = bresenhamLineAlgorithm(%monster.xIndex, %monster.yIndex, $playerSquad[%characterIndex].xIndex, $playerSquad[%characterIndex].yIndex);
		}
	}
	return %canRange;
}

// Returns status code for calculating monster ranging status.
function calculateMonsterRangedStatus (%monster, %index)
{//echo("calculateMonsterRangedStatus()");
//echo("%index =" SPC %index);
	// Enemies with ranged attacks!
	if (%monster.canRange		&&
		// This condition should really be its own function!
		!($squadSuppressionFire	&&
		  objectWithinRange(%monster.getPosition(), $playerSquad[0].getPosition(), $squadSuppressionFireRange)))
	{//echo("CAN RANGE.");
		// Function monsterCanRange() is used to determine ranged attacks on squad members.
		// Check if the monster's range and line-of-sight is adequate to attack the player.
		if (monsterCanRange(%monster, %index))
		{
			// Perform ranged action.
			//echo(%monster.name SPC "ranging" SPC $playerSquad[%index].name @ "!");
			// Deduct action points.
			%monster.currentActionPoints--;		// Monsters only require 1 Action Point to range attack.
			// Sanity check.
			if (%monster.currentActionPoints < 0)
			{
				%monster.currentActionPoints = 0;
			}
			
			// RANGED ATTACK BRANCH.
			if (%monster.name $= "FlameJack")
			{updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " shoots fire" @ $defaultColor @ "!");
				// Set monster facing.
				%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
				setMonsterFacing(%direction, %monster);
				
				// Execute Fire Elemental fireball attack.
				handleFireball(%monster.xIndex, %monster.yIndex, $playerSquad[%index].xIndex, $playerSquad[%index].yIndex);
				
				// Attack the character with a basic (melee) attack for now - testing!
				monsterRangeCharacter(%monster, %index, true);
				
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Torgon"		||
					 %monster.name $= "TorSniper"	||
					 %monster.name $= "TorScout"	||
					 %monster.name $= "TorChaser")
			{
				// Update event log.
				updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " fires energy weapon" @ $defaultColor @ "!");
				
				// Set monster facing.
				%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
				setMonsterFacing(%direction, %monster);
				
				// Execute Torgon's green energy blast attack.
				handleGreenEnergyBlast(%monster.xIndex, %monster.yIndex, $playerSquad[%index].xIndex, $playerSquad[%index].yIndex);
				
				// Attack the character with a basic (melee) attack for now - testing!
				monsterRangeCharacter(%monster, %index, false);
				
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Sapper")
			{
				// Update event log.
				updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " fires energy weapon" @ $defaultColor @ "!");
				
				// Set monster facing.
				%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
				setMonsterFacing(%direction, %monster);
				
				// Execute Sapper's grenade attack.
				createThrownGrenadeAnimation(%monster, $playerSquad[%index].xIndex, $playerSquad[%index].yIndex);
				// Calculate grenade delay based on distance between monster and target.
				%grenadeDelay = $GrenadeBaseDelay;
				%distance = mAbs(VectorDist($playerSquad[%index].getPosition(), %monster.getPosition()));
				if (%distance <= 14)
				{
					%grenadeDelay = 0.75 * $GrenadeBaseDelay;
				}
				if (%distance <= 10)
				{
					%grenadeDelay = 0.50 * $GrenadeBaseDelay;
				}
				if (%distance <=  6)
				{
					%grenadeDelay = 0.25 * $GrenadeBaseDelay;
				}
				schedule(%grenadeDelay, 0, "handleGrenadierExplosion", $playerSquad[%index].xIndex, $playerSquad[%index].yIndex);
				schedule(%grenadeDelay, 0, "grenadeEmptyAoePathDamage", $playerSquad[%index].xIndex, $playerSquad[%index].yIndex, $northDirection);	// Direction is irrelevant at the moment!
				
				// Attack the character with a basic (melee) attack for now - testing!
				schedule(%grenadeDelay, 0, "monsterRangeCharacter", %monster, %index, false);
				
				// Update floating hp!
				updateSquadMemberFloatingHealthPoints();
				updateAllMonstersFloatingHealthPointText();
				
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Brainien")
			{
				updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " crackles with psychic energy" @ $defaultColor @ "!");
				// Launch an aoe psychic attack!
				// Nothing here yet!
				return false;
			}
			else if (%monster.name $= "Gunship")
			{
				%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
				setMonsterFacing(%direction, %monster);
				// Ground Thump!
				%wallsThumped = gunshipDestroyTerrain(%monster.xIndex, %monster.yIndex);//echo("%wallsThumped =" SPC %wallsThumped);
				if (%wallsThumped > 0)
				{
					playRandomMetalWallCollapseSfx();
					%armorGain = %wallsThumped * 8;
					%monster.armorRating += %armorGain;
					%monster.currentActionPoints = 0;
					inPlaceMonsterActionGrowAndShrink(%monster);
					scrollInfoDescriptionText(%monster, "Gunship gains +" @ %armorGain @ " Armor!");
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " thumps the ground!" @ $defaultColor @ "!");
					//scrollInfoText(%monster, "Ground Thump!");
				}
				else
				{
					// Beam Attack!
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " creaks, its barrel swivelling" @ $defaultColor @ "!");
					fireGunshipBeamAnimation(%monster);
					schedule(600, 0, "useGunshipBeamShot", %monster);
				}
				return false;
			}
			else if (%monster.name $= "Brute")
			{
				// If the Brute is already charged up, execute a charge power!
				if (isObject(%monster.chargeAnimation))
				{//echo("BRUTE CHARGING");
					// Update event log.
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " charges" @ $defaultColor @ "!");
					
					// Charge toward the nearest player.
					// NOTE: How to charge?
					// Remove and null charge animation.
					(%monster.chargeAnimation).setLifetime(0.0);
					(%monster.chargeAnimation).safeDelete();
					%monster.chargeAnimation = null;
					
					// Calculate tile just before targeted character.
					%xPos = getWord($playerSquad[%index].getPosition(), 0);
					%yPos = getWord($playerSquad[%index].getPosition(), 1);
					%mxPos = getWord(%monster.getPosition(), 0);
					%myPos = getWord(%monster.getPosition(), 1);
					%xDiff = 4;
					%yDiff = 4;
					// Moving right.
					if (%yPos == %myPos)
					{
						%yDiff = 0;
						if (%xPos > %mxPos)
						{
							%xDiff = -4;
						}
					}
					// Moving down.
					else if (%xPos == %mxPos)
					{
						%xDiff = 0;
						if (%yPos > %myPos)
						{
							%yDiff = -4;
						}
					}
					
					// Move the monster.
					%monster.moveTo(%xPos + %xDiff, %yPos + %yDiff, 60.0, true, false);
					%indices = getIndicesFromTilePosition(%xPos + %xDiff, %yPos + %yDiff);
					%monster.xIndex = getWord(%indices, 0);
					%monster.yIndex = getWord(%indices, 1);
					
					// Charge always hits, but can't flank or crit!
					%damage = getRandom(30, 50);
					// Handle floating text.
					scrollFloatingText($playerSquad[%index], %damage, "damage");
					// Save character name in case character dies before event log is updated!
					%characterName = $playerSquad[%i].name;
					// Make sure squad member health does not go below zero.
					if (%damage > $playerSquad[%index].currentHealth)
					{
						$playerSquad[%index].currentHealth = 0;
						// Squad member death!
						executeSquadMemberDeath(%index, true, %monster.name);
					}
					else
					{
						$playerSquad[%index].currentHealth -= %damage;
					}
					
					// Handle blood splatter animation from charge.
					handleBloodSplatter($playerSquad[%index].xIndex, $playerSquad[%index].yIndex);
					// Play troll charge sound effect.
					playAudio("Audio:trollCharge", EffectsAudioVolume.value);
					
					// Calculate indices to move to.
					%indices = getIndicesFromTilePosition(%xPos - %xDiff, %yPos - %yDiff);
					%xIndex = getWord(%indices, 0);
					%yIndex = getWord(%indices, 1);
					// Bounds check indices.
					if (%xIndex >= 0				&&
						%xIndex < $mazeDimension	&&
						%yIndex >= 0				&&
						%yIndex < $mazeDimension)
					{
						// Make sure the tile is clear.
						if (tileIsClear(%xIndex, %yIndex)	||
							isSpaceTile(%xIndex, %yIndex))
						{
							// Handle knockback of character.
							$playerSquad[%index].moveTo(%xPos - %xDiff, %yPos - %yDiff, 60.0, true, false);
							// Set new indices.
							$playerSquad[%index].xIndex = %xIndex;
							$playerSquad[%index].yIndex = %yIndex;
							
							// Ugh. Check twice or write custom function in lieu of function tileIsClear()?
							if (isSpaceTile(%xIndex, %yIndex))
							{
								// Schedule squad member death!
								executeSquadMemberDeath(%index, false, %monster.name);
							}
						}
					}
					
					// Update the health value on the readout.
					initializeReadout();
					
					// Update event log.
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " charges " @ $squadColor @ $playerSquad[%index].name @ $defaultColor @ " for " @ $damageColor @ %damage @ " damage" @ $defaultColor @ "!");
				}
				else
				{
					// Chance to power up!
					//%chargeRoll = getRandom(1, 100);
					//if (%chargeRoll <= 100)
					// Determine larger Brutes by size!
					if (getWord(%monster.size, 0) >= 4.5)
					{
						// Set monster facing.
						%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
						setMonsterFacing(%direction, %monster);
						
						// Update event log.
						updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " starts to power up" @ $defaultColor @ "!");
						// Handle charge.
						handleFrostTrollCharge(%monster);
						
						// Every time the monster powers up it gains armor and restores some health!
						%monster.armorRating++;
						if (%monster.currentHealth + 10 <= %monster.maxHealth)
						{
							%monster.currentHealth += 10;
						}
						else
						{
							%monster.currentHealth = %monster.maxHealth;
						}
						// Scroll health gain text.
						scrollFloatingText(%monster, 10, "regen");
						// Handle monster health indicators.
						handleHealthIndicators();
					}
				}
				
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Hulk")	// Turn off charge for now! (Should be "Hulk"".)
			{//echo("Hulk ranging detected!");
				// If the Brute is already charged up, execute a charge power!
				if (isObject(%monster.chargeAnimation))
				{//echo("HULK CHARGING");
					// Update event log.
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " charges" @ $defaultColor @ "!");
					
					// Remove and null charge animation.
					(%monster.chargeAnimation).setLifetime(0.0);
					(%monster.chargeAnimation).safeDelete();
					%monster.chargeAnimation = null;
					
					chargeInDirection(%monster, %monster.facing, %monster.range / 4, %monster.baseRangeDamage);
					playAudio("Audio:trollCharge", EffectsAudioVolume.value);
					initializeReadout();
				}
				else
				{//echo("HULKING POWERING UP!");
					// Set monster facing.
					%direction = getDirectionBetweenUnits(%monster, $playerSquad[%index]);
					setMonsterFacing(%direction, %monster);
					
					// Update event log.
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " starts to power up" @ $defaultColor @ "!");
					// Handle charge.
					handleFrostTrollCharge(%monster);
					
					// Every time the monster powers up it gains armor and restores some health!
					%monster.armorRating++;
					if (%monster.currentHealth + 10 <= %monster.maxHealth)
					{
						%monster.currentHealth += 10;
					}
					else
					{
						%monster.currentHealth = %monster.maxHealth;
					}
					// Scroll health gain text.
					scrollFloatingText(%monster, 10, "regen");
					// Handle monster health indicators.
					handleHealthIndicators();
				}
				
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Matrix")
			{//echo("MATRIX RANGED CODE PATH.");
				%rangedRoll = 101;
				if (tileIsClearOfObstructions(%monster.xIndex, %monster.yIndex + 1))	// South.
				{
					%rangedRoll = getRandom(1, 100);
				}
				//%rangedRoll = 20;	// For testing purposes only!
				if (%rangedRoll <= 50)
				{
					createMatixUnitSpawnAnimation(%monster);
					spawnMatrixUnit(%monster);
				}
				else							// Ranged attack.
				{
					updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " crackles with energy" @ $defaultColor @ "!");
					createMatixAoeAnimation(%monster);
					monsterRangeCharacter(%monster, %index, false);
				}
				// Return false so the monster does not move as well!
				return false;
			}
			else if (%monster.name $= "Patriarch")
			{//echo("PATRIARCH RANGED CODE PATH.");
				%rangedRoll = getRandom(1, 100);
				//%rangedRoll = 45;	// For testing purposes only!	
				if (%rangedRoll <= 20)
				{
					patriarchRingSquad();
					%monster.currentActionPoints = 0;
					%monster.armorRating += $PatriarchArmorGain;
					scrollInfoText(%monster, "+" @ $PatriarchArmorGain @ " Armor!");
					
					// Return false so the monster does not move as well!
					return false;
				}
				else if (%rangedRoll <= 40)
				{
					patriarchChargeAttack(%monster);
					%monster.currentHealth += $PatriarchHealthGain;
					//scrollInfoText(%monster, "+" @ $PatriarchHealthGain @ " Health!");
					scrollInfoText(%monster, "Health UP!");
					updateAllMonstersFloatingHealthPointText();
					
					// Return false so the monster does not move as well!
					return false;
				}
				// Move/do regular stuff!
				return true;
			}
			
			// Return false so the monster does not move as well!
			// NOTE: Change this! Need to have monster action points to use.
			//return false;
		}
		else
		{
			// Do nothing - should continue below and act as if the monster has no ranged!
		}
	}
	else
	{
		// Check for monsters that have no offensive range spells but have defensive ranged spells!
		if (%monster.defensiveCaster)
		{
			if (%monster.name $= "Vinelasher")
			{
				// Check for nearby monsters to buff!
				for (%i = 0; %i < $monsterList.getCount(); %i++)
				{
					// Grab the monster.
					%otherMonster = $monsterList.getObject(%i);
					
					// Only cast if also within range of squad!
					if (isNearSquad(%monster, %monster.range * 9))
					{
						if (objectWithinRange(%monster.getPosition(), %otherMonster.getPosition(), %monster.range * 9))		// Magical number 9!
						{
							// Imbue the monster with strength!
							%otherMonster.baseDamage += 10;
							%otherMonster.attackModifier += 5;
							updateEventText($monsterColor @ %monster.name @ $hostileActionColor @ " blesses " @ $monsterColor @ %otherMonster.name @ $defaultColor @ "!");
							break;
						}
					}
				}
			}
		}
	}
	
	// Return true means continue!
	return true;
}