From efcf1a88cd0d0bee3973705b5975827be97f5a3a Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Fri, 6 Jan 2017 08:51:53 -0500 Subject: particles, rain --- include/common.hpp | 4 +-- include/particle.hpp | 42 ++++++++++++++++++++++ include/texture.hpp | 1 + include/weather.hpp | 67 +++++++++++++++++++++++++++++++++++ include/world.hpp | 39 +++------------------ main.cpp | 9 +++-- src/engine.cpp | 7 ++++ src/inventory.cpp | 9 +++-- src/particle.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/texture.cpp | 6 ++-- src/world.cpp | 77 ++++++++++------------------------------ xml/!town.xml | 1 + 12 files changed, 258 insertions(+), 103 deletions(-) create mode 100644 include/particle.hpp create mode 100644 include/weather.hpp create mode 100644 src/particle.cpp diff --git a/include/common.hpp b/include/common.hpp index 19af420..71039c7 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -91,11 +91,11 @@ struct vec2 { } template - const vec2 operator+(const T &n) { + vec2 operator+(T n) const { return vec2 (x + n, y + n); } - const vec2 operator+(const vec2 &v) { + vec2 operator+(const vec2 &v) { return vec2 (x + v.x, y + v.y); } diff --git a/include/particle.hpp b/include/particle.hpp new file mode 100644 index 0000000..d9aa29f --- /dev/null +++ b/include/particle.hpp @@ -0,0 +1,42 @@ +#ifndef PARTICLE_HPP_ +#define PARTICLE_HPP_ + +#include + +#include + +#include + +enum class ParticleType : char { + Drop, + Confetti +}; + +struct Particle { + vec2 location; + vec2 velocity; + ParticleType type; + int timeLeft; + + Particle(vec2 p, ParticleType t = ParticleType::Drop) + : location(p), type(t), timeLeft(3000) {} // TODO times +} __attribute__ ((packed)); + +class ParticleSystem : public entityx::System { +private: + std::vector parts; + bool max; + +public: + ParticleSystem(int count = 1024, bool m = false); + + void add(const vec2& pos, const ParticleType& type); + void addMultiple(const int& count, const ParticleType& type, std::function f); + + void render(void) const; + void update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) override; + + int getCount(void) const; +}; + +#endif // PARTICLE_HPP_ diff --git a/include/texture.hpp b/include/texture.hpp index e9082b3..878955e 100644 --- a/include/texture.hpp +++ b/include/texture.hpp @@ -91,6 +91,7 @@ namespace Colors { extern ColorTex white; /**< A solid white texture. */ extern ColorTex black; /**< A solid black texture. */ extern ColorTex red; /**< A solid red texture. */ + extern ColorTex blue; /**< A solid blue texture. */ /** * Creates the colors. diff --git a/include/weather.hpp b/include/weather.hpp new file mode 100644 index 0000000..f7b53f1 --- /dev/null +++ b/include/weather.hpp @@ -0,0 +1,67 @@ +#ifndef WEATHER_HPP_ +#define WEATHER_HPP_ + +#include + +#include +#include + +/** + * The weather type enum. + * This enum contains every type of weather currently implemented in the game. + * Weather is set by the world somewhere. + */ +enum class Weather : unsigned char { + Sunny = 0, /**< Sunny */ + Rainy, /**< Rain */ + Snowy, /**< Snow */ + count +}; + +constexpr const char *weatherStrings[] = { + "Sunny", + "Rainy", + "Snowy" +}; + +class WeatherSystem : public entityx::System { +private: + Weather weather; + +public: + WeatherSystem(Weather w = Weather::Sunny) + : weather(w) {} + + void update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) override { + (void)en; + (void)ev; + (void)dt; + + static auto& partSystem = *game::engine.getSystem(); + + switch (weather) { + case Weather::Sunny: + break; + case Weather::Rainy: + partSystem.add(vec2(offset.x - game::SCREEN_WIDTH / 2 + randGet() % game::SCREEN_WIDTH, + offset.y + game::SCREEN_HEIGHT / 2 + 100), + ParticleType::Drop); + break; // TODO + case Weather::Snowy: + break; // TODO + default: + break; + } + } + + inline void setWeather(const std::string& w) { + for (int i = 0; i < static_cast(Weather::count); i++) { + if (w == weatherStrings[i]) { + weather = static_cast(i); + return; + } + } + } +}; + +#endif // WEATHER_HPP_ diff --git a/include/world.hpp b/include/world.hpp index 8864d30..14c64d0 100644 --- a/include/world.hpp +++ b/include/world.hpp @@ -27,37 +27,17 @@ enum class WorldBGType : unsigned int { Forest = 0 /**< A forest theme. */ }; -/** - * The weather type enum. - * This enum contains every type of weather currently implemented in the game. - * Weather is set by the world somewhere. - */ -enum class WorldWeather : unsigned char { - None = 0, /**< None (sunny) */ - Rain, /**< Rain */ - Snowy /**< Snow */ -}; - -/** - * Strings to represent each type of weather. - */ -constexpr const char* WorldWeatherString[3] = { - "None", - "Rainy", - "Snowy" -}; - /** * The line structure. * This structure is used to store the world's ground, stored in vertical * lines. Dirt color and grass properties are also kept track of here. */ -typedef struct { +struct WorldData { bool grassUnpressed; /**< squishes grass if false */ float grassHeight[2]; /**< height of the two grass blades */ float groundHeight; /**< height of the 'line' */ unsigned char groundColor; /**< a value that affects the ground's color */ -} WorldData; +} __attribute__ ((packed)); /** * Defines how many game ticks it takes to go from day to night or vice versa. @@ -129,11 +109,6 @@ private: */ WorldData2 world; - /** - * The current state of weather in the world. - */ - WorldWeather weather; - /** * SDL's object for handling the background music. */ @@ -176,22 +151,16 @@ public: inline float getWidth(void) const { return world.startX * -2.0f; } + float isAboveGround(const vec2& p) const; + void receive(const BGMToggleEvent &bte); void update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) override; void render(void); - inline const std::string getWeatherStr(void) const - { return WorldWeatherString[static_cast(weather)]; } - - inline const WorldWeather& getWeatherId(void) const - { return weather; } - inline const std::string& getXMLFile(void) const { return currentXMLFile; } - void setWeather(const std::string &s); - void detect(entityx::TimeDelta dt); void goWorldLeft(Position& p); diff --git a/main.cpp b/main.cpp index 17499f7..2e33a83 100644 --- a/main.cpp +++ b/main.cpp @@ -25,6 +25,7 @@ using namespace tinyxml2; #include #include #include +#include /** * The currently used folder to look for XML files in. @@ -311,6 +312,7 @@ void render() { // draw the world and player game::engine.getSystem()->render(); + game::engine.getSystem()->render(); // draw the player's inventory //player->inv->draw(); @@ -327,9 +329,10 @@ void render() { if (ui::debug) { auto pos = game::engine.getSystem()->getPosition(); ui::putText(offset.x - SCREEN_WIDTH2, (offset.y + SCREEN_HEIGHT2) - ui::fontSize, - "loc: %s\noffset: %s\nfps: %d\nticks: %d\nxml: %s", - pos.toString().c_str(), offset.toString().c_str(), fps, - game::time::getTickCount(), game::engine.getSystem()->getXMLFile().c_str()); + "loc: %s\noffset: %s\nfps: %d\nticks: %d\npcount: %d\nxml: %s", + pos.toString().c_str(), offset.toString().c_str(), fps, + game::time::getTickCount(), game::engine.getSystem()->getCount(), + game::engine.getSystem()->getXMLFile().c_str()); } // draw the menu diff --git a/src/engine.cpp b/src/engine.cpp index 48e384d..aa0db73 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include Engine::Engine(void) : shouldRun(true), systems(game::entities, game::events) @@ -30,6 +32,9 @@ void Engine::init(void) { systems.add(); systems.add(); + systems.add(); + systems.add(); + systems.configure(); ui::initSounds(); @@ -57,6 +62,8 @@ void Engine::update(entityx::TimeDelta dt) systems.update(dt); systems.update(dt); //systems.update(dt); // doesn't do anything + systems.update(dt); + systems.update(dt); } diff --git a/src/inventory.cpp b/src/inventory.cpp index fb999ea..480c803 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -27,16 +27,19 @@ void InventorySystem::update(entityx::EntityManager &en, entityx::EventManager & (void)ev; (void)dt; - vec2 start = vec2(offset.x, 100);// - game::SCREEN_WIDTH / 2 + 20, game::SCREEN_HEIGHT - 40); + // TODO TODO TODO TODO until we do something + return; + + //vec2 start = vec2(offset.x, 100);// - game::SCREEN_WIDTH / 2 + 20, game::SCREEN_HEIGHT - 40); //std::cout << start.x << ' ' << start.y << std::endl; - Render::textShader.use(); + /*Render::textShader.use(); glActiveTexture(GL_TEXTURE0); Colors::black.use(); Render::useShader(&Render::textShader); Render::drawRect(start, start + 20, -9.9f); - Render::textShader.unuse(); + Render::textShader.unuse();*/ } void InventorySystem::receive(const KeyDownEvent &kde) diff --git a/src/particle.cpp b/src/particle.cpp new file mode 100644 index 0000000..8090e2d --- /dev/null +++ b/src/particle.cpp @@ -0,0 +1,99 @@ +#include + +#include +#include +#include + +ParticleSystem::ParticleSystem(int count, bool m) + : max(m) +{ + parts.reserve(count); +} + +void ParticleSystem::add(const vec2& pos, const ParticleType& type) +{ + // TODO enforce max + //if (max && parts.size() >= std::end(parts)) + // return; + + parts.emplace_back(pos, type); +} + +void ParticleSystem::addMultiple(const int& count, const ParticleType& type, std::function f) +{ + int togo = count; + while (togo-- > 0) + parts.emplace_back(f(), type); +} + +void ParticleSystem::render(void) const +{ + static const GLfloat tex[12] = { + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 + }; + + Render::worldShader.use(); + Render::worldShader.enable(); + Colors::blue.use(); + + for (const auto& p : parts) { + GLfloat coord[18] = { + p.location.x, p.location.y, -1, + p.location.x, p.location.y + 5, -1, + p.location.x + 5, p.location.y + 5, -1, + p.location.x + 5, p.location.y + 5, -1, + p.location.x + 5, p.location.y, -1, + p.location.x, p.location.y, -1 + }; + + glVertexAttribPointer(Render::worldShader.coord, 3, GL_FLOAT, GL_FALSE, 0, coord); + glVertexAttribPointer(Render::worldShader.tex, 2, GL_FLOAT, GL_FALSE, 0, tex); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + + Render::worldShader.disable(); + Render::worldShader.unuse(); +} + +void ParticleSystem::update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) +{ + (void)en; + (void)ev; + (void)dt; // TODO use for time to die + + auto& worldSystem = *game::engine.getSystem(); + + for (auto part = std::begin(parts); part != std::end(parts); part++) { + auto& p = *part; + + // update timers + p.timeLeft -= dt; + if (p.timeLeft <= 0) + parts.erase(part); + + // update movement + switch (p.type) { + case ParticleType::Drop: + if (p.velocity.y > -.5) + p.velocity.y -= 0.001f; + break; + case ParticleType::Confetti: + break; + } + + // really update movement + p.location.x += p.velocity.x * dt; + p.location.y += p.velocity.y * dt; + + // world collision + auto height = worldSystem.isAboveGround(p.location); + if (height != 0) + p.location.y = height + 5, p.velocity.y = randGet() % 10 / 40.0f; + } +} + +int ParticleSystem::getCount(void) const +{ + return parts.size(); +} + diff --git a/src/texture.cpp b/src/texture.cpp index 640b06e..da39ec0 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -8,11 +8,13 @@ namespace Colors ColorTex white; ColorTex black; ColorTex red; + ColorTex blue; void init(void) { white = ColorTex(Color(255, 255, 255)); - black = ColorTex(Color(0, 0, 0)); - red = ColorTex(Color(255, 0, 0)); + black = ColorTex(Color(0, 0, 0 )); + red = ColorTex(Color(255, 0, 0 )); + blue = ColorTex(Color(0, 0, 255)); } } diff --git a/src/world.cpp b/src/world.cpp index b10edac..6cce2b0 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -21,6 +21,7 @@ using namespace std::literals::chrono_literals; #include #include #include +#include // local library headers #include @@ -74,12 +75,10 @@ const unsigned int GRASS_HEIGHT = HLINES(4); std::string currentXML; // pathnames of images for world themes -using StyleList = std::array; - +using StyleList = std::array; static const std::vector bgPaths = { { // Forest - "bg.png", // daytime background - "bgn.png", // nighttime background + "bg.png", // sky/background "bgFarMountain.png", // layer 1 (furthest) "forestTileFar.png", // layer 2 "forestTileBack.png", // layer 3 @@ -154,6 +153,15 @@ void WorldSystem::generate(int width) world.startX = HLINES(width * -0.5); } +float WorldSystem::isAboveGround(const vec2& p) const +{ + int line = std::clamp(static_cast((p.x - world.startX) / game::HLINE), + 0, static_cast(world.data.size())); + + const auto& gh = world.data[line].groundHeight; + return p.y >= gh ? 0 : gh; +} + static Color ambient; bool WorldSystem::save(void) @@ -279,7 +287,8 @@ void WorldSystem::load(const std::string& file) // weather tag else if (tagName == "weather") { - setWeather(wxml->GetText()); + game::engine.getSystem()->setWeather(wxml->GetText()); + //setWeather(wxml->GetText()); } // link tags @@ -641,7 +650,7 @@ loadWorldFromXMLNoSave(std::string path) { }*/ WorldSystem::WorldSystem(void) - : weather(WorldWeather::None), bgmObj(nullptr) {} + : bgmObj(nullptr) {} WorldSystem::~WorldSystem(void) { @@ -664,10 +673,6 @@ void WorldSystem::render(void) // world width in pixels int width = HLINES(world.data.size()); - // used for alpha values of background textures - int alpha; - - static bool ambientUpdaterStarted = false; if (!ambientUpdaterStarted) { ambientUpdaterStarted = true; @@ -686,34 +691,12 @@ void WorldSystem::render(void) thAmbient.detach(); } - - switch (weather) { - case WorldWeather::Snowy: - alpha = 150; - break; - case WorldWeather::Rain: - alpha = 0; - break; - default: - alpha = 255 - worldShade * 4; - break; - } - // shade value for GLSL float shadeAmbient = std::max(0.0f, static_cast(-worldShade) / 50 + 0.5f); // 0 to 1.5f if (shadeAmbient > 0.9f) shadeAmbient = 1; - // draw background images. - GLfloat tex_coord[] = { 0.0f, 1.0f, - 1.0f, 1.0f, - 1.0f, 0.0f, - - 1.0f, 0.0f, - 0.0f, 0.0f, - 0.0f, 1.0f,}; - // TODO scroll backdrop GLfloat bgOff = game::time::getTickCount() / static_cast(DAY_CYCLE * 2); @@ -755,16 +738,6 @@ void WorldSystem::render(void) offset.x - backgroundOffset.x - 5, offset.y - backgroundOffset.y, 9.9f }; - GLfloat fron_tex_coord[] = { - offset.x - backgroundOffset.x - 5, offset.y + backgroundOffset.y, 9.8f, - offset.x + backgroundOffset.x + 5, offset.y + backgroundOffset.y, 9.8f, - offset.x + backgroundOffset.x + 5, offset.y - backgroundOffset.y, 9.8f, - - offset.x + backgroundOffset.x + 5, offset.y - backgroundOffset.y, 9.8f, - offset.x - backgroundOffset.x - 5, offset.y - backgroundOffset.y, 9.8f, - offset.x - backgroundOffset.x - 5, offset.y + backgroundOffset.y, 9.8f - }; - // rendering!! glActiveTexture(GL_TEXTURE0); @@ -785,10 +758,10 @@ void WorldSystem::render(void) makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions(0, back_tex_coord, scrolling_tex_coord, 6); - bgTex++; - glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.3 - static_cast(alpha) / 255.0f); - - makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions(0, fron_tex_coord, tex_coord, 6); + // no more night bg + //bgTex++; + //glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.3 - static_cast(alpha) / 255.0f); + //makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions(0, fron_tex_coord, tex_coord, 6); // TODO make stars dynamic /*static GLuint starTex = Texture::loadTexture("assets/style/classic/bg/star.png"); @@ -1122,18 +1095,6 @@ void WorldSystem::receive(const BGMToggleEvent &bte) } } -void WorldSystem::setWeather(const std::string &s) -{ - for (unsigned int i = 3; i--;) { - if (WorldWeatherString[i] == s) { - weather = static_cast(i); - return; - } - } - - weather = WorldWeather::None; -} - void WorldSystem::update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) { (void)en; diff --git a/xml/!town.xml b/xml/!town.xml index d98e8c6..c197d8d 100644 --- a/xml/!town.xml +++ b/xml/!town.xml @@ -4,6 +4,7 @@