# Rbot AI Cog - Alpha 0.32
# Server side - synch
#
# Created by Raynar
# Additional code by Hell Raiser

flags=0x80

symbols

message		startup
message		killed
message		pulse
message		trigger
message		timer
message		damaged
message		user0
message		user1

int			botid=0
int			botname=-1
int			team=0
template	rtemp
int			forceflags=15
int			characterflags=0

# Game setup options - set from startup cog
int			gamemode=0					local
int			difficulty=0				local

# Internal bot references
thing		rbot						local
int			botmode						local
int			forcemode					local

# Weapon fire projectiles
template	projectile_weap00=+rbotsaber		local
template	projectile_weap00a=+rbotsaber2		local
template	projectile_weap01=+punch			local
template	projectile_weap02=+bryarbolt		local
template	projectile_weap03=+stlaser			local
template	projectile_weap05=+crossbowbolt		local
template	projectile_weap05a=+crossbowbolt2	local
template	projectile_weap05b=+crossbowbolt3	local
template	projectile_weap06=+repeaterball		local
template	projectile_weap07=+raildet			local
template	projectile_weap07a=+raildet2		local
template	projectile_weap08=+seqchrg			local
template	projectile_weap08a=+seqchrg2		local
template	projectile_weap09=+concbullet		local
template	projectile_weap09a=+concblast2		local

# Force powers
template	force_lightning=+force_lightning	local
template	force_destruction=+force_dest_p4	local
template    force_blinding=+force_blind			local

# Pulled weapon templates
template	tpl0=+FPBryarPistol			local
template	tpl1=+FPBryarPistol			local
template	tpl2=+FPBryarPistol			local
template	tpl3=+FPSTrifle				local
template	tpl4=+FPDetonator			local
template	tpl5=+FPCrossbow			local
template	tpl6=+FPRepeaterGun			local
template	tpl7=+FPRailGun				local
template	tpl8=+FPSeqCharge			local
template	tpl9=+FPConcRifle			local
template	tpl10=+FPBryarPistol		local


# Projectiles to jump away from with saber
template	dproj0=+raildet				local
template	dproj1=+concbullet			local

# Force
int			mana			local

# Item models
model		itemmodel0=conp.3do		local	desc=conc rifle
model		itemmodel1=rldp.3do		local	desc=rail gun
model		itemmodel2=rptp.3do		local	desc=repeater
model		itemmodel3=strp.3do		local	desc=st rifle
model		itemmodel4=bowp.3do		local	desc=crossbow
model		itemmodel5=ecel.3do		local	desc=energy cell
model		itemmodel6=pcel.3do		local	desc=power cell
model		itemmodel7=rcrg.3do		local	desc=rail dets
model		itemmodel8=detp.3do		local	desc=thermal dets
model		itemmodel9=seqp.3do		local	desc=sequencers
model		itemmodel10=hepk.3do	local	desc=health
model		itemmodel11=shld.3do	local	desc=shield
model		itemmodel12=vest.3do	local	desc=full shield
model		itemmodel13=flgr.3do	local	desc=red flag
model		itemmodel14=flgy.3do	local	desc=gold flag
model		itemmodel15=seq0.3do	local	desc=live sequencer

# Misc
int			curweap			local
thing		newpos			local
int			lastposnum		local
thing		target			local
vector		rvec			local
vector		relpos			local
vector		blindvec		local
flex		tempflex1		local
flex		tempflex2		local
vector		tempvector1		local
vector		tempvector2		local
vector		tempvector3		local
vector		tempvector4		local
flex		disttotarg		local
int			targweap		local
int			firing=0		local
int			bin				local
int			rank			local
thing		enemy			local
int			mode			local
flex		dot				local
int			attachflags		local
flex		choice			local
vector		firepos			local
vector		firevec			local
int			rundir			local
flex		damage			local
int			damagetype		local
thing		damager			local
int			shields			local
int			stopcount		local
sector		cursector		local
thing		item			local
thing		forcetarget		local
int			griptrackID		local
thing		debris			local
int			dscount			local
int			starthealth		local
int			forcearmor		local
int			newitem			local
int			drowning		local
int			index			local
int			captured_red	local
int			captured_gold	local
int			redflag_taken	local
int			goldflag_taken	local
model		newitemmodel	local
int			itempriority	local
int			newitempriority	local
int			dummyproj		local
vector		targetvel		local
flex		speed			local
flex		distance		local
flex		time			local
int			allowdodge		local

end

# Code Section

code

startup:
	if(!IsServer()) return;

	// test
	//sleep(5.0);
	//print("multimode");
	//printint(GetMultiModeFlags());
	return;

pulse:
	if(!IsServer()) return;
	setpulse(0);
	SendTrigger(-1, 100000, botid, rbot, forcemode, -1); // request update
	call dodge;
	call move;
	syncthingpos(rbot);
	setpulse(0.1);
	return;

killed:
	if(!IsServer()) return;
	setpulse(0);
	StopThing(rbot);
	botmode=BitClear(botmode,0x10000); // bot no longer alive
	forcemode=0;
	KillTimerEx(300); // stop checking if bot is in a pit
	KillTimerEx(500); // stop force mana increase
	KillTimerEx(501); // stop force attacks
	KillTimerEx(502); // stop force defenses
	ClearActorFlags(rbot, 0x80); // make sure bot is not invisible
	SendTrigger(-1, 1000, botid, getsenderref(), getsourceref(), rbot); // send trigger to startup cog to respawn new bot
	ReleaseThing(rbot);
	return;

damaged:
	if (!IsServer()) return;

	damage=GetParam(0);
	damagetype=GetParam(1);
	damager=getsourceref(); // projectile - attacker=GetThingParent()

	if (damager!= -1 && (GetThingType(GetThingParent(damager)) == 2 || GetThingType(GetThingParent(damager)) == 10)) // attacker is another bot or player
	{
		AiSetLookPos(rbot, getthingpos(GetThingParent(damager))); // briefly look at who damaged the bot
	}

	if (BitTest(damagetype,0x20)) // drowning
	{
		drowning=1;
		KillTimerEx(150); // disable any previous drowning timer
		SetTimerEx(7.0, 150, -1, -1);
		ReturnEx(damage);
		return;
	}

	if (BitTest(forcemode,0x100)) // protection enabled
	{
		if (forcearmor <= 0) // no force armor left - switch off protection
		{
			KillTimerEx(29);
			forcemode=BitClear(forcemode,0x100); // protection is no longer active
			ClearThingFlags(rbot, 4); // remove magseal from bot
			SendTrigger(-1, 101026, botid, rbot, -1, 29); // tell client to stop playing sound
		}
		else if (BitTest(30, damagetype)) // If damage from energy, fire, force or saber
		{
			forcearmor = forcearmor-damage;
			if (forceArmor < 0) damage = -forceArmor; else damage = 0;
			if (shields > 0) // <- check here
			{
				SendTrigger(-1, 100003, botid, damage, GetThingParent(damager), -1); // update srv2 with damage
				if (damage > shields)
				{
					damage=damage-shields;
				}
				else damage=0;
			}
			if (BitTest(gamemode,0x8) && ((GetThingType(GetThingParent(damager)) == 10 && GetPlayerTeam(GetThingParent(damager)) == team) || (GetThingType(GetThingParent(damager)) == 2 && GetThingUserData(GetThingParent(damager)) == team))) damage=0;
			ReturnEx(damage);
			return;
		}
	}
	if (curweap == 10 && BitTest(botmode,0x200000)) // bot has saber - check if it can block
	{
		// Check for saber blocking
		dot = ThingViewDot(rbot, getthingparent(damager));
		if ((BitTest(damagetype, 0x10) && dot > 0.9 && dot < 1.1 && difficulty < 2) || (BitTest(damagetype, 0x10) && dot > 0.5 && dot < 1.5 && difficulty < 4) || (BitTest(damagetype, 0x10) && dot > 0.3 && dot < 1.7)) // attacker has saber & bot is facing attacker
		{
			SendTrigger(-1, 2100, botid, rbot, -1, -1); // send trigger to client to play saber block animation
			ReturnEx(0);
			return;
		}
		if ((BitTest(damagetype, 0x2) && dot > 0.9 && dot < 1.1 && difficulty < 2) || (BitTest(damagetype, 0x2) && dot > 0.5 && dot < 1.5 && difficulty < 4) || (BitTest(damagetype, 0x2) && dot > 0.3 && dot < 1.7)) // attacked by energy weapon & bot is _mainly_ facing attacker
		{
			SendTrigger(-1, 2100, botid, rbot, getthingtemplate(damager), -1); // send trigger to play saber block animation & deflect projectile
			ReturnEx(0);
			return;
		}
	}
	
	if (BitTest(damagetype,0x10)) // saber attack damage - combined health & shields
	{
		if (shields > 0)
		{
			damage=damage/2;
			SendTrigger(-1, 100003, botid, damage, GetThingParent(damager),-1); // update srv2 with damage
			if (damage > shields) damage=damage+damage-shields; else damage=0;
		}
		if (BitTest(gamemode,0x8) && ((GetThingType(GetThingParent(damager)) == 10 && GetPlayerTeam(GetThingParent(damager)) == team) || (GetThingType(GetThingParent(damager)) == 2 && GetThingUserData(GetThingParent(damager)) == team))) damage=0;
		ReturnEx(damage);
		return;
	}
	else if (BitTest(damagetype,0x08) && BitTest(forcemode,0x80)) // force damage & force absorb is active
	{
		SendTrigger(-1, 101025, botid, rbot, target, 28); // tell client to play sound
		mana=mana+damage*(1+difficulty)/2;
		if (mana > 400) mana=400;
		ReturnEx(0);
		return;
	}
	else if (BitTest(damagetype,0x01) || BitTest(damagetype,0x04) || BitTest(damagetype,0x08) || BitTest(damagetype,0x40)) // impact, explosion, force, impact from fall - take damage from health not shields
	{
		if (BitTest(gamemode,0x8) && ((GetThingType(GetThingParent(damager)) == 10 && GetPlayerTeam(GetThingParent(damager)) == team) || (GetThingType(GetThingParent(damager)) == 2 && GetThingUserData(GetThingParent(damager)) == team)))
		{
			damage=0;
			ReturnEx(damage);
		}
				
		if (damage > GetThingHealth(rbot)) // if damage is going to kill bot - switch off checking routines
		{
			// make sure bot dies
			SetThingHealth(rbot,1);
			StopThing(rbot); //Make 'em stay in place like when player dies [HR]
			ReturnEx(10);
			return;
		}
	}
	else
	{
		if (shields > 0)
		{
			SendTrigger(-1, 100003, botid, damage, GetThingParent(damager), -1); // update srv2 with damage
			if (damage > shields)
			{
				damage=damage-shields;
			}
			else damage=0;
		}
		if (BitTest(gamemode,0x8) && ((GetThingType(GetThingParent(damager)) == 10 && GetPlayerTeam(GetThingParent(damager)) == team) || (GetThingType(GetThingParent(damager)) == 2 && GetThingUserData(GetThingParent(damager)) == team))) damage=0;
		ReturnEx(damage);
	}
	return;

trigger:
	if (!IsServer()) return;
	if (getsourceref() == 800) // game setup options <- obsolete
	{
		gamemode=getparam(0);
		difficulty=getparam(1);
		return;
	}
	if (getsourceref() == 12210) // updated CTF info
	{
		captured_red=GetParam(0);
		captured_gold=GetParam(1);
		redflag_taken=GetParam(2);
		goldflag_taken=GetParam(3);
	}
	if (getparam(0) != botid) return; // not for this bot

	if (getsourceref() == 900 && !BitTest(botmode,0x10000)) // respawn new bot && bot not already in-game and alive
	{
		botmode=BitSet(botmode,0x10000); // bot alive
		gamemode=GetParam(2);
		difficulty=GetParam(3);
		
		// initialization
		rbot=CreateThingNr(rtemp,getparam(1)); // create bot
		SendTrigger(-1,950,botid,rbot,botname,team); // Confirm botuid
		capturething(rbot);
		jkstringclear();
		jkStringConcatUNIString(-1, 1000+botname);
		jkStringConcatAsciiString(" respawns");
		jkStringOutput(-3,-1);
		newpos=-1;
		lastposnum=-1;
		botmode=0;
		if (BitTest(characterflags,0x1)) botmode=BitSet(botmode,0x800000); // attack characteristic
		if (BitTest(characterflags,0x2)) botmode=BitSet(botmode,0x1000000); // defend characteristic
		if (BitTest(characterflags,0x4)) botmode=BitSet(botmode,0x2000000); // freelance characteristic
		forcemode=0; // clear active force powers
		SendTrigger(-1, 970, botid, rbot, botname, team); // return thing number
		SetTimerEx(0.1, 120, -1, -1); // force bot 'lost' - get bot to ask for 1st move position
		mana=400;
		forcearmor=0;
		if (BitTest(gamemode,0x04)) // allowed to use forcepowers
		{
			SetTimerEx(0.5, 500, -1, -1); // increase mana over time 
			SetTimerEx(5.0, 501, -1, -1); // force attacks
			SetTimerEx(5.0, 502, -1, -1); // force defensive/evasive
		}
		SetTimerEx(2.0, 300, -1, -1); // check if bot has fallen into a pit
		stopcount=30;
		allowdodge=1;
		setpulse(0.1);
	}
	else if (getsourceref() == 901) // remove bot from game
	{
		if (BitTest(botmode,0x10000)) // bot alive
		{
			jkstringclear();
			jkStringConcatUNIString(-1, 1000+botname);
			jkStringConcatAsciiString(" has left the game");
			jkStringOutput(-3,-1);
			DamageThing(rbot,500,1,rbot); // Make sure bot is dead
		}
	}
	else if (getsourceref() == 960 && BitTest(botmode,0x10000)) // query bot thing number and bot is alive
	{
		SendTrigger(-1, 970, botid, rbot, botname, team); // return thing number
	}
	else if (getsourceref() == 2100 && getparam(2) != -1) // Saber blocking projectiles
	{
		if (difficulty < 3) // test to see how random the blocked projectiles are
		{
			tempvector1=VectorSet((Rand()-0.5)*(5-difficulty), (Rand()-0.5)*(5-difficulty), 0.0);
		}
		else tempvector1=VectorSet(0.0, 0.0, 0.0);
		FireProjectile(rbot, getparam(2), -1, -1, '0.0135 0.1624 0.0', tempvector1, 1.0, 0x20, 30, 10); // make it random fire vector later
	}
	else if (getsourceref() == 10010) // nav cog busy
	{
		SetTimerEx(0.10+(rand()*0.5), 110, -1, -1); // ask again for new pos after a short delay
	}
	else if (getsourceref() == 11000) // New position available
	{
		newpos=getparam(1);
		lastposnum=getparam(2);
		rundir=getparam(3);
	}
	else if (getsourceref() == 11020) // Received special info
	{
		special=getparam(1);
		if ((BitTest(special,0x04) && !BitTest(botmode, 0x1000)) || (BitTest(special,0x08) && BitTest(botmode, 0x1000))) // Jump while running forwards or Jump while running backwards
		{
			if (!BitTest(botmode,0x4000)) AiSetLookPos(rbot, getthingpos(newpos)); // bot has no target - look at newpos
			AiSetMovePos(rbot, getthingpos(newpos));
			AddThingVel(rbot, VectorScale(GetThingVel(rbot), 1)); // increase bot speed before jump
			ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '0 0 -90'), 1.5)); // force bot to jump
		}
		else if (BitTest(special,0x200)) // run fast
		{
			AddThingVel(rbot, VectorScale(GetThingVel(rbot), 4));
			SetTimerEx(3.0, 220, -1, -1);
		}
		//else if ((BitTest(special,0x80) && rundir==0) || (BitTest(special,0x100) && rundir==1)) // wait
		else if ((BitTest(special,0x80)) || (BitTest(special,0x100))) // wait
		{
			printint(rundir);
			botmode=BitSet(botmode,0x80000);
			KillTimerEx(160);
			SetTimerEx(2.0, 160, -1, -1);
		}
	}
	else if (getsourceref() == 100001) // New info available
	{
		target=getparam(1);
		botmode=getparam(2);
		curweap=getparam(3);
	}
	else if (getsourceref() == 100002) // New info available
	{
		shields=getparam(1);
	}
	else if (getsourceref() == 101000) // weapon changed
	{
		curweap=getparam(1);
	}
	else if (getsourceref() == 101020 && !firing) // weapon fired && not already
	{
		firing=1;
		curweap=getparam(1);
		mode=getparam(2);
		nextanim=getparam(3);

		// Bot accuracy		
		if (curweap == 3) // st-rifle is always inaccurate
		{
			tempvector1=VectorSet((Rand()-0.5)*5, (Rand()-0.5)*5, 0.0);
		}
		else if (difficulty < 3) // set accuracy based on difficulty
		{
			tempvector1=VectorSet((Rand()-0.5)*(4-difficulty),(Rand()-0.5)*(4-difficulty), 0);
		}
		else
		{
			tempvector1=VectorSet(0, 0, 0); // perfect accuracy
		}

		// Lead distance
		if (difficulty == 4)
		{
			dummyproj = FireProjectile(rbot, projectile_weap0[curweap], -1, -1, '0 0 0', '0 0 0', 1.0, 0, 0.0, 0.0);
			targetvel = GetThingVel(target);
			speed = VectorDist('0 0 0', VectorSub(GetThingVel(dummyproj), targetvel));
			DestroyThing(dummyproj);
			distance = VectorDist(GetThingPos(target), GetThingPos(rbot));
			time = distance / speed;
			AiSetLookPos(rbot, VectorAdd(GetThingPos(target), VectorScale(targetvel, time)));
		}

		if (curweap == 1 && nextanim == 0) FireProjectile(rbot, projectile_weap01, -1, 8, '-0.02 0.03 0', '0 0 0', 1.0, 0, 0.0, 0.0); // fists
		else if (curweap == 1 && nextanim == 1) FireProjectile(rbot, projectile_weap01, -1, 18, '0.02 0.03 0', '0 0 0', 1.0, 0, 0.0, 0.0); // fists - alternate
		else if (curweap == 2) FireProjectile(rbot, projectile_weap02, -1, 8, '0.0135 0.1624 0.0', tempvector1, 1.0, 0x20, 30, 10); // bryar
		else if (curweap == 3) FireProjectile(rbot, projectile_weap03, -1, 8, '0.0168 0.1896 0.00', tempvector1, 1.0, 0, 0, 0); // st-rifle
		else if (curweap == 5) FireProjectile(rbot, projectile_weap05b, -1, 18, '0.0207 0.0888 0.00', tempvector1, 1.0, 0x20, 30, 20); // bowcaster
		else if (curweap == 6) FireProjectile(rbot, projectile_weap06, -1, 8, '0.0186 0.0102 0.00', tempvector1, 1.0, 0x30, 25, 10 ); // repeater
		else if (curweap == 7)
		{
			if (mode == 0) FireProjectile(rbot, projectile_weap07, -1, 18, '0.0214 0.15 0.00', tempvector1, 1.0, 0x20, 30, 10); // railgun
			else FireProjectile(rbot, projectile_weap07a, -1, 18, '0.0214 0.15 0.00', '0 0 0', 1.0, 0x20, 30, 10); // railgun - secondary fire
			ApplyForce(rbot, VectorScale(GetThingLVec(rbot), -40)); // Provide a kick backwards
		}
		else if (curweap == 8 && mode == 0) FireProjectile(rbot, projectile_weap08, -1, 16, '0 0.05 0.00', '0 0 0', 1.0, 0, 0.0, 0.0); // sequencer mine - explode now
		else if (curweap == 8 && mode == 1) FireProjectile(rbot, projectile_weap08a, -1, 16, '0 0.05 0.00', '0 0 0', 1.0, 0, 0.0, 0.0); // sequencer mine - explode on contact
		else if (curweap == 9)
		{
			if (mode == 0) FireProjectile(rbot, projectile_weap09, -1, 18, '0.02 0.15 0.0', tempvector1, 1.0, 0x20, 30, 5); // conc rifle
			else FireProjectile(rbot, projectile_weap09a, -1, 18, '0.02 0.15 0.0', tempvector1, 1.0, 0x20, 30, 5); // conc rifle - secondary fire
			ApplyForce(rbot, VectorScale(GetThingLVec(rbot), -80)); // Provide a kick backwards
		}
		else if (curweap == 10) // lightsaber
		{
			if (mode == 6) // Double swing slash
			{
				SetTimerEx(0.2, 200, -1, -1); // fire 1st swing after 0.2 secs
				SetTimerEx(0.3, 200, -1, -1); // fire 1st swing after 0.3 secs
				SetTimerEx(0.6, 201, -1, -1); // fire 2nd swing after 0.6 secs
				SetTimerEx(0.7, 201, -1, -1); // fire 2nd swing after 0.7 secs
			}
			else // Regular strike
			{
				SetTimerEx(0.2, 202, -1, -1); // fire 1st swing after 0.2 secs
				SetTimerEx(0.3, 202, -1, -1); // fire 1st swing after 0.3 secs
			}
		}
		firing=0;
	}
	else if (getsourceref() == 101040) // mana update
	{
		mana=mana+GetParam(1);
		if (mana>400) mana=400;
		if (GetParam(2) == 1)
		{
			forcemode=BitSet(forcemode,0x4000); // lightside surge now active
			SetTimerEx(20, 65, -1, -1); // switch off after 20 secs
		}
		else if (GetParam(2) == 2)
		{
			forcemode=BitSet(forcemode,0x8000); // darkside surge now active
			SetTimerEx(20, 64, -1, -1); // switch off after 20 secs
		}
	}
	return;

move:
	if (GetActorFlags(rbot) & 0x800) // bot blind
	{
		AiSetMovePos(rbot, blindvec); // move to a random position
		AiSetLookPos(rbot, blindvec); // look at the random position
		return;
	}
	if (BitTest(botmode,0x400000)) // bot is 'gripped', don't move
	{
		AiSetMovePos(rbot, getthingpos(rbot));
		return; 
	}

	if (VectorY(GetThingVel(rbot)) < 0.01) // bot is not moving forward
	{
		stopcount=stopcount-1;
		if (stopcount < 1) // if bot has not moved in 3 seconds
		{
			//for (index=0; index<GetSurfaceCount(); index=index+1)
			//{
			//	if ((VectorDist(GetSurfaceCenter(index), GetThingPos(rbot)) < 0.3) && (GetSurfaceFlags(index) & 0x4000)) // close surface & magsealed - ie: forcefield
			//	{
			//		print("found force field!");
					// do something about it

			//		stopcount=30;
			//	}
			//}

			if (stopcount!=30)
			{
				ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '0 0 -90'), 3.0)); // force bot to jump
				stopcount=30;
			}
		}
	}
	
	if (drowning) // Help bot from drowning
	{
		if (getthingflags(rbot) & 0x2000000) // bot is still underwater
		{
			if (health < 70 || difficulty <2)
			{
				//print("drowning - moving up!");
				// work out a vector postion 0.5 above the bot
				tempvector1=VectorSet(VectorX(GetThingPos(rbot)),VectorY(GetThingPos(rbot)),VectorZ(GetThingPos(rbot))+0.5);
				if (!BitTest(botmode,0x4000)) AiSetLookPos(rbot, tempvector1); // bot has no target - look at new position
				AiSetMovePos(rbot, tempvector1); // move towards new position
				call checkspeed;
				return;
			}
		}
		else
		{
			drowning=0;
			KillTimerEx(150); // disable any previous drowning timer
		}
	}

	// Look for items and move to them
	if (difficulty > 1) // only look if difficulty allows
	{
		newitem=-1;
		if (BitTest(botmode,0x4000)) newitempriority=5; else newitempriority=-1; // bot has target - only pick up high priority items
		dot=100;
		item = FirstThingInView(rbot, 40 + 20 * (2+difficulty), 3, 0x020); // check if any items nearby
		while (item != -1)
		{
			// Find closest takable item that bot needs
			if (HasLos(rbot, item)) // bot can see it
			{
				newitemmodel=GetThingModel(item);
				itempriority=-1;

				// work out item priority
				if (newitemmodel==itemmodel13) // red flag
				{
					if (captured_red == 1) itempriority=10; // flag dropped
					if (team == 1) // bot on red team
					{
						if (captured_red == 0 && goldflag_taken == rbot) itempriority=10; // red flag home & bot has gold flag
					}
					else // gold team
					{
						if (captured_red == 0) itempriority=10; // red flag at base
					}
				}
				else if (newitemmodel==itemmodel14) // gold flag
				{
					if (captured_gold == 1) itempriority=10; // flag dropped
					if (team == 2) // bot on gold team
					{
						if (captured_gold == 0 && redflag_taken == rbot) itempriority=10; // gold flag home & bot has red flag
					}
					else // red team
					{
						if (captured_gold == 0) itempriority=10; // gold flag at base
					}
				}

				tempflex1=VectorDist(GetThingPos(rbot), GetThingPos(item));
				if (tempflex1 > 0.2 && tempflex1 < dot) // item is greater than 0.2 away and is closer than the last item checked
				{
					if (newitemmodel==itemmodel10 && BitTest(botmode,0x400)) itempriority=8; // health
					if ((newitemmodel==itemmodel11 || newitemmodel==itemmodel12) && BitTest(botmode,0x800)) itempriority=7; // shield & full shield
					if ((newitemmodel==itemmodel0 && BitTest(botmode,0x1)) || (newitemmodel==itemmodel1 && BitTest(botmode,0x2)) || (newitemmodel==itemmodel2 && BitTest(botmode,0x4)) || (newitemmodel==itemmodel3 && BitTest(botmode,0x8)) || (newitemmodel==itemmodel4 && BitTest(botmode,0x10))) itempriority=6; // weapons
					if ((newitemmodel==itemmodel5 && BitTest(botmode,0x20)) || (newitemmodel==itemmodel6 && BitTest(botmode,0x40)) || (newitemmodel==itemmodel7 && BitTest(botmode,0x80)) || (newitemmodel==itemmodel8 && BitTest(botmode,0x100))) itempriority=5; // energy & power cells & rail dets
					if ((newitemmodel==itemmodel9 && BitTest(botmode,0x200)) || (GetItemFlags(item) & 4)) itempriority=4; // sequencers or backpack
				}

				if (itempriority > newitempriority) // higher priority
				{
					newitem=item;
					dot=tempflex1;
					newitempriority=itempriority;
				}
				else item = NextThingInView();
			}
			else item = NextThingInView();

		}
		if (newitem != -1)
		{
			if (!BitTest(botmode,0x4000)) AiSetLookPos(rbot, getthingpos(newitem)); // only look at new item if no target
			AiSetMovePos(rbot, getthingpos(newitem)); // move towards new item
			call checkspeed;
			KillTimerEx(120); // stop 'bot lost' timeout
			SetTimerEx(5.0, 120, -1, -1); // bot has 5 sec to reach item before it's 'lost'
			return;
		}
	}

	if (BitTest(botmode,0x4000)) // bot has target
	{
		if (!firing) AiSetLookPos(rbot, getthingpos(target));  // look at target if not already firing weapon
		if (goldflag_taken == rbot || redflag_taken == rbot || GetThingHealth(rbot)<30) call movenode; else call targettactics; // if bot has flag or is low on health keep moving
	}
	else
	{
		AiSetLookPos(rbot, getthingpos(newpos)); // look at move position
		call movenode;
	}
	return;

movenode:
	if (BitTest(botmode,0x80000)) // bot is waiting (and not airborn), don't move -> && (GetAttachFlags(rbot) & 1)
	{
		//print("don't move");
		AiSetMovePos(rbot, getthingpos(rbot));
		return; 
	}

	AiSetMovePos(rbot, getthingpos(newpos));

	// Call navigation cog for next move position
	if (VectorDist(GetThingPos(rbot), GetThingPos(newpos)) < .25)
	{
		SendTrigger(-1, 11010, botid, rbot, newpos, -1); // ask for special info on current pos
		if (rundir) botmode=BitClear(botmode,0x1000); else botmode=BitSet(botmode,0x1000); // remember run direction
		SendTrigger(-1, 10000, botid, rbot, botmode, lastposnum); // ask for new pos
		KillTimerEx(120); // stop 'bot lost' timeout
		SetTimerEx(8.0, 120, -1, -1); 
	}
	else
	{
		call checkspeed;
		// Check if bot has fallen
		tempflex1=VectorZ(GetThingPos(newpos));
		tempflex2=VectorZ(GetThingPos(rbot));
		if ((tempflex1 > tempflex2) && !(getthingflags(newpos) & 0x2000000)) // if new pos is higher than bot pos & new pos is not in water
		{
			tempvector1=VectorSet('0','0',VectorZ(GetThingPos(newpos)));
			tempvector2=VectorSet('0','0',VectorZ(GetThingPos(rbot)));

			tempvector3=VectorSet(VectorX(GetThingPos(newpos)),VectorY(GetThingPos(newpos)),'0');
			tempvector4=VectorSet(VectorX(GetThingPos(rbot)),VectorY(GetThingPos(rbot)),'0');

			if (VectorDist(tempvector1,tempvector2) > 0.85 || (VectorDist(tempvector1,tempvector2) > 0.4 && VectorDist(tempvector3,tempvector4) < 0.2)) // bot fallen
			{
				KillTimerEx(120); // stop 'bot lost' timeout
				SetTimerEx(0.1, 120, -1, -1); // force bot 'lost'
			}
		}
	}
	return;

dodge:
	// dodge projectiles
	if (!allowdodge || difficulty < 3 || (difficulty < 4 && (10*rand() > difficulty))) return; // Difficulty 0,1,2 will never dodge. Difficulty 3 will sometimes dodge. Difficulty 4 will always dodge.

	potential = FirstThingInView(rbot, 270, 0.8, 0x8); //Scan for projectiles to dodge (0x8)
	while(potential != -1) // make sure projectile is not from this bot and is valid
	{
		if (rbot != GetThingParent(potential) && (GetAttachFlags(rbot) & 1))
		{
			choice=rand();
			if (choice > 0.75)
			{
				ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '0 0 -90'), 2.0)); // jump
			}
			else if (choice > 0.30)
			{
				ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '-90 0 0'), 2.0)); // slide left
			}
			else
			{
				ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '90 0 0'), 2.0)); // slide right
			}
			allowdodge=0;
			SetTimerEx(0.4, 400, -1, -1); // allow to dodge projectiles again in 0.4 secs
		}
		potential = NextThingInView();
	}
	return;

targettactics:
	//******************Begin AI Tactics********************
	relpos=VectorSet("0.0","0.0", VectorZ(GetThingPos(target))-VectorZ(GetThingPos(rbot))); // work out relative position of target to bot
	disttotarg=VectorDist(GetThingPos(rbot), GetThingPos(target));

	//Get the current weapon of our target
	if (GetThingType(target) == 2) // target is another bot
	{
		targweap=SendMessageEx(GetThingCaptureCog(target), user1, -1, -1, -1, -1);
	}
	else // target is a player
	{
		targweap=GetCurWeapon(target);
		if (targweap==11) targweap=10; // If player has saber, set it to 10 for simplicity
	}
	if ((curweap==2 && targweap > 2 && difficulty > 0) || ((targweap==7 || targweap==9) && curweap < 6 && difficulty > 1)) // Need a better weapon than bryar
	{
		call movenode; 
		return;
	}

	if ((vectorZ(relpos) > 1 && difficulty > 1) || (getthingflags(rbot) & 0x2000000)) // Can't fall down safely or Underwater, need to keep moving.
	{
		call movenode;
		return;
	}

	// If bot not close enough to target move towards it
	if (disttotarg > 0.4)
	{
		if (difficulty < 3)
		{
			AiSetMovePos(rbot, GetThingPos(target)); // move directly towards target
		}
		else 
		{
			if (rand() > 0.4)
			{
				AiSetMovePos(rbot, GetThingPos(target)); // move directly towards target
			}
			else
			{
				tempvector1=VectorSet(VectorX(GetThingPos(target))+(0.2-rand()*0.4),VectorY(GetThingPos(target)),VectorZ(GetThingPos(target)));
				AiSetMovePos(rbot, tempvector1); // move slightly left or right of target
			}
		}
		//else // move towards where target is moving
		//{
		//	movepos=VectorNorm(VectorSub(VectorAdd(GetThingPos(target), VectorScale(GetThingVel(target), 0.4)), GetThingPos(rbot)));
		//	movevec=VectorSet(VectorX(movepos), VectorY(movepos), 0.0);
		//	AiSetMovePos(rbot, movevec);
		//}
	}
	else if (difficulty > 2) // sometimes move around target
	{
		if (rand() > 0.6)
		{
			tempvector1=VectorSet(VectorX(GetThingPos(target))+(0.3-rand()*0.6),VectorY(GetThingPos(target)),VectorZ(GetThingPos(target)));
			AiSetMovePos(rbot, tempvector1); // move slightly left or right of target
		}
	}
	call checkspeed;
	return;

checkspeed:
	if (BitTest(botmode,0x100000) || difficulty < 1)
	{
		AiSetMoveSpeed(rbot,2.0); // make bot run at 'walk speed'
	}
	else
	{
		tempvector1=GetThingVel(rbot);
		tempvector2=VectorSet(VectorX(tempvector1),VectorY(tempvector1),0);
		if (BitTest(forcemode,0x01)) // force speed active
		{
			if (VectorLen(tempvector2) < 3.0) AddThingVel(rbot, VectorScale(tempvector2, 0.33)); // Attempt to increase bot speed
		}
		else
		{
			if (VectorLen(tempvector2) < 1.0) AddThingVel(rbot, VectorScale(tempvector2, 0.33)); // Attempt to increase bot speed
		}
	}
	return;

timer:
	if(!IsServer()) return;
	if(GetSenderId() == 22) // after force speed
	{
		forcemode=BitClear(forcemode,0x01); // speed is no longer active
		SendTrigger(-1, 101026, botid, rbot, -1, 22); // tell client to stop playing sound
	}
	else if(GetSenderId() == 23) // after force seeing
	{
		forcemode=BitClear(forcemode,0x08); // seeing is no longer active
		SendTrigger(-1, 101026, botid, rbot, -1, 23); // tell client to stop playing sound
	}
	else if(GetSenderId() == 24) // after force pull
	{
		SetCollideType(GetParam(0), 1); // pulled weapon must be made takeable again
		SetPhysicsFlags(GetParam(0), 1); // make it affected by gravity
		SetLifeLeft(GetParam(0), 30); // make it last for 30 secs
	}
	else if(GetSenderId() == 26) // after force persuasion
	{
		ClearActorFlags(rbot, 0x80);
		jkClearFlags(rbot, 0x20);
		forcemode=BitClear(forcemode,0x20); // persuasion is no longer active
		SendTrigger(-1, 101026, botid, rbot, -1, 26); // tell client to stop playing sound
	}
	else if(GetSenderId() == 27) // after force blinding
	{
		ClearActorFlags(victim, 0x800);  // allow bot to see again
	}
	else if(GetSenderId() == 28) // after force absorb
	{
		forcemode=BitClear(forcemode,0x80); // absorb is no longer active
	}
	else if(GetSenderId() == 29) // after force protection
	{
		forcemode=BitClear(forcemode,0x100); // protection is no longer active
		ClearThingFlags(rbot, 4); // remove magseal from bot
		SendTrigger(-1, 101026, botid, rbot, -1, 29); // tell client to stop playing sound
	}
	else if(GetSenderId() == 31) // force grip
	{
		target=GetParam(0);
		ClearActorFlags(target, 0x40000); // allow target to move again
		if (GetParam(1) != -1 && GetThingHealth(target) > 0) StopKey(target, GetParam(1), 0.1); // stop target playing 'grip' key but only if target is still alive
		if (HasLOS(rbot,target) && GetThingHealth(rbot) > 0 && GetThingHealth(target) > 0 && VectorDist(GetThingPos(rbot), GetThingPos(target)) <= 1)
		{
			griptrackID=PlayMode(target, 25); // make target play 'gripped' key
			SetActorFlags(target, 0x40000);
			if (GetThingType(target) == 10) jkPrintUNIString(target, 300); // Print("You are taking Grip damage");
			DamageThing(target, 2 * (4+difficulty), 0x8, rbot);
			SendTrigger(-1,101025,botid,rbot,target,31); // get client to play 'grip' sound
			SetTimerEx(0.5, 31, target, griptrackID); // allow bot to grip target again in 0.5 sec
		}
		else forcemode=BitClear(forcemode,0x400); // grip is no longer active
	}
	else if(GetSenderId() == 34) // force deadly sight
	{
		dscount=dscount+1;
		if ((dscount > difficulty*3) || (GetThingHealth(rbot) < starthealth) || (GetActorFlags(rbot) & 0x800))
		{
			SendTrigger(-1, 101026, botid, rbot, target, 34); // tell client to stop playing sound
			forcemode=BitClear(forcemode,0x2000); // deadly sight is no longer active
			return;
		}
		item = FirstThingInView(rbot, 20 + 15 * (2+difficulty), 4 + difficulty, 0x404);
		while(item != -1)
		{
			if (HasLOS(rbot, item) && (item != rbot) && !(GetThingFlags(item) & 0x200))
			{
				if (GetThingType(item) == 2) // another bot
				{
					SendMessageEx(GetThingCaptureCog(item), user0, item, rbot, 34, 2+difficulty);  // send info to bot
				}
				else if (GetThingType(item) == 10) // player
				{
					SendTrigger(item, 101999, GetThingSignature(item), rbot, 34, 2+difficulty); // send info to player
				}
			}
			item = NextThingInView();
		}
		SetTimerEx(0.5, 34, -1, -1); // allow bot to deadly sight again in 0.5 sec
	}
	else if (GetSenderId() == 64) // after darkside surge
	{
		forcemode=BitClear(forcemode,0x8000); // darkside surge no longer active
	}
	else if (GetSenderId() == 65) // after lightside surge
	{
		forcemode=BitClear(forcemode,0x4000); // lightside surge no longer active
	}
	else if (GetSenderId() == 110) // request again for new position (nav cog was busy)
	{
		if (rundir) botmode=BitClear(botmode,0x1000); else botmode=BitSet(botmode,0x1000); // remember run direction
		SendTrigger(-1, 10000, botid, rbot, botmode, lastposnum); 
	}
	else if (GetSenderId() == 120) // bot has not found it's next node in time - bot lost
	{
		if (rundir) botmode=BitClear(botmode,0x1000); else botmode=BitSet(botmode,0x1000); // remember run direction
		SendTrigger(-1, 10000, botid, rbot, botmode, -1);
	}
	else if (GetSenderId() == 150) // Drowning damage should be over by now
	{
		drowning=0;
	}
	else if (GetSenderId() == 160) // allow bot to move again after waiting
	{
		print("reset botmode");
		botmode=BitClear(botmode,0x80000);
	}
	else if (GetSenderId() == 200) // fire 1st swing saber projectile
	{
		AiSetLookPos(rbot, getthingpos(target));
		FireProjectile(rbot, projectile_weap00a, -1, -1, '0.0 0.0 0.0', '0 0 0', 1.0, 0x20, 30, 10);
	}
	else if (GetSenderId() == 201) // fire 2nd swing saber projectile
	{
		AiSetLookPos(rbot, getthingpos(target));
		FireProjectile(rbot, projectile_weap00a, -1, -1, '0 0 0', '0 0 0', 1.0, 0x20, 30, 10);
	}
	else if (GetSenderId() == 202) // fire swing saber projectile
	{
		AiSetLookPos(rbot, getthingpos(target));
		FireProjectile(rbot, projectile_weap00, -1, -1, '0 0 0', '0 0 0', 1.0, 0x20, 30, 10);
	}
	else if (GetSenderId() == 220) // reduce bot speed after temporary speed increase
	{
		AiSetMoveSpeed(rbot,2.0);
	}
	else if (GetSenderId() == 230) // choose random move direction (force blinded)
	{
		blindvec = VectorSet((Rand()-0.5)*5, (Rand()-0.5)*5, 0.0);
		if (GetActorFlags(rbot) & 0x800) SetTimerEx(3+rand()*5,230,-1,-1); // bot still blind
	}
	else if (GetSenderId() == 300) // check if bot has fallen into a pit
	{
		cursector=GetThingSector(rbot);
		if (BitTest(GetSectorFlags(cursector), 0x40)) // in pit
		{
			DamageThing(rbot,500,1,-1); // kill bot
		}
		else SetTimerEx(2.0, 300, -1, -1); // schedule another check
	}
	else if (GetSenderId() == 400) // reset dodge timer
	{
		allowdodge=1;
	}
	else if (GetSenderId() == 500 && BitTest(botmode,0x10000)) // increase mana over time && bot alive
	{
		if (difficulty > 0) mana=mana+difficulty*5; else mana=mana+5;
		if (mana > 400) mana=400;
		SetTimerEx(5.0, 500, -1, -1); // schedule mana increase
	}
	else if (GetSenderId() == 501 && BitTest(botmode,0x10000)) // force power attacks && bot alive
	{
		if (mana > 0 && BitTest(botmode,0x4000)) // bot has mana & has a target
		{
			if (BitTest(forceflags,0x4) && curweap==10 && targweap!=10 && targweap!=1 && targweap!=4 && targweap!=8) // force pull allowed - bot has saber and target has gun
			{
				call force_pull;
			}
			else // choose random force power attack
			{
				tempflex1=rand()*7;
				//tempflex1=7; // hack for testing only
				if (tempflex1 <= 1 && BitTest(forceflags,0x4)) // pull
				{
					call force_pull;
				}
				else if (tempflex1 <= 2 && BitTest(forceflags,0x400)) // grip
				{
					call force_grip;
				}
				else if (tempflex1 <= 3 && BitTest(forceflags,0x40)) // blinding
				{
					call force_blinding;
				}
				else if (tempflex1 <= 4 && BitTest(forceflags,0x200)) // force throw
				{
					call force_throw;
				}
				else if (tempflex1 <= 5 && BitTest(forceflags,0x800)) // lightning
				{
					call force_lightning;
				}
				else if (tempflex1 <= 6 && BitTest(forceflags,0x1000)) // destruction
				{
					call force_destruction;
				}
				else if (BitTest(forceflags,0x2000)) // deadly sight
				{
					call force_deadlysight;
				}
			}
		}
		SetTimerEx(3.0+rand()*(6-difficulty), 501, -1, -1); // schedule another force attack
	}
	else if (GetSenderId() == 502 && BitTest(botmode,0x10000)) // force power defensive/evasive && bot alive
	{
		if (mana > 0) // bot has mana
		{
			if (BitTest(forceflags,0x10) && GetThingHealth(rbot) < 70) // force heal allowed - bot is low on health
			{
				call force_healing;
			}
			else
			{

			tempflex1=rand()*6;
			if (tempflex1 <= 1 && BitTest(forceflags,0x20))
			{
				call force_persuasion;
			}
			else if (tempflex1 <= 2 && BitTest(forceflags,0x80))
			{
				call force_absorb;
			}
			else if (tempflex1 <= 3 && BitTest(forceflags,0x8))
			{
				call force_seeing;
			}
			else if (tempflex1 <= 4 && BitTest(forceflags,0x10))
			{
				call force_healing;
			}
			else if (tempflex1 <= 5 && BitTest(forceflags,0x1))
			{
				call force_speed;
			}
			else if (tempflex1 <= 6 && BitTest(forceflags,0x100))
			{
				call force_protection;
			}
			//else if (BitTest(forceflags,0x2))
			//{
			//	call force_jump;
			//}

			}
		}
		SetTimerEx(3.0+rand()*(6-difficulty), 502, -1, -1); // schedule another force defense
	}
	return;

force_speed:
	if (!BitTest(forcemode,0x4000) && !BitTest(forcemode,0x8000)) // no lightside or darkside surge
	{
		if (mana < 20) return;
		mana = mana-20;
	}
	if (BitTest(forcemode,0x01)) return; // already active
	PlayMode(rbot, 24);
	SendTrigger(-1, 101025, botid, rbot, -1, 22); // tell client to play sound
	forcemode=BitSet(forcemode,0x01); // speed is now active
	SetTimerEx(7+(difficulty * 2.5), 24, 0, 0); // switch it off later
	return;

force_jump:
	if (!BitTest(forcemode,0x4000) && !BitTest(forcemode,0x8000)) // no lightside or darkside surge
	{
		if (mana < 20) return;
		mana = mana-20;
	}
	SendTrigger(-1,101025,botid,rbot,-1,21); // get client to play 'jump' sound
	ApplyForce(rbot, VectorScale(VectorSub(GetThingLVec(rbot), '0 0 -90'), 5.0));
	return;

force_pull:
	if (GetActorFlags(rbot) & 0x800) return; // bot blind
	if (!BitTest(forcemode,0x4000) && !BitTest(forcemode,0x8000)) // no lightside or darkside surge
	{
		if (mana < 20) return;
		mana = mana-20;
	}
	if(GetHealth(target) > 0) // target is still alive
	{
		if (GetThingType(target) == 2) // bot
		{
			targweap=SendMessageEx(GetThingCaptureCog(target), user1, -1, -1, -1, -1);
			if (targweap > 1 && targweap != 10) // don't pull fists or saber
			{
				//print("force pull - bot");
				PlayMode(rbot, 24); // play 'force attack' key
				Sleep(0.3);
				SendMessageEx(GetThingCaptureCog(target), user0, target, rbot, 24, difficulty*2);
				SendTrigger(-1,101025,botid,rbot,target,24); // get client to play 'pull' sound
			}
		}
		else if (GetThingType(target) == 10) // player
		{
			targweap=GetCurWeapon(target);
			if (targweap==11) targweap=10; // If player has saber, set it to 10 for simplicity
			if (targweap > 1 && targweap != 10) // don't pull fists or saber
			{
				PlayMode(rbot, 24); // play 'force attack' key
				Sleep(0.3);
				forcetarget=target;
				SendTrigger(-1,101025,botid,rbot,forcetarget,24); // get client to play 'pull' sound
				item = CreateThingAtPos(tpl0[GetCurWeapon(forcetarget)], GetThingSector(forcetarget), GetThingPos(forcetarget), '0 0 0'); // Create a powerup corresponding to the current weapon
				SetCollideType(item, 0); // Set the created powerup to no collide for now...
				SetTimerEx(0.3, 24, item, 0); // ...and set a timer to make it collide again later
				ApplyForce(item, VectorScale(VectorNorm(VectorSub(GetThingPos(rbot), GetThingPos(forcetarget))), 30)); // Throw the powerup at the bot
				ChangeInv(forcetarget, GetCurWeapon(forcetarget), -1); // Remove the weapon from the player
				SelectWeapon(forcetarget, 1);
			}
		}
	}
	return;

force_seeing:
	if (!BitTest(forcemode,0x4000) && !BitTest(forcemode,0x8000)) // no lightside or darkside surge
	{
		if (mana < 30) return;
		mana = mana-30;
	}
	if (BitTest(forcemode,0x08)) return; // already active
	PlayMode(rbot, 24);
	forcemode=BitSet(forcemode,0x08); // seeing is now active
	SendTrigger(-1, 101025, botid, rbot, -1, 23); // tell client to play sound
	SetTimerEx(7+(difficulty * 5), 23, 0, 0); // switch it off later
	return;

force_healing:
	if (!BitTest(forcemode,0x4000)) // no lightside surge
	{
		if (mana < 200) return;
		mana = mana-200;
	}
	if (GetThingHealth(rbot) > 99) return;
	PlayMode(rbot, 24);
	SendTrigger(-1, 101025, botid, rbot, -1, 25); // tell client to play sound
	if(GetThingHealth(rbot) > 0) HealThing(rbot, 20 * (2+difficulty));
	return;

force_persuasion:
	if (!BitTest(forcemode,0x4000)) // no lightside surge
	{
		if (mana < 250) return;
		mana = mana-250;
	}
	if (BitTest(forcemode,0x20)) return; // already active
	forcemode=BitSet(forcemode,0x20); // persuasion is now active
	SetActorFlags(rbot, 0x80);
	SendTrigger(-1, 101025, botid, rbot, -1, 26); // tell client to play sound
	SetTimerEx(2+(difficulty * 10), 26, 0, 0); // switch it off later
	return;

force_blinding:
	if (!BitTest(forcemode,0x4000)) // no lightside surge
	{
		if (mana < 100) return;
		mana = mana-100;
	}
	PlayMode(rbot, 24);
	Sleep(0.3);
	if(GetThingHealth(rbot) <= 0) Return; // He could die during the sleep
	item = CreateThingAtPos(force_blinding, GetThingSector(rbot), VectorAdd(GetThingPos(rbot), '0.0 0.0 0.04'), '0.0 0.0 0.0');
	SetThingLook(item, VectorSub(GetThingPos(target), GetThingPos(rbot)));
	SendTrigger(-1, 101025, botid, rbot, target, 27); // tell client to play sound
	if (GetThingType(target) == 2) // another bot
	{
		SendMessageEx(GetThingCaptureCog(target), user0, target, rbot, 27, 2+difficulty);  // send info to bot
	}
	else if (GetThingType(target) == 10) // player & does not have force seeing activated
	{
		SendTrigger(target, 101999, GetThingSignature(target), rbot, 27, 2+difficulty); // send info to player
	}
	return;

force_absorb:
	if (!BitTest(forcemode,0x4000)) // no lightside surge
	{
		if (mana < 200) return;
		mana = mana-200;
	}
	if (BitTest(forcemode,0x80)) return; // already active
	PlayMode(rbot, 24);
	forcemode=BitSet(forcemode,0x80); // absorb is now active
	SetTimerEx(2+(difficulty * 5), 28, 0, 0); // switch it off later
	return;

force_protection:
	if (!BitTest(forcemode,0x4000)) // no lightside surge
	{
		if (mana < 300) return;
		mana = mana-300;
	}
	if (BitTest(forcemode,0x100)) return; // already active
	SetThingFlags(rbot, 4); // Make the bot magsealed
	forcemode=BitSet(forcemode,0x100); // protection is now active
	forcearmor=(3+difficulty)*75;
	SendTrigger(-1, 101025, botid, rbot, (3+difficulty) * 10, 29); // tell client to play sound
	SetTimerEx((3+difficulty) * 10, 29, 0, 0); // switch it off later
	return;

force_throw:
	if (GetActorFlags(rbot) & 0x800) return; // bot blind
	if (!BitTest(forcemode,0x8000)) // no darkside surge
	{
		if (mana < 30) return;
		mana = mana-30;
	}
	if(HasLOS(rbot, target)) // check that we still have a LOS on it...
	{
		debris = FirstThingInView(rbot, 40 + 20 * (2+difficulty), 5 + difficulty, 0x010); // check if any debris is available before activating
		if (debris != -1)
		{
			PlayMode(rbot, 24);
			SendTrigger(-1, 101025, botid, rbot, target, 30); // tell client to play sound
			// Check for debris objects in view.
			debris = FirstThingInView(rbot, 40 + 20 * (2+difficulty), 5 + difficulty, 0x010);   // 0x010 debris
			while(debris != -1)
			{
				if(HasLOS(rbot, debris))
				{
					tempvector1 = VectorScale(VectorNorm(VectorSub(GetThingPos(target), GetThingPos(debris))), 300.0);
					DetachThing(debris);
					ApplyForce(debris, tempvector1);
				}
				debris = NextThingInView();
			}
		}
	}
	return;

force_grip:
	if (GetActorFlags(rbot) & 0x800) return; // bot blind
	if (!BitTest(forcemode,0x8000)) // no darkside surge
	{
		if (mana < 50) return;
		mana = mana-50;
	}
	PlayMode(rbot, 24);
	if (!(GetThingFlags(target) & 0x200) && HasLOS(rbot,target) && VectorDist(GetThingPos(rbot), GetThingPos(target)) <= 1) // cannot be gripped
	{
		if (GetThingType(target) == 2) // another bot
		{
			SetTimerEx(0.1, 31, target, -1);
			forcemode=BitSet(forcemode,0x400); // grip is now active
		}
		else // player - still need to work it out
		{
			if(!IsInvActivated(target, 29))        // Protection makes player immune to Grip
			{
				SetTimerEx(0.1, 31, target, -1);
				forcemode=BitSet(forcemode,0x400); // grip is now active
			}
		}
	}
	return;

force_lightning:
	if (!BitTest(forcemode,0x8000)) // no darkside surge
	{
		if (mana < 40) return;
		mana = mana-40;
	}
	FireProjectile(rbot, force_lightning, -1, 35, '0 0 0', '0 0 0', 1.0, 0, 10, 5);
	SendTrigger(-1, 101025, botid, rbot, target, 32); // tell client to play sound
	return;

force_destruction:
	if (!BitTest(forcemode,0x8000)) // no darkside surge
	{
		if (mana < 200) return;
		mana = mana-200;
	}
	FireProjectile(rbot, force_destruction, -1, 24, '0 0 0', '0 0 0', 1.0, 0, 10, 5);
	SendTrigger(-1, 101025, botid, rbot, target, 33); // tell client to play sound
	if (GetThingType(target) == 10) AddDynamicTint(victim, 0.3, 0.0, 0.15); // add tint if target is a player
	return;

force_deadlysight:
	if (GetActorFlags(rbot) & 0x800) return; // bot blind
	if (!BitTest(forcemode,0x8000)) // no darkside surge
	{
		if (mana < 300) return;
		mana = mana-300;
	}
	PlayMode(rbot, 24);
	SendTrigger(-1, 101025, botid, rbot, target, 34); // tell client to play sound
	dscount=0;
	starthealth=GetThingHealth(rbot);
	SetTimerEx(0.1, 34, -1, -1);
	forcemode=BitSet(forcemode,0x2000); // deadly sight is now active
	return;

user0: // force powers against bot
	if(!IsServer()) return;
	enemy=GetParam(1);
	bin=GetParam(2);
	rank=GetParam(3);

	if (bin == 24) // force pull
	{
		if (BitTest(forcemode,0x80)) return; // force absorb makes bot immume to pull
		if (curweap > 1 && curweap != 10) // don't force pull fists or saber
		{
			item = CreateThingAtPos(tpl0[curweap], GetThingSector(rbot), GetThingPos(rbot), '0 0 0'); // Create a powerup corresponding to the current weapon
			SetTimerEx(0.2, 24, item, 0); // Make the powerup collide later
			SetTimerEx(0.25, 24, item, 0); // Duplicate for internet games
			ApplyForce(item, VectorScale(VectorNorm(VectorSub(GetThingPos(enemy), GetThingPos(rbot))), 30)); // Throw the powerup at the player
		}
		SendTrigger(-1, 101030, botid, enemy, bin, rank); // send it to srv2
	}
	else if (bin == 27) // Force Blinding
	{
		if (BitTest(forcemode,0x08)) return; // force seeing makes bot immume to blinding
		if(GetActorFlags(rbot) & 0x800) return; // bot already blind
		SetActorFlags(rbot, 0x800); // blind bot
		SetTimerEx(0.1, 230, -1, -1); // choose random move direction
		SetTimerEx(rank * 4, 27, 0, 0);
		SendTrigger(-1, 101030, botid, enemy, bin, rank); // send it to srv2
	}
	else if (bin == 34) // Deadly Sight
	{
		DamageThing(rbot, 4 * rank, 0x8, enemy);
		SendTrigger(-1, 2004, botid, rbot, -1, -1); // tell client to create smoke effects
		SendTrigger(-1, 101030, botid, enemy, bin, rank); // send it to srv2
	}

	ReturnEx(1);
	return;

user1:
	ReturnEx(curweap); // Current Weapon
	return;

end


