/*************************************************************************************************
* _npc1# Entity Script    NPC script1
* 
* Author: GD
* Date:   4 July 2002
*
* Desc:    An NPC (non-playable charater) script for a set of NPCs that all support "Person Script"
			Person script appeared in earlier versions of Open Zelda, and now its here as a set of
			functions in this script and in the NPC library. Person script allows you to create cut-scenes
			using NPCs, or make NPCs perform certain actions at certain times with ease.
			
* Usage:   Use the MessageMap() function to set the NPCs text (what they say to the player), see the
		    Open Zelda documentation for help on that function.
		    
		    The person scripting is usually called from the screen or group script the NPC is in, since
		    all the functions are in this file you have to use CallFunction() to issue commands to the NPC.
		    There are several person script functions:
		    	
		    	WalkTo( x, y, instruction ) - makes the NPC walk to a point on the map, with collision 
		    	detection.
		    	
		    	Wait( time, instruction ) - makes the NPC wait, the time is in milliseconds
		    	
		    	WaitFor( otherNpc, targetInstruction, instruction) - used to wait for another
		    	NPC to reach a certain point in their person scripts
		    	
		    	Say(text, instruction) - Puts a text box on screen
		    	There are more functions as well, just look at the bottom of this file...
		    	
		    Using person script is easy as it doesnt behave like normal code, one instruction will
		    automatically carry on from the next, so if you wanted an NPC to walk to a point, then
		    wait 2 seconds and walk to another you would just do this in a screen or group script:
		    	
		    	CallFunction("npc1", false, "WalkTo", "nnn", 100,  200, 0);
		    	CallFunction("npc1", false, "Wait",   "nn",  2000,    	1);
		    	CallFunction("npc1", false, "WalkTo", "nnn", 50,   340, 2);
		    
		    Note the last parameter of each function call, this is the instruction number, every
		    function call you make should have a different number, and if you want each event to
		    carry on from the last they should be consecutive numbers (1, 2, 3 ..etc)

*
* Sprites: _NPCSheet1.spt
*         
**************************************************************************************************/
#include <entity>
#include <general>
#include <animation>

//   Global Data
new headDist 		= 8;        // Distance head is from the body
new shadowDist		= 1;
new Talking 		= false;    // Is the player currently talking to this NPC?
new HeadDirection   = south;	// Direction of NPCs Head
new param			= 0;

new head[4][20];	// 4 strings to hold each image of the NPCs head
new walk[4][20];	// 4 strings to hold the animation id's of the walking animations

//----------------------------------------
// Name: main()
//----------------------------------------
main()
{
	
	if (FirstRun())
	{
		new n;
		// Get the parameter for this script
		param = GetParam("this");
		
		// Set some general parameters
		SetActiveDist("this", 320);
		SetType("this", npcType);
		AllocateStrings("this", 10, 640 );   
		SetSpeed( "this", 40 );

		// Create the walking animations
		for ( n = 0; n < 4; n++ )
			CreateAnim(6, walk[n]);
			
		// Add Different sprites depending on the parameter	
		// Theres a different parameter for each chatacter
		if ( param == 'a' )	// Bar-tender man
		{
			head[0] = "_npc1aa";  // Assign the head sprites
			head[1] = "_npc1ab";
			head[2] = "_npc1ac";
			head[3] = "_npc1ad";
			AddAnimframe(walk[0], 0, 0, "_npc1ae1");  // walking north
			AddAnimframe(walk[0], 0, 0, "_npc1ae2");
			AddAnimframe(walk[1], 0, 0, "_npc1af1");  // walking east
			AddAnimframe(walk[1], 0, 0, "_npc1af2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1ag1");  // walking south
			AddAnimframe(walk[2], 0, 0, "_npc1ag2");
			AddAnimframe(walk[3], 0, 0, "_npc1ah1");  // walking west
			AddAnimframe(walk[3], 0, 0, "_npc1ah2");  
		}
		if ( param == 'b' )		// Old lady
		{
			head[0] = "_npc1ba"; 
			head[1] = "_npc1bb";
			head[2] = "_npc1bc";
			head[3] = "_npc1bd";
			AddAnimframe(walk[0], 0, 0, "_npc1be1");  
			AddAnimframe(walk[0], 0, 0, "_npc1be2");
			AddAnimframe(walk[1], 0, 0, "_npc1bf1");  
			AddAnimframe(walk[1], 0, 0, "_npc1bf2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1bg1");  
			AddAnimframe(walk[2], 0, 0, "_npc1bg2");
			AddAnimframe(walk[3], 0, 0, "_npc1bh1");  
			AddAnimframe(walk[3], 0, 0, "_npc1bh2");  
			SetSpeed( "this", 30 );
		}
		if ( param == 'c' )		// Old Guy
		{
			head[0] = "_npc1ca";
			head[1] = "_npc1cb";
			head[2] = "_npc1cc";
			head[3] = "_npc1cd";
			AddAnimframe(walk[0], 0, 0, "_npc1ce1");
			AddAnimframe(walk[0], 0, 0, "_npc1ce2");
			AddAnimframe(walk[1], 0, 0, "_npc1cf1");
			AddAnimframe(walk[1], 0, 0, "_npc1cf2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1cg1");
			AddAnimframe(walk[2], 0, 0, "_npc1cg2");
			AddAnimframe(walk[3], 0, 0, "_npc1ch1");
			AddAnimframe(walk[3], 0, 0, "_npc1ch2");
			SetSpeed( "this", 30 );  
			shadowDist = 0;
		}
		if ( param == 'd' )		// Priest
		{
			head[0] = "_npc1da";
			head[1] = "_npc1db";
			head[2] = "_npc1dc";
			head[3] = "_npc1dd";
			AddAnimframe(walk[0], 0, 0, "_npc1de1");
			AddAnimframe(walk[0], 0, 0, "_npc1de2");
			AddAnimframe(walk[1], 0, 0, "_npc1df1");
			AddAnimframe(walk[1], 0, 0, "_npc1df2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1dg1");
			AddAnimframe(walk[2], 0, 0, "_npc1dg2");
			AddAnimframe(walk[3], 0, 0, "_npc1dh1");
			AddAnimframe(walk[3], 0, 0, "_npc1dh2");
		}
		if ( param == 'e' )		// Theif
		{
			head[0] = "_npc1ea";
			head[1] = "_npc1eb";
			head[2] = "_npc1ec";
			head[3] = "_npc1ed";
			AddAnimframe(walk[0], 0, 0, "_npc1ee1");
			AddAnimframe(walk[0], 0, 0, "_npc1ee2");
			AddAnimframe(walk[1], 0, 0, "_npc1ef1");
			AddAnimframe(walk[1], 0, 0, "_npc1ef2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1eg1");
			AddAnimframe(walk[2], 0, 0, "_npc1eg2");
			AddAnimframe(walk[3], 0, 0, "_npc1eh1");
			AddAnimframe(walk[3], 0, 0, "_npc1eh2");
			headDist = 9;
		}
		if ( param == 'f' )		// Young girl
		{
			head[0] = "_npc1fa";
			head[1] = "_npc1fb";
			head[2] = "_npc1fc";
			head[3] = "_npc1fd";
			AddAnimframe(walk[0], 0, 0, "_npc1fe1");
			AddAnimframe(walk[0], 0, 0, "_npc1fe2");
			AddAnimframe(walk[1], 0, 0, "_npc1ff1");
			AddAnimframe(walk[1], 0, 0, "_npc1ff2"); 
			AddAnimframe(walk[2], 0, 0, "_npc1fg1");
			AddAnimframe(walk[2], 0, 0, "_npc1fg2");
			AddAnimframe(walk[3], 0, 0, "_npc1fh1");
			AddAnimframe(walk[3], 0, 0, "_npc1fh2");
			headDist = 10;
		}	
	}
	
	// Check if the Player wants to talk to this NPC
	if ( CallFunction("_npclib", true, "CheckForTalk", "NULL") )
		Talking = true;
		
	// Check if the player has just finished talking to an NPC
	if (Talking && FinishedReading())
	{
		CallFunction("_npclib", true, "AfterTalk", "NULL");
		Talking = false;
	}

	// Call a function for the NPC depending on its state
	switch( GetState("this") )
	{
		case standing:
			Stand(true);
		case waiting:
			Stand(false);
		case walking:
			Walk();
	}
}

//----------------------------------------
// Name: Stand()
//----------------------------------------
Stand( FacePlayer )
{
	new dir = GetDirection("this");
	new x = GetX("this");
	new y = GetY("this");
	SetAnimCount(walk[dir], 0);
	
	// Make the NPC face the player
	if ( FacePlayer )
	{
		CallFunction("_npclib", true, "FacePlayer", "NULL");
		HeadDirection = GetDirection("this");
	}
			
	// Get the width and height of the Current Image
	new width  = GetAnimWidth(walk[dir]);
	new height = GetAnimHeight(walk[dir]);
	
	// Draw the Body and the Head images
	if (isVisible("this"))
	{
		DrawAnimNoInc( walk[dir], x, y, y + height ); 		// Draw the body
		PutSprite( head[HeadDirection], x, y - headDist, y + height); // Draw head		
		PutSprite("shadow1", x, y + shadowDist, 2);			// Draw shadow
	}
	
	// Set a solid collision rectangle around the NPC
	SetCollisionRect("this", 0, true, x, y, x + width, y + height);
}

//----------------------------------------
// Name: Walk()
//----------------------------------------
Walk()
{
	new Angle;
	new dir = GetDirection("this");
	new x = GetX("this");
	new y = GetY("this");
	HeadDirection = dir;
		
	// Get the width and height of the Current Image
	new width  = GetAnimWidth(walk[dir]);
	new height = GetAnimHeight(walk[dir]);
	
	// Move the NPC if the game is completely unpaused
	if (GetPauseLevel() == 0)
	{
		// Check for Collisions
		if (!AngleCollide("this", 5, 5, 126, 0, width / 2, height / 2))
		{
			// Get the angle between the NPC and the point it's walking to
			Angle = CalculateAngle(x, y, GetValue("this", 0), GetValue("this", 1));
			SetMoveAngle("this", Angle);
			AngleMove("this");
		}
	}
	
	// Set the NPCs direction from its Move Angle
	SetDirFromAngle("this");
	
	// Get the new coordinates in x and y
	x = GetX("this");
	y = GetY("this");
	
	// Check if the NPC can stop walking
	if ( x == GetValue("this", 0) && y == GetValue("this", 1))
		SetState("this", standing);
	
	// Draw the NPC
	if (isVisible("this"))
	{
		DrawAnim( walk[dir], x, y, y + height ); 			// Draw the body
		PutSprite( head[dir], x, y - headDist, y + height); // Draw head		
		PutSprite("shadow1", x, y + shadowDist, 2);		    // Draw shadow
	}
	
	// Set a collision rectangle around the NPC
	SetCollisionRect("this", 0, false, x, y, x + width, y + height);
}


// *************************************************************************
// Person Script Functions below
// *************************************************************************
//----------------------------------------
// Name: SetInstruction()
//----------------------------------------
public SetInstruction( NewInstruction, instruction )
{
	if ( instruction != -1 )
	{
		if ( instruction != GetValue("this", 4) ) // Check this is the current instruction
			return 0;
	}
	
	// The quest maker can use this to reset the npc's person script
	// back to any point, or foward.
	SetValue("this", 4, NewInstruction);
	return 1;
}

//----------------------------------------
// Name: GetInstruction()
//----------------------------------------
public GetInstruction()
{
	// Return the current Person Script instruction
	return GetValue("this", 4);
}

//----------------------------------------
// Name: WalkTo()
//----------------------------------------
public WalkTo( x, y, instruction )
{
	
	if ( instruction != -1 )
	{
		if ( instruction != GetValue("this", 4) ) // Check this is the current instruction		
			return 0;
	}
		
	// Handle this in the NPC library
	return CallFunction("_npclib", true, "WalkTo", "nnn", x, y, instruction);
	return 0;
}

//----------------------------------------
// Name: Wait()
//----------------------------------------
public Wait( timeToWait, instruction )
{
	if ( instruction != -1 )
	{		
		if ( instruction != GetValue("this", 4) ) // Check this is the current instruction
			return 0;
	}
	
	if ( GetPauseLevel() != 0)
		return 0;
		
	// Handle this in the NPC library
	return CallFunction("_npclib", true, "Wait", "nn", timeToWait, instruction);
}

//----------------------------------------
// Name: WaitFor()
//----------------------------------------
public WaitFor( WhoToWaitFor[], TargetInstruction, instruction )
{
	if ( instruction != -1 )
	{
		if ( instruction != GetValue("this", 4) ) // Check this is the current instruction
			return 0;
	}
	
	// Handle this in the NPC library
	return CallFunction("_npclib", true, "WaitFor", "snn", WhoToWaitFor, TargetInstruction, instruction);
}

//----------------------------------------
// Name: Say()
//----------------------------------------
public Say( Text[], instruction )
{
	if ( instruction != -1 )
	{
		if ( instruction != GetValue("this", 4) ) // Check this is the current instruction
			return 0;
	}
	
	// Handle this in the NPC library
	return CallFunction("_npclib", true, "Say", "sn", Text, instruction);
}

//----------------------------------------
// Name: SetBodyDirection()
//----------------------------------------
public SetBodyDirection( dir, instruction )
{
	// Sets the direction of the player
	// Check this is the current instruction
	if ( instruction != GetValue("this", 4) )
		return 0;
	
	SetDirection("this", dir);
	SetValue("this", 4, instruction + 1);
}

//----------------------------------------
// Name: SetHeadDirection()
//----------------------------------------
public SetHeadDirection( dir, instruction )
{
	// Sets the direction of the player
	// Check this is the current instruction
	if ( instruction != GetValue("this", 4) )
		return 0;
	
	HeadDirection = dir;
	SetValue("this", 4, instruction + 1);
}

