I have a code suggestions to modify civilians' behavior. This concerns the AI_CivilianCalcBestAction function defined in the g_ai.c file.
AI_CivilianCalcBestAction is called to test each square were a civilian can go, and returns a number estimating the benefit of the move (higher value = best move). for now, this number is the sum of three factors:
- the distance to the nearest alien
- a laziness factor (to encourage civilians to consume the more TU they can)
- a random factor
The first factor (distance to the nearest alien) is taken into account only for distance <RUN_AWAY_DIST = 5 cells!! This must be modified.
I made some modifications, but I didn't have tested them much (I tried few maps only). Maybe other people involved in ufoai development can adapt my suggestion to have better results.
Here is what my modified code does:
1 - the civilian reaction is different when the distance to the nearest alien is small or big.
2 - civilians are encouraged a little to regroup when they are close
3 - moving close to a fighter is encouraged a little, but not too close to avoid figthers being blocked.
4 - civilians are encouraged to find places were aliens don't see them.
Here is my source code of AI_CivilianCalcBestAction:
static float AI_CivilianCalcBestAction (edict_t * ent, pos3_t to, aiAction_t *aia)
{
edict_t *check;
int i, move, tu;
float dist, minDist, minDistCivilian, minDistFighter;
float bestActionPoints;
/* set basic parameters */
bestActionPoints = 0.0;
memset(aia, 0, sizeof(*aia));
VectorCopy(to, ent->pos);
VectorCopy(to, aia->to);
VectorCopy(to, aia->stop);
gi.GridPosToVec(gi.routingMap, to, ent->origin);
move = gi.MoveLength(gi.routingMap, to, qtrue);
tu = ent->TU - move;
/* test for time */
if (tu < 0)
return AI_ACTION_NOTHING_FOUND;
/* check whether this civilian can use weapons */
if (ent->chr.teamDef) {
const teamDef_t* teamDef = ent->chr.teamDef;
if (ent->state & ~STATE_PANIC && teamDef->weapons)
return AI_FighterCalcBestAction(ent, to, aia);
} else
Com_Printf("AI_FighterCalcBestAction: Error - civilian team with no teamdef\n");
/* run away */
minDist = RUN_AWAY_DIST*UNIT_SIZE;
minDistCivilian=RUN_AWAY_DIST*UNIT_SIZE;
minDistFighter=RUN_AWAY_DIST*UNIT_SIZE;
for (i = 0, check = g_edicts; i < globals.num_edicts; i++, check++){
if (check->inuse && check->team == TEAM_ALIEN && !(check->state & STATE_DEAD) && ent != check) {
dist = VectorDist(ent->origin, check->origin);
if (dist && dist < minDist)
minDist = dist;
}
if (check->inuse && check->team == TEAM_CIVILIAN && !(check->state & STATE_DEAD) && ent != check) {
dist = VectorDist(ent->origin, check->origin);
if (dist && dist < minDistCivilian)
minDistCivilian = dist;
}
if (check->inuse && check->team == TEAM_PHALANX && !(check->state & STATE_DEAD) && ent != check) {
dist = VectorDist(ent->origin, check->origin);
if (dist && dist < minDistFighter)
minDistFighter = dist;
}
}
float delta=0.0;/*GUETE_RUN_AWAY * minDist / RUN_AWAY_DIST;*/
minDist/=UNIT_SIZE;
minDistCivilian/=UNIT_SIZE;
minDistFighter/=UNIT_SIZE;
if(minDist<8.0){
/*very near an alien: run away fast*/
delta=4.0 * minDist;
}else if(minDist<16.0){
/*near an alien: run away*/
delta=24.0+minDist;
}else if(minDist<24.0){
/*near an alien: run away slower*/
delta=40.0+(minDist-16)/4;
}else{
delta=42.0;
}
/*near a civilian: join him (1/3)*/
if(minDistCivilian<10.0)
delta+=(10.0-minDistCivilian)/3.0;
/*near a fighter: join him (1/5)*/
if(minDistFighter<15.0)
delta+=(15.0-minDistFighter)/5.0;
/*don't go close to a fighter to let him move*/
if(minDistFighter<2.0)
delta/=10.0;
/*try to hide*/
float reaction_trap=0.0;
for (i = 0, check = g_edicts; i < globals.num_edicts; i++, check++)
if (check->inuse && G_IsLivingActor(check) && ent != check
&& (check->team == TEAM_ALIEN || ent->state & STATE_INSANE) && G_ActorVis(check->origin, ent, qtrue) > 0.25)
reaction_trap+=25.0;
delta-=reaction_trap;
bestActionPoints += delta;
/* add laziness */
if (ent->TU)
bestActionPoints += GUETE_CIV_LAZINESS * tu / ent->TU;
/* add random effects */
bestActionPoints += GUETE_CIV_RANDOM * frand();
return bestActionPoints;
}