// Sample ET script.

// Scripts for bots are executed as part of the bots initialization process
// When this script is executed, there is a variable called 'this' that references the bot
// Properties can be set on 'this' and functions can be called on 'this' to tweak the bots behavior

print("Sample Script for Enemy Territory");

////////////////////////////////////////////
// Customize some general bot properties. //
////////////////////////////////////////////

// FieldOfView is the angle(in degrees) that the bot can 'see' in front of them.
this.FieldOfView = 90.0;

// ReactionTime is the time delay(in seconds) from when a bot first see's a target, to when
// the bot will begin to react and target them.
this.ReactionTime = 0;

// MemorySpan is how long it takes(in seconds) for a bot to consider his memory of someone or something
// 'out of date' and not considered for targeting and such
this.MemorySpan = 2.0;

// AimPersistance is how long the bot will aim in the direction of a target after the target has gone out of view.
// This is useful for keeping the bot aiming toward the target in the event of brief obstructions of their view.
this.AimPersistance = 2.0;

// MaxViewDistance is the maximum distance(in game units) the bot is capable of seeing something.
// This could be tweaked lower for maps with fog or for a closer to human view distance
// Typically this value is best set in the map script in the OnBotJoin callback.
//this.MaxViewDistance = 10000.0;

// These 3 values are aim properties. Care must be taken when tweaking aim properties, since
// improper values can produce aim oscillations and hurt the bots combat abilities.
this.MaxTurnSpeed = 720.0; // degree's / second
this.AimStiffness = 75.0;
this.AimDamping = 10.0;

// AimTolerance is an angle tolerance that effects the bot aiming and targeting. When a bot acquires a target
// and turns toward the aim position for the target, the AimTolerance is the angle that the aim vector must be within
// in order to be considered close enough to begin firing. This parameter is used by the TurnTo functions of the bot
this.AimTolerance = 3.0;

/////////////////////////////////////
// Register bot weapon properties. //
/////////////////////////////////////

// Every weapon is capable of reading from the bots script table and setting custom properties
// By default, every weapon has 'Bias', 'AimError', and 'AimOffset' More custom parameters may
// be available to tweak on certain weapons.

// To set the properties of a weapon in the script, you must access the weapon table through the bot
// like so:
//      this.Weapons["WEAPON_NAME"].Property = value;
// The WEAPON_NAME depends on the weapon, and the Property is the specific property you wish to set
// The value depends on the property you are setting. Values can be numbers, 3d vectors, strings, etc...

// NOTE: Changed in 0.52
// Weapon tables are no longer created automatically for each bot like it was previously. This saves a bit
// of memory for each bot. Because of this, the syntax for setting bot properties has changed.

// Properties
// Bias - a multiplier used when calculating the bots desire to use a weapon.
//        Setting to 0.0 will make the bot not use the weapon. 
//        Setting to 0.5 will make the weapon 50% less desirable.
//        Setting to 2.0 will make the weapon twice as desirable as default.
// AimError - a 2d vector value representing the range of random error applied to shots
//        Setting to Vector2(0.0, 0.0); adds no error to the aim.
//        Setting to Vector2(0.0, 5.0); adds +- 5 units of error on the z axis(up/down), none on the horizontal axis.
// AimOffset - a 3d vector value representing the default offset the weapon should be aimed
//        Setting to Vector3(0.0, 0.0, 0.0); adds no offset to the aim.
//        Setting to Vector3(0.0, 0.0, 5.0); adds +- 5 units of offset on the z axis(up/down)
//        Offset is applied before error. For an example, suppose you wanted a bots sniper weapon
//        to be aimed lower to reduce headshots, you might set the AimOffset to Vector3(0.0, 0.0, -5.0);
//        to make them aim 5 units lower. You could also tweak the AimError to widen their random vertical aim
//this.Weapons["WEAPON_NAME"] = 
//{
//	Bias = 0.5,
//	AimError = Vector2(0.0, 6.0),
//	AimOffset = Vector3(0.0, 0.0,5),
//};

// Note the new syntax creates a script table for each weapon. Add additional properties within the brackets of existing
// weapons, or create additional tables for weapons not yet defined.

///////////////////////////////////
// Register bot item properties. //
///////////////////////////////////

// In addition to weapons, some games may support items. Items are usually not weapons. They are
// more often items held in the bots inventory and used at various times. 
// Things such as usable health kits, gas masks, silencers, etc...

// Item properties are set the same way as weapon properties, except they use the Items table
// like so:
//       this.Items["ITEM_NAME"].Property = value;

///////////////////////////////////
// Register bot goal properties. //
///////////////////////////////////

// Goals represent an action the bot can do in the game. Capture the flag, build an MG42, snipe, repair a vehicle,
// plant dynamite, disarm dynamite, etc... are all goals. Every bot has a list of goals that it goes through
// occasionally to calculate the desirability of. The most desirable goal is the goal the bot will do over all the others.

// In order to tweak a bots goal behavior, the user can set properties on the goals, just like weapons and items.
// Every goal has at least a 'Bias' property, similar to weapons. It has a similar effect to goals as the 
// weapon Bias has to weapons. Tweak the number higher or lower to change the desirability of the bot
// performing a certain goal.

// Goal properties are set the same way as weapon and item properties, except they use the Goals table
// like so:
// Goal Properties.
//this.Goals["DEFEND"] = 
//{
//	Bias = 2.0,
//};
//this.Goals["ATTACK"] = 
//{
//	Bias = 2.0,
//};
//this.Goals["PLANT_MINE"] = 
//{
//	Bias = 2.0,
//};

//////////////////////////////
// Additional Bot Scripting //
//////////////////////////////

// In addition to setting properties for a bot for their weapons, items, and goals,
// it is also possible to set up 'script callbacks' based on certain events that
// happen to the bot. 'script callbacks' are script functions that are called as a
// result of some event being recieved by the bot.

// Here is a simple example of a function for a script callback.
voice_func = function(_source, _params)
{
	// This function will print out the parameters key and value
	foreach ( i and p in _params )
        {
		print("Parameter # " + i +" : " + p);
        } 

	if(_params.macro && (_params.macro == VOICE.FOLLOW_ME))
	{
		this.SayVoice(VOICE.NEGATIVE);
	}
};

// And another function for a different callback.
spawn_func = function(_source, _params)
{	
	print("spawn function");
};

// Once the functions are defined, as seen above, they must then be paired with events.
// By assigning the functions to different events on the bots events table, they will be called
// when the bot recieves the events.

// In this example, the spawn_func will be called when the bot recieves an EVENT.SPAWNED
// Also, the voice_func will be called whenever the bot recieves a global, team, or private voice macro message
// Care must be taken when doing things like this, if multiple bots have these callbacks defined it can cause
// a burst of message spam to occur as the bots continuously respond to each other.
this.Events[EVENT.SPAWNED] = spawn_func;
//this.Events[EVENT.GLOBAL_VOICE] = voice_func;
this.Events[EVENT.TEAM_VOICE] = voice_func;
//this.Events[EVENT.PRIVATE_VOICE] = voice_func;

////////////////////
// Goal Scripting //
////////////////////

// Perhaps the most powerful scripting available for a bot involves completely overriding the bots behavior.
// To do this, you must define a goal function for the bot. A goal function is a script that is executed
// top to bottom, and acts much like a 'thread' The goal function is capable of blocking execution while the 
// bot performs the desired action, and resuming script execution later. 

// Consider the following function. The most important aspects of this function are the calls to 
// block, yield, and sleep

// Every bot in the game is capable of having their own goal function, executing as their own 'thread'
// Although they behave much like threads, they aren't real cooperative threads, so it is up to 
// the thread to give control back to the application and prevent application freezes due to
// infinite loops or the script taking too much time to run.

// Here is a simple example of a goal function, with extra comments to explain it.
goalFunc = function()
{
	// The first thing this function does it block on the spawn event.
	// when you block the script, you are pausing execution, until the bot recieves the event that is passed to block
	// In this case, we are telling the script to pause execution until he recieves the EVENT.SPAWNED event
	// It is probably safest to always block your goal function on the SPAWNED event, to ensure that the bot has
	// actually made it into the game and is ready to actually move around the world and do stuff.
	block(EVENT.SPAWNED);

	// Since we're telling the bot to do something with the goto command below, we need to set it to script controlled.
	// This will ensure that the bots internal logic doesn't override the script.
	this.SetScriptControlled(true);
	
	// After the bot spawns, the script will begin executing again.
	
	// This function call requests a game entity from the game, by an numeric value(array index for programmers)
	// The index starts at 0, and can go as high as the number of entities the game supports.
	// In Quake 3 engine games such as ET, the first entities are always players.
	
	// This particular example is designed to run in a listen server, where index 0 will always be the human player(you)
	// This will end up getting the entity for player 0(the host in a listen server)
	playerEnt = GetGameEntityFromId(0);
	
	// Some error checking is always a good idea, to make sure the value that was returned is valid.
	// If you were to request an entity that doesn't exist, the function would return NULL, so it is best to 
	// always check for errors for functions that can fail.
	if(playerEnt != null)
	{
		// At this point we know that playerEnt is a valid entity, so lets send the bot to the players position.
		// The GoTo function adds a GoTo goal onto the bots goal list. This goal is responsible for navigating
		// the bot to the desired position.
		this.GoTo(GetEntPosition(playerEnt));
	}
	else
	{
		// A simple text message if an entity wasn't found, and a return; to exit the function.
		this.Say("Could't find player 0!!");
		return;
	}
	
	// Here is another block message, only this time we want to block while waiting for multiple events.
	// EVENT.GOAL_SUCCESS and EVENT.GOAL_FAILED are events that are fired as a result of goals
	// succeeding or failing. 
	// Since we gave the bot the GoTo goal above, the bot will signal the script with
	// EVENT.GOAL_SUCCESS or EVENT.GOAL_FAILED based on the outcome of the goal.
	// The goal may fail of the location is unreachable, or the bot gets stuck, etc...
	
	// Another important thing to note, and is demonstrated here, is that whichever event is fired,
	// the value will be returned by the block function, allowing you to compare the result with the 
	// list of events that were blocked on.
	
	// In this example, the script blocks and waits for EVENT.GOAL_SUCCESS or EVENT.GOAL_FAILED events. 
	// Whichever one is signalled is then being compared to EVENT.GOAL_SUCCESS in the if-statement
	if( block(EVENT.GOAL_SUCCESS, EVENT.GOAL_FAILED) == EVENT.GOAL_SUCCESS )
	{
		// If the GoTo goal succeeds, the script continues executing in here.
		
		// A simple text message to show the player that the bot made it.
		this.Say("Wohoo, I made it!.");
		
		// And then the script will start into an infinite loop.
		// NOTE: IT IS VERY IMPORTANT THAT ANY TIME YOU USE LOOPS, ESPECIALLY INFINITE LOOPS
		//      THAT THEY USE yield(), block(), or sleep() OCCASIONALLY TO ALLOW THE GAME TO
		//      CONTINUE PROCESSING. FAILING TO DO THIS CAN FREEZE THE GAME INTO AN INFINITE LOOP
		
		// This loop shows some simple behavior. Since we know now that the bot is at the players
		// former location, this code simply makes the bot look for ally players and turn to face them 
		// when they see them.
		dowhile(true)
		{
			// This calls the GetNearestAlly function on the bot, which searches his SensoryMemory
			// for the nearest game entity that is in the PLAYER category (CAT stands for CATEGORY)
			closest = this.GetNearestAlly(CAT.PLAYER, 0);
			
			// If there was a valid entity returned...
			if(closest != null)
			{
				// We then get the stored information for this entity.
				info = this.GetTargetInfo(closest);
				
				// Go ahead and start switching weapons as soon as we see them.
				this.SelectWeapon(WEAPON.KNIFE);
				
				// Lets go ahead and charge them too.
				this.MoveTowards(info.Position);
				
				// Once we have this information we can tell the bot to turn towards the entities position
				if( this.TurnToPosition(info.Position) == true )
				{
					// The TurnToPosition returns true once the bot is aiming within a small tolerance (~3 degrees)
					// of the target. The result of TurnToPosition is used for example by the weapon system
					// as an indication of when the bot is aiming enough at the target to begin shooting.
					
					// Since weapon switching doesn't happen instantaneously, it's a good idea to make sure
					// that the current weapon is the desired weapon. If you don't, and blindly start firing
					// the bot could begin firing the wrong weapon.
					if( this.GetCurrentWeapon() == WEAPON.KNIFE )
					{
						// The bot is now aiming directly at the player with the knife equipped.
						// Time for battle cry and attack!
						//this.Say("Die Mofo!");
						this.FireWeapon();
					}
				}
			} 
			else
			{
				// If this bot doesn't see an ally player, the value of closest will be null, 
				// and bring us to this code, which makes the bot say some simple message.
				this.Say("No Target.");
				
				// And then sleep.
				// sleep pauses script execution much like block() and yield() do, except with sleep
				// it is based on time in seconds.
				sleep(1.0);
				
				// After the 1 second pause, this continue tells the script to jump back to the top of the loop.
				// This isn't really required, because it would happen anyways after the yield() below, but
				// we'll show it just for examples sake.
				continue;
			}

			// yield pauses script execution for 1 frame. The next time the bot 'thinks' the script will be executed.
			// This yield will only happen if the bot sees an ally player, and turns toward them. By using yield,
			// we ensure that the bot will turn toward the target a little more every frame.
			yield();
		}
	}
	else
	{
		// If the GoTo goal fails, the script continues executing in here.
		this.Say("Sorry, couldn't make it.");
		// Since we aren't looping or anything after this, the script will just end at this point.
		// In reality you would probably want to either loop and keep trying, or
		// return full control back to the bot so he can continue playing the game normally.
		// Doing nothing like this will result in a bot that just stands around, since
		// technically he is still set to 'script controlled'
	}
};

// In order to execute the goal function we defined above, we need to create a script thread.
// A bot can have any number of script threads running at one time, but care must be taken to
// prevent multiple scripts from fighting over bot control.
this:thread(goalFunc);

