// FIXME: use nav handle instead of classes void G_BotNavInit() { int i; Log::Notice( "==== Bot Navigation Initialization ==== \n" ); for ( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ ) { classModelConfig_t *model; botClass_t bot; bot.polyFlagsInclude = POLYFLAGS_WALK; bot.polyFlagsExclude = POLYFLAGS_DISABLED; model = BG_ClassModelConfig( i ); if ( model->navMeshClass ) { if ( BG_ClassModelConfig( model->navMeshClass )->navMeshClass ) { Log::Warn( "class '%s': navmesh reference target class '%s' must have its own navmesh", BG_Class( i )->name, BG_Class( model->navMeshClass )->name ); return; } continue; } Q_strncpyz( bot.name, BG_Class( i )->name, sizeof( bot.name ) ); if ( !trap_BotSetupNav( &bot, &model->navHandle ) ) { return; } } navMeshLoaded = true; }
void BotSetNavmesh( gentity_t *self, class_t newClass ) { int navHandle; const classModelConfig_t *model; if ( newClass == PCL_NONE ) { return; } model = BG_ClassModelConfig( newClass ); navHandle = model->navMeshClass ? BG_ClassModelConfig( model->navMeshClass )->navHandle : model->navHandle; trap_BotSetNavMesh( self->s.number, navHandle ); }
static const char *UnlockableHumanName( unlockable_t *unlockable ) { switch ( unlockable->type ) { case UNLT_WEAPON: return BG_Weapon( unlockable->num )->humanName; case UNLT_UPGRADE: return BG_Upgrade( unlockable->num )->humanName; case UNLT_BUILDABLE: return BG_Buildable( unlockable->num )->humanName; case UNLT_CLASS: return BG_ClassModelConfig( unlockable->num )->humanName; } Com_Error( ERR_FATAL, "UnlockableHumanName: Unlockable has unknown type" ); return nullptr; }
void G_InitDamageLocations( void ) { const char *modelName; char filename[ MAX_QPATH ]; int i; int len; fileHandle_t fileHandle; char buffer[ MAX_DAMAGE_REGION_TEXT ]; for ( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ ) { modelName = BG_ClassModelConfig( i )->modelName; Com_sprintf( filename, sizeof( filename ), "configs/classes/%s.locdamage.cfg", modelName ); len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ ); if ( !fileHandle ) { G_Printf( S_COLOR_RED "file not found: %s\n", filename ); continue; } if ( len >= MAX_DAMAGE_REGION_TEXT ) { G_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_DAMAGE_REGION_TEXT ); trap_FS_FCloseFile( fileHandle ); continue; } COM_BeginParseSession( filename ); trap_FS_Read( buffer, len, fileHandle ); buffer[ len ] = 0; trap_FS_FCloseFile( fileHandle ); g_numDamageRegions[ i ] = ParseDmgScript( g_damageRegions[ i ], buffer ); } }
/* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; int attackerClass = -1; const char *message; const char *targetInfo; const char *attackerInfo; char targetName[ MAX_NAME_LENGTH ]; char attackerName[ MAX_NAME_LENGTH ]; gender_t gender; clientInfo_t *ci; qboolean teamKill = qfalse; qboolean attackerFirst = qfalse; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[ target ]; gender = ci->gender; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { attacker = ENTITYNUM_WORLD; attackerInfo = NULL; } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); if ( cgs.clientinfo[ attacker ].team == ci->team ) { teamKill = qtrue; } } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ) ); // check for single client messages switch ( mod ) { case MOD_FALLING: message = G_( "%s ^7fell foul to gravity\n" ); break; case MOD_CRUSH: message = G_( "%s ^7was squished\n" ); break; case MOD_WATER: message = G_( "%s ^7forgot to pack a snorkel\n" ); break; case MOD_SLIME: message = G_( "%s ^7melted\n" ); break; case MOD_LAVA: message = G_( "%s ^7did a back flip into the lava\n" ); break; case MOD_TARGET_LASER: message = G_( "%s ^7saw the light\n" ); break; case MOD_TRIGGER_HURT: message = G_( "%s ^7was in the wrong place\n" ); break; case MOD_HSPAWN: message = G_( "%s ^7should have run further\n" ); break; case MOD_ASPAWN: message = G_( "%s ^7shouldn't have trod in the acid\n" ); break; case MOD_MGTURRET: if ( cg_emoticonsInMessages.integer ) { message = "[turret] %s\n"; } else { message = G_( "%s ^7was gunned down by a turret\n" ); } break; case MOD_TESLAGEN: if ( cg_emoticonsInMessages.integer ) { message = "[tesla] %s\n"; } else { message = G_( "%s ^7was zapped by a tesla generator\n" ); } break; case MOD_ATUBE: if ( cg_emoticonsInMessages.integer ) { message = "[acidtube] %s\n"; } else { message = G_( "%s ^7was melted by an acid tube\n" ); } break; case MOD_OVERMIND: if ( cg_emoticonsInMessages.integer ) { message = "[overmind] %s\n"; } else { message = G_( "%s ^7got too close to the overmind\n" ); } break; case MOD_REACTOR: if ( cg_emoticonsInMessages.integer ) { message = "[reactor] %s\n"; } else { message = G_( "%s ^7got too close to the reactor\n" ); } break; case MOD_SLOWBLOB: message = G_( "%s ^7should have visited a medical station\n" ); break; case MOD_SWARM: if ( cg_emoticonsInMessages.integer ) { message = "[hive] %s\n"; } else { message = G_( "%s ^7was hunted down by the swarm\n" ); } break; default: message = NULL; break; } if ( !message && attacker == target ) { switch ( mod ) { case MOD_FLAMER_SPLASH: if ( cg_emoticonsInMessages.integer ) { message = "[flamer] %s\n"; } else { message = G_( "%s ^7toasted self\n" ); } break; case MOD_BURN: if ( cg_emoticonsInMessages.integer ) { message = "[fire] %s\n"; } else { message = G_( "%s ^7burned self\n" ); } break; case MOD_LCANNON_SPLASH: if ( cg_emoticonsInMessages.integer ) { message = "[lcannon] %s\n"; } else { message = G_( "%s ^7irradiated self\n" ); } break; case MOD_GRENADE: if ( cg_emoticonsInMessages.integer ) { message = "[grenade] %s\n"; } else // fall-through case MOD_FIREBOMB: if ( cg_emoticonsInMessages.integer ) { message = "[firebomb] %s\n"; } else { message = G_( "%s ^7blew self up\n" ); } break; case MOD_LEVEL3_BOUNCEBALL: if ( cg_emoticonsInMessages.integer ) { message = "[advdragoon] %s\n"; } else { message = G_( "%s ^7sniped self\n" ); } break; case MOD_PRIFLE: if ( cg_emoticonsInMessages.integer ) { message = "[prifle] %s\n"; } else { message = G_( "%s ^7pulse rifled self\n" ); } break; default: message = G_( "%s ^7killed self\n" ); break; } } if ( message ) { CG_Printf( message, targetName ); return; } // check for double client messages if ( !attackerInfo ) { attacker = ENTITYNUM_WORLD; strcpy( attackerName, "noname" ); } else { Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ) ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( attacker != ENTITYNUM_WORLD ) { switch ( mod ) { case MOD_PAINSAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [painsaw] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was sawn by %s%s\n" ); } break; case MOD_BLASTER: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [blaster] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was blasted by %s%s\n" ); } break; case MOD_MACHINEGUN: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [rifle] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was machinegunned by %s%s\n" ); } break; case MOD_CHAINGUN: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [chaingun] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was chaingunned by %s%s\n" ); } break; case MOD_SHOTGUN: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [shotgun] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was gunned down by %s%s\n" ); } break; case MOD_PRIFLE: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [prifle] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was pulse rifled by %s%s\n" ); } break; case MOD_MDRIVER: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [mdriver] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was mass driven by %s%s\n" ); } break; case MOD_LASGUN: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [lasgun] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was lasgunned by %s%s\n" ); } break; case MOD_FLAMER: case MOD_FLAMER_SPLASH: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [flamer] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was grilled by %s%s^7's flamer\n" ); } break; case MOD_BURN: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [flamer] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was burned by %s%s^7's fire\n" ); } break; case MOD_LCANNON: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [lcannon] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7felt the full force of %s%s^7's lucifer cannon\n" ); } break; case MOD_LCANNON_SPLASH: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [lcannon] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was caught in the fallout of %s%s^7's lucifer cannon\n" ); } break; case MOD_GRENADE: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [grenade] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7couldn't escape %s%s^7's grenade\n" ); } break; case MOD_FIREBOMB: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [firebomb] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7couldn't escape %s%s^7's firebomb\n" ); } break; case MOD_ABUILDER_CLAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [granger] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7should leave %s%s^7's buildings alone\n" ); } break; case MOD_LEVEL0_BITE: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [dretch] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was bitten by %s%s\n" ); } break; case MOD_LEVEL1_CLAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [basilisk] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was swiped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL1; } break; case MOD_LEVEL2_CLAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [marauder] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was clawed by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL2; } break; case MOD_LEVEL2_ZAP: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [advmarauder] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was zapped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL2; } break; case MOD_LEVEL3_CLAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [dragoon] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was chomped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; } break; case MOD_LEVEL3_POUNCE: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [dragoon] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was pounced upon by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; } break; case MOD_LEVEL3_BOUNCEBALL: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [advdragoon] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was sniped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; } break; case MOD_LEVEL4_CLAW: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [tyrant] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was mauled by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL4; } break; case MOD_LEVEL4_TRAMPLE: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [tyrant] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7should have gotten out of the way of %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL4; } break; case MOD_WEIGHT_H: case MOD_WEIGHT_A: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 crushed %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was crushed under %s%s^7's weight\n" ); } break; case MOD_POISON: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [booster] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7should have used a medkit against %s%s^7's poison\n" ); } break; case MOD_LEVEL1_PCLOUD: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [advbasilisk] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7was gassed by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL1; } break; case MOD_TELEFRAG: if ( cg_emoticonsInMessages.integer ) { message = "%s%s^7 [telenode] %s\n"; attackerFirst = qtrue; } else { message = G_( "%s ^7tried to invade %s%s^7's personal space\n" ); } break; default: message = G_( "%s ^7was killed by %s%s\n" ); break; } if ( message ) { if ( attackerFirst ) { // Argument order: "TEAMMATE"/"", attacker, victim CG_Printf( message, ( teamKill ) ? _("^1TEAMMATE^7 ") : "", attackerName, targetName ); } else { // Argument order: victim, ["TEAMMATE"/"", attacker [, alien class]] CG_Printf( message, targetName, ( teamKill ) ? _("^1TEAMMATE^7 ") : "", attackerName, ( attackerClass != -1 ) ? _( BG_ClassModelConfig( attackerClass )->humanName ) : NULL ); } if ( teamKill && attacker == cg.clientNum ) { CG_CenterPrint( va( _("You killed ^1TEAMMATE^7 %s"), targetName ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } return; } } // we don't know what it was CG_Printf( G_( "%s^7 died\n" ), targetName ); }
/* =============== CG_OffsetShoulderView =============== */ void CG_OffsetShoulderView( void ) { int i; int cmdNum; usercmd_t cmd, oldCmd; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaMousePitch; static float mousePitch; vec3_t forward, right, up; classModelConfig_t *classModelConfig; // Ignore following pitch; it's too jerky otherwise. if ( !cg_thirdPersonPitchFollow.integer ) { cg.refdefViewAngles[ PITCH ] = 0.0f; } AngleVectors( cg.refdefViewAngles, forward, right, up ); classModelConfig = BG_ClassModelConfig( cg.snap->ps.stats[ STAT_CLASS ] ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg ); // If someone is playing like this, the rest is already taken care of // so just get the firstperson effects and leave. if ( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { CG_OffsetFirstPersonView(); return; } // Get mouse input for camera rotation. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [30, -50] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves here. deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if ( fabs( deltaMousePitch ) < 200.0f ) { mousePitch += deltaMousePitch; } // Handle pitch. rotationAngles[ PITCH ] = mousePitch; rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) ); if ( rotationAngles [ PITCH ] < -90.0f ) { rotationAngles [ PITCH ] = -90.0f; } if ( rotationAngles [ PITCH ] > 90.0f ) { rotationAngles [ PITCH ] = 90.0f; } // Yaw and Roll are much easier. rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ]; rotationAngles[ ROLL ] = 0.0f; // Perform the rotations. AnglesToAxis( rotationAngles, axis ); if ( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) { AxisCopy( axis, rotaxis ); } AxisToAngles( rotaxis, rotationAngles ); // Actually set the viewangles. for ( i = 0; i < 3; i++ ) { cg.refdefViewAngles[ i ] = rotationAngles[ i ]; } // Now run the first person stuff so we get various effects added. CG_OffsetFirstPersonView(); }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ const char *ClientUserinfoChanged( int clientNum, bool forceName ) { gentity_t *ent; const char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; bool revertName = false; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if ( !Info_Validate( userinfo ) ) { trap_SendServerCommand( ent - g_entities, "disconnect \"illegal or malformed userinfo\"" ); trap_DropClient( ent - g_entities, "dropped: illegal or malformed userinfo" ); return "Illegal or malformed userinfo"; } // If their userinfo overflowed, tremded is in the process of disconnecting them. // If we send our own disconnect, it won't work, so just return to prevent crashes later // in this function. This check must come after the Info_Validate call. else if ( !userinfo[ 0 ] ) { return "Empty (overflowed) userinfo"; } // stickyspec toggle s = Info_ValueForKey( userinfo, "cg_stickySpec" ); client->pers.stickySpec = atoi( s ) != 0; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ), client ); if ( strcmp( oldname, newname ) ) { if ( !forceName && client->pers.namelog->nameChangeTime && level.time - client->pers.namelog->nameChangeTime <= g_minNameChangePeriod.value * 1000 ) { trap_SendServerCommand( ent - g_entities, va( "print_tr %s %d", QQ( N_("Name change spam protection (g_minNameChangePeriod = $1$)") ), g_minNameChangePeriod.integer ) ); revertName = true; } else if ( !forceName && g_maxNameChanges.integer > 0 && client->pers.namelog->nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print_tr %s %d", QQ( N_("Maximum name changes reached (g_maxNameChanges = $1$)") ), g_maxNameChanges.integer ) ); revertName = true; } else if ( !forceName && client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, va( "print_tr %s", QQ( N_("You cannot change your name while you are muted") ) ) ); revertName = true; } else if ( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print_tr %s %s %s", QQ( "$1t$ $2$" ), Quote( err ), Quote( newname ) ) ); revertName = true; } else if ( Q_UTF8_Strlen( newname ) > MAX_NAME_CHARACTERS ) { trap_SendServerCommand( ent - g_entities, va( "print_tr %s %d", QQ( N_("Name is too long! Must be less than $1$ characters.") ), MAX_NAME_CHARACTERS ) ); revertName = true; } if ( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : G_UnnamedClientName( client ), sizeof( client->pers.netname ) ); } else { if( G_IsUnnamed( newname ) ) { Q_strncpyz( client->pers.netname, G_UnnamedClientName( client ), sizeof( client->pers.netname ) ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); } if ( !forceName && client->pers.connected == CON_CONNECTED ) { client->pers.namelog->nameChangeTime = level.time; client->pers.namelog->nameChanges++; } if ( *oldname ) { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%s^7\"", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, client->pers.netname ); } } G_namelog_update_name( client ); Info_SetValueForKey(userinfo, "name", client->pers.netname, false); trap_SetUserinfo(clientNum, userinfo); } if ( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassModelConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassModelConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassModelConfig( client->pers.classSelection )->modelName, BG_ClassModelConfig( client->pers.classSelection )->skinName ); if ( BG_ClassModelConfig( client->pers.classSelection )->segmented ) { client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; } else { client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } } Q_strncpyz( model, buffer, sizeof( model ) ); // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if ( atoi( s ) ) { client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; } else { client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; } // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if ( atoi( s ) ) { client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; } else { client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; } // always sprint s = Info_ValueForKey( userinfo, "cg_sprintToggle" ); if ( atoi( s ) ) { client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE; } else { client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE; } // fly speed s = Info_ValueForKey( userinfo, "cg_flySpeed" ); if ( *s ) { client->pers.flySpeed = atoi( s ); } else { client->pers.flySpeed = BG_Class( PCL_NONE )->speed; } // disable blueprint errors s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" ); if ( atoi( s ) ) { client->pers.disableBlueprintErrors = true; } else { client->pers.disableBlueprintErrors = false; } // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( atoi( s ) != 0 ) { // teamoverlay was enabled so we need an update if ( client->pers.teamInfo == 0 ) { client->pers.teamInfo = 1; } } else { client->pers.teamInfo = 0; } s = Info_ValueForKey( userinfo, "cg_unlagged" ); if ( !s[ 0 ] || atoi( s ) != 0 ) { client->pers.useUnlagged = true; } else { client->pers.useUnlagged = false; } Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ), sizeof( client->pers.voice ) ); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.team, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return nullptr; }
static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker, assistant; int attackerClass = -1; const char *message; const char *messageAssisted = nullptr; const char *messageSuicide = nullptr; const char *targetInfo; const char *attackerInfo; const char *assistantInfo; char targetName[ MAX_NAME_LENGTH ]; char attackerName[ MAX_NAME_LENGTH ]; char assistantName[ MAX_NAME_LENGTH ]; gender_t gender; clientInfo_t *ci; team_t attackerTeam, assistantTeam = TEAM_NONE; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; assistant = ent->otherEntityNum3; assistantTeam = (team_t) ( ent->generic1 & 0xFF ); // ugly hack allowing for future expansion(!) mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[ target ]; gender = ci->gender; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { attacker = ENTITYNUM_WORLD; attackerInfo = nullptr; attackerTeam = TEAM_NONE; strcpy( attackerName, "noname" ); } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); attackerTeam = cgs.clientinfo[ attacker ].team; Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ) ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( assistant < 0 || assistant >= MAX_CLIENTS ) { assistantInfo = nullptr; } else { assistantInfo = CG_ConfigString( CS_PLAYERS + assistant ); } if ( assistantTeam < TEAM_NONE || assistantTeam >= NUM_TEAMS ) { assistantTeam = TEAM_NONE; } if ( !assistantInfo ) { strcpy( assistantName, "noname" ); } else { Q_strncpyz( assistantName, Info_ValueForKey( assistantInfo, "n" ), sizeof( assistantName ) ); } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ) ); // check for single client messages if ( cg_emoticonsInMessages.integer ) { if ( mod < MOD_UNKNOWN || mod >= ARRAY_LEN( meansOfDeath ) ) { mod = MOD_UNKNOWN; } if ( meansOfDeath[ mod ].team ) { attackerTeam = meansOfDeath[ mod ].team; } // if the long form is needed, show it; but we need a kill icon for this kill type if ( *meansOfDeath[ mod ].icon == '>' ) { goto is_long_kill_message; } // if there's text for the kill type, print a kill message if ( *meansOfDeath[ mod ].icon ) { if ( meansOfDeath[ mod ].envKill ) { if ( meansOfDeath[ mod ].showAssist && assistantInfo ) { CG_Printf( "%s (+ %s%s^7) %s %s%s\n", teamTag[ attackerTeam ], teamTag[ assistantTeam ], assistantName, meansOfDeath[ mod ].icon, teamTag[ ci->team ], targetName ); } else { CG_Printf( "%s %s %s%s\n", teamTag[ attackerTeam ], meansOfDeath[ mod ].icon, teamTag[ ci->team ], targetName ); } } else if ( attacker == target ) { CG_Printf( "%s %s%s\n", meansOfDeath[ mod ].icon, teamTag[ ci->team ], targetName ); } else { if ( meansOfDeath[ mod ].showAssist && assistantInfo ) { CG_Printf( "%s%s^7 (+ %s%s^7) %s %s%s\n", teamTag[ attackerTeam ], attackerName, teamTag[ assistantTeam ], assistantName, meansOfDeath[ mod ].icon, teamTag[ ci->team ], targetName ); } else { CG_Printf( "%s%s^7 %s %s%s\n", teamTag[ attackerTeam ], attackerName, meansOfDeath[ mod ].icon, teamTag[ ci->team ], targetName ); } // nice big message for teamkills if ( attackerTeam == ci->team && attacker == cg.clientNum ) { CG_CenterPrint( va( _("You killed ^1TEAMMATE^7 %s"), targetName ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } } } } else // Long form { is_long_kill_message: // <- use of this label == needs a kill icon // Messages which contain no killer name switch ( mod ) { // Environmental kills case MOD_FALLING: message = G_( "%s%s ^7fell to death\n" ); break; case MOD_CRUSH: message = G_( "%s%s ^7was crushed to death\n" ); break; case MOD_WATER: message = G_( "%s%s ^7drowned\n" ); break; case MOD_SLIME: message = G_( "%s%s ^7melted\n" ); break; case MOD_LAVA: message = G_( "%s%s ^7took a dip in some lava\n" ); break; case MOD_TRIGGER_HURT: message = G_( "%s%s ^7was in the wrong place\n" ); break; // Building explosions case MOD_HSPAWN: message = G_( "%s%s ^7was caught in the explosion\n" ); break; case MOD_ASPAWN: message = G_( "%s%s ^7was caught in the acid\n" ); break; // Attacked by a building case MOD_MGTURRET: message = G_( "%s%s ^7was gunned down by a turret\n" ); messageAssisted = G_( "%s%s ^7was gunned down by a turret; %s%s^7 assisted\n" ); break; case MOD_ROCKETPOD: message = G_( "%s%s ^7caught a rocket\n" ); messageAssisted = G_( "%s%s ^7caught a rocket; %s%s^7 assisted\n" ); break; case MOD_ATUBE: message = G_( "%s%s ^7was melted by an acid tube\n" ); messageAssisted = G_( "%s%s ^7was melted by an acid tube; %s%s^7 assisted\n" ); break; case MOD_SPIKER: message = G_( "%s%s ^7was flechetted by a spiker\n" ); messageAssisted = G_( "%s%s ^7was flechetted by a spiker; %s%s^7 assisted\n" ); break; case MOD_OVERMIND: message = G_( "%s%s ^7was whipped by the overmind\n" ); messageAssisted = G_( "%s%s ^7was whipped by the overmind; %s%s^7 assisted\n" ); break; case MOD_REACTOR: message = G_( "%s%s ^7was fried by the reactor\n" ); messageAssisted = G_( "%s%s ^7was fried by the reactor; %s%s^7 assisted\n" ); break; case MOD_SLOWBLOB: message = G_( "%s%s ^7couldn't avoid the granger\n" ); messageAssisted = G_( "%s%s ^7couldn't avoid the granger; %s%s^7 assisted\n" ); break; case MOD_SWARM: message = G_( "%s%s ^7was consumed by the swarm\n" ); messageAssisted = G_( "%s%s ^7was consumed by the swarm; %s%s^7 assisted\n" ); break; // Shouldn't happen case MOD_TARGET_LASER: message = G_( "%s%s ^7saw the light\n" ); messageAssisted = G_( "%s%s ^7saw the light; %s%s^7 assisted\n" ); break; default: message = nullptr; break; } if ( message ) { if ( messageAssisted && assistantInfo ) { CG_Printf( messageAssisted, teamTag[ ci->team ], targetName , teamTag[ assistantTeam ], assistantName); } else { CG_Printf( message, teamTag[ ci->team ], targetName ); } return; } // Messages which contain the killer's name switch ( mod ) { case MOD_PAINSAW: message = G_( "%s%s ^7was sawn by %s%s\n" ); messageAssisted = G_( "%s%s ^7was sawn by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_BLASTER: message = G_( "%s%s ^7was blasted by %s%s\n" ); messageAssisted = G_( "%s%s ^7was blasted by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_MACHINEGUN: message = G_( "%s%s ^7was machinegunned by %s%s\n" ); messageAssisted = G_( "%s%s ^7was machinegunned by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_CHAINGUN: message = G_( "%s%s ^7was mowed down by %s%s\n" ); messageAssisted = G_( "%s%s ^7was mowed down by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_SHOTGUN: message = G_( "%s%s ^7was gunned down by %s%s\n" ); messageAssisted = G_( "%s%s ^7was gunned down by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_PRIFLE: message = G_( "%s%s ^7was seared by %s%s^7's pulse blast\n" ); messageAssisted = G_( "%s%s ^7was seared by %s%s^7's pulse blast; %s%s^7 assisted\n" ); break; case MOD_MDRIVER: message = G_( "%s%s ^7was sniped by %s%s\n" ); messageAssisted = G_( "%s%s ^7was sniped by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_LASGUN: message = G_( "%s%s ^7was lasered by %s%s\n" ); messageAssisted = G_( "%s%s ^7was lasered by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_FLAMER: case MOD_FLAMER_SPLASH: message = G_( "%s%s ^7was grilled by %s%s^7's flame\n" ); messageAssisted = G_( "%s%s ^7was grilled by %s%s^7's flame; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was charred to a crisp" ); break; case MOD_BURN: message = G_( "%s%s ^7was burned by %s%s^7's fire\n" ); messageAssisted = G_( "%s%s ^7was burned by %s%s^7's fire; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was burned to death" ); break; case MOD_LCANNON: message = G_( "%s%s ^7was annihilated by %s%s^7's plasma blast\n" ); messageAssisted = G_( "%s%s ^7was annihilated by %s%s^7's plasma blast; %s%s^7 assisted\n" ); break; case MOD_LCANNON_SPLASH: message = G_( "%s%s ^7was irradiated by %s%s^7's plasma blast\n" ); messageAssisted = G_( "%s%s ^7was irradiated by %s%s^7's plasma blast; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was irradiated" ); break; case MOD_GRENADE: message = G_( "%s%s ^7was blown up by %s%s^7's grenade\n" ); messageAssisted = G_( "%s%s ^7was blown up by %s%s^7's grenade; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was blown up" ); break; case MOD_FIREBOMB: message = G_( "%s%s ^7was incinerated by %s%s^7's firebomb\n" ); messageAssisted = G_( "%s%s ^7was incinerated by %s%s^7's firebomb; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was incinerated" ); break; case MOD_ABUILDER_CLAW: message = G_( "%s%s ^7was gently nibbled by %s%s^7's granger\n" ); messageAssisted = G_( "%s%s ^7was gently nibbled by %s%s^7's granger; %s%s^7 assisted\n" ); break; case MOD_LEVEL0_BITE: message = G_( "%s%s ^7was bitten by %s%s\n" ); messageAssisted = G_( "%s%s ^7was bitten by %s%s^7; %s%s^7 assisted\n" ); break; case MOD_LEVEL1_CLAW: message = G_( "%s%s ^7was sliced by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was sliced by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL1; break; case MOD_LEVEL2_CLAW: message = G_( "%s%s ^7was shredded by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was shredded by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL2; break; case MOD_LEVEL2_ZAP: message = G_( "%s%s ^7was electrocuted by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was electrocuted by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL2; break; case MOD_LEVEL3_CLAW: message = G_( "%s%s ^7was chomped by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was chomped by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL3_POUNCE: message = G_( "%s%s ^7was pounced upon by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was pounced upon by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL3_BOUNCEBALL: message = G_( "%s%s ^7was barbed by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was barbed by %s%s^7's %s^7; %s%s^7 assisted\n" ); messageSuicide = G_( "%s%s ^7was barbed" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL4_CLAW: message = G_( "%s%s ^7was mauled by %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7was mauled by %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL4; break; case MOD_LEVEL4_TRAMPLE: message = G_( "%s%s ^7should have gotten out of the way of %s%s^7's %s\n" ); messageAssisted = G_( "%s%s ^7should have gotten out of the way of %s%s^7's %s^7; %s%s^7 assisted\n" ); attackerClass = PCL_ALIEN_LEVEL4; break; case MOD_WEIGHT_H: case MOD_WEIGHT_A: message = G_( "%s%s ^7was crushed under %s%s^7's weight\n" ); messageAssisted = G_( "%s%s ^7was crushed under %s%s^7's weight; %s%s^7 assisted\n" ); break; case MOD_POISON: message = G_( "%s%s ^7should have used a medkit against %s%s^7's poison\n" ); messageAssisted = G_( "%s%s ^7should have used a medkit against %s%s^7's poison; %s%s^7 assisted\n" ); break; case MOD_TELEFRAG: message = G_( "%s%s ^7tried to invade %s%s^7's personal space\n" ); break; default: message = G_( "%s%s ^7was killed by %s%s\n" ); messageAssisted = G_( "%s%s ^7was killed by %s%s^7 and %s%s\n" ); messageSuicide = G_( "%s%s ^7committed suicide\n" ); break; } if ( message ) { // shouldn't need to do this here, but it avoids char attackerClassName[ 64 ]; if ( attackerClass == -1 ) { *attackerClassName = 0; } else { Q_strncpyz( attackerClassName, _( BG_ClassModelConfig( attackerClass )->humanName ), sizeof( attackerClassName ) ); } // Argument order: victim, attacker, [class,] [assistant]. Each has team tag first. if ( messageSuicide && attacker == target ) { CG_Printf( messageSuicide, teamTag[ ci->team ], targetName ); } else if ( messageAssisted && assistantInfo ) { if ( attackerClass != -1 ) { CG_Printf( messageAssisted, teamTag[ ci->team ], targetName, teamTag[ attackerTeam ], attackerName, attackerClassName, teamTag[ assistantTeam ], assistantName ); } else { CG_Printf( messageAssisted, teamTag[ ci->team ], targetName, teamTag[ attackerTeam ], attackerName, teamTag[ assistantTeam ], assistantName ); } } else { CG_Printf( message, teamTag[ ci->team ], targetName, teamTag[ attackerTeam ], attackerName, attackerClassName ); } if ( attackerTeam == ci->team && attacker == cg.clientNum && attacker != target ) { CG_CenterPrint( va( _("You killed ^1TEAMMATE^7 %s"), targetName ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } return; } // we don't know what it was CG_Printf( G_( "%s%s^7 died\n" ), teamTag[ ci->team ], targetName ); } }
/* ============== CG_Menu ============== */ void CG_Menu( int menuType, int arg ) { int menu = -1; // Menu to open const char *longMsg = nullptr; // command parameter const char *shortMsg = nullptr; // non-modal version of message switch ( menuType ) { case MN_WELCOME: break; case MN_TEAM: menu = ROCKETMENU_TEAMSELECT; break; case MN_A_CLASS: menu = ROCKETMENU_ALIENSPAWN; break; case MN_H_SPAWN: menu = ROCKETMENU_HUMANSPAWN; break; case MN_A_BUILD: menu = ROCKETMENU_ALIENBUILD; break; case MN_H_BUILD: menu = ROCKETMENU_HUMANBUILD; break; case MN_H_ARMOURY: menu = ROCKETMENU_ARMOURYBUY; break; case MN_H_UNKNOWNITEM: shortMsg = "Unknown item"; break; case MN_A_TEAMFULL: longMsg = _("The alien team has too many players. Please wait until slots " "become available or join the human team."); shortMsg = _("The alien team has too many players"); break; case MN_H_TEAMFULL: longMsg = _("The human team has too many players. Please wait until slots " "become available or join the alien team."); shortMsg = _("The human team has too many players"); break; case MN_A_TEAMLOCKED: longMsg = _("The alien team is locked. You cannot join the aliens " "at this time."); shortMsg = _("The alien team is locked"); break; case MN_H_TEAMLOCKED: longMsg = _("The human team is locked. You cannot join the humans " "at this time."); shortMsg = _("The human team is locked"); break; case MN_PLAYERLIMIT: longMsg = _("The maximum number of playing clients has been reached. " "Please wait until slots become available."); shortMsg = _("No free player slots"); break; case MN_WARMUP: longMsg = _("You must wait until the warmup time is finished " "before joining a team. "); shortMsg = _("You cannot join a team during warmup."); break; //=============================== case MN_CMD_CHEAT: //longMsg = "This action is considered cheating. It can only be used " // "in cheat mode, which is not enabled on this server."; shortMsg = _("Cheats are not enabled on this server"); break; case MN_CMD_CHEAT_TEAM: shortMsg = _("Cheats are not enabled on this server, so " "you may not use this command while on a team"); break; case MN_CMD_TEAM: //longMsg = "You must be on a team to perform this action. Join the alien" // "or human team and try again."; shortMsg = _("Join a team first"); break; case MN_CMD_SPEC: //longMsg = "You may not perform this action while on a team. Become a " // "spectator before trying again."; shortMsg = _("You can only use this command when spectating"); break; case MN_CMD_ALIEN: //longMsg = "You must be on the alien team to perform this action."; shortMsg = _("Must be alien to use this command"); break; case MN_CMD_HUMAN: //longMsg = "You must be on the human team to perform this action."; shortMsg = _("Must be human to use this command"); break; case MN_CMD_ALIVE: //longMsg = "You must be alive to perform this action."; shortMsg = _("Must be alive to use this command"); break; //=============================== case MN_B_NOROOM: longMsg = _("There is no room to build here. Move until the structure turns " "translucent green, indicating a valid build location."); shortMsg = _("There is no room to build here"); break; case MN_B_NORMAL: longMsg = _("Cannot build on this surface. The surface is too steep or " "unsuitable for building. Please choose another site for this " "structure."); shortMsg = _("Cannot build on this surface"); break; case MN_B_CANNOT: longMsg = nullptr; shortMsg = _("You cannot build that structure"); break; case MN_B_LASTSPAWN: longMsg = _("This action would remove your team's last spawn point, " "which often quickly results in a loss. Try building more " "spawns."); shortMsg = _("You may not deconstruct the last spawn"); break; case MN_B_MAINSTRUCTURE: longMsg = _("The main structure is protected against instant removal. " "When it is marked, you can move it to another place by " "building it there."); shortMsg = _("You may not deconstruct this structure"); break; case MN_B_DISABLED: longMsg = _("Building has been disabled on the server for your team."); shortMsg = _("Building has been disabled for your team"); break; case MN_B_REVOKED: longMsg = _("Your teammates have lost faith in your ability to build " "for the team. You will not be allowed to build until your " "team votes to reinstate your building rights."); shortMsg = _("Your building rights have been revoked"); break; case MN_B_SURRENDER: longMsg = _("Your team has decided to admit defeat and concede the game: " "There's no point in building anything anymore."); shortMsg = _("Cannot build after admitting defeat"); break; case MN_H_NOBP: longMsg = _("There are no resources remaining. Free up resources by " "marking existing buildables for deconstruction."); shortMsg = _("There are no resources remaining"); break; case MN_H_NOTPOWERED: longMsg = _("This buildable is not powered. Build a Reactor and/or Repeater " "in order to power it."); shortMsg = _("This buildable is not powered"); break; case MN_H_NOPOWERHERE: longMsg = _("There is not enough power in this area. Keep a distance to other " "buildables or build a repeater to increase the local capacity."); shortMsg = _("There is not enough power here"); break; case MN_H_NOREACTOR: longMsg = _("There is no reactor and the local power supply is insufficient. " "Build the reactor or a repeater to increase the local capacity."); shortMsg = _("There is no reactor and the local power supply is insufficient"); break; case MN_H_ONEREACTOR: longMsg = _("There can only be one Reactor. Mark the existing one if you " "wish to move it."); shortMsg = _("There can only be one Reactor"); break; case MN_H_NOSLOTS: longMsg = _("You have no room to carry this. Please sell any conflicting " "upgrades before purchasing this item."); shortMsg = _("You have no room to carry this"); break; case MN_H_NOFUNDS: longMsg = _("Insufficient funds. You do not have enough credits to perform " "this action."); shortMsg = _("Insufficient funds"); break; case MN_H_ITEMHELD: longMsg = _("You already hold this item. It is not possible to carry multiple " "items of the same type."); shortMsg = _("You already hold this item"); break; case MN_H_NOARMOURYHERE: longMsg = _("You must be near a powered Armoury in order to purchase " "weapons, upgrades or ammunition."); shortMsg = _("You must be near a powered Armoury"); break; case MN_H_NOENERGYAMMOHERE: longMsg = _("You must be near a Reactor or a powered Armoury or Repeater " "in order to purchase energy ammunition."); shortMsg = _("You must be near a Reactor or a powered Armoury or Repeater"); break; case MN_H_NOROOMARMOURCHANGE: longMsg = _("There is not enough room here to change armour."); shortMsg = _("Not enough room here to change armour."); break; case MN_H_ARMOURYBUILDTIMER: longMsg = _("You are not allowed to buy or sell weapons until your " "build timer has expired."); shortMsg = _("You can not buy or sell weapons until your build timer " "expires"); break; case MN_H_DEADTOCLASS: shortMsg = _("You must be dead to use the class command"); break; case MN_H_UNKNOWNSPAWNITEM: shortMsg = _("Unknown starting item"); break; //=============================== case MN_A_NOCREEP: longMsg = _("There is no creep here. You must build near existing Eggs or " "the Overmind. Alien structures will not support themselves."); shortMsg = _("There is no creep here"); break; case MN_A_NOOVMND: longMsg = _("There is no Overmind. An Overmind must be built to control " "the structure you tried to place."); shortMsg = _("There is no Overmind"); break; case MN_A_ONEOVERMIND: longMsg = _("There can only be one Overmind. Deconstruct the existing one if you " "wish to move it."); shortMsg = _("There can only be one Overmind"); break; case MN_A_NOBP: longMsg = _("The Overmind cannot control any more structures. Deconstruct existing " "structures to build more."); shortMsg = _("The Overmind cannot control any more structures"); break; case MN_A_NOEROOM: longMsg = _("There is no room to evolve here. Move away from walls or other " "nearby objects and try again."); shortMsg = _("There is no room to evolve here"); break; case MN_A_TOOCLOSE: longMsg = _("This location is too close to the enemy to evolve. Move away " "from the enemy's presence and try again."); shortMsg = _("This location is too close to the enemy to evolve"); break; case MN_A_NOOVMND_EVOLVE: longMsg = _("There is no Overmind. An Overmind must be built to allow " "you to upgrade."); shortMsg = _("There is no Overmind"); break; case MN_A_EVOLVEBUILDTIMER: longMsg = _("You cannot evolve until your build timer has expired."); shortMsg = _("You cannot evolve until your build timer expires"); break; case MN_A_INFEST: trap_Cvar_Set( "ui_currentClass", va( "%d %d", cg.snap->ps.stats[ STAT_CLASS ], cg.snap->ps.persistant[ PERS_CREDIT ] ) ); menu = ROCKETMENU_ALIENEVOLVE; break; case MN_A_CANTEVOLVE: shortMsg = va( _("You cannot evolve into a %s"), _( BG_ClassModelConfig( arg )->humanName ) ); break; case MN_A_EVOLVEWALLWALK: shortMsg = _("You cannot evolve while wallwalking"); break; case MN_A_UNKNOWNCLASS: shortMsg = _("Unknown class"); break; case MN_A_CLASSNOTSPAWN: shortMsg = va( _("You cannot spawn as a %s"), _( BG_ClassModelConfig( arg )->humanName ) ); break; case MN_A_CLASSNOTALLOWED: shortMsg = va( _("The %s is not allowed"), _( BG_ClassModelConfig( arg )->humanName ) ); break; case MN_A_CLASSLOCKED: shortMsg = va( _("The %s has not been unlocked yet"), _( BG_ClassModelConfig( arg )->humanName ) ); break; default: Com_Printf(_( "cgame: debug: no such menu %d\n"), menu ); } if ( menu > 0 ) { Rocket_DocumentAction( rocketInfo.menu[ menu ].id, "show" ); } else if ( longMsg && cg_disableWarningDialogs.integer == 0 ) { CG_CenterPrint( longMsg, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); if ( shortMsg && cg_disableWarningDialogs.integer < 2 ) { CG_Printf( "%s\n", shortMsg ); } } else if ( shortMsg && cg_disableWarningDialogs.integer < 2 ) { CG_Printf( "%s\n", shortMsg ); } }
/* ============== CG_Menu ============== */ void CG_Menu( int menu, int arg ) { const char *cmd; // command to send const char *longMsg = NULL; // command parameter const char *shortMsg = NULL; // non-modal version of message const char *dialog; dialogType_t type = 0; // controls which cg_disable var will switch it off switch ( cg.snap->ps.stats[ STAT_TEAM ] ) { case TEAM_ALIENS: dialog = "menu tremulous_alien_dialog\n"; break; case TEAM_HUMANS: dialog = "menu tremulous_human_dialog\n"; break; default: dialog = "menu tremulous_default_dialog\n"; } cmd = dialog; switch ( menu ) { case MN_TEAM: cmd = "menu tremulous_teamselect\n"; type = DT_INTERACTIVE; break; case MN_A_CLASS: cmd = "menu tremulous_alienclass\n"; type = DT_INTERACTIVE; break; case MN_H_SPAWN: cmd = "menu tremulous_humanitem\n"; type = DT_INTERACTIVE; break; case MN_A_BUILD: cmd = "menu tremulous_alienbuild\n"; type = DT_INTERACTIVE; break; case MN_H_BUILD: cmd = "menu tremulous_humanbuild\n"; type = DT_INTERACTIVE; break; case MN_H_ARMOURY: cmd = "menu tremulous_humanarmoury\n"; type = DT_INTERACTIVE; break; case MN_H_UNKNOWNITEM: shortMsg = "Unknown item"; type = DT_ARMOURYEVOLVE; break; case MN_A_TEAMFULL: longMsg = _("The alien team has too many players. Please wait until slots " "become available or join the human team."); shortMsg = _("The alien team has too many players"); type = DT_COMMAND; break; case MN_H_TEAMFULL: longMsg = _("The human team has too many players. Please wait until slots " "become available or join the alien team."); shortMsg = _("The human team has too many players"); type = DT_COMMAND; break; case MN_A_TEAMLOCKED: longMsg = _("The alien team is locked. You cannot join the aliens " "at this time."); shortMsg = _("The alien team is locked"); type = DT_COMMAND; break; case MN_H_TEAMLOCKED: longMsg = _("The human team is locked. You cannot join the humans " "at this time."); shortMsg = _("The human team is locked"); type = DT_COMMAND; break; case MN_PLAYERLIMIT: longMsg = _("The maximum number of playing clients has been reached. " "Please wait until slots become available."); shortMsg = _("No free player slots"); type = DT_COMMAND; break; case MN_WARMUP: longMsg = _("You must wait until the warmup time is finished " "before joining a team. "); shortMsg = _("You cannot join a team during warmup."); type = DT_COMMAND; break; //=============================== // Since cheating commands have no default binds, they will often be done // via console. In light of this, perhaps opening a menu is // counterintuitive case MN_CMD_CHEAT: //longMsg = "This action is considered cheating. It can only be used " // "in cheat mode, which is not enabled on this server."; shortMsg = _("Cheats are not enabled on this server"); type = DT_COMMAND; break; case MN_CMD_CHEAT_TEAM: shortMsg = _("Cheats are not enabled on this server, so " "you may not use this command while on a team"); type = DT_COMMAND; break; case MN_CMD_TEAM: //longMsg = "You must be on a team to perform this action. Join the alien" // "or human team and try again."; shortMsg = _("Join a team first"); type = DT_COMMAND; break; case MN_CMD_SPEC: //longMsg = "You may not perform this action while on a team. Become a " // "spectator before trying again."; shortMsg = _("You can only use this command when spectating"); type = DT_COMMAND; break; case MN_CMD_ALIEN: //longMsg = "You must be on the alien team to perform this action."; shortMsg = _("Must be alien to use this command"); type = DT_COMMAND; break; case MN_CMD_HUMAN: //longMsg = "You must be on the human team to perform this action."; shortMsg = _("Must be human to use this command"); type = DT_COMMAND; break; case MN_CMD_ALIVE: //longMsg = "You must be alive to perform this action."; shortMsg = _("Must be alive to use this command"); type = DT_COMMAND; break; //=============================== case MN_B_NOROOM: longMsg = _("There is no room to build here. Move until the structure turns " "translucent green, indicating a valid build location."); shortMsg = _("There is no room to build here"); type = DT_BUILD; break; case MN_B_NORMAL: longMsg = _("Cannot build on this surface. The surface is too steep or " "unsuitable for building. Please choose another site for this " "structure."); shortMsg = _("Cannot build on this surface"); type = DT_BUILD; break; case MN_B_CANNOT: longMsg = NULL; shortMsg = _("You cannot build that structure"); type = DT_BUILD; break; // FIXME: MN_H_ and MN_A_? case MN_B_LASTSPAWN: longMsg = _("This action would remove your team's last spawn point, " "which often quickly results in a loss. Try building more " "spawns."); shortMsg = _("You may not deconstruct the last spawn"); type = DT_MISC_CP; break; case MN_B_SUDDENDEATH: longMsg = _("Neither team has prevailed after a certain time and the " "game has entered Sudden Death. During Sudden Death " "building is not allowed."); shortMsg = _("Cannot build during Sudden Death"); type = DT_BUILD; break; case MN_B_REVOKED: longMsg = _("Your teammates have lost faith in your ability to build " "for the team. You will not be allowed to build until your " "team votes to reinstate your building rights."); shortMsg = _("Your building rights have been revoked"); type = DT_BUILD; break; case MN_B_SURRENDER: longMsg = _("Your team has decided to admit defeat and concede the game:" "traitors and cowards are not allowed to build."); // too harsh? shortMsg = _("Building is denied to traitorous cowards"); type = DT_MISC_CP; break; //=============================== case MN_H_NOBP: if ( cgs.markDeconstruct ) { longMsg = _("There is no power remaining. Free up power by marking " "existing buildable objects."); } else { longMsg = _("There is no power remaining. Free up power by deconstructing " "existing buildable objects."); } shortMsg = _("There is no power remaining"); type = DT_BUILD; break; case MN_H_NOTPOWERED: longMsg = _("This buildable is not powered. Build a Reactor and/or Repeater " "in order to power it."); shortMsg = _("This buildable is not powered"); type = DT_BUILD; break; case MN_H_ONEREACTOR: longMsg = _("There can only be one Reactor. Deconstruct the existing one if you " "wish to move it."); shortMsg = _("There can only be one Reactor"); type = DT_BUILD; break; case MN_H_NOPOWERHERE: longMsg = _("There is no power here. If available, a Repeater may be used to " "transmit power to this location."); shortMsg = _("There is no power here"); type = DT_BUILD; break; case MN_H_NODCC: longMsg = _("There is no Defense Computer. A Defense Computer is needed to " "build this."); shortMsg = _("There is no Defense Computer"); type = DT_BUILD; break; case MN_H_RPTPOWERHERE: longMsg = _("This area already has power. A Repeater is not required here."); shortMsg = _("This area already has power"); type = DT_BUILD; break; case MN_H_NOSLOTS: longMsg = _("You have no room to carry this. Please sell any conflicting " "upgrades before purchasing this item."); shortMsg = _("You have no room to carry this"); type = DT_ARMOURYEVOLVE; break; case MN_H_NOFUNDS: longMsg = _("Insufficient funds. You do not have enough credits to perform " "this action."); shortMsg = _("Insufficient funds"); type = DT_ARMOURYEVOLVE; break; case MN_H_ITEMHELD: longMsg = _("You already hold this item. It is not possible to carry multiple " "items of the same type."); shortMsg = _("You already hold this item"); type = DT_ARMOURYEVOLVE; break; case MN_H_NOARMOURYHERE: longMsg = _("You must be near a powered Armoury in order to purchase " "weapons, upgrades or ammunition."); shortMsg = _("You must be near a powered Armoury"); type = DT_ARMOURYEVOLVE; break; case MN_H_NOENERGYAMMOHERE: longMsg = _("You must be near a Reactor or a powered Armoury or Repeater " "in order to purchase energy ammunition."); shortMsg = _("You must be near a Reactor or a powered Armoury or Repeater"); type = DT_ARMOURYEVOLVE; break; case MN_H_NOROOMBSUITON: longMsg = _("There is not enough room here to put on a Battle Suit. " "Make sure you have enough head room to climb in."); shortMsg = _("Not enough room here to put on a Battle Suit"); type = DT_ARMOURYEVOLVE; break; case MN_H_NOROOMBSUITOFF: longMsg = _("There is not enough room here to take off your Battle Suit. " "Make sure you have enough head room to climb out."); shortMsg = _("Not enough room here to take off your Battle Suit"); type = DT_ARMOURYEVOLVE; break; case MN_H_ARMOURYBUILDTIMER: longMsg = _("You are not allowed to buy or sell weapons until your " "build timer has expired."); shortMsg = _("You can not buy or sell weapons until your build timer " "expires"); type = DT_ARMOURYEVOLVE; break; case MN_H_DEADTOCLASS: shortMsg = _("You must be dead to use the class command"); type = DT_COMMAND; break; case MN_H_UNKNOWNSPAWNITEM: shortMsg = _("Unknown starting item"); type = DT_COMMAND; break; //=============================== case MN_A_NOCREEP: longMsg = _("There is no creep here. You must build near existing Eggs or " "the Overmind. Alien structures will not support themselves."); shortMsg = _("There is no creep here"); type = DT_BUILD; break; case MN_A_NOOVMND: longMsg = _("There is no Overmind. An Overmind must be built to control " "the structure you tried to place."); shortMsg = _("There is no Overmind"); type = DT_BUILD; break; case MN_A_ONEOVERMIND: longMsg = _("There can only be one Overmind. Deconstruct the existing one if you " "wish to move it."); shortMsg = _("There can only be one Overmind"); type = DT_BUILD; break; case MN_A_NOBP: longMsg = _("The Overmind cannot control any more structures. Deconstruct existing " "structures to build more."); shortMsg = _("The Overmind cannot control any more structures"); type = DT_BUILD; break; case MN_A_NOEROOM: longMsg = _("There is no room to evolve here. Move away from walls or other " "nearby objects and try again."); shortMsg = _("There is no room to evolve here"); type = DT_ARMOURYEVOLVE; break; case MN_A_TOOCLOSE: longMsg = _("This location is too close to the enemy to evolve. Move away " "from the enemy's presence and try again."); shortMsg = _("This location is too close to the enemy to evolve"); type = DT_ARMOURYEVOLVE; break; case MN_A_NOOVMND_EVOLVE: longMsg = _("There is no Overmind. An Overmind must be built to allow " "you to upgrade."); shortMsg = _("There is no Overmind"); type = DT_ARMOURYEVOLVE; break; case MN_A_EVOLVEBUILDTIMER: longMsg = _("You cannot evolve until your build timer has expired."); shortMsg = _("You cannot evolve until your build timer expires"); type = DT_ARMOURYEVOLVE; break; case MN_A_INFEST: trap_Cvar_Set( "ui_currentClass", va( "%d %d", cg.snap->ps.stats[ STAT_CLASS ], cg.snap->ps.persistant[ PERS_CREDIT ] ) ); cmd = "menu tremulous_alienupgrade\n"; type = DT_INTERACTIVE; break; case MN_A_CANTEVOLVE: shortMsg = va( _("You cannot evolve into a %s"), _( BG_ClassModelConfig( arg )->humanName ) ); type = DT_ARMOURYEVOLVE; break; case MN_A_EVOLVEWALLWALK: shortMsg = _("You cannot evolve while wallwalking"); type = DT_ARMOURYEVOLVE; break; case MN_A_UNKNOWNCLASS: shortMsg = _("Unknown class"); type = DT_ARMOURYEVOLVE; break; case MN_A_CLASSNOTSPAWN: shortMsg = va( _("You cannot spawn as a %s"), _( BG_ClassModelConfig( arg )->humanName ) ); type = DT_ARMOURYEVOLVE; break; case MN_A_CLASSNOTALLOWED: shortMsg = va( _("The %s is not allowed"), _( BG_ClassModelConfig( arg )->humanName ) ); type = DT_ARMOURYEVOLVE; break; case MN_A_CLASSNOTATSTAGE: shortMsg = va( _("The %s is not allowed at Stage %d"), _( BG_ClassModelConfig( arg )->humanName ), cgs.alienStage + 1 ); type = DT_ARMOURYEVOLVE; break; default: Com_Printf(_( "cgame: debug: no such menu %d\n"), menu ); } switch ( type ) { case DT_BUILD: case DT_ARMOURYEVOLVE: case DT_MISC_CP: // menu open? we need to use the modal dbox // menu closed? we want to centre print if ( !trap_Cvar_VariableIntegerValue( "ui_menuIsOpen" ) ) { cmd = NULL; // only a short message? make the long message the same longMsg = longMsg ? longMsg : shortMsg; } default: break; } if ( type == DT_ARMOURYEVOLVE && cg_disableUpgradeDialogs.integer ) { return; } if ( type == DT_BUILD && cg_disableBuildDialogs.integer ) { return; } if ( type == DT_COMMAND && cg_disableCommandDialogs.integer ) { return; } if ( cmd && cmd != dialog ) { trap_SendConsoleCommand( cmd ); } else if ( longMsg && cg_disableWarningDialogs.integer == 0 ) { if ( cmd ) { trap_Cvar_Set( "ui_dialog", longMsg ); trap_SendConsoleCommand( cmd ); } else { CG_CenterPrint( longMsg, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); if ( shortMsg && cg_disableWarningDialogs.integer < 2 ) { CG_Printf( "%s\n", shortMsg ); } } } else if ( shortMsg && cg_disableWarningDialogs.integer < 2 ) { CG_Printf( "%s\n", shortMsg ); } }
/* ============ 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; }