UC_UNLOCKPERCENT

- `UnlockPercent 40 AltMusic` - "Get 40% of alternate music"
- `UnlockPercent 1 Color` - "Get 1% of Spray Cans"
- `UnlockPercent 20 Map` - "Get 20% of Courses"
- `UnlockPercent 55 Cup` - "Get 55% of Cups"
- `UnlockPercent 100` - "Get 100% completion"
    - Provide a percentage and, optionally, an Unlockable type
    - This only works for Unlockable Types where there are expected to be more than one per board
This commit is contained in:
toaster 2023-10-14 18:42:27 +01:00
parent f1e3c547b3
commit 52674c7bb2
3 changed files with 204 additions and 47 deletions

View file

@ -2296,6 +2296,55 @@ void reademblemdata(MYFILE *f, INT32 num)
Z_Free(s);
}
static INT16 parseunlockabletype(char *type)
{
if (fastcmp(type, "EXTRAMEDAL"))
return SECRET_EXTRAMEDAL;
else if (fastcmp(type, "CUP"))
return SECRET_CUP;
else if (fastcmp(type, "MAP"))
return SECRET_MAP;
else if (fastcmp(type, "ALTMUSIC"))
return SECRET_ALTMUSIC;
else if (fastcmp(type, "SKIN"))
return SECRET_SKIN;
else if (fastcmp(type, "FOLLOWER"))
return SECRET_FOLLOWER;
else if (fastcmp(type, "COLOR"))
return SECRET_COLOR;
else if (fastcmp(type, "HARDSPEED"))
return SECRET_HARDSPEED;
else if (fastcmp(type, "MASTERMODE"))
return SECRET_MASTERMODE;
else if (fastcmp(type, "ENCORE"))
return SECRET_ENCORE;
else if (fastcmp(type, "TIMEATTACK"))
return SECRET_TIMEATTACK;
else if (fastcmp(type, "PRISONBREAK"))
return SECRET_PRISONBREAK;
else if (fastcmp(type, "SPECIALATTACK"))
return SECRET_SPECIALATTACK;
else if (fastcmp(type, "SPBATTACK"))
return SECRET_SPBATTACK;
else if (fastcmp(type, "ONLINE"))
return SECRET_ONLINE;
else if (fastcmp(type, "ADDONS"))
return SECRET_ADDONS;
else if (fastcmp(type, "EGGTV"))
return SECRET_EGGTV;
else if (fastcmp(type, "SOUNDTEST"))
return SECRET_SOUNDTEST;
else if (fastcmp(type, "ALTTITLE"))
return SECRET_ALTTITLE;
else if (fastcmp(type, "MEMETAUNTS"))
return SECRET_MEMETAUNTS;
else if (fastcmp(type, "ITEMFINDER"))
return SECRET_ITEMFINDER;
return SECRET_NONE;
}
void readunlockable(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -2350,52 +2399,7 @@ void readunlockable(MYFILE *f, INT32 num)
unlockables[num].majorunlock = (UINT8)(i != 0 || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "TYPE"))
{
if (fastcmp(word2, "NONE"))
unlockables[num].type = SECRET_NONE;
else if (fastcmp(word2, "EXTRAMEDAL"))
unlockables[num].type = SECRET_EXTRAMEDAL;
else if (fastcmp(word2, "CUP"))
unlockables[num].type = SECRET_CUP;
else if (fastcmp(word2, "MAP"))
unlockables[num].type = SECRET_MAP;
else if (fastcmp(word2, "ALTMUSIC"))
unlockables[num].type = SECRET_ALTMUSIC;
else if (fastcmp(word2, "SKIN"))
unlockables[num].type = SECRET_SKIN;
else if (fastcmp(word2, "FOLLOWER"))
unlockables[num].type = SECRET_FOLLOWER;
else if (fastcmp(word2, "COLOR"))
unlockables[num].type = SECRET_COLOR;
else if (fastcmp(word2, "HARDSPEED"))
unlockables[num].type = SECRET_HARDSPEED;
else if (fastcmp(word2, "MASTERMODE"))
unlockables[num].type = SECRET_MASTERMODE;
else if (fastcmp(word2, "ENCORE"))
unlockables[num].type = SECRET_ENCORE;
else if (fastcmp(word2, "TIMEATTACK"))
unlockables[num].type = SECRET_TIMEATTACK;
else if (fastcmp(word2, "PRISONBREAK"))
unlockables[num].type = SECRET_PRISONBREAK;
else if (fastcmp(word2, "SPECIALATTACK"))
unlockables[num].type = SECRET_SPECIALATTACK;
else if (fastcmp(word2, "SPBATTACK"))
unlockables[num].type = SECRET_SPBATTACK;
else if (fastcmp(word2, "ONLINE"))
unlockables[num].type = SECRET_ONLINE;
else if (fastcmp(word2, "ADDONS"))
unlockables[num].type = SECRET_ADDONS;
else if (fastcmp(word2, "EGGTV"))
unlockables[num].type = SECRET_EGGTV;
else if (fastcmp(word2, "SOUNDTEST"))
unlockables[num].type = SECRET_SOUNDTEST;
else if (fastcmp(word2, "ALTTITLE"))
unlockables[num].type = SECRET_ALTTITLE;
else if (fastcmp(word2, "MEMETAUNTS"))
unlockables[num].type = SECRET_MEMETAUNTS;
else if (fastcmp(word2, "ITEMFINDER"))
unlockables[num].type = SECRET_ITEMFINDER;
else
unlockables[num].type = (INT16)i;
unlockables[num].type = parseunlockabletype(word2);
unlockables[num].stringVarCache = -1;
}
else if (fastcmp(word, "VAR"))
@ -2699,6 +2703,31 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
return;
}
}
else if (fastcmp(params[0], "UNLOCKPERCENT"))
{
PARAMCHECK(1);
ty = UC_UNLOCKPERCENT;
re = atoi(params[1]);
x1 = SECRET_NONE;
// Valid percentages only!
if (re <= 0 || re > 100)
{
deh_warning("Condition percent %d out of range (1 - 100) for condition ID %d", re, id+1);
return;
}
if (params[2] && params[2][0])
{
x1 = parseunlockabletype(params[2]);
if (x1 <= SECRET_NONE || x1 >= SECRET_ONEPERBOARD)
{
deh_warning("Condition challenge type \"%s\" invalid for condition ID %d", params[2], id+1);
return;
}
}
}
else if ((offset=0) || fastcmp(params[0], "ADDON")
|| (++offset && fastcmp(params[0], "CREDITS"))
|| (++offset && fastcmp(params[0], "REPLAY"))

View file

@ -1323,6 +1323,73 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
case UC_UNLOCKPERCENT:
{
UINT16 i, unlocked = 0, total = 0;
// Special case for maps
if (cn->extrainfo1 == SECRET_MAP)
{
for (i = 0; i < basenummapheaders; i++)
{
if (!mapheaderinfo[i] || mapheaderinfo[i]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU))
continue;
total++;
// Check for completion
if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED)
&& !(mapheaderinfo[i]->records.mapvisited & MV_BEATEN))
continue;
// Check for unlock
if (M_MapLocked(i+1))
continue;
unlocked++;
}
}
// Special case for raw Challenge count
else if (cn->extrainfo1 == SECRET_NONE)
{
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (unlockables[i].type == SECRET_NONE)
continue;
total++;
if (M_Achieved(unlockables[i].conditionset - 1) == false)
continue;
unlocked++;
}
unlocked++; // Try to account for this one too
}
else
{
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (unlockables[i].type != cn->extrainfo1)
continue;
total++;
if (gamedata->unlocked[i] == false)
continue;
unlocked++;
}
}
if (!total)
return false;
// No need to do a pesky divide
return ((100 * unlocked) >= (total * cn->requirement));
}
case UC_ADDON:
return ((gamedata->everloadedaddon == true)
&& M_SecretUnlocked(SECRET_ADDONS, true));
@ -1919,6 +1986,62 @@ static const char *M_GetConditionString(condition_t *cn)
? unlockables[cn->requirement-1].name
: "???");
case UC_UNLOCKPERCENT:
{
boolean checkavailable = false;
switch (cn->extrainfo1)
{
case SECRET_NONE:
work = "completion";
break;
case SECRET_EXTRAMEDAL:
work = "of Challenge Medals";
break;
case SECRET_CUP:
work = "of Cups";
break;
case SECRET_MAP:
work = "of Courses";
break;
case SECRET_ALTMUSIC:
work = "of alternate music";
checkavailable = true;
break;
case SECRET_SKIN:
work = "of Characters";
checkavailable = true;
break;
case SECRET_FOLLOWER:
work = "of Followers";
checkavailable = true;
break;
case SECRET_COLOR:
work = "of Spray Cans";
checkavailable = true;
break;
default:
return va("INVALID CHALLENGE FOR PERCENT \"%d\"", cn->requirement);
}
if (checkavailable == true)
{
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].type != cn->extrainfo1)
continue;
if (gamedata->unlocked[i] == false)
continue;
break;
}
if (i == MAXUNLOCKABLES)
work = "of ???";
}
return va("CHALLENGES: get %u%% %s", cn->requirement, work);
}
case UC_ADDON:
if (!M_SecretUnlocked(SECRET_ADDONS, true))
return NULL;

View file

@ -56,6 +56,8 @@ typedef enum
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
UC_CONDITIONSET, // CONDITIONSET [condition set number]
UC_UNLOCKPERCENT, // Unlock <x percent> of [unlockable type]
UC_ADDON, // Ever loaded a custom file?
UC_CREDITS, // Finish watching the credits
UC_REPLAY, // Save a replay
@ -199,8 +201,11 @@ typedef enum
SECRET_FOLLOWER, // Permit this follower
SECRET_COLOR, // Permit this color
// Everything below this line is supposed to be only one per Challenges list
SECRET_ONEPERBOARD,
// Difficulty restrictions
SECRET_HARDSPEED, // Permit Hard gamespeed
SECRET_HARDSPEED = SECRET_ONEPERBOARD, // Permit Hard gamespeed
SECRET_MASTERMODE, // Permit Master Mode bots in GP
SECRET_ENCORE, // Permit Encore option