diff --git a/KickoffTimerPlugin.cpp b/KickoffTimerPlugin.cpp index 5df7bfb..e96fc6b 100644 --- a/KickoffTimerPlugin.cpp +++ b/KickoffTimerPlugin.cpp @@ -1,11 +1,21 @@ #include "KickoffTimerPlugin.h" -#include "bakkesmod\wrappers\includes.h" +#include "bakkesmod/wrappers/includes.h" #include "utils/parser.h" #include BAKKESMOD_PLUGIN(KickoffTimerPlugin, "Kickoff timer plugin", "0.1", PLUGINTYPE_FREEPLAY) static const string savefile = "bakkesmod/data/kickofftimerplugin.data"; +enum event_types { normal, better_than_normal, new_pb, slow_time }; +map color_mapping = +{ + { normal, { 255, 255, 255 }}, // White + { better_than_normal, { 255, 200, 0 }}, // Yellow-ish + { new_pb, { 0, 255, 0 }}, // Green + { slow_time, { 255, 0, 0 }} // Red +}; + +int DECIMAL_PRECISION; void KickoffTimerPlugin::onLoad() { @@ -15,14 +25,17 @@ void KickoffTimerPlugin::onLoad() spawnLocations.push_back({ "L", {2048, -2560}, 2.06905 }); spawnLocations.push_back({ "R", {-2048, -2560}, 2.06905 }); - load(); + loadPBFile(); cvarManager->registerNotifier("kickofftimer_reset", [this](std::vector params) { - for (int i = 0; i < spawnLocations.size(); i++) { - spawnLocations.at(i).personalBest = -1; - } + ResetPersonalBests(); }, "Reset personal bests", PERMISSION_ALL); + + cvarManager->registerCvar("kickofftimer_decimalPlaces", "2", "How many decimal places to record time", true, true, 2, true, 4, true); + cvarManager->getCvar("kickofftimer_decimalPlaces").addOnValueChanged(std::bind(&KickoffTimerPlugin::updateDecimalValue, this)); + + DECIMAL_PRECISION = cvarManager->getCvar("kickofftimer_decimalPlaces").getIntValue(); pDefaultTime = {""}; pBest = { ""}; pBallHitted = { ""}; @@ -36,9 +49,17 @@ void KickoffTimerPlugin::onLoad() gameWrapper->RegisterDrawable(std::bind(&KickoffTimerPlugin::Render, this, std::placeholders::_1)); } +void KickoffTimerPlugin::ResetPersonalBests() +{ + for (auto& spawn_location : spawnLocations) + { + spawn_location.personalBest = -1; + } +} + void KickoffTimerPlugin::onUnload() { - save(); + savePBFile(); } void KickoffTimerPlugin::onHitBall(std::string eventName) @@ -46,34 +67,78 @@ void KickoffTimerPlugin::onHitBall(std::string eventName) if (!gameWrapper->IsInGame() || hitted || spawn == 0) return; + const int decimalMultiplyer = pow(10, DECIMAL_PRECISION); + timeHit = gameWrapper->GetGameEventAsServer().GetSecondsElapsed() - timeStart; + timeHit = roundf(timeHit * decimalMultiplyer) / decimalMultiplyer; // Round to a certain decimal place + cvarManager->log("timeHit: " + std::to_string(timeHit)); + hitted = true; - if (spawn->personalBest == -1) + const auto current_pb = spawn->personalBest; + cvarManager->log("current_pb: " + std::to_string(current_pb)); + + auto normal_time = spawn->normalTime; + normal_time = roundf(normal_time * decimalMultiplyer) / decimalMultiplyer; // Round to a certain decimal place + cvarManager->log("normal_time: " + std::to_string(normal_time)); + + // First time, set it to PB + if (current_pb == -1) { spawn->personalBest = timeHit; - pBallHitted.color = { 255,255,255 }; + + auto message = "First time! Ball hitted after " + + to_string_with_precision(timeHit, DECIMAL_PRECISION) + " seconds."; + SetHitTextWithTimeAndText(timeHit, message); + return; } - else + + // If its above normal, thats slow my friend + if (timeHit > normal_time) { - if (timeHit < spawn->personalBest) - { - pBallHitted.color = { 255,200,0 }; - spawn->personalBest = timeHit; - } - else { - pBallHitted.color = { 255,0,0 }; - } - + pBallHitted.color = color_mapping[slow_time]; + SetHitTextWithTime(timeHit); + return; } - if (timeHit < spawn->normalTime) + + // If its a new pb, congrats! + if (timeHit < current_pb) + { + spawn->personalBest = timeHit; + pBallHitted.color = color_mapping[new_pb]; + SetHitTextWithTime(timeHit); + return; + } + + // If its higher than your current pb + // (and lower than normal, since other checks failed) + // Then you can do better! + if (timeHit > current_pb) { - pBallHitted.color = { 0,255,0 }; + pBallHitted.color = color_mapping[better_than_normal]; + SetHitTextWithTime(timeHit); + return; } + // If they end up being equal to either time, just output it normally + pBallHitted.color = color_mapping[normal]; + SetHitTextWithTime(timeHit); +} + +void KickoffTimerPlugin::SetHitTextWithTime(const float time_hit) +{ + std::ostringstream os; + + os << "Ball hitted after " << to_string_with_precision(time_hit, DECIMAL_PRECISION) << " seconds."; + const auto msg = os.str(); + pBallHitted.text = msg; +} + +void KickoffTimerPlugin::SetHitTextWithTimeAndText(const float time_hit, string& message) +{ std::ostringstream os; - os << "Ball hitted after " << to_string_with_precision(timeHit, 2) << " seconds."; - std::string msg = os.str(); + os << message; + const auto msg = os.str(); pBallHitted.text = msg; } @@ -94,18 +159,25 @@ void KickoffTimerPlugin::onReset(std::string eventName) pBallHitted.text = ""; spawn = getSpawnLocation(); - if (spawn != 0) { - std::ostringstream os; - os << spawn->name << " normal Kickoff time: " << to_string_with_precision(spawn->normalTime, 2) << " seconds"; - std::string msg = os.str(); - pDefaultTime.text = msg; - - os.str(""); - os.clear(); - if(spawn->personalBest > 0) os << "Personal Best: " << to_string_with_precision(spawn->personalBest, 2) << " seconds"; - msg = os.str(); - pBest.text = msg; + + if (nullptr == spawn) + { + cvarManager->log("Can't get spawnLocation!."); + return; } + + std::ostringstream os; + os << spawn->name << " normal Kickoff time: " << + to_string_with_precision(spawn->normalTime, DECIMAL_PRECISION) << " seconds"; + auto msg = os.str(); + pDefaultTime.text = msg; + + os.str(""); + os.clear(); + if(spawn->personalBest > 0) os << "Personal Best: " << + to_string_with_precision(spawn->personalBest, DECIMAL_PRECISION) << " seconds"; + msg = os.str(); + pBest.text = msg; } void KickoffTimerPlugin::Render(CanvasWrapper canvas) @@ -113,75 +185,89 @@ void KickoffTimerPlugin::Render(CanvasWrapper canvas) if (!gameWrapper->IsInGame() || popups.empty() || !spawn != 0) return; - auto screenSize = canvas.GetSize(); - for(int i = 0; i < popups.size(); i++) + const auto screen_size = canvas.GetSize(); + for(auto i = 0; i < popups.size(); i++) { auto pop = popups.at(i); if (pop->startLocation.X < 0) { - pop->startLocation = {(int)(screenSize.X * 0.35), (int)(screenSize.Y * 0.1 + i * 0.035 * screenSize.Y)}; + pop->startLocation = + { + static_cast(screen_size.X * 0.35), + static_cast(screen_size.Y * 0.1 + i * 0.035 * screen_size.Y) + }; } - Vector2 drawLoc = { pop->startLocation.X, pop->startLocation.Y }; - canvas.SetPosition(drawLoc); - canvas.SetColor(pop->color.R, pop->color.G, pop->color.B, 255); + const Vector2 draw_loc = { pop->startLocation.X, pop->startLocation.Y }; + canvas.SetPosition(draw_loc); + canvas.SetColor(pop->color.R, pop->color.G, pop->color.B, 225); canvas.DrawString(pop->text, 3, 3); } } -SpawnLocation* KickoffTimerPlugin::getSpawnLocation() { - auto location = gameWrapper->GetLocalCar().GetLocation(); - for(int i = 0; i < spawnLocations.size(); i++) +SpawnLocation* KickoffTimerPlugin::getSpawnLocation() +{ + const auto location = gameWrapper->GetLocalCar().GetLocation(); + + for (auto& spawn_location : spawnLocations) { - auto it = &spawnLocations.at(i); - if (location.X == it->location.X && location.Y == it->location.Y) { + const auto it = &spawn_location; + if (location.X == it->location.X && + location.Y == it->location.Y) { return it; } } - return 0; + return nullptr; } -void KickoffTimerPlugin::save() { +void KickoffTimerPlugin::savePBFile() +{ ofstream myfile; myfile.open(savefile); - if (myfile.is_open()) + + if (!myfile.is_open()) { - for (int i = 0; i < spawnLocations.size(); i++) - { - myfile << spawnLocations.at(i).personalBest << "\n"; - } + cvarManager->log("Can't write savefile."); + myfile.close(); + return; } - else + + for (auto& spawn_location : spawnLocations) { - cvarManager->log("Can't write savefile."); + myfile << spawn_location.personalBest << "\n"; } + myfile.close(); } -void KickoffTimerPlugin::load() { +void KickoffTimerPlugin::loadPBFile() +{ ifstream myfile; myfile.open(savefile); - if (myfile.good()) + if (!myfile.good()) { - float f; - char buffer[8]; + cvarManager->log("Can't read savefile."); + return; + } + + char buffer[8]; + + for (auto& spawn_location : spawnLocations) + { + myfile.getline(buffer, 8); - for (int i = 0; i < spawnLocations.size(); i++) + // There should be the same number of lines in the file as spawnLocations + if (myfile.eof()) { - myfile.getline(buffer, 8); - if (!myfile.eof()) - { - f = (float)atof(buffer); - spawnLocations.at(i).personalBest = f; - } - else - { - cvarManager->log("End of savefile reached too early!"); - break; - } + cvarManager->log("End of savefile reached too early!"); + return; } + + const auto f = static_cast(atof(buffer)); + spawn_location.personalBest = f; } - else - { - cvarManager->log("Can't read savefile."); - } +} + +void KickoffTimerPlugin::updateDecimalValue() +{ + DECIMAL_PRECISION = cvarManager->getCvar("kickofftimer_decimalPlaces").getIntValue(); } diff --git a/KickoffTimerPlugin.h b/KickoffTimerPlugin.h index c3343d9..8427ad1 100644 --- a/KickoffTimerPlugin.h +++ b/KickoffTimerPlugin.h @@ -26,24 +26,29 @@ struct SpawnLocation class KickoffTimerPlugin : public BakkesMod::Plugin::BakkesModPlugin { private: - float timeStart; - float timeHit; + float timeStart{}; + float timeHit{}; bool hitted = false; std::vector popups; Popup pDefaultTime; Popup pBallHitted; Popup pBest; std::vector spawnLocations; - SpawnLocation* spawn; + SpawnLocation* spawn{}; public: virtual void onLoad(); virtual void onUnload(); + virtual void onHitBall(std::string eventName); virtual void onStartedDriving(std::string eventName); virtual void onReset(std::string eventName); virtual void Render(CanvasWrapper canvas); virtual SpawnLocation* getSpawnLocation(); - virtual void save(); - virtual void load(); + virtual void savePBFile(); + virtual void loadPBFile(); + virtual void SetHitTextWithTime(float time_hit); + virtual void SetHitTextWithTimeAndText(float time_hit, string& message); + virtual void ResetPersonalBests(); + virtual void updateDecimalValue(); }; diff --git a/settings/kickofftimerplugin.set b/settings/kickofftimerplugin.set index c99bab9..e20f8be 100644 --- a/settings/kickofftimerplugin.set +++ b/settings/kickofftimerplugin.set @@ -1,6 +1,11 @@ Kickoff timer plugin +9|Controls +8 0|Load|plugin load kickofftimerplugin 7 0|Unload|plugin unload kickofftimerplugin 7 0|Reset Personal Best|kickofftimer_reset +9|Config Values +8 +5|How many decimal places to record time|kickofftimer_decimalPlaces|2|4