From f4b36b8f738953e4dc831d38c3f4b54dc7da02fc Mon Sep 17 00:00:00 2001 From: FinnHornhoover Date: Mon, 24 Jul 2023 00:33:19 +0300 Subject: [PATCH] added alternate racing score funcitonality --- src/Racing.cpp | 38 +++++++++++++++++++++++++++----------- src/Racing.hpp | 6 +++++- src/TableData.cpp | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/Racing.cpp b/src/Racing.cpp index 565850c..180d483 100644 --- a/src/Racing.cpp +++ b/src/Racing.cpp @@ -64,7 +64,7 @@ static void racingCancel(CNSocket* sock, CNPacketData* data) { INITSTRUCT(sP_FE2CL_REP_EP_RACE_CANCEL_SUCC, resp); sock->sendPacket(resp, P_FE2CL_REP_EP_RACE_CANCEL_SUCC); - /* + /* * This request packet is used for both cancelling the race via the * NPC at the start, *and* failing the race by running out of time. * If the latter is to happen, the client disables movement until it @@ -97,31 +97,47 @@ static void racingEnd(CNSocket* sock, CNPacketData* data) { if (EPData.find(mapNum) == EPData.end() || EPData[mapNum].EPID == 0) return; // IZ not found - uint64_t now = getTime() / 1000; + EPInfo& epInfo = EPData[mapNum]; + EPRace& epRace = EPRaces[sock]; + // if there are no divide-by-zero dangers, and at least one factor has been specified + // we switch over to OG scoring + bool useOGScoring = (epInfo.maxPods > 0) && (epInfo.maxTime > 0) && ( + (epInfo.scaleFactor > 0.0) || (epInfo.podFactor > 0.0) || (epInfo.timeFactor > 0.0)); - int timeDiff = now - EPRaces[sock].startTime; - int score = 500 * EPRaces[sock].collectedRings.size() - 10 * timeDiff; - if (score < 0) score = 0; // lol - int fm = score * plr->level * (1.0f / 36) * 0.3f; + uint64_t now = getTime() / 1000; + int timeDiff = now - epRace.startTime; + int podsCollected = epRace.collectedRings.size(); + int score = 0, fm = 0; + + if (useOGScoring) { + score = std::min(epInfo.maxScore, (int)std::exp( + (epInfo.podFactor * podsCollected) / epInfo.maxPods + - (epInfo.timeFactor * timeDiff) / epInfo.maxTime + + epInfo.scaleFactor)); + fm = (1.0 + std::exp(epInfo.scaleFactor - 1.0) * epInfo.podFactor * podsCollected) / epInfo.maxPods; + } else { + score = std::max(0, 500 * podsCollected - 10 * timeDiff); + fm = score * plr->level * (1.0f / 36) * 0.3f; + } // we submit the ranking first... Database::RaceRanking postRanking = {}; - postRanking.EPID = EPData[mapNum].EPID; + postRanking.EPID = epInfo.EPID; postRanking.PlayerID = plr->iID; - postRanking.RingCount = EPRaces[sock].collectedRings.size(); + postRanking.RingCount = podsCollected; postRanking.Score = score; postRanking.Time = timeDiff; postRanking.Timestamp = getTimestamp(); Database::postRaceRanking(postRanking); // ...then we get the top ranking, which may or may not be what we just submitted - Database::RaceRanking topRankingPlayer = Database::getTopRaceRanking(EPData[mapNum].EPID, plr->iID); + Database::RaceRanking topRankingPlayer = Database::getTopRaceRanking(epInfo.EPID, plr->iID); INITSTRUCT(sP_FE2CL_REP_EP_RACE_END_SUCC, resp); // get rank scores and rewards - std::vector* rankScores = &EPRewards[EPData[mapNum].EPID].first; - std::vector* rankRewards = &EPRewards[EPData[mapNum].EPID].second; + std::vector* rankScores = &EPRewards[epInfo.EPID].first; + std::vector* rankRewards = &EPRewards[epInfo.EPID].second; // top ranking int topRank = 0; diff --git a/src/Racing.hpp b/src/Racing.hpp index f6e6acb..90cc9f2 100644 --- a/src/Racing.hpp +++ b/src/Racing.hpp @@ -5,7 +5,11 @@ #include "servers/CNShardServer.hpp" struct EPInfo { - int zoneX, zoneY, EPID, maxScore, maxTime; + // available through XDT (maxScore may be updated by drops) + int zoneX, zoneY, EPID, maxScore; + // (maybe) available through drops + int maxTime = 0, maxPods = 0; + double scaleFactor = 0.0, podFactor = 0.0, timeFactor = 0.0; }; struct EPRace { diff --git a/src/TableData.cpp b/src/TableData.cpp index a685cee..de05177 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -363,7 +363,7 @@ static void loadPaths(json& pathData, int32_t* nextId) { Transport::NPCPaths.push_back(pathTemplate); } std::cout << "[INFO] Loaded " << Transport::NPCPaths.size() << " NPC paths" << std::endl; - + } catch (const std::exception& err) { std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl; @@ -572,8 +572,35 @@ static void loadDrops(json& dropData) { continue; } + EPInfo& epInfo = Racing::EPData[EPMap]; + // time limit isn't stored in the XDT, so we include it in the reward table instead - Racing::EPData[EPMap].maxTime = race["TimeLimit"]; + epInfo.maxTime = (int)race["TimeLimit"]; + + // update max score (if present) + if (race.find("ScoreCap") != race.end()) { + epInfo.maxScore = (int)race["ScoreCap"]; + } + + // update max pods (if present) + if (race.find("TotalPods") != race.end()) { + epInfo.maxPods = (int)race["TotalPods"]; + } + + // update scale factor (if present) + if (race.find("ScaleFactor") != race.end()) { + epInfo.scaleFactor = (double)race["ScaleFactor"]; + } + + // update pod factor (if present) + if (race.find("PodFactor") != race.end()) { + epInfo.podFactor = (double)race["PodFactor"]; + } + + // update time factor (if present) + if (race.find("TimeFactor") != race.end()) { + epInfo.timeFactor = (double)race["TimeFactor"]; + } // score cutoffs std::vector rankScores; @@ -674,7 +701,7 @@ static void loadEggs(json& eggData, int32_t* nextId) { } } -/* +/* * Load gruntwork output, if it exists */ static void loadGruntworkPre(json& gruntwork, int32_t* nextId) { @@ -1349,7 +1376,7 @@ void TableData::flush() { targetIDs.push_back(tID); for (int32_t tType : path.targetTypes) targetTypes.push_back(tType); - + pathObj["iBaseSpeed"] = path.speed; pathObj["iTaskID"] = path.escortTaskID; pathObj["bRelative"] = path.isRelative;