D:\cygwin\wf_fusion_src_r6\CVSROOT\game\bot_spawn.c

/*****************************************************************

    Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-

    ..............................................................

    This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.

    ..............................................................

    All other files are Copyright(c) Id Software, Inc.

    Please see liscense.txt in the source directory for the copyright
    information regarding those files belonging to Id Software, Inc.

    ..............................................................
    
    Should you decide to release a modified version of Eraser, you MUST
    include the following text (minus the BEGIN and END lines) in the 
    documentation for your modification.

    --- BEGIN ---

    The Eraser Bot is a product of Ryan Feltrin, and is available from
    the Eraser Bot homepage, at http://impact.frag.com.

    This program is a modification of the Eraser Bot, and is therefore
    in NO WAY supported by Ryan Feltrin.

    This program MUST NOT be sold in ANY form. If you have paid for 
    this product, you should contact Ryan Feltrin immediately, via
    the Eraser Bot homepage.

    --- END ---

    ..............................................................

    You will find p_trail.c has not been included with the Eraser
    source code release. This is NOT an error. I am unable to 
    distribute this file because it contains code that is bound by
    legal documents, and signed by myself, never to be released
    to the public. Sorry guys, but law is law.

    I have therefore include the compiled version of these files
    in .obj form in the src\Release and src\Debug directories.
    So while you cannot edit and debug code within these files,
    you can still compile this source as-is. Although these will only
    work in MSVC v5.0, linux versions can be made available upon
    request.

    NOTE: When compiling this source, you will get a warning
    message from the compiler, regarding the missing p_trail.c
    file. Just ignore it, it will still compile fine.

    ..............................................................

    I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
    use of this source code. I also am NOT willing to provide any form
    of help or support for this source code. It is provided as-is,
    as a service by me, with no documentation, other then the comments
    contained within the code. If you have any queries, I suggest you
    visit the "official" Eraser source web-board, at
    http://www.telefragged.com/epidemic/. I will stop by there from
    time to time, to answer questions and help with any problems that
    may arise.

    Otherwise, have fun, and I look forward to seeing what can be done
    with this.

    -Ryan Feltrin
    -Acrid-

 *****************************************************************/
/* bot_spawn.c */
#include "environ.h"
#include "g_local.h"
#include "m_player.h"
#include "bot_procs.h"
///Q2 Camera Begin
#include "camclient.h"
///Q2 Camera End
#include "stdlog.h"

qboolean ClientConnect (edict_t *ent, char *userinfo, qboolean loadgame);

// BEGIN: SABIN code
edict_t *bot_GetLastFreeClient (void)
{
    edict_t *bot = NULL;
    int i;

    for (i = maxclients->value; i > 0; i--)
    {
        bot = g_edicts + i + 1;

        if (!bot->inuse)
            break;
    }

    if (bot && bot->inuse)
        bot = NULL;

    return bot;
}
// END: SABIN code

void respawn_bot (edict_t *self)
{
    if (level.intermissiontime)
        return;

    self->s.event = EV_TELEPORT;

    PutClientInServer(self);

    self->last_goal = NULL;
    self->enemy = self->goalentity = self->movetarget = NULL;
    self->viewheight = 22;

    // reset weapon to blaster
    self->last_fire = level.time + 0.2;
    self->fire_interval = FIRE_INTERVAL_BLASTER;
    self->bot_fire = botBlaster;

    self->bored_suicide_time = -1;
    self->checkstuck_time = level.time;
    self->last_reached_trail = level.time + 1;

    self->client->killer_yaw = 0;   // chaingun wind-up
    self->avoid_ent = NULL;
    self->flagpath_goal = NULL;
    self->last_move_nocloser = level.time;
   
    // go for it
    walkmonster_start(self);
}

// Find an available edict, and initialize some default values
edict_t *G_SpawnBot ()
{
    edict_t     *bot = NULL;
            
    if (!deathmatch->value)
    {
        return NULL;
    }

    if ((!bot_calc_nodes->value) && !loaded_trail_flag)
    {
        my_bprintf(PRINT_HIGH, "Route-table not found!\n");
        return NULL;
    }

    last_bot_spawn = level.time;

//  bot = G_Spawn();
    bot = bot_GetLastFreeClient();

    if (!bot)
    {
        G_Printf("No client spots available!\n");
        return NULL;
    }

//  bot->bot_client = gi.TagMalloc (sizeof(struct gclient_s), TAG_GAME);
//  bot->bot_stats =  gi.TagMalloc (sizeof(bot_stats_t), TAG_GAME);
    bot->bot_stats = G_GameMalloc (game.maxclients *sizeof(bot_stats_t));

    if (!bot->bot_stats)
    {
        G_Printf("Could not allocate Bot Stats!\n");
        return NULL;
    }


    bot->classname = "player";

    bot->movetype = MOVETYPE_WALK;
    bot->solid = SOLID_BBOX;

    //K2:Begin//fixme remove
    bot->takedamage = DAMAGE_YES;
    //K2:End

    VectorSet (bot->mins, -16, -16, -24);
    VectorSet (bot->maxs, 16, 16, 32);

    bot->health = bot->max_health = 100;
    bot->mass = 200;
    bot->gravity = 1;

    bot->last_goal = NULL;

    bot->pain = bot_pain;
    bot->die = bot_die;

    bot->monsterinfo.stand = bot_run; //bot_stand;
    bot->monsterinfo.walk = bot_run; //bot_walk;
    bot->monsterinfo.run = bot_run;
    bot->monsterinfo.attack = bot_run;
    bot->monsterinfo.melee = NULL;
    bot->monsterinfo.sight = NULL;

    bot->monsterinfo.scale = MODEL_SCALE;

    bot->enemy = bot->goalentity = bot->movetarget = NULL;

    players[num_players++] = bot;

    bot->last_fire = level.time + 0.2;
    bot->fire_interval = FIRE_INTERVAL_BLASTER;
    bot->bot_fire = botBlaster;
    
    bot->bored_suicide_time = -1;
    bot->checkstuck_time = level.time;

    bot->viewheight = 22;
    bot->yaw_speed = 50;    // turn at yaw_speed degrees per FRAME

    bot->last_reached_trail = level.time + 1;

    bot->avoid_ent = NULL;
    bot->last_move_nocloser = level.time;

    return bot;
}

// Perform the ClientBegin() and ClientBeginDeathmatch() functions in one routine, that is bot-specific
edict_t *spawn_bot (char *botname)
{
    edict_t     *bot, *chat;
    bot_info_t  *botdata;
//fixme debugger    char    skin[256];
    char    userinfo[MAX_INFO_STRING];
    vec3_t      spawn_origin, spawn_angles;
    char    WFclass[256];

    botdata = GetBotData(botname);

    if (!botdata)
    {
        G_Printf("Unable to find bot, or no bots left\n");
        return NULL;
    }


    bot = G_SpawnBot();
 
    if (!bot)
    {
        G_Printf("Unable to spawn bot: cannot create entity\n");
        return NULL;
    }

    bot->bot_client = qtrue;
    bot->client = &game.clients[bot-g_edicts-1];

    memset(bot->client, 0, sizeof(*(bot->client)));

    // copy the stats across from the bot config
    botdata->ingame_count++;
    bot->botdata = botdata;

    strcpy(WFclass, botdata->wfclass);

// BEGIN: SABIN code
    // initialise userinfo
    memset (userinfo, 0, sizeof(userinfo));

    // add bot's name/skin/hand to userinfo
    Info_SetValueForKey (userinfo, "name", botdata->name);
    Info_SetValueForKey (userinfo, "WFclass", botdata->wfclass);
    Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now
// END: SABIN code
    
    ///Q2 Camera Begin
    EntityListAdd(bot);
    ///Q2 Camera End
//WF START ACRID INITIAL CLASS SPAWNING

    //set respawn protection time
    bot->client->protecttime = level.time + RESPAWN_PROTECT_TIME;   
//      bot->client->pers.player_class = bot->client->pers.next_player_class;

    bot->client->pers.player_class = atoi(WFclass);//6/99
    bot->client->pers.next_player_class = atoi(WFclass);
    
/*if (strcmp("1", WFclass) == 0)
    {   bot->client->pers.player_class = 1;
    bot->client->pers.next_player_class = 1;}
*/
    ClientConnect (bot, userinfo, qfalse);

    if (ctf->value)
    {
        G_PrintMsg (NULL, PRINT_HIGH, "%s ^7joined the %s team.\n",
            bot->client->pers.netname, CTFTeamName(bot->client->resp.ctf_team));
        sl_LogPlayerTeamChange( &gi,
                                bot->client->pers.netname,
                                CTFTeamName(bot->client->resp.ctf_team));
    }

    SelectSpawnPoint (bot, spawn_origin, spawn_angles);
//INSERT TESTS HERE
//    botDebugPrint("SPAWN_BOT %s (ACRID)\n",userinfo);

    VectorCopy(spawn_origin, bot->s.origin);
//  bot->s.origin[2] += 1;  // make sure off ground

    VectorCopy(spawn_angles, bot->s.angles);

    bot->client->killer_yaw = 0;    // chaingun wind-up

    // clear playerstate values
    memset (&bot->client->ps, 0, sizeof(bot->client->ps));

    bot->client->ps.pmove.origin[0] = bot->s.origin[0]*8;
    bot->client->ps.pmove.origin[1] = bot->s.origin[1]*8;
    bot->client->ps.pmove.origin[2] = bot->s.origin[2]*8;

    bot->client->ps.fov = 90;

    // send effect
    gi.WriteByte (svc_muzzleflash);
    gi.WriteShort (bot-g_edicts);
    gi.WriteByte (MZ_LOGIN);
    gi.multicast (bot->s.origin, MULTICAST_PVS);

    // copy the bot stats
    memcpy(bot->bot_stats, &(botdata->bot_stats), sizeof(bot_stats_t));

    // set starting skill level
    bot->skill_level = skill->value;
    AdjustRatingsToSkill(bot);

//  bot->s.modelindex = gi.modelindex(model);
    bot->s.skinnum = bot-g_edicts - 1;
    bot->s.modelindex = 255;
    bot->s.modelindex2 = 255;
//    botDebugPrint("SPAWN_BOT (ACRID)\n");
/*
    bot->client->buttons = bot->s.modelindex;
    bot->client->oldbuttons = bot->s.skinnum;
*/
    bot->map = G_CopyString(botdata->name);
    strcpy(bot->client->pers.netname, botdata->name);
    G_PrintMsg (NULL, PRINT_HIGH, "%s entered the game", bot->client->pers.netname);

    // set visible model vwep with 3.20 
    ShowGun(bot); 

/*
    if (view_weapons->value && (bot->s.modelindex2 == 255) && bot_show_connect_info->value)
    {
        my_bprintf(PRINT_HIGH, " (no view weapon)");
    }
*/
    my_bprintf(PRINT_HIGH, "\n");

    bot_count++;

    // generic bot stuff
    if (!KillBox (bot))
    {   // could't spawn in?
    }

    gi.linkentity (bot);

    bot->viewheight = 22;

    bot->inuse = qtrue;

    // go for it
    walkmonster_start(bot);

    if (random() < 0.3)
    {
        // spawn the greetings thinker
        chat = G_Spawn();
        chat->owner = bot;
        chat->think = BotGreeting;
        chat->nextthink = level.time + 1.5 + random();
    }
    bot->wf_team = bot->client->resp.ctf_team;
    wf_InitPlayerClass(bot->client);//ACRID

    //K2 botcam acrid
    bot->client->resp.inServer=qtrue;

    return bot;
}

extern int num_clients;
//K2:Added for camera..must add player back to players array
void botAddPlayer(edict_t *self)
{
    players[num_players] = self;
    num_players++;
    num_clients++;
    return;

}
void botRemovePlayer(edict_t *self)
{
    int i;

    self->health = 0;   // so other bots stop looking for us

    // remove the client from the players array
    for (i=0; i<num_players; i++)
        if (players[i] == self)
            break;

    if (i == num_players)   // didn't find them
    {
        G_Printf("WARNING: Unable to remove player from player[] array, problems will arise.\n");
        return;
    }

    i++;
    for (; i<num_players; i++)
        players[i-1] = players[i];

    players[i] = NULL;


    // remove from team list
/*  if (self->client->team)
    {
        self->client->team->num_players--;
        self->client->team->num_bots--;
        self->client->team = NULL;
    }*///remove bot team

    self->client->resp.ctf_team = CTF_NOTEAM;

    num_players--;

    if (self->bot_client)
    {
        bot_count--;
        self->botdata->ingame_count--;
    }
    else
    {
        num_clients--;
    }

    // tell all other bots not to look for this bot anymore
    for (i=0; i<num_players; i++)
        if (players[i]->enemy == self)
            players[i]->enemy = players[i]->goalentity = NULL;
}

void    botDisconnect(edict_t   *self)
{   // disconnects a bot from the game

    ClientDisconnect(self);

    self->bot_client = qfalse;
    self->bot_stats = NULL;
}