2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
2020-01-06 22:16:27 +00:00
// Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
2023-03-31 12:53:31 +00:00
// Copyright (C) 2012-2023 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file m_cond.c
/// \brief Unlockable condition system for SRB2 version 2.1
# include "m_cond.h"
# include "doomstat.h"
# include "z_zone.h"
# include "hu_stuff.h" // CEcho
# include "v_video.h" // video flags
# include "g_game.h" // record info
2020-03-09 13:54:56 +00:00
# include "r_skins.h" // numskins
2014-03-15 16:59:03 +00:00
# include "r_draw.h" // R_GetColorByName
2022-02-27 21:52:05 +00:00
gamedata_t * clientGamedata ; // Our gamedata
gamedata_t * serverGamedata ; // Server's gamedata
2014-03-15 16:59:03 +00:00
// Map triggers for linedef executors
// 32 triggers, one bit each
UINT32 unlocktriggers ;
// The meat of this system lies in condition sets
conditionset_t conditionSets [ MAXCONDITIONSETS ] ;
2019-02-03 10:05:22 +00:00
// Emblem locations
emblem_t emblemlocations [ MAXEMBLEMS ] ;
2014-03-15 16:59:03 +00:00
2019-02-03 10:05:22 +00:00
// Extra emblems
extraemblem_t extraemblems [ MAXEXTRAEMBLEMS ] ;
2014-03-15 16:59:03 +00:00
2019-02-03 10:05:22 +00:00
// Unlockables
unlockable_t unlockables [ MAXUNLOCKABLES ] ;
2014-03-15 16:59:03 +00:00
2019-02-03 10:05:22 +00:00
// Number of emblems and extra emblems
INT32 numemblems = 0 ;
INT32 numextraemblems = 0 ;
2014-03-15 16:59:03 +00:00
2022-02-27 21:52:05 +00:00
// Temporary holding place for nights data for the current map
2022-03-02 22:47:15 +00:00
nightsdata_t ntemprecords [ MAXPLAYERS ] ;
2022-02-27 21:52:05 +00:00
// Create a new gamedata_t, for start-up
gamedata_t * M_NewGameDataStruct ( void )
{
gamedata_t * data = Z_Calloc ( sizeof ( * data ) , PU_STATIC , NULL ) ;
M_ClearSecrets ( data ) ;
G_ClearRecords ( data ) ;
return data ;
}
void M_CopyGameData ( gamedata_t * dest , gamedata_t * src )
{
INT32 i , j ;
M_ClearSecrets ( dest ) ;
G_ClearRecords ( dest ) ;
dest - > loaded = src - > loaded ;
dest - > totalplaytime = src - > totalplaytime ;
dest - > timesBeaten = src - > timesBeaten ;
dest - > timesBeatenWithEmeralds = src - > timesBeatenWithEmeralds ;
dest - > timesBeatenUltimate = src - > timesBeatenUltimate ;
memcpy ( dest - > achieved , src - > achieved , sizeof ( dest - > achieved ) ) ;
memcpy ( dest - > collected , src - > collected , sizeof ( dest - > collected ) ) ;
memcpy ( dest - > extraCollected , src - > extraCollected , sizeof ( dest - > extraCollected ) ) ;
memcpy ( dest - > unlocked , src - > unlocked , sizeof ( dest - > unlocked ) ) ;
memcpy ( dest - > mapvisited , src - > mapvisited , sizeof ( dest - > mapvisited ) ) ;
// Main records
for ( i = 0 ; i < NUMMAPS ; + + i )
{
if ( ! src - > mainrecords [ i ] )
continue ;
G_AllocMainRecordData ( ( INT16 ) i , dest ) ;
dest - > mainrecords [ i ] - > score = src - > mainrecords [ i ] - > score ;
dest - > mainrecords [ i ] - > time = src - > mainrecords [ i ] - > time ;
dest - > mainrecords [ i ] - > rings = src - > mainrecords [ i ] - > rings ;
}
// Nights records
for ( i = 0 ; i < NUMMAPS ; + + i )
{
if ( ! src - > nightsrecords [ i ] | | ! src - > nightsrecords [ i ] - > nummares )
continue ;
G_AllocNightsRecordData ( ( INT16 ) i , dest ) ;
for ( j = 0 ; j < ( src - > nightsrecords [ i ] - > nummares + 1 ) ; j + + )
{
dest - > nightsrecords [ i ] - > score [ j ] = src - > nightsrecords [ i ] - > score [ j ] ;
dest - > nightsrecords [ i ] - > grade [ j ] = src - > nightsrecords [ i ] - > grade [ j ] ;
dest - > nightsrecords [ i ] - > time [ j ] = src - > nightsrecords [ i ] - > time [ j ] ;
}
dest - > nightsrecords [ i ] - > nummares = src - > nightsrecords [ i ] - > nummares ;
}
}
2014-03-15 16:59:03 +00:00
void M_AddRawCondition ( UINT8 set , UINT8 id , conditiontype_t c , INT32 r , INT16 x1 , INT16 x2 )
{
condition_t * cond ;
UINT32 num , wnum ;
2023-08-01 02:29:45 +00:00
I_Assert ( set < MAXCONDITIONSETS ) ;
2014-03-15 16:59:03 +00:00
wnum = conditionSets [ set - 1 ] . numconditions ;
num = + + conditionSets [ set - 1 ] . numconditions ;
conditionSets [ set - 1 ] . condition = Z_Realloc ( conditionSets [ set - 1 ] . condition , sizeof ( condition_t ) * num , PU_STATIC , 0 ) ;
cond = conditionSets [ set - 1 ] . condition ;
cond [ wnum ] . id = id ;
cond [ wnum ] . type = c ;
cond [ wnum ] . requirement = r ;
cond [ wnum ] . extrainfo1 = x1 ;
cond [ wnum ] . extrainfo2 = x2 ;
}
void M_ClearConditionSet ( UINT8 set )
{
if ( conditionSets [ set - 1 ] . numconditions )
{
Z_Free ( conditionSets [ set - 1 ] . condition ) ;
conditionSets [ set - 1 ] . condition = NULL ;
conditionSets [ set - 1 ] . numconditions = 0 ;
}
2022-02-27 21:52:05 +00:00
clientGamedata - > achieved [ set - 1 ] = serverGamedata - > achieved [ set - 1 ] = false ;
2014-03-15 16:59:03 +00:00
}
// Clear ALL secrets.
2022-02-27 21:52:05 +00:00
void M_ClearSecrets ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
2022-02-27 21:52:05 +00:00
memset ( data - > mapvisited , 0 , sizeof ( data - > mapvisited ) ) ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXEMBLEMS ; + + i )
2022-02-27 21:52:05 +00:00
data - > collected [ i ] = false ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXEXTRAEMBLEMS ; + + i )
2022-02-27 21:52:05 +00:00
data - > extraCollected [ i ] = false ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXUNLOCKABLES ; + + i )
2022-02-27 21:52:05 +00:00
data - > unlocked [ i ] = false ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXCONDITIONSETS ; + + i )
2022-02-27 21:52:05 +00:00
data - > achieved [ i ] = false ;
2014-03-15 16:59:03 +00:00
2022-02-27 21:52:05 +00:00
data - > timesBeaten = data - > timesBeatenWithEmeralds = data - > timesBeatenUltimate = 0 ;
2014-03-15 16:59:03 +00:00
// Re-unlock any always unlocked things
2022-02-27 21:52:05 +00:00
M_SilentUpdateUnlockablesAndEmblems ( data ) ;
M_SilentUpdateSkinAvailabilites ( ) ;
2014-03-15 16:59:03 +00:00
}
// ----------------------
// Condition set checking
// ----------------------
2022-02-27 21:52:05 +00:00
static UINT8 M_CheckCondition ( condition_t * cn , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
switch ( cn - > type )
{
case UC_PLAYTIME : // Requires total playing time >= x
2022-02-27 21:52:05 +00:00
return ( data - > totalplaytime > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_GAMECLEAR : // Requires game beaten >= x times
2022-02-27 21:52:05 +00:00
return ( data - > timesBeaten > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_ALLEMERALDS : // Requires game beaten with all 7 emeralds >= x times
2022-02-27 21:52:05 +00:00
return ( data - > timesBeatenWithEmeralds > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_ULTIMATECLEAR : // Requires game beaten on ultimate >= x times (in other words, never)
2022-02-27 21:52:05 +00:00
return ( data - > timesBeatenUltimate > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_OVERALLSCORE : // Requires overall score >= x
2022-02-27 21:52:05 +00:00
return ( M_GotHighEnoughScore ( cn - > requirement , data ) ) ;
2014-03-15 16:59:03 +00:00
case UC_OVERALLTIME : // Requires overall time <= x
2022-02-27 21:52:05 +00:00
return ( M_GotLowEnoughTime ( cn - > requirement , data ) ) ;
2014-03-15 16:59:03 +00:00
case UC_OVERALLRINGS : // Requires overall rings >= x
2022-02-27 21:52:05 +00:00
return ( M_GotHighEnoughRings ( cn - > requirement , data ) ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPVISITED : // Requires map x to be visited
2022-02-27 21:52:05 +00:00
return ( ( data - > mapvisited [ cn - > requirement - 1 ] & MV_VISITED ) = = MV_VISITED ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPBEATEN : // Requires map x to be beaten
2022-02-27 21:52:05 +00:00
return ( ( data - > mapvisited [ cn - > requirement - 1 ] & MV_BEATEN ) = = MV_BEATEN ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPALLEMERALDS : // Requires map x to be beaten with all emeralds in possession
2022-02-27 21:52:05 +00:00
return ( ( data - > mapvisited [ cn - > requirement - 1 ] & MV_ALLEMERALDS ) = = MV_ALLEMERALDS ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPULTIMATE : // Requires map x to be beaten on ultimate
2022-02-27 21:52:05 +00:00
return ( ( data - > mapvisited [ cn - > requirement - 1 ] & MV_ULTIMATE ) = = MV_ULTIMATE ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPPERFECT : // Requires map x to be beaten with a perfect bonus
2022-02-27 21:52:05 +00:00
return ( ( data - > mapvisited [ cn - > requirement - 1 ] & MV_PERFECT ) = = MV_PERFECT ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPSCORE : // Requires score on map >= x
2022-02-27 21:52:05 +00:00
return ( G_GetBestScore ( cn - > extrainfo1 , data ) > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPTIME : // Requires time on map <= x
2022-02-27 21:52:05 +00:00
return ( G_GetBestTime ( cn - > extrainfo1 , data ) < = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_MAPRINGS : // Requires rings on map >= x
2022-02-27 21:52:05 +00:00
return ( G_GetBestRings ( cn - > extrainfo1 , data ) > = cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_NIGHTSSCORE :
2022-02-27 21:52:05 +00:00
return ( G_GetBestNightsScore ( cn - > extrainfo1 , ( UINT8 ) cn - > extrainfo2 , data ) > = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_NIGHTSTIME :
2022-02-27 21:52:05 +00:00
return ( G_GetBestNightsTime ( cn - > extrainfo1 , ( UINT8 ) cn - > extrainfo2 , data ) < = ( unsigned ) cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_NIGHTSGRADE :
2022-02-27 21:52:05 +00:00
return ( G_GetBestNightsGrade ( cn - > extrainfo1 , ( UINT8 ) cn - > extrainfo2 , data ) > = cn - > requirement ) ;
2014-03-15 16:59:03 +00:00
case UC_TRIGGER : // requires map trigger set
return ! ! ( unlocktriggers & ( 1 < < cn - > requirement ) ) ;
case UC_TOTALEMBLEMS : // Requires number of emblems >= x
2022-02-27 21:52:05 +00:00
return ( M_GotEnoughEmblems ( cn - > requirement , data ) ) ;
2014-03-15 16:59:03 +00:00
case UC_EMBLEM : // Requires emblem x to be obtained
2022-02-27 21:52:05 +00:00
return data - > collected [ cn - > requirement - 1 ] ;
2014-03-15 16:59:03 +00:00
case UC_EXTRAEMBLEM : // Requires extra emblem x to be obtained
2022-02-27 21:52:05 +00:00
return data - > extraCollected [ cn - > requirement - 1 ] ;
2014-03-15 16:59:03 +00:00
case UC_CONDITIONSET : // requires condition set x to already be achieved
2022-02-27 21:52:05 +00:00
return M_Achieved ( cn - > requirement - 1 , data ) ;
2014-03-15 16:59:03 +00:00
}
return false ;
}
2022-02-27 21:52:05 +00:00
static UINT8 M_CheckConditionSet ( conditionset_t * c , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
UINT32 i ;
UINT32 lastID = 0 ;
condition_t * cn ;
UINT8 achievedSoFar = true ;
for ( i = 0 ; i < c - > numconditions ; + + i )
{
cn = & c - > condition [ i ] ;
// If the ID is changed and all previous statements of the same ID were true
// then this condition has been successfully achieved
if ( lastID & & lastID ! = cn - > id & & achievedSoFar )
return true ;
// Skip future conditions with the same ID if one fails, for obvious reasons
else if ( lastID & & lastID = = cn - > id & & ! achievedSoFar )
continue ;
lastID = cn - > id ;
2022-02-27 21:52:05 +00:00
achievedSoFar = M_CheckCondition ( cn , data ) ;
2014-03-15 16:59:03 +00:00
}
return achievedSoFar ;
}
2022-02-27 21:52:05 +00:00
void M_CheckUnlockConditions ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
conditionset_t * c ;
for ( i = 0 ; i < MAXCONDITIONSETS ; + + i )
{
c = & conditionSets [ i ] ;
2022-02-27 21:52:05 +00:00
if ( ! c - > numconditions | | data - > achieved [ i ] )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
data - > achieved [ i ] = ( M_CheckConditionSet ( c , data ) ) ;
2014-03-15 16:59:03 +00:00
}
}
2022-02-27 21:52:05 +00:00
UINT8 M_UpdateUnlockablesAndExtraEmblems ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
char cechoText [ 992 ] = " " ;
UINT8 cechoLines = 0 ;
2022-02-27 21:52:05 +00:00
M_CheckUnlockConditions ( data ) ;
2014-03-15 16:59:03 +00:00
// Go through extra emblems
for ( i = 0 ; i < numextraemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > extraCollected [ i ] | | ! extraemblems [ i ] . conditionset )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
if ( ( data - > extraCollected [ i ] = M_Achieved ( extraemblems [ i ] . conditionset - 1 , data ) ) ! = false )
2014-03-15 16:59:03 +00:00
{
strcat ( cechoText , va ( M_GetText ( " Got \" %s \" emblem! \\ " ) , extraemblems [ i ] . name ) ) ;
+ + cechoLines ;
}
}
// Fun part: if any of those unlocked we need to go through the
// unlock conditions AGAIN just in case an emblem reward was reached
if ( cechoLines )
2022-02-27 21:52:05 +00:00
M_CheckUnlockConditions ( data ) ;
2014-03-15 16:59:03 +00:00
// Go through unlockables
for ( i = 0 ; i < MAXUNLOCKABLES ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > unlocked [ i ] | | ! unlockables [ i ] . conditionset )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
if ( ( data - > unlocked [ i ] = M_Achieved ( unlockables [ i ] . conditionset - 1 , data ) ) ! = false )
2014-03-15 16:59:03 +00:00
{
if ( unlockables [ i ] . nocecho )
continue ;
strcat ( cechoText , va ( M_GetText ( " \" %s \" unlocked! \\ " ) , unlockables [ i ] . name ) ) ;
+ + cechoLines ;
}
}
// Announce
if ( cechoLines )
{
char slashed [ 1024 ] = " " ;
2019-07-29 13:55:36 +00:00
for ( i = 0 ; ( i < 19 ) & & ( i < 24 - cechoLines ) ; + + i )
2014-03-15 16:59:03 +00:00
slashed [ i ] = ' \\ ' ;
slashed [ i ] = 0 ;
strcat ( slashed , cechoText ) ;
HU_SetCEchoFlags ( V_YELLOWMAP | V_RETURN8 ) ;
HU_SetCEchoDuration ( 6 ) ;
HU_DoCEcho ( slashed ) ;
return true ;
}
2022-02-27 21:52:05 +00:00
2014-03-15 16:59:03 +00:00
return false ;
}
// Used when loading gamedata to make sure all unlocks are synched with conditions
2022-02-27 21:52:05 +00:00
void M_SilentUpdateUnlockablesAndEmblems ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
boolean checkAgain = false ;
// Just in case they aren't to sync
2022-02-27 21:52:05 +00:00
M_CheckUnlockConditions ( data ) ;
M_CheckLevelEmblems ( data ) ;
2022-03-03 15:26:36 +00:00
M_CompletionEmblems ( data ) ;
2014-03-15 16:59:03 +00:00
// Go through extra emblems
for ( i = 0 ; i < numextraemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > extraCollected [ i ] | | ! extraemblems [ i ] . conditionset )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
if ( ( data - > extraCollected [ i ] = M_Achieved ( extraemblems [ i ] . conditionset - 1 , data ) ) ! = false )
2014-03-15 16:59:03 +00:00
checkAgain = true ;
}
// check again if extra emblems unlocked, blah blah, etc
if ( checkAgain )
2022-02-27 21:52:05 +00:00
M_CheckUnlockConditions ( data ) ;
2014-03-15 16:59:03 +00:00
// Go through unlockables
for ( i = 0 ; i < MAXUNLOCKABLES ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > unlocked [ i ] | | ! unlockables [ i ] . conditionset )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
data - > unlocked [ i ] = M_Achieved ( unlockables [ i ] . conditionset - 1 , data ) ;
2014-03-15 16:59:03 +00:00
}
2022-02-27 21:52:05 +00:00
}
2019-11-24 13:24:37 +00:00
2022-02-27 21:52:05 +00:00
void M_SilentUpdateSkinAvailabilites ( void )
{
2019-11-24 13:24:37 +00:00
players [ consoleplayer ] . availabilities = players [ 1 ] . availabilities = R_GetSkinAvailabilities ( ) ; // players[1] is supposed to be for 2p
2014-03-15 16:59:03 +00:00
}
// Emblem unlocking shit
2022-02-27 21:52:05 +00:00
UINT8 M_CheckLevelEmblems ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
INT32 valToReach ;
INT16 levelnum ;
UINT8 res ;
UINT8 somethingUnlocked = 0 ;
// Update Score, Time, Rings emblems
for ( i = 0 ; i < numemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( emblemlocations [ i ] . type < = ET_SKIN | | emblemlocations [ i ] . type = = ET_MAP | | data - > collected [ i ] )
2014-03-15 16:59:03 +00:00
continue ;
levelnum = emblemlocations [ i ] . level ;
valToReach = emblemlocations [ i ] . var ;
switch ( emblemlocations [ i ] . type )
{
case ET_SCORE : // Requires score on map >= x
2022-02-27 21:52:05 +00:00
res = ( G_GetBestScore ( levelnum , data ) > = ( unsigned ) valToReach ) ;
2014-03-15 16:59:03 +00:00
break ;
case ET_TIME : // Requires time on map <= x
2022-02-27 21:52:05 +00:00
res = ( G_GetBestTime ( levelnum , data ) < = ( unsigned ) valToReach ) ;
2014-03-15 16:59:03 +00:00
break ;
case ET_RINGS : // Requires rings on map >= x
2022-02-27 21:52:05 +00:00
res = ( G_GetBestRings ( levelnum , data ) > = valToReach ) ;
2014-03-15 16:59:03 +00:00
break ;
case ET_NGRADE : // Requires NiGHTS grade on map >= x
2022-02-27 21:52:05 +00:00
res = ( G_GetBestNightsGrade ( levelnum , 0 , data ) > = valToReach ) ;
2014-03-15 16:59:03 +00:00
break ;
case ET_NTIME : // Requires NiGHTS time on map <= x
2022-02-27 21:52:05 +00:00
res = ( G_GetBestNightsTime ( levelnum , 0 , data ) < = ( unsigned ) valToReach ) ;
2014-03-15 16:59:03 +00:00
break ;
default : // unreachable but shuts the compiler up.
continue ;
}
2022-02-27 21:52:05 +00:00
data - > collected [ i ] = res ;
2014-03-15 16:59:03 +00:00
if ( res )
+ + somethingUnlocked ;
}
return somethingUnlocked ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_CompletionEmblems ( gamedata_t * data ) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
2017-03-22 16:59:16 +00:00
{
INT32 i ;
INT32 embtype ;
INT16 levelnum ;
UINT8 res ;
UINT8 somethingUnlocked = 0 ;
2017-03-28 20:30:31 +00:00
UINT8 flags ;
2017-03-22 16:59:16 +00:00
for ( i = 0 ; i < numemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( emblemlocations [ i ] . type ! = ET_MAP | | data - > collected [ i ] )
2017-03-22 16:59:16 +00:00
continue ;
levelnum = emblemlocations [ i ] . level ;
embtype = emblemlocations [ i ] . var ;
2017-03-28 20:30:31 +00:00
flags = MV_BEATEN ;
2017-09-28 15:10:24 +00:00
2017-03-22 18:45:26 +00:00
if ( embtype & ME_ALLEMERALDS )
flags | = MV_ALLEMERALDS ;
2017-09-28 15:10:24 +00:00
2017-03-22 18:45:26 +00:00
if ( embtype & ME_ULTIMATE )
flags | = MV_ULTIMATE ;
2017-09-28 15:10:24 +00:00
2017-03-22 18:45:26 +00:00
if ( embtype & ME_PERFECT )
flags | = MV_PERFECT ;
2017-09-28 15:10:24 +00:00
2022-02-27 21:52:05 +00:00
res = ( ( data - > mapvisited [ levelnum - 1 ] & flags ) = = flags ) ;
2017-09-28 15:10:24 +00:00
2022-02-27 21:52:05 +00:00
data - > collected [ i ] = res ;
2017-03-22 16:59:16 +00:00
if ( res )
+ + somethingUnlocked ;
}
return somethingUnlocked ;
}
2014-03-15 16:59:03 +00:00
// -------------------
// Quick unlock checks
// -------------------
2022-02-27 21:52:05 +00:00
UINT8 M_AnySecretUnlocked ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
for ( i = 0 ; i < MAXUNLOCKABLES ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( ! unlockables [ i ] . nocecho & & data - > unlocked [ i ] )
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_SecretUnlocked ( INT32 type , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i ;
for ( i = 0 ; i < MAXUNLOCKABLES ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( unlockables [ i ] . type = = type & & data - > unlocked [ i ] )
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_MapLocked ( INT32 mapnum , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
2023-08-07 18:35:20 +00:00
if ( dedicated )
{
// If you're in a dedicated server, every level is unlocked.
// Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but
// that's better than making dedicated server's lives hell.
return false ;
}
2023-09-14 21:05:55 +00:00
if ( cv_debug | | devparm )
return false ; // Unlock every level when in devmode.
2023-08-07 18:35:20 +00:00
2014-03-15 16:59:03 +00:00
if ( ! mapheaderinfo [ mapnum - 1 ] | | mapheaderinfo [ mapnum - 1 ] - > unlockrequired < 0 )
2022-02-27 21:52:05 +00:00
{
2014-03-15 16:59:03 +00:00
return false ;
2022-02-27 21:52:05 +00:00
}
if ( ! data - > unlocked [ mapheaderinfo [ mapnum - 1 ] - > unlockrequired ] )
{
2014-03-15 16:59:03 +00:00
return true ;
2022-02-27 21:52:05 +00:00
}
2014-03-15 16:59:03 +00:00
return false ;
}
2023-08-07 18:35:20 +00:00
UINT8 M_CampaignWarpIsCheat ( INT32 gt , INT32 mapnum , gamedata_t * data )
{
if ( M_MapLocked ( mapnum , data ) = = true )
{
// Warping to locked maps is definitely always a cheat
return true ;
}
if ( ( gametypedefaultrules [ gt ] & GTR_CAMPAIGN ) = = 0 )
{
// Not a campaign, do whatever you want.
return false ;
}
if ( G_IsSpecialStage ( mapnum ) )
{
// Warping to special stages is a cheat
return true ;
}
2023-09-09 02:03:25 +00:00
if ( ! mapheaderinfo [ mapnum - 1 ] | | mapheaderinfo [ mapnum - 1 ] - > menuflags & LF2_HIDEINMENU )
2023-08-07 18:35:20 +00:00
{
// You're never allowed to warp to this level.
return true ;
}
if ( mapheaderinfo [ mapnum - 1 ] - > menuflags & LF2_NOVISITNEEDED )
{
// You're always allowed to warp to this level.
return false ;
}
if ( mapnum = = spstage_start )
{
// Warping to the first level is never a cheat
return false ;
}
// It's only a cheat if you've never been there.
2023-08-20 03:16:14 +00:00
return ( ! ( data - > mapvisited [ mapnum - 1 ] ) ) ;
2023-08-07 18:35:20 +00:00
}
2022-02-27 21:52:05 +00:00
INT32 M_CountEmblems ( gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 found = 0 , i ;
for ( i = 0 ; i < numemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > collected [ i ] )
2014-03-15 16:59:03 +00:00
found + + ;
}
for ( i = 0 ; i < numextraemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > extraCollected [ i ] )
2014-03-15 16:59:03 +00:00
found + + ;
}
return found ;
}
// --------------------------------------
// Quick functions for calculating things
// --------------------------------------
// Theoretically faster than using M_CountEmblems()
// Stops when it reaches the target number of emblems.
2022-02-27 21:52:05 +00:00
UINT8 M_GotEnoughEmblems ( INT32 number , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 i , gottenemblems = 0 ;
for ( i = 0 ; i < numemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > collected [ i ] )
2014-03-15 16:59:03 +00:00
if ( + + gottenemblems > = number ) return true ;
}
for ( i = 0 ; i < numextraemblems ; + + i )
{
2022-02-27 21:52:05 +00:00
if ( data - > extraCollected [ i ] )
2014-03-15 16:59:03 +00:00
if ( + + gottenemblems > = number ) return true ;
}
return false ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_GotHighEnoughScore ( INT32 tscore , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 mscore = 0 ;
INT32 i ;
for ( i = 0 ; i < NUMMAPS ; + + i )
{
if ( ! mapheaderinfo [ i ] | | ! ( mapheaderinfo [ i ] - > menuflags & LF2_RECORDATTACK ) )
continue ;
2022-02-27 21:52:05 +00:00
if ( ! data - > mainrecords [ i ] )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
if ( ( mscore + = data - > mainrecords [ i ] - > score ) > tscore )
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_GotLowEnoughTime ( INT32 tictime , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 curtics = 0 ;
INT32 i ;
for ( i = 0 ; i < NUMMAPS ; + + i )
{
if ( ! mapheaderinfo [ i ] | | ! ( mapheaderinfo [ i ] - > menuflags & LF2_RECORDATTACK ) )
continue ;
2022-02-27 21:52:05 +00:00
if ( ! data - > mainrecords [ i ] | | ! data - > mainrecords [ i ] - > time )
2014-03-15 16:59:03 +00:00
return false ;
2022-02-27 21:52:05 +00:00
else if ( ( curtics + = data - > mainrecords [ i ] - > time ) > tictime )
2014-03-15 16:59:03 +00:00
return false ;
}
return true ;
}
2022-02-27 21:52:05 +00:00
UINT8 M_GotHighEnoughRings ( INT32 trings , gamedata_t * data )
2014-03-15 16:59:03 +00:00
{
INT32 mrings = 0 ;
INT32 i ;
for ( i = 0 ; i < NUMMAPS ; + + i )
{
if ( ! mapheaderinfo [ i ] | | ! ( mapheaderinfo [ i ] - > menuflags & LF2_RECORDATTACK ) )
continue ;
2022-02-27 21:52:05 +00:00
if ( ! data - > mainrecords [ i ] )
2014-03-15 16:59:03 +00:00
continue ;
2022-02-27 21:52:05 +00:00
if ( ( mrings + = data - > mainrecords [ i ] - > rings ) > trings )
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
2021-04-25 21:54:47 +00:00
// Gets the skin number for a SECRET_SKIN unlockable.
INT32 M_UnlockableSkinNum ( unlockable_t * unlock )
{
if ( unlock - > type ! = SECRET_SKIN )
{
// This isn't a skin unlockable...
return - 1 ;
}
if ( unlock - > stringVar & & strcmp ( unlock - > stringVar , " " ) )
{
// Get the skin from the string.
2021-04-25 22:44:07 +00:00
INT32 skinnum = R_SkinAvailable ( unlock - > stringVar ) ;
if ( skinnum ! = - 1 )
{
return skinnum ;
}
2021-04-25 21:54:47 +00:00
}
2021-04-25 22:44:07 +00:00
if ( unlock - > variable > = 0 & & unlock - > variable < numskins )
2021-04-25 21:54:47 +00:00
{
// Use the number directly.
return unlock - > variable ;
}
// Invalid skin unlockable.
return - 1 ;
}
// Gets the skin number for a ET_SKIN emblem.
INT32 M_EmblemSkinNum ( emblem_t * emblem )
{
if ( emblem - > type ! = ET_SKIN )
{
// This isn't a skin emblem...
return - 1 ;
}
if ( emblem - > stringVar & & strcmp ( emblem - > stringVar , " " ) )
{
// Get the skin from the string.
2021-04-25 22:44:07 +00:00
INT32 skinnum = R_SkinAvailable ( emblem - > stringVar ) ;
if ( skinnum ! = - 1 )
{
return skinnum ;
}
2021-04-25 21:54:47 +00:00
}
2021-04-25 22:44:07 +00:00
if ( emblem - > var > = 0 & & emblem - > var < numskins )
2021-04-25 21:54:47 +00:00
{
// Use the number directly.
return emblem - > var ;
}
// Invalid skin emblem.
return - 1 ;
}
2014-03-15 16:59:03 +00:00
// ----------------
// Misc Emblem shit
// ----------------
// Returns pointer to an emblem if an emblem exists for that level.
// Pass -1 mapnum to continue from last emblem.
// NULL if not found.
// note that this goes in reverse!!
emblem_t * M_GetLevelEmblems ( INT32 mapnum )
{
static INT32 map = - 1 ;
static INT32 i = - 1 ;
if ( mapnum > 0 )
{
map = mapnum ;
i = numemblems ;
}
while ( - - i > = 0 )
{
if ( emblemlocations [ i ] . level = = map )
return & emblemlocations [ i ] ;
}
return NULL ;
}
2020-02-15 08:18:41 +00:00
skincolornum_t M_GetEmblemColor ( emblem_t * em )
2014-03-15 16:59:03 +00:00
{
2020-02-15 08:18:41 +00:00
if ( ! em | | em - > color > = numskincolors )
2014-03-15 16:59:03 +00:00
return SKINCOLOR_NONE ;
return em - > color ;
}
2019-10-16 02:54:21 +00:00
const char * M_GetEmblemPatch ( emblem_t * em , boolean big )
2014-03-15 16:59:03 +00:00
{
2019-10-16 02:54:21 +00:00
static char pnamebuf [ 7 ] ;
if ( ! big )
strcpy ( pnamebuf , " GOTITn " ) ;
else
strcpy ( pnamebuf , " EMBMn0 " ) ;
2014-03-15 16:59:03 +00:00
I_Assert ( em - > sprite > = ' A ' & & em - > sprite < = ' Z ' ) ;
2019-10-16 02:54:21 +00:00
if ( ! big )
pnamebuf [ 5 ] = em - > sprite ;
else
pnamebuf [ 4 ] = em - > sprite ;
2014-03-15 16:59:03 +00:00
return pnamebuf ;
}
2020-02-15 08:18:41 +00:00
skincolornum_t M_GetExtraEmblemColor ( extraemblem_t * em )
2014-03-15 16:59:03 +00:00
{
2020-02-15 08:18:41 +00:00
if ( ! em | | em - > color > = numskincolors )
2014-03-15 16:59:03 +00:00
return SKINCOLOR_NONE ;
return em - > color ;
}
2019-10-16 02:54:21 +00:00
const char * M_GetExtraEmblemPatch ( extraemblem_t * em , boolean big )
2014-03-15 16:59:03 +00:00
{
2019-10-16 02:54:21 +00:00
static char pnamebuf [ 7 ] ;
if ( ! big )
strcpy ( pnamebuf , " GOTITn " ) ;
else
strcpy ( pnamebuf , " EMBMn0 " ) ;
2014-03-15 16:59:03 +00:00
I_Assert ( em - > sprite > = ' A ' & & em - > sprite < = ' Z ' ) ;
2019-10-16 02:54:21 +00:00
if ( ! big )
pnamebuf [ 5 ] = em - > sprite ;
else
pnamebuf [ 4 ] = em - > sprite ;
2014-03-15 16:59:03 +00:00
return pnamebuf ;
}