// Copyright (C) 2000-2002 Raven Software, Inc. // /***************************************************************************** * name: bg_saga.c * * desc: Siege module, shared for game, cgame, and ui. * * $Author: osman $ * $Revision: 1.9 $ * *****************************************************************************/ #include "q_shared.h" #include "bg_saga.h" #include "bg_weapons.h" #include "bg_public.h" #define SIEGECHAR_TAB 9 //perhaps a bit hacky, but I don't think there's any define existing for "tab" //Could use strap stuff but I don't particularly care at the moment anyway. #include "../namespace_begin.h" extern int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); extern void trap_FS_Read( void *buffer, int len, fileHandle_t f ); extern void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); extern void trap_FS_FCloseFile( fileHandle_t f ); extern int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); #ifndef QAGAME //cgame, ui qhandle_t trap_R_RegisterShaderNoMip( const char *name ); #endif char siege_info[MAX_SIEGE_INFO_SIZE]; int siege_valid = 0; siegeTeam_t *team1Theme = NULL; siegeTeam_t *team2Theme = NULL; siegeClass_t bgSiegeClasses[MAX_SIEGE_CLASSES]; int bgNumSiegeClasses = 0; siegeTeam_t bgSiegeTeams[MAX_SIEGE_TEAMS]; int bgNumSiegeTeams = 0; //class flags stringID_table_t bgSiegeClassFlagNames[] = { ENUM2STRING(CFL_MORESABERDMG), ENUM2STRING(CFL_STRONGAGAINSTPHYSICAL), ENUM2STRING(CFL_FASTFORCEREGEN), ENUM2STRING(CFL_STATVIEWER), ENUM2STRING(CFL_HEAVYMELEE), ENUM2STRING(CFL_SINGLE_ROCKET), ENUM2STRING(CFL_CUSTOMSKEL), ENUM2STRING(CFL_EXTRA_AMMO), "", -1 }; //saber stances stringID_table_t StanceTable[] = { ENUM2STRING(SS_NONE), ENUM2STRING(SS_FAST), ENUM2STRING(SS_MEDIUM), ENUM2STRING(SS_STRONG), ENUM2STRING(SS_DESANN), ENUM2STRING(SS_TAVION), ENUM2STRING(SS_DUAL), ENUM2STRING(SS_STAFF), "", 0 }; //Weapon and force power tables are also used in NPC parsing code and some other places. stringID_table_t WPTable[] = { "NULL",WP_NONE, // Player weapons ENUM2STRING(WP_STUN_BATON), ENUM2STRING(WP_MELEE), ENUM2STRING(WP_SABER), ENUM2STRING(WP_BRYAR_PISTOL), "WP_BLASTER_PISTOL", WP_BRYAR_PISTOL, ENUM2STRING(WP_BLASTER), ENUM2STRING(WP_DISRUPTOR), ENUM2STRING(WP_BOWCASTER), ENUM2STRING(WP_REPEATER), ENUM2STRING(WP_DEMP2), ENUM2STRING(WP_FLECHETTE), ENUM2STRING(WP_ROCKET_LAUNCHER), ENUM2STRING(WP_THERMAL), ENUM2STRING(WP_TRIP_MINE), ENUM2STRING(WP_DET_PACK), ENUM2STRING(WP_CONCUSSION), ENUM2STRING(WP_BRYAR_OLD), ENUM2STRING(WP_EMPLACED_GUN), ENUM2STRING(WP_TURRET), "", 0 }; stringID_table_t FPTable[] = { ENUM2STRING(FP_HEAL), ENUM2STRING(FP_LEVITATION), ENUM2STRING(FP_SPEED), ENUM2STRING(FP_PUSH), ENUM2STRING(FP_PULL), ENUM2STRING(FP_TELEPATHY), ENUM2STRING(FP_GRIP), ENUM2STRING(FP_LIGHTNING), ENUM2STRING(FP_RAGE), ENUM2STRING(FP_PROTECT), ENUM2STRING(FP_ABSORB), ENUM2STRING(FP_TEAM_HEAL), ENUM2STRING(FP_TEAM_FORCE), ENUM2STRING(FP_DRAIN), ENUM2STRING(FP_SEE), ENUM2STRING(FP_SABER_OFFENSE), ENUM2STRING(FP_SABER_DEFENSE), ENUM2STRING(FP_SABERTHROW), "", -1 }; stringID_table_t HoldableTable[] = { ENUM2STRING(HI_NONE), ENUM2STRING(HI_SEEKER), ENUM2STRING(HI_SHIELD), ENUM2STRING(HI_MEDPAC), ENUM2STRING(HI_MEDPAC_BIG), ENUM2STRING(HI_BINOCULARS), ENUM2STRING(HI_SENTRY_GUN), ENUM2STRING(HI_JETPACK), ENUM2STRING(HI_HEALTHDISP), ENUM2STRING(HI_AMMODISP), ENUM2STRING(HI_EWEB), ENUM2STRING(HI_CLOAK), "", -1 }; stringID_table_t PowerupTable[] = { ENUM2STRING(PW_NONE), ENUM2STRING(PW_QUAD), ENUM2STRING(PW_BATTLESUIT), ENUM2STRING(PW_PULL), ENUM2STRING(PW_REDFLAG), ENUM2STRING(PW_BLUEFLAG), ENUM2STRING(PW_NEUTRALFLAG), ENUM2STRING(PW_SHIELDHIT), ENUM2STRING(PW_SPEEDBURST), ENUM2STRING(PW_DISINT_4), ENUM2STRING(PW_SPEED), ENUM2STRING(PW_CLOAKED), ENUM2STRING(PW_FORCE_ENLIGHTENED_LIGHT), ENUM2STRING(PW_FORCE_ENLIGHTENED_DARK), ENUM2STRING(PW_FORCE_BOON), ENUM2STRING(PW_YSALAMIRI), "", -1 }; //====================================== //Parsing functions //====================================== void BG_SiegeStripTabs(char *buf) { int i = 0; int i_r = 0; while (buf[i]) { if (buf[i] != SIEGECHAR_TAB) { //not a tab, just stick it in buf[i_r] = buf[i]; } else { //If it's a tab, convert it to a space. buf[i_r] = ' '; } i_r++; i++; } buf[i_r] = '\0'; } int BG_SiegeGetValueGroup(char *buf, char *group, char *outbuf) { int i = 0; int j; char checkGroup[4096]; qboolean isGroup; int parseGroups = 0; while (buf[i]) { if (buf[i] != ' ' && buf[i] != '{' && buf[i] != '}' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != SIEGECHAR_TAB) { //we're on a valid character if (buf[i] == '/' && buf[i+1] == '/') { //this is a comment, so skip over it while (buf[i] && buf[i] != '\n' && buf[i] != '\r' && buf[i] != SIEGECHAR_TAB) { i++; } } else { //parse to the next space/endline/eos and check this value against our group value. j = 0; while (buf[i] != ' ' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != SIEGECHAR_TAB && buf[i] != '{' && buf[i]) { if (buf[i] == '/' && buf[i+1] == '/') { //hit a comment, break out. break; } checkGroup[j] = buf[i]; j++; i++; } checkGroup[j] = 0; //Make sure this is a group as opposed to a globally defined value. if (buf[i] == '/' && buf[i+1] == '/') { //stopped on a comment, so first parse to the end of it. while (buf[i] && buf[i] != '\n' && buf[i] != '\r') { i++; } while (buf[i] == '\n' || buf[i] == '\r') { i++; } } if (!buf[i]) { Com_Error(ERR_DROP, "Unexpected EOF while looking for group '%s'", group); } isGroup = qfalse; while (buf[i] && buf[i] == ' ' || buf[i] == SIEGECHAR_TAB || buf[i] == '\n' || buf[i] == '\r') { //parse to the next valid character i++; } if (buf[i] == '{') { //if the next valid character is an opening bracket, then this is indeed a group isGroup = qtrue; } //Is this the one we want? if (isGroup && !Q_stricmp(checkGroup, group)) { //guess so. Parse until we hit the { indicating the beginning of the group. while (buf[i] != '{' && buf[i]) { i++; } if (buf[i]) { //We're at the start of the group now, so parse to the closing bracket. j = 0; parseGroups = 0; while ((buf[i] != '}' || parseGroups) && buf[i]) { if (buf[i] == '{') { //increment for the opening bracket. parseGroups++; } else if (buf[i] == '}') { //decrement for the closing bracket parseGroups--; } if (parseGroups < 0) { //Syntax error, I guess. Com_Error(ERR_DROP, "Found a closing bracket without an opening bracket while looking for group '%s'", group); } if ((buf[i] != '{' || parseGroups > 1) && (buf[i] != '}' || parseGroups > 0)) { //don't put the start and end brackets for this group into the output buffer outbuf[j] = buf[i]; j++; } if (buf[i] == '}' && !parseGroups) { //Alright, we can break out now. break; } i++; } outbuf[j] = 0; //Verify that we ended up on the closing bracket. if (buf[i] != '}') { Com_Error(ERR_DROP, "Group '%s' is missing a closing bracket", group); } //Strip the tabs so we're friendly for value parsing. BG_SiegeStripTabs(outbuf); return 1; //we got it, so return 1. } else { Com_Error(ERR_DROP, "Error parsing group in file, unexpected EOF before opening bracket while looking for group '%s'", group); } } else if (!isGroup) { //if it wasn't a group, parse to the end of the line while (buf[i] && buf[i] != '\n' && buf[i] != '\r') { i++; } } else { //this was a group but we not the one we wanted to find, so parse by it. parseGroups = 0; while (buf[i] && (buf[i] != '}' || parseGroups)) { if (buf[i] == '{') { parseGroups++; } else if (buf[i] == '}') { parseGroups--; } if (parseGroups < 0) { //Syntax error, I guess. Com_Error(ERR_DROP, "Found a closing bracket without an opening bracket while looking for group '%s'", group); } if (buf[i] == '}' && !parseGroups) { //Alright, we can break out now. break; } i++; } if (buf[i] != '}') { Com_Error(ERR_DROP, "Found an opening bracket without a matching closing bracket while looking for group '%s'", group); } i++; } } } else if (buf[i] == '{') { //we're in a group that isn't the one we want, so parse to the end. parseGroups = 0; while (buf[i] && (buf[i] != '}' || parseGroups)) { if (buf[i] == '{') { parseGroups++; } else if (buf[i] == '}') { parseGroups--; } if (parseGroups < 0) { //Syntax error, I guess. Com_Error(ERR_DROP, "Found a closing bracket without an opening bracket while looking for group '%s'", group); } if (buf[i] == '}' && !parseGroups) { //Alright, we can break out now. break; } i++; } if (buf[i] != '}') { Com_Error(ERR_DROP, "Found an opening bracket without a matching closing bracket while looking for group '%s'", group); } } if (!buf[i]) { break; } i++; } return 0; //guess we never found it. } int BG_SiegeGetPairedValue(char *buf, char *key, char *outbuf) { int i = 0; int j; int k; char checkKey[4096]; while (buf[i]) { if (buf[i] != ' ' && buf[i] != '{' && buf[i] != '}' && buf[i] != '\n' && buf[i] != '\r') { //we're on a valid character if (buf[i] == '/' && buf[i+1] == '/') { //this is a comment, so skip over it while (buf[i] && buf[i] != '\n' && buf[i] != '\r') { i++; } } else { //parse to the next space/endline/eos and check this value against our key value. j = 0; while (buf[i] != ' ' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != SIEGECHAR_TAB && buf[i]) { if (buf[i] == '/' && buf[i+1] == '/') { //hit a comment, break out. break; } checkKey[j] = buf[i]; j++; i++; } checkKey[j] = 0; k = i; while (buf[k] && (buf[k] == ' ' || buf[k] == '\n' || buf[k] == '\r')) { k++; } if (buf[k] == '{') { //this is not the start of a value but rather of a group. We don't want to look in subgroups so skip over the whole thing. int openB = 0; while (buf[i] && (buf[i] != '}' || openB)) { if (buf[i] == '{') { openB++; } else if (buf[i] == '}') { openB--; } if (openB < 0) { Com_Error(ERR_DROP, "Unexpected closing bracket (too many) while parsing to end of group '%s'", checkKey); } if (buf[i] == '}' && !openB) { //this is the end of the group break; } i++; } if (buf[i] == '}') { i++; } } else { //Is this the one we want? if (buf[i] != '/' || buf[i+1] != '/') { //make sure we didn't stop on a comment, if we did then this is considered an error in the file. if (!Q_stricmp(checkKey, key)) { //guess so. Parse along to the next valid character, then put that into the output buffer and return 1. while ((buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\r' || buf[i] == SIEGECHAR_TAB) && buf[i]) { i++; } if (buf[i]) { //We're at the start of the value now. qboolean parseToQuote = qfalse; if (buf[i] == '\"') { //if the value is in quotes, then stop at the next quote instead of ' ' i++; parseToQuote = qtrue; } j = 0; while ( ((!parseToQuote && buf[i] != ' ' && buf[i] != '\n' && buf[i] != '\r') || (parseToQuote && buf[i] != '\"')) ) { if (buf[i] == '/' && buf[i+1] == '/') { //hit a comment after the value? This isn't an ideal way to be writing things, but we'll support it anyway. break; } outbuf[j] = buf[i]; j++; i++; if (!buf[i]) { if (parseToQuote) { Com_Error(ERR_DROP, "Unexpected EOF while looking for endquote, error finding paired value for '%s'", key); } else { Com_Error(ERR_DROP, "Unexpected EOF while looking for space or endline, error finding paired value for '%s'", key); } } } outbuf[j] = 0; return 1; //we got it, so return 1. } else { Com_Error(ERR_DROP, "Error parsing file, unexpected EOF while looking for valud '%s'", key); } } else { //if that wasn't the desired key, then make sure we parse to the end of the line, so we don't mistake a value for a key while (buf[i] && buf[i] != '\n') { i++; } } } else { Com_Error(ERR_DROP, "Error parsing file, found comment, expected value for '%s'", key); } } } } if (!buf[i]) { break; } i++; } return 0; //guess we never found it. } //====================================== //End parsing functions //====================================== //====================================== //Class loading functions //====================================== void BG_SiegeTranslateForcePowers(char *buf, siegeClass_t *siegeClass) { char checkPower[1024]; char checkLevel[256]; int l = 0; int k = 0; int j = 0; int i = 0; int parsedLevel = 0; qboolean allPowers = qfalse; qboolean noPowers = qfalse; if (!Q_stricmp(buf, "FP_ALL")) { //this is a special case, just give us all the powers on level 3 allPowers = qtrue; } if (buf[0] == '0' && !buf[1]) { //no powers then noPowers = qtrue; } //First clear out the powers, or in the allPowers case, give us all level 3. while (i < NUM_FORCE_POWERS) { if (allPowers) { siegeClass->forcePowerLevels[i] = FORCE_LEVEL_3; } else { siegeClass->forcePowerLevels[i] = 0; } i++; } if (allPowers || noPowers) { //we're done now then. return; } i = 0; while (buf[i]) { //parse through the list which is seperated by |, and add all the weapons into a bitflag if (buf[i] != ' ' && buf[i] != '|') { j = 0; while (buf[i] && buf[i] != ' ' && buf[i] != '|' && buf[i] != ',') { checkPower[j] = buf[i]; j++; i++; } checkPower[j] = 0; if (buf[i] == ',') { //parse the power level i++; l = 0; while (buf[i] && buf[i] != ' ' && buf[i] != '|') { checkLevel[l] = buf[i]; l++; i++; } checkLevel[l] = 0; parsedLevel = atoi(checkLevel); //keep sane limits on the powers if (parsedLevel < 0) { parsedLevel = 0; } if (parsedLevel > FORCE_LEVEL_5) { parsedLevel = FORCE_LEVEL_5; } } else { //if it's not there, assume level 3 I guess. parsedLevel = 3; } if (checkPower[0]) { //Got the name, compare it against the weapon table strings. k = 0; if (!Q_stricmp(checkPower, "FP_JUMP")) { //haqery strcpy(checkPower, "FP_LEVITATION"); } while (FPTable[k].id != -1 && FPTable[k].name[0]) { if (!Q_stricmp(checkPower, FPTable[k].name)) { //found it, add the weapon into the weapons value siegeClass->forcePowerLevels[k] = parsedLevel; break; } k++; } } } if (!buf[i]) { break; } i++; } } //Used for the majority of generic val parsing stuff. buf should be the value string, //table should be the appropriate string/id table. If bitflag is qtrue then the //values are accumulated into a bitflag. If bitflag is qfalse then the first value //is returned as a directly corresponding id and no further parsing is done. int BG_SiegeTranslateGenericTable(char *buf, stringID_table_t *table, qboolean bitflag) { int items = 0; char checkItem[1024]; int i = 0; int j = 0; int k = 0; if (buf[0] == '0' && !buf[1]) { //special case, no items. return 0; } while (buf[i]) { //Using basically the same parsing method as we do for weapons and forcepowers. if (buf[i] != ' ' && buf[i] != '|') { j = 0; while (buf[i] && buf[i] != ' ' && buf[i] != '|') { checkItem[j] = buf[i]; j++; i++; } checkItem[j] = 0; if (checkItem[0]) { k = 0; while (table[k].name && table[k].name[0]) { //go through the list and check the parsed flag name against the hardcoded names if (!Q_stricmp(checkItem, table[k].name)) { //Got it, so add the value into our items value. if (bitflag) { items |= (1 << table[k].id); } else { //return the value directly then. return table[k].id; } break; } k++; } } } if (!buf[i]) { break; } i++; } return items; } char *classTitles[SPC_MAX] = { "infantry", // SPC_INFANTRY "vanguard", // SPC_VANGUARD "support", // SPC_SUPPORT "jedi_general", // SPC_JEDI "demolitionist", // SPC_DEMOLITIONIST "heavy_weapons", // SPC_HEAVY_WEAPONS }; void BG_SiegeParseClassFile(const char *filename, siegeClassDesc_t *descBuffer) { fileHandle_t f; int len; int i; char classInfo[4096]; char parseBuf[4096]; len = trap_FS_FOpenFile(filename, &f, FS_READ); if (!f || len >= 4096) { return; } trap_FS_Read(classInfo, len, f); trap_FS_FCloseFile(f); classInfo[len] = 0; //first get the description if we have a buffer for it if (descBuffer) { if (!BG_SiegeGetPairedValue(classInfo, "description", descBuffer->desc)) { strcpy(descBuffer->desc, "DESCRIPTION UNAVAILABLE"); } //Hit this assert? Memory has already been trashed. Increase //SIEGE_CLASS_DESC_LEN. assert(strlen(descBuffer->desc) < SIEGE_CLASS_DESC_LEN); } BG_SiegeGetValueGroup(classInfo, "ClassInfo", classInfo); //Parse name if (BG_SiegeGetPairedValue(classInfo, "name", parseBuf)) { strcpy(bgSiegeClasses[bgNumSiegeClasses].name, parseBuf); } else { Com_Error(ERR_DROP, "Siege class without name entry"); } //Parse forced model if (BG_SiegeGetPairedValue(classInfo, "model", parseBuf)) { strcpy(bgSiegeClasses[bgNumSiegeClasses].forcedModel, parseBuf); } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].forcedModel[0] = 0; } //Parse forced skin if (BG_SiegeGetPairedValue(classInfo, "skin", parseBuf)) { strcpy(bgSiegeClasses[bgNumSiegeClasses].forcedSkin, parseBuf); } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].forcedSkin[0] = 0; } //Parse first saber if (BG_SiegeGetPairedValue(classInfo, "saber1", parseBuf)) { strcpy(bgSiegeClasses[bgNumSiegeClasses].saber1, parseBuf); } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].saber1[0] = 0; } //Parse second saber if (BG_SiegeGetPairedValue(classInfo, "saber2", parseBuf)) { strcpy(bgSiegeClasses[bgNumSiegeClasses].saber2, parseBuf); } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].saber2[0] = 0; } //Parse forced saber stance if (BG_SiegeGetPairedValue(classInfo, "saberstyle", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].saberStance = BG_SiegeTranslateGenericTable(parseBuf, StanceTable, qtrue); } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].saberStance = 0; } //Parse forced saber color if (BG_SiegeGetPairedValue(classInfo, "sabercolor", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].forcedSaberColor = atoi(parseBuf); bgSiegeClasses[bgNumSiegeClasses].hasForcedSaberColor = qtrue; } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].hasForcedSaberColor = qfalse; } //Parse forced saber2 color if (BG_SiegeGetPairedValue(classInfo, "saber2color", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].forcedSaber2Color = atoi(parseBuf); bgSiegeClasses[bgNumSiegeClasses].hasForcedSaber2Color = qtrue; } else { //It's ok if there isn't one, it's optional. bgSiegeClasses[bgNumSiegeClasses].hasForcedSaber2Color = qfalse; } //Parse weapons if (BG_SiegeGetPairedValue(classInfo, "weapons", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].weapons = BG_SiegeTranslateGenericTable(parseBuf, WPTable, qtrue); } else { Com_Error(ERR_DROP, "Siege class without weapons entry"); } if (!(bgSiegeClasses[bgNumSiegeClasses].weapons & (1 << WP_SABER))) { //make sure it has melee if there's no saber bgSiegeClasses[bgNumSiegeClasses].weapons |= (1 << WP_MELEE); //always give them this too if they are not a saber user //bgSiegeClasses[bgNumSiegeClasses].weapons |= (1 << WP_BRYAR_PISTOL); } //Parse forcepowers if (BG_SiegeGetPairedValue(classInfo, "forcepowers", parseBuf)) { BG_SiegeTranslateForcePowers(parseBuf, &bgSiegeClasses[bgNumSiegeClasses]); } else { //fine, clear out the powers. i = 0; while (i < NUM_FORCE_POWERS) { bgSiegeClasses[bgNumSiegeClasses].forcePowerLevels[i] = 0; i++; } } //Parse classflags if (BG_SiegeGetPairedValue(classInfo, "classflags", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].classflags = BG_SiegeTranslateGenericTable(parseBuf, bgSiegeClassFlagNames, qtrue); } else { //fine, we'll 0 it. bgSiegeClasses[bgNumSiegeClasses].classflags = 0; } //Parse maxhealth if (BG_SiegeGetPairedValue(classInfo, "maxhealth", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].maxhealth = atoi(parseBuf); } else { //It's alright, just default to 100 then. bgSiegeClasses[bgNumSiegeClasses].maxhealth = 100; } //Parse starthealth if (BG_SiegeGetPairedValue(classInfo, "starthealth", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].starthealth = atoi(parseBuf); } else { //It's alright, just default to 100 then. bgSiegeClasses[bgNumSiegeClasses].starthealth = 100; } //Parse startarmor if (BG_SiegeGetPairedValue(classInfo, "maxarmor", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].maxarmor = atoi(parseBuf); } else { //It's alright, just default to 0 then. bgSiegeClasses[bgNumSiegeClasses].maxarmor = 0; } //Parse startarmor if (BG_SiegeGetPairedValue(classInfo, "startarmor", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].startarmor = atoi(parseBuf); if (!bgSiegeClasses[bgNumSiegeClasses].maxarmor) { //if they didn't specify a damn max armor then use this. bgSiegeClasses[bgNumSiegeClasses].maxarmor = bgSiegeClasses[bgNumSiegeClasses].startarmor; } } else { //default to maxarmor. bgSiegeClasses[bgNumSiegeClasses].startarmor = bgSiegeClasses[bgNumSiegeClasses].maxarmor; } //Parse speed (this is a multiplier value) if (BG_SiegeGetPairedValue(classInfo, "speed", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].speed = atof(parseBuf); } else { //It's alright, just default to 1 then. bgSiegeClasses[bgNumSiegeClasses].speed = 1.0f; } //Parse shader for ui to use if (BG_SiegeGetPairedValue(classInfo, "uishader", parseBuf)) { #ifdef QAGAME bgSiegeClasses[bgNumSiegeClasses].uiPortraitShader = 0; memset(bgSiegeClasses[bgNumSiegeClasses].uiPortrait,0,sizeof(bgSiegeClasses[bgNumSiegeClasses].uiPortrait)); #elif defined CGAME bgSiegeClasses[bgNumSiegeClasses].uiPortraitShader = 0; memset(bgSiegeClasses[bgNumSiegeClasses].uiPortrait,0,sizeof(bgSiegeClasses[bgNumSiegeClasses].uiPortrait)); #else //ui bgSiegeClasses[bgNumSiegeClasses].uiPortraitShader = trap_R_RegisterShaderNoMip(parseBuf); memcpy(bgSiegeClasses[bgNumSiegeClasses].uiPortrait,parseBuf,sizeof(bgSiegeClasses[bgNumSiegeClasses].uiPortrait)); #endif } else { //I guess this is an essential.. we don't want to render bad shaders or anything. Com_Error(ERR_DROP, "Siege class without uishader entry"); } //Parse shader for ui to use if (BG_SiegeGetPairedValue(classInfo, "class_shader", parseBuf)) { #ifdef QAGAME bgSiegeClasses[bgNumSiegeClasses].classShader = 0; #else //cgame, ui bgSiegeClasses[bgNumSiegeClasses].classShader = trap_R_RegisterShaderNoMip(parseBuf); assert( bgSiegeClasses[bgNumSiegeClasses].classShader ); if ( !bgSiegeClasses[bgNumSiegeClasses].classShader ) { //Com_Error( ERR_DROP, "ERROR: could not find class_shader %s for class %s\n", parseBuf, bgSiegeClasses[bgNumSiegeClasses].name ); Com_Printf( "ERROR: could not find class_shader %s for class %s\n", parseBuf, bgSiegeClasses[bgNumSiegeClasses].name ); } // A very hacky way to determine class . . . else #endif { // Find the base player class based on the icon name - very bad, I know. int titleLength,i,arrayTitleLength; char *holdBuf; titleLength = strlen(parseBuf); for (i=0;ititleLength) // Too long { break; } holdBuf = parseBuf + ( titleLength - arrayTitleLength); if (!strcmp(holdBuf,classTitles[i])) { bgSiegeClasses[bgNumSiegeClasses].playerClass = i; break; } } // In case the icon name doesn't match up if (i>=SPC_MAX) { bgSiegeClasses[bgNumSiegeClasses].playerClass = SPC_INFANTRY; } } } else { //No entry! Bad bad bad //Com_Error( ERR_DROP, "ERROR: no class_shader defined for class %s\n", bgSiegeClasses[bgNumSiegeClasses].name ); Com_Printf( "ERROR: no class_shader defined for class %s\n", bgSiegeClasses[bgNumSiegeClasses].name ); } //Parse holdable items to use if (BG_SiegeGetPairedValue(classInfo, "holdables", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].invenItems = BG_SiegeTranslateGenericTable(parseBuf, HoldableTable, qtrue); } else { //Just don't start out with any then. bgSiegeClasses[bgNumSiegeClasses].invenItems = 0; } //Parse powerups to use if (BG_SiegeGetPairedValue(classInfo, "powerups", parseBuf)) { bgSiegeClasses[bgNumSiegeClasses].powerups = BG_SiegeTranslateGenericTable(parseBuf, PowerupTable, qtrue); } else { //Just don't start out with any then. bgSiegeClasses[bgNumSiegeClasses].powerups = 0; } //A successful read. bgNumSiegeClasses++; } // Count the number of like base classes int BG_SiegeCountBaseClass(const int team, const short classIndex) { int count = 0,i; siegeTeam_t *stm; stm = BG_SiegeFindThemeForTeam(team); if (!stm) { return(0); } for (i=0;inumClasses;i++) { if (stm->classes[i]->playerClass == classIndex) { count++; } } return(count); } char *BG_GetUIPortraitFile(const int team, const short classIndex, const short cntIndex) { int count = 0,i; siegeTeam_t *stm; stm = BG_SiegeFindThemeForTeam(team); if (!stm) { return(0); } // Loop through all the classes for this team for (i=0;inumClasses;i++) { // does it match the base class? if (stm->classes[i]->playerClass == classIndex) { if (count==cntIndex) { return(stm->classes[i]->uiPortrait); } ++count; } } return(0); } int BG_GetUIPortrait(const int team, const short classIndex, const short cntIndex) { int count = 0,i; siegeTeam_t *stm; stm = BG_SiegeFindThemeForTeam(team); if (!stm) { return(0); } // Loop through all the classes for this team for (i=0;inumClasses;i++) { // does it match the base class? if (stm->classes[i]->playerClass == classIndex) { if (count==cntIndex) { return(stm->classes[i]->uiPortraitShader); } ++count; } } return(0); } // This is really getting ugly - looking to get the base class (within a class) based on the index passed in siegeClass_t *BG_GetClassOnBaseClass(const int team, const short classIndex, const short cntIndex) { int count = 0,i; siegeTeam_t *stm; stm = BG_SiegeFindThemeForTeam(team); if (!stm) { return(0); } // Loop through all the classes for this team for (i=0;inumClasses;i++) { // does it match the base class? if (stm->classes[i]->playerClass == classIndex) { if (count==cntIndex) { return(stm->classes[i]); } ++count; } } return(0); } void BG_SiegeLoadClasses(siegeClassDesc_t *descBuffer) { int numFiles; int filelen; char filelist[4096]; char filename[MAX_QPATH]; char* fileptr; int i; bgNumSiegeClasses = 0; numFiles = trap_FS_GetFileList("ext_data/Siege/Classes", ".scl", filelist, 4096 ); fileptr = filelist; for (i = 0; i < numFiles; i++, fileptr += filelen+1) { filelen = strlen(fileptr); strcpy(filename, "ext_data/Siege/Classes/"); strcat(filename, fileptr); if (descBuffer) { BG_SiegeParseClassFile(filename, &descBuffer[i]); } else { BG_SiegeParseClassFile(filename, NULL); } } } //====================================== //End class loading functions //====================================== //====================================== //Team loading functions //====================================== siegeClass_t *BG_SiegeFindClassByName(const char *classname) { int i = 0; while (i < bgNumSiegeClasses) { if (!Q_stricmp(bgSiegeClasses[i].name, classname)) { //found it return &bgSiegeClasses[i]; } i++; } return NULL; } void BG_SiegeParseTeamFile(const char *filename) { fileHandle_t f; int len; char teamInfo[2048]; char parseBuf[1024]; char lookString[256]; int i = 1; qboolean success = qtrue; len = trap_FS_FOpenFile(filename, &f, FS_READ); if (!f || len >= 2048) { return; } trap_FS_Read(teamInfo, len, f); trap_FS_FCloseFile(f); teamInfo[len] = 0; if (BG_SiegeGetPairedValue(teamInfo, "name", parseBuf)) { strcpy(bgSiegeTeams[bgNumSiegeTeams].name, parseBuf); } else { Com_Error(ERR_DROP, "Siege team with no name definition"); } //I don't entirely like doing things this way but it's the easiest way. #ifdef CGAME if (BG_SiegeGetPairedValue(teamInfo, "FriendlyShader", parseBuf)) { bgSiegeTeams[bgNumSiegeTeams].friendlyShader = trap_R_RegisterShaderNoMip(parseBuf); } #else bgSiegeTeams[bgNumSiegeTeams].friendlyShader = 0; #endif bgSiegeTeams[bgNumSiegeTeams].numClasses = 0; if (BG_SiegeGetValueGroup(teamInfo, "Classes", teamInfo)) { while (success && i < MAX_SIEGE_CLASSES) { //keep checking for group values named class# up to MAX_SIEGE_CLASSES until we can't find one. strcpy(lookString, va("class%i", i)); success = BG_SiegeGetPairedValue(teamInfo, lookString, parseBuf); if (!success) { break; } bgSiegeTeams[bgNumSiegeTeams].classes[bgSiegeTeams[bgNumSiegeTeams].numClasses] = BG_SiegeFindClassByName(parseBuf); if (!bgSiegeTeams[bgNumSiegeTeams].classes[bgSiegeTeams[bgNumSiegeTeams].numClasses]) { Com_Error(ERR_DROP, "Invalid class specified: '%s'", parseBuf); } bgSiegeTeams[bgNumSiegeTeams].numClasses++; i++; } } if (!bgSiegeTeams[bgNumSiegeTeams].numClasses) { Com_Error(ERR_DROP, "Team defined with no allowable classes\n"); } //If we get here then it was a success, so increment the team number bgNumSiegeTeams++; } void BG_SiegeLoadTeams(void) { int numFiles; int filelen; char filelist[4096]; char filename[MAX_QPATH]; char* fileptr; int i; bgNumSiegeTeams = 0; numFiles = trap_FS_GetFileList("ext_data/Siege/Teams", ".team", filelist, 4096 ); fileptr = filelist; for (i = 0; i < numFiles; i++, fileptr += filelen+1) { filelen = strlen(fileptr); strcpy(filename, "ext_data/Siege/Teams/"); strcat(filename, fileptr); BG_SiegeParseTeamFile(filename); } } //====================================== //End team loading functions //====================================== //====================================== //Misc/utility functions //====================================== siegeTeam_t *BG_SiegeFindThemeForTeam(int team) { if (team == SIEGETEAM_TEAM1) { return team1Theme; } else if (team == SIEGETEAM_TEAM2) { return team2Theme; } return NULL; } #ifndef UI_EXPORTS //only for game/cgame //precache all the sabers for the active classes for the team extern qboolean WP_SaberParseParms( const char *SaberName, saberInfo_t *saber ); //bg_saberLoad.cpp extern int BG_ModelCache(const char *modelName, const char *skinName); //bg_misc.c void BG_PrecacheSabersForSiegeTeam(int team) { siegeTeam_t *t; saberInfo_t saber; char *saberName; int sNum; t = BG_SiegeFindThemeForTeam(team); if (t) { int i = 0; while (i < t->numClasses) { sNum = 0; while (sNum < MAX_SABERS) { switch (sNum) { case 0: saberName = &t->classes[i]->saber1[0]; break; case 1: saberName = &t->classes[i]->saber2[0]; break; default: saberName = NULL; break; } if (saberName && saberName[0]) { WP_SaberParseParms(saberName, &saber); if (!Q_stricmp(saberName, saber.name)) { //found the matching saber if (saber.model[0]) { BG_ModelCache(saber.model, NULL); } } } sNum++; } i++; } } } #endif qboolean BG_SiegeCheckClassLegality(int team, char *classname) { siegeTeam_t **teamPtr = NULL; int i = 0; if (team == SIEGETEAM_TEAM1) { teamPtr = &team1Theme; } else if (team == SIEGETEAM_TEAM2) { teamPtr = &team2Theme; } else { //spectator? Whatever, you're legal then. return qtrue; } if (!teamPtr || !(*teamPtr)) { //Well, guess the class is ok, seeing as there is no team theme to begin with. return qtrue; } //See if the class is listed on the team while (i < (*teamPtr)->numClasses) { if (!Q_stricmp(classname, (*teamPtr)->classes[i]->name)) { //found it, so it's alright return qtrue; } i++; } //Didn't find it, so copy the name of the first valid class over it. strcpy(classname, (*teamPtr)->classes[0]->name); return qfalse; } siegeTeam_t *BG_SiegeFindTeamForTheme(char *themeName) { int i = 0; while (i < bgNumSiegeTeams) { if (bgSiegeTeams[i].name && !Q_stricmp(bgSiegeTeams[i].name, themeName)) { //this is what we're looking for return &bgSiegeTeams[i]; } i++; } return NULL; } void BG_SiegeSetTeamTheme(int team, char *themeName) { siegeTeam_t **teamPtr = NULL; if (team == SIEGETEAM_TEAM1) { teamPtr = &team1Theme; } else { teamPtr = &team2Theme; } (*teamPtr) = BG_SiegeFindTeamForTheme(themeName); } int BG_SiegeFindClassIndexByName(const char *classname) { int i = 0; while (i < bgNumSiegeClasses) { if (!Q_stricmp(bgSiegeClasses[i].name, classname)) { //found it return i; } i++; } return -1; } //====================================== //End misc/utility functions //====================================== #include "../namespace_end.h"