diff options
author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
---|---|---|
committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/botlib/be_ai_char.c | |
parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/botlib/be_ai_char.c')
-rwxr-xr-x | code/botlib/be_ai_char.c | 1580 |
1 files changed, 790 insertions, 790 deletions
diff --git a/code/botlib/be_ai_char.c b/code/botlib/be_ai_char.c index 042adb4..ea34030 100755 --- a/code/botlib/be_ai_char.c +++ b/code/botlib/be_ai_char.c @@ -1,790 +1,790 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_char.c
- *
- * desc: bot characters
- *
- * $Archive: /MissionPack/code/botlib/be_ai_char.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_log.h"
-#include "l_memory.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "../game/be_ai_char.h"
-
-#define MAX_CHARACTERISTICS 80
-
-#define CT_INTEGER 1
-#define CT_FLOAT 2
-#define CT_STRING 3
-
-#define DEFAULT_CHARACTER "bots/default_c.c"
-
-//characteristic value
-union cvalue
-{
- int integer;
- float _float;
- char *string;
-};
-//a characteristic
-typedef struct bot_characteristic_s
-{
- char type; //characteristic type
- union cvalue value; //characteristic value
-} bot_characteristic_t;
-
-//a bot character
-typedef struct bot_character_s
-{
- char filename[MAX_QPATH];
- float skill;
- bot_characteristic_t c[1]; //variable sized
-} bot_character_t;
-
-bot_character_t *botcharacters[MAX_CLIENTS + 1];
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_character_t *BotCharacterFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botcharacters[handle])
- {
- botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
- return NULL;
- } //end if
- return botcharacters[handle];
-} //end of the function BotCharacterFromHandle
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpCharacter(bot_character_t *ch)
-{
- int i;
-
- Log_Write("%s", ch->filename);
- Log_Write("skill %d\n", ch->skill);
- Log_Write("{\n");
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- switch(ch->c[i].type)
- {
- case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break;
- case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break;
- case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break;
- } //end case
- } //end for
- Log_Write("}\n");
-} //end of the function BotDumpCharacter
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacterStrings(bot_character_t *ch)
-{
- int i;
-
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- if (ch->c[i].type == CT_STRING)
- {
- FreeMemory(ch->c[i].value.string);
- } //end if
- } //end for
-} //end of the function BotFreeCharacterStrings
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacter2(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
- return;
- } //end if
- if (!botcharacters[handle])
- {
- botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
- return;
- } //end if
- BotFreeCharacterStrings(botcharacters[handle]);
- FreeMemory(botcharacters[handle]);
- botcharacters[handle] = NULL;
-} //end of the function BotFreeCharacter2
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacter(int handle)
-{
- if (!LibVarGetValue("bot_reloadcharacters")) return;
- BotFreeCharacter2(handle);
-} //end of the function BotFreeCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)
-{
- int i;
-
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- if (ch->c[i].type) continue;
- //
- if (defaultch->c[i].type == CT_FLOAT)
- {
- ch->c[i].type = CT_FLOAT;
- ch->c[i].value._float = defaultch->c[i].value._float;
- } //end if
- else if (defaultch->c[i].type == CT_INTEGER)
- {
- ch->c[i].type = CT_INTEGER;
- ch->c[i].value.integer = defaultch->c[i].value.integer;
- } //end else if
- else if (defaultch->c[i].type == CT_STRING)
- {
- ch->c[i].type = CT_STRING;
- ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);
- strcpy(ch->c[i].value.string, defaultch->c[i].value.string);
- } //end else if
- } //end for
-} //end of the function BotDefaultCharacteristics
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
-{
- int indent, index, foundcharacter;
- bot_character_t *ch;
- source_t *source;
- token_t token;
-
- foundcharacter = qfalse;
- //a bot character is parsed in two phases
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(charfile);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
- return NULL;
- } //end if
- ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
- MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
- strcpy(ch->filename, charfile);
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "skill"))
- {
- if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- //if it's the correct skill
- if (skill < 0 || token.intvalue == skill)
- {
- foundcharacter = qtrue;
- ch->skill = token.intvalue;
- while(PC_ExpectAnyToken(source, &token))
- {
- if (!strcmp(token.string, "}")) break;
- if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
- {
- SourceError(source, "expected integer index, found %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- index = token.intvalue;
- if (index < 0 || index > MAX_CHARACTERISTICS)
- {
- SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (ch->c[index].type)
- {
- SourceError(source, "characteristic %d already initialized\n", index);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (token.type == TT_NUMBER)
- {
- if (token.subtype & TT_FLOAT)
- {
- ch->c[index].value._float = token.floatvalue;
- ch->c[index].type = CT_FLOAT;
- } //end if
- else
- {
- ch->c[index].value.integer = token.intvalue;
- ch->c[index].type = CT_INTEGER;
- } //end else
- } //end if
- else if (token.type == TT_STRING)
- {
- StripDoubleQuotes(token.string);
- ch->c[index].value.string = GetMemory(strlen(token.string)+1);
- strcpy(ch->c[index].value.string, token.string);
- ch->c[index].type = CT_STRING;
- } //end else if
- else
- {
- SourceError(source, "expected integer, float or string, found %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end else
- } //end if
- break;
- } //end if
- else
- {
- indent = 1;
- while(indent)
- {
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!strcmp(token.string, "{")) indent++;
- else if (!strcmp(token.string, "}")) indent--;
- } //end while
- } //end else
- } //end if
- else
- {
- SourceError(source, "unknown definition %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end else
- } //end while
- FreeSource(source);
- //
- if (!foundcharacter)
- {
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- return ch;
-} //end of the function BotLoadCharacterFromFile
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotFindCachedCharacter(char *charfile, float skill)
-{
- int handle;
-
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if ( !botcharacters[handle] ) continue;
- if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
- (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )
- {
- return handle;
- } //end if
- } //end for
- return 0;
-} //end of the function BotFindCachedCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCachedCharacter(char *charfile, float skill, int reload)
-{
- int handle, cachedhandle, intskill;
- bot_character_t *ch = NULL;
-#ifdef DEBUG
- int starttime;
-
- starttime = Sys_MilliSeconds();
-#endif //DEBUG
-
- //find a free spot for a character
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (!botcharacters[handle]) break;
- } //end for
- if (handle > MAX_CLIENTS) return 0;
- //try to load a cached character with the given skill
- if (!reload)
- {
- cachedhandle = BotFindCachedCharacter(charfile, skill);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
- return cachedhandle;
- } //end if
- } //end else
- //
- intskill = (int) (skill + 0.5);
- //try to load the character with the given skill
- ch = BotLoadCharacterFromFile(charfile, intskill);
- if (ch)
- {
- botcharacters[handle] = ch;
- //
- botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile);
-#ifdef DEBUG
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile);
- } //end if
-#endif //DEBUG
- return handle;
- } //end if
- //
- botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile);
- //
- if (!reload)
- {
- //try to load a cached default character with the given skill
- cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load the default character with the given skill
- ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile);
- return handle;
- } //end if
- //
- if (!reload)
- {
- //try to load a cached character with any skill
- cachedhandle = BotFindCachedCharacter(charfile, -1);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load a character with any skill
- ch = BotLoadCharacterFromFile(charfile, -1);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile);
- return handle;
- } //end if
- //
- if (!reload)
- {
- //try to load a cached character with any skill
- cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load a character with any skill
- ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile);
- return handle;
- } //end if
- //
- botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile);
- //couldn't load any character
- return 0;
-} //end of the function BotLoadCachedCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCharacterSkill(char *charfile, float skill)
-{
- int ch, defaultch;
-
- defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);
- ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters"));
-
- if (defaultch && ch)
- {
- BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);
- } //end if
-
- return ch;
-} //end of the function BotLoadCharacterSkill
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotInterpolateCharacters(int handle1, int handle2, float desiredskill)
-{
- bot_character_t *ch1, *ch2, *out;
- int i, handle;
- float scale;
-
- ch1 = BotCharacterFromHandle(handle1);
- ch2 = BotCharacterFromHandle(handle2);
- if (!ch1 || !ch2)
- return 0;
- //find a free spot for a character
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (!botcharacters[handle]) break;
- } //end for
- if (handle > MAX_CLIENTS) return 0;
- out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
- MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
- out->skill = desiredskill;
- strcpy(out->filename, ch1->filename);
- botcharacters[handle] = out;
-
- scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- //
- if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)
- {
- out->c[i].type = CT_FLOAT;
- out->c[i].value._float = ch1->c[i].value._float +
- (ch2->c[i].value._float - ch1->c[i].value._float) * scale;
- } //end if
- else if (ch1->c[i].type == CT_INTEGER)
- {
- out->c[i].type = CT_INTEGER;
- out->c[i].value.integer = ch1->c[i].value.integer;
- } //end else if
- else if (ch1->c[i].type == CT_STRING)
- {
- out->c[i].type = CT_STRING;
- out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);
- strcpy(out->c[i].value.string, ch1->c[i].value.string);
- } //end else if
- } //end for
- return handle;
-} //end of the function BotInterpolateCharacters
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCharacter(char *charfile, float skill)
-{
- int firstskill, secondskill, handle;
-
- //make sure the skill is in the valid range
- if (skill < 1.0) skill = 1.0;
- else if (skill > 5.0) skill = 5.0;
- //skill 1, 4 and 5 should be available in the character files
- if (skill == 1.0 || skill == 4.0 || skill == 5.0)
- {
- return BotLoadCharacterSkill(charfile, skill);
- } //end if
- //check if there's a cached skill
- handle = BotFindCachedCharacter(charfile, skill);
- if (handle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
- return handle;
- } //end if
- if (skill < 4.0)
- {
- //load skill 1 and 4
- firstskill = BotLoadCharacterSkill(charfile, 1);
- if (!firstskill) return 0;
- secondskill = BotLoadCharacterSkill(charfile, 4);
- if (!secondskill) return firstskill;
- } //end if
- else
- {
- //load skill 4 and 5
- firstskill = BotLoadCharacterSkill(charfile, 4);
- if (!firstskill) return 0;
- secondskill = BotLoadCharacterSkill(charfile, 5);
- if (!secondskill) return firstskill;
- } //end else
- //interpolate between the two skills
- handle = BotInterpolateCharacters(firstskill, secondskill, skill);
- if (!handle) return 0;
- //write the character to the log file
- BotDumpCharacter(botcharacters[handle]);
- //
- return handle;
-} //end of the function BotLoadCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int CheckCharacteristicIndex(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return qfalse;
- if (index < 0 || index >= MAX_CHARACTERISTICS)
- {
- botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index);
- return qfalse;
- } //end if
- if (!ch->c[index].type)
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function CheckCharacteristicIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Characteristic_Float(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return 0;
- //an integer will be converted to a float
- if (ch->c[index].type == CT_INTEGER)
- {
- return (float) ch->c[index].value.integer;
- } //end if
- //floats are just returned
- else if (ch->c[index].type == CT_FLOAT)
- {
- return ch->c[index].value._float;
- } //end else if
- //cannot convert a string pointer to a float
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index);
- return 0;
- } //end else if
-// return 0;
-} //end of the function Characteristic_Float
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Characteristic_BFloat(int character, int index, float min, float max)
-{
- float value;
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- if (min > max)
- {
- botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max);
- return 0;
- } //end if
- value = Characteristic_Float(character, index);
- if (value < min) return min;
- if (value > max) return max;
- return value;
-} //end of the function Characteristic_BFloat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Characteristic_Integer(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return 0;
- //an integer will just be returned
- if (ch->c[index].type == CT_INTEGER)
- {
- return ch->c[index].value.integer;
- } //end if
- //floats are casted to integers
- else if (ch->c[index].type == CT_FLOAT)
- {
- return (int) ch->c[index].value._float;
- } //end else if
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index);
- return 0;
- } //end else if
-// return 0;
-} //end of the function Characteristic_Integer
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Characteristic_BInteger(int character, int index, int min, int max)
-{
- int value;
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- if (min > max)
- {
- botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max);
- return 0;
- } //end if
- value = Characteristic_Integer(character, index);
- if (value < min) return min;
- if (value > max) return max;
- return value;
-} //end of the function Characteristic_BInteger
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Characteristic_String(int character, int index, char *buf, int size)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return;
- //an integer will be converted to a float
- if (ch->c[index].type == CT_STRING)
- {
- strncpy(buf, ch->c[index].value.string, size-1);
- buf[size-1] = '\0';
- return;
- } //end if
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index);
- return;
- } //end else if
- return;
-} //end of the function Characteristic_String
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownCharacters(void)
-{
- int handle;
-
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (botcharacters[handle])
- {
- BotFreeCharacter2(handle);
- } //end if
- } //end for
-} //end of the function BotShutdownCharacters
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_char.c + * + * desc: bot characters + * + * $Archive: /MissionPack/code/botlib/be_ai_char.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/be_ai_char.h" + +#define MAX_CHARACTERISTICS 80 + +#define CT_INTEGER 1 +#define CT_FLOAT 2 +#define CT_STRING 3 + +#define DEFAULT_CHARACTER "bots/default_c.c" + +//characteristic value +union cvalue +{ + int integer; + float _float; + char *string; +}; +//a characteristic +typedef struct bot_characteristic_s +{ + char type; //characteristic type + union cvalue value; //characteristic value +} bot_characteristic_t; + +//a bot character +typedef struct bot_character_s +{ + char filename[MAX_QPATH]; + float skill; + bot_characteristic_t c[1]; //variable sized +} bot_character_t; + +bot_character_t *botcharacters[MAX_CLIENTS + 1]; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_character_t *BotCharacterFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return NULL; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return NULL; + } //end if + return botcharacters[handle]; +} //end of the function BotCharacterFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpCharacter(bot_character_t *ch) +{ + int i; + + Log_Write("%s", ch->filename); + Log_Write("skill %d\n", ch->skill); + Log_Write("{\n"); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + switch(ch->c[i].type) + { + case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break; + case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break; + case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break; + } //end case + } //end for + Log_Write("}\n"); +} //end of the function BotDumpCharacter +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacterStrings(bot_character_t *ch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type == CT_STRING) + { + FreeMemory(ch->c[i].value.string); + } //end if + } //end for +} //end of the function BotFreeCharacterStrings +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter2(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return; + } //end if + BotFreeCharacterStrings(botcharacters[handle]); + FreeMemory(botcharacters[handle]); + botcharacters[handle] = NULL; +} //end of the function BotFreeCharacter2 +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter(int handle) +{ + if (!LibVarGetValue("bot_reloadcharacters")) return; + BotFreeCharacter2(handle); +} //end of the function BotFreeCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type) continue; + // + if (defaultch->c[i].type == CT_FLOAT) + { + ch->c[i].type = CT_FLOAT; + ch->c[i].value._float = defaultch->c[i].value._float; + } //end if + else if (defaultch->c[i].type == CT_INTEGER) + { + ch->c[i].type = CT_INTEGER; + ch->c[i].value.integer = defaultch->c[i].value.integer; + } //end else if + else if (defaultch->c[i].type == CT_STRING) + { + ch->c[i].type = CT_STRING; + ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1); + strcpy(ch->c[i].value.string, defaultch->c[i].value.string); + } //end else if + } //end for +} //end of the function BotDefaultCharacteristics +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill) +{ + int indent, index, foundcharacter; + bot_character_t *ch; + source_t *source; + token_t token; + + foundcharacter = qfalse; + //a bot character is parsed in two phases + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(charfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile); + return NULL; + } //end if + ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + strcpy(ch->filename, charfile); + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "skill")) + { + if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + //if it's the correct skill + if (skill < 0 || token.intvalue == skill) + { + foundcharacter = qtrue; + ch->skill = token.intvalue; + while(PC_ExpectAnyToken(source, &token)) + { + if (!strcmp(token.string, "}")) break; + if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) + { + SourceError(source, "expected integer index, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + index = token.intvalue; + if (index < 0 || index > MAX_CHARACTERISTICS) + { + SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (ch->c[index].type) + { + SourceError(source, "characteristic %d already initialized\n", index); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (token.type == TT_NUMBER) + { + if (token.subtype & TT_FLOAT) + { + ch->c[index].value._float = token.floatvalue; + ch->c[index].type = CT_FLOAT; + } //end if + else + { + ch->c[index].value.integer = token.intvalue; + ch->c[index].type = CT_INTEGER; + } //end else + } //end if + else if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + ch->c[index].value.string = GetMemory(strlen(token.string)+1); + strcpy(ch->c[index].value.string, token.string); + ch->c[index].type = CT_STRING; + } //end else if + else + { + SourceError(source, "expected integer, float or string, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end if + break; + } //end if + else + { + indent = 1; + while(indent) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!strcmp(token.string, "{")) indent++; + else if (!strcmp(token.string, "}")) indent--; + } //end while + } //end else + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end while + FreeSource(source); + // + if (!foundcharacter) + { + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + return ch; +} //end of the function BotLoadCharacterFromFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFindCachedCharacter(char *charfile, float skill) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if ( !botcharacters[handle] ) continue; + if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 && + (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) ) + { + return handle; + } //end if + } //end for + return 0; +} //end of the function BotFindCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCachedCharacter(char *charfile, float skill, int reload) +{ + int handle, cachedhandle, intskill; + bot_character_t *ch = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + //try to load a cached character with the given skill + if (!reload) + { + cachedhandle = BotFindCachedCharacter(charfile, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return cachedhandle; + } //end if + } //end else + // + intskill = (int) (skill + 0.5); + //try to load the character with the given skill + ch = BotLoadCharacterFromFile(charfile, intskill); + if (ch) + { + botcharacters[handle] = ch; + // + botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile); +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile); + } //end if +#endif //DEBUG + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile); + // + if (!reload) + { + //try to load a cached default character with the given skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load the default character with the given skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(charfile, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(charfile, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile); + //couldn't load any character + return 0; +} //end of the function BotLoadCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacterSkill(char *charfile, float skill) +{ + int ch, defaultch; + + defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse); + ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters")); + + if (defaultch && ch) + { + BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]); + } //end if + + return ch; +} //end of the function BotLoadCharacterSkill +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotInterpolateCharacters(int handle1, int handle2, float desiredskill) +{ + bot_character_t *ch1, *ch2, *out; + int i, handle; + float scale; + + ch1 = BotCharacterFromHandle(handle1); + ch2 = BotCharacterFromHandle(handle2); + if (!ch1 || !ch2) + return 0; + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + out->skill = desiredskill; + strcpy(out->filename, ch1->filename); + botcharacters[handle] = out; + + scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + // + if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT) + { + out->c[i].type = CT_FLOAT; + out->c[i].value._float = ch1->c[i].value._float + + (ch2->c[i].value._float - ch1->c[i].value._float) * scale; + } //end if + else if (ch1->c[i].type == CT_INTEGER) + { + out->c[i].type = CT_INTEGER; + out->c[i].value.integer = ch1->c[i].value.integer; + } //end else if + else if (ch1->c[i].type == CT_STRING) + { + out->c[i].type = CT_STRING; + out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1); + strcpy(out->c[i].value.string, ch1->c[i].value.string); + } //end else if + } //end for + return handle; +} //end of the function BotInterpolateCharacters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacter(char *charfile, float skill) +{ + int firstskill, secondskill, handle; + + //make sure the skill is in the valid range + if (skill < 1.0) skill = 1.0; + else if (skill > 5.0) skill = 5.0; + //skill 1, 4 and 5 should be available in the character files + if (skill == 1.0 || skill == 4.0 || skill == 5.0) + { + return BotLoadCharacterSkill(charfile, skill); + } //end if + //check if there's a cached skill + handle = BotFindCachedCharacter(charfile, skill); + if (handle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return handle; + } //end if + if (skill < 4.0) + { + //load skill 1 and 4 + firstskill = BotLoadCharacterSkill(charfile, 1); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 4); + if (!secondskill) return firstskill; + } //end if + else + { + //load skill 4 and 5 + firstskill = BotLoadCharacterSkill(charfile, 4); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 5); + if (!secondskill) return firstskill; + } //end else + //interpolate between the two skills + handle = BotInterpolateCharacters(firstskill, secondskill, skill); + if (!handle) return 0; + //write the character to the log file + BotDumpCharacter(botcharacters[handle]); + // + return handle; +} //end of the function BotLoadCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CheckCharacteristicIndex(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return qfalse; + if (index < 0 || index >= MAX_CHARACTERISTICS) + { + botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index); + return qfalse; + } //end if + if (!ch->c[index].type) + { + botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index); + return qfalse; + } //end if + return qtrue; +} //end of the function CheckCharacteristicIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_Float(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will be converted to a float + if (ch->c[index].type == CT_INTEGER) + { + return (float) ch->c[index].value.integer; + } //end if + //floats are just returned + else if (ch->c[index].type == CT_FLOAT) + { + return ch->c[index].value._float; + } //end else if + //cannot convert a string pointer to a float + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Float +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_BFloat(int character, int index, float min, float max) +{ + float value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max); + return 0; + } //end if + value = Characteristic_Float(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_Integer(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will just be returned + if (ch->c[index].type == CT_INTEGER) + { + return ch->c[index].value.integer; + } //end if + //floats are casted to integers + else if (ch->c[index].type == CT_FLOAT) + { + return (int) ch->c[index].value._float; + } //end else if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Integer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_BInteger(int character, int index, int min, int max) +{ + int value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max); + return 0; + } //end if + value = Characteristic_Integer(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BInteger +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Characteristic_String(int character, int index, char *buf, int size) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return; + //an integer will be converted to a float + if (ch->c[index].type == CT_STRING) + { + strncpy(buf, ch->c[index].value.string, size-1); + buf[size-1] = '\0'; + return; + } //end if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index); + return; + } //end else if + return; +} //end of the function Characteristic_String +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownCharacters(void) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (botcharacters[handle]) + { + BotFreeCharacter2(handle); + } //end if + } //end for +} //end of the function BotShutdownCharacters + |