// Handles character movements.
// Worthless Bums 2012.


function releaseCharacterActionLock ()
{//echo("releaseCharacterActionLock()");
	if (isEventPending($CALEvent))
	{
		cancel($CALEvent);
	}
	$currentCharacterActionFlag = false;
}
function engageCharacterActionLock ()
{//echo("engageCharacterActionLock()");
	$currentCharacterActionFlag = true;
	$CALTime = getScriptTimer();
	$CALEvent = schedule(2000, 0, "checkCAL");	// No CAL should last over two seconds!
}
function checkCal ()
{//echo("checkCal()");
	if ($currentCharacterActionFlag	&&
		$CALTime + 2000 > getScriptTimer())
	{
		cancel($CALEvent);
	}
	else
	{
		$CALEvent = schedule(100, 0, "checkCAL");
	}
}
// BUG: Oh shit, is this never being called by moveTo() in T2D v3.0? Should be fixed now!
function onPositionTarget (%thisObject)
{//echo("onPositionTarget()");
	// Purge unused memory!
	purgeResources();
	
	// Enforce squad persistence.
	enforceSquadPersistence();
	
	// We only care about non-squad member animated sprites!
	if ($playerSquad[0] != %thisObject	&&
		$playerSquad[1] != %thisObject	&&
		$playerSquad[2] != %thisObject	&&
		$playerSquad[3] != %thisObject)
	{//echo("Non-player unit on position!");
		%thisObject.graph = $monsterGraph;
		// For console movement/interaction!
		if (tileHasConsole(%thisObject.xIndex, %thisObject.yIndex + 1))
		{
			%thisObject.group = $ConsoleGroup + 1;
		}
		// For lockers movement/interaction!
		if (tileHasLockers(%thisObject.xIndex, %thisObject.yIndex + 1))
		{//echo("has lockers!");
			%thisObject.group = $LockersGroup + 1;
		}
		// For CryoTube movement/interaction!
		if (tileHasCryoTube(%thisObject.xIndex, %thisObject.yIndex + 1))
		{
			%thisObject.group = $CryoTubeGraph + 1;
		}
		// Update monster fog of war visibility.
		$fogOfWar.updateTargetMonsterVisibility(%thisObject);
		// Take care of lifted tile marker.
		if (%thisObject.visible)
		{
			handleLiftedTileMarker(%thisObject);
		}
		return;
	}
	else
	{//echo("Player unit on position!");
		// Execute automated enemy actions on player unit movement.
		executeEnemyUnitAutomationsOnMove();
		// Auto-Target any enemy unit in range/sight!
		autoTargetEnemyUnit($autoTargetDelay);
		// Check to see if the player moved adjacent to a tapped wall!
		%objectDistance = mAbs(VectorDist(%thisObject.xIndex SPC %thisObject.yIndex, $TappingWallTileX SPC $TappingWallTileY));
		if (%objectDistance > 0.5	&&		// Slightly arbitrary - just don't spawn if the object is directly on this tile!
			%objectDistance <= 1.0)			// Only pop on adjacent tiles!
		{
			burstTappedWallMonster();
		}
		// Check to see if the squad member is facing out a porthole into space.
		if (%thisObject.facing == $southDirection							&&		// All portholes to space are north facing.
			$mazeRoom[%thisObject.xIndex, %thisObject.yIndex - 1].porthole	&&		// Check the wall tile with the porthole to the north
			tileIsOpenSpace(%thisObject.xIndex, %thisObject.yIndex - 2))			// Check if the tile north of the porthole is an outer space tile.
		{//echo("porthole into space found!");
			schedule(500, 0, "setPortholeSpaceDialogue", %thisObject);
		}
		// For CryoTube movement/interaction!
		if (tileHasCryoTube(%thisObject.xIndex, %thisObject.yIndex + 1))
		{//echo("has cryo tube below!");
			%thisObject.group = $CryoTubeGraph + 1;
		}
		// For console movement/interaction!
		if (tileHasConsole(%thisObject.xIndex, %thisObject.yIndex + 1))
		{//echo("has console below!");
			%thisObject.group = $ConsoleGroup + 1;
		}
		// For lockers movement/interaction!
		if (tileHasLockers(%thisObject.xIndex, %thisObject.yIndex + 1))
		{//echo("has lockers below!");
			%thisObject.group = $LockersGroup + 1;
		}
		// Take care of unit shadow.
		(%thisObject.shadow).group = %thisObject.group + 1;
		// Take care of lifted tile marker.
		handleLiftedTileMarker(%thisObject);
		// Trigger context sensitive GUI buttons.
		// NOTE: Place this above the function autoGrabItem() call to avoid re-triggering improper grab button on a chest tile!
		triggerContextSensitiveUI(false);
		autoGrabItem();
	}
	playRandomFootstepSfx();
	isNextToPainting($activeCharacter);		// Is this a timing issue? Should this be handled at end of turn for all squad members instead?
	schedule(20, 0, "continueOnPositionTarget", %thisObject);	// Walking issue - increase delay?
}
function continueOnPositionTarget (%thisObject)
{//echo("finalOnPositionTarget()");
	schedule($CharacterActionLockDelay, 0, "releaseCharacterActionLock");
	characterOnTrap(%thisObject);
	if (!$leaderPersist		&&
		!$scoutPersist		&&
		!$grenadierPersist	&&
		!$supportPersist)
	{
		return;
	}
	if (strstr(%thisObject.Animation, "HulkSuit")  >= 0)
	{
		%thisObject.size = "8 8";
		%thisObject.weaponSprite.visible = false;
	}
	else
	{
		%thisObject.size = "4 4";
		%thisObject.weaponSprite.visible = false;
	}
	//enforceSquadPosition();	// Walking issue - comment out to test!
	enforceSquadFacing();
	$fogOfWar.showFogOfWar();
	scheduleMonsterTurn();
}
function t2dAnimatedSprite::onPositionTarget (%this)
{//echo("t2dAnimatedSprite::onPositionTarget()");
	onPositionTarget(%this);
}
function Sprite::onPositionTarget (%this)
{//echo("Sprite::onPositionTarget()");
	onPositionTarget(%this);
}
function canMoveToMapPosition (%x, %y, %direction)
{//echo("canMoveToMapPosition()");
	if ($CharacterSwapping)
	{
		return 1;
	}
	if (%x < 0 					||
		%y < 0					||
		%x > $mazeDimension - 1	||
		%y > $mazeDimension - 1)
	{
		return 0;
	}
	// Check for Disruptor!
	if (isObject($ElevatorDisruptor))
	{//echo(%x SPC %y);
		if (%x == $ElevatorDisruptor.xIndex	&&
			%y == $ElevatorDisruptor.yIndex)
		{
			%actionPointCost = getActionPointCost(3);
			setIndividualDialoguePopup($playerSquad[$activeCharacter], "Blasted alien tech!", "dialogue");
			inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
			setSquadMemberIndexFacing(%direction, $playerSquad[$activeCharacter], $activeCharacter);
			smashDisruptor();
			setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
			updateReadouts();
			scheduleMonsterTurn();
			$playerSquad[$activeCharacter].facing = %direction;
			$fogOfWar.showFogOfWar();
			return 0;
		}
	}
	for (%i = 0; %i < $maxSquadSize; %i++)
	{
		if (%i != $activeCharacter)
		{
			if (isObject($playerSquad[%i])		&&
				%x == ($playerSquad[%i]).xIndex	&&
				%y == ($playerSquad[%i]).yIndex)
			{//echo("SWAP SITUATION DETECTED!");
				%actionPointCost = getActionPointCost(3);
				if (($playerSquad[$activeCharacter]).currentActionPoints >= %actionPointCost	&&
					($playerSquad[%i]).currentActionPoints >= %actionPointCost)
				{//echo("SWAPPING!");
					if ($generateTutorialLevel)
					{
						if ($tutorialSection == 2)
						{
							tutorialMessage3();
						}
					}
					$CharacterSwapping = true;
					// Move the active character.
					//moveCharacter($activeCharacter, %direction);		// This is done via returning 1!
					// Move the passive character.
					moveCharacter(%i, getReverseDirection(%direction));
					$CharacterSwapping = false;
					if (!$GravityWellOn)
					{
						setCurrentActionPoints(%i, $playerSquad[%i].currentActionPoints - %actionPointCost);
					}
					$fogOfWar.schedule(225, "showFogOfWar");		// This should be handled by onPositionTarget()!
					return 1;		// Mark the active character as moving to the swapped member's position!
				}
				else
				{
					playAudio("Audio:cantSwap", EffectsAudioVolume.value);
					return 0;
				}
			}
		}
	}
	if (isObject($mazeRoom[%x, %y])	&&
		!isTileStairs(%x, %y))	// Need to be able to smash crates/interact with stuff on the elevator tile!
	{
		%roomPositionStatus = $mazeRoom[%x, %y].status;
	}
	else
	{
		%actionPointCost = getActionPointCost(9);
		if (($playerSquad[$activeCharacter]).currentActionPoints >= %actionPointCost)
		{
			%barrelIndex = hasBarrel(%x, %y);
			if (%barrelIndex != -1)
			{
				inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
				setSquadMemberIndexFacing(%direction, $playerSquad[$activeCharacter], $activeCharacter);
				smashBarrel(%barrelIndex);
				setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
				updateReadouts();
				scheduleMonsterTurn();
				$playerSquad[$activeCharacter].facing = %direction;
				$fogOfWar.showFogOfWar();
				return 0;
			}
			else
			{
				%roomPositionStatus = 1;
			}
			for (%i = 0; %i < $dispenserList.getCount(); %i++)
			{//echo("%direction =" SPC %direction);
				%dispenser = $dispenserList.getObject(%i);
				if (%dispenser.xIndex == %x	&&
					%dispenser.yIndex == %y)
				{
					if (!%dispenser.broken	&&
						(%dispenser.image $= getBigDispenserOnSprites()	||	%dispenser.image $= getSmallDispenserOnSprites()))
					{
						setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
						$fogOfWar.showFogOfWar();
						environmentInteract(%direction);
						return 0;
					}
					else
					{
						%actionPointCost = getActionPointCost(9);
						if (($playerSquad[$activeCharacter]).currentActionPoints >= %actionPointCost)
						{
							inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
							setSquadMemberIndexFacing(%direction, $playerSquad[$activeCharacter], $activeCharacter);
							%dispenser.safeDelete();
							playRandomMetalWallCollapseSfx();
							setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
							updateReadouts();
							scheduleMonsterTurn();
							$playerSquad[$activeCharacter].facing = %direction;
							$fogOfWar.showFogOfWar();
							autoTargetEnemyUnit($autoTargetDelay * 2);
							giveRandomMoney($DispenserMoneyMin, $DispenserMoneyMax);
							return 0;
						}
						else
						{
							updateEventText($whiteColor @ "Not enough action points to smash dispenser!");
							playAudio("Audio:cantSwap", EffectsAudioVolume.value);
							return 0;
						}
					}
					return false; 
				}
			}
			// If there is an Alien Merchant interact instead!
			if (tileIsClearOfUnits(%x, %y))
			{
				for (%i = 0; %i < $CryoTubeList.getCount(); %i++)
				{//echo("%direction =" SPC %direction);
					%cryoTube = $CryoTubeList.getObject(%i);
					if (%cryoTube.xIndex == %x	&&
						%cryoTube.yIndex == %y)
					{//echo("moving into a cryotube!");
						setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
						// If the marine is high enough rank let the tube alien be a merchant!
						//if ($playerSquad[$activeCharacter].rank >= 3)	// CPL.	4)	// Sergeant.
						if ($playerSquad[$activeCharacter].rank >= 0)	// Any rank!
						{
							enableGuiInProgress();
							if (!$MerchantsMet)
							{
								// Flag as having met a merchant before.
								$MerchantsMet = true;
								// Start merchant dialogue.
								setIndividualDialoguePopup(%cryoTube, "You seem strong, human...", "dialogue");
								schedule(4250, 0, "setIndividualDialoguePopup", %cryoTube, "Perhaps your enemies are mine?", "dialogue");
								schedule(8500, 0, "setIndividualDialoguePopup", %cryoTube, "Come, come - inspect my wares...", "dialogue");
								// Open merchant window.
								schedule(12750, 0, "pushMerchantGui");
							}
							else
							{
								// Start merchant dialogue.
								setIndividualDialoguePopup(%cryoTube, "Welcome back, meatbag.", "dialogue");
								// Open merchant window.
								schedule(2500, 0, "pushMerchantGui");	// Shorter delay than first merchant encounter.
							}
						}
						else
						{
							setCryoTubeDialogue($playerSquad[$activeCharacter]);
						}
						return 0;
					}
				}
			}
		}
		else
		{
			updateEventText($whiteColor @ "Not enough action points to smash crate!");
			playAudio("Audio:cantSwap", EffectsAudioVolume.value);
			return 0;
		}
	}
	if (%direction == $southDirection)
	{//echo("checking north direction!");
		for (%i = 0; %i < $consoleList.getCount(); %i++)
		{
			%console = $consoleList.getObject(%i);
			if (%console.xIndex == $playerSquad[$activeCharacter].xIndex	&&
				%console.yIndex == $playerSquad[$activeCharacter].yIndex)
			{//echo("console detected!");
				if (!%console.broken	&&
					%console.image !$= "Sprites:ConsoleOn")
				{
					environmentInteract($southDirection);
					return 0;
				}
			}
		}
		for (%i = 0; %i < $LockersList.getCount(); %i++)
		{
			%lockers = $LockersList.getObject(%i);
			if (%lockers.xIndex == $playerSquad[$activeCharacter].xIndex	&&
				%lockers.yIndex == $playerSquad[$activeCharacter].yIndex)
			{//echo("lockers detected!");
				if (!%lockers.opened	&&
					%lockers.image $= getLockersClosedSprites())
				{//echo("calling interact for lockers!");
					environmentInteract($southDirection);
					return 0;
				}
			}
		}
	}
	if ($monsterList.getCount() > 0)
	{//echo("MONSTER LIST NOT EMPTY!");
		for (%i = 0; %i < $monsterList.getCount(); %i++)
		{
			%monster = $monsterList.getObject(%i);
			%monsterXIndex = %monster.xIndex;
			%monsterYIndex = %monster.yIndex;
			if (%x == %monsterXIndex	&&
				%y == %monsterYIndex)
			{//echo("Players melee attacks a monster!");
				// Patriarch auot-knocksback units that melee it.
				if (%monster.name $= "Patriarch")
				{
					patriarchAutoKnockBack(%monster, $activeCharacter);
				}
				
				%actionPointCost = getActionPointCost(2);
				if (%actionPointCost > $playerSquad[$activeCharacter].currentActionPoints)
				{
					updateEventText($whiteColor @ "You don't have enough action points to bash!");
					playAudio("Audio:invalidAction", EffectsAudioVolume.value);
					return 0;
				}
				if (%monster.stasis)
				{
					updateEventText($whiteColor @ "Unit is in stasis!");
					playAudio("Audio:invalidAction", EffectsAudioVolume.value);
					return false;
				}
				// Check for Support class Hulk talent!
				if ($playerSquad[$activeCharacter].characterClass == 3	&&
					getWord($playerSquad[$activeCharacter].talents, 1) == 1)
				{
					%direction = getDirectionBetweenPoints($playerSquad[$activeCharacter].xIndex, $playerSquad[$activeCharacter].yIndex, %monster.xIndex, %monster.yIndex);
					knockBackUnit(%monster, %direction);
				}
				%playerDamage = 1;
				if (isLargeUnit($playerSquad[$activeCharacter]))
				{
					%playerDamage += 5;
				}
				if ($playerSquad[$activeCharacter].bash > 0)
				{//echo("$playerSquad[$activeCharacter].bash =" SPC $playerSquad[$activeCharacter].bash);
					%playerDamage += $playerSquad[$activeCharacter].bash;
				}
				if (%monster.facing == %direction)
				{//echo("facing same direction - CRIT!");
					%playerDamage *= 2;
					// Attacks from behind Brainiens can spawn items!
					if (getRandom(1, 100) <= 50)
					{
						if (%monster.name $= "Brainien")
						{//echo("Brainien item spawn!");
							%xIndex = %monster.xIndex;
							%yIndex = %monster.yIndex;
							switch (%monster.facing)
							{
								case $northDirection:	%yIndex--;
								case $southDirection:	%yIndex++;
								case $eastDirection:	%xIndex++;
								case $westDirection:	%xIndex--;
							}
							%item = createRandomItem(getRandom(0, 1), getTilePositionFromIndices(%xIndex, %yIndex), %xIndex, %yIndex);	// Medkit/Ammo Crate.
							$mapItems.add(%item);
							setIndividualDialoguePopup($playerSquad[$activeCharacter], "What was that?!", "dialogue");
							setIndividualDialoguePopup(%item, "spawned!", "dialogue");
						}
					}
				}
				if (%monster.currentHealth <= %playerDamage)
				{
					%monster.currentHealth = 0;
					$enemiesKilledInMelee++;
					updateMissionObjectives();
				}
				else
				{//echo("%monster.currentHealth =" SPC %monster.currentHealth);
					%monster.currentHealth -= %playerDamage;
				}
				$GlobalStats.totalMeleeAttacks++;
				monsterHit(%i, %playerDamage, %direction, "physical");
				updateEventText($squadColor @ $playerSquad[$activeCharacter].name @ $friendlyActionColor @ " attacks" SPC $monsterColor @ %monster.name SPC $defaultColor @ "for" @ $damageColor SPC %playerDamage SPC "damage" @ $defaultColor @ "!");
				playMetalLightSfx();
				// Set this variable before invoking paralyzeSquadMember()! Used for when paralyzeSquadMember() is called when a monster's attack paralyzes (and kills) a squad member.
				$lastHitSquadMemberName = $playerSquad[$activecharacter].name;
				/*switch$ (%monster.name)
				{
					case "Mummy":
						paralyzeSquadMember($activeCharacter);
				}*/
				setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
				setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
				$fogOfWar.showFogOfWar();
				initializeReadout();
				scheduleMonsterTurn();
				return false;
			}
		}
	}
	switch (%roomPositionStatus)
	{
		case -4:	// Opened door.
			return 1;
		case -3:	// Closed door.
			%actionPointCost = getActionPointCost(8);
			if (%actionPointCost > $playerSquad[$activeCharacter].currentActionPoints)
			{
				updateEventText($whiteColor @ "Not enough action points to open the door!");
				playAudio("Audio:invalidAction", EffectsAudioVolume.value);
				return 0;
			}
			setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
			$playerSquad[$activeCharacter].facing = %direction;
			initializeReadout();
			inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
			switch ($mazeRoom[%x, %y].frame)
			{
				case 22:
					%image = getImageFromOverworldLocation();
					%frame = 23;
					$mazeRoom[%x, %y].frame = %frame;
					$mazeRoom[%x, %y].Image = %image;
					$mazeRoom[%x, %y].Frame = %frame;
				case 24:
					%image = getImageFromOverworldLocation();
					%frame = 25;
					$mazeRoom[%x, %y].frame = %frame;
					$mazeRoom[%x, %y].Image = %image;
					$mazeRoom[%x, %y].Frame = %frame;
			}
			$mazeRoom[%x, %y].SceneLayer = $DoorOpenedLayer;
			$mazeRoom[%x, %y].status = -4;
			openDoorTrigger(%x, %y, $playerSquad[$activeCharacter]);
			setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
			$GlobalStats.totalDoorsOpened++;
			playAudio("Audio:sliding_door_open_01", EffectsAudioVolume.value);
			%direction = getDirectionBetweenPoints($playerSquad[$activeCharacter].xIndex, $playerSquad[$activeCharacter].yIndex, %x, %y);
			updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $friendlyActionColor @ "opens door" @ $defaultColor @ ".");
			triggerContextSensitiveUI(false);
			scheduleMonsterTurn();						// This is normally called in Sprite::onPositionTarget() but in this case the character does not move, so we invoke here instead so monsters get their turn(s).
			$fogOfWar.showFogOfWar();
			return 0;
		case -2:	// Possibly smashable wall (for squad members, NOT monsters).
			%actionPointCost = getActionPointCost(2);
			if (%actionPointCost > $playerSquad[$activeCharacter].currentActionPoints)
			{
				updateEventText($whiteColor @ "You don't have enough action points to bash!");
				playAudio("Audio:invalidAction", EffectsAudioVolume.value);
				return 0;
			}
			setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
			$fogOfWar.showFogOfWar();
			$playerSquad[$activeCharacter].facing = %direction;
			initializeReadout();
			if (($mazeRoom[%x, %y].painting).image $= "Sprites:PaintingVoxPickup"	&&
				($squadInventory.getCount() < $squadInventoryMaxItems))
			{
				spawnItemInPlayerInventory(19);
				($mazeRoom[%x, %y].painting).safeDelete();
				// Don't allow chance to smash the wall tile!
				%direction = getDirectionBetweenPoints($playerSquad[$activeCharacter].xIndex, $playerSquad[$activeCharacter].yIndex, %x, %y);
				setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
				inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
				updateEventText($squadColor @ $playerSquad[$activeCharacter].name @ $friendlyActionColor @ " grabs the painting" @ $defaultColor @ "!");
				playAudio("Audio:grabPainting", EffectsAudioVolume.value);
				scheduleMonsterTurn();						// This is normally called in Sprite::onPositionTarget() but in this case the character does not move, so we invoke here instead so monsters get their turn(s).
				return 0;
			}
			%smashChance = getRandom(1, 100);
			%smashWallChance = $smashableWallChance;
			if ($playerSquad[$activeCharacter].bash > 0)
			{
				%smashWallChance += $playerSquad[$activeCharacter].bash * $bashChanceModifier;
			}
			if (%smashChance <= %smashWallChance)		// Chance to knock down this type of "unsmashable" wall!
			{
				$GlobalStats.totalWallsSmashed++;
				destroyTerrainTile(%x, %y);
				inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
				updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $friendlyActionColor @ "smashes down a wall" @ $defaultColor @ "!");
				autoTargetEnemyUnit($autoTargetDelay * 2);		// Is this necessary here?
			}
			else
			{
				inPlaceActionGrowAndShrink($playerSquad[$activeCharacter]);
				playRandomHeavyMetalSfx();
				if (isObject($mazeRoom[%x, %y].light))
				{
					flickerLight($mazeRoom[%x, %y].light);
				}
				updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $greyColor @ "fails to smash a wall" @ $defaultColor @ "!");
			}
			%direction = getDirectionBetweenPoints($playerSquad[$activeCharacter].xIndex, $playerSquad[$activeCharacter].yIndex, %x, %y);
			setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
			scheduleMonsterTurn();						// This is normally called in Sprite::onPositionTarget() but in this case the character does not move, so we invoke here instead so monsters get their turn(s).
			return 0;
		case -1:	// Unpassable, unsmashable wall!
			return 0;
		case 0:		// Smashable wall.
			%actionPointCost = getActionPointCost(2);
			if (%actionPointCost > $playerSquad[$activeCharacter].currentActionPoints)
			{
				updateEventText($whiteColor @ "You don't have enough action points to bash!");
				playAudio("Audio:invalidAction", EffectsAudioVolume.value);
				return 0;
			}
			setCurrentActionPoints($activeCharacter, $playerSquad[$activeCharacter].currentActionPoints - %actionPointCost);
			setSquadMemberFacing(%direction, $playerSquad[$activeCharacter]);
			$fogOfWar.showFogOfWar();
			initializeReadout();
			%position = $mazeRoom[%x, %y].position;
			$mazeRoom[%x, %y].safeDelete();		// Because of the way safeDelete() is scheduled we don't know if it will be deleted prior to showFogOfWar() triggering. Null it on the next line!
			$mazeRoom[%x, %y] = null;
			playAudio("Audio:wallSmash", EffectsAudioVolume.value);
			updateEventText($squadColor @ $playerSquad[$activeCharacter].name SPC $friendlyActionColor @ "smashes down a wall" @ $defaultColor @ "!");
			$fogOfWar.showFogOfWar();
			scheduleMonsterTurn();						// This is normally called in Sprite::onPositionTarget() but in this case the character does not move, so we invoke here instead so monsters get their turn(s).
			return 0;
		case 1:		// Passable tile.
			return 1;
	}
}
function validActiveCharacter ()
{//echo("validActiveCharacter()");
	if ($activeCharacter == 0	||
		$activeCharacter == 1	||
		$activeCharacter == 2	||
		$activeCharacter == 3)
	{
		return true;
	}
	else
	{error("	Invalid active character! " SPC $activeCharacter);
	}
	return false;
}
function moveCharacter (%characterIndex, %direction)
{//echo("moveCharacter()");echo("%direction =" SPC %direction);
	if (%direction != $northDirection	&&
		%direction != $southDirection	&&
		%direction != $eastDirection	&&
		%direction != $westDirection)
	{error("   function moveCharacter(%characterIndex, %direction) was passed an invalid direction parameter!");
		return;
	}
	if (!validActiveCharacter())
	{error("	Invalid active character!");
		return;
	}
	if ($squadBunkerDown	&&
		$playerSquad[%characterIndex].characterClass == 3)
	{
		updateEventText($whiteColor @ "Can't move with Bunker Down active!");
		return;
	}
	%actionPointCost = getActionPointCost(1);
	if (%actionPointCost > $playerSquad[%characterIndex].currentActionPoints)
	{
		updateEventText($whiteColor @ "You don't have enough action points to move!");
		playAudio("Audio:invalidAction", EffectsAudioVolume.value);
		return;
	}
	clearTargetIndicators(false);
	// Delay new target setting!
	$targetIndicatorFree = false;
	schedule(500, 0, "freeTargetIndicator");
	engageCharacterActionLock();
	%xIndex = 0;
	%yIndex = 0;
	switch (%direction)
	{
		case $northDirection:	%yIndex = -1;
		case $southDirection:	%yIndex =  1;
		case $eastDirection:	%xIndex =  1;
		case $westDirection:	%xIndex = -1;
	}
	if (!canMoveToMapPosition($playerSquad[%characterIndex].xIndex + %xIndex, $playerSquad[%characterIndex].yIndex + %yIndex, %direction))
	{
		schedule($CharacterActionLockDelay, 0, "releaseCharacterActionLock");
		return;
	}
	%targetPosition = getTilePositionFromIndices($playerSquad[%characterIndex].xIndex + %xIndex, $playerSquad[%characterIndex].yIndex + %yIndex);
	moveUnit($playerSquad[%characterIndex], getWord(%targetPosition, 0), getWord(%targetPosition, 1), 20.0, true, true, true, 0.0, false);
	$playerSquad[%characterIndex].xIndex += %xIndex;
	$playerSquad[%characterIndex].yIndex += %yIndex;
	if (!$GravityWellOn)
	{
		setCurrentActionPoints(%characterIndex, $playerSquad[%characterIndex].currentActionPoints - %actionPointCost);
	}
	$playerSquad[%characterIndex].facing = %direction;
	setSquadMemberFacing(%direction, $playerSquad[%characterIndex]);
	mountSightToUnit($playerSquad[%characterIndex]);
	initializeReadout();
}