Removing extraemblem_t

- You can now create an unlockable that gives you an emblem only with SECRET_EXTRAEMBLEM.
    - One step above the completely rewardless squares from Kirby Air Ride.
- Added color to `unlockable_t`.
    - Can be used both with user-specified icons (remappable green->color) and with non-SECRET_SKIN default graphics (invincibility full-range remap)
- Replaced condition type UC_EXTRAEMBLEM with the more general UC_UNLOCKABLE.
- MAXUNLOCKABLES is now == MAXCONDITIONSETS
This commit is contained in:
toaster 2022-12-03 14:52:58 +00:00
parent 017871eaad
commit d061dd09fd
7 changed files with 77 additions and 259 deletions

View file

@ -2203,88 +2203,6 @@ void reademblemdata(MYFILE *f, INT32 num)
Z_Free(s);
}
void readextraemblemdata(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word = s;
char *word2;
char *tmp;
INT32 value;
memset(&extraemblems[num-1], 0, sizeof(extraemblem_t));
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
*(tmp-1) = '\0';
else
break;
strupr(word);
// Now get the part after
word2 = tmp += 2;
value = atoi(word2); // used for numerical settings
if (fastcmp(word, "NAME"))
deh_strlcpy(extraemblems[num-1].name, word2,
sizeof (extraemblems[num-1].name), va("Extra emblem %d: name", num));
else if (fastcmp(word, "OBJECTIVE"))
deh_strlcpy(extraemblems[num-1].description, word2,
sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num));
else if (fastcmp(word, "CONDITIONSET"))
extraemblems[num-1].conditionset = (UINT8)value;
else if (fastcmp(word, "SHOWCONDITIONSET"))
extraemblems[num-1].showconditionset = (UINT8)value;
else
{
strupr(word2);
if (fastcmp(word, "SPRITE"))
{
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = word2[0];
else
value += 'A'-1;
if (value < 'A' || value > 'Z')
deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num);
else
extraemblems[num-1].sprite = (UINT8)value;
}
else if (fastcmp(word, "COLOR"))
extraemblems[num-1].color = get_number(word2);
else
deh_warning("Extra emblem %d: unknown word '%s'", num, word);
}
}
} while (!myfeof(f));
if (!extraemblems[num-1].sprite)
extraemblems[num-1].sprite = 'C';
if (!extraemblems[num-1].color)
extraemblems[num-1].color = SKINCOLOR_RED;
Z_Free(s);
}
void readunlockable(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -2294,7 +2212,6 @@ void readunlockable(MYFILE *f, INT32 num)
INT32 i;
memset(&unlockables[num], 0, sizeof(unlockable_t));
unlockables[num].objective[0] = '/';
do
{
@ -2329,10 +2246,7 @@ void readunlockable(MYFILE *f, INT32 num)
if (fastcmp(word, "NAME"))
deh_strlcpy(unlockables[num].name, word2,
sizeof (unlockables[num].name), va("Unlockable %d: name", num));
else if (fastcmp(word, "OBJECTIVE"))
deh_strlcpy(unlockables[num].objective, word2,
sizeof (unlockables[num].objective), va("Unlockable %d: objective", num));
sizeof (unlockables[num].name), va("Unlockable %d: name", num+1));
else
{
strupr(word2);
@ -2351,8 +2265,6 @@ void readunlockable(MYFILE *f, INT32 num)
unlockables[num].type = SECRET_SKIN;
else if (fastcmp(word2, "WARP"))
unlockables[num].type = SECRET_WARP;
else if (fastcmp(word2, "LEVELSELECT"))
unlockables[num].type = SECRET_LEVELSELECT;
else if (fastcmp(word2, "TIMEATTACK"))
unlockables[num].type = SECRET_TIMEATTACK;
else if (fastcmp(word2, "BREAKTHECAPSULES"))
@ -2387,6 +2299,11 @@ void readunlockable(MYFILE *f, INT32 num)
Z_Free(unlockables[num].icon);
unlockables[num].icon = Z_StrDup(word2);
}
else if (fastcmp(word, "COLOR"))
{
unlockables[num].color = get_number(word2);
CONS_Printf("%d+1 has color %s\n",num+1, skincolors[unlockables[num].color].name);
}
else
deh_warning("Unlockable %d: unknown word '%s'", num+1, word);
}
@ -2515,15 +2432,15 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
return;
}
}
else if (fastcmp(params[0], "EXTRAEMBLEM"))
else if (fastcmp(params[0], "UNLOCKABLE"))
{
PARAMCHECK(1);
ty = UC_EXTRAEMBLEM;
ty = UC_UNLOCKABLE;
re = atoi(params[1]);
if (re <= 0 || re > MAXEXTRAEMBLEMS)
if (re <= 0 || re > MAXUNLOCKABLES)
{
deh_warning("Extra emblem %d out of range (1 - %d) for condition ID %d", re, MAXEXTRAEMBLEMS, id+1);
deh_warning("Unlockable %d out of range (1 - %d) for condition ID %d", re, MAXUNLOCKABLES, id+1);
return;
}
}

View file

@ -60,7 +60,6 @@ void readwipes(MYFILE *f);
void readmaincfg(MYFILE *f);
void readconditionset(MYFILE *f, UINT8 setnum);
void readunlockable(MYFILE *f, INT32 num);
void readextraemblemdata(MYFILE *f, INT32 num);
void reademblemdata(MYFILE *f, INT32 num);
void readsound(MYFILE *f, INT32 num);
void readframe(MYFILE *f, INT32 num);

View file

@ -278,32 +278,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
}
continue;
}
else if (fastcmp(word, "EXTRAEMBLEM"))
{
if (!mainfile && !gamedataadded)
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
}
else
{
if (!word2)
i = numextraemblems + 1;
if (i > 0 && i <= MAXEXTRAEMBLEMS)
{
if (numextraemblems < i)
numextraemblems = i;
readextraemblemdata(f, i);
}
else
{
deh_warning("Extra emblem number %d out of range (1 - %d)", i, MAXEXTRAEMBLEMS);
ignorelines(f);
}
}
continue;
}
if (word2)
{
if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT"))
@ -593,12 +567,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
clear_emblems();
}
if (clearall || fastcmp(word2, "EXTRAEMBLEMS"))
{
memset(&extraemblems, 0, sizeof(extraemblems));
numextraemblems = 0;
}
if (clearall || fastcmp(word2, "CONDITIONSETS"))
clear_conditionsets();

View file

@ -4389,13 +4389,6 @@ void G_LoadGameData(void)
gamedata->collected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
gamedata->extraCollected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
rtemp = READUINT8(save_p);
@ -4520,7 +4513,7 @@ void G_SaveGameData(void)
return;
}
length = (4+4+4+1+(MAXEMBLEMS)+MAXEXTRAEMBLEMS+MAXUNLOCKABLES+MAXCONDITIONSETS+4+4);
length = (4+4+4+1+(MAXEMBLEMS+MAXUNLOCKABLES+MAXCONDITIONSETS)+4+4);
length += nummapheaders * (MAXMAPLUMPNAME+1+4+4);
save_p = savebuffer = (UINT8 *)malloc(length);
@ -4546,14 +4539,6 @@ void G_SaveGameData(void)
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;) // MAXEXTRAEMBLEMS * 1;
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (gamedata->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;) // MAXUNLOCKABLES * 1;
{
btemp = 0;

View file

@ -4468,6 +4468,9 @@ void M_DrawChallenges(void)
const char *str;
INT16 offset;
unlockable_t *ref = NULL;
patch_t *pat;
UINT8 *colormap;
fixed_t siz;
{
patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE);
@ -4517,50 +4520,51 @@ void M_DrawChallenges(void)
goto drawborder;
}
pat = missingpat;
colormap = NULL;
if (ref->icon != NULL)
{
patch_t *pat = W_CachePatchName(ref->icon, PU_CACHE);
fixed_t siz = (SHORT(pat->width) << FRACBITS);
siz = FixedDiv(((ref->majorunlock) ? 32 : 16) << FRACBITS, siz);
V_DrawFixedPatch(
x*FRACUNIT, y*FRACUNIT,
siz,
0, pat,
NULL
);
goto drawborder;
pat = W_CachePatchName(ref->icon, PU_CACHE);
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
{
colormap = R_GetTranslationColormap(TC_DEFAULT, ref->color, GTC_MENUCACHE);
}
}
switch (ref->type)
else switch (ref->type)
{
// add all SECRET_ENCORE, etc up here before SECRET_SKIN
case SECRET_SKIN:
{
INT32 skin = M_UnlockableSkinNum(ref);
if (skin != -1)
{
UINT8 *colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
UINT8 size = (ref->majorunlock) ? FACE_WANTED : FACE_RANK;
V_DrawMappedPatch(x, y, 0, faceprefix[skin][size], colormap);
colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
pat = faceprefix[skin][(ref->majorunlock) ? FACE_WANTED : FACE_RANK];
}
else
{
V_DrawMappedPatch(x, y, 0, missingpat, NULL);
}
break;
}
default:
{
patch_t *pat = W_CachePatchName(va("UN_RR00%c", ref->majorunlock ? 'B' : 'A'), PU_CACHE);
V_DrawMappedPatch(x, y, 0, pat, NULL);
//V_DrawString(x, y, V_ALLOWLOWERCASE, va("%c", ref->name[0]));
pat = W_CachePatchName(va("UN_RR00%c", ref->majorunlock ? 'B' : 'A'), PU_CACHE);
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
{
CONS_Printf(" color for %d is %s\n", num, skincolors[unlockables[num].color].name);
colormap = R_GetTranslationColormap(TC_RAINBOW, ref->color, GTC_MENUCACHE);
}
break;
}
}
siz = (SHORT(pat->width) << FRACBITS);
siz = FixedDiv(((ref->majorunlock) ? 32 : 16) << FRACBITS, siz);
V_DrawFixedPatch(
x*FRACUNIT, y*FRACUNIT,
siz,
0, pat,
colormap
);
drawborder:
if (i != challengesmenu.hilix)
continue;

View file

@ -37,15 +37,11 @@ conditionset_t conditionSets[MAXCONDITIONSETS];
// Emblem locations
emblem_t emblemlocations[MAXEMBLEMS];
// Extra emblems
extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
// Unlockables
unlockable_t unlockables[MAXUNLOCKABLES];
// Number of emblems and extra emblems
// Number of emblems
INT32 numemblems = 0;
INT32 numextraemblems = 0;
// Create a new gamedata_t, for start-up
void M_NewGameDataStruct(void)
@ -442,8 +438,6 @@ void M_ClearSecrets(void)
for (i = 0; i < MAXEMBLEMS; ++i)
gamedata->collected[i] = false;
for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
gamedata->extraCollected[i] = false;
for (i = 0; i < MAXUNLOCKABLES; ++i)
gamedata->unlocked[i] = false;
for (i = 0; i < MAXCONDITIONSETS; ++i)
@ -511,8 +505,8 @@ UINT8 M_CheckCondition(condition_t *cn)
return (M_GotEnoughEmblems(cn->requirement));
case UC_EMBLEM: // Requires emblem x to be obtained
return gamedata->collected[cn->requirement-1];
case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
return gamedata->extraCollected[cn->requirement-1];
case UC_UNLOCKABLE: // Requires unlockable x to be obtained
return gamedata->unlocked[cn->requirement-1];
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
}
@ -574,32 +568,11 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud)
// Just in case they aren't to sync
M_CheckLevelEmblems();
M_CompletionEmblems();
}
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (gamedata->extraCollected[i] || !extraemblems[i].conditionset)
{
continue;
}
if ((gamedata->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1)) == false)
{
continue;
}
if (loud)
{
strcat(cechoText, va("Got \"%s\" medal!\n", 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)
// Fun part: if any of those unlocked we need to go through the
// unlock conditions AGAIN just in case an emblem reward was reached
M_CheckUnlockConditions();
}
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
@ -643,6 +616,8 @@ UINT8 M_GetNextAchievedUnlock(void)
{
UINT8 i;
M_CheckUnlockConditions();
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
@ -801,13 +776,17 @@ INT32 M_CountEmblems(void)
INT32 found = 0, i;
for (i = 0; i < numemblems; ++i)
{
if (gamedata->collected[i])
found++;
if (!gamedata->collected[i])
continue;
found++;
}
for (i = 0; i < numextraemblems; ++i)
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (gamedata->extraCollected[i])
found++;
if (unlockables[i].type != SECRET_EXTRAEMBLEM)
continue;
if (!gamedata->unlocked[i])
continue;
found++;
}
return found;
}
@ -823,13 +802,21 @@ UINT8 M_GotEnoughEmblems(INT32 number)
INT32 i, gottenemblems = 0;
for (i = 0; i < numemblems; ++i)
{
if (gamedata->collected[i])
if (++gottenemblems >= number) return true;
if (!gamedata->collected[i])
continue;
if (++gottenemblems < number)
continue;
return true;
}
for (i = 0; i < numextraemblems; ++i)
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (gamedata->extraCollected[i])
if (++gottenemblems >= number) return true;
if (unlockables[i].type != SECRET_EXTRAEMBLEM)
continue;
if (!gamedata->unlocked[i])
continue;
if (++gottenemblems < number)
continue;
return true;
}
return false;
}
@ -938,29 +925,3 @@ const char *M_GetEmblemPatch(emblem_t *em, boolean big)
return pnamebuf;
}
skincolornum_t M_GetExtraEmblemColor(extraemblem_t *em)
{
if (!em || em->color >= numskincolors)
return SKINCOLOR_NONE;
return em->color;
}
const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big)
{
static char pnamebuf[7];
if (!big)
strcpy(pnamebuf, "GOTITn");
else
strcpy(pnamebuf, "EMBMn0");
I_Assert(em->sprite >= 'A' && em->sprite <= 'Z');
if (!big)
pnamebuf[5] = em->sprite;
else
pnamebuf[4] = em->sprite;
return pnamebuf;
}

View file

@ -32,7 +32,7 @@ typedef enum
UC_TRIGGER, // TRIGGER [trigger number]
UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems]
UC_EMBLEM, // EMBLEM [emblem number]
UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number]
UC_UNLOCKABLE, // UNLOCKABLE [unlockable number]
UC_CONDITIONSET, // CONDITIONSET [condition set number]
} conditiontype_t;
@ -78,22 +78,13 @@ typedef struct
char *stringVar; ///< String version
char hint[110]; ///< Hint for emblem hints menu
} emblem_t;
typedef struct
{
char name[20]; ///< Name of the goal (used in the "emblem awarded" cecho)
char description[40]; ///< Description of goal (used in statistics)
UINT8 conditionset; ///< Condition set that awards this emblem.
UINT8 showconditionset; ///< Condition set that shows this emblem.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT16 color; ///< skincolor to use
} extraemblem_t;
// Unlockable information
typedef struct
{
char name[64];
char objective[64];
char *icon;
UINT16 color;
UINT8 conditionset;
INT16 type;
INT16 variable;
@ -105,8 +96,9 @@ typedef struct
#define SECRET_HEADER 1 // Does nothing on its own, just serves as a header for the menu
#define SECRET_SKIN 2 // Allow this character to be selected
#define SECRET_WARP 3 // Selectable warp
#define SECRET_LEVELSELECT 4 // Selectable level select
#define SECRET_WARP 3 // Selectable warp (todo Followers)
#define SECRET_EXTRAEMBLEM 4 // Extra Emblems (formerly extraemblem_t)
#define SECRET_TIMEATTACK 5 // Enables Time Attack on the main menu
#define SECRET_BREAKTHECAPSULES 6 // Enables Break the Capsules on the main menu
@ -126,8 +118,7 @@ typedef struct
// you seriously need to get a life.
#define MAXCONDITIONSETS UINT8_MAX
#define MAXEMBLEMS 512
#define MAXEXTRAEMBLEMS 16
#define MAXUNLOCKABLES (MAXCONDITIONSETS-MAXEXTRAEMBLEMS)
#define MAXUNLOCKABLES MAXCONDITIONSETS
#define CHALLENGEGRIDHEIGHT 5
@ -144,9 +135,6 @@ typedef struct
// EMBLEMS COLLECTED
boolean collected[MAXEMBLEMS];
// EXTRA EMBLEMS COLLECTED
boolean extraCollected[MAXEXTRAEMBLEMS];
// UNLOCKABLES UNLOCKED
boolean unlocked[MAXUNLOCKABLES];
@ -166,11 +154,9 @@ extern gamedata_t *gamedata;
extern conditionset_t conditionSets[MAXCONDITIONSETS];
extern emblem_t emblemlocations[MAXEMBLEMS];
extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
extern unlockable_t unlockables[MAXUNLOCKABLES];
extern INT32 numemblems;
extern INT32 numextraemblems;
extern UINT32 unlocktriggers;
@ -207,8 +193,6 @@ INT32 M_CountEmblems(void);
emblem_t *M_GetLevelEmblems(INT32 mapnum);
skincolornum_t M_GetEmblemColor(emblem_t *em);
const char *M_GetEmblemPatch(emblem_t *em, boolean big);
skincolornum_t M_GetExtraEmblemColor(extraemblem_t *em);
const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
// If you're looking to compare stats for unlocks or what not, use these
// They stop checking upon reaching the target number so they