/* =============== 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; classConfig_t* classConfig; // Ignore following pitch; it's too jerky otherwise. if( !cg_thirdPersonPitchFollow.integer ) cg.refdefViewAngles[ PITCH ] = 0.0f; AngleVectors( cg.refdefViewAngles, forward, right, up ); classConfig = BG_ClassConfig( cg.snap->ps.stats[ STAT_CLASS ] ); VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classConfig->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. ============ */ char *ClientUserinfoChanged( int clientNum, qboolean forceName ) { gentity_t *ent; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; 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\n\"" ); 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 ) ); 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 \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( !forceName && g_maxNameChanges.integer > 0 && client->pers.namelog->nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !forceName && client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { G_CensorString( client->pers.netname, newname, sizeof( client->pers.netname ), ent ); 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\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); } } G_namelog_update_name( client ); } 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_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) 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 = qtrue; else client->pers.disableBlueprintErrors = qfalse; // 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 = qtrue; else client->pers.useUnlagged = qfalse; 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.teamSelection, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return NULL; }
/* =========== 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. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; 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) ) strcpy( userinfo, "\\name\\badinfo" ); // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { // in case we need to revert and there's no oldname if( client->pers.connected != CON_CONNECTED ) Q_strncpyz( oldname, "UnnamedPlayer", sizeof( oldname ) ); if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname ) ); G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s\" -> \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; 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_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) 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; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( !*s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); 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\\c1\\%s\\c2\\%s\\" "hc\\%i\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, c1, c2, client->pers.maxHealth, BG_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
/* ============== 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"); 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"); 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_ClassConfig( 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_ClassConfig( arg )->humanName ); type = DT_ARMOURYEVOLVE; break; case MN_A_CLASSNOTALLOWED: shortMsg = va( _("The %s is not allowed"), BG_ClassConfig( arg )->humanName ); type = DT_ARMOURYEVOLVE; break; case MN_A_CLASSNOTATSTAGE: shortMsg = va( _("The %s is not allowed at Stage %d"), BG_ClassConfig( arg )->humanName, cgs.alienStage + 1 ); type = DT_ARMOURYEVOLVE; break; default: Com_Printf(_( "cgame: debug: no such menu %d\n"), menu ); } 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 != dialog ) { trap_SendConsoleCommand( cmd ); } else if ( longMsg && cg_disableWarningDialogs.integer == 0 ) { trap_Cvar_Set( "ui_dialog", longMsg ); trap_SendConsoleCommand( cmd ); } else if ( shortMsg && cg_disableWarningDialogs.integer < 2 ) { CG_Printf( "%s\n", shortMsg ); } }
/* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; char *message; char *message2; const char *targetInfo; const char *attackerInfo; char targetName[ MAX_NAME_LENGTH ]; char attackerName[ MAX_NAME_LENGTH ]; char className[ 64 ]; gender_t gender; clientInfo_t *ci; qboolean teamKill = 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( ci && 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 )); message2 = ""; // check for single client messages switch( mod ) { case MOD_FALLING: message = "fell fowl to gravity"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "forgot to pack a snorkel"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "did a back flip into the lava"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; case MOD_HSPAWN: message = "should have run further"; break; case MOD_ASPAWN: message = "shouldn't have trod in the acid"; break; case MOD_MGTURRET: message = "was gunned down by a turret"; break; case MOD_TESLAGEN: message = "was zapped by a tesla generator"; break; case MOD_ATUBE: message = "was melted by an acid tube"; break; case MOD_OVERMIND: message = "got too close to the overmind"; break; case MOD_REACTOR: message = "got too close to the reactor"; break; case MOD_SLOWBLOB: message = "should have visited a medical station"; break; case MOD_SWARM: message = "was hunted down by the swarm"; break; case MOD_BUSH://ROTAXfun message = "was lost in bush"; break; default: message = NULL; break; } if( !message && attacker == target ) { switch( mod ) { case MOD_FLAMER_SPLASH: if( gender == GENDER_FEMALE ) message = "toasted herself"; else if( gender == GENDER_NEUTER ) message = "toasted itself"; else message = "toasted himself"; break; case MOD_LCANNON_SPLASH: if( gender == GENDER_FEMALE ) message = "irradiated herself"; else if( gender == GENDER_NEUTER ) message = "irradiated itself"; else message = "irradiated himself"; break; case MOD_GRENADE: if( gender == GENDER_FEMALE ) message = "blew herself up"; else if( gender == GENDER_NEUTER ) message = "blew itself up"; else message = "blew himself up"; break; case MOD_LEVEL3_BOUNCEBALL: if( gender == GENDER_FEMALE ) message = "sniped herself"; else if( gender == GENDER_NEUTER ) message = "sniped itself"; else message = "sniped himself"; break; case MOD_PRIFLE: if( gender == GENDER_FEMALE ) message = "pulse rifled herself"; else if( gender == GENDER_NEUTER ) message = "pulse rifled itself"; else message = "pulse rifled himself"; break; //ROTAXfun case MOD_MINE: if( gender == GENDER_FEMALE ) message = "was betrayed by own mine"; else if( gender == GENDER_NEUTER ) message = "it betrayed by own mine"; else message = "was betrayed by own mine"; break; case MOD_FLARE: if( gender == GENDER_FEMALE ) message = "was terminated by own flare"; else if( gender == GENDER_NEUTER ) message = "it terminated by own flare"; else message = "was terminated by own flare"; break; case MOD_ABOMB: message = "bombed himself up"; break; default: if( gender == GENDER_FEMALE ) message = "killed herself"; else if( gender == GENDER_NEUTER ) message = "killed itself"; else message = "killed himself"; break; } } if( message ) { CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message ); 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: message = "was sawn by"; break; case MOD_BLASTER: message = "was blasted by"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_CHAINGUN: message = "was chaingunned by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_PRIFLE: message = "was pulse rifled by"; break; case MOD_MDRIVER: message = "was mass driven by"; break; case MOD_LASGUN: message = "was lasgunned by"; break; case MOD_FLAMER: message = "was grilled by"; message2 = "'s flamer"; break; case MOD_FLAMER_SPLASH: message = "was toasted by"; message2 = "'s flamer"; break; case MOD_LCANNON: message = "felt the full force of"; message2 = "'s lucifer cannon"; break; case MOD_LCANNON_SPLASH: message = "was caught in the fallout of"; message2 = "'s lucifer cannon"; break; case MOD_GRENADE: message = "couldn't escape"; message2 = "'s grenade"; break; case MOD_MINE://ROTACAkfun message = "found"; message2 = "'s mine"; break; case MOD_FLARE: message = "tasted"; message2 = "'s light"; break; case MOD_ABUILDER_CLAW: message = "should leave"; message2 = "'s buildings alone"; break; case MOD_LEVEL0_BITE: message = "was bitten by"; break; case MOD_LEVEL1_CLAW: message = "was swiped by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName ); message2 = className; break; case MOD_LEVEL2_CLAW: message = "was clawed by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName ); message2 = className; break; case MOD_LEVEL2_ZAP: message = "was zapped by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName ); message2 = className; break; case MOD_LEVEL3_CLAW: message = "was chomped by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName ); message2 = className; break; case MOD_LEVEL3_POUNCE: message = "was pounced upon by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName ); message2 = className; break; case MOD_LEVEL3_BOUNCEBALL: message = "was sniped by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName ); message2 = className; break; case MOD_LEVEL4_CLAW: message = "was mauled by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName ); message2 = className; break; case MOD_LEVEL4_TRAMPLE: message = "should have gotten out of the way of"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName ); message2 = className; break; case MOD_LEVEL4_CRUSH: message = "was crushed under"; message2 = "'s weight"; break; case MOD_POISON: message = "should have used a medkit against"; message2 = "'s poison"; break; case MOD_LEVEL1_PCLOUD: message = "was gassed by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName ); message2 = className; break; case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; case MOD_ABOMB://ROTAXfun message = "was bombed by"; Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName ); message2 = className; break; default: message = "was killed by"; break; } if( message ) { CG_Printf( "%s" S_COLOR_WHITE " %s %s%s" S_COLOR_WHITE "%s\n", targetName, message, ( teamKill ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "", attackerName, message2 ); if( teamKill && attacker == cg.clientNum ) { CG_CenterPrint( va ( "You killed " S_COLOR_RED "TEAMMATE " S_COLOR_WHITE "%s", targetName ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } return; } } // we don't know what it was CG_Printf( "%s" S_COLOR_WHITE " died\n", targetName ); }
/* ============= 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; 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 ( ci && 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: message = G_( "%s ^7was gunned down by a turret\n" ); break; case MOD_TESLAGEN: message = G_( "%s ^7was zapped by a tesla generator\n" ); break; case MOD_ATUBE: message = G_( "%s ^7was melted by an acid tube\n" ); break; case MOD_OVERMIND: message = G_( "%s ^7got too close to the overmind\n" ); break; case MOD_REACTOR: 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: 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: message = G_( "%s ^7toasted self\n" ); break; case MOD_LCANNON_SPLASH: message = G_( "%s ^7irradiated self\n" ); break; case MOD_GRENADE: message = G_( "%s ^7blew self up\n" ); break; case MOD_LEVEL3_BOUNCEBALL: message = G_( "%s ^7sniped self\n" ); break; case MOD_PRIFLE: 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: message = G_( "%s ^7was sawn by %s%s\n" ); break; case MOD_BLASTER: message = G_( "%s ^7was blasted by %s%s\n" ); break; case MOD_MACHINEGUN: message = G_( "%s ^7was machinegunned by %s%s\n" ); break; case MOD_CHAINGUN: message = G_( "%s ^7was chaingunned by %s%s\n" ); break; case MOD_SHOTGUN: message = G_( "%s ^7was gunned down by %s%s\n" ); break; case MOD_PRIFLE: message = G_( "%s ^7was pulse rifled by %s%s\n" ); break; case MOD_MDRIVER: message = G_( "%s ^7was mass driven by %s%s\n" ); break; case MOD_LASGUN: message = G_( "%s ^7was lasgunned by %s%s\n" ); break; case MOD_FLAMER: message = G_( "%s ^7was grilled by %s%s^7's flamer\n" ); break; case MOD_FLAMER_SPLASH: message = G_( "%s ^7was toasted by %s%s^7's flamer\n" ); break; case MOD_LCANNON: message = G_( "%s ^7felt the full force of %s%s^7's lucifer cannon\n" ); break; case MOD_LCANNON_SPLASH: message = G_( "%s ^7was caught in the fallout of %s%s^7's lucifer cannon\n" ); break; case MOD_GRENADE: message = G_( "%s ^7couldn't escape %s%s^7's grenade\n" ); break; case MOD_ABUILDER_CLAW: message = G_( "%s ^7should leave %s%s^7's buildings alone\n" ); break; case MOD_LEVEL0_BITE: message = G_( "%s ^7was bitten by %s%s\n" ); break; case MOD_LEVEL1_CLAW: message = G_( "%s ^7was swiped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL1; break; case MOD_LEVEL2_CLAW: message = G_( "%s ^7was clawed by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL2; break; case MOD_LEVEL2_ZAP: message = G_( "%s ^7was zapped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL2; break; case MOD_LEVEL3_CLAW: message = G_( "%s ^7was chomped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL3_POUNCE: message = G_( "%s ^7was pounced upon by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL3_BOUNCEBALL: message = G_( "%s ^7was sniped by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL3; break; case MOD_LEVEL4_CLAW: message = G_( "%s ^7was mauled by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL4; break; case MOD_LEVEL4_TRAMPLE: message = G_( "%s ^7should have gotten out of the way of %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL4; break; case MOD_LEVEL4_CRUSH: message = G_( "%s ^7was crushed under %s%s^7's weight\n" ); break; case MOD_POISON: message = G_( "%s ^7should have used a medkit against %s%s^7's poison\n" ); break; case MOD_LEVEL1_PCLOUD: message = G_( "%s ^7was gassed by %s%s^7's %s\n" ); attackerClass = PCL_ALIEN_LEVEL1; break; case MOD_TELEFRAG: 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 ) { // Argument order: victim, "TEAMMATE" (if appropriate), attacker, alien class CG_Printf( message, targetName, ( teamKill ) ? _("^1TEAMMATE^7 ") : "", attackerName, ( attackerClass != -1 ) ? BG_ClassConfig( 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 ); }