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:
Client:
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...
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