Hives can now shoot around corners (or rather, learn to shoot around corners). I am posting this separately, even though it uses the same principle as the above patch, but is not a realy improvement, just a different feature.
In this patch the hives learn to shoot around buildables/corners and the swarm itself follows the player around the corners/buildables, Yet again diff from Lakitu.
Index: src/game/g_buildable.c
===================================================================
--- src/game/g_buildable.c (revision 87)
+++ src/game/g_buildable.c (working copy)
@@ -1106,8 +1106,24 @@
//==================================================================================
+qboolean Hive_CheckWP( vec3_t org1, vec3_t org2, gentity_t *ent )
+{
+ trace_t tr;
+ gentity_t *traceEnt;
+ trap_Trace( &tr, org1, NULL, NULL, org2, ent->s.number, MASK_SHOT );
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ if( tr.entityNum == ENTITYNUM_WORLD || traceEnt->s.eType == ET_BUILDABLE )
+ return qfalse;
+ return qtrue;
+}
+
+
+
+
+
/*
================
AHive_Think
@@ -1120,10 +1136,14 @@
int entityList[ MAX_GENTITIES ];
vec3_t range = { ACIDTUBE_RANGE, ACIDTUBE_RANGE, ACIDTUBE_RANGE };
vec3_t mins, maxs;
- int i, num;
+ int i, num, j, k;
gentity_t *enemy;
vec3_t dirToTarget;
-
+ qboolean visible, fvisible;
+ int TWP = -1, freeTWP = -1, createTWP = -1;
+ qboolean WP1, WP2;
+
+
self->powered = G_IsOvermindBuilt( );
self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex );
@@ -1140,31 +1160,141 @@
if( self->timestamp < level.time )
self->active = qfalse; //nothing has returned in HIVE_REPEAT seconds, forget about it
-
+
+ if( self->RecheckTime < level.time)
+ {
+ self->RecheckTime = level.time + HIVE_RECHECK;
+ ;//trap_SendServerCommand( -1, va( "print \"Rechecking Hive, number of waypoints : %i\n\"", self->TotalWP));
+ for(j = 1; j <= self->TotalWP; j++)
+ {
+ while ( j <= self->TotalWP && (!Hive_CheckWP(self->r.currentOrigin, self->WPA[j-1], self) || (!Hive_CheckWP(self->WPA[j-1], self->WPA2[j-1], self) && self->inUse[j-1])))
+ {
+ for(k = j; k <= self->TotalWP; k++)
+ {
+ VectorCopy(self->WPA[k], self->WPA[k-1]);
+ VectorCopy(self->WPA2[k], self->WPA2[k-1]);
+ self->inUse[k-1] = self->inUse[k];
+ }
+ self->TotalWP--;
+ ;//trap_SendServerCommand( -1, va( "print \"Removed invalid waypoint\n\""));
+ }
+ }
+ }
+
+
if( self->spawned && !self->active && G_FindOvermind( self ) )
{
//do some damage
num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ self->targWP = -1;
for( i = 0; i < num; i++ )
{
enemy = &g_entities[ entityList[ i ] ];
if( enemy->flags & FL_NOTARGET )
- continue;
+ continue;
- if( enemy->health <= 0 )
+ if( enemy->health <= 0 || !enemy->client || enemy->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS)
continue;
+
+ ;//trap_SendServerCommand( -1, va( "print \"Target Detected\n\""));
+ if( !Hive_CheckWP( self->r.currentOrigin, enemy->r.currentOrigin, self ) )
+ {
+ ;//trap_SendServerCommand( -1, va( "print \"no LOS, checking waypoints\n\""));
+ TWP = freeTWP = createTWP = -1;
+ for (j = 1; j <= self->TotalWP; j++)
+ {
+ WP1 = Hive_CheckWP( self->WPA[j-1], enemy->r.currentOrigin, self );
+ if( self->inUse[j-1] )
+ WP2 = Hive_CheckWP( self->WPA2[j-1], enemy->r.currentOrigin, self );
+ else
+ WP2 = qfalse;
+
+ if (!WP1 && WP2) { TWP = -2 - (j-1); break; }
+ if (WP1 && WP2) { TWP = j-1; break; }
+ if (WP1 && !WP2 && self->inUse[j-1] ) createTWP = j-1;
+ if (WP1 && !self->inUse[j-1] ) freeTWP = j-1;
+ }
+
+ if (TWP == -1)
+ {
+ if (freeTWP == -1)
+ {
+ if (createTWP != -1 && self->TotalWP <= MAX_WPA)
+ {
+ VectorCopy( self->WPA[createTWP], self->WPA[self->TotalWP]);
+ VectorCopy( enemy->r.currentOrigin, self->WPA2[self->TotalWP]);
+ self->inUse[self->TotalWP] = qtrue;
+ self->TotalWP++;
+ self->targWP = createTWP;
+ ;//trap_SendServerCommand( -1, va( "print \"Waypoint cloned\n\""));
+ }
+ }
+ else
+ {
+ VectorCopy( enemy->r.currentOrigin, self->WPA2[freeTWP]);
+ self->inUse[freeTWP] = qtrue;
+ self->targWP = freeTWP;
+ ;//trap_SendServerCommand( -1, va( "print \"Secondary Assigned\n\""));
+ }
+ }
+ else
+ {
+ self->targWP = TWP;
+ ;//trap_SendServerCommand( -1, va( "print \"Firing normally\n\""));
+ }
+
+
+ if (self->targWP == -1)
+ continue;
+ else
+ ;//trap_SendServerCommand( -1, va( "print \"Found Waypoint, here it goes!\n\""));
+
+ }
+ else
+ {
+ ;//trap_SendServerCommand( -1, va( "print \"We have LOS, record waypoint\n\""));
+ if (self->TotalWP <= MAX_WPA)
+ {
+ visible = qfalse;
+ fvisible = qfalse;
+ for (j = 1; j <= self->TotalWP; j++)
+ {
+ if ( (self->inUse[j-1] && (visible = Hive_CheckWP(self->WPA2[j-1], enemy->r.currentOrigin, self))) ||
+ (!self->inUse[j-1] && (fvisible = Hive_CheckWP(self->WPA[j-1], enemy->r.currentOrigin, self)))) break;
+ }
+ if (!visible)
+ {
+ if (fvisible)
+ {
+ VectorCopy( enemy->r.currentOrigin, self->WPA[j-1]);
+ ;//trap_SendServerCommand( -1, va( "print \"Waypoint Replaced\n\""));
+ }
+ else
+ {
+ VectorCopy( enemy->r.currentOrigin, self->WPA[self->TotalWP]);
+ self->inUse[self->TotalWP] = qfalse;
+ self->TotalWP++;
+ ;//trap_SendServerCommand( -1, va( "print \"Waypoint Stored\n\""));
+ }
+ }
+ else
+ ;//trap_SendServerCommand( -1, va( "print \"Unnessesary Waypoint, ignored\n\""));
+ }
+ else
+ ;//trap_SendServerCommand( -1, va( "print \"Out of Memory, ignored\n\""));
+ }
+
+ ;//trap_SendServerCommand( -1, va( "print \"Launching the swarm!\n\""));
- if( !G_Visible( self, enemy ) )
- continue;
-
- if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
self->active = qtrue;
self->target_ent = enemy;
self->timestamp = level.time + HIVE_REPEAT;
-
- VectorSubtract( enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget );
+ if (self->targWP == -1)
+ VectorSubtract( enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget );
+ else
+ VectorSubtract( self->WPA[self->targWP], self->s.pos.trBase, dirToTarget );
+
VectorNormalize( dirToTarget );
vectoangles( dirToTarget, self->turretAim );
@@ -1172,7 +1302,7 @@
FireWeapon( self );
G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
return;
- }
+
}
}
Index: src/game/g_local.h
===================================================================
--- src/game/g_local.h (revision 87)
+++ src/game/g_local.h (working copy)
@@ -242,6 +242,15 @@
int lastDamageTime;
int bdnumb; // buildlog entry ID
+
+ #define MAX_WPA 31
+ #define HIVE_RECHECK 30000
+ vec3_t WPA[ MAX_WPA + 1 ];
+ vec3_t WPA2[ MAX_WPA + 1 ];
+ qboolean inUse[ MAX_WPA + 1];
+ int TotalWP;
+ int targWP;
+ int RecheckTime;
};
typedef enum
Index: src/game/g_missile.c
===================================================================
--- src/game/g_missile.c (revision 87)
+++ src/game/g_missile.c (working copy)
@@ -582,34 +582,127 @@
*/
void AHive_SearchAndDestroy( gentity_t *self )
{
+
vec3_t dir;
- trace_t tr;
-
+ trace_t tr, tr2, tr3;
+ vec3_t Torigin;
+ gentity_t *traceEnt;
+
+ if (self->TotalWP == 0 )
+ {
+ self->TotalWP++;
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ 0 ]);
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ self->TotalWP ]);
+ }
trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask );
+ traceEnt = &g_entities[ tr.entityNum ];
//if there is no LOS or the parent hive is too far away or the target is dead or notargeting, return
- if( tr.entityNum == ENTITYNUM_WORLD ||
- Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 5 ) ||
+ if( Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 30 ) ||
self->target_ent->health <= 0 || self->target_ent->flags & FL_NOTARGET )
{
self->r.ownerNum = ENTITYNUM_WORLD;
self->think = AHive_ReturnToHive;
self->nextthink = level.time + FRAMETIME;
+ return;
}
else
{
- VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir );
+ if ( tr.entityNum == ENTITYNUM_WORLD || traceEnt->s.eType == ET_BUILDABLE)
+ {
+ trap_Trace( &tr2, self->WPA[ self->TotalWP - 1 ], self->r.mins, self->r.maxs,
+ self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask );
+ traceEnt = &g_entities[ tr2.entityNum ];
+ if ( tr2.entityNum == ENTITYNUM_WORLD || traceEnt->s.eType == ET_BUILDABLE )
+ {
+ trap_Trace( &tr3, self->WPA[ self->TotalWP ], self->r.mins, self->r.maxs,
+ self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask ); // if the player turned around another corner while we were thinking, drop the target.
+ traceEnt = &g_entities[ tr3.entityNum ];
+ if ( tr3.entityNum == ENTITYNUM_WORLD || traceEnt->s.eType == ET_BUILDABLE )
+ {
+ self->r.ownerNum = ENTITYNUM_WORLD;
+
+ self->think = AHive_ReturnToHive;
+ self->nextthink = level.time + FRAMETIME;
+ //;//trap_SendServerCommand( -1, va( "print \"Lost Target\n\""));
+ return; // lost the track, get back home
+ }
+ else
+ {
+ if (self->TotalWP < MAX_WPA)
+ {
+ self->TotalWP++;
+ //;//trap_SendServerCommand( -1, va( "print \"New Waypoint, %i\n\"", self->TotalWP - 1));
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ self->TotalWP ]);
+ }
+ else
+ {
+ self->r.ownerNum = ENTITYNUM_WORLD;
+
+ self->think = AHive_ReturnToHive;
+ self->nextthink = level.time + FRAMETIME;
+ //;//trap_SendServerCommand( -1, va( "print \"Out Of memory\n\""));
+ return; // out of memory, get back home
+ }
+ }
+ }
+ else
+ {
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ self->TotalWP ]); //update latest waypoint without creating a new one, if there is a LOS from the old one
+ }
+ }
+ else
+ {
+ int i;
+ if (self->TotalWP > 1)
+ {
+ self->TotalWP = 1;
+ //;//trap_SendServerCommand( -1, va( "print \"Direct Line of Sight\n\""));
+ }
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ 0 ]);
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ self->TotalWP ]);
+
+
+ }
+
+ if (Distance( self->r.currentOrigin, self->WPA[ 0 ]) < 64 && self->TotalWP > 1) // snap the swarm to the waypoint if it is close enough.
+ {
+ int i;
+ VectorCopy(self->WPA[ 0 ], self->r.currentOrigin);
+ for (i = 0; i <= MAX_WPA; i++)
+ {
+ VectorCopy(self->WPA[i+1], self->WPA[i]);
+ }
+ self->TotalWP--;
+ if (self->TotalWP < 1) self->TotalWP = 1;
+ VectorCopy(self->target_ent->r.currentOrigin, self->WPA[ self->TotalWP ]);
+ //;//trap_SendServerCommand( -1, va( "print \"Waypoint Reached\n\""));
+ }
+
+ trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
+ self->WPA[ 0 ], self->r.ownerNum, self->clipmask );
+ traceEnt = &g_entities[ tr.entityNum ];
+ if ( tr.entityNum == ENTITYNUM_WORLD || traceEnt->s.eType == ET_BUILDABLE ) // check for doors closing
+ {
+
+ self->r.ownerNum = ENTITYNUM_WORLD;
+
+ self->think = AHive_ReturnToHive;
+ self->nextthink = level.time + FRAMETIME;
+ //;//trap_SendServerCommand( -1, va( "print \"Invalid Waypoint\n\""));
+ return;
+ }
+
+ VectorSubtract( self->WPA[ 0 ], self->r.currentOrigin, dir );
VectorNormalize( dir );
-
//change direction towards the player
VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
SnapVector( self->s.pos.trDelta ); // save net bandwidth
VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
self->s.pos.trTime = level.time;
-
- self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
+ self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD/2;
}
}
@@ -641,7 +734,29 @@
bolt->methodOfDeath = MOD_SWARM;
bolt->clipmask = MASK_SHOT;
bolt->target_ent = self->target_ent;
-
+ bolt->TotalWP = 0;
+
+ if(self->targWP != -1)
+ {
+ if(self->targWP >= 0)
+ {
+ bolt->TotalWP++;
+ VectorCopy(self->WPA[self->targWP], bolt->WPA[ 0 ]);
+ VectorCopy(self->WPA[self->targWP], bolt->WPA[ bolt->TotalWP ]);
+ ;//trap_SendServerCommand( -1, va( "print \"Swarm : Waypoint Received, proceeding to target\n\""));
+ }
+ else
+ {
+ int TW;
+ TW = abs(self->targWP) - 2;
+ bolt->TotalWP += 2;
+ VectorCopy(self->WPA[TW], bolt->WPA[ 0 ]);
+ VectorCopy(self->WPA2[TW], bolt->WPA[ 1 ]);
+ VectorCopy(self->WPA2[TW], bolt->WPA[ bolt->TotalWP ]);
+ ;//trap_SendServerCommand( -1, va( "print \"Swarm : 2 Waypoints received, proceeding to target\n\""));
+ }
+ }
+
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
Edit : pwef! added even more complicated algorythm, since the first one did not solve the problem for all cases that could happen. Not the best way to implement, but it is quite effective.