Commit 6f7ced4a authored by louiz’'s avatar louiz’

Introduce the spawn work, for building.

Also fix the selection, properly handling a mouse selection containing
buildings etc. Also fix the archive building image.
parent 5b14a9a2
......@@ -2,6 +2,7 @@
- name: Tee
speed: 3
width: 15
spawn_duration: 10
actions:
- type: page # just goes to a different action page
value: 1
......@@ -19,6 +20,12 @@
sprite: building_archive
build_time: 10
cells: 1 # size in map squares
actions:
- type: spawn
value: 0
page: 0
position: 0
image: unit60.png
--- ####### Spells
- name: boost
......
......@@ -19,6 +19,7 @@ class Fix16: public Serializable
Fix16() { value = 0; }
Fix16(const Fix16 &inValue) { value = inValue.value; }
Fix16(const int inValue) { assert(inValue <= 32767); value = fix16_from_int(inValue); }
Fix16(const unsigned int inValue) { assert(inValue <= 32767); value = fix16_from_int(inValue); }
// Fix16(const unsigned int inValue) { value = fix16_from_int(inValue); }
// Fix16(const fix16_t inValue) { value = inValue; }
Fix16(const float inValue) { value = fix16_from_float(inValue); }
......
......@@ -90,4 +90,24 @@ DoBuildEvent::DoBuildEvent(const Command* command):
else
this->valid = true;
}
SpawnEvent::SpawnEvent(const Command* command)
{
if (this->from_string(std::string(command->body,
command->body_size).c_str()) == false)
this->valid = false;
else
this->valid = true;
}
DoSpawnEvent::DoSpawnEvent(const Command* command):
ActionEvent("SPAWN")
{
if (this->from_string(std::string(command->body,
command->body_size).c_str()) == false)
this->valid = false;
else
this->valid = true;
}
unsigned long int Event::current_id = 0;
......@@ -263,5 +263,65 @@ private:
DoBuildEvent& operator=(const DoBuildEvent&);
};
class SpawnEvent: virtual public Event
{
public:
SpawnEvent(): Event() {}
SpawnEvent(const Command*);
SpawnEvent(const SpawnEvent& e):
Event(),
type_id(e.type_id),
actor(e.actor)
{ }
virtual void serialize(boost::archive::text_oarchive& ar, const unsigned int v)
{
Event::serialize(ar, v);
ar & type_id & actor;
}
virtual void serialize(boost::archive::text_iarchive& ar, const unsigned int v)
{
Event::serialize(ar, v);
ar & type_id & actor;
}
/**
* The type_id of the unit to spawn.
*/
unsigned short type_id;
/**
* The id of the building thas has to spawn the unit.
*/
unsigned short actor;
private:
SpawnEvent& operator=(const SpawnEvent&);
};
class DoSpawnEvent: public ActionEvent, public SpawnEvent
{
public:
DoSpawnEvent(const Command*);
DoSpawnEvent(const SpawnEvent& o):
ActionEvent("SPAWN"),
SpawnEvent(o)
{
}
~DoSpawnEvent() {};
virtual void serialize(boost::archive::text_oarchive& ar, const unsigned int v)
{
ActionEvent::serialize(ar, v);
SpawnEvent::serialize(ar, v);
}
virtual void serialize(boost::archive::text_iarchive& ar, const unsigned int v)
{
ActionEvent::serialize(ar, v);
SpawnEvent::serialize(ar, v);
}
private:
DoSpawnEvent(const DoSpawnEvent&);
DoSpawnEvent& operator=(const DoSpawnEvent&);
};
#endif // __EVENT_HPP__
/**@}*/
......@@ -158,14 +158,24 @@ void Camera::set_mouse_selection_to_selection()
mouse_pos.y += this->y;
// First check the number of entities inside the selection. If it's 0, do
// nothing
uint n = 0;
unsigned int number_of_units = 0;
for (std::list<Unit*>::iterator it = this->world->units.begin(); it != this->world->units.end(); ++it)
if (this->mouse_selection.contains(mouse_pos,
this->world_to_camera_position((*it)->pos),
(*it)->width + 4))
n++;
if (n > 0)
number_of_units++;
unsigned int number_of_buildings = 0;
for (std::list<Building*>::iterator it = this->world->buildings.begin(); it != this->world->buildings.end(); ++it)
{
Position pos((*it)->x * CELL_SIZE + CELL_SIZE / 2,
(*it)->y * CELL_SIZE + CELL_SIZE / 2);
if (this->mouse_selection.contains(mouse_pos, this->world_to_camera_position(pos)))
number_of_buildings++;
}
log_warning("in mouse selection: Number of units: " << number_of_units << " and buildings: " << number_of_buildings);
if (number_of_buildings + number_of_units > 0)
{
bool new_unit_was_selected = false;
for (std::list<Unit*>::iterator it = this->world->units.begin(); it != this->world->units.end(); ++it)
{
if (this->mouse_selection.contains(mouse_pos,
......@@ -173,7 +183,32 @@ void Camera::set_mouse_selection_to_selection()
(*it)->width + 4))
{
if (this->world->is_entity_selected(*it) == false)
this->world->select_entity(*it);
{
new_unit_was_selected = true;
this->world->select_entity(*it);
}
}
else
{
if (this->world->is_entity_selected(*it) == true)
this->world->unselect_entity(*it);
}
}
for (std::list<Building*>::iterator it = this->world->buildings.begin(); it != this->world->buildings.end(); ++it)
{
Position pos((*it)->x * CELL_SIZE + CELL_SIZE / 2,
(*it)->y * CELL_SIZE + CELL_SIZE / 2);
if (this->mouse_selection.contains(mouse_pos, this->world_to_camera_position(pos)))
{
if (this->world->is_entity_selected(*it) == false)
{
// We select the building only if no unit was added to the
// selection. This way we can box an area and select only the units
// inside it, because that's what we want most of the time when
// doing that.
if (new_unit_was_selected == false)
this->world->select_entity(*it);
}
}
else
{
......@@ -301,6 +336,27 @@ void Camera::draw()
this->win->draw(tile->sprite);
}
}
Building* building;
for (std::list<Building*>::iterator it = this->world->buildings.begin(); it != this->world->buildings.end(); ++it)
{
building = *it;
Position pos(building->x * static_cast<short>(CELL_SIZE), building->y * static_cast<short>(CELL_SIZE));
this->world->get_cell_at_position(pos, cellx, celly);
sf::Vector2u entpos = this->world_to_camera_position(pos);
if ((celly == y) && ((entpos.x > this->x) && (entpos.x < this->x + win_size.x) &&
(entpos.y > this->y) && (entpos.y < this->y + win_size.y)))
{
// actually call sprite->draw. Let the BuildingSprite draw itself at the position.
sf::Sprite sprite = this->screen->building_sprites[building->type_id - this->world->number_of_units_models()]->get_cursor_sprite();
sprite.setPosition(entpos.x - this->x, entpos.y - this->y - LAYER_HEIGHT);
this->win->draw(sprite);
// this->draw_unit(entity, entpos.x - this->x, entpos.y - this->y,
// this->mouse_selection.contains(mouse_pos,
// entpos, entity->width + 4) ||
// this->screen->is_entity_hovered(entity),
// rectangle);
}
}
// Draw entites on that line.
std::vector<Entity*> entities_at_that_level = entities[level];
int i = 0;
......@@ -313,10 +369,10 @@ void Camera::draw()
(entpos.y > this->y) && (entpos.y < this->y + win_size.y)))
{
this->draw_unit(entity, entpos.x - this->x, entpos.y - this->y,
this->mouse_selection.contains(mouse_pos,
entpos, entity->width + 4) ||
this->screen->is_entity_hovered(entity),
rectangle);
this->mouse_selection.contains(mouse_pos,
entpos, entity->width + 4) ||
this->screen->is_entity_hovered(entity),
rectangle);
}
}
level++;
......
......@@ -28,7 +28,7 @@ public:
* Returns whether or not the mouse selection contains that point.
*/
bool contains(const sf::Vector2i&,
const sf::Vector2u&, const uint width = 0) const;
const sf::Vector2u&, const uint width = 1) const;
private:
MouseSelection(const MouseSelection&);
MouseSelection& operator=(const MouseSelection&);
......
......@@ -37,6 +37,8 @@ int main()
boost::bind(&ClientWorld::path_callback, world, _1));
c->install_callback("BUILD",
boost::bind(&ClientWorld::build_callback, world, _1));
c->install_callback("SPAWN",
boost::bind(&ClientWorld::spawn_callback, world, _1));
// c->connect("88.190.23.192", 7879);
c->connect("127.0.0.1", 7879);
......
......@@ -17,14 +17,12 @@ std::vector<ActionPanelTable*> ClientMod::get_action_tables(Screen* screen)
{
ModActionInfos action_infos;
std::vector<ActionPanelTable*> tables;
log_warning("heu, coucou: " << this->units_doc->size());
for (unsigned int i=0; i < this->units_doc->size(); i++)
{
const YAML::Node& unit = (*this->units_doc)[i];
ActionPanelTable* table = new ActionPanelTable;
tables.push_back(table);
log_warning("Adding a new table, for a unit");
// A table always contains at least ONE page
this->fill_default_unit_actions(screen, table);
const YAML::Node& actions = unit["actions"];
for (unsigned int i=0; i < actions.size(); i++)
......@@ -34,9 +32,20 @@ std::vector<ActionPanelTable*> ClientMod::get_action_tables(Screen* screen)
this->add_action_to_table(table, action_infos, screen);
}
}
// YAML::Node buildings;
// parser.GetNextDocument(units);
for (unsigned int i=0; i < this->buildings_doc->size(); i++)
{
const YAML::Node& building = (*this->buildings_doc)[i];
ActionPanelTable* table = new ActionPanelTable;
tables.push_back(table);
this->fill_default_building_actions(screen, table);
const YAML::Node& actions = building["actions"];
for (unsigned int i=0; i < actions.size(); i++)
{
const YAML::Node& action = actions[i];
action >> action_infos;
this->add_action_to_table(table, action_infos, screen);
}
}
return tables;
}
......@@ -90,6 +99,11 @@ void ClientMod::fill_default_unit_actions(Screen* screen, ActionPanelTable* tabl
0, 4, null_left), 4);
}
void ClientMod::fill_default_building_actions(Screen* screen, ActionPanelTable* table)
{
this->add_empty_pages(table, 1);
}
void ClientMod::add_action_to_table(ActionPanelTable* table, const ModActionInfos& infos, Screen* screen)
{
t_left_click left_click = {0, 0, 0};
......@@ -111,6 +125,13 @@ void ClientMod::add_action_to_table(ActionPanelTable* table, const ModActionInfo
left_click.cursor_callback = boost::bind(&Screen::draw_build_cursor, screen, _1, _2, _3);
button = new ActionPanelButton(infos.image_filename, boost::bind(&Screen::set_left_click_callback, screen, _1), infos.position, left_click);
}
else if (infos.type == "spawn")
{
left_click.id = infos.value;
left_click.callback = 0;
left_click.cursor_callback = 0;
button = new ActionPanelButton(infos.image_filename, boost::bind(&ClientWorld::action_spawn, screen->get_world(), _1), infos.position, left_click);
}
else
{
log_error("Unknown type of action: " << infos.type);
......
......@@ -30,6 +30,7 @@ private:
ClientMod& operator=(const ClientMod&);
void fill_default_unit_actions(Screen*, ActionPanelTable*);
void fill_default_building_actions(Screen*, ActionPanelTable*);
/**
* Add empty pages until the table contains the given number of pages.
* It may not add any pages if there are already enough of them.
......
......@@ -27,6 +27,7 @@ void RemoteGameClient::install_callbacks()
this->install_callback("T", boost::bind(&RemoteGameClient::turn_callback, this, _1));
this->install_callback("MOVE", boost::bind(&ServerWorld::move_callback, world, _1));
this->install_callback("BUILD", boost::bind(&ServerWorld::build_callback, world, _1));
this->install_callback("SPAWN", boost::bind(&ServerWorld::spawn_callback, world, _1));
}
boost::asio::io_service& RemoteGameClient::get_io_service()
......
#include <world/building.hpp>
#include <world/world.hpp>
Building::Building()
{
......@@ -16,10 +17,6 @@ Building::Building(const Building& o):
{
}
void Building::tick(World*)
{
}
bool Building::is_obstructing_position(Entity* entity, const Position& position) const
{
return false;
......@@ -29,3 +26,15 @@ bool Building::contains(const Position&) const
{
return false;
}
bool Building::spawn(World* world, Work* w)
{
log_info("COCUOU je SPAWN");
SpawnWork* work = static_cast<SpawnWork*>(w);
Unit* unit = world->create_unit(work->type_id);
// find free spawning position
unit->pos.x = this->x * CELL_SIZE;
unit->pos.y = this->y * CELL_SIZE;
world->insert_unit(unit);
return true;
}
......@@ -12,6 +12,7 @@
# define __BUILDING_HPP__
#include <world/entity.hpp>
#include <world/work.hpp>
class Building: public Entity
{
......@@ -20,7 +21,6 @@ public:
~Building();
Building(const Building&);
bool contains(const Position&) const;
void tick(World*);
bool is_obstructing_position(Entity*, const Position&) const;
......@@ -33,6 +33,8 @@ public:
ar & name & type_id & x & y;
}
bool spawn(World*, Work*);
private:
Building& operator=(const Building&);
......
......@@ -163,6 +163,15 @@ bool ClientWorld::action_build(const unsigned int x, const unsigned y, const std
return true;
}
void ClientWorld::action_spawn(const t_left_click left_click)
{
assert(this->current_selection.is_empty() == false);
SpawnEvent event;
event.actor = this->current_selection.get_entities().front()->id;
event.type_id = left_click.id;
this->generate_command("SPAWN", event.to_string());
}
bool ClientWorld::action_move(const unsigned int x, const unsigned y, const std::size_t)
{
MoveEvent event;
......@@ -213,7 +222,15 @@ void ClientWorld::add_selection_change_callback(const t_selection_changed_callba
this->current_selection.on_modified_callbacks.push_back(callback);
}
void draw_build_cursor(const unsigned int, const unsigned int y, const std::size_t)
void ClientWorld::spawn_callback(Command* command)
{
DoSpawnEvent* e = new DoSpawnEvent(command);
log_info("Spawn_callback: " << e->actor << " " << e->type_id);
if (e->is_valid() == false)
{
log_warning("Invalid data for Spawn command");
return ;
}
Action* action = new Action(boost::bind(&World::do_spawn, this, _1), e);
this->insert_received_action(action, e);
}
......@@ -60,6 +60,10 @@ public:
* The server tells one unit to build the given building
*/
void build_callback(Command*);
/**
* The server tells one building to spawn the given unit
*/
void spawn_callback(Command*);
/**
* Insert an action, built from a command we received, into the turn handler,
* and confirm it or validate it, depending on if the game is started or not
......@@ -83,6 +87,7 @@ public:
*/
bool action_move(const unsigned int x, const unsigned y, const std::size_t=0);
bool action_build(const unsigned int, const unsigned int, const std::size_t);
void action_spawn(const t_left_click left_click);
/**
* Give the order to all selected and movable units to move to the given
* world coordinates with an attack order (will attack all encountered
......
......@@ -38,9 +38,32 @@ void Entity::queue_work(Work* work)
void Entity::clear_works()
{
if (this->works.empty())
return ;
Work* current = this->works.front();
this->works.pop(); // pop the current without deleting it, in case we want
// to add it back.
while (!this->works.empty())
{
delete this->works.front();
this->works.pop();
}
// if the current work is not interruptible, we put it back in the queue,
// as if we didn’t removed it.
if (!current->is_interruptible())
this->works.push(current);
else
delete current;
}
void Entity::tick(World* world)
{
if (this->works.empty())
return ;
Work* current_work = this->works.front();
if ((*current_work)(world, current_work) == true)
{
delete current_work;
this->works.pop();
}
}
......@@ -36,7 +36,7 @@ public:
/**
* Regularly update the entity.
*/
virtual void tick(World*) = 0;
void tick(World*);
void clear_works();
void set_work(Work*);
......
......@@ -279,6 +279,8 @@ bool Map::can_be_built_on(const int cellx, const int celly) const
cell_path_t Map::do_astar(const uint startx, const uint starty,
const uint endx, const uint endy)
{
log_debug(startx);
log_debug(starty);
assert(startx < this->get_width_in_tiles());
assert(starty < this->get_height_in_tiles());
assert(endx < this->get_width_in_tiles());
......
......@@ -91,6 +91,21 @@ void ServerWorld::build_callback(Command* command)
this->turn_handler->insert_action(action, doevent->turn);
}
void ServerWorld::spawn_callback(Command* command)
{
SpawnEvent event(command);
if (event.is_valid() == false)
{
log_warning("Invalid data for SPAWN command");
return ;
}
DoSpawnEvent* doevent = new DoSpawnEvent(event);
doevent->turn = this->turn_handler->get_current_turn() + 2;
this->generate_command("SPAWN", doevent->to_string());
Action* action = new Action(0, doevent, this->occupants.size());
this->turn_handler->insert_action(action, doevent->turn);
}
bool ServerWorld::validate_action(const unsigned int id, const unsigned long int by)
{
log_debug("Action " << id << " validated by " << by);
......
......@@ -33,6 +33,12 @@ public:
* Check if the position is valid and the building can be built (money, the unit actually has this ability, etc).
*/
void build_callback(Command*);
/**
* Called whenever we receive a SPAWN command from one client. Should
* check if that unit can actually do that (money, capacity to do it,
* etc), before returning a DoSpawnEvent.
*/
void spawn_callback(Command*);
bool validate_action(const unsigned int id, const unsigned long int by);
bool validate_turn(const unsigned int id, const unsigned long int by);
/**
......
......@@ -32,18 +32,6 @@ bool Unit::contains(const Position& pos) const
return false;
}
void Unit::tick(World* world)
{
if (this->works.empty())
return ;
Work* current_work = this->works.front();
if ((*current_work)(world, current_work) == true)
{
delete current_work;
this->works.pop();
}
}
bool Unit::build(World* world, Work* w)
{
BuildWork* work = static_cast<BuildWork*>(w);
......@@ -63,7 +51,7 @@ bool Unit::follow_path(World* world, Work* w)
{
if (work->calculated == false)
{
log_warning("Calculating path");
log_warning("Calculating path: " << this->pos);
work->path = world->calculate_path(work->end_position, this);
work->calculated = true;
}
......
......@@ -23,7 +23,6 @@ public:
~Unit();
Unit(const Unit&);
bool contains(const Position&) const;
void tick(World*);
bool follow_path(World*, Work*);
bool build(World*, Work*);
......
......@@ -18,3 +18,9 @@ BuildWork::BuildWork(Unit* unit, const unsigned short id, const short x, const s
y(y),
building(0)
{}
SpawnWork::SpawnWork(Building* building, const unsigned short id):
Work(boost::bind(&Building::spawn, building, _1, _2)),
type_id(id),
elapsed_time(0)
{}
......@@ -13,6 +13,7 @@ class Building;
class Entity;
#include <world/position.hpp>
#include <world/path.hpp>
/**
* The function to call at each tick of the entity owning that work. It returns true if that work is complete and must be removed from the queue, false otherwise.
......@@ -22,12 +23,24 @@ typedef boost::function<bool (World*, Work*)> t_work_callback;
class Work
{
public:
Work(t_work_callback c):
work_callback(c) {}
Work(t_work_callback callback):
work_callback(callback) {}
bool operator()(World* world, Work* work)
{
return this->work_callback(world, work);
}
/**
* Whether or not the work can be interrupted. A Work object that is not
* interruptible means that it is not removed from the queue when we call
* entity->set_work() and that this work at the front of the queue
* (meaning that it's the current work). It is however removed from the
* queue if it's not at the front. Herited class just need to override
* that method, and return false, if they are not interruptible.
*/
inline virtual bool is_interruptible() const
{
return true;
}
protected:
t_work_callback work_callback;
private:
......@@ -80,4 +93,24 @@ private:
Building* building;
};
class SpawnWork: public Work
{
friend class Building;
public:
SpawnWork(Building*, const unsigned short);
~SpawnWork();
private:
SpawnWork(const SpawnWork&);
SpawnWork& operator=(const SpawnWork&);
/**
* The type_id of the unit to spawn.
*/
const unsigned short type_id;