Bobot_common.c (2)

/*----------------------------------------------------------------------------------------------------------
3emeType
Fonction : BOBOT_COMMON_Attack. Description : gestion du tir contre un ennemi peut être amélioré.
PAV : largement amélioré par moi.
----------------------------------------------------------------------------------------------------------*/
void BOBOT_COMMON_Attack(gentity_t *bobot, usercmd_t *ucmd)
{      vec3_t        angles,ang,dir,bobotEyes;
    vec_t        dist;
    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    gentity_t    *enemy   = bobotAI->armsAction.ent;
    int    weaponGrenade,weaponScope;//,weapon3;
    int    weapon   = bobot->client->sess.playerWeapon;        // pav : Arme principale du bot.
    int    weapon2 = bobot->client->sess.playerWeapon2;

    /*if (weapon2==WP_AKIMBO_COLT || weapon2==WP_AKIMBO_SILENCEDCOLT || weapon2==WP_AKIMBO_LUGER || weapon2==WP_AKIMBO_COLT)
        weapon3=BG_AkimboSidearm(weapon2);
    else
        weapon3=weapon2;*/

    if ((enemy == NULL) || (enemy->client == NULL) || (!enemy->inuse) || (enemy->client->ps.pm_type == PM_DEAD))
    {    ucmd->buttons = 0;
        return;
    }

    if (bobot->client->sess.sessionTeam == TEAM_AXIS)
    { weaponGrenade = WP_GRENADE_LAUNCHER;}
    else
    { weaponGrenade = WP_GRENADE_PINEAPPLE; }

    VectorCopy(bobot->client->ps.origin, bobotEyes);
    bobotEyes[2] += bobot->client->ps.viewheight;
    VectorSubtract(enemy->r.currentOrigin, bobotEyes, dir);
    dist = VectorLength(dir);
    dir[2] += enemy->client->ps.viewheight;
    VectorNormalize(dir);
    vectoangles(dir,ang);
    AnglesSubtract (ang , bobot->s.angles , angles);

    /* Si le bot et son ennemi sont sous leau à moins de 70 de distance, ils se battent au couteau */
    if ((bobot->waterlevel>2) && (enemy->waterlevel>2))
    {    ucmd->weapon  = WP_KNIFE;
        if(dist < 70.0)   
        { ucmd->buttons = BUTTON_ATTACK; }
    }
    /* si le bot n'est pas sous l'eau */
    else if (bobot->waterlevel<2)
    {    /*    L'ennemi est tout près. Le bot prend son couteau ou sa seringue.  */
        if(dist <= 60.0)
        {    if(bobot->client->sess.playerType != PC_MEDIC)    // si ce n'est pas un médecin
            {    ucmd->weapon  = WP_KNIFE; }                    // Prend le couteau
            else                                            // si c'est un médecin
            {    if(!OnSameTeam(bobot,enemy))                // si ce n'est pas un team mate
                {    ucmd->weapon  = WP_MEDIC_SYRINGE; }        // le medic empoisonne son ennemi lol!
                else                                        // rappelons qu'un bot tue son teammate si celui-ci lui tire dessus exprès.
                {    ucmd->weapon  = WP_KNIFE; }                // Prend le couteau
            }
            ucmd->buttons = BUTTON_ATTACK;
        }
        /*    Sinon on ne tire que si on a des chances de toucher ! */
        else if (VectorLength(angles) < BOBOT_VIEW_PRECISION_TIR)
        {    switch (weapon)
            {    case WP_SHOTGUN:
                case WP_SHOTGUN_AXIS:
                    if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0) && (dist <= 500.0))
                    {    bobotAI->TimeLanceGrenade=level.time+2000;
                        bobotAI->UseGrenade++;
                    }
                    // gestion du shotgun en fonction de la distance du tir
                    if(dist<=g_ShotGunDistance.integer)
                    {    if (bobot->client->ps.ammoclip[BG_FindClipForWeapon(weapon)]>0)
                            ucmd->weapon = weapon;
                        else
                            BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    }
                    else
                    {    ucmd->weapon = weapon2;
                        if (VerifierMunitions(bobot,weapon2) == 0)
                        {    bobot->client->ps.ammo[BG_FindAmmoForWeapon(weapon2)] = 16;
                            ucmd->wbuttons |= WBUTTON_RELOAD;
                        }
                    }
                    ucmd->buttons = BUTTON_ATTACK;
                    break;
                case WP_FLAMETHROWER:                                        // Gestion du FLAMETHROWER.
                    if(dist <= 500.0)                                        // la distance est bonne.
                    {    /* si proche et jamais lancé de grenade, il balancera une grenade dans 2 secondes */
                        if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0))
                        {    bobotAI->TimeLanceGrenade=level.time+2000;
                            bobotAI->UseGrenade++;
                        }
                        if (VerifierMunitions(bobot,WP_FLAMETHROWER) > 0)
                        { ucmd->weapon = WP_FLAMETHROWER; }
                        else
                        {    ucmd->weapon = weapon2;
                            if (VerifierMunitions(bobot,weapon2) == 0)
                            {    bobot->client->ps.ammo[BG_FindAmmoForWeapon(weapon2)] = 16;
                                ucmd->wbuttons |= WBUTTON_RELOAD;
                            }
                        }
                    }
                    else                                                            // trop loin pour le lance flamme, il prend son arme secondaire.
                    {    ucmd->weapon = weapon2;
                        if (VerifierMunitions(bobot,weapon2) == 0)
                        {    bobot->client->ps.ammo[BG_FindAmmoForWeapon(weapon2)] = 16;
                            ucmd->wbuttons |= WBUTTON_RELOAD;
                        }
                    }
                    ucmd->buttons = BUTTON_ATTACK;
                    break;
                case WP_PANZERFAUST:                                    // PAV : Gestion du PANZERFAUST.
                    if ((VerifierMunitions(bobot, WP_PANZERFAUST) > 0) &&
                        (BOBOT_CLASS_WeaponCharged(bobot->client,WP_PANZERFAUST)) && (dist > 500.0))
                    {    ucmd->weapon = WP_PANZERFAUST;    }
                    else
                    {    /* si proche et jamais lancé de grenade, il balancera une grenade dans 2 secondes */
                        if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0) && (dist <= 500.0))
                        {    bobotAI->TimeLanceGrenade=level.time+2000;
                            bobotAI->UseGrenade++;
                        }
                        ucmd->weapon = weapon2;
                        if (VerifierMunitions(bobot, weapon2) == 0)
                        {    bobot->client->ps.ammo[BG_FindAmmoForWeapon(weapon2)] = 16;
                            ucmd->wbuttons |= WBUTTON_RELOAD;
                        }
                     }
                    ucmd->buttons = BUTTON_ATTACK;
                    break;
                case WP_BAZOOKA:
                    if ((VerifierMunitions(bobot, WP_BAZOOKA) > 0) &&
                        (BOBOT_CLASS_WeaponCharged(bobot->client,WP_BAZOOKA)) && (dist > 500.0))
                    {    ucmd->weapon = WP_BAZOOKA;    }
                    else
                    {    /* si proche et jamais lancé de grenade, il balancera une grenade dans 2 secondes */
                        if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0) && (dist <= 500.0))
                        {    bobotAI->TimeLanceGrenade=level.time+2000;
                            bobotAI->UseGrenade++;
                        }
                        ucmd->weapon = weapon2;
                        if (VerifierMunitions(bobot, weapon2) == 0)
                        {    bobot->client->ps.ammo[BG_FindAmmoForWeapon(weapon2)] = 16;
                            ucmd->wbuttons |= WBUTTON_RELOAD;
                        }
                     }
                    ucmd->buttons = BUTTON_ATTACK;
                    break;

                case WP_CARBINE:
                    if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0) && (dist <= 500.0))
                    {    bobotAI->TimeLanceGrenade=level.time+2000;
                        bobotAI->UseGrenade++;
                    }
                    if(bobot->client->ps.weapon == WP_M7)
                    {    if ( bobot->client->ps.ammoclip[BG_FindClipForWeapon(WP_M7)]<=0)
                            BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    }
                    else
                        BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    ucmd->buttons = BUTTON_ATTACK;
                    break;

                case WP_KAR98:
                    if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0) && (dist <= 500.0))
                    {    bobotAI->TimeLanceGrenade=level.time+2000;
                        bobotAI->UseGrenade++;
                    }
                    //G_Say(bobot, NULL,SAY_ALL,va("^7weapon %i, ammo%i",bobot->client->ps.weapon,bobot->client->ps.ammoclip[BG_FindClipForWeapon(bobot->client->ps.weapon)]));
                    if(bobot->client->ps.weapon == WP_GPG40)
                    {    if ( bobot->client->ps.ammoclip[BG_FindClipForWeapon(WP_GPG40)]<=0)
                            BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    }
                    else
                        BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    ucmd->buttons = BUTTON_ATTACK;
                    break;
       
                // Autres armes.
                default:
                    if(dist >= 1500.0)            // Si très loin, gestion des armes à lunette pour covertops.
                    {    switch(bobot->client->sess.playerType)
                        {    case PC_COVERTOPS:
                                if(weapon==WP_GARAND)
                                    weaponScope = WP_GARAND_SCOPE;
                                else if(weapon==WP_FG42)
                                    weaponScope = WP_FG42SCOPE;
                                else if(weapon==WP_K43)
                                    weaponScope = WP_K43_SCOPE;
                                else
                                    weaponScope = WP_NONE;
                                if (weaponScope > WP_NONE)
                                {    if (!bobot->client->ps.ammoclip[BG_FindClipForWeapon(weaponScope)])
                                        BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                                    else
                                        ucmd->weapon = weaponScope;
                                }
                                else
                                {    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd); }
                                if(g_SniperWar.integer)
                                {    if(random() < 0.03)
                                    ucmd->buttons = BUTTON_ATTACK;
                                }
                                else
                                    ucmd->buttons = BUTTON_ATTACK;
                                break;
                            case PC_FIELDOPS:    // Le field se sert des jumelles pour le support aérien.
                                if(bot_fieldusebinocularstrikes.integer)
                                {    if(BOBOT_CLASS_WeaponCharged(bobot->client,WP_SMOKE_MARKER))
                                    {    ucmd->weapon = WP_BINOCULARS;
                                        ucmd->wbuttons |= WBUTTON_ZOOM;
                                    }
                                    else
                                    { BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd); }
                                }
                                else
                                { BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd); }
                                ucmd->buttons = BUTTON_ATTACK;
                                break;
                            default:        // Les autres classes se servent de leur arme principale.
                                BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                                ucmd->buttons = BUTTON_ATTACK;
                                break;
                        }
                    }
                    // End dist > 1500.0
                    // dist > 500 < 1500 : Utilisation du Fumigène.
                    else if(dist >= 550.0)
                    {    switch(bobot->client->sess.playerType)
                        {    case  PC_COVERTOPS:
                                if(bot_covertusesmokebombe.integer)
                                {    if(BOBOT_CLASS_WeaponCharged(bobot->client,WP_SMOKE_BOMB))
                                    {    ucmd->angles[PITCH] = -28;
                                        ucmd->weapon = WP_SMOKE_BOMB;
                                        ucmd->buttons = (rand() % 2) * BUTTON_ATTACK;
                                    }
                                    else
                                    {    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                                        ucmd->buttons = BUTTON_ATTACK;
                                    }
                                }
                                else
                                {    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                                    ucmd->buttons = BUTTON_ATTACK;
                                }
                               
                                break;
                            case PC_FIELDOPS:        // PAV : Utilisation de l'AirStrike.
                                if(bot_fielduseairstrikes.integer)
                                {    if(BOBOT_CLASS_WeaponCharged(bobot->client,WP_SMOKE_MARKER))
                                    {    ucmd->angles[PITCH] = -28;
                                        ucmd->weapon = WP_SMOKE_MARKER;
                                        G_Voice( bobot, NULL, SAY_TEAM, "FTCallairstrike", qtrue); // voice airstrike
                                    }
                                    else
                                    {    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);    }
                                }
                                else
                                {    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);    }
                                ucmd->buttons = BUTTON_ATTACK;
                                break;
                            default:    // Les autres classes se servent de leur arme principale.
                                BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                                ucmd->buttons = BUTTON_ATTACK;
                                break;
                        }
                    }
                    // End dist > 550.0
                    // dist < 550.0
                    else
                    {    // si proche et jamais lancé de grenade, il balancera une grenade dans 2 secondes
                        if ((bobotAI->UseGrenade<=0) && (VerifierMunitions(bobot, weaponGrenade) > 0))
                        {    bobotAI->TimeLanceGrenade=level.time+2000;
                            bobotAI->UseGrenade++;
                        }
                        BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                        ucmd->buttons = BUTTON_ATTACK;
                    }
                break;
            }
            /* le bot lance une grenade*/
            if ((level.time >= bobotAI->TimeLanceGrenade) && (bobotAI->TimeLanceGrenade != 0))
            {    ucmd->weapon=weaponGrenade;
                ucmd->buttons = (rand() % 3) * BUTTON_ATTACK;
                // 2 secondes, à peu près le temps qu'il faut pour prendre et lancer une grenade.
                if ( level.time > (bobotAI->TimeLanceGrenade+2000))
                {    bobotAI->TimeLanceGrenade=0;
                    // Le bot reprend son arme après avoir lancé la grenade.
                    BOBOT_COMMON_TakeBestWeapon(bobot,bobotAI->myteam,ucmd);
                    ucmd->buttons = BUTTON_ATTACK;
                }
            }
        }
    }
    BOBOT_MOVEMENT_ChangeBotAngle(angles,bobot);
    bobotAI->teamPauseTime = level.time;
}

/*----------------------------------------------------------------------------------------------------------
3emeType
Fonction : protection_action_t BOBOT_COMMON_findProtectionFromEnemy
Description : cherche une position où il ne se fera pas tirer dessus par l'ennemi qu'il est en train de viser.
S'il n'en trouve pas, la fonction renvoie false, et le bot prendra alors si possible son arme secondaire.
*----------------------------------------------------------------------------------------------------------*/
protection_action_t BOBOT_COMMON_findProtectionFromEnemy(gentity_t *bobot, Sbobot *bobotAI)
{    int            i;
    gentity_t    *enemy = bobotAI->armsAction.ent;                                                // l'ennemi sur lequel il est en train de tirer
    vec3_t        possiblePosition,sonarOffset,sonarStart,sonarEnd,dir,up,forward,right,mins,maxs;
    trace_t        tr;
    vec_t        step,angle;

    if ((enemy == NULL) || (enemy->client == NULL) || (!enemy->inuse) || (enemy->client->ps.pm_type == PM_DEAD))
    { return NOT_NECESSARY; }
    if(bobotAI->skill>=3)
    {    // Le bot ne prone/duck pas s'il a son couteau ou sa seringue
        if ((bobot->client->ps.weapon != WP_MEDIC_SYRINGE) && (bobot->client->ps.weapon != WP_KNIFE))
        {     VectorCopy(bobot->r.currentOrigin,possiblePosition);                                // le bot juge d'abord de sa mise à couvert s'il s'accroupissait
            possiblePosition[2] += bobot->client->ps.crouchViewHeight;
            if(!visiblePosition(enemy,possiblePosition))                                        // si à genoux, le bot est couvert
            { return DUCK; }
            VectorCopy(bobot->r.currentOrigin,possiblePosition);                                // puis s'il se couchait
            possiblePosition[2] += PRONE_VIEWHEIGHT;
            if(!visiblePosition(enemy,possiblePosition))
            {    if ((bobot->client->ps.weapon == WP_PANZERFAUST) || (bobot->client->ps.weapon == WP_MORTAR) ||
                    (bobot->client->ps.weapon == WP_MORTAR_SET)  || (bobot->client->ps.weapon == WP_BAZOOKA))        // pas couché s'il a une arme lourde
                { return DUCK; }                                            // si à genoux, le bot est couvert
                else                                                        // si en se couchant, le bot est couvert
                { return PRONE;    }
            }
        }
    }

    /*    S'il ne peut pas se mettre à couvert facilement, alors il doit bouger...
        Voyons s'il existe un endroit peu éloigné où il pourrait aller */

    step = 2*3.14159265/NB_DIRECTIONS_EVALUATED_TO_GET_COVER;
    angle = 0;
    VectorCopy(bobotAI->move_angles,dir);                                                        // Get current direction
    AngleVectors (dir, forward, right, up);
    for(i=0; i<NB_DIRECTIONS_EVALUATED_TO_GET_COVER; i++,angle += step)
    {    int dist = RandInt(50,400);
        VectorClear(sonarOffset);
        G_ProjectSource(bobot->client->ps.origin, sonarOffset, forward, right, sonarStart);
        sonarOffset[0] = cos(angle) * dist;
        sonarOffset[1] = sin(angle) * dist;
        G_ProjectSource(bobot->client->ps.origin, sonarOffset, forward, right, sonarEnd);
        VectorSet( mins, -4, -4, -4 );                                                            // donnons un peu d'épaisseur au sonar
        VectorSet( maxs,  4,  4,  4 );
        trap_Trace(&tr, sonarStart, mins, maxs, sonarEnd, bobot->s.number, bobot->clipmask);
        /*tent = G_TempEntity( sonarStart, EV_RAILTRAIL );                                        // pour afficher le sonar
        VectorCopy(tr.endpos,tent->s.origin2); */
        if(tr.fraction >= 1.f && !visiblePosition(enemy,tr.endpos))                                // si le bot peut atteindre l'endroit...
        {    VectorCopy(tr.endpos,bobotAI->protectionPoint);                                        // et que son ennemi ne le voit pas, c'est parfait
            return GO_TO_COVERED_POINT;
        }
    }
    return NOT_POSSIBLE;
}

/*----------------------------------------------------------------------------------------------------------
3emeType : se protège d'un enemy par tous les moyens
----------------------------------------------------------------------------------------------------------*/
void BOBOT_COMMON_protectFromEnemy(gentity_t *bobot, usercmd_t *ucmd)
{    Sbobot    *bobotAI = &bobots[bobot->s.clientNum];

    switch(bobotAI->protectionAction)
    {    case DUCK:
            if(bobotAI->skill>=3)
            {    if(!(bobot->client->ps.pm_flags & PMF_DUCKED))
                {    ucmd->upmove = -48;
                    bobot->client->ps.pm_flags |= PMF_DUCKED;
                }
            }
            break;
        case PRONE:
            if(bobotAI->skill>=3)
            {    if(!(bobot->s.eFlags & EF_PRONE))
                    ucmd->wbuttons |= WBUTTON_PRONE;
            }
            break;
        case GO_TO_COVERED_POINT:
            BOBOT_Move(bobot,ucmd);
            break;
        default:
            break;
    }
}

/*----------------------------------------------------------------------------------------------------------
3emeType : Se met en position de défense. Ne va nulle part.
----------------------------------------------------------------------------------------------------------*/
void BOBOT_COMMON_defendPosition(gentity_t *bobot, usercmd_t *ucmd)
{      vec3_t        attackvector;
    vec_t        dist;
    Sbobot        *bobotAI = &bobots[bobot->s.clientNum];
    gentity_t    *enemy = bobotAI->armsAction.ent;

    // Le bot ne prone/duck pas s'il a son couteau ou sa seringue
    if ((bobot->client->ps.weapon == WP_MEDIC_SYRINGE) || (bobot->client->ps.weapon == WP_KNIFE))
        return;
    VectorSubtract(bobot->s.origin, enemy->s.origin, attackvector);
    dist = VectorLength(attackvector);
    if ( dist > 1500 )    // se coucher si loin, sinon ducker, sauf pour certaines armes lourdes
    {    if ((bobot->s.weapon != WP_PANZERFAUST) && (bobot->s.weapon != WP_MORTAR) &&
            (bobot->s.weapon != WP_MORTAR_SET) && (bobot->s.weapon != WP_BAZOOKA))
        {    if(!(bobot->s.eFlags & EF_PRONE))
            { ucmd->wbuttons |= WBUTTON_PRONE; }
        }
        else
        {    ucmd->upmove = -48;
            bobot->client->ps.pm_flags |= PMF_DUCKED;
        }
    }
    else
    {    ucmd->upmove = -48;
        bobot->client->ps.pm_flags |= PMF_DUCKED;
    }
}

/*----------------------------------------------------------------------------------------------------------
3emeType : donne le node le plus proche du bot parmi les nodes du tableau "nodesArray" de taille sizeOfArray
------------------------------------------------------------------------------------------------------------*/
void BOBOT_COMMON_determineClosestNodeFromCabinetsArray(gentity_t *bobot, Sbobot *bobotAI,
                                                        known_cabinet_t *nodesArray, int sizeOfArray,
                                                        int *closest, int *dist)
{    int                i,node;
    known_cabinet_t *cabinet;
    vec_t            distance,distMin = MAX_KNOWLEDGE_CABINET_DISTANCE,distToClosestNode;
    vec3_t            v;
    gentity_t        *ent;
    int                closestNode;
    float             (*MinCost)[MAX_NODES][MAX_NODES];

    MinCost = (bobotAI->myteam == TEAM_ALLIES ? &AlliesMinCost : &AxisMinCost);    //utilisation de AlliesMinCost ou AxisMinCost en fonction de la team du bot
    if(bobotAI->state == BOT_STATE_MOVE)                                        // évite trop de calcul la plupart du temps
    { closestNode = bobotAI->next_node; }                                        // et permet une approche du cabinet sans oscillations
    else
    { closestNode = BOBOT_NAVIGATION_FindClosestNode(bobot, NODE_DENSITY, NODE_ALL); }
    VectorSubtract(bobot->r.currentOrigin, nodes[closestNode].origin, v);
    distToClosestNode = VectorLength(v);                                        // distance au node le plus proche
    for(i=0; i<sizeOfArray; i++)
    {    cabinet = &nodesArray[i];
        node = cabinet->node;
        ent = cabinet->ent;
        if(node == INVALID)
        { continue; }
        if(ent == NULL)
        { continue; }
        if(ent->health != -9999 && ent->health <= 0)                            // on triche un peu. le bot sait d'avance si un cabinet est vide
        { continue; }
        distance = distToClosestNode + (*MinCost)[closestNode][node];            // distance à vol d'oiseau
        if(distance < distMin)                                                    // distance en passant par les nodes
        {    distMin = distance;
            *closest = node;
        }
    }
    *dist = BOBOT_distancePercentage(distMin, MAX_KNOWLEDGE_CABINET_DISTANCE);
}

/*----------------------------------------------------------------------------------------------------------
3emeType : donne la closest origin d'un pack en cherchant dans le tableau items
(qui peut être un tableau de healthPacks ou de ammoPacks, etc.)
----------------------------------------------------------------------------------------------------------*/
void BOBOT_COMMON_determineClosestItem(det_item_t *items, gentity_t **closest, int *dist)
{    int            i;
    det_item_t    *item;
    vec_t        distance,distMin = MAX_DETECTION_ITEM_DISTANCE;
   
    for(i=0; i<BOBOT_DETECT_MAX_PACKS; i++)
    {    item = &items[i];
        if(item->ent == NULL)
        { break; }                                // c'est le dernier de la liste alors on arrête là
        distance = item->dist;
        if(distance < distMin)
        {    distMin = distance;
            *closest = item->ent;
        }
    }
    if(distMin == MAX_DETECTION_ITEM_DISTANCE)    // si la distMin n'a pas changé c'est qu'aucun item n'est perçu
    { *dist = INVALID; }
    else
    { *dist = BOBOT_distancePercentage(distMin,MAX_DETECTION_ITEM_DISTANCE); }
}

/*----------------------------------End part 2 look at part 3

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