Author Topic: Smart Turrets  (Read 9930 times)

==Troy==

  • Posts: 440
  • Turrets: +65/-67
Smart Turrets
« on: August 29, 2008, 07:29:07 pm »
Another little gadged from me. If humans built DefComp, the turrets start to record where the aliens are coming from, and turn to that direction if they are idle. A little bit of mess with the turret yaw/pitch code, since otherwise it went crazy. Again, diff from latest Lakitu.

Code: [Select]
Index: src/game/g_buildable.c
===================================================================
--- src/game/g_buildable.c (revision 90)
+++ src/game/g_buildable.c (working copy)
@@ -2121,9 +2121,232 @@
   return qtrue;
 }
 
+float turret_trunc( float val )
+{
+if (val >= 0)
+  return (float)((int)(val));
+else
+  return (float)((int)(val - 1));
+}
+void TurretVectorCorrect( vec3_t vec )
+{
+vec[ YAW ] -= (float)(turret_trunc(vec[ YAW ]/360.0f)*360);
+vec[ PITCH ] -= (float)((turret_trunc(vec[ PITCH ]/360.0f))*360 + 360);
+}
 
 /*
 ================
+HMGTurret_StoreIncomingDirection
+
+Used by HMGTurret_Think to store the direction from which the target came from
+after the target is destroyed/OOR  the turret will turn back there
+================
+*/
+
+qboolean HMGTurret_StoreIncomingDirection ( gentity_t *self, gentity_t *target )  //mainly the code is copied from the turret aiming function but instead we store the initial angle
+{
+  vec3_t  dirToTarget, dttAdjusted, angleToTarget, angularDiff, xNormal;
+  vec3_t  refNormal = { 0.0f, 0.0f, 1.0f };
+  float   rotAngle;
+  int i;

+  if (!target) return qfalse;
+
+  VectorSubtract( target->s.pos.trBase, self->s.pos.trBase, dirToTarget );
+
+  VectorNormalize( dirToTarget );
+
+  CrossProduct( self->s.origin2, refNormal, xNormal );
+  VectorNormalize( xNormal );
+  rotAngle = RAD2DEG( acos( DotProduct( self->s.origin2, refNormal ) ) );
+  RotatePointAroundVector( dttAdjusted, xNormal, dirToTarget, rotAngle );
+
+  vectoangles( dttAdjusted, angleToTarget );

+

+   if ( self->totalAngles == -1)
+   {
+        self->totalAngles++;
+    VectorCopy( angleToTarget, self->angleMem[self->totalAngles] );
+    TurretVectorCorrect(self->angleMem[self->totalAngles]);
+    self->angleFrequency[self->totalAngles] += 100;
+    //trap_SendServerCommand( -1, va( "print \"Initialised\n\""));
+    return qtrue;
+   }
+  
+   for (i = 0; i <= self->totalAngles; i++)
+   {
+     angularDiff[ PITCH ] = AngleSubtract( self->angleMem[i][ PITCH ], angleToTarget[ PITCH ] );
+     angularDiff[ YAW ] = AngleSubtract( self->angleMem[i][ YAW ], angleToTarget[ YAW ] );
+     if (fabs(angularDiff[ YAW ]) < MAX_ANGLE_TOLERANCE && fabs(angularDiff[ PITCH ]) < MAX_ANGLE_TOLERANCE) // add-on the value, averaging out the direction
+     {
+   int j;
+      self->angleMem[i][ YAW ] -= angularDiff[ YAW ]/2.0f;
+ self->angleMem[i][ PITCH ] -= angularDiff[ PITCH ]/2.0f;
+ TurretVectorCorrect(self->angleMem[i]);
+ self->angleFrequency[i] += 100;
+   for (j = 0; j <= self->totalAngles; j++) // scale down the latter frequencies
+   {
+ if (j == i) continue;
+     self->angleFrequency[j] *= 0.8;
+   }
+   //trap_SendServerCommand( -1, va( "print \"Value added\n\""));
+   return qtrue;
+     }
+    }
+
+
+      if (self->totalAngles == MAX_ANGLE_MEM - 1)
+   {
+    int j, k = 0;
+    for(j = 0; j < MAX_ANGLE_MEM - 1; j++) // find the least frequent stored direction and overwrite it
+    {
+      if (self->angleFrequency[k] > self->angleFrequency[j]) k = j;
+    }
+    
+    VectorCopy( angleToTarget, self->angleMem[k] );
+    self->angleFrequency[k] += 100;
+    TurretVectorCorrect(self->angleMem[k]);
+    //trap_SendServerCommand( -1, va( "print \"Direction replaced\n\""));
+    return qtrue;
+   }
+   else // add a new direction
+   {
+    self->totalAngles++;
+    VectorCopy( angleToTarget, self->angleMem[self->totalAngles] );
+    self->angleFrequency[self->totalAngles] += 100;
+    TurretVectorCorrect(self->angleMem[self->totalAngles]);
+    //trap_SendServerCommand( -1, va( "print \"Direction created\n\""));
+    return qtrue;
+   }
+

+
+  return qfalse;
+}
+
+
+
+/*
+================
+HMGTurret_ReturnDirection
+
+Used by HMGTurret_Think to return the aiming directions to the recorded state
+================
+*/
+
+
+void HMGTurret_ReturnDirection( gentity_t *self )
+{
+  vec3_t  angleToTarget, angularDiff;
+  float   temp;
+  float   accuracyTolerance, angularSpeed;
+
+
+ if (self->totalAngles < 0) return;
+
+ if (self->returnedTo >= 0 || self->returning < 0)
+ {
+   int i, j = 0, m = 0;
+   float k;
+  
+   if ( self->totalAngles == 0 )
+   {
+     if ( self->returnedTo != 0 ) self->returning = 0;
+      }
+   else
+   {
+     int seed;
+ seed = trap_Milliseconds();
+ if (self->returnedTo >=0 && self->idleTimeStamp > level.time) return; // we didnt want to change direction this time
+     k = Q_random(&seed);
+     for (i = 0; i <= self->totalAngles; i++)
+     {
+       j += self->angleFrequency[i];
+     }
+ for (i = 0; i <= self->totalAngles; i++)
+     {
+   m += self->angleFrequency[i];
+       if( k <= (float)(m)/(float)(j) )
+   {
+ if ( i == self->returnedTo ) return;
+ //trap_SendServerCommand( -1, va( "print \"Chosen the new Direction, %i\n\"", i));
+     self->returning = i;
+ self->returnedTo = -1;
+ break;
+   }
+     }
+   }
+ }
+ else
+ {
+     if( self->lev1Grabbed )
+     {
+       //can't turn fast if grabbed
+       accuracyTolerance = MGTURRET_GRAB_ACCURACYTOLERANCE;
+       angularSpeed = MGTURRET_GRAB_ANGULARSPEED;
+     }
+     else if( self->dcced )
+     {
+       accuracyTolerance = MGTURRET_DCC_ACCURACYTOLERANCE;
+       angularSpeed = MGTURRET_DCC_ANGULARSPEED;
+     }
+     else
+     {
+       accuracyTolerance = MGTURRET_ACCURACYTOLERANCE;
+       angularSpeed = MGTURRET_ANGULARSPEED;
+     }
+   VectorCopy(self->angleMem[self->returning], angleToTarget);
+       angularDiff[ PITCH ] = AngleSubtract( self->s.angles2[ PITCH ], angleToTarget[ PITCH ] );
+          angularDiff[ YAW ] = AngleSubtract( self->s.angles2[ YAW ], angleToTarget[ YAW ] );
+  
+           //if not pointing at our target then move accordingly
+           if( angularDiff[ PITCH ] < (-accuracyTolerance) )
+             self->s.angles2[ PITCH ] += angularSpeed;
+           else if( angularDiff[ PITCH ] > accuracyTolerance )
+             self->s.angles2[ PITCH ] -= angularSpeed;
+           else
+             self->s.angles2[ PITCH ] = angleToTarget[ PITCH ];
+         
+           //disallow vertical movement past a certain limit
+           temp = fabs( self->s.angles2[ PITCH ] );
+           if( temp > 180 )
+             temp -= 360;
+
+           if( temp < -MGTURRET_VERTICALCAP )
+             self->s.angles2[ PITCH ] = (-360) + MGTURRET_VERTICALCAP;
+         
+           //if not pointing at our target then move accordingly
+           if( angularDiff[ YAW ] < (-accuracyTolerance) )
+             self->s.angles2[ YAW ] += angularSpeed;
+           else if( angularDiff[ YAW ] > accuracyTolerance )
+             self->s.angles2[ YAW ] -= angularSpeed;
+           else
+             self->s.angles2[ YAW ] = angleToTarget[ YAW ];
+
+   if ( self->s.angles2[ YAW ] == angleToTarget[ YAW ] && (self->s.angles2[ PITCH ] == angleToTarget[ PITCH ] || self->s.angles2[ PITCH ] == (-360) + MGTURRET_VERTICALCAP))
+   {
+     int seed;
+ seed = trap_Milliseconds();
+ //trap_SendServerCommand( -1, va( "print \"Reached direction, returnedTo : %i\n\"", self->returning));
+     self->returnedTo = self->returning;
+     self->returning = -1;
+ self->idleTimeStamp = level.time + MIN_IDLE_STAMP + (int)(3*MIN_IDLE_STAMP*Q_random(&seed));
+ return;
+   }
+  
+  
+ }
+
+
+}
+
+
+
+
+/*
+================
 HMGTurret_FindEnemy
 
 Used by HMGTurret_Think to locate enemy gentities
@@ -2135,7 +2358,7 @@
   vec3_t    range;
   vec3_t    mins, maxs;
   int       i, num;
-  gentity_t *target;
+  gentity_t *target, *mate;
 
   VectorSet( range, MGTURRET_RANGE, MGTURRET_RANGE, MGTURRET_RANGE );
   VectorAdd( self->s.origin, range, maxs );
@@ -2155,6 +2378,19 @@
 
       //we found a target
       self->enemy = target;
+   if (self->dcced && !target->recorded)
+   { 
+    int j;
+     target->recorded = HMGTurret_StoreIncomingDirection( self, target );
+ for (j = 0; j < num; j++)
+ {
+   mate = &g_entities[ entityList[ j ] ];
+   if ( mate != self && mate->s.eType == ET_BUILDABLE && mate->s.modelindex == BA_H_MGTURRET )
+   {
+     HMGTurret_StoreIncomingDirection( mate, target );
+   }  
+ }
+   }
       return;
     }
   }
@@ -2174,6 +2410,19 @@
 
         //we found a target
         self->enemy = target;
+     if (self->dcced && !target->recorded)
+     { 
+      int j;
+       target->recorded = HMGTurret_StoreIncomingDirection( self, target );
+     for (j = 0; j < num; j++)
+   {
+     mate = &g_entities[ entityList[ j ] ];
+     if ( mate != self && mate->s.eType == ET_BUILDABLE && mate->s.modelindex == BA_H_MGTURRET )
+     {
+       HMGTurret_StoreIncomingDirection( mate, target );
+     }  
+   }
+     }
         return;
       }
     }
@@ -2217,14 +2466,20 @@
     {
       if( self->enemy )
         self->enemy->targeted = NULL;
-
       HMGTurret_FindEnemy( self );
     }
 
     //if a new target cannot be found don't do anything
     if( !self->enemy )
-      return;
-
+ {
+   if( self->dcced ) HMGTurret_ReturnDirection( self );
+   return;
+    }
+ else
+ {
+   self->returnedTo = -1;
+ }
+
     self->enemy->targeted = self;
 
     //if we are pointing at our target and we can fire shoot it
@@ -2236,7 +2491,6 @@
       self->s.eFlags |= EF_FIRING;
       G_AddEvent( self, EV_FIRE_WEAPON, 0 );
       G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
-
       self->count = level.time + firespeed;
     }
   }
@@ -3238,6 +3492,10 @@
   built->spawned = qfalse;
   built->buildTime = built->s.time = level.time;
   built->spawnBlockTime = 0;

+  built->totalAngles = -1;
+  built->returning = -1;
+  built->returnedTo = -1;
 
   // build instantly in cheat mode
   if( builder->client && g_cheats.integer )
@@ -3588,6 +3846,9 @@
 
   built->takedamage = qtrue;
   built->spawned = qtrue; //map entities are already spawned
+  built->totalAngles = -1;
+  built->returning = -1;
+  built->returnedTo = -1;
   built->health = BG_FindHealthForBuildable( buildable );
   built->s.generic1 |= B_SPAWNED_TOGGLEBIT;
 
@@ -3916,6 +4177,9 @@
 
   built->takedamage = qtrue;
   built->spawned = qtrue; //map entities are already spawned
+  built->totalAngles = -1;
+  built->returning = -1;
+  built->returnedTo = -1;
   built->health = BG_FindHealthForBuildable( buildable );
   built->s.generic1 |= B_SPAWNED_TOGGLEBIT;
 
Index: src/game/g_client.c
===================================================================
--- src/game/g_client.c (revision 90)
+++ src/game/g_client.c (working copy)
@@ -1596,7 +1596,7 @@
   ent->waterlevel = 0;
   ent->watertype = 0;
   ent->flags = 0;
-
+  ent->recorded = qfalse;
   //TA: calculate each client's acceleration
   ent->evaluateAcceleration = qtrue;
 
Index: src/game/g_local.h
===================================================================
--- src/game/g_local.h (revision 90)
+++ src/game/g_local.h (working copy)
@@ -243,6 +243,17 @@
   int               lastDamageTime;
   
   int               bdnumb; // buildlog entry ID

+  #define MAX_ANGLE_MEM 16
+  #define MAX_ANGLE_TOLERANCE  45.0f
+  #define MIN_IDLE_STAMP  5000
+  vec3_t angleMem[ MAX_ANGLE_MEM];
+  int angleFrequency[ MAX_ANGLE_MEM ];
+  int totalAngles;
+  qboolean recorded;
+  int returning;
+  int returnedTo;
+  int idleTimeStamp;
 };
 
 typedef enum


Edit : forgot to say, in the case if the turret will find more than 2 distinct direction (from where the aliens are coming) it will "scan" both of them, turning one way or another at random time periods.
« Last Edit: September 07, 2008, 08:20:00 pm by ==Troy== »

seeeker

  • Posts: 122
  • Turrets: +19/-5
Re: Smart Turrets
« Reply #1 on: August 29, 2008, 10:40:43 pm »
Ehhh sounds nice!

TRaK

  • Posts: 442
  • Turrets: +94/-21
    • TRaK@MG
Re: Smart Turrets
« Reply #2 on: August 29, 2008, 10:50:39 pm »
Interesting feature, it would definitely make the defcomp more useful if the implementation is good :)

techhead

  • Posts: 1496
  • Turrets: +77/-73
    • My (Virtually) Infinite Source of Knowledge (and Trivia)
Re: Smart Turrets
« Reply #3 on: September 01, 2008, 02:50:30 am »
I hope Norfenstein hears about this.
I'm playing Tremulous on a Mac!
MGDev fan-club member
Techhead||TH
/"/""\"\
\"\""/"/
\\:.V.://
Copy and paste Granger into your signature!

Hendrich

  • Posts: 898
  • Turrets: +168/-149
    • TremCommands
Re: Smart Turrets
« Reply #4 on: September 04, 2008, 01:49:30 am »
Kickass.

rotacak

  • Posts: 761
  • Turrets: +39/-64
Re: Smart Turrets
« Reply #5 on: September 04, 2008, 01:50:51 pm »
I think this fixing that what should not be fixed. If turret will be allways looking at entrance, what dretch should do? Now is possible that another alien attack enemy base and turned turrets somewhere else. Then dretch can run in base too, because turrets looking somewhere else.
Or even dretch can make one attack and turn turrets to left side and die. Then make second attack and can run by right side into base before turrets turn to him.
But with this will be dretch allways dead whatever he do.

+ OPTIMUS +

  • Posts: 1098
  • Turrets: +263/-164
Re: Smart Turrets
« Reply #6 on: September 04, 2008, 01:56:59 pm »
Too Smart Turrets... :-)
success is the ability to go from failure to failure without losing your enthusiasm

+PICS+

==Troy==

  • Posts: 440
  • Turrets: +65/-67
Re: Smart Turrets
« Reply #7 on: September 04, 2008, 02:06:04 pm »
rotacak. This was an interesting thing to code, thats all that matters. Its up to server admins to decide whether they want it or not. And in any way you are required to have DC for them to work.

Ghosthree3

  • Posts: 22
  • Turrets: +0/-0
Re: Smart Turrets
« Reply #8 on: September 06, 2008, 04:21:15 am »
I applied the patch, it failed for two hunks I got two files, "g_buildable.c.rej" and "g_local.h.rej". In "g_buildable.c.rej" I get,
Code: [Select]
***************
*** 3238,3243 ****
    built->spawned = qfalse;
    built->buildTime = built->s.time = level.time;
    built->spawnBlockTime = 0;
 
    // build instantly in cheat mode
    if( builder->client && g_cheats.integer )
--- 3492,3501 ----
    built->spawned = qfalse;
    built->buildTime = built->s.time = level.time;
    built->spawnBlockTime = 0;
+   
+   built->totalAngles = -1;
+   built->returning = -1;
+   built->returnedTo = -1;
 
    // build instantly in cheat mode
    if( builder->client && g_cheats.integer )
In "g_local.h.rej" I get,
Code: [Select]
***************
*** 243,248 ****
    int               lastDamageTime;
   
    int               bdnumb; // buildlog entry ID
  };
 
 
--- 243,259 ----
    int               lastDamageTime;
   
    int               bdnumb; // buildlog entry ID
+   
+   #define MAX_ANGLE_MEM 16
+   #define MAX_ANGLE_TOLERANCE  45.0f
+   #define MIN_IDLE_STAMP  5000
+   vec3_t angleMem[ MAX_ANGLE_MEM];
+   int angleFrequency[ MAX_ANGLE_MEM ];
+   int totalAngles;
+   qboolean recorded;
+   int returning;
+   int returnedTo;
+   int idleTimeStamp;
  };
 
 

I tried applying them manually, it still didn't seem to work, or maybe I'm not sure what I was meant to be looking for.
« Last Edit: September 06, 2008, 01:07:49 pm by Ghosthree3 »

==Troy==

  • Posts: 440
  • Turrets: +65/-67
Re: Smart Turrets
« Reply #9 on: September 06, 2008, 01:24:32 pm »
(Thorn, I hate when you are being right)

Did you try to patch revision 90? (it can be seen from the patch itself).

Ghosthree3

  • Posts: 22
  • Turrets: +0/-0
Re: Smart Turrets
« Reply #10 on: September 07, 2008, 12:25:07 am »
Nope...woops, I patched rev 895, I tried to get 90 though, it wouldn't download.

==Troy==

  • Posts: 440
  • Turrets: +65/-67
Re: Smart Turrets
« Reply #11 on: September 07, 2008, 12:33:28 am »
Ah, well, you see, khm... I was making a diff from commonly used Lakitu qvm (its just easier for me to maintain and test). So you would want to use this svn : http://source.mercenariesguild.net/lakitu7-qvm/