// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
// Copyright (C) 2012-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  m_cond.c
/// \brief Unlockable condition system for SRB2 version 2.1

#include "m_cond.h"
#include "doomstat.h"
#include "z_zone.h"

#include "hu_stuff.h" // CEcho
#include "v_video.h" // video flags

#include "g_game.h" // record info
#include "r_things.h" // numskins
//#include "r_draw.h" // R_GetColorByName
#include "k_kart.h" // K_GetKartColorByName

// Map triggers for linedef executors
// 32 triggers, one bit each
UINT32 unlocktriggers;

// The meat of this system lies in condition sets
conditionset_t conditionSets[MAXCONDITIONSETS];

// Default Emblem locations
emblem_t emblemlocations[MAXEMBLEMS] =
{
	// SILVER TIME TROPHIES
	{ET_TIME, 0,0,0,  1, 'T', SKINCOLOR_GREY,  90*TICRATE, "", 0}, // Green Hills Zone - Time: 1:30
	{ET_TIME, 0,0,0,  2, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Pipe Speedway Zone - Time: 1:50
	{ET_TIME, 0,0,0,  3, 'T', SKINCOLOR_GREY, 135*TICRATE, "", 0}, // Dark Race - 2:15
	{ET_TIME, 0,0,0,  4, 'T', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // Darkvile Garden Zone - 1:45
	{ET_TIME, 0,0,0,  5, 'T', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Northern District Zone - 2:20
	{ET_TIME, 0,0,0,  6, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Sonic Speedway Zone - 2:00
	{ET_TIME, 0,0,0,  7, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Egg Zeppelin Zone - 2:00
	{ET_TIME, 0,0,0,  8, 'T', SKINCOLOR_GREY,  95*TICRATE, "", 0}, // Hill Top Zone - 1:35
	{ET_TIME, 0,0,0,  9, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Sunbeam Paradise Zone - 1:50
	{ET_TIME, 0,0,0, 10, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Diamond Square Zone - 1:50
	{ET_TIME, 0,0,0, 11, 'T', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Misty Maze Zone - 2:30
	{ET_TIME, 0,0,0, 12, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Midnight Meadow Zone - 2:00
	{ET_TIME, 0,0,0, 13, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Megablock Castle Zone - 2:10
	{ET_TIME, 0,0,0, 14, 'T', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Sub-Zero Peak Zone - 2:30
	{ET_TIME, 0,0,0, 15, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Sapphire Coast Zone - 1:50
	{ET_TIME, 0,0,0, 16, 'T', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Silvercloud Island Zone - 2:20
	{ET_TIME, 0,0,0, 17, 'T', SKINCOLOR_GREY, 135*TICRATE, "", 0}, // Petroleum Refinery Zone - 2:15
	{ET_TIME, 0,0,0, 18, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Canyon Rush Zone - 2:10
	{ET_TIME, 0,0,0, 19, 'T', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Blue Mountain Zone - 2:40
	{ET_TIME, 0,0,0, 20, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Casino Resort Zone - 1:50
	{ET_TIME, 0,0,0, 21, 'T', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // Desert Palace Zone - 1:45
	{ET_TIME, 0,0,0, 22, 'T', SKINCOLOR_GREY, 165*TICRATE, "", 0}, // Red Barrage Area - 2:45
	{ET_TIME, 0,0,0, 23, 'T', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // Vanilla Hotel Zone - 1:45
	{ET_TIME, 0,0,0, 24, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Twinkle Cart - 1:50
	{ET_TIME, 0,0,0, 25, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Pleasure Castle - 1:50
	{ET_TIME, 0,0,0, 26, 'T', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Aurora Atoll Zone - 2:20
	{ET_TIME, 0,0,0, 27, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Barren Badlands Zone - 2:10
	{ET_TIME, 0,0,0, 28, 'T', SKINCOLOR_GREY, 155*TICRATE, "", 0}, // Toxic Palace Zone - 2:35
	{ET_TIME, 0,0,0, 29, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Ancient Tomb Zone - 2:10
	{ET_TIME, 0,0,0, 30, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Cloud Cradle Zone K - 2:00
	{ET_TIME, 0,0,0, 31, 'T', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Kodachrome Void Zone - 1:50
	{ET_TIME, 0,0,0, 32, 'T', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Egg Quarters - 2:20
	{ET_TIME, 0,0,0, 33, 'T', SKINCOLOR_GREY, 115*TICRATE, "", 0}, // Boiling Bedrock Zone - 1:55
	{ET_TIME, 0,0,0, 34, 'T', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Virtual Highway Zone - 2:20
	{ET_TIME, 0,0,0, 35, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Eggman's Nightclub Zone - 2:00
	{ET_TIME, 0,0,0, 36, 'T', SKINCOLOR_GREY,  90*TICRATE, "", 0}, // KKR Ganbare Dochu 2 - 1:30
	{ET_TIME, 0,0,0, 37, 'T', SKINCOLOR_GREY,  80*TICRATE, "", 0}, // CK Chao Circuit 1 - 1:20
	{ET_TIME, 0,0,0, 38, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // CK Chao Circuit 2 - 2:10
	{ET_TIME, 0,0,0, 39, 'T', SKINCOLOR_GREY, 100*TICRATE, "", 0}, // CK Cloud Tops 2 - 1:40
	{ET_TIME, 0,0,0, 40, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // CK Regal Raceway - 2:10
	{ET_TIME, 0,0,0, 41, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // SM Dimension Heist - 2:10
	{ET_TIME, 0,0,0, 42, 'T', SKINCOLOR_GREY, 100*TICRATE, "", 0}, // SRB2 Frozen Night - 1:40
	{ET_TIME, 0,0,0, 43, 'T', SKINCOLOR_GREY, 100*TICRATE, "", 0}, // MKSC Sky Garden - 1:40
	{ET_TIME, 0,0,0, 44, 'T', SKINCOLOR_GREY,  95*TICRATE, "", 0}, // MKDS Peach Gardens - 1:35
	{ET_TIME, 0,0,0, 45, 'T', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // MKSC Rainbow Road - 1:45
	{ET_TIME, 0,0,0, 46, 'T', SKINCOLOR_GREY,  70*TICRATE, "", 0}, // SMK Mario Circuit 1 - 1:10
	{ET_TIME, 0,0,0, 47, 'T', SKINCOLOR_GREY,  90*TICRATE, "", 0}, // SMK Donut Plains 1 - 1:30
	{ET_TIME, 0,0,0, 48, 'T', SKINCOLOR_GREY,  75*TICRATE, "", 0}, // SMK Ghost Valley 2 - 1:15
	{ET_TIME, 0,0,0, 49, 'T', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // SMK Mario Circuit 3 - 1:45
	{ET_TIME, 0,0,0, 50, 'T', SKINCOLOR_GREY, 130*TICRATE, "", 0}  // SMK Rainbow Road - 2:10
	// GOLD DEV TIME TROPHIES
	// ...none yet!
};

// Default Extra Emblems
extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
{
	{"Experienced Driver", "Play 100 Matches", 10, 'X', SKINCOLOR_BLUE, 0},
};

// Default Unlockables
unlockable_t unlockables[MAXUNLOCKABLES] =
{
	// Name, Objective, Showing Conditionset, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist
	/* 01 */ {"Egg Cup",  "", -1, 1, SECRET_NONE, 0, false, false, 0},
	/* 02 */ {"SMK Cup",  "", -1, 2, SECRET_NONE, 0, false, false, 0},
	/* 03 */ {"Chao Cup", "", -1, 3, SECRET_NONE, 0, false, false, 0},

	/* 04 */ {"Hard Game Speed", "", -1, 4, SECRET_HARDSPEED, 0, false, false, 0},
	/* 05 */ {"Encore Mode",     "",  4, 5, SECRET_ENCORE, 0, false, false, 0},
	/* 06 */ {"Hell Attack",     "",  6, 6, SECRET_HELLATTACK, 0, false, false, 0},

	/* 07 */ {"Record Attack", "", -1, -1, SECRET_RECORDATTACK, 0, true, true, 0},
};

// Default number of emblems and extra emblems
INT32 numemblems = 50;
INT32 numextraemblems = 1;

// DEFAULT CONDITION SETS FOR SRB2KART:
void M_SetupDefaultConditionSets(void)
{
	memset(conditionSets, 0, sizeof(conditionSets));

	// -- 1: Collect 5 emblems OR play 10 matches
	M_AddRawCondition(1, 1, UC_TOTALEMBLEMS, 5, 0, 0);
	M_AddRawCondition(1, 2, UC_MATCHESPLAYED, 10, 0, 0);

	// -- 2: Collect 10 emblems OR play 25 matches
	M_AddRawCondition(2, 1, UC_TOTALEMBLEMS, 10, 0, 0);
	M_AddRawCondition(2, 2, UC_MATCHESPLAYED, 25, 0, 0);

	// -- 3: Collect 20 emblems OR play 50 matches
	M_AddRawCondition(3, 1, UC_TOTALEMBLEMS, 20, 0, 0);
	M_AddRawCondition(3, 2, UC_MATCHESPLAYED, 50, 0, 0);

	// -- 4: Collect 30 emblems OR play 100 matches
	M_AddRawCondition(4, 1, UC_TOTALEMBLEMS, 30, 0, 0);
	M_AddRawCondition(4, 2, UC_MATCHESPLAYED, 100, 0, 0);

	// -- 5: Collect 40 emblems OR play 150 matches
	M_AddRawCondition(5, 1, UC_TOTALEMBLEMS, 40, 0, 0);
	M_AddRawCondition(5, 2, UC_MATCHESPLAYED, 150, 0, 0);

	// -- 6: Collect 50 emblems ONLY
	M_AddRawCondition(6, 1, UC_TOTALEMBLEMS, 50, 0, 0);

	// -- 10: Play 300 matches
	M_AddRawCondition(10, 1, UC_MATCHESPLAYED, 300, 0, 0);
}

void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
{
	condition_t *cond;
	UINT32 num, wnum;

	I_Assert(set && set <= MAXCONDITIONSETS);

	wnum = conditionSets[set - 1].numconditions;
	num = ++conditionSets[set - 1].numconditions;

	conditionSets[set - 1].condition = Z_Realloc(conditionSets[set - 1].condition, sizeof(condition_t)*num, PU_STATIC, 0);

	cond = conditionSets[set - 1].condition;

	cond[wnum].id = id;
	cond[wnum].type = c;
	cond[wnum].requirement = r;
	cond[wnum].extrainfo1 = x1;
	cond[wnum].extrainfo2 = x2;
}

void M_ClearConditionSet(UINT8 set)
{
	if (conditionSets[set - 1].numconditions)
	{
		Z_Free(conditionSets[set - 1].condition);
		conditionSets[set - 1].condition = NULL;
		conditionSets[set - 1].numconditions = 0;
	}
	conditionSets[set - 1].achieved = false;
}

// Clear ALL secrets.
void M_ClearSecrets(void)
{
	INT32 i;

	memset(mapvisited, 0, sizeof(mapvisited));

	for (i = 0; i < MAXEMBLEMS; ++i)
		emblemlocations[i].collected = false;
	for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
		extraemblems[i].collected = false;
	for (i = 0; i < MAXUNLOCKABLES; ++i)
		unlockables[i].unlocked = false;
	for (i = 0; i < MAXCONDITIONSETS; ++i)
		conditionSets[i].achieved = false;

	timesBeaten = timesBeatenWithEmeralds = 0;

	// Re-unlock any always unlocked things
	M_SilentUpdateUnlockablesAndEmblems();
}

// ----------------------
// Condition set checking
// ----------------------
UINT8 M_CheckCondition(condition_t *cn)
{
	switch (cn->type)
	{
		case UC_PLAYTIME: // Requires total playing time >= x
			return (totalplaytime >= (unsigned)cn->requirement);
		case UC_MATCHESPLAYED: // Requires any level completed >= x times
			return (matchesplayed >= (unsigned)cn->requirement);
		case UC_GAMECLEAR: // Requires game beaten >= x times
			return (timesBeaten >= (unsigned)cn->requirement);
		case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times
			return (timesBeatenWithEmeralds >= (unsigned)cn->requirement);
		/*case UC_ULTIMATECLEAR: // Requires game beaten on ultimate >= x times (in other words, never)
			return (timesBeatenUltimate >= (unsigned)cn->requirement);*/
		case UC_OVERALLTIME: // Requires overall time <= x
			return (M_GotLowEnoughTime(cn->requirement));
		/*case UC_OVERALLSCORE: // Requires overall score >= x
			return (M_GotHighEnoughScore(cn->requirement));
		case UC_OVERALLRINGS: // Requires overall rings >= x
			return (M_GotHighEnoughRings(cn->requirement));*/
		case UC_MAPVISITED: // Requires map x to be visited
			return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
		case UC_MAPBEATEN: // Requires map x to be beaten
			return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
		case UC_MAPALLEMERALDS: // Requires map x to be beaten with all emeralds in possession
			return ((mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
		/*case UC_MAPULTIMATE: // Requires map x to be beaten on ultimate
			return ((mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
		case UC_MAPPERFECT: // Requires map x to be beaten with a perfect bonus
			return ((mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);*/
		case UC_MAPTIME: // Requires time on map <= x
			return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement);
		/*case UC_MAPSCORE: // Requires score on map >= x
			return (G_GetBestScore(cn->extrainfo1) >= (unsigned)cn->requirement);
		case UC_MAPRINGS: // Requires rings on map >= x
			return (G_GetBestRings(cn->extrainfo1) >= cn->requirement);
		case UC_NIGHTSSCORE:
			return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2) >= (unsigned)cn->requirement);
		case UC_NIGHTSTIME:
			return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2) <= (unsigned)cn->requirement);
		case UC_NIGHTSGRADE:
			return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2) >= cn->requirement);*/
		case UC_TRIGGER: // requires map trigger set
			return !!(unlocktriggers & (1 << cn->requirement));
		case UC_TOTALEMBLEMS: // Requires number of emblems >= x
			return (M_GotEnoughEmblems(cn->requirement));
		case UC_EMBLEM: // Requires emblem x to be obtained
			return emblemlocations[cn->requirement-1].collected;
		case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
			return extraemblems[cn->requirement-1].collected;
		case UC_CONDITIONSET: // requires condition set x to already be achieved
			return M_Achieved(cn->requirement-1);
	}
	return false;
}

static UINT8 M_CheckConditionSet(conditionset_t *c)
{
	UINT32 i;
	UINT32 lastID = 0;
	condition_t *cn;
	UINT8 achievedSoFar = true;

	for (i = 0; i < c->numconditions; ++i)
	{
		cn = &c->condition[i];

		// If the ID is changed and all previous statements of the same ID were true
		// then this condition has been successfully achieved
		if (lastID && lastID != cn->id && achievedSoFar)
			return true;

		// Skip future conditions with the same ID if one fails, for obvious reasons
		else if (lastID && lastID == cn->id && !achievedSoFar)
			continue;

		lastID = cn->id;
		achievedSoFar = M_CheckCondition(cn);
	}

	return achievedSoFar;
}

void M_CheckUnlockConditions(void)
{
	INT32 i;
	conditionset_t *c;

	for (i = 0; i < MAXCONDITIONSETS; ++i)
	{
		c = &conditionSets[i];
		if (!c->numconditions || c->achieved)
			continue;

		c->achieved = (M_CheckConditionSet(c));
	}
}

UINT8 M_UpdateUnlockablesAndExtraEmblems(boolean force)
{
	INT32 i;
	char cechoText[992] = "";
	UINT8 cechoLines = 0;

	if (modifiedgame && !savemoddata
		&& !force) // SRB2Kart: for enabling unlocks online in modified servers
		return false;

	M_CheckUnlockConditions();

	// Go through extra emblems
	for (i = 0; i < numextraemblems; ++i)
	{
		if (extraemblems[i].collected || !extraemblems[i].conditionset)
			continue;
		if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
		{
			strcat(cechoText, va(M_GetText("Got \"%s\" emblem!\\"), 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)
		M_CheckUnlockConditions();

	// Go through unlockables
	for (i = 0; i < MAXUNLOCKABLES; ++i)
	{
		if (unlockables[i].unlocked || !unlockables[i].conditionset)
			continue;
		if ((unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1)) != false)
		{
			if (unlockables[i].nocecho)
				continue;
			strcat(cechoText, va(M_GetText("\"%s\" unlocked!\\"), unlockables[i].name));
			++cechoLines;
		}
	}

	// Announce
	if (cechoLines)
	{
		char slashed[1024] = "";
		for (i = 0; (i < 21) && (i < 24 - cechoLines); ++i)
			slashed[i] = '\\';
		slashed[i] = 0;

		strcat(slashed, cechoText);

		HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
		HU_SetCEchoDuration(6);
		HU_DoCEcho(slashed);
		return true;
	}
	return false;
}

// Used when loading gamedata to make sure all unlocks are synched with conditions
void M_SilentUpdateUnlockablesAndEmblems(void)
{
	INT32 i;
	boolean checkAgain = false;

	// Just in case they aren't to sync
	M_CheckUnlockConditions();
	M_CheckLevelEmblems();

	// Go through extra emblems
	for (i = 0; i < numextraemblems; ++i)
	{
		if (extraemblems[i].collected || !extraemblems[i].conditionset)
			continue;
		if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
			checkAgain = true;
	}

	// check again if extra emblems unlocked, blah blah, etc
	if (checkAgain)
		M_CheckUnlockConditions();

	// Go through unlockables
	for (i = 0; i < MAXUNLOCKABLES; ++i)
	{
		if (unlockables[i].unlocked || !unlockables[i].conditionset)
			continue;
		unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
	}
}

// Emblem unlocking shit
UINT8 M_CheckLevelEmblems(void)
{
	INT32 i;
	INT32 valToReach;
	INT16 levelnum;
	UINT8 res;
	UINT8 somethingUnlocked = 0;

	// Update Score, Time, Rings emblems
	for (i = 0; i < numemblems; ++i)
	{
		if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].collected)
			continue;

		levelnum = emblemlocations[i].level;
		valToReach = emblemlocations[i].var;

		switch (emblemlocations[i].type)
		{
			/*case ET_SCORE: // Requires score on map >= x
				res = (G_GetBestScore(levelnum) >= (unsigned)valToReach);
				break;*/
			case ET_TIME: // Requires time on map <= x
				res = (G_GetBestTime(levelnum) <= (unsigned)valToReach);
				break;
			/*case ET_RINGS: // Requires rings on map >= x
				res = (G_GetBestRings(levelnum) >= valToReach);
				break;
			case ET_NGRADE: // Requires NiGHTS grade on map >= x
				res = (G_GetBestNightsGrade(levelnum, 0) >= valToReach);
				break;
			case ET_NTIME: // Requires NiGHTS time on map <= x
				res = (G_GetBestNightsTime(levelnum, 0) <= (unsigned)valToReach);
				break;*/
			default: // unreachable but shuts the compiler up.
				continue;
		}

		emblemlocations[i].collected = res;
		if (res)
			++somethingUnlocked;
	}
	return somethingUnlocked;
}

// -------------------
// Quick unlock checks
// -------------------
UINT8 M_AnySecretUnlocked(void)
{
	INT32 i;
	for (i = 0; i < MAXUNLOCKABLES; ++i)
	{
		if (!unlockables[i].nocecho && unlockables[i].unlocked)
			return true;
	}
	return false;
}

UINT8 M_SecretUnlocked(INT32 type)
{
	INT32 i;
	for (i = 0; i < MAXUNLOCKABLES; ++i)
	{
		if (unlockables[i].type == type && unlockables[i].unlocked)
			return true;
	}
	return false;
}

UINT8 M_MapLocked(INT32 mapnum)
{
	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
		return false;
	if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked)
		return true;
	return false;
}

INT32 M_CountEmblems(void)
{
	INT32 found = 0, i;
	for (i = 0; i < numemblems; ++i)
	{
		if (emblemlocations[i].collected)
			found++;
	}
	for (i = 0; i < numextraemblems; ++i)
	{
		if (extraemblems[i].collected)
			found++;
	}
	return found;
}

// --------------------------------------
// Quick functions for calculating things
// --------------------------------------

// Theoretically faster than using M_CountEmblems()
// Stops when it reaches the target number of emblems.
UINT8 M_GotEnoughEmblems(INT32 number)
{
	INT32 i, gottenemblems = 0;
	for (i = 0; i < numemblems; ++i)
	{
		if (emblemlocations[i].collected)
			if (++gottenemblems >= number) return true;
	}
	for (i = 0; i < numextraemblems; ++i)
	{
		if (extraemblems[i].collected)
			if (++gottenemblems >= number) return true;
	}
	return false;
}

/*UINT8 M_GotHighEnoughScore(INT32 tscore)
{
	INT32 mscore = 0;
	INT32 i;

	for (i = 0; i < NUMMAPS; ++i)
	{
		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
			continue;
		if (!mainrecords[i])
			continue;

		if ((mscore += mainrecords[i]->score) > tscore)
			return true;
	}
	return false;
}*/

UINT8 M_GotLowEnoughTime(INT32 tictime)
{
	INT32 curtics = 0;
	INT32 i;

	for (i = 0; i < NUMMAPS; ++i)
	{
		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
			continue;

		if (!mainrecords[i] || !mainrecords[i]->time)
			return false;
		else if ((curtics += mainrecords[i]->time) > tictime)
			return false;
	}
	return true;
}

/*UINT8 M_GotHighEnoughRings(INT32 trings)
{
	INT32 mrings = 0;
	INT32 i;

	for (i = 0; i < NUMMAPS; ++i)
	{
		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
			continue;
		if (!mainrecords[i])
			continue;

		if ((mrings += mainrecords[i]->rings) > trings)
			return true;
	}
	return false;
}*/

// ----------------
// Misc Emblem shit
// ----------------

// Returns pointer to an emblem if an emblem exists for that level.
// Pass -1 mapnum to continue from last emblem.
// NULL if not found.
// note that this goes in reverse!!
emblem_t *M_GetLevelEmblems(INT32 mapnum)
{
	static INT32 map = -1;
	static INT32 i = -1;

	if (mapnum > 0)
	{
		map = mapnum;
		i = numemblems;
	}

	while (--i >= 0)
	{
		if (emblemlocations[i].level == map)
			return &emblemlocations[i];
	}
	return NULL;
}

skincolors_t M_GetEmblemColor(emblem_t *em)
{
	if (!em || em->color >= MAXSKINCOLORS)
		return SKINCOLOR_NONE;
	return em->color;
}

const char *M_GetEmblemPatch(emblem_t *em)
{
	static char pnamebuf[7] = "GOTITn";

	I_Assert(em->sprite >= 'A' && em->sprite <= 'Z');
	pnamebuf[5] = em->sprite;
	return pnamebuf;
}

skincolors_t M_GetExtraEmblemColor(extraemblem_t *em)
{
	if (!em || em->color >= MAXSKINCOLORS)
		return SKINCOLOR_NONE;
	return em->color;
}

const char *M_GetExtraEmblemPatch(extraemblem_t *em)
{
	static char pnamebuf[7] = "GOTITn";

	I_Assert(em->sprite >= 'A' && em->sprite <= 'Z');
	pnamebuf[5] = em->sprite;
	return pnamebuf;
}
