@@ -38,6 +38,8 @@
#include "Font.hpp"
#include "Label.hpp"
#include "Hud.hpp"
#include "ParticleManager.hpp"
#include "SimpleLineEmitter.hpp"

using namespace LM;
using namespace std;
@@ -73,6 +75,9 @@ GuiClient::GuiClient() {
if (m_config->get_bool("Debug", "physics_overlay")) {
add_extra_draw(m_debugdraw);
}

m_particle_manager = new ParticleManager(&m_root, 50000, true);
add_extra_draw(m_particle_manager);

m_map = NULL;
m_player = NULL;
@@ -95,8 +100,11 @@ GuiClient::~GuiClient() {
delete m_cache;
delete m_input;
delete m_debugdraw;
delete m_particle_manager;
delete m_view;
delete m_config;

m_graphical_weapons.clear();

// Clean up associations to prevent deletion failures
m_root.set_parent(NULL);
@@ -118,6 +126,8 @@ void GuiClient::preload() {
preload_image("blue_frontleg.png");
preload_image("blue_backleg.png");
preload_image("aim.png");
preload_image("blue_particle.png");
preload_image("red_particle.png");

// XXX un-hardcode
set_font(load_font("DustHomeMedium.ttf", 12), FONT_BADGE);
@@ -269,12 +279,25 @@ GraphicalMap* GuiClient::make_map() {
return new GraphicalMap(m_cache);
}

Weapon* GuiClient::make_weapon(WeaponReader& weapon_data) {
Weapon* GuiClient::make_weapon(uint32_t index, WeaponReader& weapon_data) {
GraphicalWeapon* gw = new GraphicalWeapon(m_cache);
Weapon* w = Weapon::new_weapon(weapon_data, gw);

if (w == NULL) {
delete gw;
}

while(index > m_graphical_weapons.size()) {
m_graphical_weapons.push_back(NULL);
}

if (index == m_graphical_weapons.size()) {
m_graphical_weapons.push_back(gw);
} else {
m_graphical_weapons.erase(m_graphical_weapons.begin() + index);
m_graphical_weapons.insert(m_graphical_weapons.begin() + index, gw);
}

return w;
}

@@ -352,6 +375,8 @@ void GuiClient::run() {
crosshair_bone.set_rotation(m_gcontrol->get_aim() * RADIANS_TO_DEGREES);

update_gui();

m_particle_manager->update(current_time - last_time);

m_window->redraw();
last_time = current_time;
@@ -377,6 +402,13 @@ void GuiClient::add_extra_draw(Widget* draw) {
m_view->add_child(draw, GameView::OVERLAY);
}

GraphicalWeapon* GuiClient::get_weapon(uint32_t id) {
if (id >= m_graphical_weapons.size()) {
return NULL;
}
return m_graphical_weapons.at(id);
}

void GuiClient::key_pressed(const KeyEvent& event) {
m_input_sink->key_pressed(event);

@@ -421,3 +453,67 @@ void GuiClient::round_over(const Packet& p) {

Client::round_over(p);
}

void GuiClient::weapon_discharged(const Packet& p) {
Client::weapon_discharged(p);

Player* player = get_player(p.weapon_discharged.player_id);
if (player == NULL) {
WARN("Player " << p.weapon_discharged.player_id << " fired, but does not exist.");
return;
}

GraphicalPlayer* g_player = static_cast<GraphicalPlayer*>(player);
if (g_player == NULL) {
WARN("Player " << p.weapon_discharged.player_id << " could not be converted to a graphical player.");
return;
}

Graphic* weapon_graphic = g_player->get_curr_visible_weapon_graphic();
if (weapon_graphic == NULL) {
return;
}

add_weapon_fired_emitter(g_player->get_current_weapon_id(), g_player, player->get_x(), player->get_y(), p.weapon_discharged.end_x, p.weapon_discharged.end_y, p.weapon_discharged.direction);
}

Packet* GuiClient::attempt_firing() {
Packet* packet = Client::attempt_firing();

if (packet == NULL) {
return packet;
}

if (m_player == NULL) {
WARN("Attempted to fire without having a player.");
return packet;
}

Graphic* weapon_graphic = m_player->get_curr_visible_weapon_graphic();
if (weapon_graphic == NULL) {
return packet;
}

add_weapon_fired_emitter(m_player->get_current_weapon_id(), m_player, m_player->get_x(), m_player->get_y(), packet->weapon_discharged.end_x, packet->weapon_discharged.end_y, packet->weapon_discharged.direction);

return packet;
}

void GuiClient::add_weapon_fired_emitter(int weapon_id, GraphicalPlayer* player, float player_x, float player_y, float end_x, float end_y, float rotation_rads) {
float start_x = player_x;
float start_y = player_y;
// Move the start position to be closer to the gun muzzle
Point offset_to_shoulder(0, -16);
offset_to_shoulder.rotate(player->get_rotation_radians());
Point offset_to_gun(50, 0);
offset_to_gun.rotate(player->get_rotation_radians() + player->get_gun_rotation_radians());
start_x += offset_to_shoulder.x + offset_to_gun.x;
start_y += offset_to_shoulder.y + offset_to_gun.y;

GraphicalWeapon* weapon = get_weapon(weapon_id);
if (weapon == NULL) {
DEBUG("No weapon found for generating a particle emitter.");
return;
}
weapon->generate_fired_emitter(m_particle_manager, m_cache, start_x, start_y, end_x, end_y, rotation_rads, player->get_team());
}
@@ -45,6 +45,7 @@ namespace LM {
class ConvolveKernel;
class Hud;
class Configuration;
class ParticleManager;

class GuiClient : public Client, public InputSink {
private:
@@ -64,8 +65,12 @@ namespace LM {
PhysicsDraw* m_debugdraw;
Configuration* m_config;

ParticleManager* m_particle_manager;

GraphicalPlayer* m_player;
GraphicalMap* m_map;

std::vector<GraphicalWeapon*> m_graphical_weapons;

ResourceCache* m_cache;
std::vector<std::string> m_preloaded_images;
@@ -107,7 +112,7 @@ namespace LM {

virtual GraphicalPlayer* make_player(const char* name, uint32_t id, char team);
virtual GraphicalMap* make_map();
virtual Weapon* make_weapon(WeaponReader& weapon_data);
virtual Weapon* make_weapon(uint32_t index, WeaponReader& weapon_data);

virtual void name_change(Player* player, const std::string& new_name);
virtual void team_change(Player* player, char new_team);
@@ -117,6 +122,8 @@ namespace LM {
virtual void run();
void update_gui();
void add_extra_draw(Widget* draw);

GraphicalWeapon* get_weapon(uint32_t id);

virtual void key_pressed(const KeyEvent& event);
virtual void mouse_moved(const MouseMotionEvent& event);
@@ -126,6 +133,10 @@ namespace LM {
virtual void disconnect();

virtual void round_over(const Packet& p);
virtual void weapon_discharged(const Packet& p);

virtual Packet* attempt_firing();
virtual void add_weapon_fired_emitter(int weapon_id, GraphicalPlayer* player, float player_x, float player_y, float end_x, float end_y, float rotation_rads);
};
}

@@ -32,12 +32,24 @@ using namespace std;
SimpleLineEmitter::SimpleLineEmitter(ParticleManager* manager, Point center, Image* image, DrawContext::BlendMode mode) : ParticleEmitter(manager, center, image, mode) {
m_lifetime = 0;
m_spawned_total = 0;
m_settings = NULL;
m_delete_settings = false;
m_leftover_diff = 0;
}

SimpleLineEmitter::~SimpleLineEmitter() {
if (m_delete_settings) {
delete m_settings;
}
}

void SimpleLineEmitter::init(const SimpleLineEmitterSettings* settings) {
void SimpleLineEmitter::init(const SimpleLineEmitterSettings* settings, bool delete_settings) {
if (m_delete_settings) {
delete m_settings;
}

m_delete_settings = delete_settings;

m_settings = settings;

// Initialize the vertex/color arrays to be at least the max expected particles
@@ -50,9 +62,6 @@ void SimpleLineEmitter::init(const SimpleLineEmitterSettings* settings) {
} else {
init_arrays(MAX_PARTICLES_AT_A_TIME);
}

m_leftover_diff = 0;
m_lifetime = 0;
}

bool SimpleLineEmitter::update(uint64_t timediff) {
@@ -157,3 +166,54 @@ void SimpleLineEmitter::set_endpoint(float x, float y) {
m_endpoint.y = y;
}

SimpleLineEmitterSettings* SimpleLineEmitter::parse_settings_string(string settings_string) {
SimpleLineEmitterSettings* settings = new SimpleLineEmitterSettings();

while(settings_string.length() > 0) {
int loc = settings_string.find("|");

if (loc < 0) {
break;
}

string token = settings_string.substr(0, loc);
int equalsloc = token.find("=");

if (equalsloc < 0) {
continue;
}

string name = token.substr(0, equalsloc);
string value = token.substr(equalsloc+1);

parse_param(settings, name, value);

settings_string = settings_string.substr(loc+1);
}

return settings;
}

void SimpleLineEmitter::parse_param(SimpleLineEmitterSettings* settings, string name, string value) {
if (name == "particle_speed") {
settings->particle_speed = atof(value.c_str());
} else if (name == "speed_variance") {
settings->speed_variance = atof(value.c_str());
} else if (name == "spawn_per_second") {
settings->spawn_per_second = atoi(value.c_str());
} else if (name == "spawn_variance") {
settings->spawn_variance = atoi(value.c_str());
} else if (name == "lifetime_millis") {
settings->lifetime_millis = atoi(value.c_str());
} else if (name == "lifetime_variance") {
settings->lifetime_variance = atoi(value.c_str());
} else if (name == "rotation_variance") {
settings->rotation_variance = atof(value.c_str());
} else if (name == "max_spawn") {
settings->max_spawn = atoi(value.c_str());
} else if (name == "emitter_stop_spawning_millis") {
settings->emitter_stop_spawning_millis = atoi(value.c_str());
} else if (name == "emitter_lifetime_millis") {
settings->emitter_lifetime_millis = atoi(value.c_str());
}
}
@@ -48,6 +48,8 @@ namespace LM {
uint64_t m_leftover_diff;
int m_spawned_total;
Point m_endpoint;

bool m_delete_settings;

const SimpleLineEmitterSettings* m_settings;

@@ -56,12 +58,16 @@ namespace LM {
SimpleLineEmitter(ParticleManager* manager, Point center, Image* image, DrawContext::BlendMode mode = DrawContext::BLEND_ADD);
virtual ~SimpleLineEmitter();

void init(const SimpleLineEmitterSettings* settings);
void init(const SimpleLineEmitterSettings* settings, bool delete_settings = false);

virtual bool update(uint64_t timediff);

void set_endpoint(Point point);
void set_endpoint(float x, float y);

static SimpleLineEmitterSettings* parse_settings_string(std::string settings_string);

static void parse_param(SimpleLineEmitterSettings* settings, std::string name, std::string value);
};
}

@@ -32,12 +32,25 @@ using namespace std;
SimpleRadialEmitter::SimpleRadialEmitter(ParticleManager* manager, Point center, Image* image, DrawContext::BlendMode mode) : ParticleEmitter(manager, center, image, mode) {
m_lifetime = 0;
m_spawned_total = 0;
m_settings = NULL;
m_delete_settings = false;
m_curr_rotation = 0;
m_leftover_diff = 0;
}

SimpleRadialEmitter::~SimpleRadialEmitter() {
if (m_delete_settings) {
delete m_settings;
}
}

void SimpleRadialEmitter::init(const SimpleRadialEmitterSettings* settings) {
void SimpleRadialEmitter::init(const SimpleRadialEmitterSettings* settings, bool delete_settings) {
if (m_delete_settings) {
delete m_settings;
}

m_delete_settings = delete_settings;

m_settings = settings;

// Initialize the vertex/color arrays to be at least the max expected particles
@@ -50,9 +63,6 @@ void SimpleRadialEmitter::init(const SimpleRadialEmitterSettings* settings) {
} else {
init_arrays(MAX_PARTICLES_AT_A_TIME);
}

m_leftover_diff = 0;
m_lifetime = 0;
}

bool SimpleRadialEmitter::update(uint64_t timediff) {
@@ -132,9 +142,9 @@ void SimpleRadialEmitter::init_particle(Particle* particle) {
particle->m_pos = get_center();
particle->m_prev_pos = particle->m_pos;
float speed = m_settings->particle_speed + (rand()/(float)RAND_MAX) * m_settings->speed_variance;
float dir = m_settings->rotation_rads + (rand()/(float)RAND_MAX) * m_settings->rotation_variance - m_settings->rotation_variance/2;
float dir = m_curr_rotation + m_settings->rotation_rads + (rand()/(float)RAND_MAX) * m_settings->rotation_variance - m_settings->rotation_variance/2;

particle->m_vel = Vector(speed * sin(dir), speed * cos(dir));
particle->m_vel = Vector(speed * cos(dir), speed * sin(dir));

particle->m_energy_left = m_settings->lifetime_millis + rand() % m_settings->lifetime_variance;
particle->m_initial_energy = particle->m_energy_left;
@@ -146,3 +156,61 @@ void SimpleRadialEmitter::init_particle(Particle* particle) {

particle->m_size = 1.0f;
}

SimpleRadialEmitterSettings* SimpleRadialEmitter::parse_settings_string(string settings_string) {
SimpleRadialEmitterSettings* settings = new SimpleRadialEmitterSettings();

while(settings_string.length() > 0) {
int loc = settings_string.find("|");

if (loc < 0) {
break;
}

string token = settings_string.substr(0, loc);
int equalsloc = token.find("=");

if (equalsloc < 0) {
continue;
}

string name = token.substr(0, equalsloc);
string value = token.substr(equalsloc+1);

parse_param(settings, name, value);

settings_string = settings_string.substr(loc+1);
}

return settings;
}

void SimpleRadialEmitter::parse_param(SimpleRadialEmitterSettings* settings, string name, string value) {
if (name == "particle_speed") {
settings->particle_speed = atof(value.c_str());
} else if (name == "speed_variance") {
settings->speed_variance = atof(value.c_str());
} else if (name == "spawn_per_second") {
settings->spawn_per_second = atoi(value.c_str());
} else if (name == "spawn_variance") {
settings->spawn_variance = atoi(value.c_str());
} else if (name == "lifetime_millis") {
settings->lifetime_millis = atoi(value.c_str());
} else if (name == "lifetime_variance") {
settings->lifetime_variance = atoi(value.c_str());
} else if (name == "rotation_rads") {
settings->rotation_rads = atof(value.c_str());
} else if (name == "rotation_variance") {
settings->rotation_variance = atof(value.c_str());
} else if (name == "max_spawn") {
settings->max_spawn = atoi(value.c_str());
} else if (name == "emitter_stop_spawning_millis") {
settings->emitter_stop_spawning_millis = atoi(value.c_str());
} else if (name == "emitter_lifetime_millis") {
settings->emitter_lifetime_millis = atoi(value.c_str());
}
}

void SimpleRadialEmitter::set_rotation(float rads) {
m_curr_rotation = rads;
}
@@ -48,17 +48,25 @@ namespace LM {
uint64_t m_lifetime;
uint64_t m_leftover_diff;
int m_spawned_total;
float m_curr_rotation;

const SimpleRadialEmitterSettings* m_settings;
bool m_delete_settings;

void init_particle(Particle* particle);
public:
SimpleRadialEmitter(ParticleManager* manager, Point center, Image* image, DrawContext::BlendMode mode = DrawContext::BLEND_ADD);
virtual ~SimpleRadialEmitter();

void init(const SimpleRadialEmitterSettings* settings);
void init(const SimpleRadialEmitterSettings* settings, bool delete_settings = false);

virtual bool update(uint64_t timediff);

static SimpleRadialEmitterSettings* parse_settings_string(std::string settings_string);

static void parse_param(SimpleRadialEmitterSettings* settings, std::string name, std::string value);

void set_rotation(float rads);
};
}