From 704015deac09a9d874dc5a573b09e813860b7a9e Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 27 Sep 2023 19:44:51 +0200 Subject: [PATCH] port: modconfig: add user defined extended weather config includes a way to specify weatherproof rooms and other hardcoded stuff this might break something, as it removes some hardcoded checks --- port/src/mod.c | 276 ++++++++++++++++++++++++++++++---------- src/game/bg.c | 3 + src/game/weather.c | 221 +++++++++++++++++++++++++++++++- src/game/weatherreset.c | 51 +++++++- src/include/constants.h | 10 ++ src/include/data.h | 4 + src/include/types.h | 21 +++ 7 files changed, 517 insertions(+), 69 deletions(-) diff --git a/port/src/mod.c b/port/src/mod.c index 7d536bf35..01aeb385c 100644 --- a/port/src/mod.c +++ b/port/src/mod.c @@ -13,6 +13,35 @@ extern struct stagemusic g_StageTracks[]; extern struct stageallocation g_StageAllocations8Mb[]; +#define PARSE_STAGE_FLOAT(sec, name, v, min, max) \ + p = modConfigParseFloatValue(p, token, &v); \ + if (!p || v < (min) || v > (max)) { \ + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \ + return NULL; \ + } + +#define PARSE_STAGE_INT(sec, name, v, min, max) \ + p = modConfigParseIntValue(p, token, &v); \ + if (!p || v < (min) || v > (max)) { \ + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \ + return NULL; \ + } + +#define PARSE_STAGE_FILENAME(sec, name, v) \ + p = modConfigParseFileValue(p, token, &v); \ + if (!p) { \ + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \ + return NULL; \ + } + +#define PARSE_STAGE_STRING(sec, name, v) \ + p = strParseToken(p, token, NULL); \ + if (!p) { \ + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \ + return NULL; \ + } \ + v = strUnquote(token); + static inline char *modConfigParseFileValue(char *p, char *token, s32 *filenum) { p = strParseToken(p, token, NULL); @@ -35,7 +64,7 @@ static inline char *modConfigParseFileValue(char *p, char *token, s32 *filenum) return NULL; } -static inline char *modConfigParseNumericValue(char *p, char *token, s32 *out) +static inline char *modConfigParseIntValue(char *p, char *token, s32 *out) { p = strParseToken(p, token, NULL); if (!token[0]) { @@ -50,7 +79,22 @@ static inline char *modConfigParseNumericValue(char *p, char *token, s32 *out) return p; } -static inline char *modConfigParseStageMusic(char *p, char *token, s32 stagenum) +static inline char *modConfigParseFloatValue(char *p, char *token, f32 *out) +{ + p = strParseToken(p, token, NULL); + if (!token[0]) { + return NULL; // empty + } + char *endp = token; + const f32 num = strtof(token, &endp); + if (num == 0.f && (endp == token || *endp != '\0')) { + return NULL; + } + *out = num; + return p; +} + +static char *modConfigParseStageMusic(char *p, char *token, s32 stagenum) { struct stagemusic *smus = NULL; for (struct stagemusic *p = g_StageTracks; p->stagenum; ++p) { @@ -76,25 +120,13 @@ static inline char *modConfigParseStageMusic(char *p, char *token, s32 stagenum) p = strParseToken(p, token, NULL); while (p && token[0] && strcmp(token, "}") != 0) { if (!strcmp(token, "primarytrack")) { - p = modConfigParseNumericValue(p, token, &tmp); - if (!p || tmp < 0 || tmp > 128) { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music: invalid primarytrack value: %s", stagenum, token); - return NULL; - } + PARSE_STAGE_INT("music:", "primarytrack", tmp, 0, 128); smus->primarytrack = tmp; } else if (!strcmp(token, "ambienttrack")) { - p = modConfigParseNumericValue(p, token, &tmp); - if (!p || tmp < 0 || tmp > 128) { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music: invalid ambienttrack value: %s", stagenum, token); - return NULL; - } + PARSE_STAGE_INT("music:", "ambienttrack", tmp, 0, 128); smus->ambienttrack = tmp; } else if (!strcmp(token, "xtrack")) { - p = modConfigParseNumericValue(p, token, &tmp); - if (!p || tmp < 0 || tmp > 128) { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music: invalid xtrack value: %s", stagenum, token); - return NULL; - } + PARSE_STAGE_INT("music:", "xtrack", tmp, 0, 128); smus->xtrack = tmp; } else { sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music: invalid key: %s", stagenum, token); @@ -111,7 +143,136 @@ static inline char *modConfigParseStageMusic(char *p, char *token, s32 stagenum) return p; } -static inline char *modConfigParseStage(char *p, char *token) +static char *modConfigParseStageWeatherRooms(char *p, char *token, s32 stagenum, struct weathercfg *wcfg) +{ + // determine where we can start adding rooms + s32 idx; + for (idx = 0; idx < WEATHERCFG_MAX_SKIPROOMS && wcfg->skiprooms[idx]; ++idx); + + // eat opening bracket + p = strParseToken(p, token, NULL); + if (token[0] != '{' || token[1] != '\0') { + return NULL; + } + + // check if user wants to clear the whole list + p = strParseToken(p, token, NULL); + if (!strcmp(token, "clear")) { + memset(wcfg->skiprooms, 0, sizeof(wcfg->skiprooms)); + idx = 0; + p = strParseToken(p, token, NULL); + } + + s32 tmp = 0; + while (p && token[0] && strcmp(token, "}") != 0) { + if (token[0] == ',' && !token[1]) { + p = strParseToken(p, token, NULL); + continue; + } + + tmp = strtol(token, NULL, 0); + if (tmp <= 0 || tmp > 32767) { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: rooms: invalid room %s", stagenum, token); + return NULL; + } + + if (idx < WEATHERCFG_MAX_SKIPROOMS) { + wcfg->skiprooms[idx++] = tmp; + } + + p = strParseToken(p, token, NULL); + } + + if (token[0] != '}') { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: unterminated rooms block", stagenum); + return NULL; + } + + return p; +} + +static char *modConfigParseStageWeather(char *p, char *token, s32 stagenum) +{ + s32 wi; + struct weathercfg *wcfg = NULL; + for (wi = 0; wi < ARRAYCOUNT(g_WeatherConfig) && g_WeatherConfig[wi].stagenum; ++wi) { + if (g_WeatherConfig[wi].stagenum == stagenum) { + break; + } + } + + if (wi >= WEATHERCFG_MAX_STAGES) { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: no more space for weather config", stagenum); + return NULL; + } + + wcfg = &g_WeatherConfig[wi]; + + if (!wcfg->stagenum) { + // new weather config; initialize with defaults + *wcfg = g_DefaultWeatherConfig; + wcfg->stagenum = stagenum; + } else { + // flags have to be re-specified + wcfg->flags = 0; + } + + // eat opening bracket + p = strParseToken(p, token, NULL); + if (token[0] != '{' || token[1] != '\0') { + return NULL; + } + + // parse keyvalues until } is reached + s32 tmpi = 0; + f32 tmpf = 0.f; + p = strParseToken(p, token, NULL); + while (p && token[0] && strcmp(token, "}") != 0) { + if (!strcmp(token, "include_rooms") || !strcmp(token, "exclude_rooms")) { + // include_rooms | exclude_rooms { ROOM_NUMBERS... } + const s32 include = (token[0] == 'i'); + p = modConfigParseStageWeatherRooms(p, token, stagenum, wcfg); + if (!p) { + return NULL; + } + if (wcfg->skiprooms[0] && include) { + wcfg->flags |= WEATHERFLAG_INCLUDE; + } + } else if (!strcmp(token, "cutscene_only")) { + wcfg->flags |= WEATHERFLAG_CUTSCENE_ONLY; + } else if (!strcmp(token, "constant_wind")) { + PARSE_STAGE_FLOAT("weather:", "constant_wind (0)", wcfg->windanglerad, -M_TAU, M_TAU); + PARSE_STAGE_FLOAT("weather:", "constant_wind (1)", wcfg->windspeedx, -1024.f, 1024.f); + PARSE_STAGE_FLOAT("weather:", "constant_wind (2)", wcfg->windspeedz, -1024.f, 1024.f); + wcfg->flags |= WEATHERFLAG_FORCE_WINDDIR; + } else if (!strcmp(token, "windspeed")) { + PARSE_STAGE_FLOAT("weather:", "windspeed", tmpf, -1024.f, 1024.f); + wcfg->windspeed = tmpf; + } else if (!strcmp(token, "ymin")) { + PARSE_STAGE_FLOAT("weather:", "ymin", tmpf, -65536.f, 65536.f); + wcfg->ymin = tmpf; + } else if (!strcmp(token, "ymax")) { + PARSE_STAGE_FLOAT("weather:", "ymax", tmpf, -65536.f, 65536.f); + wcfg->ymax = tmpf; + } else if (!strcmp(token, "zmax")) { + PARSE_STAGE_FLOAT("weather:", "zmax", tmpf, -65536.f, 65536.f); + wcfg->zmax = tmpf; + } else { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: invalid key: %s", stagenum, token); + return NULL; + } + p = strParseToken(p, token, NULL); + } + + if (token[0] != '}') { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: unterminated weather block", stagenum); + return NULL; + } + + return p; +} + +static char *modConfigParseStage(char *p, char *token) { // stage number p = strParseToken(p, token, NULL); @@ -132,6 +293,9 @@ static inline char *modConfigParseStage(char *p, char *token) const s32 sidx = stageGetIndex(stagenum); if (sidx >= 0) { stab = &g_Stages[sidx]; + } else { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: unknown stage number", stagenum); + return NULL; } for (struct stageallocation *p = g_StageAllocations8Mb; p->stagenum; ++p) { if (p->stagenum == stagenum) { @@ -142,65 +306,36 @@ static inline char *modConfigParseStage(char *p, char *token) // parse keyvalues until } is reached s32 tmp = 0; + char *tmps = NULL; p = strParseToken(p, token, NULL); while (p && token[0] && strcmp(token, "}") != 0) { if (!strcmp(token, "bgfile")) { // bg FILE_NAME_OR_NUM - p = modConfigParseFileValue(p, token, &tmp); - if (p && stab) { - stab->bgfileid = tmp; - } else { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid bgfile value: %s", stagenum, token); - return NULL; - } + PARSE_STAGE_FILENAME("", "bgfile", tmp); + stab->bgfileid = tmp; } else if (!strcmp(token, "tilesfile")) { - // tiles FILE_NAME_OR_NUM - p = modConfigParseFileValue(p, token, &tmp); - if (p && stab) { - stab->tilefileid = tmp; - } else { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid tilesfile value: %s", stagenum, token); - return NULL; - } + // tilesfile FILE_NAME_OR_NUM + PARSE_STAGE_FILENAME("", "tilesfile", tmp); + stab->tilefileid = tmp; } else if (!strcmp(token, "padsfile")) { - // tiles FILE_NAME_OR_NUM - p = modConfigParseFileValue(p, token, &tmp); - if (p && stab) { - stab->padsfileid = tmp; - } else { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid padsfile value: %s", stagenum, token); - return NULL; - } + // padsfile FILE_NAME_OR_NUM + PARSE_STAGE_FILENAME("", "padsfile", tmp); + stab->padsfileid = tmp; } else if (!strcmp(token, "setupfile")) { - // setup FILE_NAME_OR_NUM - p = modConfigParseFileValue(p, token, &tmp); - if (p && stab) { - stab->setupfileid = tmp; - } else { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid setupfile value: %s", stagenum, token); - return NULL; - } + // setupfile FILE_NAME_OR_NUM + PARSE_STAGE_FILENAME("", "setupfile", tmp); + stab->setupfileid = tmp; } else if (!strcmp(token, "mpsetupfile")) { - // mpsetup FILE_NAME_OR_NUM - p = modConfigParseFileValue(p, token, &tmp); - if (p && stab) { - stab->mpsetupfileid = tmp; - } else { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid mpsetupfile value: %s", stagenum, token); - return NULL; - } + // mpsetupfile FILE_NAME_OR_NUM + PARSE_STAGE_FILENAME("", "mpsetupfile", tmp); + stab->mpsetupfileid = tmp; } else if (!strcmp(token, "allocation")) { // allocation "ALLOCSTRING" - p = strParseToken(p, token, NULL); - char *str = strUnquote(token); - if (!p || !str[0] || !salloc) { - sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid allocation value: %s", stagenum, token); - return NULL; - } + PARSE_STAGE_STRING("", "allocation", tmps); // FIXME: this leaks - str = strDuplicate(str); - if (str) { - salloc->string = str; + tmps = strDuplicate(tmps); + if (tmps) { + salloc->string = tmps; } } else if (!strcmp(token, "music")) { // music { KEYVALUES... } @@ -208,6 +343,15 @@ static inline char *modConfigParseStage(char *p, char *token) if (!p) { return NULL; } + } else if (!strcmp(token, "weather")) { + // weather { KEYVALUES... } + p = modConfigParseStageWeather(p, token, stagenum); + if (!p) { + return NULL; + } + } else { + sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid key: %s", stagenum, token); + return NULL; } p = strParseToken(p, token, NULL); } diff --git a/src/game/bg.c b/src/game/bg.c index 20219795a..aead192ca 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -1686,6 +1686,9 @@ void bgBuildTables(s32 stagenum) g_Rooms[i].unk4d = 0; g_Rooms[i].lightop = 0; g_Rooms[i].unk4e_04 = 0; +#ifndef PLATFORM_N64 + g_Rooms[i].extra_flags = 0; +#endif } bgSetStageTranslationThing(g_Stages[g_StageIndex].unk14); diff --git a/src/game/weather.c b/src/game/weather.c index 800d1d090..9a95769d6 100644 --- a/src/game/weather.c +++ b/src/game/weather.c @@ -24,6 +24,179 @@ struct weatherdata *g_WeatherData = NULL; +#ifndef PLATFORM_N64 + +struct weathercfg g_WeatherConfig[WEATHERCFG_MAX_STAGES] = { + { + STAGE_G5BUILDING, + WEATHERFLAG_CUTSCENE_ONLY, + 20.f, + -800.f, 800.f, + 0.f, + }, + { + STAGE_CHICAGO, + 0, + 20.f, + -800.f, 800.f, + 0.f, + { + ROOM_PETE_0062, + ROOM_PETE_0061, + ROOM_PETE_000F, + ROOM_PETE_0052, + ROOM_PETE_0045, + ROOM_PETE_0044, + ROOM_PETE_0043, + ROOM_PETE_003D, + ROOM_PETE_000E, + ROOM_PETE_000D, + ROOM_PETE_000C, + ROOM_PETE_000B, + ROOM_PETE_000A, + ROOM_PETE_0009, + ROOM_PETE_0008, + ROOM_PETE_0007, + ROOM_PETE_0006, + ROOM_PETE_0005, + ROOM_PETE_0004, + ROOM_PETE_0037, + ROOM_PETE_005B, + ROOM_PETE_005F, + ROOM_PETE_0060, + ROOM_PETE_005D, + ROOM_PETE_001D, + ROOM_PETE_001F, + ROOM_PETE_0027, + ROOM_PETE_0050, + ROOM_PETE_002C, + ROOM_PETE_002F, + ROOM_PETE_0030, + ROOM_PETE_0011, + ROOM_PETE_0024, + ROOM_PETE_0033, + ROOM_PETE_0034, + ROOM_PETE_0035, + ROOM_PETE_0036, + ROOM_PETE_003C, + ROOM_PETE_0046, + ROOM_PETE_0047, + ROOM_PETE_0049, + ROOM_PETE_0055, + ROOM_PETE_0056, + ROOM_PETE_005E, + ROOM_PETE_0063, + ROOM_PETE_0069, + ROOM_PETE_0003, + ROOM_PETE_0016, + ROOM_PETE_004F, + ROOM_PETE_004B, + ROOM_PETE_003F, + ROOM_PETE_0013, + ROOM_PETE_0019, + 0 + } + }, + { + STAGE_AIRBASE, + WEATHERFLAG_INCLUDE | WEATHERFLAG_FORCE_WINDDIR, + 5.f, + -800.f, 800.f, + -2000.f, + { + ROOM_CAVE_0088, + ROOM_CAVE_0091, + ROOM_CAVE_0087, + ROOM_CAVE_0090, + ROOM_CAVE_0086, + ROOM_CAVE_0080, + ROOM_CAVE_0083, + ROOM_CAVE_0084, + ROOM_CAVE_0085, + ROOM_CAVE_007F, + ROOM_CAVE_007E, + ROOM_CAVE_0082, + ROOM_CAVE_008F, + ROOM_CAVE_008E, + ROOM_CAVE_007B, + ROOM_CAVE_007C, + ROOM_CAVE_007D, + ROOM_CAVE_0081, + ROOM_CAVE_006F, + 0 + }, + 1.5707963705063f, + 0.f, + -5.f + }, + { + STAGE_CRASHSITE, + 0, + 10.f, + -500.f, 500.f, + 0.f, + { + ROOM_AZT_001F, + ROOM_AZT_0020, + ROOM_AZT_0021, + ROOM_AZT_0022, + ROOM_AZT_0023, + ROOM_AZT_0024, + ROOM_AZT_0051, + ROOM_AZT_0052, + ROOM_AZT_0053, + ROOM_AZT_0054, + ROOM_AZT_0055, + ROOM_AZT_0056, + ROOM_AZT_0057, + ROOM_AZT_0058, + ROOM_AZT_0059, + ROOM_AZT_005A, + ROOM_AZT_005B, + ROOM_AZT_005C, + ROOM_AZT_005D, + ROOM_AZT_005E, + ROOM_AZT_005F, + ROOM_AZT_0060, + ROOM_AZT_0061, + ROOM_AZT_0062, + ROOM_AZT_0063, + ROOM_AZT_0064, + ROOM_AZT_002D, + ROOM_AZT_0040, + ROOM_AZT_0041, + ROOM_AZT_0042, + ROOM_AZT_0043, + ROOM_AZT_0044, + ROOM_AZT_0045, + ROOM_AZT_0046, + ROOM_AZT_0047, + ROOM_AZT_0048, + ROOM_AZT_0049, + ROOM_AZT_004A, + ROOM_AZT_004B, + ROOM_AZT_004C, + ROOM_AZT_004D, + ROOM_AZT_004E, + ROOM_AZT_004F, + ROOM_AZT_0050, + 0 + } + }, +}; + +const struct weathercfg g_DefaultWeatherConfig = { + 0, + 0, + 20.f, + -800.f, 800.f, + 0.f +}; + +const struct weathercfg *g_CurWeatherConfig = &g_DefaultWeatherConfig; + +#endif + Gfx *weatherRender(Gfx *gdl) { struct weatherdata *weather; @@ -32,6 +205,7 @@ Gfx *weatherRender(Gfx *gdl) return gdl; } +#ifdef PLATFORM_N64 if (g_StageIndex == STAGEINDEX_AIRBASE && g_Vars.currentplayer->cam_pos.z < -2000.0f) { return gdl; } @@ -39,6 +213,15 @@ Gfx *weatherRender(Gfx *gdl) if (g_StageIndex == STAGEINDEX_G5BUILDING && g_Vars.tickmode != TICKMODE_CUTSCENE) { return gdl; } +#else + if (g_CurWeatherConfig->zmax && g_Vars.currentplayer->cam_pos.z < g_CurWeatherConfig->zmax) { + return gdl; + } + + if ((g_CurWeatherConfig->flags & WEATHERFLAG_CUTSCENE_ONLY) && g_Vars.tickmode != TICKMODE_CUTSCENE) { + return gdl; + } +#endif weather = g_WeatherData; @@ -89,11 +272,15 @@ struct weatherparticledata *weatherAllocateParticles(void) weatherSetBoundaries(data, 0, -800, 800); +#ifdef PLATFORM_N64 if ((u32)g_StageIndex == STAGEINDEX_CRASHSITE) { weatherSetBoundaries(data, 1, -500, 500); } else { weatherSetBoundaries(data, 1, -800, 800); } +#else + weatherSetBoundaries(data, 1, g_CurWeatherConfig->ymin, g_CurWeatherConfig->ymax); +#endif weatherSetBoundaries(data, 2, -800, 800); @@ -340,12 +527,22 @@ void weatherTickRain(struct weatherdata *weather) } } +#ifdef PLATFORM_N64 if (g_StageIndex == STAGEINDEX_AIRBASE) { // Force weather direction - but Air Base doesn't use rain... weather->windanglerad = 1.5707963705063f; weather->windspeedz = -weather->windspeed; weather->windspeedx = 0; - } else if (weather->unk10 > 0) { + } +#else + if (g_CurWeatherConfig->flags & WEATHERFLAG_FORCE_WINDDIR) { + // force weather direction if requested + weather->windanglerad = g_CurWeatherConfig->windanglerad; + weather->windspeedz = g_CurWeatherConfig->windspeedz; + weather->windspeedx = g_CurWeatherConfig->windspeedx; + } +#endif + else if (weather->unk10 > 0) { s32 lvupdate = g_Vars.lvupdate60; if (weather->unk10 < lvupdate) { @@ -437,11 +634,21 @@ void weatherTickSnow(struct weatherdata *weather) mainOverrideVariable("snowspeed", &g_SnowSpeed); mainOverrideVariable("snowspeedxtra", &g_SnowSpeedExtra); +#ifdef PLATFORM_N64 if (g_StageIndex == STAGEINDEX_AIRBASE) { weather->windanglerad = 1.5707963705063f; weather->windspeedz = -weather->windspeed; weather->windspeedx = 0; - } else if (weather->unk10 > 0) { + } +#else + if (g_CurWeatherConfig->flags & WEATHERFLAG_FORCE_WINDDIR) { + // force weather direction if requested + weather->windanglerad = g_CurWeatherConfig->windanglerad; + weather->windspeedz = g_CurWeatherConfig->windspeedz; + weather->windspeedx = g_CurWeatherConfig->windspeedx; + } +#endif + else if (weather->unk10 > 0) { s32 lvupdate = g_Vars.lvupdate60; if (weather->unk10 < lvupdate) { @@ -618,6 +825,7 @@ void weatherConfigureSnow(u32 intensity) bool weatherIsRoomWeatherProof(s32 room) { +#ifdef PLATFORM_N64 if (g_StageIndex == STAGEINDEX_CHICAGO) { // Rooms listed do not have weather if (room == ROOM_PETE_0062 @@ -757,6 +965,15 @@ bool weatherIsRoomWeatherProof(s32 room) return false; } +#else + if (room >= 0 && room < g_Vars.roomcount) { + // check room's extra_flags + if (g_Rooms[room].extra_flags & ROOMFLAG_EX_WEATHERPROOF) { + // this room has no weather + return true; + } + } +#endif return false; } diff --git a/src/game/weatherreset.c b/src/game/weatherreset.c index 3bba7cc86..5c7c1683b 100644 --- a/src/game/weatherreset.c +++ b/src/game/weatherreset.c @@ -11,16 +11,59 @@ u32 var800623f4 = 0x00000000; u32 var800623f8 = 0x00000000; u32 var800623fc = 0x00000000; +#ifndef PLATFORM_N64 + +static void weatherResetRooms(void) +{ + if (g_CurWeatherConfig->flags & WEATHERFLAG_INCLUDE) { + // weather is present only in skiprooms, so mark every room as weatherproof + for (s32 i = 0; i < g_Vars.roomcount; ++i) { + g_Rooms[i].extra_flags |= ROOMFLAG_EX_WEATHERPROOF; + } + // then unmark skiprooms + for (s32 i = 0; i < WEATHERCFG_MAX_SKIPROOMS && g_CurWeatherConfig->skiprooms[i]; ++i) { + const RoomNum room = g_CurWeatherConfig->skiprooms[i]; + if (room >= 0 && room < g_Vars.roomcount) { + g_Rooms[room].extra_flags &= ~ROOMFLAG_EX_WEATHERPROOF; + } + } + } else { + // weather is present in all rooms except skiprooms + for (s32 i = 0; i < WEATHERCFG_MAX_SKIPROOMS && g_CurWeatherConfig->skiprooms[i]; ++i) { + const RoomNum room = g_CurWeatherConfig->skiprooms[i]; + if (room >= 0 && room < g_Vars.roomcount) { + g_Rooms[room].extra_flags |= ROOMFLAG_EX_WEATHERPROOF; + } + } + } +} + +#endif + void weatherReset(void) { g_WeatherActive = false; g_WeatherData = NULL; +#ifndef PLATFORM_N64 + // find the weather config for this stage, if any + g_CurWeatherConfig = &g_DefaultWeatherConfig; + for (s32 i = 0; i < ARRAYCOUNT(g_WeatherConfig) && g_WeatherConfig[i].stagenum; ++i) { + if (g_WeatherConfig[i].stagenum == g_Stages[g_StageIndex].id) { + g_CurWeatherConfig = &g_WeatherConfig[i]; + break; + } + } + + if (g_CurWeatherConfig != &g_DefaultWeatherConfig) +#else if ((g_StageIndex == STAGEINDEX_CHICAGO || g_StageIndex == STAGEINDEX_AIRBASE || g_StageIndex == STAGEINDEX_G5BUILDING || g_StageIndex == STAGEINDEX_CRASHSITE) - && PLAYERCOUNT() < 2) { + && PLAYERCOUNT() < 2) +#endif + { g_WeatherData = mempAlloc(sizeof(struct weatherdata), MEMPOOL_STAGE); g_WeatherData->particledata[0] = weatherAllocateParticles(); g_WeatherData->type = -1; @@ -28,6 +71,7 @@ void weatherReset(void) g_WeatherData->unk0c = 0; g_WeatherData->unk10 = 1; +#ifdef PLATFORM_N64 if (g_StageIndex == STAGEINDEX_CHICAGO || g_StageIndex == STAGEINDEX_G5BUILDING) { g_WeatherData->windspeed = 20; } else if (g_StageIndex == STAGEINDEX_CRASHSITE) { @@ -35,6 +79,11 @@ void weatherReset(void) } else { g_WeatherData->windspeed = 5; } +#else + g_WeatherData->windspeed = g_CurWeatherConfig->windspeed; + // set up the weatherproof flags in all rooms + weatherResetRooms(); +#endif g_WeatherData->audiohandles[0] = 0; g_WeatherData->audiohandles[1] = 0; diff --git a/src/include/constants.h b/src/include/constants.h index d55b65b85..c5a2e17cb 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -3594,6 +3594,9 @@ #define ROOMFLAG_PLAYAMBIENTTRACK 0x4000 #define ROOMFLAG_OUTDOORS 0x8000 +// flags for the extra_flags field +#define ROOMFLAG_EX_WEATHERPROOF 0x0001 + #define RUMBLESTATE_1 1 #define RUMBLESTATE_ENABLED_STOPPED 2 #define RUMBLESTATE_ENABLED_STARTING 3 @@ -4490,6 +4493,13 @@ enum weaponnum { #define WEATHERTYPE_RAIN 0 #define WEATHERTYPE_SNOW 1 +#define WEATHERCFG_MAX_SKIPROOMS 128 +#define WEATHERCFG_MAX_STAGES 16 + +#define WEATHERFLAG_INCLUDE 0x0001 // rooms listed in skiprooms are the only ones that have weather +#define WEATHERFLAG_CUTSCENE_ONLY 0x0002 // weather only in cutscenes +#define WEATHERFLAG_FORCE_WINDDIR 0x0004 // force constant wind direction + // Reasons for playing X music #define XREASON_0 0 #define XREASON_DEFAULT 1 diff --git a/src/include/data.h b/src/include/data.h index d47d1090c..26613a80f 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -531,6 +531,10 @@ extern struct menudialogdef g_HangarListMenuDialog; #ifndef PLATFORM_N64 +extern struct weathercfg g_WeatherConfig[WEATHERCFG_MAX_STAGES]; +extern const struct weathercfg g_DefaultWeatherConfig; +extern const struct weathercfg *g_CurWeatherConfig; + extern f32 g_PlayerCrosshairSway; extern f32 g_PlayerDefaultFovY; extern s32 g_PlayerMouseAimMode; diff --git a/src/include/types.h b/src/include/types.h index ebfc9b78a..249b93e69 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -3670,6 +3670,10 @@ struct room { /*0x80*/ s32 gfxdatalen; // when inflated /*0x84*/ struct wallhit *opawallhits; // opaque /*0x88*/ struct wallhit *xluwallhits; // translucent + +#ifndef PLATFORM_N64 + /*0x8c*/ u16 extra_flags; +#endif }; struct fireslotthing { @@ -4892,6 +4896,23 @@ struct weatherparticledata { /*0x3efc*/ u32 unk3efc; }; +#ifndef PLATFORM_N64 + +struct weathercfg { + s32 stagenum; + u32 flags; + f32 windspeed; + f32 ymin; + f32 ymax; + f32 zmax; + RoomNum skiprooms[WEATHERCFG_MAX_SKIPROOMS]; // if flags has WEATHERFLAG_INCLUDE, rooms that have weather, else rooms that don't + f32 windanglerad; // wind fields only used if flags has WEATHERFLAG_FORCE_WINDDIR + f32 windspeedx; + f32 windspeedz; +}; + +#endif + struct texture { u32 soundsurfacetype : 4; u32 surfacetype : 4;