Add unlock condition text to Challenges menu

Has ifdef'd out code for conditions to change between white and yellow when achieved to match V1 behaviour... wasn't able to figure out why it wasn't working, so dummied out for now.
This commit is contained in:
toaster 2022-12-12 16:55:56 +00:00
parent 6d3a812ff3
commit cc9a65c8f8
5 changed files with 248 additions and 11 deletions

View file

@ -1101,6 +1101,8 @@ extern struct challengesmenu_s {
INT16 offset; // To make the icons move smoothly when we transition! INT16 offset; // To make the icons move smoothly when we transition!
UINT8 currentunlock; UINT8 currentunlock;
char *unlockcondition;
tic_t unlockanim; tic_t unlockanim;
SINT8 row, hilix, focusx; SINT8 row, hilix, focusx;

View file

@ -4720,6 +4720,9 @@ challengedesc:
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str); V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
} }
if (!challengesmenu.fade) // Conditions for unlock
V_DrawThinString(20, 120 + 60, V_ALLOWLOWERCASE, "Press (B)"); if (challengesmenu.unlockcondition != NULL)
{
V_DrawCenteredString(BASEVIDWIDTH/2, 120 + 40, V_ALLOWLOWERCASE, challengesmenu.unlockcondition);
}
} }

View file

@ -1776,15 +1776,14 @@ static inline size_t M_StringHeight(const char *string)
void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype) void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype)
{ {
const UINT8 pid = 0; const UINT8 pid = 0;
size_t max = 0, start = 0, i, strlines; size_t max = 0, start = 0, strlines = 0, i;
static char *message = NULL; static char *message = NULL;
Z_Free(message); Z_Free(message);
message = Z_StrDup(string); message = Z_StrDup(string);
DEBFILE(message); DEBFILE(message);
// Rudementary word wrapping. // Rudementary word wrapping.
// Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares. // Simple and effective. Does not handle nonuniform letter sizes, etc. but who cares.
strlines = 0;
for (i = 0; message[i]; i++) for (i = 0; message[i]; i++)
{ {
if (message[i] == ' ') if (message[i] == ' ')
@ -1799,6 +1798,8 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
max = 0; max = 0;
continue; continue;
} }
else if (message[i] & 0x80)
continue;
else else
max += 8; max += 8;
@ -6859,13 +6860,14 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
if (challengesmenu.pending || desiredmenu == NULL) if (challengesmenu.pending || desiredmenu == NULL)
{ {
memset(setup_explosions, 0, sizeof(setup_explosions));
challengesmenu.currentunlock = MAXUNLOCKABLES; challengesmenu.currentunlock = MAXUNLOCKABLES;
M_PopulateChallengeGrid(); challengesmenu.unlockcondition = NULL;
M_PopulateChallengeGrid();
if (gamedata->challengegrid) if (gamedata->challengegrid)
challengesmenu.extradata = M_ChallengeGridExtraData(); challengesmenu.extradata = M_ChallengeGridExtraData();
memset(setup_explosions, 0, sizeof(setup_explosions));
memset(&challengesmenu.unlockcount, 0, sizeof(challengesmenu.unlockcount)); memset(&challengesmenu.unlockcount, 0, sizeof(challengesmenu.unlockcount));
for (i = 0; i < MAXUNLOCKABLES; i++) for (i = 0; i < MAXUNLOCKABLES; i++)
{ {
@ -6899,6 +6901,7 @@ static void M_ChallengesAutoFocus(UINT8 unlockid, boolean fresh)
return; return;
challengesmenu.currentunlock = unlockid; challengesmenu.currentunlock = unlockid;
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
challengesmenu.unlockanim = 0; challengesmenu.unlockanim = 0;
if (gamedata->challengegrid == NULL || challengesmenu.extradata == NULL) if (gamedata->challengegrid == NULL || challengesmenu.extradata == NULL)
@ -7099,6 +7102,9 @@ void M_ChallengesTick(void)
gamedata->unlocked[challengesmenu.currentunlock] = true; gamedata->unlocked[challengesmenu.currentunlock] = true;
M_UpdateUnlockablesAndExtraEmblems(true); M_UpdateUnlockablesAndExtraEmblems(true);
// Update shown description just in case..?
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
challengesmenu.unlockcount[CC_TALLY]++; challengesmenu.unlockcount[CC_TALLY]++;
challengesmenu.unlockcount[CC_ANIM]++; challengesmenu.unlockcount[CC_ANIM]++;
@ -7213,6 +7219,8 @@ boolean M_ChallengesInputs(INT32 ch)
Z_Free(challengesmenu.extradata); Z_Free(challengesmenu.extradata);
challengesmenu.extradata = NULL; challengesmenu.extradata = NULL;
challengesmenu.unlockcondition = NULL;
return true; return true;
} }
@ -7336,6 +7344,7 @@ boolean M_ChallengesInputs(INT32 ch)
// After movement has been determined, figure out the current selection. // After movement has been determined, figure out the current selection.
i = (challengesmenu.col * CHALLENGEGRIDHEIGHT) + challengesmenu.row; i = (challengesmenu.col * CHALLENGEGRIDHEIGHT) + challengesmenu.row;
challengesmenu.currentunlock = (gamedata->challengegrid[i]); challengesmenu.currentunlock = (gamedata->challengegrid[i]);
challengesmenu.unlockcondition = M_BuildConditionSetString(challengesmenu.currentunlock);
challengesmenu.hilix = challengesmenu.col; challengesmenu.hilix = challengesmenu.col;
challengesmenu.hiliy = challengesmenu.row; challengesmenu.hiliy = challengesmenu.row;

View file

@ -480,6 +480,8 @@ void M_ClearSecrets(void)
// ---------------------- // ----------------------
// Condition set checking // Condition set checking
// ---------------------- // ----------------------
// See also M_GetConditionString
UINT8 M_CheckCondition(condition_t *cn) UINT8 M_CheckCondition(condition_t *cn)
{ {
switch (cn->type) switch (cn->type)
@ -564,7 +566,226 @@ static UINT8 M_CheckConditionSet(conditionset_t *c)
return achievedSoFar; return achievedSoFar;
} }
void M_CheckUnlockConditions(void) // See also M_CheckCondition
static const char *M_GetConditionString(condition_t *cn)
{
INT32 i;
char *title = NULL;
const char *work = NULL;
#define BUILDCONDITIONTITLE(i) (M_MapLocked(i+1) ? Z_StrDup("???") : G_BuildMapTitle(i+1))
switch (cn->type)
{
case UC_PLAYTIME: // Requires total playing time >= x
return va("Play for %i:%02i:%02i",
G_TicsToHours(cn->requirement),
G_TicsToMinutes(cn->requirement, false),
G_TicsToSeconds(cn->requirement));
case UC_MATCHESPLAYED: // Requires any level completed >= x times
return va("Play %d matches", cn->requirement);
case UC_POWERLEVEL: // Requires power level >= x on a certain gametype
return va("Get a PWR of %d in %s", cn->requirement,
(cn->extrainfo1 == PWRLV_RACE)
? "Race"
: "Battle");
case UC_GAMECLEAR: // Requires game beaten >= x times
if (cn->requirement > 1)
return va("Beat game %d times", cn->requirement);
else
return va("Beat the game");
case UC_OVERALLTIME: // Requires overall time <= x
return va("Get overall time of %i:%02i:%02i",
G_TicsToHours(cn->requirement),
G_TicsToMinutes(cn->requirement, false),
G_TicsToSeconds(cn->requirement));
case UC_MAPVISITED: // Requires map x to be visited
case UC_MAPBEATEN: // Requires map x to be beaten
case UC_MAPENCORE: // Requires map x to be beaten in encore
{
if (cn->requirement >= nummapheaders || !mapheaderinfo[cn->requirement])
return va("INVALID MAP CONDITION \"%d:%d\"", cn->type, cn->requirement);
title = BUILDCONDITIONTITLE(cn->requirement);
work = va("%s %s%s",
(cn->type == UC_MAPVISITED) ? "Visit" : "Beat",
title,
(cn->type == UC_MAPENCORE) ? " in Encore Mode" : "");
Z_Free(title);
return work;
}
case UC_MAPTIME: // Requires time on map <= x
{
if (cn->extrainfo1 >= nummapheaders || !mapheaderinfo[cn->extrainfo1])
return va("INVALID MAP CONDITION \"%d:%d:%d\"", cn->type, cn->extrainfo1, cn->requirement);
title = BUILDCONDITIONTITLE(cn->extrainfo1);
work = va("Beat %s in %i:%02i.%02i", title,
G_TicsToMinutes(cn->requirement, true),
G_TicsToSeconds(cn->requirement),
G_TicsToCentiseconds(cn->requirement));
Z_Free(title);
return work;
}
case UC_TOTALEMBLEMS: // Requires number of emblems >= x
return va("Get %d medals", cn->requirement);
case UC_EMBLEM: // Requires emblem x to be obtained
{
INT32 checkLevel;
i = cn->requirement-1;
checkLevel = G_MapNumber(emblemlocations[i].level);
if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
return va("INVALID MEDAL MAP \"%d:%d\"", cn->requirement, checkLevel);
title = BUILDCONDITIONTITLE(checkLevel);
switch (emblemlocations[i].type)
{
case ET_MAP:
work = va("Beat %s", title);
break;
case ET_TIME:
if (emblemlocations[i].color <= 0 || emblemlocations[i].color >= numskincolors)
{
Z_Free(title);
return va("INVALID MEDAL COLOR \"%d:%d\"", cn->requirement, checkLevel);
}
work = va("Get the %s Medal for %s", skincolors[emblemlocations[i].color].name, title);
break;
case ET_GLOBAL:
default:
work = va("Find a secret in %s", title);
break;
}
Z_Free(title);
return work;
}
case UC_UNLOCKABLE: // Requires unlockable x to be obtained
return va("Get \"%s\"",
gamedata->unlocked[cn->requirement-1]
? unlockables[cn->requirement-1].name
: "???");
default:
break;
}
// UC_MAPTRIGGER and UC_CONDITIONSET are explicitly very hard to support proper descriptions for
return va("UNSUPPORTED CONDITION \"%d\"", cn->type);
#undef BUILDCONDITIONTITLE
}
//#define ACHIEVEDBRITE
char *M_BuildConditionSetString(UINT8 unlockid)
{
conditionset_t *c = NULL;
UINT32 lastID = 0;
condition_t *cn;
#ifdef ACHIEVEDBRITE
boolean achieved = false;
#endif
size_t len = 1024, worklen;
static char message[1024] = "";
const char *work = NULL;
size_t max = 0, start = 0, strlines = 0, i;
message[0] = '\0';
if (unlockid >= MAXUNLOCKABLES)
{
return NULL;
}
if (!unlockables[unlockid].conditionset)
{
return NULL;
}
c = &conditionSets[unlockables[unlockid].conditionset-1];
for (i = 0; i < c->numconditions; ++i)
{
cn = &c->condition[i];
if (i > 0)
{
worklen = 3;
if (lastID == cn->id)
{
strncat(message, "\n& ", len);
}
else
{
strncat(message, "\nOR ", len);
worklen++;
}
len -= worklen;
}
lastID = cn->id;
#ifdef ACHIEVEDBRITE
achieved = M_CheckCondition(cn);
if (achieved)
{
strncat(message, "\0x82", len);
len--;
}
#endif
work = M_GetConditionString(cn);
worklen = strlen(work);
strncat(message, work, len);
len -= worklen;
#ifdef ACHIEVEDBRITE
if (achieved)
{
strncat(message, "\0x80", len);
len--;
}
#endif
}
// Rudementary word wrapping.
// Simple and effective. Does not handle nonuniform letter sizes, etc. but who cares.
for (i = 0; message[i]; i++)
{
if (message[i] == ' ')
{
start = i;
max += 4;
}
else if (message[i] == '\n')
{
strlines = i;
start = 0;
max = 0;
continue;
}
else if (message[i] & 0x80)
continue;
else
max += 8;
// Start trying to wrap if presumed length exceeds the screen width.
if (max >= BASEVIDWIDTH && start > 0)
{
message[start] = '\n';
max -= (start-strlines)*8;
strlines = start;
start = 0;
}
}
return message;
}
static void M_CheckUnlockConditions(void)
{ {
INT32 i; INT32 i;
conditionset_t *c; conditionset_t *c;

View file

@ -171,12 +171,15 @@ extern INT32 numemblems;
extern UINT32 unlocktriggers; extern UINT32 unlocktriggers;
void M_NewGameDataStruct(void); void M_NewGameDataStruct(void);
// Challenges menu stuff
void M_PopulateChallengeGrid(void); void M_PopulateChallengeGrid(void);
UINT8 *M_ChallengeGridExtraData(void); UINT8 *M_ChallengeGridExtraData(void);
char *M_BuildConditionSetString(UINT8 unlockid);
#define CHE_NONE 0 #define CHE_NONE 0
#define CHE_HINT 1 #define CHE_HINT 1
#define CHE_CONNECTEDLEFT 2 #define CHE_CONNECTEDLEFT (1<<1)
#define CHE_CONNECTEDUP 4 #define CHE_CONNECTEDUP (1<<2)
#define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP) #define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP)
// Condition set setup // Condition set setup
@ -187,7 +190,6 @@ void M_ClearConditionSet(UINT8 set);
void M_ClearSecrets(void); void M_ClearSecrets(void);
// Updating conditions and unlockables // Updating conditions and unlockables
void M_CheckUnlockConditions(void);
UINT8 M_CheckCondition(condition_t *cn); UINT8 M_CheckCondition(condition_t *cn);
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud); boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud);
UINT8 M_GetNextAchievedUnlock(void); UINT8 M_GetNextAchievedUnlock(void);