mirror of
https://github.com/id-Software/Enemy-Territory.git
synced 2026-03-20 00:49:36 +01:00
768 lines
24 KiB
C
768 lines
24 KiB
C
/*
|
||
===========================================================================
|
||
|
||
Wolfenstein: Enemy Territory GPL Source Code
|
||
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
|
||
|
||
This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).
|
||
|
||
Wolf ET 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 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
Wolf ET 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 Wolf ET Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below.
|
||
|
||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||
|
||
===========================================================================
|
||
*/
|
||
|
||
|
||
/*****************************************************************************
|
||
* name: be_ai_char.c
|
||
*
|
||
* desc: bot characters
|
||
*
|
||
*
|
||
*****************************************************************************/
|
||
|
||
#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];
|
||
int 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
|
||
PS_SetBaseFolder( "botfiles" );
|
||
source = LoadSourceFile( charfile );
|
||
PS_SetBaseFolder( "" );
|
||
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, int skill ) {
|
||
int handle;
|
||
|
||
for ( handle = 1; handle <= MAX_CLIENTS; handle++ )
|
||
{
|
||
if ( !botcharacters[handle] ) {
|
||
continue;
|
||
}
|
||
if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
|
||
( skill < 0 || botcharacters[handle]->skill == skill ) ) {
|
||
return handle;
|
||
} //end if
|
||
} //end for
|
||
return 0;
|
||
} //end of the function BotFindCachedCharacter
|
||
//===========================================================================
|
||
//
|
||
// Parameter: -
|
||
// Returns: -
|
||
// Changes Globals: -
|
||
//===========================================================================
|
||
int BotLoadCachedCharacter( char *charfile, int skill, int reload ) {
|
||
int handle, cachedhandle;
|
||
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 %d from %s\n", skill, charfile);
|
||
return cachedhandle;
|
||
} //end if
|
||
} //end else
|
||
//try to load the character with the given skill
|
||
ch = BotLoadCharacterFromFile( charfile, skill );
|
||
if ( ch ) {
|
||
botcharacters[handle] = ch;
|
||
//
|
||
//botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", skill, charfile);
|
||
#ifdef DEBUG
|
||
if ( bot_developer ) {
|
||
botimport.Print( PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", skill, Sys_MilliSeconds() - starttime, charfile );
|
||
} //end if
|
||
#endif //DEBUG
|
||
return handle;
|
||
} //end if
|
||
//
|
||
botimport.Print( PRT_WARNING, "couldn't find skill %d in %s\n", skill, charfile );
|
||
//
|
||
if ( !reload ) {
|
||
//try to load a cached default character with the given skill
|
||
cachedhandle = BotFindCachedCharacter( "bots/default_c.c", skill );
|
||
if ( cachedhandle ) {
|
||
botimport.Print( PRT_MESSAGE, "loaded cached default skill %d from %s\n", skill, charfile );
|
||
return cachedhandle;
|
||
} //end if
|
||
} //end if
|
||
//try to load the default character with the given skill
|
||
ch = BotLoadCharacterFromFile( DEFAULT_CHARACTER, skill );
|
||
if ( ch ) {
|
||
botcharacters[handle] = ch;
|
||
//botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", skill, 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 %d 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 %d 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 %d 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 %d 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, int 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, int 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 - 1 ) / ( 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, int skill ) {
|
||
int skill1, skill4, handle;
|
||
|
||
//make sure the skill is in the valid range
|
||
if ( skill < 1 ) {
|
||
skill = 1;
|
||
} else if ( skill > 5 ) {
|
||
skill = 5;
|
||
}
|
||
//skill 1, 4 and 5 should be available in the character files
|
||
if ( skill == 1 || skill == 4 || skill == 5 ) {
|
||
return BotLoadCharacterSkill( charfile, skill );
|
||
} //end if
|
||
//check if there's a cached skill 2 or 3
|
||
handle = BotFindCachedCharacter( charfile, skill );
|
||
if ( handle ) {
|
||
//botimport.Print(PRT_MESSAGE, "loaded cached skill %d from %s\n", skill, charfile);
|
||
return handle;
|
||
} //end if
|
||
//load skill 1 and 4
|
||
skill1 = BotLoadCharacterSkill( charfile, 1 );
|
||
if ( !skill1 ) {
|
||
return 0;
|
||
}
|
||
skill4 = BotLoadCharacterSkill( charfile, 4 );
|
||
if ( !skill4 ) {
|
||
return skill1;
|
||
}
|
||
//interpolate between 1 and 4 to create skill 2 or 3
|
||
handle = BotInterpolateCharacters( skill1, skill4, 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
|