mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Made sure there is more than enough headroom for our current purposes. It should be easy to double again if necessary now that the datatypes have been increased... but that would be obscene at this point - 1024 Unlockables and 1024 Conditions (these were always tied together in slots) - 2048 emblems (Medals + nonmedals). If we ship with ~250 maps this permits 8 Medals per map - which is higher than we intend right now but could easily fill out in patches
4341 lines
110 KiB
C
4341 lines
110 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 1999-2020 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 deh_soc.c
|
|
/// \brief Load SOC file and change tables and text
|
|
|
|
#include "doomdef.h"
|
|
#include "d_main.h" // for srb2home
|
|
#include "g_game.h"
|
|
#include "sounds.h"
|
|
#include "info.h"
|
|
#include "d_think.h"
|
|
#include "m_argv.h"
|
|
#include "z_zone.h"
|
|
#include "w_wad.h"
|
|
#include "y_inter.h"
|
|
#include "k_menu.h"
|
|
#include "m_misc.h"
|
|
#include "f_finale.h"
|
|
#include "st_stuff.h"
|
|
#include "i_system.h"
|
|
#include "p_setup.h"
|
|
#include "r_data.h"
|
|
#include "r_textures.h"
|
|
#include "r_draw.h"
|
|
#include "r_picformats.h"
|
|
#include "r_things.h" // R_Char2Frame
|
|
#include "r_sky.h"
|
|
#include "fastcmp.h"
|
|
#include "lua_script.h" // Reluctantly included for LUA_EvalMath
|
|
#include "d_clisrv.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_light.h"
|
|
#endif
|
|
|
|
#include "m_cond.h"
|
|
|
|
#include "dehacked.h"
|
|
#include "deh_soc.h"
|
|
#include "deh_lua.h" // included due to some LUA_SetLuaAction hack smh
|
|
#include "deh_tables.h"
|
|
|
|
// SRB2Kart
|
|
#include "filesrch.h" // refreshdirmenu
|
|
#include "k_follower.h"
|
|
#include "doomstat.h" // MAXMUSNAMES
|
|
|
|
// Loops through every constant and operation in word and performs its calculations, returning the final value.
|
|
fixed_t get_number(const char *word)
|
|
{
|
|
return LUA_EvalMath(word);
|
|
|
|
/*// DESPERATELY NEEDED: Order of operations support! :x
|
|
fixed_t i = find_const(&word);
|
|
INT32 o;
|
|
while(*word) {
|
|
o = operation_pad(&word);
|
|
if (o != -1)
|
|
i = OPERATIONS[o].v(i,find_const(&word));
|
|
else
|
|
break;
|
|
}
|
|
return i;*/
|
|
}
|
|
|
|
#define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0)
|
|
|
|
/* ======================================================================== */
|
|
// Load a dehacked file format
|
|
/* ======================================================================== */
|
|
/* a sample to see
|
|
Thing 1 (Player) { // MT_PLAYER
|
|
INT32 doomednum; ID # = 3232 -1, // doomednum
|
|
INT32 spawnstate; Initial frame = 32 "PLAY", // spawnstate
|
|
INT32 spawnhealth; Hit points = 3232 100, // spawnhealth
|
|
INT32 seestate; First moving frame = 32 "PLAY_RUN1", // seestate
|
|
INT32 seesound; Alert sound = 32 sfx_None, // seesound
|
|
INT32 reactiontime; Reaction time = 3232 0, // reactiontime
|
|
INT32 attacksound; Attack sound = 32 sfx_None, // attacksound
|
|
INT32 painstate; Injury frame = 32 "PLAY_PAIN", // painstate
|
|
INT32 painchance; Pain chance = 3232 255, // painchance
|
|
INT32 painsound; Pain sound = 32 sfx_plpain, // painsound
|
|
INT32 meleestate; Close attack frame = 32 "NULL", // meleestate
|
|
INT32 missilestate; Far attack frame = 32 "PLAY_ATK1", // missilestate
|
|
INT32 deathstate; Death frame = 32 "PLAY_DIE1", // deathstate
|
|
INT32 xdeathstate; Exploding frame = 32 "PLAY_XDIE1", // xdeathstate
|
|
INT32 deathsound; Death sound = 32 sfx_pldeth, // deathsound
|
|
INT32 speed; Speed = 3232 0, // speed
|
|
INT32 radius; Width = 211812352 16*FRACUNIT, // radius
|
|
INT32 height; Height = 211812352 56*FRACUNIT, // height
|
|
INT32 dispoffset; DispOffset = 0 0, // dispoffset
|
|
INT32 mass; Mass = 3232 100, // mass
|
|
INT32 damage; Missile damage = 3232 0, // damage
|
|
INT32 activesound; Action sound = 32 sfx_None, // activesound
|
|
INT32 flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH,
|
|
INT32 raisestate; Respawn frame = 32 S_NULL // raisestate
|
|
}, */
|
|
|
|
#ifdef HWRENDER
|
|
static INT32 searchvalue(const char *s)
|
|
{
|
|
while (s[0] != '=' && s[0])
|
|
s++;
|
|
if (s[0] == '=')
|
|
return atoi(&s[1]);
|
|
else
|
|
{
|
|
deh_warning("No value found");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static float searchfvalue(const char *s)
|
|
{
|
|
while (s[0] != '=' && s[0])
|
|
s++;
|
|
if (s[0] == '=')
|
|
return (float)atof(&s[1]);
|
|
else
|
|
{
|
|
deh_warning("No value found");
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// These are for clearing all of various things
|
|
void clear_emblems(void)
|
|
{
|
|
INT32 i;
|
|
|
|
for (i = 0; i < MAXEMBLEMS; ++i)
|
|
{
|
|
Z_Free(emblemlocations[i].level);
|
|
emblemlocations[i].level = NULL;
|
|
|
|
Z_Free(emblemlocations[i].stringVar);
|
|
emblemlocations[i].stringVar = NULL;
|
|
|
|
Z_Free(emblemlocations[i].stringVar2);
|
|
emblemlocations[i].stringVar2 = NULL;
|
|
}
|
|
|
|
memset(&emblemlocations, 0, sizeof(emblemlocations));
|
|
numemblems = 0;
|
|
}
|
|
|
|
void clear_unlockables(void)
|
|
{
|
|
INT32 i;
|
|
|
|
for (i = 0; i < MAXUNLOCKABLES; ++i)
|
|
{
|
|
Z_Free(unlockables[i].stringVar);
|
|
unlockables[i].stringVar = NULL;
|
|
|
|
Z_Free(unlockables[i].icon);
|
|
unlockables[i].icon = NULL;
|
|
}
|
|
|
|
memset(&unlockables, 0, sizeof(unlockables));
|
|
}
|
|
|
|
void clear_conditionsets(void)
|
|
{
|
|
UINT16 i;
|
|
for (i = 0; i < MAXCONDITIONSETS; ++i)
|
|
M_ClearConditionSet(i);
|
|
}
|
|
|
|
void clear_levels(void)
|
|
{
|
|
// This is potentially dangerous but if we're resetting these headers,
|
|
// we may as well try to save some memory, right?
|
|
while (nummapheaders > 0)
|
|
{
|
|
nummapheaders--;
|
|
|
|
if (!mapheaderinfo[nummapheaders])
|
|
continue;
|
|
|
|
// Custom map header info
|
|
// (no need to set num to 0, we're freeing the entire header shortly)
|
|
Z_Free(mapheaderinfo[nummapheaders]->customopts);
|
|
|
|
P_DeleteHeaderFollowers(nummapheaders);
|
|
|
|
Patch_Free(mapheaderinfo[nummapheaders]->thumbnailPic);
|
|
Patch_Free(mapheaderinfo[nummapheaders]->minimapPic);
|
|
|
|
Z_Free(mapheaderinfo[nummapheaders]->lumpname);
|
|
|
|
Z_Free(mapheaderinfo[nummapheaders]);
|
|
mapheaderinfo[nummapheaders] = NULL;
|
|
}
|
|
|
|
// Clear out the cache
|
|
{
|
|
cupheader_t *cup = kartcupheaders;
|
|
UINT8 i;
|
|
|
|
while (cup)
|
|
{
|
|
for (i = 0; i < CUPCACHE_MAX; i++)
|
|
{
|
|
cup->cachedlevels[i] = NEXTMAP_INVALID;
|
|
}
|
|
cup = cup->next;
|
|
}
|
|
}
|
|
|
|
// Exit the current gamemap as a safeguard
|
|
if (Playing())
|
|
COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed
|
|
}
|
|
|
|
// TODO: Figure out how to do undolines for this....
|
|
// TODO: Warnings for running out of freeslots
|
|
void readfreeslots(MYFILE *f)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word,*type;
|
|
char *tmp;
|
|
int i;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
type = strtok(s, "_");
|
|
if (type)
|
|
strupr(type);
|
|
else
|
|
break;
|
|
|
|
word = strtok(NULL, "\n");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
// TODO: Check for existing freeslot mobjs/states/etc. and make errors.
|
|
// TODO: Out-of-slots warnings/errors.
|
|
// TODO: Name too long (truncated) warnings.
|
|
if (fastcmp(type, "SFX"))
|
|
{
|
|
CONS_Printf("Sound sfx_%s allocated.\n",word);
|
|
S_AddSoundFx(word, false, 0, false);
|
|
}
|
|
else if (fastcmp(type, "SPR"))
|
|
{
|
|
for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
|
|
{
|
|
if (used_spr[(i-SPR_FIRSTFREESLOT)/8] & (1<<(i%8)))
|
|
{
|
|
if (!sprnames[i][4] && memcmp(sprnames[i],word,4)==0)
|
|
sprnames[i][4] = (char)f->wad;
|
|
continue; // Already allocated, next.
|
|
}
|
|
// Found a free slot!
|
|
strncpy(sprnames[i],word,4);
|
|
//sprnames[i][4] = 0;
|
|
CONS_Printf("Sprite SPR_%s allocated.\n",word);
|
|
used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now.
|
|
break;
|
|
}
|
|
if (i > SPR_LASTFREESLOT)
|
|
I_Error("Out of Sprite Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
|
|
}
|
|
else if (fastcmp(type, "S"))
|
|
{
|
|
for (i = 0; i < NUMSTATEFREESLOTS; i++)
|
|
if (!FREE_STATES[i]) {
|
|
CONS_Printf("State S_%s allocated.\n",word);
|
|
FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
|
|
strcpy(FREE_STATES[i],word);
|
|
freeslotusage[0][0]++;
|
|
break;
|
|
}
|
|
if (i == NUMSTATEFREESLOTS)
|
|
I_Error("Out of State Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
|
|
}
|
|
else if (fastcmp(type, "MT"))
|
|
{
|
|
for (i = 0; i < NUMMOBJFREESLOTS; i++)
|
|
if (!FREE_MOBJS[i]) {
|
|
CONS_Printf("MobjType MT_%s allocated.\n",word);
|
|
FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
|
|
strcpy(FREE_MOBJS[i],word);
|
|
freeslotusage[1][0]++;
|
|
break;
|
|
}
|
|
if (i == NUMMOBJFREESLOTS)
|
|
I_Error("Out of Mobj Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
|
|
}
|
|
else if (fastcmp(type, "SKINCOLOR"))
|
|
{
|
|
for (i = 0; i < NUMCOLORFREESLOTS; i++)
|
|
if (!FREE_SKINCOLORS[i]) {
|
|
CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word);
|
|
FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
|
|
strcpy(FREE_SKINCOLORS[i],word);
|
|
M_AddMenuColor(numskincolors++);
|
|
break;
|
|
}
|
|
if (i == NUMCOLORFREESLOTS)
|
|
I_Error("Out of Skincolor Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
}
|
|
else if (fastcmp(type, "SPR2"))
|
|
{
|
|
// Search if we already have an SPR2 by that name...
|
|
for (i = SPR2_FIRSTFREESLOT; i < (int)free_spr2; i++)
|
|
if (memcmp(spr2names[i],word,4) == 0)
|
|
break;
|
|
// We found it? (Two mods using the same SPR2 name?) Then don't allocate another one.
|
|
if (i < (int)free_spr2)
|
|
continue;
|
|
// Copy in the spr2 name and increment free_spr2.
|
|
if (free_spr2 < NUMPLAYERSPRITES) {
|
|
strncpy(spr2names[free_spr2],word,4);
|
|
spr2defaults[free_spr2] = 0;
|
|
spr2names[free_spr2++][4] = 0;
|
|
} else
|
|
I_Error("Out of SPR2 Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
}
|
|
else if (fastcmp(type, "TOL"))
|
|
{
|
|
// Search if we already have a typeoflevel by that name...
|
|
for (i = 0; TYPEOFLEVEL[i].name; i++)
|
|
if (fastcmp(word, TYPEOFLEVEL[i].name))
|
|
break;
|
|
|
|
// We found it? Then don't allocate another one.
|
|
if (TYPEOFLEVEL[i].name)
|
|
continue;
|
|
|
|
// We don't, so freeslot it.
|
|
if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags.
|
|
I_Error("Out of Typeoflevel Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
else
|
|
{
|
|
G_AddTOL(lastcustomtol, word);
|
|
lastcustomtol <<= 1;
|
|
}
|
|
}
|
|
else if (fastcmp(type, "PRECIP"))
|
|
{
|
|
// Search if we already have a PRECIP by that name...
|
|
for (i = PRECIP_FIRSTFREESLOT; i < (int)precip_freeslot; i++)
|
|
if (fastcmp(word, precipprops[i].name))
|
|
break;
|
|
|
|
// We found it? Then don't allocate another one.
|
|
if (i < (int)precip_freeslot)
|
|
continue;
|
|
|
|
// We don't, so allocate a new one.
|
|
if (precip_freeslot < MAXPRECIP)
|
|
{
|
|
precipprops[i].name = Z_StrDup(word);
|
|
precip_freeslot++;
|
|
} else
|
|
I_Error("Out of Precipitation Freeslots while allocating \"%s\"\nLoad less addons to fix this.", word);
|
|
}
|
|
else
|
|
deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readthing(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word, *word2;
|
|
char *tmp;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
strupr(word2);
|
|
else
|
|
break;
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
|
|
if (fastcmp(word, "MAPTHINGNUM") || fastcmp(word, "DOOMEDNUM"))
|
|
{
|
|
mobjinfo[num].doomednum = (INT32)atoi(word2);
|
|
}
|
|
else if (fastcmp(word, "SPAWNSTATE"))
|
|
{
|
|
mobjinfo[num].spawnstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SPAWNHEALTH"))
|
|
{
|
|
mobjinfo[num].spawnhealth = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SEESTATE"))
|
|
{
|
|
mobjinfo[num].seestate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SEESOUND"))
|
|
{
|
|
mobjinfo[num].seesound = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "REACTIONTIME"))
|
|
{
|
|
mobjinfo[num].reactiontime = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ATTACKSOUND"))
|
|
{
|
|
mobjinfo[num].attacksound = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "PAINSTATE"))
|
|
{
|
|
mobjinfo[num].painstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "PAINCHANCE"))
|
|
{
|
|
mobjinfo[num].painchance = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "PAINSOUND"))
|
|
{
|
|
mobjinfo[num].painsound = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "MELEESTATE"))
|
|
{
|
|
mobjinfo[num].meleestate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "MISSILESTATE"))
|
|
{
|
|
mobjinfo[num].missilestate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "DEATHSTATE"))
|
|
{
|
|
mobjinfo[num].deathstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "DEATHSOUND"))
|
|
{
|
|
mobjinfo[num].deathsound = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "XDEATHSTATE"))
|
|
{
|
|
mobjinfo[num].xdeathstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SPEED"))
|
|
{
|
|
mobjinfo[num].speed = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "RADIUS"))
|
|
{
|
|
mobjinfo[num].radius = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "HEIGHT"))
|
|
{
|
|
mobjinfo[num].height = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "DISPOFFSET"))
|
|
{
|
|
mobjinfo[num].dispoffset = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "MASS"))
|
|
{
|
|
mobjinfo[num].mass = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "DAMAGE"))
|
|
{
|
|
mobjinfo[num].damage = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ACTIVESOUND"))
|
|
{
|
|
mobjinfo[num].activesound = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "FLAGS"))
|
|
{
|
|
mobjinfo[num].flags = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "RAISESTATE"))
|
|
{
|
|
mobjinfo[num].raisestate = get_number(word2);
|
|
}
|
|
else
|
|
deh_warning("Thing %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readskincolor(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *word2;
|
|
char *tmp;
|
|
|
|
Color_cons_t[num].value = num;
|
|
|
|
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;
|
|
|
|
if (fastcmp(word, "NAME"))
|
|
{
|
|
size_t namesize = sizeof(skincolors[num].name);
|
|
char truncword[namesize];
|
|
UINT16 dupecheck;
|
|
|
|
deh_strlcpy(truncword, word2, namesize, va("Skincolor %d: name", num)); // truncate here to check for dupes
|
|
dupecheck = R_GetColorByName(truncword);
|
|
if (truncword[0] != '\0' && (!stricmp(truncword, skincolors[SKINCOLOR_NONE].name) || (dupecheck && dupecheck != num)))
|
|
{
|
|
size_t lastchar = strlen(truncword);
|
|
char oldword[lastchar+1];
|
|
char dupenum = '1';
|
|
|
|
strlcpy(oldword, truncword, lastchar+1);
|
|
lastchar--;
|
|
if (lastchar == namesize-2) // exactly max length, replace last character with 0
|
|
truncword[lastchar] = '0';
|
|
else // append 0
|
|
{
|
|
strcat(truncword, "0");
|
|
lastchar++;
|
|
}
|
|
|
|
while (R_GetColorByName(truncword))
|
|
{
|
|
truncword[lastchar] = dupenum;
|
|
if (dupenum == '9')
|
|
dupenum = 'A';
|
|
else if (dupenum == 'Z') // give up :?
|
|
break;
|
|
else
|
|
dupenum++;
|
|
}
|
|
|
|
deh_warning("Skincolor %d: name %s is a duplicate of another skincolor's name - renamed to %s", num, oldword, truncword);
|
|
}
|
|
|
|
strlcpy(skincolors[num].name, truncword, namesize); // already truncated
|
|
}
|
|
else if (fastcmp(word, "RAMP"))
|
|
{
|
|
UINT8 i;
|
|
tmp = strtok(word2,",");
|
|
for (i = 0; i < COLORRAMPSIZE; i++) {
|
|
skincolors[num].ramp[i] = (UINT8)get_number(tmp);
|
|
if ((tmp = strtok(NULL,",")) == NULL)
|
|
break;
|
|
}
|
|
skincolor_modified[num] = true;
|
|
}
|
|
else if (fastcmp(word, "INVCOLOR"))
|
|
{
|
|
UINT16 v = (UINT16)get_number(word2);
|
|
if (v < numskincolors)
|
|
skincolors[num].invcolor = v;
|
|
else
|
|
skincolors[num].invcolor = SKINCOLOR_GREEN;
|
|
}
|
|
else if (fastcmp(word, "INVSHADE"))
|
|
{
|
|
skincolors[num].invshade = get_number(word2)%COLORRAMPSIZE;
|
|
}
|
|
else if (fastcmp(word, "CHATCOLOR"))
|
|
{
|
|
skincolors[num].chatcolor = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ACCESSIBLE"))
|
|
{
|
|
if (num > FIRSTSUPERCOLOR)
|
|
skincolors[num].accessible = (boolean)(atoi(word2) || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else
|
|
deh_warning("Skincolor %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
#ifdef HWRENDER
|
|
void readlight(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *tmp;
|
|
INT32 value;
|
|
float fvalue;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
fvalue = searchfvalue(s);
|
|
value = searchvalue(s);
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
if (fastcmp(word, "TYPE"))
|
|
{
|
|
lspr[num].type = (UINT16)value;
|
|
}
|
|
else if (fastcmp(word, "OFFSETX"))
|
|
{
|
|
lspr[num].light_xoffset = fvalue;
|
|
}
|
|
else if (fastcmp(word, "OFFSETY"))
|
|
{
|
|
lspr[num].light_yoffset = fvalue;
|
|
}
|
|
else if (fastcmp(word, "CORONACOLOR"))
|
|
{
|
|
lspr[num].corona_color = value;
|
|
}
|
|
else if (fastcmp(word, "CORONARADIUS"))
|
|
{
|
|
lspr[num].corona_radius = fvalue;
|
|
}
|
|
else if (fastcmp(word, "DYNAMICCOLOR"))
|
|
{
|
|
lspr[num].dynamic_color = value;
|
|
}
|
|
else if (fastcmp(word, "DYNAMICRADIUS"))
|
|
{
|
|
lspr[num].dynamic_radius = fvalue;
|
|
|
|
/// \note Update the sqrradius! unnecessary?
|
|
lspr[num].dynamic_sqrradius = fvalue * fvalue;
|
|
}
|
|
else
|
|
deh_warning("Light %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
#endif // HWRENDER
|
|
|
|
void readsprite2(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word, *word2;
|
|
char *tmp;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
strupr(word2);
|
|
else
|
|
break;
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
|
|
if (fastcmp(word, "DEFAULT"))
|
|
spr2defaults[num] = get_number(word2);
|
|
else
|
|
deh_warning("Sprite2 %s: unknown word '%s'", spr2names[num], word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readgametype(MYFILE *f, char *gtname)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2, *word2lwr = NULL;
|
|
char *tmp;
|
|
INT32 i, j;
|
|
|
|
gametype_t *newgametype = NULL;
|
|
|
|
UINT32 newgtrules = 0;
|
|
UINT32 newgttol = 0;
|
|
INT32 newgtpointlimit = 0;
|
|
INT32 newgttimelimit = 0;
|
|
UINT8 newgtinttype = 0;
|
|
char gtconst[MAXLINELEN];
|
|
|
|
// Empty strings.
|
|
gtconst[0] = '\0';
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
{
|
|
if (!word2lwr)
|
|
word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
strcpy(word2lwr, word2);
|
|
strupr(word2);
|
|
}
|
|
else
|
|
break;
|
|
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
i = atoi(word2);
|
|
|
|
// Game type rules
|
|
if (fastcmp(word, "RULES"))
|
|
{
|
|
// GTR_
|
|
newgtrules = (UINT32)get_number(word2);
|
|
}
|
|
// Identifier
|
|
else if (fastcmp(word, "IDENTIFIER"))
|
|
{
|
|
// GT_
|
|
strncpy(gtconst, word2, MAXLINELEN);
|
|
}
|
|
// Point and time limits
|
|
else if (fastcmp(word, "DEFAULTPOINTLIMIT"))
|
|
newgtpointlimit = (INT32)i;
|
|
else if (fastcmp(word, "DEFAULTTIMELIMIT"))
|
|
newgttimelimit = (INT32)i;
|
|
// Intermission type
|
|
else if (fastcmp(word, "INTERMISSIONTYPE"))
|
|
{
|
|
// Case sensitive
|
|
newgtinttype = (int)get_number(word2lwr);
|
|
}
|
|
// Type of level
|
|
else if (fastcmp(word, "TYPEOFLEVEL"))
|
|
{
|
|
if (i) // it's just a number
|
|
newgttol = (UINT32)i;
|
|
else
|
|
{
|
|
UINT32 tol = 0;
|
|
tmp = strtok(word2,",");
|
|
do {
|
|
for (i = 0; TYPEOFLEVEL[i].name; i++)
|
|
if (fasticmp(tmp, TYPEOFLEVEL[i].name))
|
|
break;
|
|
if (!TYPEOFLEVEL[i].name)
|
|
deh_warning("readgametype %s: unknown typeoflevel flag %s\n", gtname, tmp);
|
|
tol |= TYPEOFLEVEL[i].flag;
|
|
} while((tmp = strtok(NULL,",")) != NULL);
|
|
newgttol = tol;
|
|
}
|
|
}
|
|
// The SOC probably provided gametype rules as words,
|
|
// instead of using the RULES keyword.
|
|
// Like for example "NOSPECTATORSPAWN = TRUE".
|
|
// This is completely valid, and looks better anyway.
|
|
else
|
|
{
|
|
UINT32 wordgt = 0;
|
|
for (j = 0; GAMETYPERULE_LIST[j]; j++)
|
|
if (fastcmp(word, GAMETYPERULE_LIST[j])) {
|
|
wordgt |= (1<<j);
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
newgtrules |= wordgt;
|
|
break;
|
|
}
|
|
if (!wordgt)
|
|
deh_warning("readgametype %s: unknown word '%s'", gtname, word);
|
|
}
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
// Free strings.
|
|
Z_Free(s);
|
|
if (word2lwr)
|
|
Z_Free(word2lwr);
|
|
|
|
// Ran out of gametype slots
|
|
if (numgametypes == GT_LASTFREESLOT)
|
|
{
|
|
I_Error("Out of Gametype Freeslots while allocating \"%s\"\nLoad less addons to fix this.", gtname);
|
|
}
|
|
|
|
if (gtname[0] == '\0')
|
|
{
|
|
deh_warning("Custom gametype must have a name");
|
|
return;
|
|
}
|
|
|
|
if (strlen(gtname) >= MAXGAMETYPELENGTH)
|
|
{
|
|
deh_warning("Custom gametype \"%s\"'s name must be %d long at most", gtname, MAXGAMETYPELENGTH-1);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < numgametypes; i++)
|
|
if (fastcmp(gtname, gametypes[i]->name))
|
|
break;
|
|
|
|
if (i < numgametypes)
|
|
{
|
|
deh_warning("Custom gametype \"%s\"'s name is already in use", gtname);
|
|
return;
|
|
}
|
|
|
|
// Add the new gametype
|
|
newgametype = Z_Calloc(sizeof (gametype_t), PU_STATIC, NULL);
|
|
if (!newgametype)
|
|
{
|
|
I_Error("Out of memory allocating gametype \"%s\"", gtname);
|
|
}
|
|
|
|
if (gtconst[0] == '\0')
|
|
strncpy(gtconst, gtname, MAXLINELEN);
|
|
|
|
newgametype->name = Z_StrDup((const char *)gtname);
|
|
newgametype->rules = newgtrules;
|
|
newgametype->constant = G_PrepareGametypeConstant((const char *)gtconst);
|
|
newgametype->tol = newgttol;
|
|
newgametype->intermission = newgtinttype;
|
|
newgametype->pointlimit = newgtpointlimit;
|
|
newgametype->timelimit = newgttimelimit;
|
|
|
|
gametypes[numgametypes++] = newgametype;
|
|
|
|
CONS_Printf("Added gametype %s\n", gtname);
|
|
}
|
|
|
|
void readlevelheader(MYFILE *f, char * name)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
|
|
char *word;
|
|
char *word2;
|
|
//char *word3; // Non-uppercase version of word2
|
|
|
|
char *tmp;
|
|
INT32 i;
|
|
|
|
INT32 num = G_MapNumber(name);
|
|
|
|
if (num >= nummapheaders)
|
|
{
|
|
P_AllocMapHeader((INT16)(num = nummapheaders));
|
|
}
|
|
else if (f->wad > mainwads)
|
|
{
|
|
// only mark as a major mod if it replaces an already-existing mapheaderinfo
|
|
G_SetGameModified(multiplayer, true);
|
|
}
|
|
|
|
if (mapheaderinfo[num]->lumpname == NULL)
|
|
{
|
|
mapheaderinfo[num]->lumpnamehash = quickncasehash(name, MAXMAPLUMPNAME);
|
|
|
|
// Check to see if we have any custom map record data that we could substitute in.
|
|
unloaded_mapheader_t *unloadedmap, *prev = NULL;
|
|
for (unloadedmap = unloadedmapheaders; unloadedmap; prev = unloadedmap, unloadedmap = unloadedmap->next)
|
|
{
|
|
if (unloadedmap->lumpnamehash != mapheaderinfo[num]->lumpnamehash)
|
|
continue;
|
|
|
|
if (strcasecmp(name, unloadedmap->lumpname) != 0)
|
|
continue;
|
|
|
|
// Copy in mapvisited, time, lap, etc.
|
|
unloadedmap->records.mapvisited &= MV_MAX;
|
|
M_Memcpy(&mapheaderinfo[num]->records, &unloadedmap->records, sizeof(recorddata_t));
|
|
|
|
// Reuse the zone-allocated lumpname string.
|
|
mapheaderinfo[num]->lumpname = unloadedmap->lumpname;
|
|
|
|
// Remove this entry from the chain.
|
|
if (prev)
|
|
{
|
|
prev->next = unloadedmap->next;
|
|
}
|
|
else
|
|
{
|
|
unloadedmapheaders = unloadedmap->next;
|
|
}
|
|
|
|
// Finally, free.
|
|
Z_Free(unloadedmap);
|
|
|
|
break;
|
|
}
|
|
|
|
if (mapheaderinfo[num]->lumpname == NULL)
|
|
{
|
|
// If there was no string to reuse, dup our own.
|
|
mapheaderinfo[num]->lumpname = Z_StrDup(name);
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
// Set / reset word, because some things (Lua.) move it
|
|
word = s;
|
|
|
|
// 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;
|
|
i = atoi(word2); // used for numerical settings
|
|
|
|
|
|
if (fastcmp(word, "LEVELNAME"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->lvlttl, word2,
|
|
sizeof(mapheaderinfo[num]->lvlttl), va("Level header %d: levelname", num));
|
|
continue;
|
|
}
|
|
// CHEAP HACK: move this over here for lowercase subtitles
|
|
if (fastcmp(word, "SUBTITLE"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->subttl, word2,
|
|
sizeof(mapheaderinfo[num]->subttl), va("Level header %d: subtitle", num));
|
|
continue;
|
|
}
|
|
|
|
// Lua custom options also go above, contents may be case sensitive.
|
|
if (fastncmp(word, "LUA.", 4))
|
|
{
|
|
UINT8 j;
|
|
customoption_t *modoption;
|
|
|
|
// Note: we actualy strlwr word here, so things are made a little easier for Lua
|
|
strlwr(word);
|
|
word += 4; // move past "lua."
|
|
|
|
// ... and do a simple name sanity check; the name must start with a letter
|
|
if (*word < 'a' || *word > 'z')
|
|
{
|
|
deh_warning("Level header %d: invalid custom option name \"%s\"", num, word);
|
|
continue;
|
|
}
|
|
|
|
// Sanity limit of 128 params
|
|
if (mapheaderinfo[num]->numCustomOptions == 128)
|
|
{
|
|
deh_warning("Level header %d: too many custom parameters", num);
|
|
continue;
|
|
}
|
|
j = mapheaderinfo[num]->numCustomOptions++;
|
|
|
|
mapheaderinfo[num]->customopts =
|
|
Z_Realloc(mapheaderinfo[num]->customopts,
|
|
sizeof(customoption_t) * mapheaderinfo[num]->numCustomOptions, PU_STATIC, NULL);
|
|
|
|
// Newly allocated
|
|
modoption = &mapheaderinfo[num]->customopts[j];
|
|
|
|
strncpy(modoption->option, word, 31);
|
|
modoption->option[31] = '\0';
|
|
strncpy(modoption->value, word2, 255);
|
|
modoption->value[255] = '\0';
|
|
continue;
|
|
}
|
|
|
|
// Now go to uppercase
|
|
strupr(word2);
|
|
|
|
// List of followers that are be freed in this map
|
|
if (fastcmp(word, "FOLLOWERS"))
|
|
{
|
|
if (fastcmp(word2, "NONE"))
|
|
P_DeleteHeaderFollowers(num);
|
|
else if (fastcmp(word2, "DEFAULT"))
|
|
P_SetDefaultHeaderFollowers(num);
|
|
else
|
|
{
|
|
INT16 tmpfollowers[MAXHEADERFOLLOWERS];
|
|
mapheaderinfo[num]->numFollowers = 0;
|
|
tmp = strtok(word2,",");
|
|
// get up to the first MAXHEADERFOLLOWERS followers
|
|
do {
|
|
if (mapheaderinfo[num]->numFollowers == MAXHEADERFOLLOWERS) // never going to get above that number
|
|
{
|
|
deh_warning("Level header %d: too many followers\n", num);
|
|
break;
|
|
}
|
|
|
|
{
|
|
i = K_FollowerAvailable(tmp);
|
|
if (i == -1)
|
|
{
|
|
deh_warning("Level header %d: unknown follower selection %s\n", num, tmp);
|
|
continue;
|
|
}
|
|
tmpfollowers[mapheaderinfo[num]->numFollowers] = i;
|
|
mapheaderinfo[num]->numFollowers++;
|
|
}
|
|
} while ((tmp = strtok(NULL,",")) != NULL);
|
|
|
|
if (mapheaderinfo[num]->numFollowers)
|
|
{
|
|
size_t newsize = sizeof(INT16) * mapheaderinfo[num]->numFollowers;
|
|
mapheaderinfo[num]->followers = Z_Realloc(mapheaderinfo[num]->followers, newsize, PU_STATIC, NULL);
|
|
// now we add them to the list!
|
|
M_Memcpy(mapheaderinfo[num]->followers, tmpfollowers, newsize);
|
|
}
|
|
else
|
|
deh_warning("Level header %d: no valid follower types found\n", num);
|
|
}
|
|
}
|
|
|
|
// Strings that can be truncated
|
|
else if (fastcmp(word, "ZONETITLE"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->zonttl, word2,
|
|
sizeof(mapheaderinfo[num]->zonttl), va("Level header %d: zonetitle", num));
|
|
}
|
|
else if (fastcmp(word, "RELEVANTSKIN"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->relevantskin, word2,
|
|
sizeof(mapheaderinfo[num]->relevantskin), va("Level header %d: relevantskin", num));
|
|
}
|
|
else if (fastcmp(word, "SCRIPTNAME"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->scriptname, word2,
|
|
sizeof(mapheaderinfo[num]->scriptname), va("Level header %d: scriptname", num));
|
|
}
|
|
else if (fastcmp(word, "RUNSOC"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->runsoc, word2,
|
|
sizeof(mapheaderinfo[num]->runsoc), va("Level header %d: runsoc", num));
|
|
}
|
|
else if (fastcmp(word, "ACT"))
|
|
{
|
|
if (i >= 0 && i <= 99) // 0 for no act number
|
|
mapheaderinfo[num]->actnum = (UINT8)i;
|
|
else
|
|
deh_warning("Level header %d: invalid act number %d", num, i);
|
|
}
|
|
else if (fastcmp(word, "TYPEOFLEVEL"))
|
|
{
|
|
if (i || isdigit(word2[0])) // it's just a number
|
|
mapheaderinfo[num]->typeoflevel = (UINT32)i;
|
|
else
|
|
{
|
|
UINT32 tol = 0;
|
|
tmp = strtok(word2,",");
|
|
do {
|
|
for (i = 0; TYPEOFLEVEL[i].name; i++)
|
|
if (fastcmp(tmp, TYPEOFLEVEL[i].name))
|
|
break;
|
|
if (!TYPEOFLEVEL[i].name)
|
|
deh_warning("Level header %d: unknown typeoflevel flag %s\n", num, tmp);
|
|
tol |= TYPEOFLEVEL[i].flag;
|
|
} while((tmp = strtok(NULL,",")) != NULL);
|
|
mapheaderinfo[num]->typeoflevel = tol;
|
|
}
|
|
}
|
|
else if (fastcmp(word, "KEYWORDS"))
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->keywords, word2,
|
|
sizeof(mapheaderinfo[num]->keywords), va("Level header %d: keywords", num));
|
|
}
|
|
else if (fastcmp(word, "MUSIC"))
|
|
{
|
|
if (fastcmp(word2, "NONE"))
|
|
{
|
|
mapheaderinfo[num]->musname[0][0] = 0; // becomes empty string
|
|
mapheaderinfo[num]->musname_size = 0;
|
|
}
|
|
else
|
|
{
|
|
UINT8 j = 0; // i was declared elsewhere
|
|
tmp = strtok(word2, ",");
|
|
do {
|
|
if (j >= MAXMUSNAMES)
|
|
break;
|
|
deh_strlcpy(mapheaderinfo[num]->musname[j], tmp,
|
|
sizeof(mapheaderinfo[num]->musname[j]), va("Level header %d: music", num));
|
|
j++;
|
|
} while ((tmp = strtok(NULL,",")) != NULL);
|
|
|
|
if (tmp != NULL)
|
|
deh_warning("Level header %d: additional music slots past %d discarded", num, MAXMUSNAMES);
|
|
mapheaderinfo[num]->musname_size = j;
|
|
}
|
|
}
|
|
else if (fastcmp(word, "ASSOCIATEDMUSIC"))
|
|
{
|
|
if (fastcmp(word2, "NONE"))
|
|
{
|
|
mapheaderinfo[num]->associatedmus[0][0] = 0; // becomes empty string
|
|
mapheaderinfo[num]->associatedmus_size = 0;
|
|
}
|
|
else
|
|
{
|
|
UINT8 j = 0; // i was declared elsewhere
|
|
tmp = strtok(word2, ",");
|
|
do {
|
|
if (j >= MAXMUSNAMES)
|
|
break;
|
|
deh_strlcpy(mapheaderinfo[num]->associatedmus[j], tmp,
|
|
sizeof(mapheaderinfo[num]->associatedmus[j]), va("Level header %d: associated music", num));
|
|
j++;
|
|
} while ((tmp = strtok(NULL,",")) != NULL);
|
|
|
|
if (tmp != NULL)
|
|
deh_warning("Level header %d: additional associated music slots past %d discarded", num, MAXMUSNAMES);
|
|
mapheaderinfo[num]->associatedmus_size = j;
|
|
}
|
|
}
|
|
else if (fastcmp(word, "POSITIONMUSIC"))
|
|
{
|
|
if (fastcmp(word2, "NONE"))
|
|
{
|
|
mapheaderinfo[num]->positionmus[0] = 0; // becomes empty string
|
|
}
|
|
else
|
|
{
|
|
deh_strlcpy(mapheaderinfo[num]->positionmus, word2,
|
|
sizeof(mapheaderinfo[num]->positionmus), va("Level header %d: POSITION!! music", num));
|
|
}
|
|
}
|
|
else if (fastcmp(word, "MUSICTRACK"))
|
|
mapheaderinfo[num]->mustrack = ((UINT16)i - 1);
|
|
else if (fastcmp(word, "MUSICPOS"))
|
|
mapheaderinfo[num]->muspos = (UINT32)get_number(word2);
|
|
else if (fastcmp(word, "WEATHER"))
|
|
mapheaderinfo[num]->weather = get_precip(word2);
|
|
else if (fastcmp(word, "SKYTEXTURE"))
|
|
deh_strlcpy(mapheaderinfo[num]->skytexture, word2,
|
|
sizeof(mapheaderinfo[num]->skytexture), va("Level header %d: sky texture", num));
|
|
else if (fastcmp(word, "PRECUTSCENENUM"))
|
|
mapheaderinfo[num]->precutscenenum = (UINT8)i;
|
|
else if (fastcmp(word, "CUTSCENENUM"))
|
|
mapheaderinfo[num]->cutscenenum = (UINT8)i;
|
|
else if (fastcmp(word, "PALETTE"))
|
|
mapheaderinfo[num]->palette = (UINT16)i;
|
|
else if (fastcmp(word, "ENCOREPAL"))
|
|
mapheaderinfo[num]->encorepal = (UINT16)i;
|
|
else if (fastcmp(word, "NUMLAPS"))
|
|
mapheaderinfo[num]->numlaps = (UINT8)i;
|
|
else if (fastcmp(word, "SKYBOXSCALE"))
|
|
mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i;
|
|
else if (fastcmp(word, "SKYBOXSCALEX"))
|
|
mapheaderinfo[num]->skybox_scalex = (INT16)i;
|
|
else if (fastcmp(word, "SKYBOXSCALEY"))
|
|
mapheaderinfo[num]->skybox_scaley = (INT16)i;
|
|
else if (fastcmp(word, "SKYBOXSCALEZ"))
|
|
mapheaderinfo[num]->skybox_scalez = (INT16)i;
|
|
else if (fastcmp(word, "LEVELFLAGS"))
|
|
mapheaderinfo[num]->levelflags = get_number(word2);
|
|
else if (fastcmp(word, "MENUFLAGS"))
|
|
mapheaderinfo[num]->menuflags = get_number(word2);
|
|
// SRB2Kart
|
|
else if (fastcmp(word, "MOBJSCALE"))
|
|
mapheaderinfo[num]->mobj_scale = get_number(word2);
|
|
else if (fastcmp(word, "DEFAULTWAYPOINTRADIUS"))
|
|
mapheaderinfo[num]->default_waypoint_radius = get_number(word2);
|
|
else if (fastcmp(word, "LIGHTCONTRAST"))
|
|
{
|
|
mapheaderinfo[num]->light_contrast = (UINT8)i;
|
|
}
|
|
else if (fastcmp(word, "LIGHTANGLE"))
|
|
{
|
|
if (fastcmp(word2, "EVEN"))
|
|
{
|
|
mapheaderinfo[num]->use_light_angle = false;
|
|
mapheaderinfo[num]->light_angle = 0;
|
|
}
|
|
else
|
|
{
|
|
mapheaderinfo[num]->use_light_angle = true;
|
|
mapheaderinfo[num]->light_angle = FixedAngle(FloatToFixed(atof(word2)));
|
|
}
|
|
}
|
|
// Individual triggers for level flags, for ease of use (and 2.0 compatibility)
|
|
else if (fastcmp(word, "SCRIPTISFILE"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->levelflags |= LF_SCRIPTISFILE;
|
|
else
|
|
mapheaderinfo[num]->levelflags &= ~LF_SCRIPTISFILE;
|
|
}
|
|
else if (fastcmp(word, "NOZONE"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->levelflags |= LF_NOZONE;
|
|
else
|
|
mapheaderinfo[num]->levelflags &= ~LF_NOZONE;
|
|
}
|
|
else if (fastcmp(word, "SECTIONRACE"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->levelflags |= LF_SECTIONRACE;
|
|
else
|
|
mapheaderinfo[num]->levelflags &= ~LF_SECTIONRACE;
|
|
}
|
|
else if (fastcmp(word, "SUBTRACTNUM"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->levelflags |= LF_SUBTRACTNUM;
|
|
else
|
|
mapheaderinfo[num]->levelflags &= ~LF_SUBTRACTNUM;
|
|
}
|
|
|
|
// Individual triggers for menu flags
|
|
else if (fastcmp(word, "HIDDEN"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->menuflags |= LF2_HIDEINMENU;
|
|
else
|
|
mapheaderinfo[num]->menuflags &= ~LF2_HIDEINMENU;
|
|
}
|
|
else if (fastcmp(word, "HIDEINSTATS"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->menuflags |= LF2_HIDEINSTATS;
|
|
else
|
|
mapheaderinfo[num]->menuflags &= ~LF2_HIDEINSTATS;
|
|
}
|
|
else if (fastcmp(word, "NOTIMEATTACK") || fastcmp(word, "NORECORDATTACK"))
|
|
{ // RECORDATTACK is an accepted alias
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->menuflags |= LF2_NOTIMEATTACK;
|
|
else
|
|
mapheaderinfo[num]->menuflags &= ~LF2_NOTIMEATTACK;
|
|
}
|
|
else if (fastcmp(word, "FINISHNEEDED"))
|
|
{
|
|
if (i || word2[0] == 'T' || word2[0] == 'Y')
|
|
mapheaderinfo[num]->menuflags |= LF2_FINISHNEEDED;
|
|
else
|
|
mapheaderinfo[num]->menuflags &= ~LF2_FINISHNEEDED;
|
|
}
|
|
else if (fastcmp(word, "GRAVITY"))
|
|
mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2));
|
|
else
|
|
deh_warning("Level header %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
|
|
{
|
|
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
INT32 i;
|
|
UINT16 usi;
|
|
UINT8 picid;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
if (fastcmp(word, "SCENETEXT"))
|
|
{
|
|
char *scenetext = NULL;
|
|
char *buffer;
|
|
const int bufferlen = 4096;
|
|
|
|
for (i = 0; i < MAXLINELEN; i++)
|
|
{
|
|
if (s[i] == '=')
|
|
{
|
|
scenetext = &s[i+2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!scenetext)
|
|
{
|
|
Z_Free(cutscenes[num]->scene[scenenum].text);
|
|
cutscenes[num]->scene[scenenum].text = NULL;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < MAXLINELEN; i++)
|
|
{
|
|
if (s[i] == '\0')
|
|
{
|
|
s[i] = '\n';
|
|
s[i+1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
buffer = Z_Malloc(4096, PU_STATIC, NULL);
|
|
strcpy(buffer, scenetext);
|
|
|
|
strcat(buffer,
|
|
myhashfgets(scenetext, bufferlen
|
|
- strlen(buffer) - 1, f));
|
|
|
|
// A cutscene overwriting another one...
|
|
Z_Free(cutscenes[num]->scene[scenenum].text);
|
|
|
|
cutscenes[num]->scene[scenenum].text = Z_StrDup(buffer);
|
|
|
|
Z_Free(buffer);
|
|
|
|
continue;
|
|
}
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
strupr(word2);
|
|
else
|
|
break;
|
|
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
i = atoi(word2);
|
|
usi = (UINT16)i;
|
|
|
|
|
|
if (fastcmp(word, "NUMBEROFPICS"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].numpics = (UINT8)i;
|
|
}
|
|
else if (fastncmp(word, "PIC", 3))
|
|
{
|
|
picid = (UINT8)atoi(word + 3);
|
|
if (picid > 8 || picid == 0)
|
|
{
|
|
deh_warning("CutSceneScene %d: unknown word '%s'", num, word);
|
|
continue;
|
|
}
|
|
--picid;
|
|
|
|
if (fastcmp(word+4, "NAME"))
|
|
{
|
|
strncpy(cutscenes[num]->scene[scenenum].picname[picid], word2, 8);
|
|
}
|
|
else if (fastcmp(word+4, "HIRES"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else if (fastcmp(word+4, "DURATION"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].picduration[picid] = usi;
|
|
}
|
|
else if (fastcmp(word+4, "XCOORD"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].xcoord[picid] = usi;
|
|
}
|
|
else if (fastcmp(word+4, "YCOORD"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].ycoord[picid] = usi;
|
|
}
|
|
else
|
|
deh_warning("CutSceneScene %d: unknown word '%s'", num, word);
|
|
}
|
|
else if (fastcmp(word, "MUSIC"))
|
|
{
|
|
strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7);
|
|
cutscenes[num]->scene[scenenum].musswitch[6] = 0;
|
|
}
|
|
else if (fastcmp(word, "MUSICTRACK"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK;
|
|
}
|
|
else if (fastcmp(word, "MUSICPOS"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].musswitchposition = (UINT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "MUSICLOOP"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else if (fastcmp(word, "TEXTXPOS"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].textxpos = usi;
|
|
}
|
|
else if (fastcmp(word, "TEXTYPOS"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].textypos = usi;
|
|
}
|
|
else if (fastcmp(word, "FADEINID"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].fadeinid = (UINT8)i;
|
|
}
|
|
else if (fastcmp(word, "FADEOUTID"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].fadeoutid = (UINT8)i;
|
|
}
|
|
else if (fastcmp(word, "FADECOLOR"))
|
|
{
|
|
cutscenes[num]->scene[scenenum].fadecolor = (UINT8)i;
|
|
}
|
|
else
|
|
deh_warning("CutSceneScene %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readcutscene(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
|
|
// Allocate memory for this cutscene if we don't yet have any
|
|
if (!cutscenes[num])
|
|
cutscenes[num] = Z_Calloc(sizeof (cutscene_t), PU_STATIC, NULL);
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " ");
|
|
if (word2)
|
|
value = atoi(word2);
|
|
else
|
|
{
|
|
deh_warning("No value for token %s", word);
|
|
continue;
|
|
}
|
|
|
|
if (fastcmp(word, "NUMSCENES"))
|
|
{
|
|
cutscenes[num]->numscenes = value;
|
|
}
|
|
else if (fastcmp(word, "SCENE"))
|
|
{
|
|
if (1 <= value && value <= 128)
|
|
{
|
|
readcutscenescene(f, num, value - 1);
|
|
}
|
|
else
|
|
deh_warning("Scene number %d out of range (1 - 128)", value);
|
|
|
|
}
|
|
else
|
|
deh_warning("Cutscene %d: unknown word '%s', Scene <num> expected.", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
|
|
{
|
|
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
INT32 i;
|
|
UINT16 usi;
|
|
UINT8 picid;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
if (fastcmp(word, "PAGETEXT"))
|
|
{
|
|
char *pagetext = NULL;
|
|
char *buffer;
|
|
const int bufferlen = 4096;
|
|
|
|
for (i = 0; i < MAXLINELEN; i++)
|
|
{
|
|
if (s[i] == '=')
|
|
{
|
|
pagetext = &s[i+2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pagetext)
|
|
{
|
|
Z_Free(textprompts[num]->page[pagenum].text);
|
|
textprompts[num]->page[pagenum].text = NULL;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < MAXLINELEN; i++)
|
|
{
|
|
if (s[i] == '\0')
|
|
{
|
|
s[i] = '\n';
|
|
s[i+1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
buffer = Z_Malloc(4096, PU_STATIC, NULL);
|
|
strcpy(buffer, pagetext);
|
|
|
|
// \todo trim trailing whitespace before the #
|
|
// and also support # at the end of a PAGETEXT with no line break
|
|
|
|
strcat(buffer,
|
|
myhashfgets(pagetext, bufferlen
|
|
- strlen(buffer) - 1, f));
|
|
|
|
// A text prompt overwriting another one...
|
|
Z_Free(textprompts[num]->page[pagenum].text);
|
|
|
|
textprompts[num]->page[pagenum].text = Z_StrDup(buffer);
|
|
|
|
Z_Free(buffer);
|
|
|
|
continue;
|
|
}
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
strupr(word2);
|
|
else
|
|
break;
|
|
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
i = atoi(word2);
|
|
usi = (UINT16)i;
|
|
|
|
// copypasta from readcutscenescene
|
|
if (fastcmp(word, "NUMBEROFPICS"))
|
|
{
|
|
textprompts[num]->page[pagenum].numpics = (UINT8)i;
|
|
}
|
|
else if (fastcmp(word, "PICMODE"))
|
|
{
|
|
UINT8 picmode = 0; // PROMPT_PIC_PERSIST
|
|
if (usi == 1 || word2[0] == 'L') picmode = PROMPT_PIC_LOOP;
|
|
else if (usi == 2 || word2[0] == 'D' || word2[0] == 'H') picmode = PROMPT_PIC_DESTROY;
|
|
textprompts[num]->page[pagenum].picmode = picmode;
|
|
}
|
|
else if (fastcmp(word, "PICTOLOOP"))
|
|
textprompts[num]->page[pagenum].pictoloop = (UINT8)i;
|
|
else if (fastcmp(word, "PICTOSTART"))
|
|
textprompts[num]->page[pagenum].pictostart = (UINT8)i;
|
|
else if (fastcmp(word, "PICSMETAPAGE"))
|
|
{
|
|
if (usi && usi <= textprompts[num]->numpages)
|
|
{
|
|
UINT8 metapagenum = usi - 1;
|
|
|
|
textprompts[num]->page[pagenum].numpics = textprompts[num]->page[metapagenum].numpics;
|
|
textprompts[num]->page[pagenum].picmode = textprompts[num]->page[metapagenum].picmode;
|
|
textprompts[num]->page[pagenum].pictoloop = textprompts[num]->page[metapagenum].pictoloop;
|
|
textprompts[num]->page[pagenum].pictostart = textprompts[num]->page[metapagenum].pictostart;
|
|
|
|
for (picid = 0; picid < MAX_PROMPT_PICS; picid++)
|
|
{
|
|
strncpy(textprompts[num]->page[pagenum].picname[picid], textprompts[num]->page[metapagenum].picname[picid], 8);
|
|
textprompts[num]->page[pagenum].pichires[picid] = textprompts[num]->page[metapagenum].pichires[picid];
|
|
textprompts[num]->page[pagenum].picduration[picid] = textprompts[num]->page[metapagenum].picduration[picid];
|
|
textprompts[num]->page[pagenum].xcoord[picid] = textprompts[num]->page[metapagenum].xcoord[picid];
|
|
textprompts[num]->page[pagenum].ycoord[picid] = textprompts[num]->page[metapagenum].ycoord[picid];
|
|
}
|
|
}
|
|
}
|
|
else if (fastncmp(word, "PIC", 3))
|
|
{
|
|
picid = (UINT8)atoi(word + 3);
|
|
if (picid > MAX_PROMPT_PICS || picid == 0)
|
|
{
|
|
deh_warning("textpromptscene %d: unknown word '%s'", num, word);
|
|
continue;
|
|
}
|
|
--picid;
|
|
|
|
if (fastcmp(word+4, "NAME"))
|
|
{
|
|
strncpy(textprompts[num]->page[pagenum].picname[picid], word2, 8);
|
|
}
|
|
else if (fastcmp(word+4, "HIRES"))
|
|
{
|
|
textprompts[num]->page[pagenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else if (fastcmp(word+4, "DURATION"))
|
|
{
|
|
textprompts[num]->page[pagenum].picduration[picid] = usi;
|
|
}
|
|
else if (fastcmp(word+4, "XCOORD"))
|
|
{
|
|
textprompts[num]->page[pagenum].xcoord[picid] = usi;
|
|
}
|
|
else if (fastcmp(word+4, "YCOORD"))
|
|
{
|
|
textprompts[num]->page[pagenum].ycoord[picid] = usi;
|
|
}
|
|
else
|
|
deh_warning("textpromptscene %d: unknown word '%s'", num, word);
|
|
}
|
|
else if (fastcmp(word, "MUSIC"))
|
|
{
|
|
strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7);
|
|
textprompts[num]->page[pagenum].musswitch[6] = 0;
|
|
}
|
|
else if (fastcmp(word, "MUSICTRACK"))
|
|
{
|
|
textprompts[num]->page[pagenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK;
|
|
}
|
|
else if (fastcmp(word, "MUSICLOOP"))
|
|
{
|
|
textprompts[num]->page[pagenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
// end copypasta from readcutscenescene
|
|
else if (fastcmp(word, "NAME"))
|
|
{
|
|
if (*word2 != '\0')
|
|
{
|
|
INT32 j;
|
|
|
|
// HACK: Add yellow control char now
|
|
// so the drawing function doesn't call it repeatedly
|
|
char name[34];
|
|
name[0] = '\x82'; // color yellow
|
|
name[1] = 0;
|
|
strncat(name, word2, 33);
|
|
name[33] = 0;
|
|
|
|
// Replace _ with ' '
|
|
for (j = 0; j < 32 && name[j]; j++)
|
|
{
|
|
if (name[j] == '_')
|
|
name[j] = ' ';
|
|
}
|
|
|
|
strncpy(textprompts[num]->page[pagenum].name, name, 32);
|
|
}
|
|
else
|
|
*textprompts[num]->page[pagenum].name = '\0';
|
|
}
|
|
else if (fastcmp(word, "ICON"))
|
|
strncpy(textprompts[num]->page[pagenum].iconname, word2, 8);
|
|
else if (fastcmp(word, "ICONALIGN"))
|
|
textprompts[num]->page[pagenum].rightside = (i || word2[0] == 'R');
|
|
else if (fastcmp(word, "ICONFLIP"))
|
|
textprompts[num]->page[pagenum].iconflip = (i || word2[0] == 'T' || word2[0] == 'Y');
|
|
else if (fastcmp(word, "LINES"))
|
|
textprompts[num]->page[pagenum].lines = usi;
|
|
else if (fastcmp(word, "BACKCOLOR"))
|
|
{
|
|
INT32 backcolor;
|
|
if (i == 0 || fastcmp(word2, "WHITE")) backcolor = 0;
|
|
else if (i == 1 || fastcmp(word2, "GRAY") || fastcmp(word2, "GREY") ||
|
|
fastcmp(word2, "BLACK")) backcolor = 1;
|
|
else if (i == 2 || fastcmp(word2, "SEPIA")) backcolor = 2;
|
|
else if (i == 3 || fastcmp(word2, "BROWN")) backcolor = 3;
|
|
else if (i == 4 || fastcmp(word2, "PINK")) backcolor = 4;
|
|
else if (i == 5 || fastcmp(word2, "RASPBERRY")) backcolor = 5;
|
|
else if (i == 6 || fastcmp(word2, "RED")) backcolor = 6;
|
|
else if (i == 7 || fastcmp(word2, "CREAMSICLE")) backcolor = 7;
|
|
else if (i == 8 || fastcmp(word2, "ORANGE")) backcolor = 8;
|
|
else if (i == 9 || fastcmp(word2, "GOLD")) backcolor = 9;
|
|
else if (i == 10 || fastcmp(word2, "YELLOW")) backcolor = 10;
|
|
else if (i == 11 || fastcmp(word2, "EMERALD")) backcolor = 11;
|
|
else if (i == 12 || fastcmp(word2, "GREEN")) backcolor = 12;
|
|
else if (i == 13 || fastcmp(word2, "CYAN") || fastcmp(word2, "AQUA")) backcolor = 13;
|
|
else if (i == 14 || fastcmp(word2, "STEEL")) backcolor = 14;
|
|
else if (i == 15 || fastcmp(word2, "PERIWINKLE")) backcolor = 15;
|
|
else if (i == 16 || fastcmp(word2, "BLUE")) backcolor = 16;
|
|
else if (i == 17 || fastcmp(word2, "PURPLE")) backcolor = 17;
|
|
else if (i == 18 || fastcmp(word2, "LAVENDER")) backcolor = 18;
|
|
else if (i >= 256 && i < 512) backcolor = i; // non-transparent palette index
|
|
else if (i < 0) backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured
|
|
else backcolor = 1; // default gray
|
|
textprompts[num]->page[pagenum].backcolor = backcolor;
|
|
}
|
|
else if (fastcmp(word, "ALIGN"))
|
|
{
|
|
UINT8 align = 0; // left
|
|
if (usi == 1 || word2[0] == 'R') align = 1;
|
|
else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2;
|
|
textprompts[num]->page[pagenum].align = align;
|
|
}
|
|
else if (fastcmp(word, "VERTICALALIGN"))
|
|
{
|
|
UINT8 align = 0; // top
|
|
if (usi == 1 || word2[0] == 'B') align = 1;
|
|
else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2;
|
|
textprompts[num]->page[pagenum].verticalalign = align;
|
|
}
|
|
else if (fastcmp(word, "TEXTSPEED"))
|
|
textprompts[num]->page[pagenum].textspeed = get_number(word2);
|
|
else if (fastcmp(word, "TEXTSFX"))
|
|
textprompts[num]->page[pagenum].textsfx = get_number(word2);
|
|
else if (fastcmp(word, "HIDEHUD"))
|
|
{
|
|
UINT8 hidehud = 0;
|
|
if ((word2[0] == 'F' && (word2[1] == 'A' || !word2[1])) || word2[0] == 'N') hidehud = 0; // false
|
|
else if (usi == 1 || word2[0] == 'T' || word2[0] == 'Y') hidehud = 1; // true (hide appropriate HUD elements)
|
|
else if (usi == 2 || word2[0] == 'A' || (word2[0] == 'F' && word2[1] == 'O')) hidehud = 2; // force (hide all HUD elements)
|
|
textprompts[num]->page[pagenum].hidehud = hidehud;
|
|
}
|
|
else if (fastcmp(word, "METAPAGE"))
|
|
{
|
|
if (usi && usi <= textprompts[num]->numpages)
|
|
{
|
|
UINT8 metapagenum = usi - 1;
|
|
|
|
strncpy(textprompts[num]->page[pagenum].name, textprompts[num]->page[metapagenum].name, 32);
|
|
strncpy(textprompts[num]->page[pagenum].iconname, textprompts[num]->page[metapagenum].iconname, 8);
|
|
textprompts[num]->page[pagenum].rightside = textprompts[num]->page[metapagenum].rightside;
|
|
textprompts[num]->page[pagenum].iconflip = textprompts[num]->page[metapagenum].iconflip;
|
|
textprompts[num]->page[pagenum].lines = textprompts[num]->page[metapagenum].lines;
|
|
textprompts[num]->page[pagenum].backcolor = textprompts[num]->page[metapagenum].backcolor;
|
|
textprompts[num]->page[pagenum].align = textprompts[num]->page[metapagenum].align;
|
|
textprompts[num]->page[pagenum].verticalalign = textprompts[num]->page[metapagenum].verticalalign;
|
|
textprompts[num]->page[pagenum].textspeed = textprompts[num]->page[metapagenum].textspeed;
|
|
textprompts[num]->page[pagenum].textsfx = textprompts[num]->page[metapagenum].textsfx;
|
|
textprompts[num]->page[pagenum].hidehud = textprompts[num]->page[metapagenum].hidehud;
|
|
|
|
// music: don't copy, else each page change may reset the music
|
|
}
|
|
}
|
|
else if (fastcmp(word, "TAG"))
|
|
strncpy(textprompts[num]->page[pagenum].tag, word2, 33);
|
|
else if (fastcmp(word, "NEXTPROMPT"))
|
|
textprompts[num]->page[pagenum].nextprompt = usi;
|
|
else if (fastcmp(word, "NEXTPAGE"))
|
|
textprompts[num]->page[pagenum].nextpage = usi;
|
|
else if (fastcmp(word, "NEXTTAG"))
|
|
strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33);
|
|
else if (fastcmp(word, "TIMETONEXT"))
|
|
textprompts[num]->page[pagenum].timetonext = get_number(word2);
|
|
else
|
|
deh_warning("PromptPage %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readtextprompt(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
|
|
// Allocate memory for this prompt if we don't yet have any
|
|
if (!textprompts[num])
|
|
textprompts[num] = Z_Calloc(sizeof (textprompt_t), PU_STATIC, NULL);
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " ");
|
|
if (word2)
|
|
value = atoi(word2);
|
|
else
|
|
{
|
|
deh_warning("No value for token %s", word);
|
|
continue;
|
|
}
|
|
|
|
if (fastcmp(word, "NUMPAGES"))
|
|
{
|
|
textprompts[num]->numpages = min(max(value, 0), MAX_PAGES);
|
|
}
|
|
else if (fastcmp(word, "PAGE"))
|
|
{
|
|
if (1 <= value && value <= MAX_PAGES)
|
|
{
|
|
textprompts[num]->page[value - 1].backcolor = 1; // default to gray
|
|
textprompts[num]->page[value - 1].hidehud = 1; // hide appropriate HUD elements
|
|
readtextpromptpage(f, num, value - 1);
|
|
}
|
|
else
|
|
deh_warning("Page number %d out of range (1 - %d)", value, MAX_PAGES);
|
|
|
|
}
|
|
else
|
|
deh_warning("Prompt %d: unknown word '%s', Page <num> expected.", num, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readframe(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word1;
|
|
char *word2 = NULL;
|
|
char *tmp;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word1 = strtok(s, " ");
|
|
if (word1)
|
|
strupr(word1);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
if (word2)
|
|
strupr(word2);
|
|
else
|
|
break;
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
|
|
if (fastcmp(word1, "SPRITENUMBER") || fastcmp(word1, "SPRITENAME"))
|
|
{
|
|
states[num].sprite = get_sprite(word2);
|
|
}
|
|
else if (fastcmp(word1, "SPRITESUBNUMBER") || fastcmp(word1, "SPRITEFRAME"))
|
|
{
|
|
states[num].frame = (INT32)get_number(word2); // So the FF_ flags get calculated
|
|
}
|
|
else if (fastcmp(word1, "DURATION"))
|
|
{
|
|
states[num].tics = (INT32)get_number(word2); // So TICRATE can be used
|
|
}
|
|
else if (fastcmp(word1, "NEXT"))
|
|
{
|
|
states[num].nextstate = get_state(word2);
|
|
}
|
|
else if (fastcmp(word1, "VAR1"))
|
|
{
|
|
states[num].var1 = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word1, "VAR2"))
|
|
{
|
|
states[num].var2 = (INT32)get_number(word2);
|
|
}
|
|
else if (fastcmp(word1, "ACTION"))
|
|
{
|
|
size_t z;
|
|
boolean found = false;
|
|
char actiontocompare[32];
|
|
|
|
memset(actiontocompare, 0x00, sizeof(actiontocompare));
|
|
strlcpy(actiontocompare, word2, sizeof (actiontocompare));
|
|
strupr(actiontocompare);
|
|
|
|
for (z = 0; z < 32; z++)
|
|
{
|
|
if (actiontocompare[z] == '\n' || actiontocompare[z] == '\r')
|
|
{
|
|
actiontocompare[z] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (z = 0; actionpointers[z].name; z++)
|
|
{
|
|
if (actionpointers[z].action.acv == states[num].action.acv)
|
|
break;
|
|
}
|
|
|
|
z = 0;
|
|
found = LUA_SetLuaAction(&states[num], actiontocompare);
|
|
if (!found)
|
|
while (actionpointers[z].name)
|
|
{
|
|
if (fastcmp(actiontocompare, actionpointers[z].name))
|
|
{
|
|
states[num].action = actionpointers[z].action;
|
|
states[num].action.acv = actionpointers[z].action.acv; // assign
|
|
states[num].action.acp1 = actionpointers[z].action.acp1;
|
|
found = true;
|
|
break;
|
|
}
|
|
z++;
|
|
}
|
|
|
|
if (!found)
|
|
deh_warning("Unknown action %s", actiontocompare);
|
|
}
|
|
else
|
|
deh_warning("Frame %d: unknown word '%s'", num, word1);
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readsound(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
|
|
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.
|
|
|
|
// Set / reset word
|
|
word = s;
|
|
|
|
// 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, "SINGULAR"))
|
|
{
|
|
S_sfx[num].singularity = value;
|
|
}
|
|
else if (fastcmp(word, "PRIORITY"))
|
|
{
|
|
S_sfx[num].priority = value;
|
|
}
|
|
else if (fastcmp(word, "FLAGS"))
|
|
{
|
|
S_sfx[num].pitch = value;
|
|
}
|
|
else if (fastcmp(word, "VOLUME"))
|
|
{
|
|
S_sfx[num].volume = value;
|
|
}
|
|
else if (fastcmp(word, "CAPTION") || fastcmp(word, "DESCRIPTION"))
|
|
{
|
|
deh_strlcpy(S_sfx[num].caption, word2,
|
|
sizeof(S_sfx[num].caption), va("Sound effect %d: caption", num));
|
|
}
|
|
else
|
|
deh_warning("Sound %d : unknown word '%s'",num,word);
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
/** Checks if a game data file name for a mod is good.
|
|
* "Good" means that it contains only alphanumerics, _, and -;
|
|
* ends in ".dat"; has at least one character before the ".dat";
|
|
* and is not "gamedata.dat" (tested case-insensitively).
|
|
*
|
|
* Assumption: that gamedata.dat is the only .dat file that will
|
|
* ever be treated specially by the game.
|
|
*
|
|
* Note: Check for the tail ".dat" case-insensitively since at
|
|
* present, we get passed the filename in all uppercase.
|
|
*
|
|
* \param s Filename string to check.
|
|
* \return True if the filename is good.
|
|
* \sa readmaincfg()
|
|
* \author Graue <graue@oceanbase.org>
|
|
*/
|
|
static boolean GoodDataFileName(const char *s)
|
|
{
|
|
const char *p;
|
|
const char *tail = ".dat";
|
|
|
|
for (p = s; *p != '\0'; p++)
|
|
if (!isalnum(*p) && *p != '_' && *p != '-' && *p != '.')
|
|
return false;
|
|
|
|
p = s + strlen(s) - strlen(tail);
|
|
if (p <= s) return false; // too short
|
|
if (!fasticmp(p, tail)) return false; // doesn't end in .dat
|
|
if (fasticmp(s, "gamedata.dat")) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void reademblemdata(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
|
|
memset(&emblemlocations[num-1], 0, sizeof(emblem_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
|
|
|
|
strupr(word2);
|
|
|
|
if (fastcmp(word, "TYPE"))
|
|
{
|
|
if (fastcmp(word2, "GLOBAL"))
|
|
emblemlocations[num-1].type = ET_GLOBAL;
|
|
else if (fastcmp(word2, "MAP"))
|
|
emblemlocations[num-1].type = ET_MAP;
|
|
else if (fastcmp(word2, "TIME"))
|
|
emblemlocations[num-1].type = ET_TIME;
|
|
else
|
|
emblemlocations[num-1].type = (UINT8)value;
|
|
}
|
|
else if (fastcmp(word, "TAG"))
|
|
emblemlocations[num-1].tag = (INT16)value;
|
|
else if (fastcmp(word, "MAPNAME"))
|
|
{
|
|
emblemlocations[num-1].level = Z_StrDup(word2);
|
|
emblemlocations[num-1].levelCache = NEXTMAP_INVALID;
|
|
}
|
|
else 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
|
|
emblemlocations[num-1].sprite = (UINT8)value;
|
|
}
|
|
else if (fastcmp(word, "COLOR"))
|
|
emblemlocations[num-1].color = get_number(word2);
|
|
else if (fastcmp(word, "VAR"))
|
|
{
|
|
Z_Free(emblemlocations[num-1].stringVar);
|
|
emblemlocations[num-1].stringVar = Z_StrDup(word2);
|
|
|
|
emblemlocations[num-1].var = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "VAR2"))
|
|
{
|
|
Z_Free(emblemlocations[num-1].stringVar2);
|
|
emblemlocations[num-1].stringVar2 = Z_StrDup(word2);
|
|
|
|
emblemlocations[num-1].var2 = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "FLAGS"))
|
|
emblemlocations[num-1].flags = get_number(word2);
|
|
else
|
|
deh_warning("Emblem %d: unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
// Default sprite and color definitions for lazy people like me
|
|
if (!emblemlocations[num-1].sprite)
|
|
{
|
|
emblemlocations[num-1].sprite = 'A';
|
|
}
|
|
if (!emblemlocations[num-1].color)
|
|
{
|
|
emblemlocations[num-1].color = SKINCOLOR_GOLD;
|
|
}
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readunlockable(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 i;
|
|
|
|
memset(&unlockables[num], 0, sizeof(unlockable_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;
|
|
|
|
i = atoi(word2); // used for numerical settings
|
|
|
|
if (fastcmp(word, "NAME"))
|
|
deh_strlcpy(unlockables[num].name, word2,
|
|
sizeof (unlockables[num].name), va("Unlockable %d: name", num+1));
|
|
else
|
|
{
|
|
strupr(word2);
|
|
|
|
if (fastcmp(word, "CONDITIONSET"))
|
|
unlockables[num].conditionset = (UINT16)i;
|
|
else if (fastcmp(word, "MAJORUNLOCK"))
|
|
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, "SKIN"))
|
|
unlockables[num].type = SECRET_SKIN;
|
|
else if (fastcmp(word2, "FOLLOWER"))
|
|
unlockables[num].type = SECRET_FOLLOWER;
|
|
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, "ITEMFINDER"))
|
|
unlockables[num].type = SECRET_ITEMFINDER;
|
|
else
|
|
unlockables[num].type = (INT16)i;
|
|
unlockables[num].stringVarCache = -1;
|
|
}
|
|
else if (fastcmp(word, "VAR"))
|
|
{
|
|
Z_Free(unlockables[num].stringVar);
|
|
unlockables[num].stringVar = Z_StrDup(word2);
|
|
unlockables[num].stringVarCache = -1;
|
|
unlockables[num].variable = (INT16)i;
|
|
}
|
|
else if (fastcmp(word, "ICON"))
|
|
{
|
|
Z_Free(unlockables[num].icon);
|
|
unlockables[num].icon = Z_StrDup(word2);
|
|
}
|
|
else if (fastcmp(word, "COLOR"))
|
|
{
|
|
unlockables[num].color = get_number(word2);
|
|
}
|
|
else
|
|
deh_warning("Unlockable %d: unknown word '%s'", num+1, word);
|
|
}
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|
{
|
|
INT32 i;
|
|
char *params[5]; // condition, requirement, extra info, extra info, stringvar
|
|
char *spos;
|
|
char *stringvar = NULL;
|
|
|
|
conditiontype_t ty;
|
|
INT32 re = 0;
|
|
INT16 x1 = 0, x2 = 0;
|
|
|
|
INT32 offset = 0;
|
|
|
|
#if 0
|
|
char *endpos = word2 + strlen(word2);
|
|
#endif
|
|
|
|
spos = strtok(word2, " ");
|
|
|
|
for (i = 0; i < 5; ++i)
|
|
{
|
|
if (spos != NULL)
|
|
{
|
|
params[i] = spos;
|
|
spos = strtok(NULL, " ");
|
|
}
|
|
else
|
|
params[i] = NULL;
|
|
}
|
|
|
|
if (!params[0])
|
|
{
|
|
deh_warning("condition line is empty for condition ID %d", id+1);
|
|
return;
|
|
}
|
|
|
|
if (fastcmp(params[0], "PLAYTIME"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_PLAYTIME + offset;
|
|
re = atoi(params[1]);
|
|
}
|
|
else if (fastcmp(params[0], "ROUNDSPLAYED"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_ROUNDSPLAYED;
|
|
re = atoi(params[1]);
|
|
x1 = GDGT_MAX;
|
|
|
|
if (re == 0)
|
|
{
|
|
deh_warning("Rounds played requirement is %d for condition ID %d", re, id+1);
|
|
return;
|
|
}
|
|
|
|
if (params[2])
|
|
{
|
|
if (fastcmp(params[2], "RACE"))
|
|
x1 = GDGT_RACE;
|
|
else if (fastcmp(params[2], "BATTLE"))
|
|
x1 = GDGT_BATTLE;
|
|
else if (fastcmp(params[2], "PRISONS"))
|
|
x1 = GDGT_PRISONS;
|
|
else if (fastcmp(params[2], "SPECIAL"))
|
|
x1 = GDGT_SPECIAL;
|
|
else if (fastcmp(params[2], "CUSTOM"))
|
|
x1 = GDGT_CUSTOM;
|
|
else
|
|
{
|
|
deh_warning("gametype requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "TOTALRINGS"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_TOTALRINGS;
|
|
re = atoi(params[1]);
|
|
|
|
if (re < 2 || re > GDMAX_RINGS)
|
|
{
|
|
deh_warning("Total Rings requirement %d out of range (%d - %d) for condition ID %d", re, 2, GDMAX_RINGS, id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "GAMECLEAR"))
|
|
{
|
|
ty = UC_GAMECLEAR;
|
|
re = (params[1]) ? atoi(params[1]) : 1;
|
|
}
|
|
else if (fastcmp(params[0], "OVERALLTIME"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_OVERALLTIME;
|
|
re = atoi(params[1]);
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "MAPVISITED")
|
|
|| (++offset && fastcmp(params[0], "MAPBEATEN"))
|
|
|| (++offset && fastcmp(params[0], "MAPENCORE"))
|
|
|| (++offset && fastcmp(params[0], "MAPSPBATTACK")))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_MAPVISITED + offset;
|
|
re = G_MapNumber(params[1]);
|
|
|
|
if (re >= nummapheaders)
|
|
{
|
|
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "MAPTIME"))
|
|
{
|
|
PARAMCHECK(2);
|
|
ty = UC_MAPTIME;
|
|
re = atoi(params[2]);
|
|
x1 = G_MapNumber(params[1]);
|
|
|
|
if (x1 >= nummapheaders)
|
|
{
|
|
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "ALLCHAOS")
|
|
|| (++offset && fastcmp(params[0], "ALLSUPER"))
|
|
|| (++offset && fastcmp(params[0], "ALLEMERALDS")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UC_ALLCHAOS + offset;
|
|
re = KARTSPEED_NORMAL;
|
|
if (params[1])
|
|
{
|
|
if (fastcmp(params[1], "NORMAL"))
|
|
;
|
|
else if (fastcmp(params[1], "HARD"))
|
|
x1 = KARTSPEED_HARD;
|
|
else if (fastcmp(params[1], "MASTER"))
|
|
x1 = KARTGP_MASTER;
|
|
else
|
|
{
|
|
deh_warning("gamespeed requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "TOTALMEDALS"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_TOTALMEDALS;
|
|
re = atoi(params[1]);
|
|
}
|
|
else if (fastcmp(params[0], "EMBLEM"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_EMBLEM;
|
|
re = atoi(params[1]);
|
|
|
|
if (re <= 0 || re > MAXEMBLEMS)
|
|
{
|
|
deh_warning("Emblem %d out of range (1 - %d) for condition ID %d", re, MAXEMBLEMS, id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "UNLOCKABLE"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_UNLOCKABLE;
|
|
re = atoi(params[1]);
|
|
|
|
if (re <= 0 || re > MAXUNLOCKABLES)
|
|
{
|
|
deh_warning("Unlockable %d out of range (1 - %d) for condition ID %d", re, MAXUNLOCKABLES, id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "CONDITIONSET"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UC_CONDITIONSET;
|
|
re = atoi(params[1]);
|
|
|
|
if (re <= 0 || re > MAXCONDITIONSETS)
|
|
{
|
|
deh_warning("Condition set %d out of range (1 - %d) for condition ID %d", re, MAXCONDITIONSETS, id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "ADDON")
|
|
|| (++offset && fastcmp(params[0], "REPLAY"))
|
|
|| (++offset && fastcmp(params[0], "CRASH")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UC_ADDON + offset;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "AND")
|
|
|| (++offset && fastcmp(params[0], "COMMA")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UC_AND + offset;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "PREFIX_GRANDPRIX")
|
|
|| (++offset && fastcmp(params[0], "PREFIX_BONUSROUND"))
|
|
|| (++offset && fastcmp(params[0], "PREFIX_TIMEATTACK"))
|
|
|| (++offset && fastcmp(params[0], "PREFIX_PRISONBREAK"))
|
|
|| (++offset && fastcmp(params[0], "PREFIX_SEALEDSTAR")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UCRP_PREFIX_GRANDPRIX + offset;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "PREFIX_ISMAP")
|
|
|| (++offset && fastcmp(params[0], "ISMAP")))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_PREFIX_ISMAP + offset;
|
|
re = G_MapNumber(params[1]);
|
|
|
|
if (re >= nummapheaders)
|
|
{
|
|
deh_warning("Invalid level %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "ISCHARACTER"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_ISCHARACTER;
|
|
#if 0
|
|
{
|
|
re = R_SkinAvailable(params[1]);
|
|
|
|
if (re < 0)
|
|
{
|
|
deh_warning("Invalid character %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
stringvar = Z_StrDup(params[1]);
|
|
re = -1;
|
|
}
|
|
#endif
|
|
}
|
|
else if (fastcmp(params[0], "ISENGINECLASS"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_ISENGINECLASS;
|
|
if (!params[1][1]
|
|
&& params[1][0] >= 'A' && params[1][0] <= 'J')
|
|
{
|
|
re = params[1][0] - 'A';
|
|
}
|
|
else
|
|
{
|
|
deh_warning("engine class requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "ISDIFFICULTY"))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UCRP_ISDIFFICULTY;
|
|
re = KARTSPEED_NORMAL;
|
|
if (params[1])
|
|
{
|
|
if (fastcmp(params[1], "NORMAL"))
|
|
;
|
|
else if (fastcmp(params[1], "HARD"))
|
|
x1 = KARTSPEED_HARD;
|
|
else if (fastcmp(params[1], "MASTER"))
|
|
x1 = KARTGP_MASTER;
|
|
else
|
|
{
|
|
deh_warning("gamespeed requirement \"%s\" invalid for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "PODIUMCUP"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_PODIUMCUP;
|
|
{
|
|
cupheader_t *cup = kartcupheaders;
|
|
UINT32 hash = quickncasehash(params[1], MAXCUPNAME);
|
|
while (cup)
|
|
{
|
|
if (hash == cup->namehash && !strcmp(cup->name, params[1]))
|
|
break;
|
|
cup = cup->next;
|
|
}
|
|
|
|
if (!cup)
|
|
{
|
|
deh_warning("Invalid cup %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
|
|
re = cup->id;
|
|
}
|
|
|
|
if (params[2])
|
|
{
|
|
if (params[2][0] && !params[2][1])
|
|
{
|
|
x2 = 1;
|
|
|
|
switch (params[2][0])
|
|
{
|
|
case 'E': { x1 = GRADE_E; break; }
|
|
case 'D': { x1 = GRADE_D; break; }
|
|
case 'C': { x1 = GRADE_C; break; }
|
|
case 'B': { x1 = GRADE_B; break; }
|
|
case 'A': { x1 = GRADE_A; break; }
|
|
case 'S': { x1 = GRADE_S; break; }
|
|
default:
|
|
deh_warning("Invalid grade %s for condition ID %d", params[2], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if ((offset=0) || fastcmp(params[2], "GOLD")
|
|
|| (++offset && fastcmp(params[2], "SILVER"))
|
|
|| (++offset && fastcmp(params[2], "BRONZE")))
|
|
{
|
|
x1 = offset + 1;
|
|
}
|
|
else
|
|
{
|
|
deh_warning("Invalid cup result %s for condition ID %d", params[2], id+1);
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "PODIUMEMERALD")
|
|
|| (++offset && fastcmp(params[0], "PODIUMPRIZE")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UCRP_PODIUMEMERALD + offset;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "FINISHCOOL")
|
|
|| (++offset && fastcmp(params[0], "FINISHALLPRISONS"))
|
|
|| (++offset && fastcmp(params[0], "NOCONTEST")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UCRP_FINISHCOOL + offset;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "FINISHPLACE")
|
|
|| (++offset && fastcmp(params[0], "FINISHPLACEEXACT")))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_FINISHPLACE + offset;
|
|
re = atoi(params[1]);
|
|
|
|
if (re < 1 || re > MAXPLAYERS)
|
|
{
|
|
deh_warning("Invalid place %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "FINISHTIME")
|
|
|| (++offset && fastcmp(params[0], "FINISHTIMEEXACT"))
|
|
|| (++offset && fastcmp(params[0], "FINISHTIMELEFT")))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_FINISHTIME + offset;
|
|
re = get_number(params[1]);
|
|
|
|
if (re < 0)
|
|
{
|
|
deh_warning("Invalid time %s for condition ID %d", params[1], id+1);
|
|
return;
|
|
}
|
|
}
|
|
else if (fastcmp(params[0], "TRIGGER"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_TRIGGER;
|
|
re = atoi(params[1]);
|
|
|
|
// constrained by 32 bits
|
|
if (re < 0 || re > 31)
|
|
{
|
|
deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1);
|
|
return;
|
|
}
|
|
|
|
// The following undid the effects of strtok.
|
|
// Unfortunately, there is no way it can reasonably undo the effects of strupr.
|
|
// If we want custom descriptions for map execution triggers, we're gonna need a different method.
|
|
#if 0
|
|
// undo affect of strtok
|
|
i = 5;
|
|
// so spos will still be the strtok from earlier
|
|
while (i >= 2)
|
|
{
|
|
if (!spos)
|
|
continue;
|
|
while (*spos != '\0')
|
|
spos++;
|
|
if (spos < endpos)
|
|
*spos = ' ';
|
|
spos = params[--i];
|
|
}
|
|
|
|
stringvar = Z_StrDup(params[2]);
|
|
#endif
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "FALLOFF")
|
|
|| (++offset && fastcmp(params[0], "TOUCHOFFROAD"))
|
|
|| (++offset && fastcmp(params[0], "TOUCHSNEAKERPANEL"))
|
|
|| (++offset && fastcmp(params[0], "RINGDEBT")))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_FALLOFF + offset;
|
|
re = 1;
|
|
|
|
if (params[1][0] == 'F' || params[1][0] == 'N' || params[1][0] == '0')
|
|
re = 0;
|
|
}
|
|
else if ((offset=0) || fastcmp(params[0], "TRIPWIREHYUU")
|
|
|| (++offset && fastcmp(params[0], "SPBNEUTER"))
|
|
|| (++offset && fastcmp(params[0], "LANDMINEDUNK"))
|
|
|| (++offset && fastcmp(params[0], "HITMIDAIR")))
|
|
{
|
|
//PARAMCHECK(1);
|
|
ty = UCRP_TRIPWIREHYUU + offset;
|
|
}
|
|
else if (fastcmp(params[0], "WETPLAYER"))
|
|
{
|
|
PARAMCHECK(1);
|
|
ty = UCRP_WETPLAYER;
|
|
re = MFE_UNDERWATER;
|
|
x1 = 1;
|
|
|
|
if (params[2])
|
|
{
|
|
if (fastcmp(params[2], "STRICT"))
|
|
re |= MFE_TOUCHWATER;
|
|
else
|
|
{
|
|
deh_warning("liquid strictness requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
stringvar = Z_StrDup(params[1]);
|
|
}
|
|
else
|
|
{
|
|
deh_warning("Invalid condition name %s for condition ID %d", params[0], id+1);
|
|
return;
|
|
}
|
|
|
|
M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar);
|
|
}
|
|
|
|
void readconditionset(MYFILE *f, UINT8 setnum)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *word2;
|
|
char *tmp;
|
|
UINT8 id;
|
|
UINT8 previd = 0;
|
|
|
|
M_ClearConditionSet(setnum);
|
|
|
|
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;
|
|
strupr(word2);
|
|
|
|
if (fastncmp(word, "CONDITION", 9))
|
|
{
|
|
id = (UINT8)atoi(word + 9);
|
|
if (id == 0)
|
|
{
|
|
deh_warning("Condition set %d: unknown word '%s'", setnum+1, word);
|
|
continue;
|
|
}
|
|
else if (previd > id)
|
|
{
|
|
// out of order conditions can cause problems, so enforce proper order
|
|
deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum+1);
|
|
continue;
|
|
}
|
|
previd = id;
|
|
|
|
readcondition(setnum, id, word2);
|
|
}
|
|
else
|
|
deh_warning("Condition set %d: unknown word '%s'", setnum, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readmaincfg(MYFILE *f, boolean mainfile)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
boolean doClearLevels = false;
|
|
|
|
#ifdef DEVELOP
|
|
(void)mainfile;
|
|
#endif
|
|
|
|
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;
|
|
strupr(word2);
|
|
|
|
value = atoi(word2); // used for numerical settings
|
|
|
|
if (fastcmp(word, "GAMEDATA"))
|
|
{
|
|
size_t filenamelen;
|
|
|
|
// Check the data filename so that mods
|
|
// can't write arbitrary files.
|
|
if (!GoodDataFileName(word2))
|
|
I_Error("Maincfg: bad data file name '%s'\n", word2);
|
|
|
|
G_SaveGameData();
|
|
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
|
|
strlwr(gamedatafilename);
|
|
savemoddata = true;
|
|
majormods = false;
|
|
|
|
// Also save a time attack folder
|
|
filenamelen = strlen(gamedatafilename)-4; // Strip off the extension
|
|
filenamelen = min(filenamelen, sizeof (timeattackfolder));
|
|
strncpy(timeattackfolder, gamedatafilename, filenamelen);
|
|
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
|
|
|
|
strcpy(savegamename, timeattackfolder);
|
|
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
|
|
// can't use sprintf since there is %u in savegamename
|
|
strcatbf(savegamename, srb2home, PATHSEP);
|
|
|
|
strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder));
|
|
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
|
|
|
refreshdirmenu |= REFRESHDIR_GAMEDATA;
|
|
gamedataadded = true;
|
|
titlechanged = true;
|
|
|
|
clear_unlockables();
|
|
clear_conditionsets();
|
|
clear_emblems();
|
|
//clear_levels();
|
|
doClearLevels = true;
|
|
|
|
G_ClearRecords();
|
|
M_ClearStats();
|
|
M_ClearSecrets();
|
|
|
|
// Don't softlock the Stereo on if you won't be able to access it anymore!?
|
|
if (soundtest.playing && M_SecretUnlocked(SECRET_SOUNDTEST, true) == false)
|
|
S_SoundTestStop();
|
|
}
|
|
#ifndef DEVELOP
|
|
else if (!mainfile && !gamedataadded)
|
|
{
|
|
deh_warning("You must define a custom gamedata to use \"%s\"", word);
|
|
}
|
|
#endif
|
|
else if (fastcmp(word, "CLEARLEVELS"))
|
|
{
|
|
doClearLevels = (UINT8)(value == 0 || word2[0] == 'F' || word2[0] == 'N');
|
|
}
|
|
else if (fastcmp(word, "EXECCFG"))
|
|
{
|
|
if (strchr(word2, '.'))
|
|
COM_BufAddText(va("exec %s\n", word2));
|
|
else
|
|
{
|
|
lumpnum_t lumpnum;
|
|
char newname[9];
|
|
|
|
strncpy(newname, word2, 8);
|
|
|
|
newname[8] = '\0';
|
|
|
|
lumpnum = W_CheckNumForName(newname);
|
|
|
|
if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0)
|
|
CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname);
|
|
else
|
|
COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
|
|
}
|
|
}
|
|
else if (fastcmp(word, "REDTEAM"))
|
|
{
|
|
skincolor_redteam = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "BLUETEAM"))
|
|
{
|
|
skincolor_blueteam = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "REDRING"))
|
|
{
|
|
skincolor_redring = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "BLUERING"))
|
|
{
|
|
skincolor_bluering = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "INVULNTICS"))
|
|
{
|
|
invulntics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SNEAKERTICS"))
|
|
{
|
|
sneakertics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "FLASHINGTICS"))
|
|
{
|
|
flashingtics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "TAILSFLYTICS"))
|
|
{
|
|
tailsflytics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "UNDERWATERTICS"))
|
|
{
|
|
underwatertics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "SPACETIMETICS"))
|
|
{
|
|
spacetimetics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "EXTRALIFETICS"))
|
|
{
|
|
extralifetics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "NIGHTSLINKTICS"))
|
|
{
|
|
nightslinktics = (UINT16)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "GAMEOVERTICS"))
|
|
{
|
|
gameovertics = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "AMMOREMOVALTICS"))
|
|
{
|
|
ammoremovaltics = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "INTROTOPLAY"))
|
|
{
|
|
introtoplay = (UINT8)get_number(word2);
|
|
// range check, you morons.
|
|
if (introtoplay > 128)
|
|
introtoplay = 128;
|
|
introchanged = true;
|
|
}
|
|
else if (fastcmp(word, "CREDITSCUTSCENE"))
|
|
{
|
|
creditscutscene = (UINT8)get_number(word2);
|
|
// range check, you morons.
|
|
if (creditscutscene > 128)
|
|
creditscutscene = 128;
|
|
}
|
|
else if (fastcmp(word, "USEBLACKROCK"))
|
|
{
|
|
useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else if (fastcmp(word, "LOOPTITLE"))
|
|
{
|
|
looptitle = (value || word2[0] == 'T' || word2[0] == 'Y');
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEMAP"))
|
|
{
|
|
Z_Free(titlemap);
|
|
titlemap = Z_StrDup(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE"))
|
|
{
|
|
hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y');
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSMODE"))
|
|
{
|
|
if (fastcmp(word2, "USER"))
|
|
ttmode = TTMODE_USER;
|
|
else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE"))
|
|
{
|
|
ttmode = TTMODE_USER;
|
|
ttname[0] = 0;
|
|
hidetitlepics = true;
|
|
}
|
|
else if (fastcmp(word2, "RINGRACERS"))
|
|
ttmode = TTMODE_RINGRACERS;
|
|
else if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS"))
|
|
ttmode = TTMODE_OLD;
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSNAME"))
|
|
{
|
|
strncpy(ttname, word2, 9);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSX"))
|
|
{
|
|
ttx = (INT16)get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSY"))
|
|
{
|
|
tty = (INT16)get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSLOOP"))
|
|
{
|
|
ttloop = (INT16)get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLEPICSTICS"))
|
|
{
|
|
tttics = (UINT16)get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED"))
|
|
{
|
|
titlescrollxspeed = get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "TITLESCROLLYSPEED"))
|
|
{
|
|
titlescrollyspeed = get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "NUMDEMOS"))
|
|
{
|
|
numDemos = (UINT8)get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "DEMODELAYTIME"))
|
|
{
|
|
demoDelayTime = get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "DEMOIDLETIME"))
|
|
{
|
|
demoIdleTime = get_number(word2);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "USE1UPSOUND"))
|
|
{
|
|
use1upSound = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
|
|
}
|
|
else if (fastcmp(word, "MAXXTRALIFE"))
|
|
{
|
|
maxXtraLife = (UINT8)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "RESETDATA"))
|
|
{
|
|
P_ResetData(value);
|
|
titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "CUSTOMVERSION"))
|
|
{
|
|
strlcpy(customversionstring, word2, sizeof (customversionstring));
|
|
//titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "BOOTMAP"))
|
|
{
|
|
Z_Free(bootmap);
|
|
bootmap = Z_StrDup(word2);
|
|
//titlechanged = true;
|
|
}
|
|
else if (fastcmp(word, "PODIUMMAP"))
|
|
{
|
|
Z_Free(podiummap);
|
|
podiummap = Z_StrDup(word2);
|
|
}
|
|
else
|
|
deh_warning("Maincfg: unknown word '%s'", word);
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
if (doClearLevels)
|
|
{
|
|
clear_levels();
|
|
}
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readwipes(MYFILE *f)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word = s;
|
|
char *pword = word;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 value;
|
|
INT32 wipeoffset;
|
|
|
|
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 (value < -1 || value > 99)
|
|
{
|
|
deh_warning("Wipes: bad value '%s'", word2);
|
|
continue;
|
|
}
|
|
else if (value == -1)
|
|
value = UINT8_MAX;
|
|
|
|
// error catching
|
|
wipeoffset = -1;
|
|
|
|
if (fastncmp(word, "LEVEL_", 6))
|
|
{
|
|
pword = word + 6;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_level_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_level_final;
|
|
}
|
|
else if (fastncmp(word, "INTERMISSION_", 13))
|
|
{
|
|
pword = word + 13;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_intermission_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_intermission_final;
|
|
}
|
|
else if (fastncmp(word, "VOTING_", 7))
|
|
{
|
|
pword = word + 7;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_voting_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_voting_final;
|
|
}
|
|
else if (fastncmp(word, "CONTINUING_", 11))
|
|
{
|
|
pword = word + 11;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_continuing_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_continuing_final;
|
|
}
|
|
else if (fastncmp(word, "TITLESCREEN_", 12))
|
|
{
|
|
pword = word + 12;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_titlescreen_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_titlescreen_final;
|
|
}
|
|
else if (fastncmp(word, "MENU_", 11))
|
|
{
|
|
pword = word + 11;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_menu_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_menu_final;
|
|
}
|
|
else if (fastncmp(word, "CREDITS_", 8))
|
|
{
|
|
pword = word + 8;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_credits_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_credits_final;
|
|
else if (fastcmp(pword, "INTERMEDIATE"))
|
|
wipeoffset = wipe_credits_intermediate;
|
|
}
|
|
else if (fastncmp(word, "EVALUATION_", 11))
|
|
{
|
|
pword = word + 11;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_evaluation_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_evaluation_final;
|
|
}
|
|
else if (fastncmp(word, "GAMEEND_", 8))
|
|
{
|
|
pword = word + 8;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_gameend_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_gameend_final;
|
|
}
|
|
else if (fastncmp(word, "CEREMONY_", 9))
|
|
{
|
|
pword = word + 9;
|
|
if (fastcmp(pword, "TOBLACK"))
|
|
wipeoffset = wipe_ceremony_toblack;
|
|
else if (fastcmp(pword, "FINAL"))
|
|
wipeoffset = wipe_ceremony_final;
|
|
}
|
|
else if (fastncmp(word, "ENCORE_", 7))
|
|
{
|
|
pword = word + 7;
|
|
if (fastcmp(pword, "TOINVERT"))
|
|
wipeoffset = wipe_encore_toinvert;
|
|
else if (fastcmp(pword, "TOWHITE"))
|
|
wipeoffset = wipe_encore_towhite;
|
|
}
|
|
|
|
if (wipeoffset < 0)
|
|
{
|
|
deh_warning("Wipes: unknown word '%s'", word);
|
|
continue;
|
|
}
|
|
|
|
if (value == UINT8_MAX
|
|
&& (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_encore_toinvert))
|
|
{
|
|
// Cannot disable non-toblack wipes
|
|
// (or the level toblack wipe, or the special encore wipe)
|
|
deh_warning("Wipes: can't disable wipe of type '%s'", word);
|
|
continue;
|
|
}
|
|
|
|
wipedefs[wipeoffset] = (UINT8)value;
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
//
|
|
// SRB2KART
|
|
//
|
|
|
|
static void invalidateacrosscups(UINT16 map)
|
|
{
|
|
cupheader_t *cup = kartcupheaders;
|
|
UINT8 i;
|
|
|
|
if (map >= nummapheaders)
|
|
return;
|
|
|
|
while (cup)
|
|
{
|
|
for (i = 0; i < CUPCACHE_MAX; i++)
|
|
{
|
|
if (cup->cachedlevels[i] != map)
|
|
continue;
|
|
cup->cachedlevels[i] = NEXTMAP_INVALID;
|
|
}
|
|
cup = cup->next;
|
|
}
|
|
|
|
mapheaderinfo[map]->cup = NULL;
|
|
}
|
|
|
|
void readcupheader(MYFILE *f, cupheader_t *cup)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
char *tmp;
|
|
INT32 i;
|
|
|
|
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.
|
|
|
|
// Set / reset word, because some things (Lua.) move it
|
|
word = s;
|
|
|
|
// 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;
|
|
i = atoi(word2); // used for numerical settings
|
|
strupr(word2);
|
|
|
|
if (fastcmp(word, "MONITOR"))
|
|
{
|
|
if (i > 0 && i < 10)
|
|
cup->monitor = i;
|
|
else if (!word2[0] || word2[1] != '\0' || word2[0] == '0')
|
|
deh_warning("%s Cup: Invalid monitor type \"%s\" (should be 1-9 or A-Z)\n", cup->name, word2);
|
|
else
|
|
cup->monitor = (word2[0] - 'A') + 10;
|
|
}
|
|
else if (fastcmp(word, "ICON"))
|
|
{
|
|
deh_strlcpy(cup->icon, word2,
|
|
sizeof(cup->icon), va("%s Cup: icon", cup->name));
|
|
}
|
|
else if (fastcmp(word, "LEVELLIST"))
|
|
{
|
|
while (cup->numlevels > 0)
|
|
{
|
|
cup->numlevels--;
|
|
Z_Free(cup->levellist[cup->numlevels]);
|
|
cup->levellist[cup->numlevels] = NULL;
|
|
if (cup->cachedlevels[cup->numlevels] == NEXTMAP_INVALID)
|
|
continue;
|
|
invalidateacrosscups(cup->cachedlevels[cup->numlevels]);
|
|
}
|
|
|
|
tmp = strtok(word2,",");
|
|
do {
|
|
if (cup->numlevels >= MAXLEVELLIST)
|
|
{
|
|
deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST);
|
|
break;
|
|
}
|
|
|
|
cup->levellist[cup->numlevels] = Z_StrDup(tmp);
|
|
cup->cachedlevels[cup->numlevels] = NEXTMAP_INVALID;
|
|
cup->numlevels++;
|
|
} while((tmp = strtok(NULL,",")) != NULL);
|
|
}
|
|
else if (fastcmp(word, "BONUSGAME"))
|
|
{
|
|
while (cup->numbonus > 0)
|
|
{
|
|
cup->numbonus--;
|
|
Z_Free(cup->levellist[CUPCACHE_BONUS + cup->numbonus]);
|
|
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = NULL;
|
|
if (cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] == NEXTMAP_INVALID)
|
|
continue;
|
|
invalidateacrosscups(cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus]);
|
|
}
|
|
|
|
tmp = strtok(word2,",");
|
|
do {
|
|
if (cup->numbonus >= MAXBONUSLIST)
|
|
{
|
|
deh_warning("%s Cup: reached max bonus list (%d)\n", cup->name, MAXBONUSLIST);
|
|
break;
|
|
}
|
|
|
|
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = Z_StrDup(tmp);
|
|
cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] = NEXTMAP_INVALID;
|
|
cup->numbonus++;
|
|
} while((tmp = strtok(NULL,",")) != NULL);
|
|
}
|
|
else if (fastcmp(word, "SPECIALSTAGE"))
|
|
{
|
|
invalidateacrosscups(cup->cachedlevels[CUPCACHE_SPECIAL]);
|
|
Z_Free(cup->levellist[CUPCACHE_SPECIAL]);
|
|
cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2);
|
|
cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID;
|
|
}
|
|
else if (fastcmp(word, "EMERALDNUM"))
|
|
{
|
|
if (i >= 0 && i <= 14)
|
|
cup->emeraldnum = (UINT8)i;
|
|
else
|
|
deh_warning("%s Cup: invalid emerald number %d", cup->name, i);
|
|
}
|
|
else
|
|
deh_warning("%s Cup: unknown word '%s'", cup->name, word);
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readfollower(MYFILE *f)
|
|
{
|
|
char *s;
|
|
char *word, *word2;
|
|
char *tmp;
|
|
char testname[SKINNAMESIZE+1];
|
|
|
|
boolean nameset;
|
|
INT32 fallbackstate = 0;
|
|
INT32 res;
|
|
INT32 i;
|
|
|
|
if (numfollowers >= MAXSKINS)
|
|
{
|
|
I_Error("Out of Followers\nLoad less addons to fix this.");
|
|
}
|
|
|
|
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
|
|
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
|
|
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
|
|
followers[numfollowers].scale = FRACUNIT;
|
|
followers[numfollowers].bubblescale = 0; // No bubble by default
|
|
followers[numfollowers].atangle = FixedAngle(230 * FRACUNIT);
|
|
followers[numfollowers].dist = 32*FRACUNIT; // changed from 16 to 32 to better account for ogl models
|
|
followers[numfollowers].height = 16*FRACUNIT;
|
|
followers[numfollowers].zoffs = 32*FRACUNIT;
|
|
followers[numfollowers].horzlag = 3*FRACUNIT;
|
|
followers[numfollowers].vertlag = 6*FRACUNIT;
|
|
followers[numfollowers].anglelag = 8*FRACUNIT;
|
|
followers[numfollowers].bobspeed = TICRATE*2;
|
|
followers[numfollowers].bobamp = 4*FRACUNIT;
|
|
followers[numfollowers].hitconfirmtime = TICRATE;
|
|
followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
|
|
followers[numfollowers].category = UINT8_MAX;
|
|
strcpy(followers[numfollowers].icon, "MISSING");
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
|
|
if (!word2)
|
|
break;
|
|
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
|
|
if (fastcmp(word, "NAME"))
|
|
{
|
|
strlcpy(followers[numfollowers].name, word2, SKINNAMESIZE+1);
|
|
nameset = true;
|
|
}
|
|
else if (fastcmp(word, "ICON"))
|
|
{
|
|
strlcpy(followers[numfollowers].icon, word2, 8+1);
|
|
nameset = true;
|
|
}
|
|
else if (fastcmp(word, "CATEGORY"))
|
|
{
|
|
INT32 j;
|
|
for (j = 0; j < numfollowercategories; j++)
|
|
{
|
|
if (!stricmp(followercategories[j].name, word2))
|
|
{
|
|
followers[numfollowers].category = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == numfollowercategories)
|
|
{
|
|
deh_warning("Follower %d: unknown follower category '%s'", numfollowers, word2);
|
|
}
|
|
}
|
|
else if (fastcmp(word, "MODE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
|
|
if (fastcmp(word2, "FLOAT") || fastcmp(word2, "DEFAULT"))
|
|
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
|
|
else if (fastcmp(word2, "GROUND"))
|
|
followers[numfollowers].mode = FOLLOWERMODE_GROUND;
|
|
else
|
|
deh_warning("Follower %d: unknown follower mode '%s'", numfollowers, word2);
|
|
}
|
|
else if (fastcmp(word, "DEFAULTCOLOR"))
|
|
{
|
|
INT32 j;
|
|
for (j = 0; j < numskincolors +2; j++) // +2 because of Match and Opposite
|
|
{
|
|
if (!stricmp(Followercolor_cons_t[j].strvalue, word2))
|
|
{
|
|
followers[numfollowers].defaultcolor = Followercolor_cons_t[j].value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == numskincolors+2)
|
|
{
|
|
deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2);
|
|
}
|
|
}
|
|
else if (fastcmp(word, "SCALE"))
|
|
{
|
|
followers[numfollowers].scale = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "BUBBLESCALE"))
|
|
{
|
|
followers[numfollowers].bubblescale = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ATANGLE"))
|
|
{
|
|
followers[numfollowers].atangle = (angle_t)(get_number(word2) * ANG1);
|
|
}
|
|
else if (fastcmp(word, "HORZLAG"))
|
|
{
|
|
followers[numfollowers].horzlag = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "VERTLAG"))
|
|
{
|
|
followers[numfollowers].vertlag = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ANGLELAG"))
|
|
{
|
|
followers[numfollowers].anglelag = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "BOBSPEED"))
|
|
{
|
|
followers[numfollowers].bobspeed = (tic_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "BOBAMP"))
|
|
{
|
|
followers[numfollowers].bobamp = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS")))
|
|
{
|
|
followers[numfollowers].zoffs = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "DISTANCE") || (fastcmp(word, "DIST")))
|
|
{
|
|
followers[numfollowers].dist = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "HEIGHT"))
|
|
{
|
|
followers[numfollowers].height = (fixed_t)get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "IDLESTATE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].idlestate = get_number(word2);
|
|
fallbackstate = followers[numfollowers].idlestate;
|
|
}
|
|
else if (fastcmp(word, "FOLLOWSTATE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].followstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "HURTSTATE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].hurtstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "LOSESTATE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].losestate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "WINSTATE"))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].winstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "HITSTATE") || (fastcmp(word, "HITCONFIRMSTATE")))
|
|
{
|
|
if (word2)
|
|
strupr(word2);
|
|
followers[numfollowers].hitconfirmstate = get_number(word2);
|
|
}
|
|
else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME")))
|
|
{
|
|
followers[numfollowers].hitconfirmtime = (tic_t)get_number(word2);
|
|
}
|
|
else
|
|
{
|
|
deh_warning("Follower %d: unknown word '%s'", numfollowers, word);
|
|
}
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
if (!nameset)
|
|
{
|
|
// well this is problematic.
|
|
strlcpy(followers[numfollowers].name, va("Follower%d", numfollowers), SKINNAMESIZE+1);
|
|
strcpy(testname, followers[numfollowers].name);
|
|
}
|
|
else
|
|
{
|
|
strcpy(testname, followers[numfollowers].name);
|
|
|
|
// now that the skin name is ready, post process the actual name to turn the underscores into spaces!
|
|
for (i = 0; followers[numfollowers].name[i]; i++)
|
|
{
|
|
if (followers[numfollowers].name[i] == '_')
|
|
followers[numfollowers].name[i] = ' ';
|
|
}
|
|
|
|
res = K_FollowerAvailable(followers[numfollowers].name);
|
|
if (res > -1) // yikes, someone else has stolen our name already
|
|
{
|
|
deh_warning("Follower%d: Name \"%s\" already in use!", numfollowers, testname);
|
|
strlcpy(followers[numfollowers].name, va("Follower%d", numfollowers), SKINNAMESIZE+1);
|
|
}
|
|
}
|
|
|
|
// fallbacks for variables
|
|
// Print a warning if the variable is on a weird value and set it back to the minimum available if that's the case.
|
|
|
|
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
|
|
{
|
|
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
|
|
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", testname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
|
|
}
|
|
|
|
#define FALLBACK(field, field2, threshold, set) \
|
|
if ((signed)followers[numfollowers].field < threshold) \
|
|
{ \
|
|
followers[numfollowers].field = set; \
|
|
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", testname, field2, threshold, set); \
|
|
} \
|
|
|
|
FALLBACK(dist, "DIST", 0, 0);
|
|
FALLBACK(height, "HEIGHT", 1, 1);
|
|
FALLBACK(zoffs, "ZOFFS", 0, 0);
|
|
FALLBACK(horzlag, "HORZLAG", FRACUNIT, FRACUNIT);
|
|
FALLBACK(vertlag, "VERTLAG", FRACUNIT, FRACUNIT);
|
|
FALLBACK(anglelag, "ANGLELAG", FRACUNIT, FRACUNIT);
|
|
FALLBACK(bobamp, "BOBAMP", 0, 0);
|
|
FALLBACK(bobspeed, "BOBSPEED", 0, 0);
|
|
FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1);
|
|
FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale
|
|
FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale
|
|
|
|
#undef FALLBACK
|
|
|
|
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
|
|
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.
|
|
|
|
#define NOSTATE(field, field2) \
|
|
if (!followers[numfollowers].field) \
|
|
{ \
|
|
followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \
|
|
if (!fallbackstate) \
|
|
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", testname, field2); \
|
|
} \
|
|
|
|
NOSTATE(idlestate, "IDLESTATE");
|
|
NOSTATE(followstate, "FOLLOWSTATE");
|
|
NOSTATE(hurtstate, "HURTSTATE");
|
|
NOSTATE(losestate, "LOSESTATE");
|
|
NOSTATE(winstate, "WINSTATE");
|
|
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
|
|
#undef NOSTATE
|
|
|
|
CONS_Printf("Added follower '%s'\n", testname);
|
|
if (followers[numfollowers].category < numfollowercategories)
|
|
followercategories[followers[numfollowers].category].numincategory++;
|
|
numfollowers++; // add 1 follower
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readfollowercategory(MYFILE *f)
|
|
{
|
|
char *s;
|
|
char *word, *word2;
|
|
char *tmp;
|
|
|
|
boolean nameset;
|
|
|
|
if (numfollowercategories == MAXFOLLOWERCATEGORIES)
|
|
{
|
|
I_Error("Out of Follower categories\nLoad less addons to fix this.");
|
|
}
|
|
|
|
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
|
|
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
|
|
strcpy(followercategories[numfollowercategories].icon, "MISSING");
|
|
followercategories[numfollowercategories].numincategory = 0;
|
|
|
|
do
|
|
{
|
|
if (myfgets(s, MAXLINELEN, f))
|
|
{
|
|
if (s[0] == '\n')
|
|
break;
|
|
|
|
tmp = strchr(s, '#');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
if (s == tmp)
|
|
continue; // Skip comment lines, but don't break.
|
|
|
|
word = strtok(s, " ");
|
|
if (word)
|
|
strupr(word);
|
|
else
|
|
break;
|
|
|
|
word2 = strtok(NULL, " = ");
|
|
|
|
if (!word2)
|
|
break;
|
|
|
|
if (word2[strlen(word2)-1] == '\n')
|
|
word2[strlen(word2)-1] = '\0';
|
|
|
|
if (fastcmp(word, "NAME"))
|
|
{
|
|
strcpy(followercategories[numfollowercategories].name, word2);
|
|
nameset = true;
|
|
}
|
|
else if (fastcmp(word, "ICON"))
|
|
{
|
|
strcpy(followercategories[numfollowercategories].icon, word2);
|
|
nameset = true;
|
|
}
|
|
}
|
|
} while (!myfeof(f)); // finish when the line is empty
|
|
|
|
if (!nameset)
|
|
{
|
|
// well this is problematic.
|
|
strcpy(followercategories[numfollowercategories].name, va("Followercategory%d", numfollowercategories)); // this is lazy, so what
|
|
}
|
|
|
|
CONS_Printf("Added follower category '%s'\n", followercategories[numfollowercategories].name);
|
|
numfollowercategories++; // add 1 follower
|
|
Z_Free(s);
|
|
}
|
|
|
|
void readweather(MYFILE *f, INT32 num)
|
|
{
|
|
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
|
char *word;
|
|
char *word2;
|
|
char *tmp;
|
|
|
|
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.
|
|
|
|
// Set / reset word
|
|
word = s;
|
|
|
|
// 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;
|
|
|
|
if (fastcmp(word, "TYPE"))
|
|
{
|
|
precipprops[num].type = get_mobjtype(word2);
|
|
}
|
|
else if (fastcmp(word, "EFFECTS"))
|
|
{
|
|
precipprops[num].effects = get_number(word2);
|
|
}
|
|
else
|
|
deh_warning("Weather %d : unknown word '%s'", num, word);
|
|
}
|
|
} while (!myfeof(f));
|
|
|
|
Z_Free(s);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
mobjtype_t get_mobjtype(const char *word)
|
|
{ // Returns the value of MT_ enumerations
|
|
mobjtype_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("MT_",word,3))
|
|
word += 3; // take off the MT_
|
|
for (i = 0; i < NUMMOBJFREESLOTS; i++) {
|
|
if (!FREE_MOBJS[i])
|
|
break;
|
|
if (fastcmp(word, FREE_MOBJS[i]))
|
|
return MT_FIRSTFREESLOT+i;
|
|
}
|
|
for (i = 0; i < MT_FIRSTFREESLOT; i++)
|
|
if (fastcmp(word, MOBJTYPE_LIST[i]+3))
|
|
return i;
|
|
deh_warning("Couldn't find mobjtype named 'MT_%s'",word);
|
|
return MT_NULL;
|
|
}
|
|
|
|
statenum_t get_state(const char *word)
|
|
{ // Returns the value of S_ enumerations
|
|
statenum_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("S_",word,2))
|
|
word += 2; // take off the S_
|
|
for (i = 0; i < NUMSTATEFREESLOTS; i++) {
|
|
if (!FREE_STATES[i])
|
|
break;
|
|
if (fastcmp(word, FREE_STATES[i]))
|
|
return S_FIRSTFREESLOT+i;
|
|
}
|
|
for (i = 0; i < S_FIRSTFREESLOT; i++)
|
|
if (fastcmp(word, STATE_LIST[i]+2))
|
|
return i;
|
|
deh_warning("Couldn't find state named 'S_%s'",word);
|
|
return S_NULL;
|
|
}
|
|
|
|
skincolornum_t get_skincolor(const char *word)
|
|
{ // Returns the value of SKINCOLOR_ enumerations
|
|
skincolornum_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("SKINCOLOR_",word,10))
|
|
word += 10; // take off the SKINCOLOR_
|
|
for (i = 0; i < NUMCOLORFREESLOTS; i++) {
|
|
if (!FREE_SKINCOLORS[i])
|
|
break;
|
|
if (fastcmp(word, FREE_SKINCOLORS[i]))
|
|
return SKINCOLOR_FIRSTFREESLOT+i;
|
|
}
|
|
for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++)
|
|
if (fastcmp(word, COLOR_ENUMS[i]))
|
|
return i;
|
|
deh_warning("Couldn't find skincolor named 'SKINCOLOR_%s'",word);
|
|
return SKINCOLOR_GREEN;
|
|
}
|
|
|
|
spritenum_t get_sprite(const char *word)
|
|
{ // Returns the value of SPR_ enumerations
|
|
spritenum_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("SPR_",word,4))
|
|
word += 4; // take off the SPR_
|
|
for (i = 0; i < NUMSPRITES; i++)
|
|
if (!sprnames[i][4] && memcmp(word,sprnames[i],4)==0)
|
|
return i;
|
|
deh_warning("Couldn't find sprite named 'SPR_%s'",word);
|
|
return SPR_NULL;
|
|
}
|
|
|
|
playersprite_t get_sprite2(const char *word)
|
|
{ // Returns the value of SPR2_ enumerations
|
|
playersprite_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("SPR2_",word,5))
|
|
word += 5; // take off the SPR2_
|
|
for (i = 0; i < NUMPLAYERSPRITES; i++)
|
|
if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0)
|
|
return i;
|
|
deh_warning("Couldn't find sprite named 'SPR2_%s'",word);
|
|
return SPR2_STIN;
|
|
}
|
|
|
|
sfxenum_t get_sfx(const char *word)
|
|
{ // Returns the value of SFX_ enumerations
|
|
sfxenum_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("SFX_",word,4))
|
|
word += 4; // take off the SFX_
|
|
else if (fastncmp("DS",word,2))
|
|
word += 2; // take off the DS
|
|
for (i = 0; i < NUMSFX; i++)
|
|
if (S_sfx[i].name && fasticmp(word, S_sfx[i].name))
|
|
return i;
|
|
deh_warning("Couldn't find sfx named 'SFX_%s'",word);
|
|
return sfx_None;
|
|
}
|
|
|
|
/*static INT16 get_gametype(const char *word)
|
|
{ // Returns the value of GT_ enumerations
|
|
INT16 i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("GT_",word,3))
|
|
word += 3; // take off the GT_
|
|
for (i = 0; i < MAXGAMETYPES; i++)
|
|
if (fastcmp(word, Gametype_ConstantNames[i]+3))
|
|
return i;
|
|
deh_warning("Couldn't find gametype named 'GT_%s'",word);
|
|
return GT_COOP;
|
|
}*/
|
|
|
|
preciptype_t get_precip(const char *word)
|
|
{ // Returns the value of PRECIP_ enumerations
|
|
preciptype_t i;
|
|
if (*word >= '0' && *word <= '9')
|
|
return atoi(word);
|
|
if (fastncmp("PRECIP_",word,4))
|
|
word += 7; // take off the PRECIP_
|
|
for (i = 0; i < MAXPRECIP; i++)
|
|
{
|
|
if (precipprops[i].name == NULL)
|
|
break;
|
|
if (fasticmp(word, precipprops[i].name))
|
|
return i;
|
|
}
|
|
deh_warning("Couldn't find weather type named 'PRECIP_%s'",word);
|
|
return PRECIP_RAIN;
|
|
}
|
|
|
|
/// \todo Make ANY of this completely over-the-top math craziness obey the order of operations.
|
|
static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; }
|
|
static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; }
|
|
static fixed_t op_add(fixed_t a, fixed_t b) { return a+b; }
|
|
static fixed_t op_sub(fixed_t a, fixed_t b) { return a-b; }
|
|
static fixed_t op_or(fixed_t a, fixed_t b) { return a|b; }
|
|
static fixed_t op_and(fixed_t a, fixed_t b) { return a&b; }
|
|
static fixed_t op_lshift(fixed_t a, fixed_t b) { return a<<b; }
|
|
static fixed_t op_rshift(fixed_t a, fixed_t b) { return a>>b; }
|
|
|
|
struct {
|
|
const char c;
|
|
fixed_t (*v)(fixed_t,fixed_t);
|
|
} OPERATIONS[] = {
|
|
{'*',op_mul},
|
|
{'/',op_div},
|
|
{'+',op_add},
|
|
{'-',op_sub},
|
|
{'|',op_or},
|
|
{'&',op_and},
|
|
{'<',op_lshift},
|
|
{'>',op_rshift},
|
|
{0,NULL}
|
|
};
|
|
|
|
// Returns the full word, cut at the first symbol or whitespace
|
|
/*static char *read_word(const char *line)
|
|
{
|
|
// Part 1: You got the start of the word, now find the end.
|
|
const char *p;
|
|
INT32 i;
|
|
for (p = line+1; *p; p++) {
|
|
if (*p == ' ' || *p == '\t')
|
|
break;
|
|
for (i = 0; OPERATIONS[i].c; i++)
|
|
if (*p == OPERATIONS[i].c) {
|
|
i = -1;
|
|
break;
|
|
}
|
|
if (i == -1)
|
|
break;
|
|
}
|
|
|
|
// Part 2: Make a copy of the word and return it.
|
|
{
|
|
size_t len = (p-line);
|
|
char *word = malloc(len+1);
|
|
M_Memcpy(word,line,len);
|
|
word[len] = '\0';
|
|
return word;
|
|
}
|
|
}
|
|
|
|
static INT32 operation_pad(const char **word)
|
|
{ // Brings word the next operation and returns the operation number.
|
|
INT32 i;
|
|
for (; **word; (*word)++) {
|
|
if (**word == ' ' || **word == '\t')
|
|
continue;
|
|
for (i = 0; OPERATIONS[i].c; i++)
|
|
if (**word == OPERATIONS[i].c)
|
|
{
|
|
if ((**word == '<' && *(*word+1) == '<') || (**word == '>' && *(*word+1) == '>')) (*word)++; // These operations are two characters long.
|
|
else if (**word == '<' || **word == '>') continue; // ... do not accept one character long.
|
|
(*word)++;
|
|
return i;
|
|
}
|
|
deh_warning("Unknown operation '%c'",**word);
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void const_warning(const char *type, const char *word)
|
|
{
|
|
deh_warning("Couldn't find %s named '%s'",type,word);
|
|
}
|
|
|
|
static fixed_t find_const(const char **rword)
|
|
{ // Finds the value of constants and returns it, bringing word to the next operation.
|
|
INT32 i;
|
|
fixed_t r;
|
|
char *word = read_word(*rword);
|
|
*rword += strlen(word);
|
|
if ((*word >= '0' && *word <= '9') || *word == '-') { // Parse a number
|
|
r = atoi(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
if (!*(word+1) && // Turn a single A-z symbol into numbers, like sprite frames.
|
|
((*word >= 'A' && *word <= 'Z') || (*word >= 'a' && *word <= 'z'))) {
|
|
r = R_Char2Frame(*word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
if (fastncmp("MF_", word, 3)) {
|
|
char *p = word+3;
|
|
for (i = 0; MOBJFLAG_LIST[i]; i++)
|
|
if (fastcmp(p, MOBJFLAG_LIST[i])) {
|
|
free(word);
|
|
return (1<<i);
|
|
}
|
|
|
|
// Not found error
|
|
const_warning("mobj flag",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
else if (fastncmp("MF2_", word, 4)) {
|
|
char *p = word+4;
|
|
for (i = 0; MOBJFLAG2_LIST[i]; i++)
|
|
if (fastcmp(p, MOBJFLAG2_LIST[i])) {
|
|
free(word);
|
|
return (1<<i);
|
|
}
|
|
|
|
// Not found error
|
|
const_warning("mobj flag2",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
else if (fastncmp("MFE_", word, 4)) {
|
|
char *p = word+4;
|
|
for (i = 0; MOBJEFLAG_LIST[i]; i++)
|
|
if (fastcmp(p, MOBJEFLAG_LIST[i])) {
|
|
free(word);
|
|
return (1<<i);
|
|
}
|
|
|
|
// Not found error
|
|
const_warning("mobj eflag",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
else if (fastncmp("PF_", word, 3)) {
|
|
char *p = word+3;
|
|
for (i = 0; PLAYERFLAG_LIST[i]; i++)
|
|
if (fastcmp(p, PLAYERFLAG_LIST[i])) {
|
|
free(word);
|
|
return (1<<i);
|
|
}
|
|
if (fastcmp(p, "FULLSTASIS"))
|
|
return PF_FULLSTASIS;
|
|
|
|
// Not found error
|
|
const_warning("player flag",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
else if (fastncmp("S_",word,2)) {
|
|
r = get_state(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("SKINCOLOR_",word,10)) {
|
|
r = get_skincolor(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("MT_",word,3)) {
|
|
r = get_mobjtype(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("SPR_",word,4)) {
|
|
r = get_sprite(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("SFX_",word,4) || fastncmp("DS",word,2)) {
|
|
r = get_sfx(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("PW_",word,3)) {
|
|
r = get_power(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("MN_",word,3)) {
|
|
r = get_menutype(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("GT_",word,3)) {
|
|
r = get_gametype(word);
|
|
free(word);
|
|
return r;
|
|
}
|
|
else if (fastncmp("GTR_", word, 4)) {
|
|
char *p = word+4;
|
|
for (i = 0; GAMETYPERULE_LIST[i]; i++)
|
|
if (fastcmp(p, GAMETYPERULE_LIST[i])) {
|
|
free(word);
|
|
return (1<<i);
|
|
}
|
|
|
|
// Not found error
|
|
const_warning("game type rule",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
else if (fastncmp("TOL_", word, 4)) {
|
|
char *p = word+4;
|
|
for (i = 0; TYPEOFLEVEL[i].name; i++)
|
|
if (fastcmp(p, TYPEOFLEVEL[i].name)) {
|
|
free(word);
|
|
return TYPEOFLEVEL[i].flag;
|
|
}
|
|
|
|
// Not found error
|
|
const_warning("typeoflevel",word);
|
|
free(word);
|
|
return 0;
|
|
}
|
|
for (i = 0; INT_CONST[i].n; i++)
|
|
if (fastcmp(word,INT_CONST[i].n)) {
|
|
free(word);
|
|
return INT_CONST[i].v;
|
|
}
|
|
|
|
// Not found error.
|
|
const_warning("constant",word);
|
|
free(word);
|
|
return 0;
|
|
}*/
|