From 52674c7bb2e7bf664d02996b04a3ae2f551a6212 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 14 Oct 2023 18:42:27 +0100 Subject: [PATCH] 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 --- src/deh_soc.c | 121 ++++++++++++++++++++++++++++++------------------- src/m_cond.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/m_cond.h | 7 ++- 3 files changed, 204 insertions(+), 47 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 03f77b71c..a0a1c5335 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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")) diff --git a/src/m_cond.c b/src/m_cond.c index 0fc90036c..5d285f615 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -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; diff --git a/src/m_cond.h b/src/m_cond.h index d7224b563..39b1ecb02 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -56,6 +56,8 @@ typedef enum UC_UNLOCKABLE, // UNLOCKABLE [unlockable number] UC_CONDITIONSET, // CONDITIONSET [condition set number] + UC_UNLOCKPERCENT, // Unlock 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