diff --git a/src/d_main.cpp b/src/d_main.cpp index c6e64ed05..0f4191c9d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -176,6 +176,7 @@ char srb2home[256] = "."; char srb2path[256] = "."; boolean usehome = true; const char *pandf = "%s" PATHSEP "%s"; +const char *spandf = "%s" PATHSEP "%s" PATHSEP "%s"; // subdirs wooo char addonsdir[MAX_WADPATH]; char downloaddir[sizeof addonsdir + sizeof DOWNLOADDIR_PART] = "DOWNLOAD"; @@ -1388,24 +1389,24 @@ static void IdentifyVersion(void) // if you change the ordering of this or add/remove a file, be sure to update the md5 // checking in D_SRB2Main - D_AddFile(startupiwads, va(pandf,srb2waddir,"scripts.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"gfx.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"textures_general.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"textures_segazones.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"textures_originalzones.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"maps.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"unlocks.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"staffghosts.pk3")); - D_AddFile(startupiwads, va(pandf,srb2waddir,"shaders.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","scripts.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","gfx.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","textures_general.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","textures_segazones.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","textures_originalzones.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","chars.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","followers.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","maps.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","unlocks.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","staffghosts.pk3")); + D_AddFile(startupiwads, va(spandf,srb2waddir,"data","shaders.pk3")); #ifdef USE_PATCH_FILE D_AddFile(startupiwads, va(pandf,srb2waddir,"patch.pk3")); #endif #define MUSICTEST(str) \ {\ - const char *musicpath = va(pandf,srb2waddir,str);\ + const char *musicpath = va(spandf,srb2waddir,"data",str);\ int ms = W_VerifyNMUSlumps(musicpath, false); \ if (ms == 1) \ { \ diff --git a/src/d_player.h b/src/d_player.h index 6d6a7b8bb..aa1597297 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -1049,6 +1049,7 @@ struct player_t UINT16 overshield; fixed_t overdrivepower; UINT8 overdriveready; + boolean overdrivelenient; UINT8 itemflags; // holds IF_ flags (see itemflags_t) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 95aca0f03..3517795c6 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -172,7 +172,7 @@ demoghost *ghosts = NULL; // - 0x000C (Ring Racers v2.2) // - 0x000D (Ring Racers v2.3) -#define DEMOVERSION 0x000D +#define DEMOVERSION 0x000E boolean G_CompatLevel(UINT16 level) { diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index a8890ed21..66269392d 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -115,76 +115,73 @@ void srb2::save_ng_gamedata() } } ng.timesBeaten = gamedata->timesBeaten; - for (int i = 0; i < numskins; i++) + + auto skintojson = [](skinrecord_t *records) { srb2::GamedataSkinJson skin {}; + skin.records.wins = records->wins; + skin.records.rounds = records->rounds; + skin.records.time.total = records->timeplayed; + skin.records.time.race = records->modetimeplayed[GDGT_RACE]; + skin.records.time.battle = records->modetimeplayed[GDGT_BATTLE]; + skin.records.time.prisons = records->modetimeplayed[GDGT_PRISONS]; + skin.records.time.special = records->modetimeplayed[GDGT_SPECIAL]; + skin.records.time.custom = records->modetimeplayed[GDGT_CUSTOM]; + skin.records.time.tumble = records->tumbletime; + + return skin; + }; + + for (int i = 0; i < numskins; i++) + { skin_t& memskin = skins[i]; + auto skin = skintojson(&memskin.records); std::string name = std::string(memskin.name); - skin.records.wins = memskin.records.wins; - skin.records.rounds = memskin.records.rounds; - skin.records.time.total = memskin.records.timeplayed; - skin.records.time.race = memskin.records.modetimeplayed[GDGT_RACE]; - skin.records.time.battle = memskin.records.modetimeplayed[GDGT_BATTLE]; - skin.records.time.prisons = memskin.records.modetimeplayed[GDGT_PRISONS]; - skin.records.time.special = memskin.records.modetimeplayed[GDGT_SPECIAL]; - skin.records.time.custom = memskin.records.modetimeplayed[GDGT_CUSTOM]; - skin.records.time.tumble = memskin.records.tumbletime; ng.skins[name] = std::move(skin); } for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next) { - srb2::GamedataSkinJson skin {}; + auto skin = skintojson(&unloadedskin->records); std::string name = std::string(unloadedskin->name); - skin.records.wins = unloadedskin->records.wins; ng.skins[name] = std::move(skin); } - for (int i = 0; i < nummapheaders; i++) + + auto maptojson = [](recorddata_t *records) { srb2::GamedataMapJson map {}; + map.visited.visited = records->mapvisited & MV_VISITED; + map.visited.beaten = records->mapvisited & MV_BEATEN; + map.visited.encore = records->mapvisited & MV_ENCORE; + map.visited.spbattack = records->mapvisited & MV_SPBATTACK; + map.visited.mysticmelody = records->mapvisited & MV_MYSTICMELODY; + map.stats.timeattack.besttime = records->timeattack.time; + map.stats.timeattack.bestlap = records->timeattack.lap; + map.stats.spbattack.besttime = records->spbattack.time; + map.stats.spbattack.bestlap = records->spbattack.lap; + map.stats.time.total = records->timeplayed; + map.stats.time.netgame = records->netgametimeplayed; + map.stats.time.race = records->modetimeplayed[GDGT_RACE]; + map.stats.time.battle = records->modetimeplayed[GDGT_BATTLE]; + map.stats.time.prisons = records->modetimeplayed[GDGT_PRISONS]; + map.stats.time.special = records->modetimeplayed[GDGT_SPECIAL]; + map.stats.time.custom = records->modetimeplayed[GDGT_CUSTOM]; + map.stats.time.timeattack = records->timeattacktimeplayed; + map.stats.time.spbattack = records->spbattacktimeplayed; + + return map; + }; + + for (int i = 0; i < nummapheaders; i++) + { + auto map = maptojson(&mapheaderinfo[i]->records); std::string lumpname = std::string(mapheaderinfo[i]->lumpname); - map.visited.visited = mapheaderinfo[i]->records.mapvisited & MV_VISITED; - map.visited.beaten = mapheaderinfo[i]->records.mapvisited & MV_BEATEN; - map.visited.encore = mapheaderinfo[i]->records.mapvisited & MV_ENCORE; - map.visited.spbattack = mapheaderinfo[i]->records.mapvisited & MV_SPBATTACK; - map.visited.mysticmelody = mapheaderinfo[i]->records.mapvisited & MV_MYSTICMELODY; - map.stats.timeattack.besttime = mapheaderinfo[i]->records.timeattack.time; - map.stats.timeattack.bestlap = mapheaderinfo[i]->records.timeattack.lap; - map.stats.spbattack.besttime = mapheaderinfo[i]->records.spbattack.time; - map.stats.spbattack.bestlap = mapheaderinfo[i]->records.spbattack.lap; - map.stats.time.total = mapheaderinfo[i]->records.timeplayed; - map.stats.time.netgame = mapheaderinfo[i]->records.netgametimeplayed; - map.stats.time.race = mapheaderinfo[i]->records.modetimeplayed[GDGT_RACE]; - map.stats.time.battle = mapheaderinfo[i]->records.modetimeplayed[GDGT_BATTLE]; - map.stats.time.prisons = mapheaderinfo[i]->records.modetimeplayed[GDGT_PRISONS]; - map.stats.time.special = mapheaderinfo[i]->records.modetimeplayed[GDGT_SPECIAL]; - map.stats.time.custom = mapheaderinfo[i]->records.modetimeplayed[GDGT_CUSTOM]; - map.stats.time.timeattack = mapheaderinfo[i]->records.timeattacktimeplayed; - map.stats.time.spbattack = mapheaderinfo[i]->records.spbattacktimeplayed; ng.maps[lumpname] = std::move(map); } for (auto unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next) { - srb2::GamedataMapJson map {}; + auto map = maptojson(&unloadedmap->records); std::string lumpname = std::string(unloadedmap->lumpname); - map.visited.visited = unloadedmap->records.mapvisited & MV_VISITED; - map.visited.beaten = unloadedmap->records.mapvisited & MV_BEATEN; - map.visited.encore = unloadedmap->records.mapvisited & MV_ENCORE; - map.visited.spbattack = unloadedmap->records.mapvisited & MV_SPBATTACK; - map.visited.mysticmelody = unloadedmap->records.mapvisited & MV_MYSTICMELODY; - map.stats.timeattack.besttime = unloadedmap->records.timeattack.time; - map.stats.timeattack.bestlap = unloadedmap->records.timeattack.lap; - map.stats.spbattack.besttime = unloadedmap->records.spbattack.time; - map.stats.spbattack.bestlap = unloadedmap->records.spbattack.lap; - map.stats.time.total = unloadedmap->records.timeplayed; - map.stats.time.netgame = unloadedmap->records.netgametimeplayed; - map.stats.time.race = unloadedmap->records.modetimeplayed[GDGT_RACE]; - map.stats.time.battle = unloadedmap->records.modetimeplayed[GDGT_BATTLE]; - map.stats.time.prisons = unloadedmap->records.modetimeplayed[GDGT_PRISONS]; - map.stats.time.special = unloadedmap->records.modetimeplayed[GDGT_SPECIAL]; - map.stats.time.custom = unloadedmap->records.modetimeplayed[GDGT_CUSTOM]; - map.stats.time.timeattack = unloadedmap->records.timeattacktimeplayed; - map.stats.time.spbattack = unloadedmap->records.spbattacktimeplayed; ng.maps[lumpname] = std::move(map); } for (int i = 0; i < gamedata->numspraycans; i++) @@ -219,20 +216,18 @@ void srb2::save_ng_gamedata() spraycan.map = std::string(mapheader->lumpname); ng.spraycans.emplace_back(std::move(spraycan)); } - for (auto cup = kartcupheaders; cup; cup = cup->next) + + auto cuptojson = [](cupwindata_t *windata) { - if (cup->windata[0].best_placement == 0 && cup->windata[1].got_emerald == false) - { - continue; - } srb2::GamedataCupJson cupdata {}; - cupdata.name = std::string(cup->name); + for (size_t i = 0; i < KARTGP_MAX; i++) { srb2::GamedataCupRecordsJson newrecords {}; - newrecords.bestgrade = cup->windata[i].best_grade; - newrecords.bestplacement = cup->windata[i].best_placement; - skinreference_t& skinref = cup->windata[i].best_skin; + + newrecords.bestgrade = windata[i].best_grade; + newrecords.bestplacement = windata[i].best_placement; + skinreference_t& skinref = windata[i].best_skin; if (skinref.unloaded) { newrecords.bestskin = std::string(skinref.unloaded->name); @@ -241,9 +236,23 @@ void srb2::save_ng_gamedata() { newrecords.bestskin = std::string(skins[skinref.id].name); } - newrecords.gotemerald = cup->windata[i].got_emerald; + newrecords.gotemerald = windata[i].got_emerald; + cupdata.records.emplace_back(std::move(newrecords)); } + + return cupdata; + }; + + for (auto cup = kartcupheaders; cup; cup = cup->next) + { + if (cup->windata[0].best_placement == 0 && cup->windata[1].got_emerald == false) + { + continue; + } + + auto cupdata = cuptojson(cup->windata); + cupdata.name = std::string(cup->name); ng.cups[cupdata.name] = std::move(cupdata); } for (auto unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next) @@ -252,25 +261,9 @@ void srb2::save_ng_gamedata() { continue; } - srb2::GamedataCupJson cupdata {}; + + auto cupdata = cuptojson(unloadedcup->windata); cupdata.name = std::string(unloadedcup->name); - for (int i = 0; i < KARTGP_MAX; i++) - { - srb2::GamedataCupRecordsJson newrecords {}; - newrecords.bestgrade = unloadedcup->windata[i].best_grade; - newrecords.bestplacement = unloadedcup->windata[i].best_placement; - skinreference_t& skinref = unloadedcup->windata[i].best_skin; - if (skinref.unloaded) - { - newrecords.bestskin = std::string(skinref.unloaded->name); - } - else - { - newrecords.bestskin = std::string(skins[skinref.id].name); - } - newrecords.gotemerald = unloadedcup->windata[i].got_emerald; - cupdata.records.emplace_back(std::move(newrecords)); - } ng.cups[cupdata.name] = std::move(cupdata); } diff --git a/src/k_kart.c b/src/k_kart.c index 2c008048c..727b0cca7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4130,7 +4130,7 @@ void K_AwardPlayerAmps(player_t *player, UINT8 amps) { // Auto Overdrive! // If this is a fresh OD, give 'em some extra juice to make up for lack of flexibility. - if (!player->overdrive && player->mo && !P_MobjWasRemoved(player->mo)) + if (!player->overdrive && player->mo && !P_MobjWasRemoved(player->mo) && player->overdriveready == 0) { S_StartSound(player->mo, sfx_gshac); player->amps *= 2; @@ -4263,6 +4263,7 @@ boolean K_Overdrive(player_t *player) player->amps = 0; player->overdriveready = 0; + player->overdrivelenient = false; return true; } @@ -4284,6 +4285,8 @@ boolean K_DefensiveOverdrive(player_t *player) player->overdrivepower = FRACUNIT; player->amps = 0; + player->overdrivelenient = true; + player->overdriveready = false; return true; } @@ -9405,15 +9408,25 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } - if (player->overdrive > 0 && onground == true) + if (player->overdrivelenient) { - player->overdrive--; + // This is a Defensive Overdrive and shouldn't start deducting time until we recover + if (!P_PlayerInPain(player)) + player->overdrivelenient = false; + } + else + { + if (player->overdrive > 0 && onground == true) + { + player->overdrive--; + } + + if (player->overshield > 0 && onground == true) + { + player->overshield--; + } } - if (player->overshield > 0 && onground == true) - { - player->overshield--; - } if (player->wavedashboost == 0 || player->wavedashpower > FRACUNIT) { diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 59796ac60..2eb7b4a1b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -376,6 +376,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->overdrivepower); else if (fastcmp(field,"overdriveready")) lua_pushinteger(L, plr->overdriveready); + else if (fastcmp(field,"overdrivelenient")) + lua_pushinteger(L, plr->overdrivelenient); else if (fastcmp(field,"speedpunt")) lua_pushinteger(L, plr->speedpunt); else if (fastcmp(field,"trickcharge")) @@ -962,6 +964,8 @@ static int player_set(lua_State *L) plr->overdrivepower = luaL_checkinteger(L, 3); else if (fastcmp(field,"overdriveready")) plr->overdriveready = luaL_checkinteger(L, 3); + else if (fastcmp(field,"overdrivelenient")) + plr->overdrivelenient = luaL_checkinteger(L, 3); else if (fastcmp(field,"speedpunt")) plr->speedpunt = luaL_checkinteger(L, 3); else if (fastcmp(field,"trickcharge")) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index ca712989c..6ef391b18 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -45,6 +45,8 @@ #define UFO_NUMARMS (3) #define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) +#define UFO_START_GLASSFRAMES (1) +#define UFO_NUM_GLASSFRAMES (10) #define ufo_emeraldnum(o) ((o)->cvmem) #define ufo_waypoint(o) ((o)->extravalue1) @@ -56,6 +58,8 @@ #define ufo_piece_type(o) ((o)->extravalue1) +#define ufo_piece_glass_flickerframe(o) ((o)->cusval) + #define ufo_piece_owner(o) ((o)->target) #define ufo_piece_next(o) ((o)->hnext) #define ufo_piece_prev(o) ((o)->hprev) @@ -676,6 +680,40 @@ spawn_shard } } +static void +set_flickerframe (mobj_t *ufo, mobj_t *piece) +{ + INT32 healthcalc = (UFO_NUM_GLASSFRAMES - 1); + + if (ufo && !P_MobjWasRemoved(ufo)) + { + INT32 maxhealth = mobjinfo[MT_SPECIAL_UFO].spawnhealth; + healthcalc = (maxhealth - ufo->health); + + if (healthcalc > 0) + { + maxhealth /= UFO_NUM_GLASSFRAMES; + if (maxhealth <= 0) + maxhealth = 1; + healthcalc /= maxhealth; + if (healthcalc >= UFO_NUM_GLASSFRAMES) + healthcalc = (UFO_NUM_GLASSFRAMES - 1); + if (healthcalc < 0) + healthcalc = 0; + } + else + { + healthcalc = 0; + } + } + + healthcalc = (healthcalc|FF_FULLBRIGHT) + UFO_START_GLASSFRAMES; + + ufo_piece_glass_flickerframe(piece) = healthcalc; + piece->frame = healthcalc; +} + + static void spawn_debris (mobj_t *part) { @@ -704,6 +742,7 @@ static void UFOCopyHitlagToPieces(mobj_t *ufo) if (ufo_piece_type(piece) == UFO_PIECE_TYPE_GLASS) { + set_flickerframe (ufo, piece); spawn_debris (piece); } @@ -728,6 +767,11 @@ static void UFOKillPiece(mobj_t *piece) switch (ufo_piece_type(piece)) { case UFO_PIECE_TYPE_GLASS: + { + set_flickerframe(NULL, piece); + piece->tics = 1; + return; + } case UFO_PIECE_TYPE_GLASS_UNDER: case UFO_PIECE_TYPE_STEM: { @@ -1115,6 +1159,21 @@ void Obj_UFOPieceThink(mobj_t *piece) break; } case UFO_PIECE_TYPE_GLASS: + { + // Flicker glass cracks for visibility + if (piece->frame == FF_SEMIBRIGHT) + { + piece->frame = ufo_piece_glass_flickerframe(piece); + } + else + { + piece->frame = FF_SEMIBRIGHT; + } + + piece->destscale = 5 * ufo->destscale / 3; + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z); + break; + } case UFO_PIECE_TYPE_GLASS_UNDER: { piece->destscale = 5 * ufo->destscale / 3; @@ -1222,6 +1281,12 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) specialstageinfo.maxDist = ufo_distancetofinish(ufo); } + // Set specialDamage as early as possible, for glass ball's sake + if (grandprixinfo.gp && grandprixinfo.specialDamage) + { + ufo->health -= min(4*(UINT32)mobjinfo[MT_SPECIAL_UFO].spawnhealth/10, grandprixinfo.specialDamage/6); + } + ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed)); // Adjustable Special Stage emerald color/shape @@ -1295,6 +1360,8 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetMobjState(piece, S_SPECIAL_UFO_GLASS); ufo_piece_type(piece) = UFO_PIECE_TYPE_GLASS; + set_flickerframe(ufo, piece); + /*overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY); P_SetTarget(&overlay->target, piece); P_SetMobjState(overlay, S_SPECIAL_UFO_GLASS_UNDER); @@ -1347,11 +1414,6 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_piece_prev(piece), prevPiece); prevPiece = piece; - if (grandprixinfo.gp && grandprixinfo.specialDamage) - { - ufo->health -= min(4*(UINT32)mobjinfo[MT_SPECIAL_UFO].spawnhealth/10, grandprixinfo.specialDamage/6); - } - return ufo; } diff --git a/src/p_inter.c b/src/p_inter.c index 8464cf0b9..01928424d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -451,7 +451,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->fuse) // This box is respawning, but was broken very recently (see P_FuseThink) { // What was this box broken as? - if (special->cvmem) + if (special->cvmem && !(special->flags2 & MF2_BOSSDEAD)) K_StartItemRoulette(player, false); else K_StartItemRoulette(player, true); diff --git a/src/p_saveg.c b/src/p_saveg.c index 2183821c2..9fb6bc1d5 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -602,6 +602,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEFIXED(save->p, players[i].wavedashpower); WRITEFIXED(save->p, players[i].overdrivepower); WRITEUINT8(save->p, players[i].overdriveready); + WRITEUINT8(save->p, players[i].overdrivelenient); WRITEUINT16(save->p, players[i].speedpunt); WRITEUINT16(save->p, players[i].trickcharge); @@ -1226,6 +1227,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].wavedashpower = READFIXED(save->p); players[i].overdrivepower = READFIXED(save->p); players[i].overdriveready = READUINT8(save->p); + players[i].overdrivelenient = READUINT8(save->p); players[i].speedpunt = READUINT16(save->p); players[i].trickcharge = READUINT16(save->p);