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 → Synced Remotes (with a switch) - A more challenging problem
Synced Remotes (with a switch) - A more challenging problem
2005-04-09, 9:52 AM #1
Greetings fellow massassians!

I know this is my third request in a row, but I think the other two weren't that challenging.
This one however....

G-Man and I are trying to spawn synced remotes with a switch.
HellRaiser
Code:
# Jedi Knight Cog Script
#
# ais_basic_svr.cog
#
# Basic Synced AI: MOTS Version
#
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# AIS Beta .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.
# JK Version.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# 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 control flags that can be use o control the behaviour of an AI, making seeking, punching,
# and sentry AI's, as well as AI's that initialize when an ajoin is crossed instead of at startup.
# Also 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 ReeYees on team 5, all stormtroopers on team
# 6, that way ReeYees can target ST's and vice versa, but ST's won't attack each other
# and ReeYees 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 (use only with patrol flag enabled)
#	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:
#
#		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
#		controlflags = 16
#		standoffdist = 0.5
#		arriveddist = 1.0
#		minfirerate = 0.25
#		maxfirerate = 1.25
#		drop1 = +drepeatergun
#		drop2 = +dpowercell
#		aitpl = ais_st3
#		trig = whatever you set the server cog's trig to
#		firesnd = repeat-1.wav
#		weapon = +repeaterball
#
#	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 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

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

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

sound		firesnd=pistol-1.wav
sound		punchsnd=swingfist01.wav
sound		HurtSnd=i00s127z.wav
sound		DieSnd=i00s129z.wav

template	weapon=+ais_laser
template	punch=+ais_punch

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

surface		SurfActivate

end

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

code

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

spawnAI:

	# _________________________________________________________________________________
	#
	# 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 = 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;
	gunoffset = GetThingFireOffSet(ai);

	SetThingVel(ai, '0 .2 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 SpawnAI;
	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 = sightdist + 1;
		victim = FirstThingInView(ai, 225, 5.5, 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 = VectorDist(GetThingPos(ai), GetThingPos(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;
		statflags = 0x3;
		aiflags = 0x2;

		SetThingVel(ai, '0 .2 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: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;

		PlayKey(ai, HurtKey, 2, 32);
		PlaySoundThing(HurtSnd, ai, 1.0, -1, -1, 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();

		SetThingMass(ai, 0);
		SetThingFlags(ai, 0x200);
		StopThing(ai);
		PlayKey(ai, DieKey, 1, 32);
		PlaySoundThing(DieSnd, ai, 1.0, -1, -1, 0);
		SetTimerEx(GetKeyLen(DieKey), 10, 0.0, 0.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;

				FireProjectile(ai, weapon, firesnd, 8, gunoffset, '0 0 0', 1.0, 0x20, 25, .75);
				PlayKey(ai, FireKey, 2, 32);
				}
			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;

				FireProjectile(ai, punch, punchsnd, 8, gunoffset, '0 0 0', 1.0, 0x20, 25, .75);
				PlayKey(ai, punchKey, 2, 32);
				}

			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);

		SetThingVel(ai, '0 .2 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);

		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;
		}

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

	if(GetSenderID() == 10)
		{
		DestroyThing(ai);
		}

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

	return;


activated:

	if((GetSenderType() == 6) && (GetSenderRef() == SurfActivate))
	{
		controlflags = BitClear(controlflags, 0x8);
		call SpawnAI;
		return;
	}

	# _________________________________________________________________________________
	#
	# 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

ORJ / My Level: ORJ Temple Tournament I
2005-04-12, 3:35 PM #2
I think it's something with the respawn timer... or am I stating the obvious? It's a lot of code... lol
ORJ / My Level: ORJ Temple Tournament I
2005-04-12, 4:51 PM #3
Could you include a small test level, otherwise I'll have to try to walk you through debugging it.
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"
2005-04-12, 5:17 PM #4
Yeah, G-Man made a test level for it. I'll upload it to my site and let you know. :)
ORJ / My Level: ORJ Temple Tournament I
2005-04-12, 5:23 PM #5
Remotetest

Thanks for taking a look :)
ORJ / My Level: ORJ Temple Tournament I
2005-04-12, 9:51 PM #6
Fixed.

Actually it wasn't a cog problem. The problem was that you set the cogs health for the remote as 250, but the remote template only had 30 health. When it kept dying before the cog thought it should, it got borked. You can fix this by making a new remote template with 9999 health. Then the cog controls how much health the remote has.
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"
2005-04-13, 4:58 AM #7
Wow! Thanks! :D

I'd never have figured that out!

But I only know how to make templates for 3do's... not for AI. I'll ask G-Man how to do that.

Could I also fix it by setting the remote's health to 30 in the cog? I think I'll try that first...

Thanks for helping us out :D
ORJ / My Level: ORJ Temple Tournament I
2005-04-13, 4:06 PM #8
Setting the cogs health to 30 or less does fix it.

The template for the remote is the exact same as the template for the remote, except the health variable is now 9999 instead of 30.
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"
2005-04-15, 5:05 AM #9
Ok, it worked in the test level. But now I added the cog to my 'real' level (changed the remote's template too), and now my JK freezes like 10 seconds after I get in game! It doesnt matter if I call the remote or not. Even if I stand still, the level will totally freeze on me :o

What could be causing this?
ORJ / My Level: ORJ Temple Tournament I
2005-04-15, 8:16 PM #10
Did you try removing the cog just to make sure that it is the problem?

I honestly don't know. Since it's just that level, I can't help fix it without knowing what ALL is in it. I doubt you would want to send me the actual level, so I'll try to walk you through debugging it.
First, check the cog like I already said (remove it, if it crashes, then it's not the cog)

Also, did you make a NEW template or change the old one? If you changed the old one, then undo your changes and make a new template.
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"
2005-04-15, 8:56 PM #11
The level worked perfectly before I added the cog. (It's the only thing I changed about the level besides the template of the remote. And yes, I modified the original remote template.

I'll try those two things you said, one after the other.

Thanks :)
ORJ / My Level: ORJ Temple Tournament I
2005-04-16, 8:10 PM #12
Allright, the game stopped crashing once I changed the remote template's health to 999.00 instead of 9999.00

Now there's only one odd detail... If I kill the remote with the saber, it wont respawn. If I kill it with a Force Push, it explodes, but it instantly respawns! =\

And I set the health to 30 in the cog setup. When I used to spawn unsynced remotes, they wouldnt respawn after a Force Push.

there's also one other detail... There should be something in the cog that prevents people from tapping the surface multiple times to spawn a lot of remotes in a short time. That's definitely going to crash my big level in which this preview room will eventually be.... meh.

I'd try a sleep command, if only I knew where to put it...lol. Slaw built it into my item spawning cog which I use in this same level.
ORJ / My Level: ORJ Temple Tournament I

↑ Up to the top!