Massassi Forums Logo

This is the static archive of the Massassi Forums. The forums are closed indefinitely. Thanks for all the memories!

You can also download Super Old Archived Message Boards from when Massassi first started.

"View" counts are as of the day the forums were archived, and will no longer increase.

ForumsCog Forum → I need some help with syncing AI
I need some help with syncing AI
2005-09-20, 4:23 PM #1
So, I decided to try and make a little MotS Multiplayer level with some synced AI... however, I have almost no experience with AI.

I used this cog (I can't remember where I got it from, but I think it is the synced AI cog from the massassi downloads section, not sure though.)

The cog itself shows an example of setting up a synced rancor. So I followed the steps and it's working, I have a rancor in my level, however, it does not behave the same as other rancors in MotS.

If I use a simple cog script to 'spawn' an unsynced rancor (with a switch), I get one that attacks me on sight, chases me everywhere, and executes various attacks (slash, bite and grab). When I die, it starts eating me. On top of that, it can't be hurt by blasterbolts (they deflect, as they should).

The synced rancor I get with the other cog, gets hurt by blasterbolts, follows me, but stops at a certain distance from me, and only attacks when I get very close to it. It does not attack me by itself. When it attacks, it only slashes with its claw. It won't bite or do any other attacks. And it doesn't eat me after I die. lol.

The cog had told me to include the mots.tpl (that was in the cog zip) in my project folder, so I assumed it had something to do with the rancor template. So I opened up the mots.tpl and indeed, there was a new ai template for the rancor that was different from the normal template. So I thought about modifying the new template entry to my needs, but I couldn't figure out which property controls what.

My questions:

1. How do I get my synced rancor to be like the other rancor? (more aggressive, more attacks, blaster-resistant and body-eating?)

2. If I wanted to sync a different creature, let's say a Vornskr, would I have to create a new template for it? If so, what would have to be different about the template for it to work with the AI synced cog?

(I'm asking because I tried and replaced the new rancor template with the old one, and that gave me some weird but hilarious results; I ended up being followed by a dead rancor body that wouldn't dissolve, and being attacked by a ghost rancor whenever I let that dead body come too close - if you don't believe me I'll 'Fraps' it for ye ;) )

The cog I used:

Server:

Code:
# Jedi Knight Cog Script
#
# ais_basic_svr.cog
#
# Basic Synced AI: MOTS Version
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# AIS v.5
# Incomplete, but functional.  You may use this in your levels, but
# be aware that there may still be a few bugs, and that improved versions
# with more features will be coming out.
# This version for MOTS only.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# Nightmare, knud@raex.com
# 8/21/99
# ----------------------------------------------------------------------------------------
# Version 0.1 
# First Alpha Version
# ----------------------------------------------------------------------------------------
# Version 0.15
# Added some failsafes to handle unexpected errors. Increased packets sent, to 
# try and eliminate lost packet problems.  Fixed some bugs in movement and firing.
# ----------------------------------------------------------------------------------------
# Version 0.2
# Reworked to reduce lag and lost packet problems.
# Also moved all the different messages sent to an paramater of the trigger message,
# making assignment of the triggers easier and less prone to error.
# ----------------------------------------------------------------------------------------
# Version 0.25
# Added a count to the triggers, to help with the communications with the client cog.
# Added a 'team' variable, so that the AI will only attack opposing players.
# Upped status to 'Beta', since it's working with no known bugs.
# ----------------------------------------------------------------------------------------
# Version 0.27
# Made it so that if the AI is activated by a member of it's team, it will follow that
# until activated again or until it finds a target.
# ----------------------------------------------------------------------------------------
# Version 0.5
# Added behaviour control flags to make AI for civilians, seeking AI's, AI's that punch
# and sentry AI's, and AI's that initialize when an ajoin is crossed instead of at startup,
# as well as added support for limited lives on the AI.  Mostly stuff for co-op.
# ========================================================================================
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
# Implementing the code in your level   #
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
#
# At present, all you have to do for a standard fieldtrooper is place both the client
# and server cog in jed (make sure to use the MOTS.tpl file included with the cog),
# set the integer 'trig' in both cogs, and 'aipos' (a ghost object) in the server cog.
# You can get almost any enemy by changing the varibles in the cog.
#
# The team variable: set to a team number and the AI will not attack players/AI of
# that team.  1 = Red, 2 = Gold, 3 = Blue, 4 = Green.  Set to greater than 4 to make
# it attack everyone.  Don't use 0, as that is the team number for neutral players.
# You can also use team numbers greater than 4 to make groups of enemies that will
# not attack each other, for instace, all rancors on team 5, all stormtroopers on team
# 6, that way Rancors can target ST's and vice versa, but ST's won't attack each other
# and Rancors won't attack each other.
#
# controlflags: this integer is for setting flags that affect the behavior of the AI.
# just choose the flags you want then add together all the numbers to arrive at the
# value you should enter in JED.
# Here's a list of the flags.
#
#	1	Seeking: The AI will go looking for a target rather than just waiting for one
#	2	Sentry: The AI won't move towards/away from a target, moves only if following a player
#	4	Patrol: when seeking, follow a defined path (the frames of the 'seeker' ghost) rather than just wandering
#	8	Spawning: generates when a ajoin is crossed, rather than at startup, to reduce lag in co-op levels.
#	16	punching: if below minimum range, uses a different weapon
#
# 'ammo' and 'lives': These integers limit the number of shots an AI can make, and how many times
# it will respawn, mostly for co-op levels to make them stop after awhile. Setting them to 0
# makes them unlimited.
#
# To make an ai 'seek' a target, you need to place another ghost, assign it to 'seeker', and
# give it frames to move along the path you want the ai to follow.  'seekframes' is the number
# of the last frame.  None of these need to be filled in unless you set the control flags to
# make the AI seek along a path.  Each waypoint should have LOS to the next with no obstacles
# between.  Also, I would suggest that you use the 'sentry' flag as well, so that it won't wander
# far from the path when it finds a target.
#
# I've provided a list of variables to change to get the troopers with repeaters and a rancor.
# You should be able to make almost any enemy by making an ais_* template and setting the
# symbols of the cog from JED.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To make a fieldtrooper with repeater:
#
#	In Server Cog Set these Variables:
#		aipos = a ghost in your level, the place where you want the AI to be
#		trig = a number used as a trigger only in this cog
#		standoffdist = 0.5
#		arriveddist = 1.0
#		minfirerate = 0.25
#		maxfirerate = 1.25
#		drop1 = +drepeatergun
#		drop2 = +dpowercell
#		aitpl = ais_st3
#		
#	In Client Cog Set these Variables:
#		trig = whatever you set the server cog's trig to
#		firesnd = repeat-1.wav
#		weapon = +repeaterball
#
#	Leave all other variables at thier defualt values.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To Make a Rancor
#
#	In Server Cog Set these Variables:
#		aipos = a ghost in your level, the place where you want the AI to be
#		trig = a number used as a trigger only in this cog
#		standoffdist = 0.15
#		minfirerate = 0.75
#		maxfirerate = 2.25
#		maxrange = 0.6
#		hitpoints = 750
#		drop1 = blank (this is an actual template named blank, don't leave the box empty)
#		drop2 = blank
#		aitpl = ais_rancor
#		
#	In Client Cog Set these Variables:
#		trig = whatever you set the server cog's trig to
#		FireKey = ragrab.key
#		HurtKey = rahit.key
#		DieKey = radie.key
#		firesnd = rancattack1.wav
#		hurtsound = ranchurt1.wav
#		diesound = rancdie.wav
#		weapon = +ais_ranc_claw
#
#	Leave all other variables at thier defualt values.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# ========================================================================================
# =-=-=-=-=-=-=-=-= #
# Notes on the Code #
# =-=-=-=-=-=-=-=-= #
#
# This is a beta: you may use this cog in your levels, but I will be releasing new
# versions with added features.
#
# Planned improvements include:
#
#	Support for carbon freeze (maybe)
#
#	Support for force powers like pull, grip, and deadlysight.
#
#	Support for multiple weapons, close range weapons (such as punching)
#	and a special version for MP Dark Jedi (maybe)
# ========================================================================================

symbols

message		startup
message		timer
message		damaged
message		pulse
message		activated
message		crossed
message		skill

thing		aipos					nolink
thing		ai			mask=-1		local

int		trig
int		team=0
int		controlflags=0x10

thing		seeker
int		seekframes
flex		seekspeed=120.0				local

flex		sightdist=5.0
flex		standoffdist=.75
flex		arriveddist=1.5
flex		movespeed=0.35
flex		damage_modifier=.75

flex		minfirerate=.75
flex		maxfirerate=4.5
flex		maxrange=4.5
flex		minrange=.3
flex		punchrange=.25
flex		lead=0.4
flex		respawndelay=10.0

int		hitpoints=250
int		healrate=5
int		ammo=0
int		lives=0
int		killpnts=1
int		vision=225

template	drop1=+dstrifle
template	drop2=+denergycell
template	aitpl=ais_st2

surface		ajoin0
surface		ajoin1
surface		ajoin2

thing		target			local
thing		movetarget		local
thing		victim			local
thing		killer			local
thing		player			local

sector		sec0			local

flex		damage_type		local
flex		damage_amount		local
flex		targetswitch		local
flex		Dot			local
flex		MaxDot			local

int		statflags		local
int		aimodeflags		local

int		LOScountdown=0		local
int		hp			local
int		weapon			local
int		curframe		local
int		shotcount=0		local
int		hitcount=0		local
int		incarnation=0		local

vector		firepos			local
vector		firevec			local
vector		movevec			local
vector		movepos			local
vector		interceptvec		local
vector		dodgevec		local
vector		gunoffset		local

int		debug=0

end

# ========================================================================================

code

# ========================================================================================

startup:

	# _________________________________________________________________________________
	#
	# INITIALIZATION
	# _________________________________________________________________________________
	#
	# Create the AI thing and capture it so that the cog recieves the
	# 'damaged' messages. Set the initial 'statflags' and 'aiflags'
	# settings and start the pulse.
	# _________________________________________________________________________________

	if(BitTest(controlflags, 0x8)) return;
	if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
	CaptureThing(ai);
	SetThingUserData(ai, team);
	movetarget = aipos;
	hp = hitpoints;
	statflags = 0x3;
	aiflags = 0x2;
	SetPulse(.5);
	target = -1;
	shotcount = 0;
	hitcount = 0;
	incarnation = 0;

	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);
	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);
	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);

	if(debug)
		{
		jkStringClear();
		jkStringConcatInt(trig);
		jkStringConcatAsciiString("s:startup");
		jkStringOutput(-1, -1);
		}

		if(BitTest(controlflags, 0x1))
			{
			curframe = 0;
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			SkipToFrame(seeker, 0, seekspeed);
			}

	return;

# ========================================================================================

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
	# _________________________________________________________________________________
	#
	# AJOIN CROSSED
	# _________________________________________________________________________________
	#
	# If one of the ajoins is crossed, call the initialization routine.
	# _________________________________________________________________________________


crossed:

	controlflags = BitClear(controlflags, 0x8);
	call startup;
	return;

# ========================================================================================

pulse:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
	# _________________________________________________________________________________
	#
	# PULSE
	# _________________________________________________________________________________
	#
	# If it isn't alive, don't do anything at all.
	# Syncronize the position of the thing so it's in the same place on all computers.
	# Heal the AI if it's been hurt, this keeps it from wearing down by attrition.
	# _________________________________________________________________________________

	if(BitTest(aiflags, 0x1)) return;
	SyncThingPos(ai);
	if(hp < hitpoints) hp = hp + healrate;

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# PULSE: LOOK FOR TARGET
	# _________________________________________________________________________________
	#
	# If the AI doesn't have a target, cycle through all the things in view and look
	# for opponets.  If a opponet is found, set him/her as the target, adjust the AI
	# and status flags, and begin firing at random intervals.
	# _________________________________________________________________________________

	if(BitTest(aiflags, 0x2))
		{
		victim = -1;
		maxDot = 0;
		victim = FirstThingInView(ai, vision, sightdist, 0x404);
		while(victim != -1)
			{
			if((HasLOS(ai, victim)) && ((GetThingType(victim) == 10 && GetPlayerTeam(victim) != team) || (GetThingType(victim) == 2 && GetThingUserData(victim) != team)) && (VectorDist(GetThingPos(ai), GetThingPos(victim)) <= sightdist) && (victim != ai))
				{
				dot = ThingViewDot(ai, victim);
				if(dot > maxDot)
					{
					target = victim;
					maxDot = dot;
					}
				}
			victim = NextThingInView();
			}
		if(target != -1)
			{
			if(BitTest(controlflags, 0x1)) StopThing(seeker);
			movetarget = target;
			aiflags = BitClear(aiflags, 0x2);
			aiflags = BitSet(aiflags, 0x8);
			aiflags = BitSet(aiflags, 0x20);
			if(BitTest(aiflags, 0x80)) aiflags = BitClear(aiflags, 0x80);
			statflags = BitSet(statflags, 0x4);
			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:startup");
				jkStringOutput(-1, -1);
				}
			}
		}


	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# PULSE: MOVEMENT
	# _________________________________________________________________________________
	#
	# If the almighty 'aiflags' say to move forward, find a spot a little ahead of the
	# target (so the AI intercepts the target rather than following the target) and
	# move towards that spot.  If too close to the target, reverse and reduce the
	# movement vector so that the AI tries to maintain a constant distance.
	# Follow close to 'movetarget' if following a player who activated the AI or the
	# seeking route ghost.
	# _________________________________________________________________________________


	if(BitTest(aiflags, 0x20) && !BitTest(controlflags, 0x2))
		{
		interceptvec = VectorNorm(VectorSub(VectorAdd(GetThingPos(movetarget), VectorScale(GetThingVel(movetarget), lead)), GetThingPos(ai)));
		movevec = VectorSet(VectorX(interceptvec), VectorY(interceptvec), 0.0);
		SetThingLook(ai, movevec);
		if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) < standoffdist) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), -movespeed));
		if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) > (arriveddist)) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), movespeed));
		}

	if(BitTest(aiflags, 0x80))
		{
		if(GetThingType(movetarget) != 10 && BitTest(controlflags, 0x1))
			{
			if(!IsthingMoving(seeker) && VectorDist(GetThingPos(ai), GetThingPos(movetarget)) < .25)
				{
				curframe = curframe + 1;
				if(curframe > seekframes) curframe = 0;
				SkipToFrame(seeker, curframe, seekspeed);
				}
			}
		if(GetThingType(movetarget) == 10 && !HasLOS(ai, movetarget))
			{
			movetarget = -1;
			aiflags = BitClear(aiflags, 0x80);
			}
		if(HasLOS(ai, movetarget))
			{
			interceptvec = VectorNorm(VectorSub(VectorAdd(GetThingPos(movetarget), VectorScale(GetThingVel(movetarget), lead)), GetThingPos(ai)));
			movevec = VectorSet(VectorX(interceptvec), VectorY(interceptvec), 0.0);
			SetThingLook(ai, movevec);
			if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) > .25) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), movespeed));
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# PULSE: TARGET CHECK
	# _________________________________________________________________________________
	#
	# The target is considered 'lost' if it either falls out of range of LOS is lost.
	# If the target is lost for 8 consecutive pulses (4 sec), dies, or ceases to exist,
	# reset the status and ai flags and begin looking for a new target.
	# _________________________________________________________________________________


	if(BitTest(aiflags, 0x8))
		{
		if(HasLOS(ai, target)) LOScountdown = 0;
		if(!HasLOS(ai, target) || VectorDist(GetThingPos(ai), GetThingPos(target)) < sightdist) LOScountdown = LOScountdown + 1;
		if(LOScountdown >= 8 || (GetThingFlags(target) & 0x200))
			{
			target = -1;
			movetarget = -1;
			aiflags = BitSet(aiflags, 0x2);
			aiflags = BitClear(aiflags, 0x8);
			aiflags = BitClear(aiflags, 0x20);
			statflags = BitClear(statflags, 0x4);

			if(BitTest(controlflags, 0x1))
				{
				movetarget = seeker;
				aiflags = BitSet(aiflags, 0x80);
				SkipToFrame(seeker, curframe, seekspeed);
				}

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:target lost");
				jkStringOutput(-1, -1);
				}
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# PULSE: FAILSAFES
	# _________________________________________________________________________________
	#
	# These are just checks to catch and reset the AI if anything unexpected should
	# happen to it, such as if it were destroyed or changed by another cog.
	# Also check to see if the AI is in a pit, if so, respawn.
	# _________________________________________________________________________________


	sec0 = GetThingSector(ai);
	if((ai == -1) || (GetThingTemplate(ai) != aitpl) || (GetSectorFlags(sec0) & 0x40))
		{
		target = -1;
		if(ai > -1)
			{
			StopThing(ai);
			ReleaseThing(ai);
			DestroyThing(ai);
			}
		if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
		CaptureThing(ai);
		incarnation = incarnation + 1;
		SetThingUserData(ai, team);
		hp = hitpoints;
		shotcount = 0;
		hitcount = 0;

		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);

		statflags = 0x3;
		aiflags = 0x2;

		if(BitTest(controlflags, 0x1))
			{
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			curframe = 0;
			SkipToFrame(seeker, 0, seekspeed);
			}

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:failsafe reset");
				jkStringOutput(-1, -1);
				}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;

# ========================================================================================

damaged:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# DAMAGED
	# _________________________________________________________________________________
	#
	# Only take damage if the the AI is alive and it's damaged by an oppnet. If the damage
	# fit the type mask, apply the damage modifier. Reduce the hitpoints by final result.
	# _________________________________________________________________________________


	if(!BitTest(statflags, 0x1)) return;
	killer = GetThingParent(GetSourceRef());
	if((killer == -1) || ((GetThingType(killer) == 10 && GetPlayerTeam(killer) == team) || (GetThingType(killer) == 2 && GetThingUserData(killer) == team))) return;

	damage_type = GetParam(1);
	damage_amount = GetParam(0);

	if(BitTest(damage_type, 0x15)) damage_amout = damage_amount * damage_modifier;

	hp = hp - damage_amount;

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# DAMAGED: HURT
	# _________________________________________________________________________________
	#
	# If the AI is still alive after taking damage, send a trigger to do the hurt effects,
	# and if possibly switch targets to the player damaging it (so if it's not vulnerable
	# to others player while pursuing a target).  Always select a target if it has none.
	# _________________________________________________________________________________


	if(hp > 0)
		{
		targetswitch = damage_amount + (rand() * 80);
		if((targetswitch > 100) || (!BitTest(statflags, 0x4)) && ((GetThingType(killer) == 10 && GetPlayerTeam(killer) != team) || (GetThingType(killer) == 2 && GetThingUserData(killer) != team)))
			{
			if(BitTest(controlflags, 0x1)) StopThing(seeker);
			target = killer;
			movetarget = killer;
			LOScountdown = 8;

			if(BitTest(aiflags, 0x2)) aiflags = BitClear(aiflags, 0x2);
			if(!BitTest(aiflags, 0x8)) aiflags = BitSet(aiflags, 0x8);
			if(!BitTest(aiflags, 0x20)) aiflags = BitSet(aiflags, 0x20);
			if(BitTest(aiflags, 0x80)) aiflags = BitClear(aiflags, 0x80);
			if(!BitTest(statflags, 0x4)) statflags = BitSet(statflags, 0x4);

			KillTimerEx(4);
			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);
			}

		hitcount = hitcount + 1;

		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);
		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);
		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:damaged - hurt");
			jkStringOutput(-1, -1);
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# DAMAGED: DEATH
	# _________________________________________________________________________________
	#
	# If the AI's hitpoints have been reduced to zero, set the flags so as to prevent
	# the AI from doing anything, send the trigger to do the death effects, add a point
	# and a kill to the player who fragged it, kill the pulse, and set the respawn timer. 
	# _________________________________________________________________________________

	if(hp <= 0)
		{
		if(BitTest(controlflags, 0x1)) StopThing(seeker);
		target = -1;
		ReleaseThing(ai);
		movetarget = aipos;
		statflags = 0x0;
		aiflags = 0x1;
		SetPulse(0);
		KillTimerEx(4);
		CreateThing(drop1, ai);
		CreateThing(drop2, ai);
		SetPlayerScore(killer, GetPlayerScore(killer) + killpnts);
		SetPlayerKills(killer, GetPlayerKills(killer) + killpnts);
		SyncScores();

		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);

		if(lives == 1)
			{
			SetPulse(0);
			aiflags = 0x0;
			statflags = 0x0;
			controlflags = 0x0;
			}
		if(lives != 1)
			{
			SetTimerEx(respawndelay, 7, 0.0, 0.0);
			if(lives > 1) lives = lives - 1;
			}

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:damaged - death");
			jkStringOutput(-1, -1);
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;



# ========================================================================================

timer:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# TIMER: FIRE WEAPON
	# _________________________________________________________________________________
	#
	# Check that the AI is alive, has a target, has ammo, and is attacking.
	# Lead the target a little, don't aim right at it, but a little in front of it.
	# Create a Projectile and send a trigger to do the fire effects. Clear the flags
	# after short delay so the AI can move again.
	# _________________________________________________________________________________


	if(GetSenderID() == 4)
		{
		if(BitTest(aiflags, 0x8))
			{
			if((ammo == 0 || shotcount < ammo) && ((VectorDist(GetThingPos(ai), GetThingPos(target)) > minrange) && (VectorDist(GetThingPos(ai), GetThingPos(target)) < maxrange) && (HasLOS(ai, target))))
				{
				StopThing(ai);
				firepos = VectorAdd(GetThingPos(target), VectorScale(GetThingVel(target), lead));
				firevec = VectorNorm(VectorSub(firepos, GetThingPos(ai)));
				SetThingLook(ai, firevec);
				shotcount = shotcount + 1;

				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				}
			if((VectorDist(GetThingPos(ai), GetThingPos(target)) < punchrange && HasLOS(ai, target)) && BitTest(controlflags, 0x10))
				{
				StopThing(ai);
				firepos = VectorAdd(GetThingPos(target), VectorScale(GetThingVel(target), lead));
				firevec = VectorNorm(VectorSub(firepos, GetThingPos(ai)));
				SetThingLook(ai, firevec);
				shotcount = shotcount + 1;

				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				}

			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:fire");
				jkStringOutput(-1, -1);
				}
			}
		return;
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# _________________________________________________________________________________
	#
	# TIMER: RESPAWN
	# _________________________________________________________________________________
	#
	# Release and destroy the dead AI thing, create and capture a new one.
	# Set all the numbers, and restart the pulse.
	# _________________________________________________________________________________


	if(GetSenderID() == 7)
		{
		if(ai != -1) DestroyThing(ai);
		if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
		CaptureThing(ai);
		SetThingUserData(ai, team);
		incarnation = incarnation + 1;
		hp = hitpoints;
		shotcount = 0;
		hitcount = 0;
		statflags = 0x3;
		aiflags = 0x2;
		SetPulse(.5);

		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);

		if(BitTest(controlflags, 0x1))
			{
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			curframe = 0;
			SkipToFrame(seeker, 0, seekspeed);
			}

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:respawn");
			jkStringOutput(-1, -1);
			}

		return;
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;


activated:

	# _________________________________________________________________________________
	#
	# ACTIVATION
	# _________________________________________________________________________________
	#
	# If a player on the AI's team activates it with the spacebar, follow that character
	# until activated again or until it finds a target.
	# _________________________________________________________________________________

	if(GetSenderRef() == ai)
		{
		player = GetSourceRef();
		if((GetPlayerTeam(player) == team) && (BitTest(aiflags, 0x2)))
			{
			if(!BitTest(aiflags, 0x80))
				{
				movetarget = player;
				aiflags = BitSet(aiflags, 0x80);

				if(debug)
					{
					jkStringClear();
					jkStringConcatInt(trig);
					jkStringConcatAsciiString("s:follow");
					jkStringOutput(-1, -1);
					}
				return;
				}
			if(BitTest(aiflags, 0x80))
				{
				movetarget = -1;
				aiflags = BitClear(aiflags, 0x80);

				if(debug)
					{
					jkStringClear();
					jkStringConcatInt(trig);
					jkStringConcatAsciiString("s:stop");
					jkStringOutput(-1, -1);
					}
				return;
				}
			}
		}
	return;

# ========================================================================================

end



Client:

Code:
# Jedi Knight Cog Script
#
# ais_basic_cli.cog
#
# Basic Synced AI
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# AI Synced Client Side Script, v.5
#
# Plays KEYS, Makes sounds, Ect. for ais_basic_svr.cog.  Client-Side script.
# Very straight forward, just does effects for actions in the server script.
# See the server script for complete information on using the cog.
# MOTS Only.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Nightmare, knud@raex.com
# 8/21/99
# ----------------------------------------------------------------------------------------
# Version 0.1
# First Alpha Version
# ----------------------------------------------------------------------------------------
# Version 0.15 
# Added handling to prevent multiple calls, allowing server cog to make multiple
# calls, helping to eliminate lost packet problems.
# ----------------------------------------------------------------------------------------
# Version 0.2
# Added 'init' trigger, added FireProjectileLocal() to fire section.
# Moved the message being sent to a param of the trigger instead of
# using different triggers for each event, reducing problems with
# trigger assignment.
# ----------------------------------------------------------------------------------------
# Version 0.25
# Made some modifications, mainly error and lost packet fixes.
# ----------------------------------------------------------------------------------------
# Version 0.5
# Added punching attacks.
# ========================================================================================

flags=0x100

symbols

thing		ai		local

int		trig
int		trignum		local

int		paramtemp	local
int		weap		local

int		hitcount=-1	local
int		shotcount=-1	local
int		incarnation=-1	local
int		deaths=-1	local

keyframe	FireKey=stfire.key
keyframe	PunchKey=s5punch.key
keyframe	HurtKey=sthit.key
keyframe	DieKey=stdie.key

sound		firesnd=hsblaster.wav
sound		punchsnd=swingfist01.wav
sound		HurtSnd=st50173.wav
sound		DieSnd=st50175.wav

template	weapon=+ais_laser
template	punch=+ais_punch

vector		gunoffset	local

int		debug=0

message		trigger
message		timer

end

# ========================================================================================

code

trigger:

	if(GetSourceRef() != trig) return;
	trignum = GetParam(0);
	ai = GetParam(1);
	paramtemp = GetParam(2);
	weap = GetParam(3);

	if(trignum == 1)		// init
		{
		if(paramtemp != incarnation)
			{
			incarnation = paramtemp;
			shotcount = -1;
			hitcount = -1;
			gunoffset = GetThingFireOffSet(ai);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("c:init");
				jkStringOutput(-1, -1);
				}
			}
		}

	if(trignum == 4)		// fire
		{
		if(paramtemp != shotcount)
			{
			shotcount = paramtemp;
			if(weap == 0)		// primary weapon
				{
				FireProjectile(ai, weapon, firesnd, 8, gunoffset, '0 0 0', 1.0, 0x20, 25, .75);
				PlayKey(ai, FireKey, 2, 32);
				}

			if(weap == 1)		// punching weapon
				{
				FireProjectile(ai, punch, punchsnd, 8, gunoffset, '0 0 0', 1.0, 0x20, 25, .75);
				PlayKey(ai, punchKey, 2, 32);
				}

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("c:fire");
				jkStringOutput(-1, -1);
				}
			}
		return;
		}

	if(trignum == 5)		// hurt
		{
		if(paramtemp != hitcount)
			{
			hitcount = paramtemp;
			PlayKey(ai, HurtKey, 2, 32);
			PlaySoundThing(HurtSnd, ai, 1.0, -1, -1, 0);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("c:hit");
				jkStringOutput(-1, -1);
				}
			}
		return;
		}

	if(trignum == 6)		// die
		{
		if(paramtemp != deaths)
			{
			deaths = paramtemp;
			SetThingMass(ai, 0);
			SetThingFlags(ai, 0x200);
			StopThing(ai);
			PlayKey(ai, DieKey, 1, 32);
			PlaySoundThing(DieSnd, ai, 1.0, -1, -1, 0);
			SetTimer(GetKeyLen(DieKey));

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("c:death");
				jkStringOutput(-1, -1);
				}
			}
		return;
		}

	return;

timer:

	DestroyThing(ai);
	return;


end


P.S. I don't understand anything about cog. If someone has a better synced AI cog (for example one that can spawn them with a switch), please tell me. Also, there was another cog included in the zip called 'ais_basic_jk.cog'. I didn't know what to do with it, so I didn't use it.

Any help would be welcome... :)
ORJ / My Level: ORJ Temple Tournament I
2005-09-20, 7:14 PM #2
Code:
# Jedi Knight Cog Script
#
# ais_basic_svr.cog
#
# Basic Synced AI: MOTS Version
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# AIS v.5
# Incomplete, but functional.  You may use this in your levels, but
# be aware that there may still be a few bugs, and that improved versions
# with more features will be coming out.
# This version for MOTS only.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# Nightmare, knud@raex.com
# 8/21/99
# ----------------------------------------------------------------------------------------
# edited by Reid
# made it instead of an adjoin crossed to a switch activated
# ----------------------------------------------------------------------------------------
# Version 0.1 
# First Alpha Version
# ----------------------------------------------------------------------------------------
# Version 0.15
# Added some failsafes to handle unexpected errors. Increased packets sent, to 
# try and eliminate lost packet problems.  Fixed some bugs in movement and firing.
# ----------------------------------------------------------------------------------------
# Version 0.2
# Reworked to reduce lag and lost packet problems.
# Also moved all the different messages sent to an paramater of the trigger message,
# making assignment of the triggers easier and less prone to error.
# ----------------------------------------------------------------------------------------
# Version 0.25
# Added a count to the triggers, to help with the communications with the client cog.
# Added a 'team' variable, so that the AI will only attack opposing players.
# Upped status to 'Beta', since it's working with no known bugs.
# ----------------------------------------------------------------------------------------
# Version 0.27
# Made it so that if the AI is activated by a member of it's team, it will follow that
# until activated again or until it finds a target.
# ----------------------------------------------------------------------------------------
# Version 0.5
# Added behaviour control flags to make AI for civilians, seeking AI's, AI's that punch
# and sentry AI's, and AI's that initialize when an ajoin is crossed instead of at startup,
# as well as added support for limited lives on the AI.  Mostly stuff for co-op.
# ==================================================  ======================================
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
# Implementing the code in your level   #
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
#
# At present, all you have to do for a standard fieldtrooper is place both the client
# and server cog in jed (make sure to use the MOTS.tpl file included with the cog),
# set the integer 'trig' in both cogs, and 'aipos' (a ghost object) in the server cog.
# You can get almost any enemy by changing the varibles in the cog.
#
# The team variable: set to a team number and the AI will not attack players/AI of
# that team.  1 = Red, 2 = Gold, 3 = Blue, 4 = Green.  Set to greater than 4 to make
# it attack everyone.  Don't use 0, as that is the team number for neutral players.
# You can also use team numbers greater than 4 to make groups of enemies that will
# not attack each other, for instace, all rancors on team 5, all stormtroopers on team
# 6, that way Rancors can target ST's and vice versa, but ST's won't attack each other
# and Rancors won't attack each other.
#
# controlflags: this integer is for setting flags that affect the behavior of the AI.
# just choose the flags you want then add together all the numbers to arrive at the
# value you should enter in JED.
# Here's a list of the flags.
#
#	1	Seeking: The AI will go looking for a target rather than just waiting for one
#	2	Sentry: The AI won't move towards/away from a target, moves only if following a player
#	4	Patrol: when seeking, follow a defined path (the frames of the 'seeker' ghost) rather than just wandering
#	8	Spawning: generates when a ajoin is crossed, rather than at startup, to reduce lag in co-op levels.
#	16	punching: if below minimum range, uses a different weapon
#
# 'ammo' and 'lives': These integers limit the number of shots an AI can make, and how many times
# it will respawn, mostly for co-op levels to make them stop after awhile. Setting them to 0
# makes them unlimited.
#
# To make an ai 'seek' a target, you need to place another ghost, assign it to 'seeker', and
# give it frames to move along the path you want the ai to follow.  'seekframes' is the number
# of the last frame.  None of these need to be filled in unless you set the control flags to
# make the AI seek along a path.  Each waypoint should have LOS to the next with no obstacles
# between.  Also, I would suggest that you use the 'sentry' flag as well, so that it won't wander
# far from the path when it finds a target.
#
# I've provided a list of variables to change to get the troopers with repeaters and a rancor.
# You should be able to make almost any enemy by making an ais_* template and setting the
# symbols of the cog from JED.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To make a fieldtrooper with repeater:
#
#	In Server Cog Set these Variables:
#		aipos = a ghost in your level, the place where you want the AI to be
#		trig = a number used as a trigger only in this cog
#		standoffdist = 0.5
#		arriveddist = 1.0
#		minfirerate = 0.25
#		maxfirerate = 1.25
#		drop1 = +drepeatergun
#		drop2 = +dpowercell
#		aitpl = ais_st3
#		
#	In Client Cog Set these Variables:
#		trig = whatever you set the server cog's trig to
#		firesnd = repeat-1.wav
#		weapon = +repeaterball
#
#	Leave all other variables at thier defualt values.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To Make a Rancor
#
#	In Server Cog Set these Variables:
#		aipos = a ghost in your level, the place where you want the AI to be
#		trig = a number used as a trigger only in this cog
#		standoffdist = 0.15
#		minfirerate = 0.75
#		maxfirerate = 2.25
#		maxrange = 0.6
#		hitpoints = 750
#		drop1 = blank (this is an actual template named blank, don't leave the box empty)
#		drop2 = blank
#		aitpl = ais_rancor
#		
#	In Client Cog Set these Variables:
#		trig = whatever you set the server cog's trig to
#		FireKey = ragrab.key
#		HurtKey = rahit.key
#		DieKey = radie.key
#		firesnd = rancattack1.wav
#		hurtsound = ranchurt1.wav
#		diesound = rancdie.wav
#		weapon = +ais_ranc_claw
#
#	Leave all other variables at thier defualt values.
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# ==================================================  ======================================
# =-=-=-=-=-=-=-=-= #
# Notes on the Code #
# =-=-=-=-=-=-=-=-= #
#
# This is a beta: you may use this cog in your levels, but I will be releasing new
# versions with added features.
#
# Planned improvements include:
#
#	Support for carbon freeze (maybe)
#
#	Support for force powers like pull, grip, and deadlysight.
#
#	Support for multiple weapons, close range weapons (such as punching)
#	and a special version for MP Dark Jedi (maybe)
# ==================================================  ======================================

symbols

message		startup
message		timer
message		damaged
message		pulse
message		activated
message		skill

thing		aipos					nolink
thing		ai			mask=-1		local

int		trig
int		team=0
int		controlflags=0x10

thing		seeker
int		seekframes
flex		seekspeed=120.0				local

flex		sightdist=5.0
flex		standoffdist=.75
flex		arriveddist=1.5
flex		movespeed=0.35
flex		damage_modifier=.75

flex		minfirerate=.75
flex		maxfirerate=4.5
flex		maxrange=4.5
flex		minrange=.3
flex		punchrange=.25
flex		lead=0.4
flex		respawndelay=10.0

flex		volume
flex		balance

int		hitpoints=250
int		healrate=5
int		ammo=0
int		lives=0
int		killpnts=1
int		vision=225

template	drop1=+dstrifle
template	drop2=+denergycell
template	aitpl=ais_st2

surface		switch

sound		activated

thing		target			local
thing		movetarget		local
thing		victim			local
thing		killer			local
thing		player			local

sector		sec0			local

flex		damage_type		local
flex		damage_amount		local
flex		targetswitch		local
flex		Dot			local
flex		MaxDot			local

int		statflags		local
int		aimodeflags		local

int		LOScountdown=0		local
int		hp			local
int		weapon			local
int		curframe		local
int		shotcount=0		local
int		hitcount=0		local
int		incarnation=0		local

vector		firepos			local
vector		firevec			local
vector		movevec			local
vector		movepos			local
vector		interceptvec		local
vector		dodgevec		local
vector		gunoffset		local

int		debug=0

end

# ==================================================  ======================================

code

# ==================================================  ======================================

startup:

	# __________________________________________________  _______________________________
	#
	# INITIALIZATION
	# __________________________________________________  _______________________________
	#
	# Create the AI thing and capture it so that the cog recieves the
	# 'damaged' messages. Set the initial 'statflags' and 'aiflags'
	# settings and start the pulse.
	# __________________________________________________  _______________________________

	if(BitTest(controlflags, 0x8)) return;
	if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
	CaptureThing(ai);
	SetThingUserData(ai, team);
	movetarget = aipos;
	hp = hitpoints;
	statflags = 0x3;
	aiflags = 0x2;
	SetPulse(.5);
	target = -1;
	shotcount = 0;
	hitcount = 0;
	incarnation = 0;

	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);
	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);
	SendTrigger(-1, trig, 1, GetGuidThing(ai), 0, 0);

	if(debug)
		{
		jkStringClear();
		jkStringConcatInt(trig);
		jkStringConcatAsciiString("s:startup");
		jkStringOutput(-1, -1);
		}

		if(BitTest(controlflags, 0x1))
			{
			curframe = 0;
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			SkipToFrame(seeker, 0, seekspeed);
			}

	return;

# ==================================================  ======================================

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
	# __________________________________________________  _______________________________
	#
	# AJOIN CROSSED
	# __________________________________________________  _______________________________
	#
	# When the switch is activated, call the initialization routine.
	# __________________________________________________  _______________________________


activated:
	if(getSenderRef == switch)
	{	
		setSurfaceCel(switch, 1);
		controlflags = BitClear(controlflags, 0x8);
		call startup;
		playSoundLocal(activated, volume, balance, 0x0);
		sleep(0.5);
		setSurfaceCel(switch, 0);
	}
	return;

# ==================================================  ======================================

pulse:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #
	# __________________________________________________  _______________________________
	#
	# PULSE
	# __________________________________________________  _______________________________
	#
	# If it isn't alive, don't do anything at all.
	# Syncronize the position of the thing so it's in the same place on all computers.
	# Heal the AI if it's been hurt, this keeps it from wearing down by attrition.
	# __________________________________________________  _______________________________

	if(BitTest(aiflags, 0x1)) return;
	SyncThingPos(ai);
	if(hp < hitpoints) hp = hp + healrate;

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# PULSE: LOOK FOR TARGET
	# __________________________________________________  _______________________________
	#
	# If the AI doesn't have a target, cycle through all the things in view and look
	# for opponets.  If a opponet is found, set him/her as the target, adjust the AI
	# and status flags, and begin firing at random intervals.
	# __________________________________________________  _______________________________

	if(BitTest(aiflags, 0x2))
		{
		victim = -1;
		maxDot = 0;
		victim = FirstThingInView(ai, vision, sightdist, 0x404);
		while(victim != -1)
			{
			if((HasLOS(ai, victim)) && ((GetThingType(victim) == 10 && GetPlayerTeam(victim) != team) || (GetThingType(victim) == 2 && GetThingUserData(victim) != team)) && (VectorDist(GetThingPos(ai), GetThingPos(victim)) <= sightdist) && (victim != ai))
				{
				dot = ThingViewDot(ai, victim);
				if(dot > maxDot)
					{
					target = victim;
					maxDot = dot;
					}
				}
			victim = NextThingInView();
			}
		if(target != -1)
			{
			if(BitTest(controlflags, 0x1)) StopThing(seeker);
			movetarget = target;
			aiflags = BitClear(aiflags, 0x2);
			aiflags = BitSet(aiflags, 0x8);
			aiflags = BitSet(aiflags, 0x20);
			if(BitTest(aiflags, 0x80)) aiflags = BitClear(aiflags, 0x80);
			statflags = BitSet(statflags, 0x4);
			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:startup");
				jkStringOutput(-1, -1);
				}
			}
		}


	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# PULSE: MOVEMENT
	# __________________________________________________  _______________________________
	#
	# If the almighty 'aiflags' say to move forward, find a spot a little ahead of the
	# target (so the AI intercepts the target rather than following the target) and
	# move towards that spot.  If too close to the target, reverse and reduce the
	# movement vector so that the AI tries to maintain a constant distance.
	# Follow close to 'movetarget' if following a player who activated the AI or the
	# seeking route ghost.
	# __________________________________________________  _______________________________


	if(BitTest(aiflags, 0x20) && !BitTest(controlflags, 0x2))
		{
		interceptvec = VectorNorm(VectorSub(VectorAdd(GetThingPos(movetar  get), VectorScale(GetThingVel(movetarget), lead)), GetThingPos(ai)));
		movevec = VectorSet(VectorX(interceptvec), VectorY(interceptvec), 0.0);
		SetThingLook(ai, movevec);
		if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) < standoffdist) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), -movespeed));
		if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) > (arriveddist)) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), movespeed));
		}

	if(BitTest(aiflags, 0x80))
		{
		if(GetThingType(movetarget) != 10 && BitTest(controlflags, 0x1))
			{
			if(!IsthingMoving(seeker) && VectorDist(GetThingPos(ai), GetThingPos(movetarget)) < .25)
				{
				curframe = curframe + 1;
				if(curframe > seekframes) curframe = 0;
				SkipToFrame(seeker, curframe, seekspeed);
				}
			}
		if(GetThingType(movetarget) == 10 && !HasLOS(ai, movetarget))
			{
			movetarget = -1;
			aiflags = BitClear(aiflags, 0x80);
			}
		if(HasLOS(ai, movetarget))
			{
			interceptvec = VectorNorm(VectorSub(VectorAdd(GetThingPos(movetar  get), VectorScale(GetThingVel(movetarget), lead)), GetThingPos(ai)));
			movevec = VectorSet(VectorX(interceptvec), VectorY(interceptvec), 0.0);
			SetThingLook(ai, movevec);
			if(VectorDist(GetThingPos(ai), GetThingPos(movetarget)) > .25) SetThingVel(ai, VectorScale(VectorNorm(GetThingLVec(ai)), movespeed));
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# PULSE: TARGET CHECK
	# __________________________________________________  _______________________________
	#
	# The target is considered 'lost' if it either falls out of range of LOS is lost.
	# If the target is lost for 8 consecutive pulses (4 sec), dies, or ceases to exist,
	# reset the status and ai flags and begin looking for a new target.
	# __________________________________________________  _______________________________


	if(BitTest(aiflags, 0x8))
		{
		if(HasLOS(ai, target)) LOScountdown = 0;
		if(!HasLOS(ai, target) || VectorDist(GetThingPos(ai), GetThingPos(target)) < sightdist) LOScountdown = LOScountdown + 1;
		if(LOScountdown >= 8 || (GetThingFlags(target) & 0x200))
			{
			target = -1;
			movetarget = -1;
			aiflags = BitSet(aiflags, 0x2);
			aiflags = BitClear(aiflags, 0x8);
			aiflags = BitClear(aiflags, 0x20);
			statflags = BitClear(statflags, 0x4);

			if(BitTest(controlflags, 0x1))
				{
				movetarget = seeker;
				aiflags = BitSet(aiflags, 0x80);
				SkipToFrame(seeker, curframe, seekspeed);
				}

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:target lost");
				jkStringOutput(-1, -1);
				}
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# PULSE: FAILSAFES
	# __________________________________________________  _______________________________
	#
	# These are just checks to catch and reset the AI if anything unexpected should
	# happen to it, such as if it were destroyed or changed by another cog.
	# Also check to see if the AI is in a pit, if so, respawn.
	# __________________________________________________  _______________________________


	sec0 = GetThingSector(ai);
	if((ai == -1) || (GetThingTemplate(ai) != aitpl) || (GetSectorFlags(sec0) & 0x40))
		{
		target = -1;
		if(ai > -1)
			{
			StopThing(ai);
			ReleaseThing(ai);
			DestroyThing(ai);
			}
		if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
		CaptureThing(ai);
		incarnation = incarnation + 1;
		SetThingUserData(ai, team);
		hp = hitpoints;
		shotcount = 0;
		hitcount = 0;

		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);

		statflags = 0x3;
		aiflags = 0x2;

		if(BitTest(controlflags, 0x1))
			{
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			curframe = 0;
			SkipToFrame(seeker, 0, seekspeed);
			}

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:failsafe reset");
				jkStringOutput(-1, -1);
				}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;

# ==================================================  ======================================

damaged:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# DAMAGED
	# __________________________________________________  _______________________________
	#
	# Only take damage if the the AI is alive and it's damaged by an oppnet. If the damage
	# fit the type mask, apply the damage modifier. Reduce the hitpoints by final result.
	# __________________________________________________  _______________________________


	if(!BitTest(statflags, 0x1)) return;
	killer = GetThingParent(GetSourceRef());
	if((killer == -1) || ((GetThingType(killer) == 10 && GetPlayerTeam(killer) == team) || (GetThingType(killer) == 2 && GetThingUserData(killer) == team))) return;

	damage_type = GetParam(1);
	damage_amount = GetParam(0);

	if(BitTest(damage_type, 0x15)) damage_amout = damage_amount * damage_modifier;

	hp = hp - damage_amount;

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# DAMAGED: HURT
	# __________________________________________________  _______________________________
	#
	# If the AI is still alive after taking damage, send a trigger to do the hurt effects,
	# and if possibly switch targets to the player damaging it (so if it's not vulnerable
	# to others player while pursuing a target).  Always select a target if it has none.
	# __________________________________________________  _______________________________


	if(hp > 0)
		{
		targetswitch = damage_amount + (rand() * 80);
		if((targetswitch > 100) || (!BitTest(statflags, 0x4)) && ((GetThingType(killer) == 10 && GetPlayerTeam(killer) != team) || (GetThingType(killer) == 2 && GetThingUserData(killer) != team)))
			{
			if(BitTest(controlflags, 0x1)) StopThing(seeker);
			target = killer;
			movetarget = killer;
			LOScountdown = 8;

			if(BitTest(aiflags, 0x2)) aiflags = BitClear(aiflags, 0x2);
			if(!BitTest(aiflags, 0x8)) aiflags = BitSet(aiflags, 0x8);
			if(!BitTest(aiflags, 0x20)) aiflags = BitSet(aiflags, 0x20);
			if(BitTest(aiflags, 0x80)) aiflags = BitClear(aiflags, 0x80);
			if(!BitTest(statflags, 0x4)) statflags = BitSet(statflags, 0x4);

			KillTimerEx(4);
			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);
			}

		hitcount = hitcount + 1;

		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);
		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);
		SendTrigger(-1, trig, 5, GetGuidThing(ai), hitcount, 0);

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:damaged - hurt");
			jkStringOutput(-1, -1);
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# DAMAGED: DEATH
	# __________________________________________________  _______________________________
	#
	# If the AI's hitpoints have been reduced to zero, set the flags so as to prevent
	# the AI from doing anything, send the trigger to do the death effects, add a point
	# and a kill to the player who fragged it, kill the pulse, and set the respawn timer. 
	# __________________________________________________  _______________________________

	if(hp <= 0)
		{
		if(BitTest(controlflags, 0x1)) StopThing(seeker);
		target = -1;
		ReleaseThing(ai);
		movetarget = aipos;
		statflags = 0x0;
		aiflags = 0x1;
		SetPulse(0);
		KillTimerEx(4);
		CreateThing(drop1, ai);
		CreateThing(drop2, ai);
		SetPlayerScore(killer, GetPlayerScore(killer) + killpnts);
		SetPlayerKills(killer, GetPlayerKills(killer) + killpnts);
		SyncScores();

		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 6, GetGuidThing(ai), incarnation, 0);

		if(lives == 1)
			{
			SetPulse(0);
			aiflags = 0x0;
			statflags = 0x0;
			controlflags = 0x0;
			}
		if(lives != 1)
			{
			SetTimerEx(respawndelay, 7, 0.0, 0.0);
			if(lives > 1) lives = lives - 1;
			}

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:damaged - death");
			jkStringOutput(-1, -1);
			}
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;



# ==================================================  ======================================

timer:

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# TIMER: FIRE WEAPON
	# __________________________________________________  _______________________________
	#
	# Check that the AI is alive, has a target, has ammo, and is attacking.
	# Lead the target a little, don't aim right at it, but a little in front of it.
	# Create a Projectile and send a trigger to do the fire effects. Clear the flags
	# after short delay so the AI can move again.
	# __________________________________________________  _______________________________


	if(GetSenderID() == 4)
		{
		if(BitTest(aiflags, 0x8))
			{
			if((ammo == 0 || shotcount < ammo) && ((VectorDist(GetThingPos(ai), GetThingPos(target)) > minrange) && (VectorDist(GetThingPos(ai), GetThingPos(target)) < maxrange) && (HasLOS(ai, target))))
				{
				StopThing(ai);
				firepos = VectorAdd(GetThingPos(target), VectorScale(GetThingVel(target), lead));
				firevec = VectorNorm(VectorSub(firepos, GetThingPos(ai)));
				SetThingLook(ai, firevec);
				shotcount = shotcount + 1;

				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 0);
				}
			if((VectorDist(GetThingPos(ai), GetThingPos(target)) < punchrange && HasLOS(ai, target)) && BitTest(controlflags, 0x10))
				{
				StopThing(ai);
				firepos = VectorAdd(GetThingPos(target), VectorScale(GetThingVel(target), lead));
				firevec = VectorNorm(VectorSub(firepos, GetThingPos(ai)));
				SetThingLook(ai, firevec);
				shotcount = shotcount + 1;

				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				SendTrigger(-1, trig, 4, GetGuidThing(ai), shotcount, 1);
				}

			SetTimerEx(minfirerate + (rand() * (maxfirerate - minfirerate)), 4, 0.0, 0.0);

			if(debug)
				{
				jkStringClear();
				jkStringConcatInt(trig);
				jkStringConcatAsciiString("s:fire");
				jkStringOutput(-1, -1);
				}
			}
		return;
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	# __________________________________________________  _______________________________
	#
	# TIMER: RESPAWN
	# __________________________________________________  _______________________________
	#
	# Release and destroy the dead AI thing, create and capture a new one.
	# Set all the numbers, and restart the pulse.
	# __________________________________________________  _______________________________


	if(GetSenderID() == 7)
		{
		if(ai != -1) DestroyThing(ai);
		if(!IsMulti() || IsServer()) ai = GetGuidThing(CreateThing(aitpl, aipos));
		CaptureThing(ai);
		SetThingUserData(ai, team);
		incarnation = incarnation + 1;
		hp = hitpoints;
		shotcount = 0;
		hitcount = 0;
		statflags = 0x3;
		aiflags = 0x2;
		SetPulse(.5);

		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);
		SendTrigger(-1, trig, 1, GetGuidThing(ai), incarnation, 0);

		if(BitTest(controlflags, 0x1))
			{
			movetarget = seeker;
			aiflags = BitSet(aiflags, 0x80);
			TeleportThing(seeker, aipos);
			curframe = 0;
			SkipToFrame(seeker, 0, seekspeed);
			}

		if(debug)
			{
			jkStringClear();
			jkStringConcatInt(trig);
			jkStringConcatAsciiString("s:respawn");
			jkStringOutput(-1, -1);
			}

		return;
		}

	# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #

	return;


activated:

	# __________________________________________________  _______________________________
	#
	# ACTIVATION
	# __________________________________________________  _______________________________
	#
	# If a player on the AI's team activates it with the spacebar, follow that character
	# until activated again or until it finds a target.
	# __________________________________________________  _______________________________

	if(GetSenderRef() == ai)
		{
		player = GetSourceRef();
		if((GetPlayerTeam(player) == team) && (BitTest(aiflags, 0x2)))
			{
			if(!BitTest(aiflags, 0x80))
				{
				movetarget = player;
				aiflags = BitSet(aiflags, 0x80);

				if(debug)
					{
					jkStringClear();
					jkStringConcatInt(trig);
					jkStringConcatAsciiString("s:follow");
					jkStringOutput(-1, -1);
					}
				return;
				}
			if(BitTest(aiflags, 0x80))
				{
				movetarget = -1;
				aiflags = BitClear(aiflags, 0x80);

				if(debug)
					{
					jkStringClear();
					jkStringConcatInt(trig);
					jkStringConcatAsciiString("s:stop");
					jkStringOutput(-1, -1);
					}
				return;
				}
			}
		}
	return;

# ==================================================  ======================================

end

that should do it for a switch. I also made it play a sound. If you have any problems pm me or my email is invader_scoog@yahoo.com.
2005-09-20, 7:52 PM #3
My answers:
1. Do not use the AI sync cogs that you posted. Instead, use your "simple cog script to 'spawn' an unsynced rancor (with a switch)".

If the simple cog is the rancor's actor cog (i.e. the cog is listed in the rancor's template), then insert this snippet (from the actor cogs in Shred's JK Co-op project) to it:
Code:
created:
	the_actor = GetSenderRef(); 
	SetPulse(3.0);
	return;

pulse:
	//sync over multi
	SyncThingPos(the_actor);
	return;

If your simple cog is not an actor cog, then insert
Code:
SetPulse(3.0);
after the CreateThing line, and insert the pulse handler shown above.

2. Use the same method as described in first answer, but use the corresponding template for the actor.


:)
2005-09-20, 11:25 PM #4
Ok, thanks Reid. Is that a modified version of my server cog? Or is it an entirely new one? Also, don't I need a client version too?

Thanks, Zeq. Are you saying that is all the code I need to sync AI? I'll show you what I mean with a 'simple cogscript to spawn a rancor', it's an itemspawner that gbk wrote for me (with a slight modification by Darthslaw):

Code:
# 05/2004, 08/2004 gbk - 03/2005 darth slaw
Symbols

Message		Activated
Surface		Toucheee
Thing		Touchee
Thing		Ghost
Template	Item
int		wait=0		local
flex		delay=3.0

End
Code

Activated:
	if(wait) return;
	wait = 1;
	Creatething(Item, Ghost);
	Sleep(delay);
	wait = 0;
Stop;
End


It has a sleep command, I'm guessing that's going to be a problem. The sleep line is there to prevent people from spawning multiples by tapping the switch all the time.

Should it look like this?

Code:
# 05/2004, 08/2004 gbk - 03/2005 darth slaw - 09/2005 ZeqMacaw
Symbols

Message		Activated
Surface		Toucheee
Thing		Touchee
Thing		Ghost
Template	Item
int		wait=0		local
flex		delay=3.0

End
Code

Activated:
	if(wait) return;
	wait = 1;
	Creatething(Item, Ghost);
	Sleep(delay);
	wait = 0;
	SetPulse(3.0);
created:
	the_actor = GetSenderRef(); 
	SetPulse(3.0);
	return;
pulse:
	//sync over multi
	SyncThingPos(the_actor);
	return;
Stop;
End


It seems to be working, but I'm guessing something isn't right....
Also, I haven't tested if it's synced yet.
ORJ / My Level: ORJ Temple Tournament I
2005-09-21, 3:27 PM #5
yeah, it's a modified version of the original, I added that so you know.
where did the "Stop;" part enter the cog, I'm not sure if Jedi Knight will recognize that(I'm probably wrong). I know theres different methods of doing it. And by God there is no way the ai can be synced with that short a cog.
2005-09-21, 5:31 PM #6
AFAIR, `Created` only gets called in a class cog

Originally posted by Reid:
...where did the "Stop;" part enter the cog, I'm not sure if Jedi Knight will recognize that(I'm probably wrong)....


Stop is the same as Return. I used it in all my cogs...which is odd, because I dont remember writing that cog... :o
And when the moment is right, I'm gonna fly a kite.
2005-09-21, 6:11 PM #7
You wrote it last year. I think the topic got lost when the forums changed... I asked for an 'item-conjuring cog' and you came up with this. Slaw added the sleep command later on. I inserted some stuff that Zeq suggested in the last version that's posted here. I think I didn't do it right, though. That, or the method isn't working. I tested it in multiplayer, and the ai is unsynced and dies only when the host kills it. =\
ORJ / My Level: ORJ Temple Tournament I
2005-09-22, 5:51 PM #8
thank you gbk for informing me of that
2005-09-23, 7:36 AM #9
Anyone else have a solution? I think I will use the cog that Reid posted, to spawn it with a switch... but I still don't understand how to modify the templates to my needs. (Is that even what I should do? - I'm not sure)
ORJ / My Level: ORJ Temple Tournament I
2005-09-23, 4:05 PM #10
Did it work with no problems?

↑ Up to the top!