Snowflakes!

This commit is contained in:
BenCat07 2019-12-26 17:46:16 +01:00
parent f24bad1958
commit 4ae0040bdc
3 changed files with 203 additions and 1 deletions

View File

@ -22,7 +22,7 @@ bool settings::SettingsWriter::saveTo(std::string path, bool autosave)
{
logging::Info("cat_save: Saving to %s", path.c_str());
}
this->only_changed = true;
stream.open(path, std::ios::out);

View File

@ -11,8 +11,13 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/atlas.cpp"
target_sources(cathook PRIVATE ${files})
list(REMOVE_ITEM ignore_files ${files})
set(ignore_files ${ignore_files} CACHE INTERNAL "")
if(EnableGUI)
add_subdirectory(menu)
set(files "${CMAKE_CURRENT_LIST_DIR}/GUISnow.cpp")
target_sources(cathook PRIVATE ${files})
list(REMOVE_ITEM ignore_files ${files})
set(ignore_files ${ignore_files} CACHE INTERNAL "")
endif()
if (EnableImGuiDrawing)

197
src/visual/GUISnow.cpp Normal file
View File

@ -0,0 +1,197 @@
#include "common.hpp"
#include "drawing.hpp"
#include "Menu.hpp"
#include "GuiInterface.hpp"
namespace snowflakes
{
// Control variables
// Base Speed the flakes spawn at
static settings::Float min_snowflake_speed_x{ "visual.snowflakes.min-speed.x", "0.0f" };
static settings::Float max_snowflake_speed_x{ "visual.snowflakes.max-speed.x", "00.0f" };
static settings::Float min_snowflake_speed_y{ "visual.snowflakes.min-speed.y", "10.0f" };
static settings::Float max_snowflake_speed_y{ "visual.snowflakes.max-speed.y", "150.0f" };
// Speed Variance
static settings::Float snowflake_speed_deviation_x{ "visual.snowflakes.speed-variance.x", "5.0f" };
static settings::Float snowflake_speed_deviation_y{ "visual.snowflakes.speed-variance.y", "30.0f" };
// Snowflake size
static settings::Float snowflake_size{ "visual.snowflakes.size", "16.0f" };
// Snowflake Amount
static settings::Int snowflake_amount{ "visual.snowflakes.count", "150" };
// Static timer to mark start of starting cathook. We use this to avoid a high floating point inprecision on timers
static std::chrono::milliseconds start_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
// Snowflake class
class Snowflake
{
// Position
float x{};
float y{};
// Fall speed (pixel/s)
float x_speed{};
float y_speed{};
// Fall speed variation (in pixels)
float x_speed_variation{};
float y_speed_variation{};
// Neccessary to make snowflakes not depend on frames
float last_update_time{};
// Static timer to mark start of spawning the flake. We use this to avoid a high floating point inprecision on timers
std::chrono::milliseconds start_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
public:
// Needed for std::find
constexpr bool operator==(const Snowflake &rhs) const
{
// Ignore the warnings, this is perfectly valid syntax in this case
return x == rhs.x && y == rhs.y && x_speed == rhs.x_speed && x_speed_variation == rhs.x_speed_variation && y_speed == rhs.y_speed && y_speed_variation == rhs.y_speed_variation;
}
// Init the snowflake
void Init(float x, float y, float x_speed, float y_speed, float x_speed_variation = 0.0f, float y_speed_variation = 0.0f)
{
// Starting point
this->x = x;
this->y = y;
// Base fall speed (pixel/s)
this->x_speed = x_speed;
this->y_speed = y_speed;
// Variation amount (pixel/s)
this->x_speed_variation = x_speed_variation;
this->y_speed_variation = y_speed_variation;
// Update time
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) - start_ms;
auto cur_time = ms.count() / 1000.0f;
this->last_update_time = cur_time;
}
// Update x and y coords
void Update(std::chrono::milliseconds current_time)
{
// Every snowflake has it's own one of these, so use it.
current_time -= start_ms;
auto cur_time = current_time.count() / 1000.0f;
float variation_x, variation_y;
// Update speed
// We want a smooth variation, so let's use sine/cosine
variation_x = (sin(DEG2RAD(cur_time * 180.0f)) * x_speed_variation);
// The +1 and /2.0f is so snowflakes don't just freeze and stop moving
variation_y = (cos(DEG2RAD(cur_time * 180.0f) + 1) * y_speed_variation / 2.0f);
// Move this speed in pixels/s
this->x += (x_speed + variation_x) * (cur_time - this->last_update_time);
this->y += (y_speed + variation_y) * (cur_time - this->last_update_time);
// Update last draw time accordingly
this->last_update_time = cur_time;
}
// Draw the Snowflake
void Draw() const
{
draw::RectangleTextured(this->x, this->y, *snowflake_size, *snowflake_size, colors::white, textures::atlas().texture, 257, 0, 16, 16, 0.0f);
}
// Is the Snowflake off-screen?
bool IsOffScreen() const
{
// We don't care too much about snowflakes going off of the left/right of the screen
return y - *snowflake_size > draw::height || y < -10.0f;
}
};
// Snowflake Manager
class SnowflakeManager
{
std::vector<Snowflake> snowflakes;
// Remove a(n off-screen) snowflake
void RemoveSnowflake(Snowflake &flake)
{
auto pos = std::find(snowflakes.begin(), snowflakes.end(), flake);
if (pos != snowflakes.end())
snowflakes.erase(pos);
}
public:
// Add a Snowflake to the vector
void AddSnowflake()
{
snowflakes.resize(snowflakes.size() + 1);
Snowflake &flake = snowflakes[snowflakes.size() - 1];
// Random position at top of screen
float flake_x = RandFloatRange(0.0f, draw::width);
float flake_y = 0.0f;
// Random Speed
float flake_speed_x = RandFloatRange(*min_snowflake_speed_x, *max_snowflake_speed_x);
float flake_speed_y = RandFloatRange(*min_snowflake_speed_y, *max_snowflake_speed_y);
// Init flake
flake.Init(flake_x, flake_y, flake_speed_x, flake_speed_y, *snowflake_speed_deviation_x, *snowflake_speed_deviation_y);
}
// Update and Draw the snowflakes
void UpdateAndDraw()
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
// We want to update snowflakes regardless if we're in the menu or not
for (auto &flake : snowflakes)
{
flake.Update(ms);
// Remove offscreen flakes
if (flake.IsOffScreen())
RemoveSnowflake(flake);
// Invalid menu/Not in Menu
else if (zerokernel::Menu::instance && !zerokernel::Menu::instance->isInGame())
flake.Draw();
}
if (snowflakes.size() < static_cast<unsigned>(*snowflake_amount))
AddSnowflake();
}
// Clear all snowflakes
void Clear()
{
snowflakes.clear();
}
};
static SnowflakeManager snow_manager;
void DrawSnowflakes()
{
snow_manager.UpdateAndDraw();
}
template <typename T> void rvarCallback(settings::VariableBase<T> &, T)
{
snow_manager.Clear();
}
static InitRoutine init([]() {
time_t theTime = time(nullptr);
struct tm *aTime = localtime(&theTime);
int day = aTime->tm_mday;
int month = aTime->tm_mon + 1; // Month is 0 - 11, add 1 to get a jan-dec 1-12 concept
// We only want to draw around christmas time, let's use 12th of december+ til 12th of january
if ((month == 12 && day >= 12) || (month == 1 && day <= 12))
{
// Very early to not draw over the menu
EC::Register(EC::Draw, DrawSnowflakes, "draw_snowflakes", EC::ec_priority::very_early);
min_snowflake_speed_x.installChangeCallback(rvarCallback<float>);
max_snowflake_speed_x.installChangeCallback(rvarCallback<float>);
min_snowflake_speed_y.installChangeCallback(rvarCallback<float>);
max_snowflake_speed_y.installChangeCallback(rvarCallback<float>);
snowflake_speed_deviation_x.installChangeCallback(rvarCallback<float>);
snowflake_speed_deviation_y.installChangeCallback(rvarCallback<float>);
}
});
} // namespace snowflakes