this.GoalType = "CAMP";
this.Version = 1;
this.DefaultPriority = 0.5;
this.MinRadius = 30;
this.DefaultRenderRadius = 1024;
this.MaxUsers_InUse(1);
this.MaxUsers_InProgress(2);
this.RolePriorityBonus = 0.01;

//////////////////////////////////////////////////////////////////////////
// Couple callbacks to enforce additional constraints on property values.
CheckMinCamp = function(var)
{
	if(var <= this.MaxCampTime)
	{
		return true;
	}
	// returning any string is an error string
	return "MinCampTime must be <= MaxCampTime";
};
CheckMaxCamp = function(var)
{
	if(var >= this.MinCampTime)
	{
		return true;
	}
	// returning any string is an error string
	return "MaxCampTime must be >= MinCampTime";
};

// Schema is a 'blueprint' of expected data that the goal needs to operate.
// It is used for validating the expected fields and field values for correctness.
this.Schema = Schema();
this.Schema.AimVectors = Validate.TableOf("vector3").Default({});
this.Schema.Weapons = Validate.TableOf("int").CheckValue(WEAPON).Default({});
this.Schema.Stance = Validate.Enum("stand","crouch","prone","peek").Default("stand");
this.Schema.MinCampTime = Validate.NumRange(0,9999).Default(10).CheckCallback(CheckMinCamp);
this.Schema.MaxCampTime = Validate.NumRange(0,9999).Default(20).CheckCallback(CheckMaxCamp);


//////////////////////////////////////////////////////////////////////////

this.InitNewGoal = function()
{
	// the schema will initialize all fields with a default
	// so as long as everything has a default, we can let the schema
	// set up the data for us.
	this.Schema.Check(this);

	this.AimVectors[0] = GetLocalFacing();
	localEnt = GetLocalEntity();

	if (GetEntFlags(localEnt, ENTFLAG.CROUCHED))
	{
		this.Stance = "crouch";
	}
	else if (GetEntFlags(localEnt, ENTFLAG.PRONE))
	{
		this.Stance = "prone";
	}

	this.Radius = 32;
	this.DisableCM = 0;
};

//////////////////////////////////////////////////////////////////////////

this.UpgradeVersion = function(Props)
{
	if(Props.OldType=="attack" || Props.OldType=="defend" || Props.OldType=="snipe")
	{
		//Props.OldType = null;
		Props.Version = 1;
		Props.AimVectors = { ToVector(Props.Facing) };

		this.Schema.Check(Props);

		// cs: can't do this since it is unreliable. constructable bridges for example
		//this.SetPosition( GroundPoint(this.GetPosition()) );

		// offset instead
		this.SetPosition(this.GetPosition() + Util.WaypointGoalOffset);

		this.SetBounds(Vec3(-5,-5,0),Vec3(5,5,96));

		return;
	}

	// version 0 is string,string key/values from the old waypoint format
	if(Props.Version==0)
	{
		// Version 1 Schema, upgrade from waypoint format
		Props.Version = 1;
		Props.AimVectors = { ToVector(Props.Facing) };

		this.Schema.Check(Props);

		// cs: can't do this since it is unreliable. constructable bridges for example
		//this.SetPosition( GroundPoint(this.GetPosition()) );

		this.SetBounds(Vec3(-5,-5,0),Vec3(5,5,96));
	}
	else if(Props.Version == this.Version)
	{
		this.Schema.Check(Props);

		this.AimVectors = Props.AimVectors;
		this.Stance = Props.Stance;

		this.MinCampTime = Props.MinCampTime;
		this.MaxCampTime = Props.MaxCampTime;

		// convert from indexed table to weapon id keyed table
		this.Weapons = Props.Weapons;
		if (!this.Weapons)
		{
			this.Weapons = {};
		}
		else if ( tableCount(this.Weapons) > 0 )
		{
			if (this.Weapons[WEAPON.PANZERFAUST]) {
				this.Weapons[WeaponTable.AlliedPanzer] = 1;
			}
			this.LimitToWeapon(this.Weapons);

			this.DefaultPriority += 0.2;
		}

		if ( Props.DisableCM ) {
			this.DisableCM = Props.DisableCM;
		}
		else {
			this.DisableCM = 0;
		}

		// detect 0 facing vectors
		if ( typeId(Props.AimVectors) == 6 )
		{
			foreach(id and facing in Props.AimVectors ) {
				if ( facing.IsZero() ) {
					Props.AimVectors[id] = null; // clear it
					Util.MapDebugPrint(this.GetName() + " facing " + id + " is zero!", true);
				}
			}
		}

		if(!this.Schema.Check(this))
		{
			this.DisableGoal(true);
		}
	}
};

//////////////////////////////////////////////////////////////////////////

this.Render = function(editMode)
{
	offset2 = Vector3(0,0,64);

	while(this.RenderGoal == true)
	{
		goalBasePos = this.GetPosition();
		goalPos = goalBasePos + offset2;

		if(this.ExtraDebugText==null)
		{
			this.ExtraDebugText = "";

			if (tableCount(this.Weapons) > 0 && (this.GoalType!="Snipe" || tableCount(this.Weapons) < 3))
			{
				foreach(wpnId and b in this.Weapons)
				{
					if(this.ExtraDebugText != "")
					{
						this.ExtraDebugText += ",";
					}
					else
					{
						this.ExtraDebugText += "Weapon: ";
					}
					this.ExtraDebugText += Util.WeaponName(wpnId);
				}
				this.ExtraDebugText += "\n";
			}

			this.ExtraDebugText += format("Stance: %s\n",this.Stance);
			this.ExtraDebugText += format("CampTime: %g to %g secs.\n",ToFloat(this.MinCampTime),ToFloat(this.MaxCampTime));

			if ( this.DisableCM ) {
				this.ExtraDebugText += format("DisableCM: %d", this.DisableCM);
			}
		}

		if ( this.Stance == "crouch" )
		{
			stanceOffset = -24;
		}
		else if ( this.Stance == "prone" )
		{
			stanceOffset = -50;
		}
		else
		{
			stanceOffset = 0;
		}

		this.RenderDefault();

		if(this.IsDisabled())
		{
			DrawLine(goalPos,goalBasePos,COLOR.RED,2);
		}
		else
		{
			DrawLine(goalPos,goalBasePos,COLOR.GREEN,2);
		}

		if ( typeId(this.AimVectors) == 6 )
		{
			aimPos = goalPos + Vector3(0,0,stanceOffset);
			foreach ( id and face in this.AimVectors )
			{
				DrawArrow(aimPos,aimPos+face*32,COLOR.BLUE,2);
				DrawText3d(aimPos+face*32,ToString(id),COLOR.WHITE,2,512);
			}
		}

		sleep(2);
	}
};

//////////////////////////////////////////////////////////////////////////

this.SaveToTable = function(SaveTable)
{
	// save the goal specific properties
	SaveTable.AimVectors = this.AimVectors;
	if (this.Stance!="stand"){ SaveTable.Stance = this.Stance; }
	SaveTable.MaxCampTime = this.MaxCampTime;
	SaveTable.MinCampTime = this.MinCampTime;

	// don't save Weapons table if all sniper weapons are allowed
	if ((this.GoalType!="Snipe" || tableCount(this.Weapons) < 3) && tableCount(this.Weapons) > 0){
		SaveTable.Weapons = this.Weapons;
	}

	if (this.DisableCM) { SaveTable.DisableCM = this.DisableCM; }
};

//////////////////////////////////////////////////////////////////////////

this.Help = function()
{
	print(this.GoalType,"goal, version",this.Version);
	print("Requires:");
	print("     facing - Direction the bot should face");
	print("Optional:");
	print("     stance - stand, crouch, prone, peek");
	print("     weapon - only this weapon is usable here, supports multiple");
	print("     mincamptime - minimum time a bot should camp at the goal");
	print("     maxcamptime - maximum time a bot should camp at the goal");
	print("     disablecm - disable combat movement at this goal");
};

//////////////////////////////////////////////////////////////////////////

this.SetProperty = function(property, value)
{
	proplower = property.Lower(0);
	switch(proplower)
	{
		case "facing":
		{
			if ( value == "clearall" || value == "clear" )
			{
				tableClear(this.AimVectors);
			}
			else
			{
				i = ToInt(value);
				if ( typeId(i) == 1)
				{
					// make em add sequentially
					n = tableCount(this.AimVectors);
					if ( i < 0 || i > n )
					{
						i = n;
					}

					playerFace = GetLocalFacing();
					this.AimVectors[ i ] = playerFace;
					print("Facing",i,"set to",playerFace);
				}
				else
				{
					print(this.GoalType, ": Invalid facing index, expecting int");
				}
			}
		}
		case "stance": // crouch, prone, default stand
		{
			if(this.Schema.Stance.CheckPrintErrors(this,value))
			{
				this.Stance = value;
			}
		}
		case "weapon": // limit to any number of weapons
		{
			if ( value == "clearall" || value == "clear" )
			{
				tableClear(this.Weapons);
			}
			else
			{
				weap = Util.WeaponNameToId(value);
				if ( weap != -1 )
				{
					// use the weapon id as the key
					this.Weapons[ weap ] = 1;
				}
				else
				{
					print(this.GoalType, ": Invalid Weapon Name" );
					print( "Valid Weapon Names: ");
					Util.ListTableMembers(WEAPON);
				}
			}

			this.LimitToWeapon(this.Weapons);
		}
		case "snipe":
		{
			this.Weapons = Util.IndexedSniperWeapons;
			this.LimitToWeapon(this.Weapons);

			print("camp goal is now sniper weapon limited");
		}
		case "mincamptime":
		{
			v = ToFloat(value);
			if(this.Schema.MinCampTime.CheckPrintErrors(this,v))
			{
				this.MinCampTime = v;
			}
		}
		case "maxcamptime":
		{
			v = ToFloat(value);
			if(this.Schema.MaxCampTime.CheckPrintErrors(this,v))
			{
				this.MaxCampTime = v;
			}
		}
		case "disablecm":
		{
			if (value == ""){ this.DisableCM = !this.DisableCM; }
			else { this.DisableCM = ToBool(value); }
		}
		default:
		{
			print(this.GoalType, ": Invalid property:", property);
			return;
		}
	}

	// null the debug text so it will be rebuilt
	this.ExtraDebugText = null;
};

//////////////////////////////////////////////////////////////////////////
global CreateGui = function(object, guidef)
{
};
//////////////////////////////////////////////////////////////////////////

this.HudDisplay = function(window)
{
	// Create Gui elements to edit Schema properties
	this.CreateGuiFromSchema(this.Schema);
};

