mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-03-08 04:06:25 +00:00
M_PopulateChallengeGrid
Basic challenge grid data is now generated the first time you head to the challenges menu.
- Large tiles are placed first.
- `UINT8 majorunlock` in `unlockable_t`.
- Builds a list of all possible positions the first large tile could be plonked at.
- Randomly selects from that list, then removes every position that overlaps the given spot before the next large tile is handled.
- Smaller tiles are filled into all the remaining gaps.
- Currently bubbles gaps through the random list if empty spots after large tile placement > number of small tiles to place, but all the gaps could be forced to the end.
- Has a REALLY prelim drawer, literally just enough to confirm the tilegrid data is correct visually.
- DEVELOP: Can be regenerated by pressing (C) while the challenges are up.
Also, general maintenance.
- Remove `showconditionset`, `nocecho`, and `nochecklist` from `unlockable_t` for not fitting with our new intent for challenges
- Remove M_AnySecretUnlocked - Not currently used, but its eventual use - stopping a player from seeing a completely blank challenges grid - isn't in the spirit.
- M_MapLocked no longer permits all map transitions in DEVELOP, so unlocks can actually be tested.
This commit is contained in:
parent
f0e5e1b71a
commit
1391cbe01e
6 changed files with 255 additions and 36 deletions
|
|
@ -2336,12 +2336,8 @@ void readunlockable(MYFILE *f, INT32 num)
|
|||
|
||||
if (fastcmp(word, "CONDITIONSET"))
|
||||
unlockables[num].conditionset = (UINT8)i;
|
||||
else if (fastcmp(word, "SHOWCONDITIONSET"))
|
||||
unlockables[num].showconditionset = (UINT8)i;
|
||||
else if (fastcmp(word, "NOCECHO"))
|
||||
unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "NOCHECKLIST"))
|
||||
unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "MAJORUNLOCK"))
|
||||
unlockables[num].majorunlock = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
||||
else if (fastcmp(word, "TYPE"))
|
||||
{
|
||||
if (fastcmp(word2, "NONE"))
|
||||
|
|
|
|||
30
src/g_game.c
30
src/g_game.c
|
|
@ -4411,6 +4411,23 @@ void G_LoadGameData(void)
|
|||
i += j;
|
||||
}
|
||||
|
||||
gamedata->challengegridwidth = READUINT16(save_p);
|
||||
Z_Free(gamedata->challengegrid);
|
||||
if (gamedata->challengegridwidth)
|
||||
{
|
||||
gamedata->challengegrid = Z_Malloc(
|
||||
(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)),
|
||||
PU_STATIC, NULL);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
gamedata->challengegrid[i] = READUINT8(save_p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gamedata->challengegrid = NULL;
|
||||
}
|
||||
|
||||
gamedata->timesBeaten = READUINT32(save_p);
|
||||
|
||||
// Main records
|
||||
|
|
@ -4554,6 +4571,19 @@ void G_SaveGameData(void)
|
|||
i += j;
|
||||
}
|
||||
|
||||
if (gamedata->challengegrid)
|
||||
{
|
||||
WRITEUINT16(save_p, gamedata->challengegridwidth);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
WRITEUINT8(save_p, gamedata->challengegrid[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITEUINT16(save_p, 0);
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, gamedata->timesBeaten); // 4
|
||||
|
||||
// Main records
|
||||
|
|
|
|||
|
|
@ -4460,7 +4460,8 @@ void M_DrawAddons(void)
|
|||
|
||||
void M_DrawChallenges(void)
|
||||
{
|
||||
INT32 x, y;
|
||||
INT32 x = 20, y = 20;
|
||||
UINT8 i, j, unlock;
|
||||
|
||||
{
|
||||
patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE);
|
||||
|
|
@ -4469,16 +4470,34 @@ void M_DrawChallenges(void)
|
|||
|
||||
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
|
||||
{
|
||||
V_DrawThinString(currentMenu->x, currentMenu->y, V_ALLOWLOWERCASE, unlockables[challengesmenu.currentunlock].name);
|
||||
V_DrawThinString(x, y, V_ALLOWLOWERCASE, unlockables[challengesmenu.currentunlock].name);
|
||||
|
||||
if (challengesmenu.unlockanim >= UNLOCKTIME)
|
||||
V_DrawThinString(currentMenu->x, currentMenu->y + 10, V_ALLOWLOWERCASE, "Press (A)");
|
||||
V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE, "Press (A)");
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawThinString(currentMenu->x, currentMenu->y, V_ALLOWLOWERCASE, va("pending = %c", challengesmenu.pending ? 'T' : 'F'));
|
||||
V_DrawThinString(x, y, V_ALLOWLOWERCASE, va("pending = %c", challengesmenu.pending ? 'T' : 'F'));
|
||||
|
||||
if (challengesmenu.unlockanim >= UNLOCKTIME)
|
||||
V_DrawThinString(currentMenu->x, currentMenu->y + 10, V_ALLOWLOWERCASE, "Press (B)");
|
||||
V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE, "Press (B)");
|
||||
}
|
||||
|
||||
x = currentMenu->x;
|
||||
y = currentMenu->y;
|
||||
|
||||
for (i = 0; i < gamedata->challengegridwidth; i++)
|
||||
{
|
||||
for (j = 0; j < CHALLENGEGRIDHEIGHT; j++)
|
||||
{
|
||||
unlock = gamedata->challengegrid[(i * CHALLENGEGRIDHEIGHT) + j];
|
||||
// very WIP render of tilegrid
|
||||
if (unlock >= MAXUNLOCKABLES)
|
||||
V_DrawString(x + 10*i, y + 10*j, V_ALLOWLOWERCASE, ".");
|
||||
else if (gamedata->unlocked[unlock] == false)
|
||||
V_DrawString(x + 10*i, y + 10*j, V_ALLOWLOWERCASE, "?");
|
||||
else
|
||||
V_DrawString(x + 10*i, y + 10*j, V_ALLOWLOWERCASE, va("%c", unlockables[unlock].name[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -781,8 +781,10 @@ static boolean M_PrevOpt(void)
|
|||
|
||||
if (M_GetNextAchievedUnlock(false) < MAXUNLOCKABLES)
|
||||
{
|
||||
MISC_ChallengesDef.prevMenu = desiredmenu;
|
||||
challengesmenu.pending = true;
|
||||
challengesmenu.currentunlock = MAXUNLOCKABLES;
|
||||
M_PopulateChallengeGrid();
|
||||
return &MISC_ChallengesDef;
|
||||
}
|
||||
|
||||
|
|
@ -6856,6 +6858,17 @@ boolean M_ChallengesInputs(INT32 ch)
|
|||
{
|
||||
;
|
||||
}
|
||||
#ifdef DEVELOP
|
||||
else if (M_MenuExtraPressed(pid)) // debugging
|
||||
{
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
M_PopulateChallengeGrid();
|
||||
challengesmenu.unlockanim = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if (challengesmenu.pending)
|
||||
{
|
||||
if ((M_MenuConfirmPressed(pid) || start))
|
||||
|
|
|
|||
199
src/m_cond.c
199
src/m_cond.c
|
|
@ -11,6 +11,7 @@
|
|||
/// \brief Unlockable condition system for SRB2 version 2.1
|
||||
|
||||
#include "m_cond.h"
|
||||
#include "m_random.h" // M_RandomKey
|
||||
#include "doomstat.h"
|
||||
#include "z_zone.h"
|
||||
|
||||
|
|
@ -54,6 +55,179 @@ void M_NewGameDataStruct(void)
|
|||
G_ClearRecords();
|
||||
}
|
||||
|
||||
void M_PopulateChallengeGrid(void)
|
||||
{
|
||||
UINT16 i, j;
|
||||
UINT16 numunlocks = 0, nummajorunlocks = 0, numempty = 0;
|
||||
UINT8 selection[2][MAXUNLOCKABLES + (CHALLENGEGRIDHEIGHT-1)];
|
||||
|
||||
if (gamedata->challengegrid != NULL)
|
||||
{
|
||||
// todo tweak your grid if unlocks are changed
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through unlockables
|
||||
for (i = 0; i < MAXUNLOCKABLES; ++i)
|
||||
{
|
||||
if (!unlockables[i].conditionset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlockables[i].majorunlock)
|
||||
{
|
||||
selection[1][nummajorunlocks++] = i;
|
||||
//CONS_Printf(" found %d (LARGE)\n", selection[1][nummajorunlocks-1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
selection[0][numunlocks++] = i;
|
||||
//CONS_Printf(" found %d\n", selection[0][numunlocks-1]);
|
||||
}
|
||||
|
||||
if (numunlocks + nummajorunlocks == 0)
|
||||
{
|
||||
gamedata->challengegridwidth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
gamedata->challengegridwidth = (numunlocks + (nummajorunlocks * 4) + (CHALLENGEGRIDHEIGHT-1))/CHALLENGEGRIDHEIGHT;
|
||||
|
||||
if (nummajorunlocks)
|
||||
{
|
||||
// Getting the number of 2-highs you can fit into two adjacent columns.
|
||||
UINT8 majorpad = (CHALLENGEGRIDHEIGHT/2);
|
||||
majorpad = (nummajorunlocks/majorpad)+1;
|
||||
if (gamedata->challengegridwidth < majorpad*2)
|
||||
gamedata->challengegridwidth = majorpad*2;
|
||||
}
|
||||
|
||||
gamedata->challengegrid = Z_Malloc(
|
||||
(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)),
|
||||
PU_STATIC, NULL);
|
||||
|
||||
memset(gamedata->challengegrid,
|
||||
MAXUNLOCKABLES,
|
||||
(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)));
|
||||
|
||||
// Attempt to place all large tiles first.
|
||||
if (nummajorunlocks)
|
||||
{
|
||||
// You lose one from CHALLENGEGRIDHEIGHT because it is impossible to place a 2-high tile on the bottom row.
|
||||
UINT16 numspots = gamedata->challengegridwidth * (CHALLENGEGRIDHEIGHT-1);
|
||||
// 0 is row, 1 is column
|
||||
UINT8 quickcheck[numspots][2];
|
||||
|
||||
// Prepare the easy-grab spots.
|
||||
for (i = 0; i < numspots; i++)
|
||||
{
|
||||
quickcheck[i][0] = i%(CHALLENGEGRIDHEIGHT-1);
|
||||
quickcheck[i][1] = i/(CHALLENGEGRIDHEIGHT-1);
|
||||
}
|
||||
|
||||
// Place in random valid locations.
|
||||
while (nummajorunlocks > 0 && numspots > 0)
|
||||
{
|
||||
UINT8 row, col;
|
||||
j = M_RandomKey(numspots);
|
||||
row = quickcheck[j][0];
|
||||
col = quickcheck[j][1];
|
||||
|
||||
// We always take from selection[1][] in order, but the PLACEMENT is still random.
|
||||
nummajorunlocks--;
|
||||
|
||||
//CONS_Printf("--- %d (LARGE) placed at (%d, %d)\n", selection[1][nummajorunlocks], row, col);
|
||||
|
||||
i = row + (col * CHALLENGEGRIDHEIGHT);
|
||||
gamedata->challengegrid[i] = gamedata->challengegrid[i+1] = selection[1][nummajorunlocks];
|
||||
if (col == gamedata->challengegridwidth-1)
|
||||
{
|
||||
i = row;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += CHALLENGEGRIDHEIGHT;
|
||||
}
|
||||
gamedata->challengegrid[i] = gamedata->challengegrid[i+1] = selection[1][nummajorunlocks];
|
||||
|
||||
if (nummajorunlocks == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < numspots; i++)
|
||||
{
|
||||
quickcheckagain:
|
||||
if (abs(((signed)quickcheck[i][0]) - ((signed)row)) <= 1 // Row distance
|
||||
|| abs(((signed)quickcheck[i][1]) - ((signed)col)) <= 1 // Column distance
|
||||
|| (quickcheck[i][1] == 0 && col == gamedata->challengegridwidth-1) // Wraparounds l->r
|
||||
|| (quickcheck[i][1] == gamedata->challengegridwidth-1 && col == 0)) // Wraparounds r->l
|
||||
{
|
||||
numspots--; // Remove from possible indicies
|
||||
if (i == numspots)
|
||||
break;
|
||||
// Shuffle remaining so we can keep on using M_RandomKey
|
||||
quickcheck[i][0] = quickcheck[numspots][0];
|
||||
quickcheck[i][1] = quickcheck[numspots][1];
|
||||
// Woah there - we've gotta check the one that just got put in our place.
|
||||
goto quickcheckagain;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (nummajorunlocks > 0)
|
||||
{
|
||||
I_Error("M_PopulateChallengeGrid: was not able to populate %d large tiles", nummajorunlocks);
|
||||
}
|
||||
}
|
||||
|
||||
// Space out empty entries to pepper into unlock list
|
||||
for (i = 0; i < gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; i++)
|
||||
{
|
||||
if (gamedata->challengegrid[i] != MAXUNLOCKABLES)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
numempty++;
|
||||
}
|
||||
|
||||
if (numunlocks > numempty)
|
||||
{
|
||||
I_Error("M_PopulateChallengeGrid: %d small unlocks vs %d empty spaces (%d gap)", numunlocks, numempty, (numunlocks-numempty));
|
||||
}
|
||||
|
||||
//CONS_Printf(" %d unlocks vs %d empty spaces\n", numunlocks, numempty);
|
||||
|
||||
while (numunlocks < numempty)
|
||||
{
|
||||
//CONS_Printf(" adding empty)\n");
|
||||
selection[0][numunlocks++] = MAXUNLOCKABLES;
|
||||
}
|
||||
|
||||
// Fill the remaining spots with random ordinary unlocks (and empties).
|
||||
for (i = 0; i < gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; i++)
|
||||
{
|
||||
if (gamedata->challengegrid[i] != MAXUNLOCKABLES)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
j = M_RandomKey(numunlocks); // Get an entry
|
||||
gamedata->challengegrid[i] = selection[0][j]; // Set that entry
|
||||
//CONS_Printf(" %d placed at (%d, %d)\n", selection[0][j], i/CHALLENGEGRIDHEIGHT, i%CHALLENGEGRIDHEIGHT);
|
||||
numunlocks--; // Remove from possible indicies
|
||||
selection[0][j] = selection[0][numunlocks]; // Shuffle remaining so we can keep on using M_RandomKey
|
||||
|
||||
if (numunlocks == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
|
||||
{
|
||||
condition_t *cond;
|
||||
|
|
@ -105,6 +279,10 @@ void M_ClearSecrets(void)
|
|||
for (i = 0; i < MAXCONDITIONSETS; ++i)
|
||||
gamedata->achieved[i] = false;
|
||||
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
|
||||
gamedata->timesBeaten = 0;
|
||||
|
||||
// Re-unlock any always unlocked things
|
||||
|
|
@ -406,22 +584,6 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
|
|||
// -------------------
|
||||
// Quick unlock checks
|
||||
// -------------------
|
||||
UINT8 M_AnySecretUnlocked(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (1)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < MAXUNLOCKABLES; ++i)
|
||||
{
|
||||
if (!unlockables[i].nocecho && gamedata->unlocked[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8 M_SecretUnlocked(INT32 type)
|
||||
{
|
||||
|
|
@ -455,10 +617,6 @@ UINT8 M_SecretUnlocked(INT32 type)
|
|||
|
||||
UINT8 M_MapLocked(INT32 mapnum)
|
||||
{
|
||||
#ifdef DEVELOP
|
||||
(void)mapnum;
|
||||
return false;
|
||||
#else
|
||||
// Don't lock maps in dedicated servers.
|
||||
// That just makes hosts' lives hell.
|
||||
if (dedicated)
|
||||
|
|
@ -471,7 +629,6 @@ UINT8 M_MapLocked(INT32 mapnum)
|
|||
return true;
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
INT32 M_CountEmblems(void)
|
||||
|
|
|
|||
12
src/m_cond.h
12
src/m_cond.h
|
|
@ -94,12 +94,10 @@ typedef struct
|
|||
char name[64];
|
||||
char objective[64];
|
||||
UINT8 conditionset;
|
||||
UINT8 showconditionset;
|
||||
INT16 type;
|
||||
INT16 variable;
|
||||
char *stringVar;
|
||||
UINT8 nocecho;
|
||||
UINT8 nochecklist;
|
||||
UINT8 majorunlock;
|
||||
} unlockable_t;
|
||||
|
||||
#define SECRET_NONE 0 // Does nil. Use with levels locked by UnlockRequired
|
||||
|
|
@ -130,6 +128,8 @@ typedef struct
|
|||
#define MAXEXTRAEMBLEMS 16
|
||||
#define MAXUNLOCKABLES 32
|
||||
|
||||
#define CHALLENGEGRIDHEIGHT 5
|
||||
|
||||
// GAMEDATA STRUCTURE
|
||||
// Everything that would get saved in gamedata.dat
|
||||
typedef struct
|
||||
|
|
@ -149,6 +149,10 @@ typedef struct
|
|||
// UNLOCKABLES UNLOCKED
|
||||
boolean unlocked[MAXUNLOCKABLES];
|
||||
|
||||
// CHALLENGE GRID
|
||||
UINT16 challengegridwidth;
|
||||
UINT8 *challengegrid;
|
||||
|
||||
// # OF TIMES THE GAME HAS BEEN BEATEN
|
||||
UINT32 timesBeaten;
|
||||
|
||||
|
|
@ -170,6 +174,7 @@ extern INT32 numextraemblems;
|
|||
extern UINT32 unlocktriggers;
|
||||
|
||||
void M_NewGameDataStruct(void);
|
||||
void M_PopulateChallengeGrid(void);
|
||||
|
||||
// Condition set setup
|
||||
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
|
||||
|
|
@ -187,7 +192,6 @@ UINT8 M_CheckLevelEmblems(void);
|
|||
UINT8 M_CompletionEmblems(void);
|
||||
|
||||
// Checking unlockable status
|
||||
UINT8 M_AnySecretUnlocked(void);
|
||||
UINT8 M_SecretUnlocked(INT32 type);
|
||||
UINT8 M_MapLocked(INT32 mapnum);
|
||||
INT32 M_CountEmblems(void);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue