Bobot_nodes.c

/*--------------------------------------------------------------------------
crée par Joc le 1/02/04
NODES FUNCTION LISTING
BOBOT_NODES_SaveNodes()
BOBOT_NODES_LoadNodes()
BOBOT_NODES_ResolveAllPaths()
                     ____            
   ____   _____   __( __/____  ______
  /    \ /  _  \ / _  | / __ \/  ___/
 |   |  (  /_/  ) /_/ |/  ___/\____(    _   _   _
 |___|  /\_____/\___  |\___  )____  )  (_) (_) (_)
      \/            \/     \/     \/

 botbot_nodes.c
 Pathing routines for the FUSION bot.
--------------------------------------------------------------------------*/

#include "../game/g_local.h"
#include "bobot.h"
int number_of_nodes;                        //Total number of nodes
char *pNodes;                                //Data pour écrire les fichiers
node_t nodes[MAX_NODES];                    //Array for node data
gentity_t *nodep[MAX_NODES];                //Tableau de pointeurs qui relie les nodes et les objectifs !!
float AlliesMinCost[MAX_NODES][MAX_NODES];    //Distances minimales pour les alliés
float AxisMinCost[MAX_NODES][MAX_NODES];    //Distances minimales pour les axis
qboolean restart_controleur;
gentity_t *controleur = NULL;

/*--------------------------------------------------------------------------
Joc : une version de Save nodes utilisant le moteur de quake ;)
--------------------------------------------------------------------------*/
void BOBOT_NODES_SaveNodes(void)
{    gentity_t        *ent = &g_entities[0];
    char            filename[MAX_QPATH];
    vmCvar_t        mapname;
    fileHandle_t    f;
    int                data,len;
    char            nodscript[MAX_QPATH];
    FILE            *FicNodscript;

    BOBOT_NODES_ResolveAllPaths();
    trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
    Q_strlwr(mapname.string);
    Q_strncpyz( filename, "nodes/", sizeof(filename) );
    Q_strcat( filename, sizeof(filename), mapname.string );
    Q_strcat( filename, sizeof(filename), ".nod" );
    trap_FS_FOpenFile( filename, &f, FS_WRITE );
    data = number_of_nodes;
    trap_FS_Write( &data, sizeof(data), f );
    trap_FS_Write( &nodes, sizeof(nodes), f );
    trap_FS_FCloseFile( f );
    if (shownodes)
    {    if (ent != NULL)
        {  CP(va("cp \"File %s recorded on disk (%i nodes).\" 1", filename,number_of_nodes)); }
    }
    G_Printf("\nFile %s recorded on disk (%i nodes).", filename,number_of_nodes);
    /*    PAV : Nodscript généré automatiquement dans ../bobot s'il n'existe pas.*/
    Q_strncpyz( filename, "nodes/", sizeof(filename) );
    Q_strcat( filename, sizeof(filename), mapname.string );
    Q_strcat(filename,sizeof(filename),".nodscript");
    len = trap_FS_FOpenFile(filename, &f, FS_READ) ;
    if(len < 0)
    {    len = trap_FS_FOpenFile(filename, &f, FS_WRITE);
        if(len < 0)
        {    return;    }
        trap_FS_Write("//Generated by bobot soft.", 26, f);
        trap_FS_Write("\ntrigger init", 13, f);
        trap_FS_Write("\n{", 2, f);
        trap_FS_Write("\nset all move", 13, f);
        trap_FS_Write("\n}\n", 3, f);
        trap_FS_FCloseFile( f );
    }
    else
        trap_FS_FCloseFile( f );
}

/*--------------------------------------------------------------------------
Joc : une version de load nodes utilisant le moteur de quake ;)
--------------------------------------------------------------------------*/
void BOBOT_NODES_LoadNodes()
{    char            filename[MAX_QPATH];
    vmCvar_t        mapname;
    fileHandle_t    f;
    int                len,len1;

    trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
    Q_strlwr(mapname.string);
    Q_strncpyz( filename, "nodes/", sizeof(filename) );
    Q_strcat( filename, sizeof(filename), mapname.string );
    Q_strcat( filename, sizeof(filename), ".nod" );
    len = trap_FS_FOpenFile( filename, &f, FS_READ );
    if( len < 0 )
    {    G_Printf("bobot_nodes.c BOBOT_NODES_LoadNodes(): File %s no found.\n",filename);
        return;
    }
    pNodes = G_Alloc( len + 1 );
    trap_FS_Read( pNodes, len, f );
    *(pNodes + len) = '\0';
    memcpy( &number_of_nodes, pNodes, sizeof(number_of_nodes));
    len1 = number_of_nodes * sizeof(node_t);
    if (len1 > len + sizeof(number_of_nodes))
    { G_Printf("bobot_nodes.c BOBOT_NODES_LoadNodes(): File %s not valid.",filename); }
    else
    {    pNodes =(pNodes + sizeof(number_of_nodes));
        memcpy(&nodes , pNodes,len1 );
    }
    trap_FS_FCloseFile( f );
    BOBOT_NODES_ResolveAllPaths();     //joc newpath : c pour reinitiliser A le tableau des couts
    restart_controleur = qtrue;     //il faut charger le controleur
}

/*--------------------------------------------------------------------------
Joc : une version alléger de resolve all path :
--------------------------------------------------------------------------*/
void BOBOT_NODES_ResolveAllPaths()
{    int i, j, x;
    float new_weight;
   
    /*    Joc : on gere 2 tableaux maintenant, celui des allies et celui des axis */
    for (i = 0; i < MAX_NODES; i++)
    {    for (j = 0; j < MAX_NODES; j++)
        {    if (i == j)
            {    AlliesMinCost[i][j] = 0;
                AxisMinCost[i][j] = 0;
            }
            else
            {    AlliesMinCost[i][j] = NO_CONNECTION;
                AxisMinCost[i][j] = NO_CONNECTION;
            }
        }
    }

    /* Now fill in the directly connected nodes */
    for (i = 0; i < number_of_nodes; i++)
    {    for (j = 0; j < MAX_NODELINKS; j++)
        {    if (nodes[i].links[j].targetNode != INVALID)
            {    if((nodes[i].links[j].type == CONNEC_NORMAL) || (nodes[i].links[j].type == CONNEC_ECHELLE) || (nodes[i].links[j].type == CONNEC_TOUT_DROIT))
                {    //la connection est utilisable par les 2 teams
                    AlliesMinCost[i][nodes[i].links[j].targetNode] = nodes[i].links[j].cost;
                    AxisMinCost[i][nodes[i].links[j].targetNode] = nodes[i].links[j].cost;
                }
                else if (nodes[i].links[j].type == CONNEC_ALLIES)
                {    //la connection est utilisable par les allies
                    AlliesMinCost[i][nodes[i].links[j].targetNode] = nodes[i].links[j].cost;
                }
                else if (nodes[i].links[j].type == CONNEC_AXIS)
                {    //la connection est utilisable par les axis
                    AxisMinCost[i][nodes[i].links[j].targetNode] = nodes[i].links[j].cost;
                }
                //G_Printf("\n node=%i nodes[i].links[j].targetNode=%i nodes[i].links[j].type %i",i,nodes[i].links[j].targetNode,nodes[i].links[j].type);
            }
        }
    }

    /*    Calculate least-cost paths */
    for (j = 0; j < number_of_nodes; j++)
    {    for (i = 0; i < number_of_nodes; i++)
        {    if (AlliesMinCost[i][j] != NO_CONNECTION)
            {    for (x = 0; x < number_of_nodes; x++)
                {    if (AlliesMinCost[j][x] > 0 && AlliesMinCost[j][x] < NO_CONNECTION)
                    {    new_weight = AlliesMinCost[i][j] + AlliesMinCost[j][x];
                        AlliesMinCost[i][x] = ( new_weight > AlliesMinCost[i][x] ? AlliesMinCost[i][x] : new_weight );
                    }
                }
            }
            if (AxisMinCost[i][j] != NO_CONNECTION)
            {    for (x = 0; x < number_of_nodes; x++)
                {    if (AxisMinCost[j][x] > 0 && AxisMinCost[j][x] < NO_CONNECTION)
                    {    new_weight = AxisMinCost[i][j] + AxisMinCost[j][x];
                        AxisMinCost[i][x] = ( new_weight > AxisMinCost[i][x] ? AxisMinCost[i][x] : new_weight );
                    }
                }
            }
        }
    }

    /*    Now run a check on all the nodes */
    /*for (i = 0; i < number_of_nodes; i++)
    {    for (j = 0; j < number_of_nodes; j++)
        {    if (A[i][j] == NO_CONNECTION)
            { Bobot_Printf("No connection from %i to %i.\n", i, j); }
        }
    }*/
    //G_Printf("\nAll paths of connections are resolved, %i nodes updated. \n", number_of_nodes);
}
/*--------------------------------------------------------------------------
Joc
lie les nodes et les objectifs
idées :
utiliser spawnflag & AXIS_CONSTRUCTIBLE ou AXIS_OBJECTIVE
voir Think_SetupObjectiveInfo
--------------------------------------------------------------------------*/
void BOBOT_NODES_LierNode(int i )
{    int        j;
    vec3_t        mins,maxs,origin;
    int        num;
    int        touch[MAX_GENTITIES];
    gentity_t    *hit;

    nodep[i] = NULL;
    if(nodes[i].type & NODE_LINKED)
    {    //Bobot_Printf("Node %i : \n",i);
        VectorSet(mins, -nodes[i].radius, -nodes[i].radius, -nodes[i].radius);
        VectorSet(maxs, nodes[i].radius, nodes[i].radius, nodes[i].radius);
        VectorCopy( nodes[i].origin, origin );
        SnapVector( origin );
        VectorAdd( origin, mins, mins );
        VectorAdd( origin, maxs, maxs );
        num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
        for ( j=0 ; j<num; j++ )
        {    hit = &g_entities[touch[j]];
            Bobot_Printf("^$BOBOT : Possible link with ");
            Bobot_Printf(hit->classname);
            Bobot_Printf("\n");
            if (hit->s.eType == ET_TRAP && !Q_stricmp( hit->classname, "team_WOLF_checkpoint" ))        //flag-respawnpoint (comme sur oasis)
            {    nodep[i] = hit;
                Bobot_Printf("^$BOBOT : The node %i is linked to ",i);
                Bobot_Printf(hit->classname);
                Bobot_Printf("\n");
                continue;
            }
            if(!Q_stricmp( hit->classname, "misc_mg42") || !Q_stricmp( hit->classname, "misc_aagun"))    //+joc_MG42
            {    nodep[i] = hit;
                Bobot_Printf("^$BOBOT : The node %i is linked to ",i);
                Bobot_Printf(hit->classname);
                Bobot_Printf("\n");
                continue;
            }
            if (nodes[i].type & NODE_DRAPEAU )                                                            //Joc "drapeau " ou objet à ramener quoi
            {    if (!Q_stricmp(hit->classname,"team_CTF_redflag") || !Q_stricmp( hit->classname, "team_CTF_blueflag"))
                {    nodep[i] = hit;
                    Bobot_Printf("^$BOBOT : The node %i is linked to ",i);
                    Bobot_Printf(hit->classname);
                    Bobot_Printf("\n");
                    continue;
                }
                continue;
            }
            if ( !( hit->r.contents & CONTENTS_TRIGGER ))                                                //-joc_MG42
            { continue; }
            if (hit->s.eType == ET_OID_TRIGGER)
            {    Bobot_Printf("^$BOBOT : The node %i is linked to ",i);
                nodep[i] = hit;
                Bobot_Printf(hit->classname);
                Bobot_Printf("\n");
            }
                /*    if (hit->spawnflags & (AXIS_OBJECTIVE) )
                        Bobot_Printf("objective axis");
                    else if (hit->spawnflags & ALLIED_OBJECTIVE)
                        Bobot_Printf("Objective allies");
                    else if (hit->spawnflags & OBJECTIVE_DESTROYED)
                        Bobot_Printf("objectif detruit");
                    else
                        Bobot_Printf("Objective sans spawnflag ");
                    // Arnout - only if it targets a func_explosive
                    if(hit->target_ent &&
                       Q_stricmp( hit->target_ent->classname, "func_explosive"))
                        continue;
                    if (hit->spawnflags & AXIS_OBJECTIVE ||
                        hit->spawnflags & ALLIED_OBJECTIVE )
                    {
                    }*/
        }
            // Arnout - first see if the dynamite is planted near
            // a constructable object that can be destroyed
            /*{
                int        entityList[MAX_GENTITIES];
                int        numListedEntities;
                int        e;
                vec3_t  org;
                gentity_t *hit;

                VectorCopy( nodes[i].origin, org );
                org[2] += 4;    // move out of ground

                G_TempTraceIgnorePlayersAndBodies();
                numListedEntities = EntsThatRadiusCanDamage( org, nodes[i].radius*10, entityList );
                G_ResetTempTraceIgnoreEnts();

                for( e = 0; e < numListedEntities; e++ ) {
                    hit = &g_entities[entityList[ e ]];

                //if( hit->s.eType != ET_CONSTRUCTIBLE )
                //continue;
                if(hit->spawnflags & AXIS_CONSTRUCTIBLE)
                    Bobot_Printf("Constructible par les axis: ");
                else if (hit->spawnflags & AXIS_CONSTRUCTIBLE)
                    Bobot_Printf("Constructible par les allies: ");
                else
                    Bobot_Printf("constructible par personne");
                nodep[i] = hit;
                //Bobot_Printf("tiens entscandamage sert à qqch\n");
                Bobot_Printf("(2)Le node %i est lié à ",i);
                Bobot_Printf(hit->classname);
                Bobot_Printf("\n");
                   
            }
           
            //if (nodep[i] == NULL)
            //G_Printf("Le node %i  ne peut etre lier \n",i);*/
    }
}

/*--------------------------------------------------------------------------
Joc : Lance une entité qui gére les nodes
--------------------------------------------------------------------------*/
void BOBOT_NODES_LaunchController()
{    int i;

    if (controleur)
    { G_FreeEntity(controleur); }
    controleur = G_Spawn();
    controleur->inuse = qtrue;
    controleur->think = BOBOT_NODES_Controller_Think;
    controleur->nextthink = level.time + 1000;
    restart_controleur = qtrue;
    for (i=0;i<number_of_nodes;i++)
    { nodep[i] = NULL; }                                    //on remets tout à 0 à chaque fois que l'on charge les nodes
    BOBOT_NODES_Controller_Think(controleur);
}

/*--------------------------------------------------------------------------
Joc : L entité qui gére les nodes
--------------------------------------------------------------------------*/
void BOBOT_NODES_Controller_Think(gentity_t *ent)
{    int i;

    restart_controleur = qfalse;
    for(i=0;i<number_of_nodes;i++)
    {    if(nodes[i].type & NODE_LINKED)                        //permet de lier ou delier un node dynamiquement
        {    if (nodep[i] == NULL)
            { BOBOT_NODES_LierNode(i); }
            BOBOT_NODES_UpdateType(i);
        }
    }
    controleur->nextthink = level.time + 1000;                //chaque seconde on update
}

/*--------------------------------------------------------------------------
Joc : on calcul un nombre pour evaluer les nodes ou il faut se connecter
--------------------------------------------------------------------------*/
float BOBOT_NODES_Calc(int n1,int n2, int ntank)
{    vec3_t v1,v2;

    VectorSubtract(nodes[n1].origin,nodes[ntank].origin,v1);
    VectorNormalize(v1);
    VectorSubtract(nodes[n2].origin,nodes[ntank].origin,v2);
    VectorNormalize(v2);
    return DotProduct(v1,v2);
}

/*--------------------------------------------------------------------------
Joc : on crée des connexions le long du chemin du tank
--------------------------------------------------------------------------*/
void BOBOT_NODES_Tank_DoConnection(int destroy,int create,int ntank)
{
    BOBOT_MAPPING_DestroyNodeConnection(destroy,ntank);
    BOBOT_MAPPING_DestroyNodeConnection(ntank,destroy);
    BOBOT_MAPPING_CreateNodeConnection(create , ntank , CONNEC_NORMAL);
    BOBOT_MAPPING_CreateNodeConnection(ntank , create  ,CONNEC_NORMAL);
    BOBOT_NODES_ResolveAllPaths();
}

/*------------------------------------------------------------------------------
Joc : on crée des connexions le long du chemin du tank
num passé en paramètre est le node du tank.
n1 et n2 sont les 2 nodes auxquels le node tank est relié
n1min et n2min sont les deux nodes auxquels ont doit relier le tank
si ce sont les memes on ne fait rien
sinon on détruit les connexions n1 n2 et on crée n1min n2min
l'algo parait imbitable mais il ne doit pas prendre bcp de ressource en fait ;)
--------------------------------------------------------------------------------*/
void BOBOT_NODES_Tank_Connection(int num)
{    int i,j,k,l,n1,n2,n1min,n2min;
    float eval,evalmin;
   
    n1min = INVALID;
    n2min = INVALID;

    /*    le node du tank est relié à 2 nodes TANK_WAY toujours ! */
    n1 = INVALID;
    n2 = INVALID;
    evalmin = 99999.0f;

    /*    s'il n'y a aucune connection avec le node du tank ... */
    for(i=0;i<MAX_NODELINKS;i++)
    {    if(nodes[num].links[i].targetNode != INVALID)
        {    j = nodes[num].links[i].targetNode;
            if (nodes[j].type & NODE_TANK_WAY)                // nodes connectés au tank
            {    if(n1 == INVALID)
                { n1 = j; }
                else if(n2 == INVALID)
                { n2 = j; }
                for(k=0;k<MAX_NODELINKS;k++)
                {    if(nodes[j].links[k].targetNode != INVALID)
                    {    l = nodes[j].links[k].targetNode;
                        if (nodes[l].type & NODE_TANK_WAY)
                        {    eval = BOBOT_NODES_Calc(l,j,num);
                            if (eval < evalmin)
                            {    evalmin = eval;
                                n1min = l;
                                n2min = j;
                            }
                        }
                    }
                }
            }
        }
    }
    if(n1 == INVALID)
    {    Bobot_Printf("^1BOBOT : Error: two nodes must be connected to the node of the mobile vehicle.\n ");
        return;
    }
    if (n2 == INVALID)
    {    Bobot_Printf("^1BOBOT : Error: two nodes must be connected to the node of the mobile vehicle.\n ");
        return;
    }
    if(n1min == INVALID || n2min == INVALID)
    {    Bobot_Printf("^1BOBOT : BOBOT_NODES_Tank_Connection : n1min or n2min invalid !\n");
        return;
    }
    if(n1==n1min && n2 != n2min)
    { BOBOT_NODES_Tank_DoConnection(n2,n2min,num); }
    else if (n1==n2min && n2 != n1min)
    { BOBOT_NODES_Tank_DoConnection(n2,n1min,num); }
    else if(n2==n1min && n1 != n2min)
    { BOBOT_NODES_Tank_DoConnection(n1,n2min,num); }
    else if(n2 == n2min && n1 != n1min)
    { BOBOT_NODES_Tank_DoConnection(n1,n1min,num); }
}

/*--------------------------------------------------------------------------
Joc
L entité lié peut-elle être construite ? et pour quelle team ? (ou detruite)
reprise de G_IsConstructible
pas testé les constructions en plusieurs stage
--------------------------------------------------------------------------*/
void BOBOT_NODES_UpdateType(int i)
{    gentity_t    *constructible;
    gentity_t    *TOI;
   
    /*    on écrase les flags, attention à ne pas enlever objectif_pri ou protection */
    nodes[i].type &= ~( NODE_CONSTRUCTION | NODE_DYNAMITE | NODE_TEAM_ALLIES | NODE_TEAM_AXIS | NODE_DRAPEAU);

    /*    no construction during prematch aussi pour les bobots */
    if(level.warmupTime)
    { return; }
    TOI = nodep[i];
    if(!TOI)
    { return; }

    /*    d'abord est-ce un drapeau ? */
    if (TOI->s.eType == ET_TRAP && !Q_stricmp( TOI->classname,"team_WOLF_checkpoint"))
    {    nodes[i].type |= NODE_DRAPEAU;
        if (TOI->count == TEAM_ALLIES)                    //en fait la team est stocké dans count
        { nodes[i].type |= NODE_TEAM_ALLIES; }
        else
        { nodes[i].type |= NODE_TEAM_AXIS; }
        return;
    }

    /*    MG42 */
    if( !Q_stricmp( TOI->classname, "misc_mg42" ) || !Q_stricmp( TOI->classname, "misc_aagun"))
    {    if (TOI->s.frame != 0)
        {    nodes[i].type |= NODE_CONSTRUCTION;
            nodes[i].type |= NODE_TEAM_ALLIES;
            nodes[i].type |= NODE_TEAM_AXIS;
        }
        else
        {    nodes[i].type |= NODE_MG42;
            nodes[i].type |= NODE_HEAVY_MACHINGUN;
        }
        return;
    }
    if (!Q_stricmp(TOI->classname,"team_CTF_redflag") || !Q_stricmp(TOI->classname,"team_CTF_blueflag"))
    {    if(TOI->flags & FL_DROPPED_ITEM)
        { nodes[i].type |= NODE_CONSTRUCTION; }
        else
        { nodes[i].type |= NODE_DYNAMITE; }
    }
    if(nodes[i].type & NODE_MOBIL)                            // node mobile
    { VectorCopy(TOI->r.currentOrigin,nodes[i].origin); }
    if(nodes[i].type & NODE_TANK)                            //on crée des connexions dynamiques
    { BOBOT_NODES_Tank_Connection(i); }
    if(TOI->s.eType != ET_OID_TRIGGER)                        //-joc_MG42
    { return; }

    /*    peut-on construire ou detruire en allies ? */
    constructible = G_ConstructionForTeam( TOI, TEAM_ALLIES );
    if (constructible)
    {    if(G_ConstructionIsFullyBuilt( constructible ) ||
          (constructible->chain && G_ConstructionBegun(constructible->chain)))
        {    if(!( constructible->spawnflags & CONSTRUCTIBLE_INVULNERABLE ||
              (constructible->parent && constructible->parent->spawnflags & 8) ))
            {    nodes[i].type |= NODE_DYNAMITE;
                nodes[i].type |= NODE_TEAM_ALLIES;
            }
        }
        else
        {    nodes[i].type |= NODE_CONSTRUCTION;
            nodes[i].type |= NODE_TEAM_ALLIES;
        }
    }

    /*    peut-on construire ou detruire en axis ? */
    constructible = G_ConstructionForTeam( TOI, TEAM_AXIS );
    if (constructible)
    {    if(G_ConstructionIsFullyBuilt( constructible) ||
          (constructible->chain && G_ConstructionBegun(constructible->chain)))
        {    if(!( constructible->spawnflags & CONSTRUCTIBLE_INVULNERABLE ||
                (constructible->parent && constructible->parent->spawnflags & 8) ))
            {    nodes[i].type |= NODE_DYNAMITE;
                nodes[i].type |= NODE_TEAM_AXIS;
            }
        }
        else
        {    nodes[i].type |= NODE_CONSTRUCTION;
            nodes[i].type |= NODE_TEAM_AXIS;
        }
    }

    /*    si le tank est construit on peut utiliser sa machingun */
    if(nodes[i].type & NODE_TANK && !(nodes[i].type & NODE_CONSTRUCTION))
    { nodes[i].type |= NODE_HEAVY_MACHINGUN; }

    /*    peut-on dynamiter ? */
    if (nodep[i]->target_ent && Q_stricmp( nodep[i]->target_ent->classname, "func_explosive"))
    { return; }
    if (nodep[i]->spawnflags & AXIS_OBJECTIVE)
    {    nodes[i].type |= NODE_DYNAMITE;
        nodes[i].type |= NODE_TEAM_AXIS;    //les axis doivent le faire peter donc il appartient aux alliés
    }
    if (nodep[i]->spawnflags & ALLIED_OBJECTIVE)
    {    nodes[i].type |= NODE_DYNAMITE;
        nodes[i].type |= NODE_TEAM_ALLIES;    //les allies doivent le faire peter donc il appartient aux axis
    }
}

/*---------------------------------------------- version faite à partir G_weapon.c tryconstructible */

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