From 9d39702824e8fae5127e09fb5a05b521b48cd028 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 9 Nov 2008 11:43:28 +0000 Subject: docking menus --- src/client/client.cc | 10 ++ src/client/input.cc | 8 +- src/client/view.cc | 18 ++++ src/core/Makefile.am | 9 +- src/core/descriptions.cc | 245 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/descriptions.h | 128 ++++++++++++++++++++++++ src/core/entity.cc | 29 ++++++ src/core/entity.h | 19 ++++ src/core/net.h | 2 +- src/core/netclient.cc | 21 ++-- src/core/netclient.h | 7 +- src/core/netconnection.cc | 37 ++++++- src/core/netserver.cc | 25 +++-- src/core/parser.cc | 16 +-- src/core/parser.h | 3 - src/core/player.cc | 48 ++++----- src/game/base/base.cc | 63 +++++++++--- src/game/base/base.h | 1 + src/render/camera.cc | 11 ++- src/render/camera.h | 1 + src/ui/Makefile.am | 9 +- src/ui/container.cc | 25 ++--- src/ui/container.h | 11 --- src/ui/menu.cc | 23 +---- src/ui/menuview.cc | 95 ++++++++++++++++++ src/ui/menuview.h | 42 ++++++++ src/ui/ui.cc | 48 ++++++++- src/ui/ui.h | 8 ++ src/ui/widget.cc | 5 + src/ui/widget.h | 6 ++ 30 files changed, 838 insertions(+), 135 deletions(-) create mode 100644 src/core/descriptions.cc create mode 100644 src/core/descriptions.h create mode 100644 src/ui/menuview.cc create mode 100644 src/ui/menuview.h (limited to 'src') diff --git a/src/client/client.cc b/src/client/client.cc index a83beb7..617b569 100644 --- a/src/client/client.cc +++ b/src/client/client.cc @@ -187,11 +187,21 @@ void Client::frame(unsigned long timestamp) // show the join menu when player does not control an entity } else if (core::game()->time() && !core::localcontrol()) { ui::root()->show_menu("join"); + + // show the view menu when docked + } else if (core::localcontrol() && core::localplayer()->view()) { + ui::root()->show_menuview("main"); } + } else { if (core::localcontrol()) { + // hide join menu if (ui::root()->active()->label().compare("join") == 0) { ui::root()->hide_menu(); + + // hide view menu + } else if (!core::localplayer()->view() && (ui::root()->active()->label().compare("view") == 0)) { + ui::root()->hide_menu(); } } } diff --git a/src/client/input.cc b/src/client/input.cc index 9cb48a1..dcddea3 100644 --- a/src/client/input.cc +++ b/src/client/input.cc @@ -133,7 +133,7 @@ void func_ui_control(std::string const &args) void func_view_next(std::string const &args) { - if (core::application()->connected() && core::localcontrol()) { + if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { render::Camera::view_next(); local_roll = 0; local_pitch = 0; @@ -144,7 +144,7 @@ void func_view_next(std::string const &args) void func_view_prev(std::string const &args) { - if (core::application()->connected() && core::localcontrol()) { + if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { render::Camera::view_previous(); local_roll = 0; local_pitch = 0; @@ -511,7 +511,7 @@ void key_pressed(Key *key) if (ui::root()->input_key(true, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers)) { return; - } else if (core::application()->connected() && core::localcontrol()) { + } else if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { char c = key->bind(convert_SDL_modifier(keyboard_modifiers)).c_str()[0]; if (c == '@') { @@ -529,7 +529,7 @@ void key_pressed(Key *key) } else if (core::application()->connected()) { char c = key->bind(convert_SDL_modifier(keyboard_modifiers)).c_str()[0]; - if (c && c != '+') { + if (c && c != '+' && c != '@') { // normal bind core::cmd() << key->bind(convert_SDL_modifier(keyboard_modifiers)) << "\n"; } diff --git a/src/client/view.cc b/src/client/view.cc index 9eb9c33..304d2a5 100644 --- a/src/client/view.cc +++ b/src/client/view.cc @@ -455,6 +455,15 @@ void draw_entity_target(core::Entity *entity, bool is_active_target) glVertex3f(cx, cy-r+2, 0); render::gl::end(); + if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) { + render::gl::begin(render::gl::LineLoop); + glVertex3f(cx+ (r*0.25f), cy+2, 0); + glVertex3f(cx, cy+(r*0.25f)+2, 0); + glVertex3f(cx-(r*0.25f), cy+2, 0); + glVertex3f(cx, cy-(r*0.25f)+2, 0); + render::gl::end(); + } + if (entity == core::localplayer()->mission_target()) { render::gl::color(1, 0.5f, 1, 1); // FIXME mission color } else if (entity->type() == core::Entity::Controlable) { @@ -471,6 +480,15 @@ void draw_entity_target(core::Entity *entity, bool is_active_target) glVertex3f(cx, cy-r, 0); render::gl::end(); + if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) { + render::gl::begin(render::gl::LineLoop); + glVertex3f(cx+(r*0.25f), cy, 0); + glVertex3f(cx, cy+(r*0.25f), 0); + glVertex3f(cx-(r*0.25f), cy, 0); + glVertex3f(cx, cy-(r*0.25f), 0); + render::gl::end(); + } + render::gl::enable(GL_TEXTURE_2D); if (is_active_target) { diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 61622e0..2c90dec 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -2,9 +2,9 @@ METASOURCES = AUTO INCLUDES = -I$(top_srcdir)/src libcore_la_SOURCES = application.cc clientstate.cc commandbuffer.cc core.cc \ - cvar.cc entity.cc func.cc gameconnection.cc gameinterface.cc gameserver.cc \ - module.cc netclient.cc netconnection.cc netplayer.cc netserver.cc parser.cc \ - player.cc stats.cc timer.cc zone.cc + cvar.cc descriptions.cc entity.cc func.cc gameconnection.cc gameinterface.cc \ + gameserver.cc module.cc netclient.cc netconnection.cc netplayer.cc netserver.cc \ + parser.cc player.cc stats.cc timer.cc zone.cc libcore_la_LDFLAGS = -avoid-version -no-undefined libcore_la_LIBADD = $(top_builddir)/src/model/libmodel.la \ $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/math/libmath.la $(top_builddir)/src/sys/libsys.la \ @@ -14,5 +14,4 @@ noinst_LTLIBRARIES = libcore.la noinst_HEADERS = application.h clientstate.h commandbuffer.h core.h cvar.h \ entity.h func.h gameconnection.h gameinterface.h gameserver.h message.h module.h \ net.h netclient.h netconnection.h netserver.h player.h range.h stats.h \ - timer.h parser.h - + timer.h parser.h descriptions.h diff --git a/src/core/descriptions.cc b/src/core/descriptions.cc new file mode 100644 index 0000000..fe37d2c --- /dev/null +++ b/src/core/descriptions.cc @@ -0,0 +1,245 @@ +/* + core/descriptions.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "core/descriptions.h" +#include "auxiliary/functions.h" +#include "sys/sys.h" + +namespace core { + +/* ---- class ButtonDescription ------------------------------------ */ + +ButtonDescription::ButtonDescription() +{ + button_model = 0; + button_align = Center; +} + +ButtonDescription::~ButtonDescription() +{ +} + +void ButtonDescription::set_text(const std::string &text) +{ + button_text.assign(text); +} + +void ButtonDescription::set_command(const std::string &command) +{ + button_command.assign(command); +} + +void ButtonDescription::set_modelname(const std::string &modelname) +{ + button_modelname.assign(modelname); + + button_model = model::Model::load(modelname); +} + +void ButtonDescription::set_alignment(Align align) +{ + button_align = align; +} + +/* ---- class MenuDescription -------------------------------------- */ + +MenuDescription::MenuDescription() +{ +} + +MenuDescription::~MenuDescription() +{ + for (Buttons::iterator it = buttons().begin(); it != buttons().end(); it++) { + delete (*it); + } + + buttons().clear(); +} + +void MenuDescription::set_text(const std::string &text) +{ + menu_text.assign(text); +} + +void MenuDescription::set_label(const std::string &label) +{ + menu_label.assign(label); +} + +void MenuDescription::add_button(ButtonDescription *button) +{ + buttons().push_back(button); +} + +/* ---- class Descriptions ----------------------------------------- */ + + +void Descriptions::serialize(MenuDescription *menu, std::ostream & os) +{ + os << menu->label() << " " + << "\"" << menu->text() << "\" " + << menu->buttons().size() << " "; + + for (MenuDescription::Buttons::iterator it = menu-> buttons().begin(); it != menu->buttons().end(); it++) { + ButtonDescription *button = (*it); + os << "\"" << button->text() << "\" " + << button->alignment() << " " + << "\"" << button->command() << "\" " + << "\"" << button->modelname() << "\" "; + } +} + +MenuDescription * Descriptions::receive(std::istream &is) +{ + MenuDescription *menu = new MenuDescription(); + + int a; + std::string n; + size_t nb; + char c; + + // menu label + is >> n; + menu->set_label(n); + + // menu text + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + + if (n.size()) + menu->set_text(n); + + // menu buttons + is >> nb; + for (size_t i=0; i < nb; i++) { + ButtonDescription *button = new ButtonDescription(); + // button text + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + if (n.size()) button->set_text(n); + + // button alignment + a = ButtonDescription::Center; + is >> a; + if (a == ButtonDescription::Left) + button->set_alignment(ButtonDescription::Left); + else if (a == ButtonDescription::Right) + button->set_alignment(ButtonDescription::Right); + else + button->set_alignment(ButtonDescription::Center); + + // button command + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + if (n.size()) button->set_command(n); + + // button modelname + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + if (n.size()) button->set_modelname(n); + + menu->add_button(button); + } + + return menu; + +} + +bool Descriptions::load_entity_menus(core::Entity *entity, const std::string &menufilename) +{ + filesystem::IniFile inifile; + inifile.open(menufilename); + + if (!inifile.is_open()) { + return false; + } + + con_debug << " " << inifile.name() << std::endl; + + std::string strval; + MenuDescription *menu = 0; + ButtonDescription *button = 0; + + while (inifile.getline()) { + + if (inifile.got_section()) { + if (inifile.got_section("menu")) { + menu = new MenuDescription; + entity->add_menu(menu); + + } else if (inifile.got_section("button")) { + if (menu) { + button = new ButtonDescription(); + menu->add_button(button); + } + } else { + inifile.unknown_section(); + } + + } else if (inifile.got_key()) { + + if (inifile.in_section("menu")) { + + if (inifile.got_key_string("label", strval)) { + aux::to_label(strval); + menu->set_label(strval); + } else if (inifile.got_key_string("text", strval)) { + aux::strip_quotes(strval); + menu->set_text(strval); + } else { + inifile.unkown_key(); + } + + } else if (inifile.in_section("button")) { + + if (!button) { + continue; + } else if (inifile.got_key_string("text", strval)) { + aux::strip_quotes(strval); + button->set_text(strval); + } else if (inifile.got_key_string("command", strval)) { + for (size_t i =0; i <= strval.size(); i++) { + if (strval[i] == ',') strval[i] = ';'; + } + aux::strip_quotes(strval); + button->set_command(strval); + } else if (inifile.got_key_string("model", strval)) { + button->set_modelname(strval); + } else if (inifile.got_key_string("align", strval)) { + aux::to_label(strval); + if (strval.compare("left") == 0) { + button->set_alignment(ButtonDescription::Left); + } else if (strval.compare("center") == 0) { + button->set_alignment(ButtonDescription::Center); + } else if (strval.compare("right") == 0) { + button->set_alignment(ButtonDescription::Right); + } else { + inifile.unknown_value(); + } + } else { + inifile.unkown_key(); + } + + } + } + } + + inifile.close(); + return true; +} + +} + + + diff --git a/src/core/descriptions.h b/src/core/descriptions.h new file mode 100644 index 0000000..345d354 --- /dev/null +++ b/src/core/descriptions.h @@ -0,0 +1,128 @@ +/* + core/descriptions.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_CORE_DESCRIPTIONS_H__ +#define __INCLUDED_CORE_DESCRIPTIONS_H__ + +#include +#include +#include + +namespace core { +class ButtonDescription; +class MenuDescription; +} + +#include "core/entity.h" +#include "model/model.h" +#include "filesystem/inifile.h" + +namespace core { + +/// description of a menu button +class ButtonDescription +{ +public: + enum Align {Center=0, Left=1, Right=2}; + + ButtonDescription(); + ~ButtonDescription(); + + /* -- inspectors ------------------------------------------- */ + + /// button text + inline const std::string & text() const { return button_text; } + + /// button command + inline const std::string & command() const { return button_command; } + + /// button info view model name + inline const std::string & modelname() const { return button_modelname; } + + /// button info view model + inline const model::Model *model() { return button_model; } + + /// button text alignment + inline Align alignment() const { return button_align; } + + /* -- mutators -------------------------------------------- */ + + /// set button text + void set_text(const std::string &text); + + /// set button command + void set_command(const std::string &command); + + /// set button name + void set_modelname(const std::string &modelname); + + /// set text alignment + void set_alignment(Align align); + +private: + std::string button_text; + std::string button_command; + std::string button_modelname; + Align button_align; + + model::Model *button_model; +}; + +/// description of an entity menu +class MenuDescription +{ +public: + MenuDescription(); + ~MenuDescription(); + + typedef std::list Buttons; + + /* -- inspectors ------------------------------------------- */ + + /// menu label + inline const std::string & label() const { return menu_label; } + + /// menu text + inline const std::string & text() const { return menu_text; } + + /// menu buttons + inline Buttons &buttons() { return menu_buttons; } + + /* -- mutators -------------------------------------------- */ + + /// set menu text + void set_text(const std::string &text); + + /// set menu label + void set_label(const std::string &label); + + /// add a menu button + void add_button(ButtonDescription *button); + +private: + std::string menu_label; + std::string menu_text; + + Buttons menu_buttons; +}; + + +/// descriptions loader class +class Descriptions { +public: + /// read entity menus from ini file + static bool load_entity_menus(core::Entity *entity, const std::string &menufilename); + /// serialize entity menus to a stream + static void serialize(MenuDescription *menu, std::ostream & os); + /// read entity menus from a stream + static MenuDescription * receive(std::istream &is); +}; + +} + +#endif // __INCLUDED_CORE_DESCRIPTIONS_H__ + + diff --git a/src/core/entity.cc b/src/core/entity.cc index 9bc3f3c..e2aac1c 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -133,6 +133,12 @@ Entity::Entity(std::istream & is) Entity::~Entity() { + // delete entity menus + for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { + delete (*it); + } + menus().clear(); + if (entity_clientstate) { delete entity_clientstate; entity_clientstate = 0; @@ -346,6 +352,29 @@ void Entity::frame(float seconds) { } +void Entity::add_menu(MenuDescription *menu) +{ + entity_menus.push_back(menu); +} + +MenuDescription *Entity::find_menu(std::string const &label) +{ + for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { + if (label.compare((*it)->label()) == 0) + return (*it); + } + return 0; +} + +void Entity::remove_menu(std::string const &label) +{ + for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { + if (label.compare((*it)->label()) == 0) + menus().erase(it); + return; + } +} + /* ---- class EntityDynamic ---------------------------------------- */ EntityDynamic::EntityDynamic(unsigned int flags) : diff --git a/src/core/entity.h b/src/core/entity.h index 09b52a1..2e3b443 100644 --- a/src/core/entity.h +++ b/src/core/entity.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "model/model.h" #include "math/axis.h" @@ -24,6 +25,7 @@ class EntityControlable; } #include "core/clientstate.h" +#include "core/descriptions.h" #include "core/player.h" #include "core/zone.h" @@ -46,6 +48,9 @@ public: /// EntityDynamic event state classes enum Event {Normal=0, NoPower=1, ImpulseInitiate=2, Impulse=3, JumpInitiate=4, Jump=5, Docked=6}; + /// entity menus collection typedef + typedef std::list Menus; + /// create a new entity and add it to the registry Entity(unsigned int flags = 0); @@ -117,6 +122,12 @@ public: /// general visibility inline bool visible() const { return entity_visible; } + /// entity menus + inline Menus &menus() { return entity_menus; } + + /// find a menu + MenuDescription *find_menu(std::string const &label); + /*----- serializers ----------------------------------------------- */ /// serialize the entity to a stream @@ -191,6 +202,12 @@ public: /// unset a flag void unset_flag(Flags flag); + /// add an entity menu + void add_menu(MenuDescription *menu); + + /// remove an entity menu + void remove_menu(std::string const &label); + /// clear all update flags virtual void clear_updates(); @@ -252,6 +269,8 @@ private: model::Model *entity_model; std::string entity_modelname; + Menus entity_menus; + static Registry entity_registry; static size_t entity_nextid; diff --git a/src/core/net.h b/src/core/net.h index fe264e8..f3774b5 100644 --- a/src/core/net.h +++ b/src/core/net.h @@ -11,7 +11,7 @@ namespace core { /// network protocol version -const unsigned int PROTOCOLVERSION = 13; +const unsigned int PROTOCOLVERSION = 14; /// maximum lenght of a (compressed) network message block const unsigned int FRAMESIZE = 1152; diff --git a/src/core/netclient.cc b/src/core/netclient.cc index f75d8f3..253cf4f 100644 --- a/src/core/netclient.cc +++ b/src/core/netclient.cc @@ -18,19 +18,24 @@ namespace core { -NetClient::NetClient(std::string host, int port) : +NetClient::NetClient(std::string host, int port, int fd) : client_host(host) { client_error = true; client_state = Connecting; + client_fd = fd; + client_host = host; + client_port = port; client_player = new NetPlayer(this); + if (!fd) { + con_warn << "Network invalid client file descriptor!" << std::endl; + abort(); + return; + } con_print << host << ":" << port << " connected." << std::endl; - client_host = host; - client_port = port; - client_addr.sin_family = AF_INET; client_addr.sin_port = htons(port); client_addr.sin_addr.s_addr = inet_addr(host.c_str()); @@ -120,10 +125,14 @@ void NetClient::send_raw(std::string const &msg) if (error()) return; + if ((sendq.size()) && (sendq.size() + msg.size() >= BLOCKSIZE - 16 )) { + transmit(); + } + sendq.append(msg); } -void NetClient::transmit(int serverfd) +void NetClient::transmit() { if (!sendq.size()) { if (client_keepalive + NETTIMEOUT/2 < application()->time()) { @@ -166,7 +175,7 @@ void NetClient::transmit(int serverfd) size_t total_sent = 0; while (total_sent < total_size && !error()) { - ssize_t bytes_sent = ::sendto(serverfd, data, total_size - total_sent, 0, + ssize_t bytes_sent = ::sendto(fd(), data, total_size - total_sent, 0, (struct sockaddr *)&client_addr, sizeof(client_addr)); if (bytes_sent < 0) { diff --git a/src/core/netclient.h b/src/core/netclient.h index 6e5bcf9..c0cdf8d 100644 --- a/src/core/netclient.h +++ b/src/core/netclient.h @@ -43,7 +43,7 @@ namespace core class NetClient { public: - NetClient(std::string host, int port); + NetClient(std::string host, int port, int fd); ~NetClient(); /// the remote hostname the client is connected to @@ -68,7 +68,7 @@ public: void retreive(std::string & message); /// transmit messages in the send queue to the remote client - void transmit(int serverfd); + void transmit(); inline bool error() const { return client_error; } @@ -84,9 +84,12 @@ public: float client_keepalive; private: + inline int fd() const { return client_fd; } + struct sockaddr_in client_addr; std::string client_host; int client_port; + int client_fd; bool client_error; NetPlayer *client_player; diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index ba23df3..0bb33db 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -515,7 +515,7 @@ void NetConnection::parse_incoming_message(const std::string & message) entity = new EntityGlobe(msgstream); break; default: - con_warn << "Received create for unknown entity type " << type << std::endl; + con_warn << "Received create for unknown entity type " << type << "!" << std::endl; return; break; } @@ -527,6 +527,37 @@ void NetConnection::parse_incoming_message(const std::string & message) game()->update_entity_clientstate(entity); } + } else if (command == "menu") { + + unsigned int id = 0; + if (msgstream >> id) { + if (!id) { + con_warn << "Received menu for NULL entity!" << std::endl; + return; + } + + Entity *entity = Entity::find(id); + if (!entity) { + con_warn << "Received menu for unknown entity " << id << "!" << std::endl; + return; + } + + MenuDescription *menu = Descriptions::receive(msgstream); + if (!menu->label().size()) { + con_warn << "Received menu without label for entity " << id << "!" << std::endl; + delete menu; + return; + } + + // remove the menu if it already exists + entity->remove_menu(menu->label()); + + //con_debug << "receiving menu " << entity->label() << " " << menu->label() << std::endl; + entity->add_menu(menu); + } else { + con_warn << "Received illegal menu message!" << std::endl; + } + } else if (command.compare("zone") == 0) { unsigned int id; @@ -554,7 +585,7 @@ void NetConnection::parse_incoming_message(const std::string & message) int player_id; if (!(msgstream >> player_id)) { - con_warn << "Received illegal update player info for player!" << std::endl; + con_warn << "Received illegal player info message!" << std::endl; return; } @@ -607,7 +638,7 @@ void NetConnection::parse_incoming_message(const std::string & message) Entity *entity = Entity::find(id); if (!entity) { // FIXME request entity from the server - con_warn << "Update for unknown entity " << id << std::endl; + con_warn << "Update for unknown entity " << id << "!" << std::endl; } else { // FIXME check of the received update matches the actual entity entity->receive_server_update(msgstream); diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 3f0d2e8..b61ae1f 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -104,7 +104,7 @@ NetServer::~NetServer() server()->player_disconnect((*it)->player()); (*it)->send_raw(netmsg); - (*it)->transmit(fd()); + (*it)->transmit(); delete (*it); } @@ -265,7 +265,7 @@ NetClient * NetServer::client_connect(std::string const host, int const port) { con_debug << "client_connect " << host << ":" << port << "\n"; - NetClient *client = new NetClient(host, port); + NetClient *client = new NetClient(host, port, fd()); if (client->error()) { con_warn << client->host() << ":" << client->port() << " connection failed!\n"; delete(client); @@ -283,7 +283,7 @@ void NetServer::client_initialize(NetClient *client) { std::string welcome("^B"); welcome.append(Cvar::sv_name->str()); client->player()->send(welcome); - client->transmit(fd()); + client->transmit(); // send zones for (Zone::Registry::iterator it = Zone::registry().begin(); it != Zone::registry().end(); it++) { @@ -293,7 +293,7 @@ void NetServer::client_initialize(NetClient *client) { // send connect completed std::string connect("connect\n"); client->send_raw(connect); - client->transmit(fd()); + client->transmit(); // set client state to pending client->client_state = NetClient::Pending; @@ -373,7 +373,7 @@ void NetServer::frame(unsigned long timestamp) for (Clients::iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; - client->transmit(fd()); + client->transmit(); if (client->state() == NetClient::Connected) client_frame(client, timestamp); @@ -386,7 +386,7 @@ void NetServer::frame(unsigned long timestamp) client->player()->player_dirty = false; client->player()->player_zonechange = false; } - client->transmit(fd()); + client->transmit(); } } @@ -425,7 +425,7 @@ void NetServer::send_message(NetClient *client, const char *channel, std::string void NetServer::send_disconnect(NetClient *client) { client->send_raw("disconnect\n"); - client->transmit(netserver_fd); + client->transmit(); client->abort(); } @@ -450,6 +450,17 @@ void NetServer::send_entity_create(NetClient *client, Entity *entity) entity->serialize_server_create(msg); msg << '\n'; client->send_raw(msg.str()); + + // send entity menus + for (Entity::Menus::iterator it = entity->menus().begin(); it != entity->menus().end(); it++) { + msg.clear(); + msg.str(""); + msg << "menu " << entity->id() << " "; + Descriptions::serialize((*it), msg); + msg << '\n'; + client->send_raw(msg.str()); + // con_debug << "sending menu " << entity->label() << " " << (*it)->label() << std::endl; + } } } diff --git a/src/core/parser.cc b/src/core/parser.cc index 6af3903..1b35592 100644 --- a/src/core/parser.cc +++ b/src/core/parser.cc @@ -4,26 +4,12 @@ the terms of the GNU General Public License version 2 */ -#include "core/parser.h" #include "auxiliary/functions.h" +#include "core/parser.h" #include "sys/sys.h" namespace core { -bool Parser::read_entity_menu(core::Entity *entity, const std::string &menufilename) -{ - filesystem::IniFile inifile; - inifile.open(menufilename); - - if (!inifile.is_open()) { - return false; - } - - con_debug << " " << inifile.name() << std::endl; - - inifile.close(); - return true; -} bool Parser::got_entity_key(filesystem::IniFile &inifile, core::Entity *entity) { diff --git a/src/core/parser.h b/src/core/parser.h index fec27c4..78a1087 100644 --- a/src/core/parser.h +++ b/src/core/parser.h @@ -18,9 +18,6 @@ class Parser { public: /// read default entity keys from an ini file static bool got_entity_key(filesystem::IniFile &inifile, core::Entity *entity); - - /// read entity menus - static bool read_entity_menu(core::Entity *entity, const std::string &menufilename); }; } diff --git a/src/core/player.cc b/src/core/player.cc index ca985ab..f968fc3 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -133,7 +133,7 @@ void Player::serialize_client_update(std::ostream & os) os << player_color << " " << player_color_second << " " << "\"" << player_name << "\" " - << "\"" << player_rconpassword << "\""; + << "\"" << player_rconpassword << "\" "; } @@ -162,52 +162,53 @@ void Player::receive_client_update(std::istream &is) void Player::serialize_server_update(std::ostream & os) const { - unsigned int zo = (zone() ? zone()->id() : 0); - unsigned int co = (player_control ? player_control->id() : 0); - unsigned int mission = (player_mission_target ? player_mission_target->id() : 0); - unsigned int view = (player_view ? player_view->id() : 0); + unsigned int zone_id = (zone() ? zone()->id() : 0); + unsigned int view_id = (player_view ? player_view->id() : 0); + unsigned int control_id = (player_control ? player_control->id() : 0); + unsigned int mission_id = (player_mission_target ? player_mission_target->id() : 0); - os << player_id << " " << zo << " " << view << " " << co << " " << mission << " " << player_color << " \"" << player_name << "\""; + os << player_id << " " << zone_id << " " << view_id << " " << control_id << " " << mission_id << " " << player_color << " "; } void Player::receive_server_update(std::istream &is) { is >> player_id; - unsigned int zo = 0; - is >> zo; - set_zone(Zone::find(zo)); + unsigned int zone_id = 0; + is >> zone_id; + set_zone(Zone::find(zone_id)); - unsigned int view = 0; - is >> view; - set_view(Entity::find(view)); + unsigned int view_id = 0; + is >> view_id; + set_view(Entity::find(view_id)); - unsigned int co = 0; - is >> co; - if (co) { - Entity *e = Entity::find(co); + unsigned int control_id = 0; + is >> control_id; + if (control_id) { + Entity *e = Entity::find(control_id); if (e && e->type() == Entity::Controlable) { player_control = static_cast(e); } else { player_control = 0; - con_warn << "control set to unknown entity " << co << "\n"; + con_warn << "control set to unknown entity " << control_id << "\n"; } } else { player_control = 0; } - unsigned int mission = 0; - is >> mission; - if (mission) { - player_mission_target = Entity::find(mission); + unsigned int mission_id = 0; + is >> mission_id; + if (mission_id) { + player_mission_target = Entity::find(mission_id); if (!player_mission_target) { - con_warn << "mission target set to unknown entity " << co << "\n"; + con_warn << "mission target set to unknown entity " << mission_id << "\n"; } } else { player_mission_target = 0; } is >> player_color; - + + /* std::string n; char c; while ( (is.get(c)) && (c != '"')); @@ -216,6 +217,7 @@ void Player::receive_server_update(std::istream &is) if (n.size()) player_name = n; + */ } void Player::add_asset(EntityControlable *entity) diff --git a/src/game/base/base.cc b/src/game/base/base.cc index 7fa5299..ce5eb1c 100644 --- a/src/game/base/base.cc +++ b/src/game/base/base.cc @@ -11,6 +11,7 @@ #include "auxiliary/functions.h" #include "core/gameserver.h" #include "core/parser.h" +#include "core/descriptions.h" #include "filesystem/filesystem.h" #include "filesystem/inifile.h" #include "base/base.h" @@ -54,11 +55,13 @@ void Base::func_join(core::Player *player, std::string const &args) ship->set_zone(player->zone()); player->set_control(ship); -/* core::Entity *dock = ship->zone()->default_view(); - ship->entity_location.assign(dock->location() + (dock->axis().forward() * dock->radius()*2.0f)); - ship->entity_axis.assign(dock->axis()); -*/ + if (dock) { + ship->entity_location.assign(dock->location() + (dock->axis().forward() * ((ship->radius()+ dock->radius())*2.0f))); + ship->entity_axis.assign(dock->axis()); + ship->set_eventstate(core::Entity::Docked); + player->set_view(dock); + } player->sound("game/buy-ship"); std::string message("^B"); @@ -118,15 +121,19 @@ void Base::func_buy(core::Player *player, std::string const &args) player->remove_asset(player->control()); } + core::Entity *dock = player->view(); Ship * ship = new Ship(player, shipmodel); ship->set_zone(player->zone()); - if (player->view()) { - core::Entity *dock = player->view(); - ship->entity_location.assign(dock->location() + (dock->axis().forward() * dock->radius()*2.0f)); + player->set_control(ship); + + if (dock) { + player->control()->location().assign(dock->location()); + player->control()->set_eventstate(core::Entity::Docked); ship->entity_axis.assign(dock->axis()); + ship->entity_axis.change_direction(180.0f); + player->set_view(dock); } - player->set_control(ship); core::server()->broadcast("^B" + player->name() + " ^Bpurchased " + aux::article(shipmodel->name())); player->sound("game/buy-ship"); @@ -200,7 +207,7 @@ void Base::func_dock(core::Player *player,core::Entity *entity) if (player->control()->eventstate() == core::Entity::Docked) return; - if (math::distance(entity->location(), player->control()->location()) > 2.0f * entity->radius()) { + if (math::distance(entity->location(), player->control()->location()) > 2.0f * (entity->radius() + player->control()->radius())) { player->send("^B" + entity->name() + " is out of range!"); return; } @@ -226,12 +233,43 @@ void Base::func_launch(core::Player *player, std::string const &args) assert(player->view()->zone() == player->control()->zone()); core::Entity *dock = player->view(); - player->control()->entity_location.assign(dock->location() + (dock->axis().forward() * dock->radius()*2.0f)); + player->control()->entity_location.assign(dock->location() + (dock->axis().forward() * (player->control()->radius()+ dock->radius())*2.0f)); player->control()->entity_axis.assign(dock->axis()); player->control()->set_eventstate(core::Entity::Normal); player->set_view(0); } +// instantaniously goto a specified entity within the zone +void Base::func_goto(core::Player *player, const std::string &args) +{ + if (!args.size()) + return; + + if (!g_devel->value()) + return; + + if (!player->control()) + return; + + std::string label(args); + aux::to_label(label); + + core::Zone *zone = player->control()->zone(); + for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it ++) { + core::Entity *dock = (*it); + std::string str(dock->label()); + aux::to_label(str); + if (str.find(label) != std::string::npos) { + player->control()->entity_location.assign(dock->location() + (dock->axis().forward() * (player->control()->radius()+dock->radius())*2.0f)); + player->control()->entity_axis.assign(dock->axis()); + player->control()->entity_axis.change_direction(180.0f); + player->control()->set_eventstate(core::Entity::Normal); + player->set_view(0); + return; + } + } +} + /* -- class Base -------------------------------------------------- */ Base::Base() : core::Module("base", "Project::OSiRiON", true) @@ -285,6 +323,9 @@ void Base::init() func = core::Func::add("launch", Base::func_launch); func->set_info("launch to space when docked"); + func = core::Func::add("goto", Base::func_goto); + func->set_info("[string] goto to an entity within the zone"); + func = core::Func::add("@dock", Base::func_dock); func->set_info("dock with target object"); @@ -556,7 +597,7 @@ bool Base::load_zone(core::Zone *zone) } else if (zoneini.got_key_bool("dock", b)) { if (b) { entity->set_flag(core::Entity::Dockable); - //core::Parser::read_entity_menu(entity, "zones/" + zone->label() + "/" + entity->label()); + core::Descriptions::load_entity_menus(entity, "zones/" + zone->label() + "/" + entity->label()); } else { entity->unset_flag(core::Entity::Dockable); } diff --git a/src/game/base/base.h b/src/game/base/base.h index 6979335..926060c 100644 --- a/src/game/base/base.h +++ b/src/game/base/base.h @@ -94,6 +94,7 @@ private: static void func_impulse(core::Player *player, std::string const &args); static void func_dock(core::Player *player,core::Entity *entity); static void func_launch(core::Player *player, std::string const &args); + static void func_goto(core::Player *player, const std::string &args); }; } diff --git a/src/render/camera.cc b/src/render/camera.cc index 04d899f..4544b75 100644 --- a/src/render/camera.cc +++ b/src/render/camera.cc @@ -32,6 +32,7 @@ math::Vector3f Camera::camera_eye; math::Vector3f Camera::camera_target; math::Axis Camera::camera_axis; Camera::Mode Camera::camera_mode; +Camera::Mode Camera::camera_previous_mode; // current and target yaw angle in XZ plane, positive is looking left float Camera::direction_current; @@ -65,6 +66,8 @@ void Camera::init() distance = 0.4f; + camera_mode = Overview; + camera_previous_mode = Track; set_mode(Track); camera_axis.clear(); @@ -100,6 +103,9 @@ void Camera::set_mode(Mode newmode) { camera_axis.clear(); + if (camera_mode != Overview) + camera_previous_mode = camera_mode; + switch(newmode) { case Track: // switch camera to Track mode @@ -213,7 +219,7 @@ void Camera::frame(float seconds) } } else if (core::localcontrol()) { if (camera_mode == Overview) { - set_mode(Track); + set_mode(camera_previous_mode); } } else { if (camera_mode != Overview) { @@ -230,13 +236,14 @@ void Camera::frame(float seconds) camera_axis.assign(core::localplayer()->view()->axis()); camera_axis.change_direction(180.0f); distance = math::max(core::localplayer()->view()->radius(), 1.0f) * 2.0f; - +/* } else if (core::localplayer()->zone()->default_view()) { // default zone view entity camera_target.assign(core::localplayer()->zone()->default_view()->location()); camera_axis.assign(core::localplayer()->zone()->default_view()->axis()); camera_axis.change_direction(180.0f); distance = math::max(core::localplayer()->zone()->default_view()->radius(), 1.0f) * 2.0f; +*/ } else { // default location (0,0,0) camera_target.clear(); diff --git a/src/render/camera.h b/src/render/camera.h index b19521e..5394d1b 100644 --- a/src/render/camera.h +++ b/src/render/camera.h @@ -89,6 +89,7 @@ private: static math::Vector3f camera_target; static math::Axis camera_axis; static Mode camera_mode; + static Mode camera_previous_mode; static float camera_aspect; static float camera_frustum_size; static float camera_frustum_front; diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am index b2126fd..e230203 100644 --- a/src/ui/Makefile.am +++ b/src/ui/Makefile.am @@ -7,10 +7,11 @@ else noinst_LTLIBRARIES = libui.la endif -noinst_HEADERS = bitmap.h button.h console.h container.h definitions.h font.h inputbox.h label.h \ - menu.h paint.h palette.h scrollpane.h ui.h widget.h window.h +noinst_HEADERS = bitmap.h button.h console.h container.h definitions.h font.h \ + inputbox.h label.h menu.h menuview.h paint.h palette.h scrollpane.h ui.h \ + widget.h window.h libui_la_SOURCES = bitmap.cc button.cc console.cc console.h container.cc \ - font.cc inputbox.cc label.cc menu.cc paint.cc palette.cc scrollpane.cc ui.cc \ - widget.cc window.cc + font.cc inputbox.cc label.cc menu.cc menuview.cc paint.cc palette.cc \ + scrollpane.cc ui.cc widget.cc window.cc libui_la_LDFLAGS = -avoid-version -no-undefined diff --git a/src/ui/container.cc b/src/ui/container.cc index 977ff4d..915ec98 100644 --- a/src/ui/container.cc +++ b/src/ui/container.cc @@ -7,6 +7,7 @@ #include "ui/container.h" #include "ui/paint.h" +#include "ui/ui.h" namespace ui { @@ -19,9 +20,6 @@ Container::Container(Widget *parent) : Window(parent) set_border(true); set_background(true); - - container_childsize.assign(256, 48); - container_margin = 24; } Container::~Container() @@ -30,31 +28,22 @@ Container::~Container() void Container::resize() { - float w = container_childsize.width() * 1.5f; - float h = children().size() * (container_childsize.height() + margin()) + container_childsize.height(); + float w = UI::elementsize.width() * 1.5f; + float h = children().size() * (UI::elementsize.height() + UI::elementmargin) + UI::elementsize.height(); set_size(w, h); - const float x = container_childsize.width() * 0.25f; - float y = container_childsize.height() * 0.5f; + const float x = UI::elementsize.width() * 0.25f; + float y = UI::elementsize.height() * 0.5f; // reposition all children within the container for (Children::iterator it = children().begin(); it != children().end(); it++) { Widget *w = (*it); - w->set_size(container_childsize); + w->set_size(UI::elementsize); w->set_location(x, y); - y += container_childsize.height() + container_margin; + y += UI::elementsize.height() + UI::elementmargin; } } -void Container::set_childsize(const float w, const float h) -{ - container_childsize.assign(w, h); -} - -void Container::set_margin(const float margin) -{ - container_margin = margin; -} void Container::draw_border() { diff --git a/src/ui/container.h b/src/ui/container.h index 5556759..2045772 100644 --- a/src/ui/container.h +++ b/src/ui/container.h @@ -19,21 +19,10 @@ public: Container(Widget *parent); ~Container(); - void set_margin(const float); - void set_childsize(const float width, const float height); - - inline const math::Vector2f & childsize() const { return container_childsize; } - - inline float margin() const { return container_margin; } - protected: virtual void draw_border(); virtual void resize(); - -private: - float container_margin; - math::Vector2f container_childsize; }; } diff --git a/src/ui/menu.cc b/src/ui/menu.cc index 8421f7f..36599be 100644 --- a/src/ui/menu.cc +++ b/src/ui/menu.cc @@ -47,16 +47,11 @@ void Menu::load() std::string strval; Button *button = 0; Label *label = 0; - float w = menu_container->childsize().width(); - float h = menu_container->childsize().height(); - float m = menu_container->margin(); while (ini.getline()) { if (ini.got_section()) { - - //con_debug << " " << ini.name() << " [" << ini.section() << "]" << std::endl; - if (ini.got_section("menu")) { + continue; } else if (ini.got_section("button")) { button = add_button(); @@ -67,31 +62,23 @@ void Menu::load() } else if (ini.got_section()) { ini.unknown_section(); } - - } else if (ini.got_key()) { - - //con_debug << " " << ini.name() << " " << ini.key() << "=" << ini.value() << std::endl; - + } else if (ini.got_key()) { if (ini.in_section("menu")) { if (ini.got_key_string("background", strval)) { menu_background->set_texture(strval); - } else if (ini.got_key_float("elementwidth", w)) { - menu_container->set_childsize(w,h); - } else if (ini.got_key_float("elementheight", h)) { - menu_container->set_childsize(w,h); - } else if (ini.got_key_float("elementmargin", m)) { - menu_container->set_margin(m); } else { ini.unkown_key(); } } else if (ini.in_section("button")) { if (ini.got_key_string("text", strval)) { + aux::strip_quotes(strval); button->set_text(strval); } else if (ini.got_key_string("command", strval)) { for (size_t i =0; i <= strval.size(); i++) { if (strval[i] == ',') strval[i] = ';'; } + aux::strip_quotes(strval); button->set_command(strval); } else if (ini.got_key_string("align", strval)) { @@ -157,7 +144,7 @@ void Menu::resize() { set_size(parent()->size()); menu_background->set_size(size()); - menu_container->set_location(menu_container->childsize().width() * 0.25, (height() - menu_container->height()) / 2.0f); + menu_container->set_location(UI::elementsize.width() * 0.25, (height() - menu_container->height()) / 2.0f); } } diff --git a/src/ui/menuview.cc b/src/ui/menuview.cc new file mode 100644 index 0000000..71bdd1a --- /dev/null +++ b/src/ui/menuview.cc @@ -0,0 +1,95 @@ +/* + ui/menuview.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "ui/menuview.h" +#include "ui/ui.h" + +namespace ui +{ + +MenuView::MenuView(Window *parent, const char * label) : Window(parent) +{ + set_border(false); + set_background(false); + set_label(label); + + menu_container = new Container(this); + menu_label = new Label(this); + + hide(); +} + +MenuView::~MenuView() +{ +} + +void MenuView::resize() +{ + set_size(parent()->size()); + menu_label->set_location(UI::elementsize.width() * 0.25, UI::elementsize.width() * 0.25); + menu_label->set_size(UI::elementsize.width() * 1.5f, UI::elementsize.height()); + + menu_container->set_location(UI::elementsize.width() * 0.25, (height() - menu_container->height()) / 2.0f); +} + +void MenuView::generate(core::Entity *entity, const char *menulabel) +{ + if (!menulabel) + return; + + + con_debug << "generating menu " << entity->label() << " " << menulabel << std::endl; + + remove_children(); + menu_container = new Container(this); + + menu_label = new Label(this, entity->name().c_str()); + menu_label->set_background(true); + menu_label->set_alignment(AlignCenter); + menu_label->set_font(ui::root()->font_large()); + + core::MenuDescription *menudescr = 0; + for (core::Entity::Menus::iterator it = entity->menus().begin(); it != entity->menus().end(); it++) { + if ((*it)->label().compare(menulabel) == 0) { + menudescr = (*it); + } + } + + if (!menudescr) { + menu_container->event_resize(); + resize(); + return; + } + + if (menudescr->text().size()) { + Label *label = new Label(menu_container); + label->set_text(menudescr->text()); + label->set_alignment(AlignCenter); + label->set_border(false); + label->set_font(ui::root()->font_large()); + } + + for (core::MenuDescription::Buttons::iterator it = menudescr->buttons().begin(); it != menudescr->buttons().end(); it++) { + core::ButtonDescription *buttondescr = (*it); + Button *button = new Button(menu_container, buttondescr->text().c_str(), buttondescr->command().c_str()); + switch (buttondescr->alignment()) { + case core::ButtonDescription::Center: + button->set_alignment(AlignCenter); + break; + case core::ButtonDescription::Left: + button->set_alignment(AlignLeft | AlignVCenter); + break; + case core::ButtonDescription::Right: + button->set_alignment(AlignRight | AlignVCenter); + break; + } + } + + menu_container->event_resize(); + resize(); +} + +} diff --git a/src/ui/menuview.h b/src/ui/menuview.h new file mode 100644 index 0000000..041d71a --- /dev/null +++ b/src/ui/menuview.h @@ -0,0 +1,42 @@ +/* + ui/menuview.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_UI_MENUVIEW_H__ +#define __INCLUDED_UI_MENUVIEW_H__ + +#include "core/entity.h" +#include "ui/bitmap.h" +#include "ui/container.h" +#include "ui/button.h" +#include "ui/label.h" +#include "ui/window.h" + +namespace ui +{ + +/// a menu container +class MenuView : public Window +{ +public: + /// create a new menu + MenuView(Window *parent, const char * label); + ~MenuView(); + + /// generate a menu from menu descriptions + void generate(core::Entity *entity, const char *menulabel); + +protected: + /// resize event + virtual void resize(); + +private: + Container *menu_container; + Label *menu_label; +}; + +} + +#endif // __INCLUDED_UI_MENUVIEW_H__ diff --git a/src/ui/ui.cc b/src/ui/ui.cc index 4774387..7d57ce0 100644 --- a/src/ui/ui.cc +++ b/src/ui/ui.cc @@ -16,6 +16,7 @@ #include "ui/button.h" #include "ui/label.h" #include "ui/menu.h" +#include "ui/menuview.h" #include "ui/paint.h" #include "ui/ui.h" #include "ui/widget.h" @@ -27,6 +28,9 @@ namespace ui bool UI::ui_debug = false; +float UI::elementmargin = 16; +math::Vector2f UI::elementsize(256, 48); + UI *global_ui = 0; void func_list_ui(std::string const &args) @@ -137,6 +141,12 @@ void func_menu(std::string const &args) } else if (command.compare("list") == 0) { root()->list_menus(); + } else if (command.compare("view") == 0) { + std::string label; + if (!(argstr >> label)) { + label.assign("main"); + } + root()->show_menuview(label); } else { root()->show_menu(command.c_str()); } @@ -244,6 +254,8 @@ void UI::load() } ui_menus.clear(); + add_menu(new MenuView(this, "view")); + ui_mouse_focus = this; ui_active_menu = 0; @@ -261,6 +273,11 @@ void UI::load() std::string strval; math::Color color; Menu *menu = 0; + + float w = elementsize.width(); + float h = elementsize.height(); + float m = elementmargin; + while (ini.getline()) { @@ -291,6 +308,12 @@ void UI::load() add_menu(menu); menu->load(); continue; + } else if (ini.got_key_float("elementwidth", w)) { + elementsize.assign(w, h); + } else if (ini.got_key_float("elementheight", h)) { + elementsize.assign(w, h); + } else if (ini.got_key_float("elementmargin", m)) { + elementmargin = m; } else { ini.unkown_key(); } @@ -402,6 +425,20 @@ void UI::add_menu(Window *menu) } +void UI::show_menuview(const std::string &label) +{ + if (!core::localplayer()->view()) + return; + + if (!core::localplayer()->view()->menus().size()) + return; + + MenuView *menuview = static_cast(find_menu("view")); + menuview->generate(core::localplayer()->view(), label.c_str()); + + show_menu("view"); +} + void UI::show_menu(const char *label) { Window *menu = find_menu(label); @@ -501,8 +538,15 @@ bool UI::on_keypress(const int key, const unsigned int modifier) case SDLK_ESCAPE: if (active()) { - hide_menu(); - audio::play("ui/menu"); + if (active()->label().compare("view") == 0) { + if (core::application()->connected()) { + show_menu("game"); + audio::play("ui/menu"); + } + } else { + hide_menu(); + audio::play("ui/menu"); + } } else { if (core::application()->connected()) { show_menu("game"); diff --git a/src/ui/ui.h b/src/ui/ui.h index f41df24..ae42ad6 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -37,6 +37,9 @@ public: /// make a window the active window void show_menu(const char *label); + /// make the entity view menu the active window + void show_menuview(const std::string &label); + /// hide the active window void hide_menu(); @@ -89,6 +92,11 @@ public: static bool ui_debug; + + + static float elementmargin; + static math::Vector2f elementsize; + protected: typedef std::list Menus; diff --git a/src/ui/widget.cc b/src/ui/widget.cc index 046cb01..1270997 100644 --- a/src/ui/widget.cc +++ b/src/ui/widget.cc @@ -34,6 +34,11 @@ Widget::Widget(Widget *parent) } Widget::~Widget() +{ + remove_children(); +} + +void Widget::remove_children() { for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { delete(*it); diff --git a/src/ui/widget.h b/src/ui/widget.h index 60670b6..c002c73 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -278,8 +278,14 @@ protected: /// draw the widget border virtual void draw_border(); + /// add a child widget virtual void add_child(Widget *child); + + /// remove a child widget virtual void remove_child(Widget *child); + + /// remove all child widgets + virtual void remove_children(); private: void draw_debug_border(); -- cgit v1.2.3