ORJ_JoS
04-09-2005, 12:52 PM
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 [HR] helped us modifying the cog below to what is shown here, and it works.
However, once you activate the switch, the remotes keep respawning. =\
It's only supposed to spawn when one uses the switch ofcourse.
Anyone have any idea how to fix this?
# 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(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;
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
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 [HR] helped us modifying the cog below to what is shown here, and it works.
However, once you activate the switch, the remotes keep respawning. =\
It's only supposed to spawn when one uses the switch ofcourse.
Anyone have any idea how to fix this?
# 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(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;
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