Snowflakes!
This commit is contained in:
parent
f24bad1958
commit
4ae0040bdc
@ -22,7 +22,7 @@ bool settings::SettingsWriter::saveTo(std::string path, bool autosave)
|
|||||||
{
|
{
|
||||||
logging::Info("cat_save: Saving to %s", path.c_str());
|
logging::Info("cat_save: Saving to %s", path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
this->only_changed = true;
|
this->only_changed = true;
|
||||||
|
|
||||||
stream.open(path, std::ios::out);
|
stream.open(path, std::ios::out);
|
||||||
|
@ -11,8 +11,13 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/atlas.cpp"
|
|||||||
target_sources(cathook PRIVATE ${files})
|
target_sources(cathook PRIVATE ${files})
|
||||||
list(REMOVE_ITEM ignore_files ${files})
|
list(REMOVE_ITEM ignore_files ${files})
|
||||||
set(ignore_files ${ignore_files} CACHE INTERNAL "")
|
set(ignore_files ${ignore_files} CACHE INTERNAL "")
|
||||||
|
|
||||||
if(EnableGUI)
|
if(EnableGUI)
|
||||||
add_subdirectory(menu)
|
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()
|
endif()
|
||||||
|
|
||||||
if (EnableImGuiDrawing)
|
if (EnableImGuiDrawing)
|
||||||
|
197
src/visual/GUISnow.cpp
Normal file
197
src/visual/GUISnow.cpp
Normal 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
|
Reference in New Issue
Block a user