void AICast_ScriptParse( cast_state_t *cs ) { #define MAX_SCRIPT_EVENTS 64 gentity_t *ent; char *pScript; char *token; qboolean wantName; qboolean inScript; int eventNum; cast_script_event_t events[MAX_SCRIPT_EVENTS]; int numEventItems; cast_script_event_t *curEvent; char params[MAX_QPATH]; cast_script_stack_action_t *action; int i; int bracketLevel; qboolean buildScript; //----(SA) added if ( !level.scriptAI ) { return; } ent = &g_entities[cs->entityNum]; if ( !ent->aiName ) { return; } buildScript = trap_Cvar_VariableIntegerValue( "com_buildScript" ); buildScript = qtrue; pScript = level.scriptAI; wantName = qtrue; inScript = qfalse; COM_BeginParseSession( "AICast_ScriptParse" ); bracketLevel = 0; numEventItems = 0; memset( events, 0, sizeof( events ) ); while ( 1 ) { token = COM_Parse( &pScript ); if ( !token[0] ) { if ( !wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } break; } // end of script if ( token[0] == '}' ) { if ( inScript ) { break; } if ( wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine() ); } wantName = qtrue; } else if ( token[0] == '{' ) { if ( wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine() ); } } else if ( wantName ) { if ( !Q_strcasecmp( ent->aiName, token ) ) { inScript = qtrue; numEventItems = 0; } wantName = qfalse; } else if ( inScript ) { if ( !Q_strcasecmp( token, "attributes" ) ) { // read in all the attributes AICast_CheckLevelAttributes( cs, ent, &pScript ); continue; } eventNum = AICast_EventForString( token ); if ( eventNum < 0 ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token ); } if ( numEventItems >= MAX_SCRIPT_EVENTS ) { G_Error( "AICast_ScriptParse(), Error (line %d): MAX_SCRIPT_EVENTS reached (%d)\n", COM_GetCurrentParseLine(), MAX_SCRIPT_EVENTS ); } // if this is a "friendlysightcorpse" event, then disable corpse vis sharing if ( !Q_stricmp( token, "friendlysightcorpse" ) ) { cs->aiFlags &= ~AIFL_CORPSESIGHTING; } curEvent = &events[numEventItems]; curEvent->eventNum = eventNum; memset( params, 0, sizeof( params ) ); // parse any event params before the start of this event's actions while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '{' ) ) { if ( !token[0] ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } if ( eventNum == 13 ) { // statechange event, check params if ( strlen( token ) > 1 ) { if ( BG_IndexForString( token, animStateStr, qtrue ) < 0 ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown state type '%s'.\n", COM_GetCurrentParseLine(), token ); } } } if ( strlen( params ) ) { // add a space between each param Q_strcat( params, sizeof( params ), " " ); } Q_strcat( params, sizeof( params ), token ); } if ( strlen( params ) ) { // copy the params into the event curEvent->params = G_Alloc( strlen( params ) + 1 ); Q_strncpyz( curEvent->params, params, strlen( params ) + 1 ); } // parse the actions for this event while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '}' ) ) { if ( !token[0] ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } action = AICast_ActionForString( cs, token ); if ( !action ) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token ); } curEvent->stack.items[curEvent->stack.numItems].action = action; memset( params, 0, sizeof( params ) ); token = COM_ParseExt( &pScript, qfalse ); for ( i = 0; token[0]; i++ ) { if ( strlen( params ) ) { // add a space between each param Q_strcat( params, sizeof( params ), " " ); } if ( i == 0 ) { // Special case: playsound's need to be cached on startup to prevent in-game pauses if ( !Q_stricmp( action->actionString, "playsound" ) ) { G_SoundIndex( token ); } //----(SA) added a bit more if ( buildScript && ( !Q_stricmp( action->actionString, "mu_start" ) || !Q_stricmp( action->actionString, "mu_play" ) || !Q_stricmp( action->actionString, "mu_queue" ) || !Q_stricmp( action->actionString, "startcam" ) || !Q_stricmp( action->actionString, "startcamblack" ) ) ) { if ( strlen( token ) ) { // we know there's a [0], but don't know if it's '0' trap_SendServerCommand( cs->entityNum, va( "addToBuild %s\n", token ) ); } } if ( !Q_stricmp( action->actionString, "giveweapon" ) ) { // register weapon for client pre-loading gitem_t *weap = BG_FindItem2( token ); // (SA) FIXME: rats, need to fix this for weapon names with spaces: 'mauser rifle' // if(weap) RegisterItem( weap ); // don't be nice, just do it. if it can't find it, you'll bomb out to the error menu } //----(SA) end } if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word Q_strcat( params, sizeof( params ), "\"" ); } Q_strcat( params, sizeof( params ), token ); if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word Q_strcat( params, sizeof( params ), "\"" ); } token = COM_ParseExt( &pScript, qfalse ); } if ( strlen( params ) ) { // copy the params into the event curEvent->stack.items[curEvent->stack.numItems].params = G_Alloc( strlen( params ) + 1 ); Q_strncpyz( curEvent->stack.items[curEvent->stack.numItems].params, params, strlen( params ) + 1 ); } curEvent->stack.numItems++; if ( curEvent->stack.numItems >= AICAST_MAX_SCRIPT_STACK_ITEMS ) { G_Error( "AICast_ScriptParse(): script exceeded MAX_SCRIPT_ITEMS (%d), line %d\n", AICAST_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine() ); } } numEventItems++; } else // skip this character completely { // TTimo: gcc: suggest () around assignment used as truth value while ( ( token = COM_Parse( &pScript ) ) ) { if ( !token[0] ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } else if ( token[0] == '{' ) { bracketLevel++; } else if ( token[0] == '}' ) { if ( !--bracketLevel ) { break; } } } } } // alloc and copy the events into the cast_state_t for this cast if ( numEventItems > 0 ) { cs->castScriptEvents = G_Alloc( sizeof( cast_script_event_t ) * numEventItems ); memcpy( cs->castScriptEvents, events, sizeof( cast_script_event_t ) * numEventItems ); cs->numCastScriptEvents = numEventItems; cs->castScriptStatus.castScriptEventIndex = -1; } }
/* ============== AICast_ScriptParse Parses the script for the given character ============== */ void AICast_ScriptParse( cast_state_t *cs ) { #define MAX_SCRIPT_EVENTS 64 gentity_t *ent; char *pScript; char *token; qboolean wantName; qboolean inScript; int eventNum; cast_script_event_t events[MAX_SCRIPT_EVENTS]; int numEventItems; cast_script_event_t *curEvent; char params[MAX_QPATH]; cast_script_stack_action_t *action; int i; int bracketLevel; if (!level.scriptAI) return; ent = &g_entities[cs->entityNum]; if (!ent->aiName) return; pScript = level.scriptAI; wantName = qtrue; inScript = qfalse; COM_BeginParseSession("AICast_ScriptParse"); bracketLevel = 0; numEventItems = 0; memset( events, 0, sizeof(events) ); while (1) { token = COM_Parse( &pScript ); if ( !token[0] ) { if ( !wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } break; } // end of script if ( token[0] == '}' ) { if ( inScript ) { break; } if ( wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine() ); } wantName = qtrue; } else if ( token[0] == '{' ) { if ( wantName ) { G_Error( "AICast_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine() ); } } else if ( wantName ) { if ( !Q_strcasecmp( ent->aiName, token ) ) { inScript = qtrue; numEventItems = 0; } wantName = qfalse; } else if ( inScript ) { if ( !Q_strcasecmp( token, "attributes" ) ) { // read in all the attributes AICast_CheckLevelAttributes( cs, ent, &pScript ); continue; } eventNum = AICast_EventForString( token ); if (eventNum < 0) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token ); } if (numEventItems >= MAX_SCRIPT_EVENTS) { G_Error( "AICast_ScriptParse(), Error (line %d): MAX_SCRIPT_EVENTS reached (%d)\n", COM_GetCurrentParseLine(), MAX_SCRIPT_EVENTS ); } // if this is a "friendlysightcorpse" event, then disable corpse vis sharing if (!Q_stricmp(token, "friendlysightcorpse")) { cs->aiFlags &= ~AIFL_CORPSESIGHTING; } curEvent = &events[numEventItems]; curEvent->eventNum = eventNum; memset( params, 0, sizeof(params) ); // parse any event params before the start of this event's actions while ((token = COM_Parse( &pScript )) && (token[0] != '{')) { if (!token[0]) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } if (eventNum == 13) { // statechange event, check params if (strlen( token ) > 1) { if (BG_IndexForString( token, animStateStr, qtrue ) < 0) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown state type '%s'.\n", COM_GetCurrentParseLine(), token ); } } } if (strlen( params )) // add a space between each param Q_strcat( params, sizeof(params), " " ); Q_strcat( params, sizeof(params), token ); } if (strlen( params )) { // copy the params into the event curEvent->params = G_Alloc( strlen( params ) + 1 ); Q_strncpyz( curEvent->params, params, strlen(params)+1 ); } // parse the actions for this event while ((token = COM_Parse( &pScript )) && (token[0] != '}')) { if (!token[0]) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } action = AICast_ActionForString( token ); if (!action) { G_Error( "AICast_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token ); } curEvent->stack.items[curEvent->stack.numItems].action = action; memset( params, 0, sizeof(params) ); token = COM_ParseExt( &pScript, qfalse ); for (i=0; token[0]; i++) { if (strlen( params )) { // add a space between each param Q_strcat( params, sizeof(params), " " ); } // Special case: playsound's need to be cached on startup to prevent in-game pauses if ((i==0) && !Q_stricmp(action->actionString, "playsound")) { G_SoundIndex(token); } if (strrchr(token,' ')) // need to wrap this param in quotes since it has more than one word Q_strcat( params, sizeof(params), "\"" ); Q_strcat( params, sizeof(params), token ); if (strrchr(token,' ')) // need to wrap this param in quotes since it has more than one word Q_strcat( params, sizeof(params), "\"" ); token = COM_ParseExt( &pScript, qfalse ); } if (strlen( params )) { // copy the params into the event curEvent->stack.items[curEvent->stack.numItems].params = G_Alloc( strlen( params ) + 1 ); Q_strncpyz( curEvent->stack.items[curEvent->stack.numItems].params, params, strlen(params)+1 ); } curEvent->stack.numItems++; if (curEvent->stack.numItems >= AICAST_MAX_SCRIPT_STACK_ITEMS) { G_Error( "AICast_ScriptParse(): script exceeded MAX_SCRIPT_ITEMS (%d), line %d\n", AICAST_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine() ); } } numEventItems++; } else // skip this character completely { // TTimo gcc: suggest parentheses around assignment used as truth value while ( ( token = COM_Parse( &pScript ) ) ) { if (!token[0]) { G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() ); } else if (token[0] == '{') { bracketLevel++; } else if (token[0] == '}') { if (!--bracketLevel) break; } } } } // alloc and copy the events into the cast_state_t for this cast if (numEventItems > 0) { cs->castScriptEvents = G_Alloc( sizeof(cast_script_event_t) * numEventItems ); memcpy( cs->castScriptEvents, events, sizeof(cast_script_event_t) * numEventItems ); cs->numCastScriptEvents = numEventItems; cs->castScriptStatus.castScriptEventIndex = -1; } }