Game Development Community

Getting Units to Return Fire

by J.C. Smith · in RTS Starter Kit · 04/29/2005 (10:42 am) · 16 replies

I've been fiddling with the RTS kit the last couple days trying to get a good feel for it. One thing I wanted to get implemented was giving units the ability to return fire when they are attacked, instead of standing there braindead. I wasn't able to get that working tonight and figured I'd pose the question before I went to sleep.

It looks like setting the the SetAimTarget will make a unit go into attack mode, but I haven't found a good place to call it. I'll go back to hacking away in the morning but I spent a few hours today trying to do the simple task, and figured I might save some time tomorrow if anyone has already been through these loops.

#1
04/29/2005 (11:14 am)
Add some logic to RTSUnitData::damage so they go to attack mode when damaged by fire..
That will only make them return fire when they're hit...

If you want them to return fire when shots land near them, then it's more work :)
#2
04/29/2005 (3:31 pm)
But not much you should be able to get your projectiles onCollision and add startscan(%col, %obj); and maybe need to switch all instances of %obj with %col and vice versa im not sure.
startscan(%col, %obj)
{
%maxRange = "20";

InitContainerRadiusSearch(%col.getPosition(), %maxRange, $TypeMasks::PlayerObjectType);

while ((%targetObject = containerSearchNext()) != 0)
{
%tagetObject.selection = new SimSet();
%tagetObject.selection.add(%tagetObject); 
%tagetObject.client.sendattackevent(%targetObject.selection, %obj);
}
}

NOTE: This was made on the fly so there might be errors.
#3
04/29/2005 (9:16 pm)
Sorry I didn't give a better description last night, was braindead from programming and my girlfriend was rushing me to get to sleep. I'll go through Robert's code when I get home. The first thing I had tried though was placing a check in the RTSUbitData::damage area, basically I just tried to do a call to getAimObject and if it was -1 then I figured I could setAimTarget from there. But it didn't work and after putting echo commands throughout the code I couldn't ever find an instance where RTSUnitData was actually being executed.

I'll check Robert's code after work. Thanks for the help.
#4
04/30/2005 (12:58 am)
Okay I'm at home now, and still having basically the same problem, and it's probably to do with me not comprehending the way the inner workings of the RTS Starter Kit. Although I've been reading through threads and docs for a couple of days now.

The problem is that the functions RTSUnitData:Damage and CrossbowProjectile::OnCollision never seem to be called at all. I've set up echos at the very start of their functions to try to debug the problem and the echos are never being displayed so the functions are never being called. The only function that is being reliably called for me is the RTSUnitData::onDamage but I can't seem to work out a way to set the aim target given the limited information passed to onDamage.
#5
04/30/2005 (9:07 am)
That's why the damage callback exists, as there you can get the actual id of the client who fired the projectile :)

iirc, RTSKit uses the same mechanism as stock TGE for this, so you would get the ID of a firing player by getting the Player member of the %sourceClient variable, in the damage callback.
Take this with a grain of salt, I'm not that familiar with the RTSKit and anyone more up to date on its inner workings should pipe up if I'm spouting off crazy talk :)

did you make sure you had no typos in the code you added ?
Check you console logs for any errors in getting the modified scripts compiled, otherwise it reverts to using the already compiled .dso
#6
04/30/2005 (11:15 am)
Ok well onCollision never gets called because it never collides, unless you allow them to fire on ground. althought im not sure. So if you want the guy you fire at to return fire and all its surrounding forces. call that same code in your rifleman.cs riflemanBlock::onDamage like this startscan(%this, %obj); I think that is the right spot. Anyway would you prefer a Attack on sight function?
#7
04/30/2005 (11:39 pm)
Attack on sight would actaully be better, but I started with this because I figured it would be easier to implement.
#8
05/01/2005 (8:24 am)
Well I have a fairly good Attack on Sight Function you might be able to use.

function RTSUnit::startscan(%player)
{
%maxRange = "20";

InitContainerRadiusSearch(%player.getPosition(), %maxRange, $TypeMasks::PlayerObjectType);

while ((%targetObject = containerSearchNext()) != 0)
{
//        echo ("Attacker" SPC %player);
//        echo ("Victim" SPC %targetObject);
   if(%targetObject.owner $= %player.owner)
{
     //%player.client.sendAttackEvent(%player.selectionWrapper, %targetObject);
}
else
{
     %player.client.sendAttackEvent(%player.selectionWrapper, %targetObject);
     %player.setAimObject(%targetObject);
}
//   %targetDistance = vectorDist(%this.getTransform(), %targetObject.getTransform());
//   if(%targetDistance < %nearestTarget && %this.checkLOSToObject(%targetObject, $Type
//Masks::TerrainObjectType | $TypeMasks::InteriorObjectType))
//   {
//      %this.attackTargetObject = %targetObject;
//      %nearestTarget = %targetDistance;
//      %targetAquired = true;
//      //echo("target aquired");
//   }
}
        cancel(%player.scanloop);
        %player.scanloop = %player.schedule(200,"startscan", %player); // Maybe change 200 to something higher.. 800?
}
Now just call that function in createplayer or whereever you make your units, call like this %player.startscan();

Anyone see a flaw in this design? Stephan what do you think about this way?
#9
05/01/2005 (1:08 pm)
I don't see much, if anything at all wrong with it! How is performance?

I was a bit concerned about performance on this myself with a lot of units, so I did it in code, but this looks fine.

It looks like you coded a checkLOSToOBject function that you may want to provide if you wish.
#10
05/01/2005 (1:39 pm)
No I dident code a LOS Function it was part of a new AIPlayer code I think, im not using it. Although if you want LOS Function use calcExplosionCoverage(%cameraPoint, %next, %obstructMasks); it appears to do the job, something like this. Just try and merge the two codes toghether or call this first to see if there is anything in the way.
findVisibleObjects(%cameraPoint, %visibleDist, %mask)
{
   InitContainerRadiusSearch(%cameraPoint, %visibleDist, %mask);
   
   %index = 0;
   %next = ContainerSearchNext();

   // object types that could block vision (other masks can be added)
   %obstructMasks = $TypeMasks::InteriorObjectType |  $TypeMasks::TerrainObjectType;

   while(%next != 0)
   {
      //is it within the line of site?
      %coverage = calcExplosionCoverage(%cameraPoint, %next, %obstructMasks);
      
      if(%coverage == 0)
      {
         //get next object
         %next = ContainerSearchNext();
         continue;   //skip unseen object
      }

      //add to return array
      %objects[%index++] = %next;
      
      //get next object
      %next = ContainerSearchNext();
   }

   return %objects;
}

Also Stephan the performance im getting is great, although I have not tested the game on anything other than my 3g P4 (well since I made the Attack on Sight Function)
#11
05/03/2005 (8:30 am)
Sorry I hadn't had time to respond. I did implement the code though and it works great. Thanks a bunch.
#12
05/17/2005 (7:28 pm)
Hi chaps,
I got a slight problem with this locking up the game on both a client and a client hosting the game,
The lockup occurs when the unit is getting removed (after death when it fades out of view)
The locking up is hardly noticable when u send 1 unit to go and die but with more units, it's realy bad
I implemented this with a nearly fresh RTSKit (fixed all the reported bugs, I think??),
anyway, to get it to work on my end I had to change it to the following
function RTSUnit::startscan(%player){
%maxRange = "20";

InitContainerRadiusSearch(%player.getPosition(), %maxRange, $TypeMasks::PlayerObjectType);

while ((%targetObject = containerSearchNext()) != 0){
if(%targetObject.client $= %player.client){
     //%player.client.sendAttackEvent(%player.selectionWrapper, %targetObject);
} else {
    //%player.client.sendAttackEvent(%player.selectionWrapper, %targetObject);
     %player.setAimObject(%targetObject);
}
}
        cancel(%player.scanloop);
        %player.scanloop = %player.schedule(800,"startscan", %player); 
	// Maybe change 200 to something higher.. 800?
	// have tried it around 2500
}
and I added the %player.startscan(); in the function RTSConnection::createPlayer
after %this.units.add(%player);
I did also add a call to cancel the sanloop in RTSUnitData::onDisabled and RTSUnitData::damage
where there are references to the obj State being Dead
on both machines AMD AthlonXP 2.2ghz 1 has 1gb ram with winxp the other 768 mdk10.2
not that i would have thought these machines are too slow

I would appreciate any pointers, to help me solve this
thanx
#13
05/21/2005 (9:59 am)
Sorry i havednt been on much lately, but ill try and read through this and help you fix it soon.
#14
05/21/2005 (7:28 pm)
Hi guys sorry havn't been back with some findings, anyway, I compared the above to the AIGuard resource and although the above is basic, (exactly what is needed at the moment) they are very similer, so I did a test with the AIGuard in TGE with 40 guard bots and 4 real peps online and there was no probs, so I tied to debug it (hehe not that good at debugging c++) anyway
without the above code getting run, I found it still happens when the RTSUnit is getting removed,
result, yep I'm sure it's the vismanager.cc and with the semi fix for it just made it worse
so best whait till the new vismanager is out I guess.
Hope this helps. ;-)
#15
02/07/2010 (4:28 pm)
Think the current release still has the vismanager crash bug ... i also the the units "popping" in/out of view as they rush over the terrain towards my units. Seems silly to worry about whats visible in the current "view screen" ... all units should be visible regardless of vision range. but anyhow- will search for a vismanager fix.

Meanwhile, I've merged/updated the code to work for now -- as follows;
function RTSUnit::startscan(%player,%maxRange)
{   
   if(! isObject(%player))
      return; // do i exist?
      
   if( isObject(%player.getAimObject()) )
      return; // exit if already have an aim object
      
   if (%player.getState() $= "Dead")
      return; // if i am dead    
        
   // ok search
   InitContainerRadiusSearch(%player.getPosition(), %maxRange, $TypeMasks::PlayerObjectType);

   // delay tactic - "cool" down period
   if ((%player.searchDelay < 1) || (%player.searchDelay > 30000))
      %player.searchDelay = 2500; // default 2.5s delay
   else
      %player.searchDelay += 2500; // increase delay each time
      
   echo ("unit-" SPC %player.RTSUnitTypeName SPC "done waiting " SPC (%player.searchDelay/1000) SPC "seconds and is searching for a target");
   
   // init the variables
   %nearestTarget = (%maxRange + 1); // find a target closer than this
   %targetAquired = false;
   %theTargetObject = 0; // final target
   
   while ((%targetObject = containerSearchNext()) != 0)
   {
 //     echo ("Target " SPC %targetObject.getName() SPC "is on team" SPC %targetObject.getTeam() );
     // if(%targetObject.owner $= %player.owner)
      if ( %targetObject.getTeam() $= %player.getTeam() )
      {
           //%player.client.sendAttackEvent(%player.selectionWrapper, %targetObject);
 //          echo(" ... ignoring - same team");           
      }
      else if (%targetObject.getClassName() $= "RTSBuilding")
         {
            // ignore buildings for now
         }
         else
         {          
            // bad guy found - find the closest one
            %targetDistance = vectorDist(%player.getTransform(), %targetObject.getTransform());
   //         if( (%targetDistance < %nearestTarget) && checkLOSToObject(%player.getPosition(), %targetObject, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType))
            if  (%targetDistance < %nearestTarget)
            {
               %nearestTarget = %targetDistance;
               %theTargetObject = %targetObject;
               %targetAquired = true;
               echo("target aquired");
            }
         }      
   }
   
  if (%targetAquired $= true)
  {
//     %player.client.sendAttackEvent(%player.selectionWrapper, %theTargetObject);
//     %player.setAimObject(%theTargetObject);     
     AISetToAttack(%theTargetObject,%player); 
     
     %player.searchDelay=5000; // 60 seconds delay     
  }
  else
  {
     // no target found, wander aimlessly - a short distance ?     
  }

  cancel(%player.scanloop);
  %player.scanloop = %player.schedule(%player.searchDelay,"startscan", %player, %maxRange);  
}
#16
02/07/2010 (8:15 pm)
the vismanager update "released to GG" never seems to have made it into the last release .... the bug is still there, just like Stephen mentions in this other thread;

http://www.torquepowered.com/community/forums/viewthread/23688


... digging for a solution, might just make them all visible - all the time.