Commit 00b0b45e authored by louiz’'s avatar louiz’

Introduce the concept of graphical Effects, Particles, etc

parent 21d01bb4
......@@ -3,6 +3,8 @@
#include <utils/time.hpp>
#include <world/world_callbacks.hpp>
#include <world/task.hpp>
#include <gui/effects/explosion.hpp>
#include <world/location.hpp>
#include "orders.pb.h"
#include "requests.pb.h"
......@@ -374,6 +376,13 @@ void GameClient::on_entity_deleted(const Entity* entity)
this->current_selection.remove_from_selection(entity);
if (entity->get_type() == 2)
this->sounds_handler.play(SoundType::EmpExplode, false, 100.f);
if (entity->get_type() == 0)
{
auto location = entity->get<Location>();
if (location)
this->camera.add_effect(std::make_unique<Explosion>(location->position(), 10.f));
}
}
bool GameClient::tick()
......
......@@ -11,6 +11,7 @@
#include <network/client_base.hpp>
#include <network/tcp_socket.hpp>
#include <gui/screen/screen.hpp>
#include <gui/camera/camera.hpp>
#include <game/selection.hpp>
#include <game/turn_handler.hpp>
#include <gui/hud/debug_hud.hpp>
......
......@@ -5,6 +5,7 @@
#include <gui/sprites/tourbillon_sprite.hpp>
#include <gui/sprites/bullet_sprite.hpp>
#include <gui/sprites/emp_sprite.hpp>
#include <gui/effect.hpp>
#include <world/world.hpp>
#include <world/layer.hpp>
#include <world/entity.hpp>
......@@ -14,6 +15,7 @@
#include <game/game_client.hpp>
#include <climits>
#include <cstdlib>
#include <gui/effects/explosion.hpp>
#include <world/abilities.hpp>
#include <world/location.hpp>
......@@ -857,3 +859,8 @@ const GameClient* Camera::get_game_client() const
{
return this->game;
}
void Camera::add_effect(std::unique_ptr<Effect>&& effect)
{
this->effects.push_back(std::move(effect));
}
......@@ -36,6 +36,7 @@ class GameClient;
class Entity;
class EntitySprite;
class Task;
class Effect;
class Camera: public ScreenElement
{
......@@ -156,6 +157,8 @@ public:
void on_entity_task_changed(const Entity* entity, const Task* task);
void graphical_tick();
void add_effect(std::unique_ptr<Effect>&& effect);
const sf::Vector2u get_win_size() const;
/**
* The left position of the camera
......@@ -205,6 +208,10 @@ private:
Tileset tileset;
Fog fog;
public:
std::vector<std::unique_ptr<Effect>> effects;
/**
* Various accessors
*/
......
#ifndef EFFECT_HPP_INCLUDED
#define EFFECT_HPP_INCLUDED
#include <SFML/Graphics.hpp>
#include <gui/positionnable.hpp>
#include <utils/time.hpp>
#include <gui/sprites/world_sprite.hpp>
/**
* Defines a graphical effect that lives on the Camera. It can contains
* particles, light effects, etc.
*/
class Effect: public Positionnable, public WorldSprite
{
public:
Effect(const Position& pos, const utils::Duration& lifetime):
Positionnable(pos),
lifetime(lifetime),
life(lifetime)
{
}
~Effect() = default;
virtual void update(const utils::Duration& dt)
{
this->life -= dt;
this->on_update(dt);
}
const utils::Duration& current_life() const
{
return this->life;
}
Position get_world_pos() const override final
{
return this->position;
}
private:
virtual void on_update(const utils::Duration& dt) {}
/**
* Maximum duration of the effect.
*/
const utils::Duration lifetime;
/**
* Current remaining life. Equals lifetime at the beginning. When it
* reaches 0, the effect is destroyed.
*/
utils::Duration life;
Effect(const Effect&) = delete;
Effect(Effect&&) = delete;
Effect& operator=(const Effect&) = delete;
Effect& operator=(Effect&&) = delete;
};
#endif /* EFFECT_HPP_INCLUDED */
#include <gui/effects/explosion.hpp>
#include <gui/utils.hpp>
#include <chrono>
using namespace std::chrono_literals;
Explosion::Explosion(const Position& pos, const float height):
ParticleEffect(pos, 2s, height)
{
for (auto i = 0; i < 100; ++i)
{
auto n = std::uniform_int_distribution<int>(0, 3)(gui::rng());
Particle particle(800ms);
particle.sprite.setTexture(this->texture);
particle.sprite.setTextureRect({{64 * n, 64},
{64, 64}});
particle.sprite.setOrigin(64/2, 64/2);
particle.start_size = 0.3f + gui::frand() * 0.4f;
auto vel = gui::random_dir();
vel.x = vel.x * (100 + 300*gui::frand());
vel.y = vel.y * (100 + 300*gui::frand());
particle.velocity = vel;
sf::Color a = sf::Color::Black;
sf::Color b = sf::Color(120, 120, 120, 200);
particle.sprite.setColor(gui::mix(a, b, gui::frand()));
particle.friction = 8.f;
particle.gravity = -600.f;
this->particles.push_back(std::move(particle));
}
}
#ifndef EXPLOSION_HPP_INCLUDED
#define EXPLOSION_HPP_INCLUDED
#include <gui/particle_effect.hpp>
class Explosion: public ParticleEffect
{
public:
Explosion(const Position& pos, const float height);
~Explosion() = default;
private:
Explosion(const Explosion&) = delete;
Explosion(Explosion&&) = delete;
Explosion& operator=(const Explosion&) = delete;
Explosion& operator=(Explosion&&) = delete;
};
#endif /* EXPLOSION_HPP_INCLUDED */
#include <gui/effects/smoke_trail.hpp>
#include <gui/utils.hpp>
#include <utils/math.hpp>
#include <chrono>
using namespace std::chrono_literals;
SmokeTrail::SmokeTrail(const Position& pos, const float height):
ParticleEffect(pos, 1s, height)
{
std::random_device rd;
std::mt19937 gen(rd());
auto n = std::uniform_int_distribution<int>(0, 3)(gui::rng());
Particle particle(800ms);
particle.sprite.setTexture(this->texture);
particle.sprite.setTextureRect({{64 * n, 64},
{64, 64}});
particle.sprite.setOrigin(64/2, 64/2);
particle.start_size = 0.2f + gui::frand() * 0.5f;
particle.velocity.x = 0;
particle.velocity.y = -(500 + 1000*gui::frand());
particle.sprite.setColor(gui::mix(sf::Color(120, 120, 120, 200),
sf::Color::White, gui::frand()));
particle.friction = 10.f;
particle.gravity = -300.f;
this->particles.push_back(std::move(particle));
}
#ifndef SMOKE_TRAIL_HPP_INCLUDED
#define SMOKE_TRAIL_HPP_INCLUDED
#include <gui/particle_effect.hpp>
class SmokeTrail: public ParticleEffect
{
public:
SmokeTrail(const Position& pos, const float height);
~SmokeTrail() = default;
private:
SmokeTrail(const SmokeTrail&) = delete;
SmokeTrail(SmokeTrail&&) = delete;
SmokeTrail& operator=(const SmokeTrail&) = delete;
SmokeTrail& operator=(SmokeTrail&&) = delete;
};
#endif /* SMOKE_TRAIL_HPP_INCLUDED */
#ifndef PARTICLE_HPP_INCLUDED
#define PARTICLE_HPP_INCLUDED
#include <SFML/Graphics.hpp>
#include <utils/time.hpp>
#include <logging/logging.hpp>
using namespace std::chrono_literals;
class Particle
{
friend class ParticleEffect;
friend class Explosion;
friend class SmokeTrail;
public:
Particle(const Particle&) = default;
Particle(Particle&&) = default;
Particle& operator=(const Particle&) = default;
Particle& operator=(Particle&&) = default;
Particle(const utils::Duration& lifetime):
lifetime(lifetime),
life(lifetime),
sprite{},
position{},
velocity{},
rotation(0.f),
rotation_speed(0.f),
friction(0.f),
gravity(0.f),
start_size(1.f),
end_size(0.f)
{}
~Particle() = default;
void update(const utils::Duration& dt)
{
this->life -= dt;
const float fsec = utils::sec(dt).count();
this->position += this->velocity * fsec;
this->rotation += this->rotation_speed * fsec;
this->velocity.y += this->gravity * fsec;
this->velocity *= 1.0f - (this->friction * fsec);
this->rotation_speed *= this->friction * fsec;
}
void draw(sf::RenderTarget& surface, const sf::RenderStates& states) const
{
auto plife = static_cast<float>(this->life.count()) / this->lifetime.count();
auto size = this->end_size + ((this->start_size - this->end_size) * plife);
this->sprite.setScale(size, size);
this->sprite.setPosition(this->position);
surface.draw(this->sprite, states);
}
private:
utils::Duration lifetime;
utils::Duration life;
mutable sf::Sprite sprite;
sf::Vector2<float> position;
sf::Vector2<float> velocity;
float rotation;
float rotation_speed;
float friction;
float gravity;
float start_size;
float end_size;
};
#endif /* PARTICLE_HPP_INCLUDED */
#include <gui/particle_effect.hpp>
sf::Texture ParticleEffect::texture;
#ifndef PARTICLE_EFFECT_HPP_INCLUDED
#define PARTICLE_EFFECT_HPP_INCLUDED
#include <gui/effect.hpp>
#include <gui/particle.hpp>
#include <vector>
#include <chrono>
using namespace std::chrono_literals;
/**
* An Effect that contains a list of particles
*/
class ParticleEffect: public Effect
{
public:
ParticleEffect(const Position& pos, const utils::Duration& lifetime):
ParticleEffect(pos, lifetime, 0.f)
{}
ParticleEffect(const Position& pos, const utils::Duration& lifetime, const float height):
Effect(pos, lifetime),
height(height)
{}
~ParticleEffect() = default;
void update(const utils::Duration& dt) override final
{
for (auto it = this->particles.begin(); it != this->particles.end(); )
{
it->update(dt);
if (it->life <= 0s)
it = this->particles.erase(it);
else
++it;
}
Effect::update(dt);
}
void draw(sf::RenderTarget& surface, const sf::RenderStates& states) const override final
{
auto hstates = states;
hstates.transform.translate(0, -this->height);
for (auto& particle: this->particles)
particle.draw(surface, hstates);
}
static sf::Texture texture;
protected:
std::vector<Particle> particles;
const float height;
ParticleEffect(const ParticleEffect&) = delete;
ParticleEffect(ParticleEffect&&) = delete;
ParticleEffect& operator=(const ParticleEffect&) = delete;
ParticleEffect& operator=(ParticleEffect&&) = delete;
};
#endif /* PARTICLE_EFFECT_HPP_INCLUDED */
#ifndef POSITIONNABLE_HPP_INCLUDED
#define POSITIONNABLE_HPP_INCLUDED
#include <world/position.hpp>
class Positionnable
{
public:
Positionnable(const Position& pos):
position(pos)
{}
~Positionnable() = default;
protected:
Position position;
private:
Positionnable(const Positionnable&) = delete;
Positionnable(Positionnable&&) = delete;
Positionnable& operator=(const Positionnable&) = delete;
Positionnable& operator=(Positionnable&&) = delete;
};
#endif /* POSITIONNABLE_HPP_INCLUDED */
#include <gui/utils.hpp>
#include <utils/math.hpp>
#include <cstdint>
namespace gui
{
sf::Color mix(const sf::Color& first, const sf::Color& second,
const float coef)
{
......@@ -14,3 +17,23 @@ sf::Color mix(const sf::Color& first, const sf::Color& second,
static_cast<uint8_t>(a)};
}
std::mt19937& rng()
{
static std::random_device rd;
static std::mt19937 gen(rd());
return gen;
}
float frand()
{
return std::uniform_real_distribution<float>(0, 1)(rng());
}
sf::Vector2<float> random_dir()
{
return normalize(sf::Vector2<float>(frand() - 0.5f, frand() - 0.5f));
}
} // namespace gui
......@@ -2,15 +2,20 @@
#define GUI_COLORS_HPP_INCLUDED
#include <SFML/Graphics.hpp>
#include <random>
/**
* Mix two colors to create a new color. The coef arg tels us how much of
* the second color there is. If the coef is 1.0, the color will be the 2nd,
* if it’s 0.0 it will be the first, and in between, it will mix
* approprietely.
*/
sf::Color mix(const sf::Color& first, const sf::Color& second,
const float coef);
namespace gui
{
sf::Color mix(const sf::Color& first, const sf::Color& second,
const float coef);
float frand();
sf::Vector2<float> random_dir();
std::mt19937& rng();
}
#endif /* GUI_COLORS_HPP_INCLUDED */
#ifndef MATH_HPP_INCLUDED
#define MATH_HPP_INCLUDED
#include <SFML/System/Vector2.hpp>
template<typename T>
inline sf::Vector2<T> normalize(const sf::Vector2<T>& v)
{
T l = static_cast<T>(1.0f/::sqrtf(v.x*v.x + v.y*v.y));
return sf::Vector2<T>(v.x*l, v.y*l);
}
#endif /* MATH_HPP_INCLUDED */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment