Bobot_move.c

#include "../game/g_local.h"
#include "bobot.h"
#include "bobot_move.h"
#include "bobot_utils.h"
qboolean BOBOT_heavy_weapon(int weapon);
vec3_t        sonarsStart[BOBOT_MOVE_BRAIN__NUM_SONARS];
vec3_t        sonarsEnd[BOBOT_MOVE_BRAIN__NUM_SONARS];
vec3_t        sonarsOffset[BOBOT_MOVE_BRAIN__NUM_SONARS];
trace_t        tr[BOBOT_MOVE_BRAIN__NUM_SONARS];
double        moveInputs[BOBOT_MOVE_BRAIN__NUM_INPUTS];
double        moveOutputs[BOBOT_MOVE_BRAIN__NUM_OUTPUTS];
vec3_t        rotation;
vec_t        seeUp,seeRight;

//=====================================================================================================
// Routine      : BOBOT_setMoveInputs
// Description  : donne des sensations aux bots (modifie l'état des entrées de son réseau de neurones)
//=====================================================================================================
void BOBOT_setMoveInputs(gentity_t *bobot,vec_t distanceFromNextNode, vec3_t angles)
{    int    i;
    vec3_t    dir,up,forward,right,mins,maxs;
    double temp;
    //gentity_t    *tent; // pour l'affichage des sonars (debug)
    Sbobot    *bobotAI;

    bobotAI = &bobots[bobot->s.clientNum];
   
    /*    Get current direction */
    VectorCopy(bobotAI->move_angles,dir);
    AngleVectors (dir, forward, right, up);

    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    { VectorSet(sonarsOffset[i], 0, 0, 0); }
    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    { G_ProjectSource(bobot->client->ps.origin, sonarsOffset[i], forward, right, sonarsStart[i]); }

    /*    réglage du PITCH de l'offset des rayons
        un sonar dans la ligne de mire du bot */
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_CENTER][0]    = BOBOT_MOVE_BRAIN__SONARS_LENGTH;

    /*    un autre à l'extrémité droite de son champ de vision */
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT][0]    = cos(BOBOT_ANGLE_OF_VISION/2) * BOBOT_MOVE_BRAIN__SONARS_LENGTH;
   
    /*    un autre à l'extrémité gauche de son champ de vision */
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT][0]    = sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT][0];
   
    /*    un sonar parallèle au premier mais plus bas */
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_CENTER_DOWN][0] = BOBOT_MOVE_BRAIN__SONARS_LENGTH;
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT_DOWN][0]  = sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT][0];
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT_DOWN][0]   = sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT][0];

    /*    réglage du YAW de l'offset des rayons */
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT][1]    = sin(BOBOT_ANGLE_OF_VISION/2) * BOBOT_MOVE_BRAIN__SONARS_LENGTH;
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT][1]    = -sin(BOBOT_ANGLE_OF_VISION/2) * BOBOT_MOVE_BRAIN__SONARS_LENGTH;
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT_DOWN][1] = sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_RIGHT][1];
    sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT_DOWN][1]  = sonarsOffset[BOBOT_MOVE_BRAIN__SONAR_LEFT][1];

    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    { G_ProjectSource(bobot->client->ps.origin, sonarsOffset[i], forward, right, sonarsEnd[i]); }
   
    /*    hauteurs des rayons réglées en fonction de la position des yeux du bot */
    sonarsStart[BOBOT_MOVE_BRAIN__SONAR_RIGHT][2]      += bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_MOVE_BRAIN__SONAR_RIGHT][2]        += bobot->client->ps.viewheight;
    sonarsStart[BOBOT_MOVE_BRAIN__SONAR_CENTER][2]     += bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_MOVE_BRAIN__SONAR_CENTER][2]       += bobot->client->ps.viewheight;
    sonarsStart[BOBOT_MOVE_BRAIN__SONAR_LEFT][2]    += bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_MOVE_BRAIN__SONAR_LEFT][2]    += bobot->client->ps.viewheight;

    /*    donnons un peu d'épaisseur aux sonars */
    VectorSet( mins, -4, -4, -4 );
    VectorSet( maxs,  4,  4,  4 );

    /*    on active les sonars */
    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    { trap_Trace(&tr[i], sonarsStart[i], mins, maxs, sonarsEnd[i], bobot->s.number, bobot->clipmask); }

    /*    ATTRIBUE LES PERCEPTIONS DU BOT AUX ENTREES DU NN */

    /*    les entrées du réseau de neurones sont normalisées */
    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    {    /*    une notion d'urgence aide le bot : les distances sont au carré, 1 indique un obstacle très proche */
        moveInputs[i] = (1.0 - tr[i].fraction) * (1.0 - tr[i].fraction);
    }

    /*    l'output est injecté dans les inputs. le sonar 3 (arrière) n'est pas encore utilisé (et ne le sera peut être jamais) */
    moveInputs[BOBOT_MOVE_BRAIN__LAST_ROTATION] = bobotAI->last_moveOutputs[BOBOT_MOVE_BRAIN__ROTATION];
    moveInputs[BOBOT_MOVE_BRAIN__LAST_VERTICAL_ROTATION] = bobotAI->last_moveOutputs[BOBOT_MOVE_BRAIN__VERTICAL_ROTATION];
   
    /*    le bot est renseigné sur la position du prochain node */
   
    /*    le yaw est converti en valeur entre -1 et 1 */
    moveInputs[BOBOT_MOVE_BRAIN__NODE_YAW] = angles[1]/180;
   
    /*    le pitch aussi, dans le but de le faire monter ou descendre les pentes ou escaliers */
    moveInputs[BOBOT_MOVE_BRAIN__NODE_PITCH] = angles[0]/180;

    /*    le bot connait la distance qui le sépare du prochain node, avec aussi une notion d'urgence : */
    temp = 1-(distanceFromNextNode / 1000);

    /*    le bot peut ainsi avoir une idée de la distance qui le sépare du prochain node */
    if(distanceFromNextNode < 1000)
    { moveInputs[BOBOT_MOVE_BRAIN__DISTANCE_FROM_NEXT_NODE] = temp * temp; }
    else
    { moveInputs[BOBOT_MOVE_BRAIN__DISTANCE_FROM_NEXT_NODE] = 0.0; }

    // affichage des sonars (debug)
    /*for(i=0;i<BOBOT_MOVE_BRAIN__NUM_SONARS;i++)
    {    tent = G_TempEntity( sonarsStart[i], EV_RAILTRAIL );
        VectorCopy(tr[i].endpos,tent->s.origin2);
    }*/
}

//===========================================================================
// Routine      : BOBOT_getMoveOutputs
// Description  : Attribue au bot les sorties du réseau de neurones (NN)
//===========================================================================
void BOBOT_getMoveOutputs(gentity_t *bobot,usercmd_t *ucmd)
{    int        jumpOrNot;
    Sbobot    *bobotAI;

    bobotAI = &bobots[bobot->s.clientNum];
   
    /*    regarder en haut ou en bas. transforme l'output (entre 0 et 1) en valeur entre -1 et 1 */
    seeUp = 2*moveOutputs[BOBOT_MOVE_BRAIN__VERTICAL_ROTATION] - 1;
   
    /*    se tourner à droite ou à gauche. */
    seeRight = 2*moveOutputs[BOBOT_MOVE_BRAIN__ROTATION] - 1;

    /*     gestion du saut*/
    if(moveOutputs[BOBOT_MOVE_BRAIN__JUMP] > 0.5)
    {    jumpOrNot = 0;
        if(bobot->client->ps.groundEntityNum != 1023) // si un saut n'est pas en cours
        { bobotAI->is_jumping = qfalse;    }
    }
    else
    {    jumpOrNot = 127;
        bobotAI->is_jumping = qtrue;
    }
    VectorSet(rotation,seeUp/2,seeRight,0);
    VectorMA(bobotAI->move_angles,BOBOT_MOVE_BRAIN__MAX_ROTATION_SPEED,rotation,bobotAI->move_angles);
    bobotAI->move_angles[YAW] = AngleNormalize180(bobotAI->move_angles[YAW]);
    bobotAI->move_angles[PITCH] = AngleNormalize180(bobotAI->move_angles[PITCH]);
    bobotAI->move_angles[ROLL] = AngleNormalize180(bobotAI->move_angles[ROLL]);
    ucmd->upmove = jumpOrNot;
}

//===========================================================================
// Routine      : BOBOT_AI_setFitness
// Description  : Evalue les compétences du bot. Lui donne une note (fitness)
//===========================================================================
void BOBOT_setMoveFitness(gentity_t *bobot,vec_t distanceFromNextNode,vec3_t angles)
{    vec3_t        mins,maxs;
    double        tmp;
    trace_t        collisionTrace;
    Sbobot        *bobotAI;

    bobotAI = &bobots[bobot->s.clientNum];

    /*     les comportements oscillatoires sont punis*/
    if(seeRight * bobotAI->last_seeRight > 0)
    { bobotAI->moveFitness -= 1; }
    if(seeUp * bobotAI->last_seeUp > 0)
    { bobotAI->moveFitness -= 1; }

    /*    on trace une bounding box un peu plus large que la bounding box du bot */
    VectorCopy(bobot->client->ps.mins,mins);
    VectorCopy(bobot->client->ps.maxs,maxs);
   
    /*les pieds du bot ne sont pas dedans (pour éviter une collision en montant un escalier) */
    mins[0] -= 4; mins[1] -= 4; mins[2] += 10;
    maxs[0] += 4; maxs[1] += 4;
    trap_Trace(&collisionTrace, bobot->client->ps.origin,mins, maxs, bobot->client->ps.origin, bobot->s.number, bobot->clipmask);
    if ( collisionTrace.allsolid && (collisionTrace.contents & ~CONTENTS_BODY))
    { bobotAI->moveFitness -= 10; }
   
    /*    pénalise les grosses variations de rotation */
    tmp = fabs(VectorLength(rotation)) + 1;
    bobotAI->moveFitness -= tmp * tmp - 1;

    /*    pénalise les bots qui ne regardent pas dans la direction du prochain node du point de vue de la rotation verticale */
    tmp = fabs(angles[0])/180 + 1;
    bobotAI->moveFitness -= tmp * tmp - 1;

    /*    pas de lapins chez moi ! */
    if(bobotAI->is_jumping == qtrue)
    { bobotAI->moveFitness -= 1; }
   
    /*    c'est pas bien de perdre de la vie. et plus on en perd moins c'est bien. */
    if(bobot->health != bobotAI->last_health) // si le bot perd de la vie
    {    if(bobotAI->last_health == 100)
        { bobotAI->moveFitness -= ((100 - bobot->health)*(100 - bobot->health))/100; }
        else
        { bobotAI->moveFitness -= ((100 - bobot->health)*(100 - bobot->health) - (100 - bobotAI->last_health)*(100 - bobotAI->last_health))/100; }
        bobotAI->last_health = bobot->health;
    }

    /*    Bobot, approche toi le plus possible du prochain node ! */
    if(distanceFromNextNode < bobotAI->minDistanceFromNextNode)
    {    bobotAI->moveFitness += (bobotAI->minDistanceFromNextNode - distanceFromNextNode)*BOBOT_COEFF_IMPORTANCE_OF_NEXT_NODE_DISTANCE;
        bobotAI->minDistanceFromNextNode = distanceFromNextNode;
    }
    if(distanceFromNextNode > bobotAI->maxDistanceFromNextNode)
    {    bobotAI->moveFitness -= (distanceFromNextNode - bobotAI->maxDistanceFromNextNode)*BOBOT_COEFF_IMPORTANCE_OF_NEXT_NODE_DISTANCE;
        bobotAI->maxDistanceFromNextNode = distanceFromNextNode;
    }
}

//===========================================================================
// Routine      : BOBOT_Move
// Description  : Fonction principale de décision du déplacement
//===========================================================================
void BOBOT_Move(gentity_t *bobot, usercmd_t *ucmd)
{    int            i;
    vec3_t        angles;
    float        angle,a1,a2;
    vec_t        distanceFromNextNode;

    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    gentity_t    *goalentity = bobotAI->currentAction.ent;        // 3emeType. was : bobotAI->goalentity;
   
    /*    Ne pas avancer couché (pour le moment) */
    if(bobot->s.eFlags & EF_PRONE)
    { ucmd->wbuttons |= WBUTTON_PRONE; }

    /*    Le bot se deplace vers son goal si il existe ou vers un lieu précis (node). */
    if (goalentity != NULL || bobotAI->currentAction.type == PROTECT_FROM_ENEMY)
    { VectorSubtract(bobotAI->currentAction.lastKnownPos,bobot->r.currentOrigin,bobotAI->move_vector); }

    /*    Trouver un passage. */
    else if (!FUSION_MAPPING_FollowPath(bobot)) // Pas de passage trouvé.
    {    bobotAI->state        = BOT_STATE_POSITION;
        bobotAI->goal_node    = INVALID;
        bobotAI->tries        = 0;
        bobotAI->wander_timeout    = level.time + 1000;
        return;
    }
    vectoangles(bobotAI->move_vector, angles );

    /*    On construit un vecteur représentant La différence entre la "visée" actuelle du bot
        (ce n'est pas la vraie visée mais une visée virtuelle) Et la position du prochain node */
    VectorSubtract(bobotAI->move_angles,angles, angles);
    angles[0] = AngleNormalize180(angles[0]);
    angles[1] = AngleNormalize180(angles[1]);

    /*    Calcul de la distance par rapport au prochain node */
    distanceFromNextNode = VectorLength(bobotAI->move_vector);

    /*    On prépare les entrées du réseau de neurones */
    BOBOT_setMoveInputs(bobot,distanceFromNextNode,angles);

    /*    Travail du réseau de neurones. calcul des sorties et gestion de la symétrie dans les mouvements
        Si l'obstacle le plus proche est à droite */
    if(max(moveInputs[BOBOT_MOVE_BRAIN__SONAR_LEFT],moveInputs[BOBOT_MOVE_BRAIN__SONAR_LEFT_DOWN]) < max(moveInputs[BOBOT_MOVE_BRAIN__SONAR_RIGHT_DOWN],moveInputs[BOBOT_MOVE_BRAIN__SONAR_RIGHT]))
    {    /*    Alors on fait comme si l'obstacle était à gauche */
        swap(&moveInputs[BOBOT_MOVE_BRAIN__SONAR_LEFT],&moveInputs[BOBOT_MOVE_BRAIN__SONAR_RIGHT]);
        swap(&moveInputs[BOBOT_MOVE_BRAIN__SONAR_LEFT_DOWN],&moveInputs[BOBOT_MOVE_BRAIN__SONAR_RIGHT_DOWN]);
        moveInputs[BOBOT_MOVE_BRAIN__LAST_ROTATION] = 1 - moveInputs[BOBOT_MOVE_BRAIN__LAST_ROTATION];    // l'ancienne valeur de la rotation est inversée
        moveInputs[BOBOT_MOVE_BRAIN__NODE_YAW] = - moveInputs[BOBOT_MOVE_BRAIN__NODE_YAW];                // le yaw du node aussi
       
        /*    Et le NN décide de tourner à droite (s'il a bien appris)
            Calcul des sorties du NN */
        UpdateNN(&(bobotAI->itsMoveBrain),moveInputs,moveOutputs);
       
        /*    donc on inverse la rotation choisie par le NN, et le bot tourne alors à gauche */
        moveOutputs[BOBOT_MOVE_BRAIN__ROTATION] = 1 - moveOutputs[BOBOT_MOVE_BRAIN__ROTATION];
        /*    On inverse aussi les autres sorties concernées */
    }
    else
    { UpdateNN(&(bobotAI->itsMoveBrain),moveInputs,moveOutputs); }

    /*    Attribue au bot les sorties du NN */
    BOBOT_getMoveOutputs(bobot,ucmd);

    /*    Prendre la direction choisie */
    a1 = AngleNormalize180(bobot->client->ps.viewangles[YAW]);
    a2 = AngleNormalize180(bobotAI->move_angles[YAW]);
    if(!(a1*a2>0) || (fabs(a1) < 90 && fabs(a2) < 90))
    {    if(a1<0)
        { a1 += 360; }
        if(a2<0)
        { a2 += 360; }
    }
    angle = AngleSubtract(a2,a1) * (M_PI*2 / 360);
    ucmd->forwardmove = cos(angle) * BOBOT_FORWARD;
    ucmd->rightmove   = -sin(angle) * BOBOT_RIGHT;

    /*    Calcul du fitness */
    if(BOBOT_MOVE_BRAIN__IS_LEARNING)
    { BOBOT_setMoveFitness(bobot,distanceFromNextNode,angles); }

    /*    préparation du prochain think */
    for(i=0;i<BOBOT_MOVE_BRAIN__NUM_OUTPUTS;i++)
    { bobotAI->last_moveOutputs[i] = moveOutputs[i]; }
    bobotAI->last_seeRight = seeRight;
    bobotAI->last_seeUp = seeUp;
    bobotAI->lastDistanceFromNextNode = distanceFromNextNode;
}

void BOBOT_BasicMove(gentity_t *bobot, usercmd_t *ucmd)
{    vec3_t        angles;
    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    gentity_t    *goalentity = bobotAI->currentAction.ent;        // 3emeType. was : bobotAI->goalentity;

    /*    Ne pas avancer couché (pour le moment) */
    /*if(bobot->s.eFlags & EF_PRONE)
    { ucmd->wbuttons |= WBUTTON_PRONE; }*/

    if (goalentity != NULL && goalentity != bobot)
    { VectorSubtract(goalentity->r.currentOrigin,bobot->r.currentOrigin,bobotAI->move_vector); }
    else if (!FUSION_MAPPING_FollowPath(bobot))
    {    bobotAI->state        = BOT_STATE_POSITION;
           bobotAI->goal_node    = INVALID;
        bobotAI->tries        = 0;
           bobotAI->wander_timeout    = level.time + 1000;
           return;
    }
    else
    { VectorSubtract(nodes[bobotAI->next_node].origin ,bobot->r.currentOrigin,bobotAI->move_vector); }
    vectoangles( bobotAI->move_vector, angles );
    VectorCopy( angles, bobotAI->move_angles );
    VectorCopy(bobotAI->move_angles,bobot->s.angles);
    ucmd->forwardmove = BOBOT_FORWARD;
  }


/*************************************************************************************************
joc
void BOBOT_SpecialMove(gentity_t *bobot, usercmd_t *ucmd)
pour que le bobot monte les echelles
*************************************************************************************************/
void BOBOT_SpecialMove(gentity_t *bobot, usercmd_t *ucmd)
{    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    float        angle,a1,a2;
    gentity_t    *goalentity = bobotAI->currentAction.ent;
   
    if (goalentity != NULL && goalentity != bobot)
    { VectorSubtract(goalentity->r.currentOrigin,bobot->r.currentOrigin,bobotAI->move_vector); }
    else if (!FUSION_MAPPING_FollowPath(bobot))
    {    bobotAI->state        = BOT_STATE_POSITION;
           bobotAI->goal_node    = INVALID;
        bobotAI->tries        = 0;
           bobotAI->wander_timeout    = level.time + 1000;
           return;
    }
    else
    { VectorSubtract(nodes[bobotAI->next_node].origin ,bobot->r.currentOrigin,bobotAI->move_vector); }
    vectoangles(bobotAI->move_vector , bobotAI->move_angles);
    a1 = AngleNormalize180(bobot->client->ps.viewangles[YAW]);
    a2 = AngleNormalize180(bobotAI->move_angles[YAW]);
    if(!(a1*a2>0) || (fabs(a1) < 90 && fabs(a2) < 90))
    {    if(a1<0)
        { a1 += 360; }
        if(a2<0)
        { a2 += 360; }
    }

    /*    Ne pas monter couché (pour le moment) */
    if(bobot->s.eFlags & EF_PRONE)
    { ucmd->wbuttons |= WBUTTON_PRONE; }

    angle = AngleSubtract(a2,a1) * (M_PI*2 / 360);
    ucmd->forwardmove = cos(angle) * BOBOT_FORWARD;
    //ucmd->rightmove   = -sin(angle) * BOBOT_RIGHT;
    ucmd->upmove = 0;
    ucmd->rightmove    = 0;
   
}

/* PAV P'tit modif de BOBOT_SpecialMove : On regarde en l'air ou en bas et on va tout droit.*/
/*void BOBOT_SpecialMove(gentity_t *bobot, usercmd_t *ucmd)
{    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    vec3_t        angles;
       
    if (!FUSION_MAPPING_FollowPath(bobot))
    {    bobotAI->state        = BOT_STATE_POSITION;
           bobotAI->goal_node    = INVALID;
        bobotAI->tries        = 0;
           bobotAI->wander_timeout    = level.time + 1000;
           return;
    }
    else
    {    VectorSubtract(nodes[bobotAI->next_node].origin ,bobot->r.currentOrigin,bobotAI->move_vector);
        bobotAI->move_vector[2] -= bobot->client->ps.viewheight;
        vectoangles (bobotAI->move_vector, angles);
        AnglesSubtract (angles , bobot->s.angles , angles);
        BOBOT_MOVEMENT_ChangeBotAngle(angles,bobot);
    }
    ucmd->forwardmove = BOBOT_FORWARD;
    ucmd->rightmove   = 0;
    ucmd->upmove = 0;
}*/

Créer un site gratuit avec e-monsite - Signaler un contenu illicite sur ce site