diff --git a/README.md b/README.md index 51fe9e5..3b78d16 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ A legendary 90s era Quake 3 Arena mod. - [x] ~~Third person traceable crosshair~~ - [x] ~~Breakable map entities ("func_breakable")~~ - [x] ~~Ki trails (use ki to move, cg_kiTrail >10 )~~ +- [x] ~~Instant character model changing~~ - [ ] Make ki energy regeneration, ki use, attacks, charging balance indicated on old docs - [ ] Powerlevel and Power Tiers indicated on old docs - [x] ~~Hit Stun (makes player can't use ki, melee, block and charge)~~ diff --git a/source/cgame/cg_cvar.h b/source/cgame/cg_cvar.h index 4dcbba7..0906082 100644 --- a/source/cgame/cg_cvar.h +++ b/source/cgame/cg_cvar.h @@ -86,9 +86,9 @@ CG_CVAR( cg_superdeformed, "cg_superdeformed", "0", CVAR_ARCHIVE ) // BFP - Supe CG_CVAR( cg_yrgolroxor, "cg_yrgolroxor", "0", 0 ) // BFP - Yrgol Roxor easter egg CG_CVAR( cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE ) CG_CVAR( cg_teamChatHeight, "cg_teamChatHeight", "0", CVAR_ARCHIVE ) -CG_CVAR( cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE ) +CG_CVAR( cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE ) // BFP - TODO: In the future, remove cg_forceModel, which wasn't removed originally? CG_CVAR( cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE ) -CG_CVAR( cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE ) +CG_CVAR( cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE ) // BFP - TODO: In the future, remove cg_deferPlayers, which wasn't removed originally? CG_CVAR( cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE ) CG_CVAR( cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO ) CG_CVAR( cg_stats, "cg_stats", "0", 0 ) diff --git a/source/cgame/cg_main.c b/source/cgame/cg_main.c index 6986891..387c171 100644 --- a/source/cgame/cg_main.c +++ b/source/cgame/cg_main.c @@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // cg_main.c -- initialization and primary entry point for cgame #include "cg_local.h" -int forceModelModificationCount = -1; +// int forceModelModificationCount = -1; void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ); void CG_Shutdown( void ); @@ -119,7 +119,7 @@ void CG_RegisterCvars( void ) { trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) ); cgs.localServer = atoi( var ); - forceModelModificationCount = cg_forceModel.modificationCount; + // forceModelModificationCount = cg_forceModel.modificationCount; trap_Cvar_Register(NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); @@ -127,7 +127,9 @@ void CG_RegisterCvars( void ) { //trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE ); } -/* +// BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?) +#if 0 +/* =================== CG_ForceModelChange =================== @@ -145,6 +147,7 @@ static void CG_ForceModelChange( void ) { CG_NewClientInfo( i ); } } +#endif /* ================= @@ -175,11 +178,14 @@ void CG_UpdateCvars( void ) { trap_Cvar_Set( "teamoverlay", "1" ); } + // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?) +#if 0 // if force model changed if ( forceModelModificationCount != cg_forceModel.modificationCount ) { forceModelModificationCount = cg_forceModel.modificationCount; CG_ForceModelChange(); } +#endif } int CG_CrosshairPlayer( void ) { diff --git a/source/cgame/cg_players.c b/source/cgame/cg_players.c index e85709b..8bbae71 100644 --- a/source/cgame/cg_players.c +++ b/source/cgame/cg_players.c @@ -737,6 +737,8 @@ static void CG_LoadClientInfo( clientInfo_t *ci ) { } } +// BFP - Unused static functions, remove? I think so +#if 0 /* ====================== CG_CopyClientInfoModel @@ -870,6 +872,7 @@ static void CG_SetDeferredClientInfo( clientInfo_t *ci ) { CG_LoadClientInfo( ci ); } +#endif /* @@ -943,6 +946,8 @@ void CG_NewClientInfo( int clientNum ) { // model v = Info_ValueForKey( configstring, "model" ); + // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?) +#if 0 if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches @@ -971,22 +976,24 @@ void CG_NewClientInfo( int clientNum ) { Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); } } - } else { - Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) ); + } else +#endif + Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) ); - slash = strchr( newInfo.modelName, '/' ); - if ( !slash ) { - // modelName didn not include a skin name - Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); - } else { - Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); - // truncate modelName - *slash = 0; - } + slash = strchr( newInfo.modelName, '/' ); + if ( !slash ) { + // modelName didn not include a skin name + Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); + } else { + Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); + // truncate modelName + *slash = 0; } // head model v = Info_ValueForKey( configstring, "hmodel" ); + // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?) +#if 0 if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches @@ -1015,23 +1022,27 @@ void CG_NewClientInfo( int clientNum ) { Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); } } - } else { - Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) ); + } else +#endif + Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) ); - slash = strchr( newInfo.headModelName, '/' ); - if ( !slash ) { - // modelName didn not include a skin name - Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); - } else { - Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); - // truncate modelName - *slash = 0; - } + slash = strchr( newInfo.headModelName, '/' ); + if ( !slash ) { + // modelName didn not include a skin name + Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); + } else { + Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); + // truncate modelName + *slash = 0; } + // BFP - Change the model without impeding with defer + // BFP - TODO: Remove cg_deferPlayers cvar and its unnecessary code in the future +#if 0 // scan for an existing clientinfo that matches this modelname // so we can avoid loading checks if possible - if ( !CG_ScanForExistingClientInfo( &newInfo ) ) { + // if ( !CG_ScanForExistingClientInfo( &newInfo ) ) + // { qboolean forceDefer; forceDefer = trap_MemoryRemaining() < 4000000; @@ -1046,9 +1057,10 @@ void CG_NewClientInfo( int clientNum ) { newInfo.deferred = qfalse; } } else { +#endif CG_LoadClientInfo( &newInfo ); - } - } + // } + // } // replace whatever was there with the new one newInfo.infoValid = qtrue; diff --git a/source/game/g_client.c b/source/game/g_client.c index 0906efa..890e878 100644 --- a/source/game/g_client.c +++ b/source/game/g_client.c @@ -704,6 +704,10 @@ void ClientUserinfoChanged( int clientNum ) { char blueTeam[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; + // BFP - Model prefix load + char newModelPrefix[MAX_QPATH]; + char *oldModelDash, *newModelDash; + ent = g_entities + clientNum; client = ent->client; @@ -779,27 +783,6 @@ void ClientUserinfoChanged( int clientNum ) { team = client->sess.sessionTeam; } -/* NOTE: all client side now - - // team - switch( team ) { - case TEAM_RED: - ForceClientSkin(client, model, "red"); -// ForceClientSkin(client, headModel, "red"); - break; - case TEAM_BLUE: - ForceClientSkin(client, model, "blue"); -// ForceClientSkin(client, headModel, "blue"); - break; - } - // don't ever use a default skin in teamplay, it would just waste memory - // however bots will always join a team but they spawn in as spectator - if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { - ForceClientSkin(client, model, "red"); -// ForceClientSkin(client, headModel, "red"); - } -*/ - // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { @@ -807,15 +790,6 @@ void ClientUserinfoChanged( int clientNum ) { } else { client->pers.teamInfo = qfalse; } - /* - s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); - if ( !*s || atoi( s ) == 0 ) { - client->pers.pmoveFixed = qfalse; - } - else { - client->pers.pmoveFixed = qtrue; - } - */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); @@ -844,6 +818,35 @@ void ClientUserinfoChanged( int clientNum ) { trap_SetConfigstring( CS_PLAYERS+clientNum, s ); + // BFP - Model prefix handling + { + // extract model prefixes safely + oldModelDash = strchr(ent->oldModel, '-'); + if ( oldModelDash ) { + Q_strncpyz( ent->oldModelPrefix, ent->oldModel, oldModelDash - ent->oldModel + 1 ); + } else { + Q_strncpyz( ent->oldModelPrefix, ent->oldModel, sizeof( ent->oldModelPrefix ) ); + } + + newModelDash = strchr(model, '-'); + if ( newModelDash ) { + Q_strncpyz( newModelPrefix, model, newModelDash - model + 1 ); + } else { + Q_strncpyz( newModelPrefix, model, sizeof( newModelPrefix ) ); + } + + // compare model prefixes + if ( Q_stricmp( ent->oldModelPrefix, newModelPrefix ) ) { + // prefixes differ, kill the player + ent->flags &= ~FL_GODMODE; + ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; + player_die( ent, ent, NULL, 100000, MOD_UNKNOWN ); + } + + // save the new model as the old model for the next time this function runs + Q_strncpyz( ent->oldModel, model, sizeof( ent->oldModel ) ); + } + // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); } diff --git a/source/game/g_combat.c b/source/game/g_combat.c index 4e54411..5f85001 100644 --- a/source/game/g_combat.c +++ b/source/game/g_combat.c @@ -334,10 +334,13 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int return; } - // check for an almost capture - CheckAlmostCapture( self, attacker ); - // check for a player that almost brought in cubes - CheckAlmostScored( self, attacker ); + // BFP - For compilation safety from shared objects (.so) and dll + if ( attacker != NULL ) { + // check for an almost capture + CheckAlmostCapture( self, attacker ); + // check for a player that almost brought in cubes + CheckAlmostScored( self, attacker ); + } // BFP - no hook #if 0 @@ -425,8 +428,11 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int AddScore( self, self->r.currentOrigin, -1 ); } - // Add team bonuses - Team_FragBonuses(self, inflictor, attacker); + // BFP - For compilation safety from shared objects (.so) and dll + if ( attacker != NULL ) { + // Add team bonuses + Team_FragBonuses(self, inflictor, attacker); + } // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { diff --git a/source/game/g_local.h b/source/game/g_local.h index 7577ebd..81d804d 100644 --- a/source/game/g_local.h +++ b/source/game/g_local.h @@ -92,6 +92,10 @@ struct gentity_s { char *model; char *model2; int freetime; // level.time when the object was freed + + // BFP - Model prefix handling + char oldModel[MAX_QPATH]; + char oldModelPrefix[MAX_QPATH]; int eventTime; // events will be cleared EVENT_VALID_MSEC after set qboolean freeAfterEvent;