Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 159 additions & 73 deletions KickoffTimerPlugin.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
#include "KickoffTimerPlugin.h"
#include "bakkesmod\wrappers\includes.h"
#include "bakkesmod/wrappers/includes.h"
#include "utils/parser.h"
#include <string>

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<event_types, Color> 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()
{
Expand All @@ -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<string> 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 = { ""};
Expand All @@ -36,44 +49,96 @@ 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)
{
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;
}

Expand All @@ -94,94 +159,115 @@ 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)
{
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<int>(screen_size.X * 0.35),
static_cast<int>(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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to just spawn_location? No need for it anymore, or?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It think using 'it' still makes sense. Its shorter to write that compared to spawn_location, but I see what you mean :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so pls change that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't as clear as I should have been in the last one. It's shorter to write what's there now (using 'it'), so I think we should keep it

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<float>(atof(buffer));
spawn_location.personalBest = f;
}
else
{
cvarManager->log("Can't read savefile.");
}
}

void KickoffTimerPlugin::updateDecimalValue()
{
DECIMAL_PRECISION = cvarManager->getCvar("kickofftimer_decimalPlaces").getIntValue();
}
15 changes: 10 additions & 5 deletions KickoffTimerPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Popup*> popups;
Popup pDefaultTime;
Popup pBallHitted;
Popup pBest;
std::vector<SpawnLocation> 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();
};
5 changes: 5 additions & 0 deletions settings/kickofftimerplugin.set
Original file line number Diff line number Diff line change
@@ -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