static AIValueFunc_t *newValueFunc( pc_token_list **list ) { AIValueFunc_t *ret = nullptr; AIValueFunc_t v; pc_token_list *current = *list; pc_token_list *parenBegin = nullptr; struct AIConditionMap_s *f; memset( &v, 0, sizeof( v ) ); f = (struct AIConditionMap_s*) bsearch( current->token.string, conditionFuncs, ARRAY_LEN( conditionFuncs ), sizeof( *conditionFuncs ), cmdcmp ); if ( !f ) { Log::Warn( "Unknown function: %s on line %d", current->token.string, current->token.line ); *list = current->next; return nullptr; } v.expType = EX_FUNC; v.retType = f->retType; v.func = f->func; v.nparams = f->nparams; parenBegin = current->next; // if the function has no parameters, allow it to be used without parenthesis if ( v.nparams == 0 && parenBegin->token.string[ 0 ] != '(' ) { ret = ( AIValueFunc_t * ) BG_Alloc( sizeof( *ret ) ); memcpy( ret, &v, sizeof( *ret ) ); *list = current->next; return ret; } v.params = parseFunctionParameters( list, &v.nparams, f->nparams, f->nparams ); if ( !v.params && f->nparams > 0 ) { return nullptr; } // create the value op ret = ( AIValueFunc_t * ) BG_Alloc( sizeof( *ret ) ); // copy the members memcpy( ret, &v, sizeof( *ret ) ); return ret; }
/* ============== G_CopyString ============== */ char *G_CopyString( const char *str ) { size_t size = strlen( str ) + 1; char *cp = (char*) BG_Alloc( size ); memcpy( cp, str, size ); return cp; }
void G_namelog_connect( gclient_t *client ) { namelog_t *n, *p = NULL; int i; for( n = level.namelogs; n; p = n, n = n->next ) { if( n->slot != -1 ) continue; if( !Q_stricmp( client->pers.guid, n->guid ) ) break; } if( !n ) { n = BG_Alloc( sizeof( namelog_t ) ); strcpy( n->guid, client->pers.guid ); if( p ) p->next = n; else level.namelogs = n; } client->pers.namelog = n; n->slot = client - level.clients; n->banned = qfalse; G_namelog_update_name( client ); for( i = 0; i < MAX_NAMELOG_ADDRS && n->ip[ i ].str[ 0 ]; i++ ) if( !strcmp( n->ip[ i ].str, client->pers.ip.str ) ) return; if( i == MAX_NAMELOG_ADDRS ) i--; memcpy( &n->ip[ i ], &client->pers.ip, sizeof( n->ip[ i ] ) ); }
/* ============= G_NewString Builds a copy of the string, translating \n to real linefeeds so message texts can be multi-line ============= */ char *G_NewString( const char *string ) { char *newb, *new_p; int i, l; l = strlen( string ) + 1; newb = BG_Alloc( l ); new_p = newb; // turn \n into a real linefeed for ( i = 0; i < l; i++ ) { if ( string[ i ] == '\\' && i < l - 1 ) { i++; if ( string[ i ] == 'n' ) { *new_p++ = '\n'; } else { *new_p++ = '\\'; } } else { *new_p++ = string[ i ]; } } return newb; }
/* ============= G_NewTarget ============= */ gentityCallDefinition_t G_NewCallDefinition( char *eventKey, const char *string ) { char *stringPointer; int i, stringLength; gentityCallDefinition_t newCallDefinition = { NULL, ON_DEFAULT, NULL, NULL, ECA_NOP }; stringLength = strlen( string ) + 1; if(stringLength == 1) return newCallDefinition; stringPointer = BG_Alloc( stringLength ); newCallDefinition.name = stringPointer; for ( i = 0; i < stringLength; i++ ) { if ( string[ i ] == ':' && !newCallDefinition.action ) { *stringPointer++ = '\0'; newCallDefinition.action = stringPointer; continue; } *stringPointer++ = string[ i ]; } newCallDefinition.actionType = G_GetCallActionTypeFor( newCallDefinition.action ); newCallDefinition.event = eventKey; newCallDefinition.eventType = G_GetCallEventTypeFor( newCallDefinition.event ); return newCallDefinition; }
pc_token_list *CreateTokenList( int handle ) { pc_token_t token; char filename[ MAX_QPATH ]; pc_token_list *current = nullptr; pc_token_list *root = nullptr; while ( trap_Parse_ReadToken( handle, &token ) ) { pc_token_list *list = ( pc_token_list * ) BG_Alloc( sizeof( pc_token_list ) ); if ( current ) { list->prev = current; current->next = list; } else { list->prev = list; root = list; } current = list; current->next = nullptr; current->token.floatvalue = token.floatvalue; current->token.intvalue = token.intvalue; current->token.subtype = token.subtype; current->token.type = token.type; current->token.string = BG_strdup( token.string ); trap_Parse_SourceFileAndLine( handle, filename, ¤t->token.line ); } return root; }
void *B_Alloc(int size) { #ifdef BOT_ZMALLOC void *ptr = NULL; int i = 0; #ifdef BOTMEMTRACK int free = 0; int used = 0; while (i < MAX_BALLOC) { if (!BAllocList[i]) { free++; } else { used++; } i++; } trap->Print("Allocations used: %i\nFree allocation slots: %i\n", used, free); i = 0; #endif ptr = trap->BotGetMemoryGame(size); while (i < MAX_BALLOC) { if (!BAllocList[i]) { BAllocList[i] = ptr; break; } i++; } if (i == MAX_BALLOC) { //If this happens we'll have to rely on this chunk being freed manually with B_Free, which it hopefully will be #ifdef DEBUG trap->Print("WARNING: MAXIMUM B_ALLOC ALLOCATIONS EXCEEDED\n"); #endif } return ptr; #else return BG_Alloc(size); #endif }
/* =============== G_ParseInfos =============== */ int G_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "<NULL>" ); } Info_SetValueForKey( info, key, token ); } if(!BG_CanAlloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1)) break; //Not enough memory. Don't even try //NOTE: extra space for arena number //KK-OAX Changed to Tremulous's BG_Alloc infos[count] = BG_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); if (infos[count]) { strcpy(infos[count], info); count++; } } return count; }
static AIOp_t *newOp( pc_token_list *list ) { pc_token_list *current = list; AIOp_t *ret = nullptr; AIOpType_t op = opTypeFromToken( ¤t->token ); if ( isBinaryOp( op ) ) { AIBinaryOp_t *b = ( AIBinaryOp_t * ) BG_Alloc( sizeof( *b ) ); b->opType = op; ret = ( AIOp_t * ) b; } else if ( isUnaryOp( op ) ) { AIUnaryOp_t *u = ( AIUnaryOp_t * ) BG_Alloc( sizeof( *u ) ); u->opType = op; ret = ( AIOp_t * ) u; } return ret; }
static AIValue_t *newValueLiteral( pc_token_list **list ) { AIValue_t *ret; pc_token_list *current = *list; pc_token_stripped_t *token = ¤t->token; ret = ( AIValue_t * ) BG_Alloc( sizeof( *ret ) ); *ret = AIBoxToken( token ); *list = current->next; return ret; }
void AddTreeToList( AITreeList_t *list, AIBehaviorTree_t *tree ) { if ( list->maxTrees == list->numTrees ) { AIBehaviorTree_t **trees = ( AIBehaviorTree_t ** ) BG_Alloc( sizeof( AIBehaviorTree_t * ) * list->maxTrees ); list->maxTrees *= 2; memcpy( trees, list->trees, sizeof( AIBehaviorTree_t * ) * list->numTrees ); BG_Free( list->trees ); list->trees = trees; } list->trees[ list->numTrees ] = tree; list->numTrees++; }
// Create/Allocate a new Animal Vehicle (initializing it as well). //this is a BG function too in MP so don't un-bg-compatibilify it -rww void G_CreateWalkerNPC( Vehicle_t **pVeh, const char *strAnimalType ) { // Allocate the Vehicle. #ifdef PROJECT_GAME //these will remain on entities on the client once allocated because the pointer is //never stomped. on the server, however, when an ent is freed, the entity struct is //memset to 0, so this memory would be lost.. G_AllocateVehicleObject( pVeh ); #else if ( !*pVeh ) { //only allocate a new one if we really have to (*pVeh) = (Vehicle_t *)BG_Alloc( sizeof(Vehicle_t) ); } #endif memset( *pVeh, 0, sizeof(Vehicle_t) ); (*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )]; }
void G_namelog_connect( gclient_t *client ) { namelog_t *n, *p = NULL; int i; char *newname; for( n = level.namelogs; n; p = n, n = n->next ) { if( n->slot != -1 ) continue; if( !Q_stricmp( client->pers.guid, n->guid ) ) break; } if( !n ) { n = BG_Alloc( sizeof( namelog_t ) ); strcpy( n->guid, client->pers.guid ); n->guidless = client->pers.guidless; if( p ) { p->next = n; n->id = p->id + 1; } else { level.namelogs = n; n->id = MAX_CLIENTS; } } client->pers.namelog = n; n->slot = client - level.clients; n->banned = qfalse; newname = n->name[ n->nameOffset ]; // If they're muted, copy in their last known name - this will stop people // reconnecting to get around the name change protection. if( n->muted && G_admin_name_check( &g_entities[ n->slot ], newname, NULL, 0 ) ) Q_strncpyz( client->pers.netname, newname, MAX_NAME_LENGTH ); for( i = 0; i < MAX_NAMELOG_ADDRS && n->ip[ i ].str[ 0 ]; i++ ) if( !strcmp( n->ip[ i ].str, client->pers.ip.str ) ) return; if( i == MAX_NAMELOG_ADDRS ) i--; memcpy( &n->ip[ i ], &client->pers.ip, sizeof( n->ip[ i ] ) ); }
int G_BotAddNames( team_t team, int arg, int last ) { int i = botNames[team].count; int added = 0; char name[MAX_NAME_LENGTH]; while ( arg < last && i < MAX_CLIENTS ) { int j, t; trap_Argv( arg++, name, sizeof( name ) ); // name already in the list? (quick check, including colours & invalid) for ( t = 1; t < NUM_TEAMS; ++t ) { for ( j = 0; j < botNames[t].count; ++j ) { if ( !Q_stricmp( botNames[t].name[j].name, name ) ) { goto next; } } } botNames[team].name[i].name = ( char * )BG_Alloc( strlen( name ) + 1 ); strcpy( botNames[team].name[i].name, name ); botNames[team].count = ++i; ++added; next: ; } return added; }
/** * Add one bot into a team * @param name [string] bot's name * @param team [team_t] alien or human */ qboolean G_BotAdd( char *name, team_t team ) { int i; int clientNum; char userinfo[MAX_INFO_STRING]; int reservedSlots = 0; gentity_t *ent; namelog_t *namelog; char name_s[ MAX_NAME_LENGTH ]; char name_tmp_s[ MAX_NAME_LENGTH ] = ""; reservedSlots = trap_Cvar_VariableIntegerValue( "sv_privateclients" ); //If bot name does not contains [BOT], prepend it. if(g_bot_tagname.integer == 1) { G_DecolorString(name, name_s, MAX_NAME_LENGTH); if (! (Com_StringContains(name_s, "[BOT]", 0))) { if(team == TEAM_HUMANS) { strcat(name_tmp_s, "^4[BOT] "); } else if(team == TEAM_ALIENS) { strcat(name_tmp_s, "^1[BOT] "); } strcat(name_tmp_s, name); strcpy(name, name_tmp_s); } } // LEPE: check if no player/bot exists with that name G_SanitiseString(name, name_s, sizeof(name_s) ); for( namelog = level.namelogs; namelog; namelog = namelog->next ) { if( namelog->slot >= 0 ) { for( i = 0; i < MAX_NAMELOG_NAMES && namelog->name[ i ][ 0 ]; i++ ) { G_SanitiseString(namelog->name[ i ], name_tmp_s, sizeof(name_tmp_s) ); if( i == namelog->nameOffset && namelog->slot > -1 && !strcmp( name_s, name_tmp_s ) ) { trap_Print("Nick already exists\n"); return qfalse; } } } } // find what clientNum to use for bot // LEPE: clientNum calculation was modified to prevent player hijacking // We will assign slots from maxclients - 1 to maxclients - reservedSlots - 1 clientNum = -1; for( i = level.maxclients - 1; i > level.maxclients - reservedSlots - 1; i-- ) { if( !g_entities[i].inuse ) { clientNum = i; break; } } if(clientNum == -1) { trap_Print("Error: Bots were not assigned correctly\n"); //LEPE return qfalse; } if(clientNum < level.maxclients - reservedSlots - 1) { trap_Print("no more slots for bot\n"); return qfalse; } ent = &g_entities[ clientNum ]; ent->inuse = qtrue; ent->bot = (bot_t *)BG_Alloc( sizeof(bot_t) ); // ent->bot->path.crumb = BG_Alloc( sizeof(level.paths) ); ent->r.svFlags |= SVF_BOT; // register user information userinfo[0] = '\0'; Info_SetValueForKey( userinfo, "name", name ); Info_SetValueForKey( userinfo, "rate", "25000" ); //25000 Info_SetValueForKey( userinfo, "snaps", "40" ); //so we can connect if server is password protected if(g_needpass.integer == 1) { Info_SetValueForKey( userinfo, "password", g_password.string); } trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if(ClientConnect(clientNum, qtrue) != NULL ) { G_Printf("Something weird happened. Bot was not added."); // won't let us join return qfalse; } ClientBegin( clientNum ); G_BotDebug(ent, BOT_VERB_IMPORTANT, BOT_DEBUG_GENERAL, "Bot Added\n"); G_ChangeTeam( ent, team ); BotInit( ent ); if(team == TEAM_HUMANS) { BotInitHuman( ent ); level.humanBots++; } else if(team == TEAM_ALIENS) { BotInitAlien( ent ); level.alienBots++; } //TODO: load profile return qtrue; }
/* ============ BG_VoiceParseCommand ============ */ static voiceTrack_t *BG_VoiceParseCommand( int handle ) { pc_token_t token; qboolean parsingTrack = qfalse; voiceTrack_t *voiceTracks = NULL; voiceTrack_t *top = NULL; while ( trap_Parse_ReadToken( handle, &token ) ) { if ( !parsingTrack && token.string[ 0 ] == '}' ) { return top; } if ( parsingTrack ) { if ( token.string[ 0 ] == '{' ) { BG_VoiceParseTrack( handle, voiceTracks ); parsingTrack = qfalse; continue; } else { BG_VoiceParseError( handle, va( "BG_VoiceParseCommand(): " "parse error at \"%s\"", token.string ) ); } } if ( top == NULL ) { voiceTracks = BG_Alloc( sizeof( voiceTrack_t ) ); top = voiceTracks; } else { voiceTracks->next = BG_Alloc( sizeof( voiceCmd_t ) ); voiceTracks = voiceTracks->next; } if ( !trap_FS_FOpenFile( token.string, NULL, FS_READ ) ) { int line; char filename[ MAX_QPATH ]; trap_Parse_SourceFileAndLine( handle, filename, &line ); Com_Printf( S_WARNING "BG_VoiceParseCommand(): " "track \"%s\" referenced on line %d of %s does not exist\n", token.string, line, filename ); } else { #ifdef CGAME voiceTracks->track = trap_S_RegisterSound( token.string, qfalse ); voiceTracks->duration = 0; // FIXME: Was always zero... #endif } voiceTracks->team = -1; voiceTracks->pClass = -1; voiceTracks->weapon = -1; voiceTracks->enthusiasm = 0; voiceTracks->text = NULL; voiceTracks->next = NULL; parsingTrack = qtrue; } return NULL; }
/* ============ BG_VoiceParse ============ */ static voiceCmd_t *BG_VoiceParse( const char *name ) { voiceCmd_t *voiceCmds = NULL; voiceCmd_t *top = NULL; pc_token_t token; qboolean parsingCmd = qfalse; int handle; handle = trap_Parse_LoadSource( va( "voice/%s.voice", name ) ); if ( !handle ) { return NULL; } while ( trap_Parse_ReadToken( handle, &token ) ) { if ( parsingCmd ) { if ( token.string[ 0 ] == '{' ) { voiceCmds->tracks = BG_VoiceParseCommand( handle ); parsingCmd = qfalse; continue; } else { int line; char filename[ MAX_QPATH ]; trap_Parse_SourceFileAndLine( handle, filename, &line ); Com_Error( ERR_FATAL, "BG_VoiceParse(): " "parse error on line %d of %s", line, filename ); } } if ( strlen( token.string ) >= MAX_VOICE_CMD_LEN ) { int line; char filename[ MAX_QPATH ]; trap_Parse_SourceFileAndLine( handle, filename, &line ); Com_Error( ERR_FATAL, "BG_VoiceParse(): " "command \"%s\" exceeds MAX_VOICE_CMD_LEN (%d) on line %d of %s", token.string, MAX_VOICE_CMD_LEN, line, filename ); } if ( top == NULL ) { voiceCmds = BG_Alloc( sizeof( voiceCmd_t ) ); top = voiceCmds; } else { voiceCmds->next = BG_Alloc( sizeof( voiceCmd_t ) ); voiceCmds = voiceCmds->next; } Q_strncpyz( voiceCmds->cmd, token.string, sizeof( voiceCmds->cmd ) ); voiceCmds->next = NULL; parsingCmd = qtrue; } trap_Parse_FreeSource( handle ); return top; }
/* ============ BG_VoiceList ============ */ static voice_t *BG_VoiceList( void ) { char fileList[ MAX_VOICES * ( MAX_VOICE_NAME_LEN + 6 ) ] = { "" }; int numFiles, i, fileLen = 0; int count = 0; char *filePtr; voice_t *voices = NULL; voice_t *top = NULL; numFiles = trap_FS_GetFileList( "voice", ".voice", fileList, sizeof( fileList ) ); if ( numFiles < 1 ) { return NULL; } // special case for default.voice. this file is REQUIRED and will // always be loaded first in the event of overflow of voice definitions if ( !trap_FS_FOpenFile( "voice/default.voice", NULL, FS_READ ) ) { Com_Printf( "voice/default.voice missing, voice system disabled.\n" ); return NULL; } voices = ( voice_t * ) BG_Alloc( sizeof( voice_t ) ); Q_strncpyz( voices->name, "default", sizeof( voices->name ) ); voices->cmds = NULL; voices->next = NULL; count = 1; top = voices; filePtr = fileList; for ( i = 0; i < numFiles; i++, filePtr += fileLen + 1 ) { fileLen = strlen( filePtr ); // accounted for above if ( !Q_stricmp( filePtr, "default.voice" ) ) { continue; } if ( fileLen >= MAX_VOICE_NAME_LEN + 6 ) { Com_Printf( S_WARNING "MAX_VOICE_NAME_LEN is %d. " "skipping \"%s\", filename too long\n", MAX_VOICE_NAME_LEN, filePtr ); continue; } // trap_FS_GetFileList() buffer has overflowed if ( !trap_FS_FOpenFile( va( "voice/%s", filePtr ), NULL, FS_READ ) ) { Com_Printf( S_WARNING "BG_VoiceList(): detected " "an invalid .voice file \"%s\" in directory listing. You have " "probably named one or more .voice files with outrageously long " "names.\n", filePtr ); break; } if ( count >= MAX_VOICES ) { Com_Printf( S_WARNING ".voice file overflow. " "%d of %d .voice files loaded. MAX_VOICES is %d\n", count, numFiles, MAX_VOICES ); break; } voices->next = ( voice_t * ) BG_Alloc( sizeof( voice_t ) ); voices = voices->next; Q_strncpyz( voices->name, filePtr, sizeof( voices->name ) ); // strip extension voices->name[ fileLen - 6 ] = '\0'; voices->cmds = NULL; voices->next = NULL; count++; } return top; }
/* =============== G_AllocateNode Allocate memory for a node_t =============== */ static node_t *G_AllocateNode( void ) { node_t *node = BG_Alloc( sizeof( node_t ) ); return node; }
/* =============== CG_ParseInfos =============== */ int CG_ParseInfos( const char *buf, int max, char *infos[] ) { char *token; int count; char key[ MAX_TOKEN_CHARS ]; char info[ MAX_INFO_STRING ]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[ 0 ] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[ 0 ] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, true ); if ( !token[ 0 ] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, false ); if ( !token[ 0 ] ) { strcpy( token, "<NULL>" ); } Info_SetValueForKey( info, key, token, false ); } //NOTE: extra space for arena number infos[ count ] = (char*) BG_Alloc( strlen( info ) + strlen( "\\num\\" ) + strlen( va( "%d", MAX_ARENAS ) ) + 1 ); if ( infos[ count ] ) { strcpy( infos[ count ], info ); count++; } } return count; }
static AIValue_t *parseFunctionParameters( pc_token_list **list, int *nparams, int minparams, int maxparams ) { pc_token_list *current = *list; pc_token_list *parenBegin = current->next; pc_token_list *parenEnd; pc_token_list *parse; AIValue_t *params = nullptr; int numParams = 0; // functions should always be proceeded by a '(' if they have parameters if ( !expectToken( "(", &parenBegin, false ) ) { *list = current; return nullptr; } // find the end parenthesis around the function's args parenEnd = findCloseParen( parenBegin, nullptr ); if ( !parenEnd ) { Log::Warn( "could not find matching ')' for '(' on line %d", parenBegin->token.line ); *list = parenBegin->next; return nullptr; } // count the number of parameters parse = parenBegin->next; while ( parse != parenEnd ) { if ( parse->token.type == tokenType_t::TT_NUMBER || parse->token.type == tokenType_t::TT_STRING ) { numParams++; } else if ( parse->token.string[ 0 ] != ',' ) { Log::Warn( "Invalid token %s in parameter list on line %d", parse->token.string, parse->token.line ); *list = parenEnd->next; // skip invalid function expression return nullptr; } parse = parse->next; } // warn if too many or too few parameters if ( numParams < minparams ) { Log::Warn( "too few parameters for %s on line %d", current->token.string, current->token.line ); *list = parenEnd->next; return nullptr; } if ( numParams > maxparams ) { Log::Warn( "too many parameters for %s on line %d", current->token.string, current->token.line ); *list = parenEnd->next; return nullptr; } *nparams = numParams; if ( numParams ) { // add the parameters params = ( AIValue_t * ) BG_Alloc( sizeof( *params ) * numParams ); numParams = 0; parse = parenBegin->next; while ( parse != parenEnd ) { if ( parse->token.type == tokenType_t::TT_NUMBER || parse->token.type == tokenType_t::TT_STRING ) { params[ numParams ] = AIBoxToken( &parse->token ); numParams++; } parse = parse->next; } } *list = parenEnd->next; return params; }
/* ====================== ReadBehaviorTree Load a behavior tree of the given name from a file ====================== */ AIBehaviorTree_t *ReadBehaviorTree( const char *name, AITreeList_t *list ) { int i; char treefilename[ MAX_QPATH ]; int handle; pc_token_list *tokenlist; AIBehaviorTree_t *tree; pc_token_list *current; AIGenericNode_t *node; currentList = list; // check if this behavior tree has already been loaded for ( i = 0; i < list->numTrees; i++ ) { AIBehaviorTree_t *tree = list->trees[ i ]; if ( !Q_stricmp( tree->name, name ) ) { return tree; } } // add preprocessor defines for use in the behavior tree // add upgrades D( UP_LIGHTARMOUR ); D( UP_MEDIUMARMOUR ); D( UP_BATTLESUIT ); D( UP_RADAR ); D( UP_JETPACK ); D( UP_GRENADE ); D( UP_MEDKIT ); // add weapons D( WP_MACHINEGUN ); D( WP_PAIN_SAW ); D( WP_SHOTGUN ); D( WP_LAS_GUN ); D( WP_MASS_DRIVER ); D( WP_CHAINGUN ); D( WP_FLAMER ); D( WP_PULSE_RIFLE ); D( WP_LUCIFER_CANNON ); D( WP_HBUILD ); // add teams D( TEAM_ALIENS ); D( TEAM_HUMANS ); D( TEAM_NONE ); // add AIEntitys D( E_NONE ); D( E_A_SPAWN ); D( E_A_OVERMIND ); D( E_A_BARRICADE ); D( E_A_ACIDTUBE ); D( E_A_TRAPPER ); D( E_A_BOOSTER ); D( E_A_HIVE ); D( E_A_LEECH ); D( E_H_SPAWN ); D( E_H_MGTURRET ); D( E_H_ROCKETPOD ); D( E_H_ARMOURY ); D( E_H_MEDISTAT ); D( E_H_DRILL ); D( E_H_REACTOR ); D( E_H_REPEATER ); D( E_GOAL ); D( E_ENEMY ); D( E_DAMAGEDBUILDING ); D( E_SELF ); // add player classes D( PCL_NONE ); D( PCL_ALIEN_BUILDER0 ); D( PCL_ALIEN_BUILDER0_UPG ); D( PCL_ALIEN_LEVEL0 ); D( PCL_ALIEN_LEVEL1 ); D( PCL_ALIEN_LEVEL2 ); D( PCL_ALIEN_LEVEL2_UPG ); D( PCL_ALIEN_LEVEL3 ); D( PCL_ALIEN_LEVEL3_UPG ); D( PCL_ALIEN_LEVEL4 ); D( PCL_HUMAN_NAKED ); D( PCL_HUMAN_LIGHT ); D( PCL_HUMAN_MEDIUM ); D( PCL_HUMAN_BSUIT ); D( MOVE_FORWARD ); D( MOVE_BACKWARD ); D( MOVE_RIGHT ); D( MOVE_LEFT ); D2( ET_BUILDABLE, Util::ordinal(entityType_t::ET_BUILDABLE) ); // node return status D( STATUS_RUNNING ); D( STATUS_SUCCESS ); D( STATUS_FAILURE ); D( SAY_ALL ); D( SAY_TEAM ); D( SAY_AREA ); D( SAY_AREA_TEAM ); Q_strncpyz( treefilename, va( "bots/%s.bt", name ), sizeof( treefilename ) ); handle = trap_Parse_LoadSource( treefilename ); if ( !handle ) { Log::Warn( "Cannot load behavior tree %s: File not found", treefilename ); return nullptr; } tokenlist = CreateTokenList( handle ); trap_Parse_FreeSource( handle ); tree = ( AIBehaviorTree_t * ) BG_Alloc( sizeof( AIBehaviorTree_t ) ); Q_strncpyz( tree->name, name, sizeof( tree->name ) ); tree->run = BotBehaviorNode; tree->type = BEHAVIOR_NODE; AddTreeToList( list, tree ); current = tokenlist; node = ReadNode( ¤t ); if ( node ) { tree->root = node; } else { RemoveTreeFromList( list, tree ); tree = nullptr; } FreeTokenList( tokenlist ); return tree; }
/* ============ BG_VoiceParseTrack ============ */ static qboolean BG_VoiceParseTrack( int handle, voiceTrack_t *voiceTrack ) { pc_token_t token; qboolean found = qfalse; qboolean foundText = qfalse; qboolean foundToken = qfalse; foundToken = trap_Parse_ReadToken( handle, &token ); while ( foundToken ) { if ( token.string[ 0 ] == '}' ) { if ( foundText ) { return qtrue; } else { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): " "missing text attribute for track" ); } } else if ( !Q_stricmp( token.string, "team" ) ) { foundToken = trap_Parse_ReadToken( handle, &token ); found = qfalse; while ( foundToken ) { if ( voiceTrack->team < 0 ) { voiceTrack->team = 0; } if ( !Q_stricmp( token.string, "humans" ) ) { voiceTrack->team |= 1 << TEAM_HUMANS; } else if ( !Q_stricmp( token.string, "aliens" ) ) { voiceTrack->team |= 1 << TEAM_ALIENS; } else { break; } found = qtrue; foundToken = trap_Parse_ReadToken( handle, &token ); } if ( !found ) { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): missing \"team\" name" ); } continue; } else if ( !Q_stricmp( token.string, "class" ) ) { qboolean negate = qfalse; foundToken = trap_Parse_ReadToken( handle, &token ); found = qfalse; while ( foundToken ) { classModelConfig_t *model; int modelno = -1; if ( voiceTrack->pClass < 0 ) { voiceTrack->pClass = 0; } if ( !Q_stricmp( token.string, "all" ) ) { modelno = PCL_ALL_CLASSES; } else if ( !Q_stricmp( token.string, "humans" ) ) { modelno = PCL_HUMAN_CLASSES; } else if ( !Q_stricmp( token.string, "aliens" ) ) { modelno = PCL_ALIEN_CLASSES; } else if ( !Q_stricmp( token.string, "-" ) ) // this must be outside quotation marks { negate = qtrue; modelno = 0; } else { model = BG_ClassModelConfigByName( token.string ); if ( model != BG_ClassModelConfigByName( NULL ) ) { modelno = 1 << ( model - BG_ClassModelConfig( 0 ) ); if ( modelno <= 1) { modelno = -1; // match failure } } } if ( modelno > 0 ) { if ( negate ) { negate = qfalse; voiceTrack->pClass &= ~modelno; } else { voiceTrack->pClass |= modelno; } } else if ( modelno < 0 ) { break; // possibly the next keyword } found = qtrue; foundToken = trap_Parse_ReadToken( handle, &token ); } if ( !found ) { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): missing \"class\" name" ); } continue; } else if ( !Q_stricmp( token.string, "text" ) ) { if ( foundText ) { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): " "duplicate \"text\" definition for track" ); } foundToken = trap_Parse_ReadToken( handle, &token ); if ( !foundToken ) { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): " "missing \"text\" value" ); } foundText = qtrue; if ( strlen( token.string ) >= MAX_SAY_TEXT ) { BG_VoiceParseError( handle, va( "BG_VoiceParseTrack(): " "\"text\" value " "\"%s\" exceeds MAX_SAY_TEXT length", token.string ) ); } voiceTrack->text = ( char * ) BG_Alloc( strlen( token.string ) + 1 ); Q_strncpyz( voiceTrack->text, token.string, strlen( token.string ) + 1 ); foundToken = trap_Parse_ReadToken( handle, &token ); continue; } else if ( !Q_stricmp( token.string, "enthusiasm" ) ) { foundToken = trap_Parse_ReadToken( handle, &token ); if ( token.type == TT_NUMBER ) { voiceTrack->enthusiasm = token.intvalue; } else { BG_VoiceParseError( handle, "BG_VoiceParseTrack(): " "missing \"enthusiasm\" value" ); } foundToken = trap_Parse_ReadToken( handle, &token ); continue; } else { BG_VoiceParseError( handle, va( "BG_VoiceParseTrack():" " unknown token \"%s\"", token.string ) ); } } return qfalse; }
// functions for keeping a list of behavior trees loaded void InitTreeList( AITreeList_t *list ) { list->trees = ( AIBehaviorTree_t ** ) BG_Alloc( sizeof( AIBehaviorTree_t * ) * 10 ); list->maxTrees = 10; list->numTrees = 0; }