From a95028547981614e06ea7a6d22b853b85418cea3 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Wed, 15 Apr 2009 17:08:51 +0000 Subject: added info registry, list_info added network info transfer added info based buy menu and related game changes --- src/client/Makefile.am | 8 +- src/client/buymenu.cc | 132 ++++++++++++++++++++++++++++++++ src/client/buymenu.h | 58 ++++++++++++++ src/client/client.cc | 21 ++++-- src/client/client.h | 4 + src/client/entitymenu.cc | 75 ++++++++++++++++++- src/client/entitymenu.h | 2 +- src/client/input.cc | 7 +- src/client/playerview.cc | 79 +++++++++++++++---- src/client/playerview.h | 10 ++- src/client/targets.cc | 5 +- src/client/trademenu.cc | 32 ++++++++ src/client/trademenu.h | 29 +++++++ src/core/Makefile.am | 3 +- src/core/commandbuffer.cc | 33 +++++++- src/core/descriptions.cc | 27 +++++-- src/core/descriptions.h | 9 ++- src/core/gameconnection.cc | 22 ++++++ src/core/gameconnection.h | 3 + src/core/gameinterface.cc | 4 + src/core/gameinterface.h | 8 +- src/core/gameserver.cc | 5 ++ src/core/gameserver.h | 3 + src/core/info.cc | 183 +++++++++++++++++++++++++++++++++++++++++++++ src/core/info.h | 108 ++++++++++++++++++++++++++ src/core/net.h | 2 +- src/core/netconnection.cc | 33 +++++++- src/core/netconnection.h | 4 + src/core/netserver.cc | 30 +++++++- src/core/netserver.h | 4 + src/game/base/collision.cc | 25 ++++--- src/game/base/game.cc | 44 +++++++++-- src/game/base/game.h | 1 + src/game/base/shipmodel.cc | 35 ++++++++- src/game/base/shipmodel.h | 8 ++ src/ui/modelview.cc | 5 ++ 36 files changed, 991 insertions(+), 70 deletions(-) create mode 100644 src/client/buymenu.cc create mode 100644 src/client/buymenu.h create mode 100644 src/client/trademenu.cc create mode 100644 src/client/trademenu.h create mode 100644 src/core/info.cc create mode 100644 src/core/info.h diff --git a/src/client/Makefile.am b/src/client/Makefile.am index 557adc1..2786d80 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -7,9 +7,9 @@ else noinst_LTLIBRARIES = libclient.la endif -libclient_la_SOURCES = action.cc chat.cc client.cc clientext.cc entitymenu.cc \ - hud.cc infowidget.cc input.cc joystick.cc key.cc keyboard.cc map.cc \ - notifications.cc playerview.cc soundext.cc targets.cc video.cc worldview.cc +libclient_la_SOURCES = action.cc buymenu.cc chat.cc client.cc clientext.cc \ + entitymenu.cc hud.cc infowidget.cc input.cc joystick.cc key.cc keyboard.cc map.cc \ + notifications.cc playerview.cc soundext.cc targets.cc trademenu.cc video.cc worldview.cc libclient_la_CFLAGS = $(LIBSDL_CFLAGS) $(GL_CFLAGS) libclient_la_LDFLAGS = -avoid-version -no-undefined $(GL_LIBS) $(LIBSDL_LIBS) @@ -19,3 +19,5 @@ noinst_HEADERS = action.h chat.h client.h clientext.h hud.h entitymenu.h \ libclient_la_LIBADD = $(top_builddir)/src/core/libcore.la $(top_builddir)/src/audio/libaudio.la \ $(top_builddir)/src/render/librender.la $(top_builddir)/src/ui/libui.la +_SOURCES = trademenu.h +_SOURCES = buymenu.h diff --git a/src/client/buymenu.cc b/src/client/buymenu.cc new file mode 100644 index 0000000..b0a0b48 --- /dev/null +++ b/src/client/buymenu.cc @@ -0,0 +1,132 @@ +/* + client/buymenu.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/ui.h" +#include "ui/button.h" +#include "ui/paint.h" +#include "client/buymenu.h" +#include "core/info.h" +#include "core/application.h" +namespace client +{ + +BuyMenu::BuyMenu(ui::Widget *parent, const char * label) : ui::Window(parent) +{ + set_border(false); + set_background(true); + + if (label) + set_label(label); + else + set_label("buymenu"); + + // model pane (left) + menu_modelpane = new ui::Window(this); + menu_modelpane->set_label("modelpane"); + menu_modelpane->set_background(true); + menu_modelpane->set_border(true); + + menu_closebutton = new ui::Button(menu_modelpane, "Close","view hide"); + + // text pane (right) + menu_textpane = new ui::Window(this); + menu_textpane->set_label("textpane"); + menu_textpane->set_background(true); + menu_textpane->set_border(true); + + menu_namelabel = new ui::Label(menu_textpane); + menu_namelabel->set_label("infolabel"); + menu_namelabel->set_background(false); + menu_namelabel->set_border(false); + menu_namelabel->set_font(ui::root()->font_large()); + menu_namelabel->set_alignment(ui::AlignCenter); + menu_buybutton = new ui::Button(menu_textpane, "Buy"); + menu_scrollpane = new ui::ScrollPane(menu_textpane, menu_infotext); + menu_scrollpane->set_background(false); + menu_scrollpane->set_border(false); + menu_scrollpane->set_alignment(ui::AlignTop); + + menu_infotimestamp = 0; + menu_inforecord = 0; + + hide(); +} + +BuyMenu::~BuyMenu() +{ + +} + +void BuyMenu::set_item(std::string const & itemtype, std::string const & itemname) +{ + menu_itemtype.assign(itemtype); + aux::to_label(menu_itemtype); + + menu_itemname.assign(itemname); + aux::to_label(menu_itemname); + + menu_buybutton->set_command("remote buy " + itemtype + ' ' + itemname + "; view hide"); + menu_buybutton->set_label("buy " + itemname); + + menu_infotext.clear(); + menu_namelabel->set_text(0); + + core::Info *info = core::game()->info(itemtype+'/'+itemname); + if (info) { + menu_namelabel->set_text(info->name()); + for (core::Info::Text::iterator it = info->text().begin(); it != info->text().end(); it++) { + menu_infotext.push_back((*it)); + } + menu_infotimestamp = info->timestamp(); + } else { + menu_infotext.push_back("Information is not available"); + menu_infotimestamp = 0; + } + + menu_inforecord = info; +} + +void BuyMenu::resize() +{ + const float smallmargin = ui::UI::elementsize.height(); + + set_size(parent()->size()); + + // reposition model pane (left) + menu_modelpane->set_size(ui::UI::elementsize.width() * 1.5f, height() - smallmargin * 4.0f); + menu_modelpane->set_location(smallmargin, smallmargin * 2.0f); + + menu_closebutton->set_size(ui::UI::elementsize); + menu_closebutton->set_location((menu_modelpane->width() - menu_closebutton->width()) * 0.5f, + menu_modelpane->height() - menu_closebutton->height() - ui::UI::elementsize.height() * 0.5f); + + // reposition text pane (right) + menu_textpane->set_size(width() - smallmargin * 3.0f - menu_modelpane->width(), height() - smallmargin * 4.0f); + menu_textpane->set_location(smallmargin * 2.0f + menu_modelpane->width(), smallmargin * 2.0f); + + menu_buybutton->set_size(ui::UI::elementsize); + menu_buybutton->set_location((menu_textpane->width() - menu_buybutton->width()) * 0.5f, + menu_textpane->height() - menu_buybutton->height() - ui::UI::elementsize.height() * 0.5f); + + menu_namelabel->set_size(menu_textpane->width(), menu_namelabel->font()->height() * 2.0f); + menu_namelabel->set_location(0, 4); + + menu_scrollpane->set_size(menu_textpane->width() - 8, menu_buybutton->top() - menu_namelabel->bottom() - 8 ); + menu_scrollpane->set_location(4, menu_namelabel->bottom() + 4); + +} + +void BuyMenu::draw() +{ + // update content if necessary + if (menu_infotimestamp && (menu_infotimestamp != menu_inforecord->timestamp())) + set_item(menu_itemtype, menu_itemname); + + ui::Window::draw(); +} + +} + diff --git a/src/client/buymenu.h b/src/client/buymenu.h new file mode 100644 index 0000000..81a87bd --- /dev/null +++ b/src/client/buymenu.h @@ -0,0 +1,58 @@ +/* + client/buymenu.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_CLIENT_BUYMENU_H__ +#define __INCLUDED_CLIENT_BUYMENU_H__ + +#include "core/info.h" +#include "ui/container.h" +#include "ui/button.h" +#include "ui/label.h" +#include "ui/window.h" +#include "ui/scrollpane.h" + +namespace client +{ + +/// The buy menu is used to trade a single item, like a ship or an upgrade +class BuyMenu : public ui::Window +{ +public: + /// create a new menu + BuyMenu(ui::Widget *parent, const char * label = 0); + ~BuyMenu(); + + void set_item(std::string const & itemtype, std::string const & itemname); + +protected: + /// resize event + virtual void resize(); + + /// draw event + virtual void draw(); + +private: + + ui::Window *menu_modelpane; + ui::Window *menu_textpane; + ui::Label *menu_namelabel; + ui::ScrollPane *menu_scrollpane; + + ui::Button *menu_closebutton; + ui::Button *menu_buybutton; + + std::string menu_itemtype; + std::string menu_itemname; + + core::Info *menu_inforecord; + ui::Text menu_infotext; + unsigned long menu_infotimestamp; +}; + +} + +#endif // __INCLUDED_CLIENT_BUYMENU_H__ + diff --git a/src/client/client.cc b/src/client/client.cc index dffa1ca..47af682 100644 --- a/src/client/client.cc +++ b/src/client/client.cc @@ -144,6 +144,9 @@ void Client::init(int count, char **arguments) func = core::Func::add("menu", func_menu); func->set_info("[command] menu functions"); + func = core::Func::add("view", func_view); + func->set_info("[command] view menu functions"); + previous_timestamp = 0; } @@ -256,6 +259,7 @@ void Client::shutdown() core::Func::remove("ui_map"); core::Func::remove("ui_menu"); core::Func::remove("menu"); + core::Func::remove("view"); audio::shutdown(); @@ -495,6 +499,15 @@ void Client::func_ui_menu(std::string const &args) } } +// entity menus +void Client::func_view(std::string const &args) +{ + if (client()->worldview()) { + client()->worldview()->playerview()->show_menu(args); + } +} + +// global menus void Client::func_menu(std::string const &args) { if (!ui::root()) { @@ -535,14 +548,6 @@ void Client::func_menu(std::string const &args) } else if (command.compare("list") == 0) { ui::root()->list_menus(); - } else if (command.compare("view") == 0) { - if (client()->worldview()) { - std::string label; - if (!(argstr >> label)) { - label.assign("main"); - } - client()->worldview()->playerview()->show_menu(label); - } } else { ui::root()->show_menu(command.c_str()); } diff --git a/src/client/client.h b/src/client/client.h index 375a9ce..dbb2c44 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -76,13 +76,17 @@ private: static void func_ui_restart(std::string const &args); static void func_ui_console(std::string const &args); static void func_list_menu(std::string const &args); + static void func_ui_help(); + static void func_ui(std::string const &args); static void func_ui_chat(std::string const &args); static void func_ui_chatbar(std::string const &args); static void func_ui_map(std::string const &args); static void func_ui_menu(std::string const &args); + static void func_menu(std::string const &args); + static void func_view(std::string const &args); WorldView *client_worldview; diff --git a/src/client/entitymenu.cc b/src/client/entitymenu.cc index f727ff3..f1529e1 100644 --- a/src/client/entitymenu.cc +++ b/src/client/entitymenu.cc @@ -16,8 +16,10 @@ EntityMenu::EntityMenu(ui::Widget *parent, const char * label) : ui::Window(pare { set_border(false); set_background(false); - set_label(label); - set_label("entitymenu"); + if (label) + set_label(label); + else + set_label("entitymenu"); menu_container = new ui::Container(this); hide(); @@ -73,7 +75,74 @@ void EntityMenu::generate(core::Entity *entity, const char *menulabel) 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()); + + // parse the command sequence + size_t i = 0; + std::string result; + std::string current; + bool quote = false; + + while (i < buttondescr->command().size()) { + const char c = buttondescr->command()[i]; + + if (c == '"') { + quote = !quote; + result += c; + + } else if (c == ';') { + + if (quote) { + current += c; + + } else if (current.size()) { + if (buttondescr->command_type() == core::ButtonDescription::CommandGame) { + if (result.size()) { + result += ';'; + } + + result.append("remote "); + result.append(current); + + } else if (buttondescr->command_type() == core::ButtonDescription::CommandMenu) { + if (result.size()) { + result += ';'; + } + + result.append("view "); + result.append(current); + } + current.clear(); + } + + } else { + current += c; + } + + i++; + } + + if (current.size()) { + if (buttondescr->command_type() == core::ButtonDescription::CommandGame) { + if (result.size()) { + result += ';'; + } + + result.append("remote "); + result.append(current); + + } else if (buttondescr->command_type() == core::ButtonDescription::CommandMenu) { + if (result.size()) { + result += ';'; + } + + result.append("view "); + result.append(current); + } + + } + + Button *button = new Button(menu_container, buttondescr->text().c_str(), result.c_str()); + switch (buttondescr->alignment()) { case core::ButtonDescription::Center: button->set_alignment(AlignCenter); diff --git a/src/client/entitymenu.h b/src/client/entitymenu.h index ac5a793..adc31a9 100644 --- a/src/client/entitymenu.h +++ b/src/client/entitymenu.h @@ -15,7 +15,7 @@ namespace client { -/// entity menus +/// entity menu class EntityMenu : public ui::Window { public: diff --git a/src/client/input.cc b/src/client/input.cc index 3ccf15c..943bed1 100644 --- a/src/client/input.cc +++ b/src/client/input.cc @@ -516,6 +516,9 @@ void key_pressed(Key *key) } if (ui::root()->input_key(true, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers)) { + + // work-around for the mouse release event FIXME + //key->key_pressed = 0; return; } else if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { @@ -549,12 +552,14 @@ void key_released(Key *key) if (core::application()->connected() && core::localcontrol()) { + // FIXME mouse release selection should be handled inside the hud if ((key->sym() == 512 + SDL_BUTTON_LEFT) && targets::hover() && (key->waspressed() <= (input_mousedelay->value()/1000.0f) ) ) { // hovering target selected targets::select_target(targets::hover()); } - // the release event still must be processed as usual + + // the release event must still be processed as usual char c = 0; c = key->bind(Key::None).c_str()[0]; if (c == '+') { diff --git a/src/client/playerview.cc b/src/client/playerview.cc index 54d444d..e4a889a 100644 --- a/src/client/playerview.cc +++ b/src/client/playerview.cc @@ -4,6 +4,9 @@ the terms and conditions of the GNU General Public License version 2 */ +#include +#include + #include "audio/audio.h" #include "client/playerview.h" #include "ui/ui.h" @@ -32,10 +35,11 @@ PlayerView::PlayerView(ui::Widget *parent) : ui::Widget(parent) view_chat = new Chat(this); view_map = new Map(this); view_hud = new HUD(this); - view_menu = new EntityMenu(this); + view_entitymenu = new EntityMenu(this); + view_buymenu = new BuyMenu(this); view_hud->lower(); - view_menu->raise(); + view_entitymenu->raise(); } PlayerView::~PlayerView() @@ -49,7 +53,7 @@ void PlayerView::clear() view_chat->hide(); view_map->hide(); - view_menu->hide(); + view_entitymenu->hide(); view_lastentity = 0; } @@ -67,8 +71,8 @@ void PlayerView::toggle_map() if(chat()->visible() && !chat()->small_view()) chat()->hide(); - if (view_menu->visible()) - view_menu->hide(); + if (view_entitymenu->visible()) + view_entitymenu->hide(); } map()->toggle(); @@ -85,8 +89,8 @@ void PlayerView::toggle_chat() if(map()->visible()) map()->hide(); - if (view_menu->visible()) - view_menu->hide(); + if (view_entitymenu->visible()) + view_entitymenu->hide(); } chat()->set_small_view(false); chat()->toggle(); @@ -99,16 +103,47 @@ void PlayerView::toggle_chatbar() chat()->toggle(); } -void PlayerView::show_menu(const std::string & label) +void PlayerView::show_menu(const std::string & args) { + if (!args.size()) + return; + if (!core::localplayer()->view()) return; if (!core::localplayer()->view()->menus().size()) return; - view_menu->generate(core::localplayer()->view(), label.c_str()); - view_menu->show(); + std::stringstream argstr(args); + std::string label; + if (!(argstr >> label)) + return; + + if (label.compare("buy") == 0) { + // buy menu, single item + std::string itemtype; + std::string itemname; + + if ((argstr >> itemtype) && (argstr >> itemname)) { + view_buymenu->set_item(itemtype, itemname); + view_buymenu->show(); + view_entitymenu->hide(); + } else { + con_print << "usage: view buy [string] [string] buy menu for item type and name" << std::endl; + } + + } else if (label.compare("trade") == 0) { + // trade menu, multiple items + + } else if (label.compare("hide") == 0) { + view_buymenu->hide(); + view_entitymenu->hide(); + + } else { + view_entitymenu->generate(core::localplayer()->view(), label.c_str()); + view_entitymenu->show(); + view_buymenu->hide(); + } if (chat()->visible() && chat()->small_view()) chat()->raise(); @@ -121,6 +156,9 @@ void PlayerView::resize() set_size(parent()->size()); + // reposition buy menu + view_buymenu->event_resize(); + // set hud geometry view_hud->set_geometry(0,0, width(), height()); view_hud->event_resize(); @@ -142,6 +180,8 @@ void PlayerView::resize() void PlayerView::draw() { + const float smallmargin = ui::UI::elementsize.height(); + if (core::localplayer()->view()) { // hide hide when a view is set @@ -164,12 +204,17 @@ void PlayerView::draw() audio::play("ui/menu"); view_lastentity = core::localplayer()->view(); - } else if (!view_menu->visible() && !map()->visible() && (!chat()->visible() || chat()->small_view()) ) { + } else if (!view_entitymenu->visible() && !view_buymenu->visible() && + !map()->visible() && (!chat()->visible() || chat()->small_view()) ) { + // show the menu if there's no other window open menu()->show(); audio::play("ui/menu"); } + view_notify->set_size(width() - smallmargin * 3.0f - ui::UI::elementsize.width() * 1.5f, height() - smallmargin * 4.0f); + view_notify->set_location(smallmargin * 2.0f + ui::UI::elementsize.width() * 1.5f, smallmargin * 2.0f); + } else { // entity without menus, plain view view_lastentity = 0; @@ -179,8 +224,13 @@ void PlayerView::draw() } } else { - if (view_menu->visible()) { - view_menu->hide(); + view_notify->set_geometry(view_map->location(), view_map->size()); + + if (view_entitymenu->visible()) { + view_entitymenu->hide(); + } + if (view_buymenu->visible()) { + view_buymenu->hide(); } label_viewname->hide(); @@ -193,9 +243,6 @@ void PlayerView::draw() label_zonename->set_text(core::localplayer()->zone()->name()); } - //const float largemargin = ui::UI::elementsize.width() * 0.25; - const float smallmargin = ui::UI::elementsize.height(); - // reposition chat widget if (view_chat->small_view()) { view_chat->set_size(width() - smallmargin * 2, font()->height() * 2); diff --git a/src/client/playerview.h b/src/client/playerview.h index 9cf8a94..a09f5ca 100644 --- a/src/client/playerview.h +++ b/src/client/playerview.h @@ -8,7 +8,9 @@ #define __INCLUDED_CLIENT_PLAYERVIEW_H__ #include "client/chat.h" +#include "client/buymenu.h" #include "client/entitymenu.h" +#include "client/trademenu.h" #include "client/hud.h" #include "client/map.h" #include "client/notifications.h" @@ -40,10 +42,11 @@ public: /// show entity menus void show_menu(const std::string & label); + inline HUD *hud() { return view_hud; } inline Map *map() { return view_map; } inline Chat *chat() { return view_chat; } inline Notifications *notify() { return view_notify; } - inline EntityMenu *menu() { return view_menu; } + inline EntityMenu *menu() { return view_entitymenu; } protected: virtual void draw(); @@ -54,7 +57,10 @@ private: HUD *view_hud; Chat *view_chat; Map *view_map; - EntityMenu *view_menu; + + EntityMenu *view_entitymenu; + BuyMenu *view_buymenu; + TradeMenu *view_trademenu; core::Entity *view_lastentity; diff --git a/src/client/targets.cc b/src/client/targets.cc index 8a50510..af8ec17 100644 --- a/src/client/targets.cc +++ b/src/client/targets.cc @@ -362,7 +362,7 @@ void frame() if (entity->id() == current_target_id) { current_target = entity; } - + // check if the mouse is hovering the entity Vector3f v(entity->location() - render::Camera::eye()); v.normalize(); @@ -390,10 +390,11 @@ void frame() } } } + } } - + if (!current_target) { current_target_id = 0; } else { diff --git a/src/client/trademenu.cc b/src/client/trademenu.cc new file mode 100644 index 0000000..9db1299 --- /dev/null +++ b/src/client/trademenu.cc @@ -0,0 +1,32 @@ +/* + client/trademenu.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/ui.h" +#include "ui/button.h" +#include "ui/paint.h" +#include "client/trademenu.h" + +namespace client +{ + +TradeMenu::TradeMenu(ui::Widget *parent, const char * label) : ui::Window(parent) +{ + set_border(false); + set_background(false); + if (label) + set_label(label); + else + set_label("trademenu"); + + hide(); +} + +TradeMenu::~TradeMenu() +{ + +} + +} diff --git a/src/client/trademenu.h b/src/client/trademenu.h new file mode 100644 index 0000000..feb91f4 --- /dev/null +++ b/src/client/trademenu.h @@ -0,0 +1,29 @@ +/* + client/trademenu.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_CLIENT_TRADEMENU_H__ +#define __INCLUDED_CLIENT_TRADEMENU_H__ + +#include "ui/container.h" +#include "ui/label.h" +#include "ui/window.h" + +namespace client +{ + +/// trade menu +class TradeMenu : public ui::Window +{ +public: + /// create a new menu + TradeMenu(ui::Widget *parent, const char * label = 0); + ~TradeMenu(); +}; + +} + +#endif // __INCLUDED_CLIENT_TRADEMENU_H__ + diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 317a99e..af59d00 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -3,7 +3,7 @@ INCLUDES = -I$(top_srcdir)/src libcore_la_SOURCES = application.cc commandbuffer.cc core.cc cvar.cc \ descriptions.cc entity.cc extension.cc func.cc gameconnection.cc gameinterface.cc \ - gameserver.cc loader.cc module.cc netclient.cc netconnection.cc netplayer.cc \ + gameserver.cc info.cc loader.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 \ @@ -15,3 +15,4 @@ noinst_HEADERS = application.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 descriptions.h \ extension.h loader.h +_SOURCES = info.h diff --git a/src/core/commandbuffer.cc b/src/core/commandbuffer.cc index 21002bd..84b63be 100644 --- a/src/core/commandbuffer.cc +++ b/src/core/commandbuffer.cc @@ -16,6 +16,7 @@ #include "core/commandbuffer.h" #include "core/gameconnection.h" #include "core/func.h" +#include "core/info.h" #include "core/cvar.h" #include "core/loader.h" #include "core/zone.h" @@ -23,6 +24,8 @@ namespace core { +std::stringstream CommandBuffer::cmdbuf(std::stringstream::in | std::stringstream::out); + void func_print(std::string const &args) { con_print << args << "\n"; @@ -38,6 +41,15 @@ void func_print_file(std::string const &args) CommandBuffer::print_file(filename); } +void func_list_info(std::string const &args) +{ + Info *info = Info::find(args); + if (info) + info->print(); + else + Info::list(); +} + void func_list_func(std::string const &args) { Func::list(); @@ -151,7 +163,19 @@ void func_exec(std::string const &args) CommandBuffer::exec_file(filename); } -std::stringstream CommandBuffer::cmdbuf(std::stringstream::in | std::stringstream::out); +void func_remote(std::string const &args) +{ + if (connection()) { + if ((args[0] == '\\') || (args[0] == '/')) { + connection()->forward(args.substr(1, args.size()-1)); + } else { + connection()->forward(args); + } + } else { + cmd() << args; + CommandBuffer::exec(); + } +} void CommandBuffer::init() { @@ -164,6 +188,9 @@ void CommandBuffer::init() func = Func::add("list_func", (FuncPtr)func_list_func); func->set_info("list functions"); + func = Func::add("list_info", (FuncPtr)func_list_info); + func->set_info("list infos"); + func = Func::add("list_var", (FuncPtr)func_list_var); func->set_info("list variables"); @@ -190,6 +217,9 @@ void CommandBuffer::init() func = Func::add("exec", (FuncPtr)func_exec); func->set_info("[filename] execute commands from file"); + + func = Func::add("remote", (FuncPtr)func_remote); + func->set_info("[command] send a command to the game server"); } void CommandBuffer::shutdown() @@ -200,6 +230,7 @@ void CommandBuffer::shutdown() Func::remove("toggle"); Func::remove("list_var"); Func::remove("list_func"); + Func::remove("list_info"); Func::remove("list_ent"); Func::remove("list_model"); Func::remove("list_module"); diff --git a/src/core/descriptions.cc b/src/core/descriptions.cc index fe37d2c..8c0f4ee 100644 --- a/src/core/descriptions.cc +++ b/src/core/descriptions.cc @@ -16,6 +16,7 @@ ButtonDescription::ButtonDescription() { button_model = 0; button_align = Center; + button_commandtype = CommandNone; } ButtonDescription::~ButtonDescription() @@ -27,8 +28,9 @@ void ButtonDescription::set_text(const std::string &text) button_text.assign(text); } -void ButtonDescription::set_command(const std::string &command) +void ButtonDescription::set_command(const std::string &command, const CommandType command_type) { + button_commandtype = command_type; button_command.assign(command); } @@ -85,9 +87,10 @@ void Descriptions::serialize(MenuDescription *menu, std::ostream & os) for (MenuDescription::Buttons::iterator it = menu-> buttons().begin(); it != menu->buttons().end(); it++) { ButtonDescription *button = (*it); - os << "\"" << button->text() << "\" " + os << "\"" << button->text() << "\" " << button->alignment() << " " - << "\"" << button->command() << "\" " + << button->command_type() << " " + << "\"" << button->command() << "\" " << "\"" << button->modelname() << "\" "; } } @@ -97,6 +100,7 @@ MenuDescription * Descriptions::receive(std::istream &is) MenuDescription *menu = new MenuDescription(); int a; + int t; std::string n; size_t nb; char c; @@ -135,12 +139,22 @@ MenuDescription * Descriptions::receive(std::istream &is) else button->set_alignment(ButtonDescription::Center); + // button command type + is >> t; + // button command n.clear(); while ( (is.get(c)) && (c != '"')); while ( (is.get(c)) && (c != '"')) n += c; - if (n.size()) button->set_command(n); + + if (n.size()) { + if (t == ButtonDescription::CommandGame) { + button->set_command(n, ButtonDescription::CommandGame); + } else if (t == ButtonDescription::CommandMenu) { + button->set_command(n, ButtonDescription::CommandMenu); + } + } // button modelname n.clear(); @@ -213,7 +227,10 @@ bool Descriptions::load_entity_menus(core::Entity *entity, const std::string &me if (strval[i] == ',') strval[i] = ';'; } aux::strip_quotes(strval); - button->set_command(strval); + + // default command is a game command + button->set_command(strval, ButtonDescription::CommandGame); + } else if (inifile.got_key_string("model", strval)) { button->set_modelname(strval); } else if (inifile.got_key_string("align", strval)) { diff --git a/src/core/descriptions.h b/src/core/descriptions.h index 345d354..dec7896 100644 --- a/src/core/descriptions.h +++ b/src/core/descriptions.h @@ -28,6 +28,8 @@ class ButtonDescription public: enum Align {Center=0, Left=1, Right=2}; + enum CommandType {CommandNone=0, CommandGame=1, CommandMenu=2}; + ButtonDescription(); ~ButtonDescription(); @@ -36,6 +38,9 @@ public: /// button text inline const std::string & text() const { return button_text; } + /// button command type + inline const CommandType command_type() const {return button_commandtype; } + /// button command inline const std::string & command() const { return button_command; } @@ -54,7 +59,7 @@ public: void set_text(const std::string &text); /// set button command - void set_command(const std::string &command); + void set_command(const std::string &command, const CommandType command_type); /// set button name void set_modelname(const std::string &modelname); @@ -64,10 +69,10 @@ public: private: std::string button_text; + CommandType button_commandtype; std::string button_command; std::string button_modelname; Align button_align; - model::Model *button_model; }; diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index c2763ee..2dc0e32 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -74,6 +74,28 @@ GameConnection::~GameConnection() connection_instance = 0; } +Info *GameConnection::info(const std::string &label) +{ + // check if we already have the info record + Info *info = Info::find(label); + if (info) + return info; + + // create a new information record and set the label + info = new Info(label); + Info::add(info); + + info->text().push_back("Requesting information..."); + + // send an information request to the server + if (connection_network) { + connection_network->send_info_request(info); + } else { + info->text().push_back("^RNot connected."); + } + return info; +} + void GameConnection::abort() { connection_running = false; diff --git a/src/core/gameconnection.h b/src/core/gameconnection.h index e1a7008..5880f9b 100644 --- a/src/core/gameconnection.h +++ b/src/core/gameconnection.h @@ -51,6 +51,9 @@ public: /// localplayer sends a private message to another player void private_message(std::string const &args); + /// returns an info record + Info *info(const std::string &label); + /*----- static ---------------------------------------------------- */ /// return the current game connection diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc index f658619..0ade34b 100644 --- a/src/core/gameinterface.cc +++ b/src/core/gameinterface.cc @@ -12,6 +12,7 @@ #include "core/application.h" #include "core/cvar.h" #include "core/func.h" +#include "core/info.h" #include "core/gameinterface.h" #include "core/player.h" #include "core/zone.h" @@ -120,6 +121,9 @@ void GameInterface::clear() // remove all models model::Model::clear(); + // remove infos + Info::clear(); + // clear player list for (Players::iterator it = game_players.begin(); it != game_players.end(); it++) { Player *player = (*it); diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h index 22b85d5..8dc401b 100644 --- a/src/core/gameinterface.h +++ b/src/core/gameinterface.h @@ -8,6 +8,7 @@ #define __INCLUDED_CORE_GAMEINTERFACE_H__ #include "core/player.h" +#include "core/info.h" namespace core { @@ -38,6 +39,9 @@ public: /// show a list of connected players void list_players(); + /// return the current game time, in seconds + float time() const { return ((float)(timestamp()) / 1000.0f); } + /*----- virtual inspectors --------------------------------------- */ /// returns true if the game server can run a time frime @@ -49,8 +53,8 @@ public: /// return the current game time virtual unsigned long timestamp() const = 0; - /// return the current game time, in seconds - float time() const { return ((float)(timestamp()) / 1000.0f); } + /// returns an info record + virtual Info *info(const std::string &label) = 0; /*----- mutators ------------------------------------------------- */ diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index fc7a814..b82946b 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -226,6 +226,11 @@ GameServer::~GameServer() server_instance = 0; } +Info *GameServer::info(const std::string &label) +{ + return Info::find(label); +} + void GameServer::abort() { server_running = false; diff --git a/src/core/gameserver.h b/src/core/gameserver.h index 31cfca6..ac5f19e 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -77,6 +77,9 @@ public: /// time the server was started inline const unsigned long startup() const { return server_startup; } + /// returns an info record + Info *info(const std::string &label); + /*----- static ---------------------------------------------------- */ /// return the current game server diff --git a/src/core/info.cc b/src/core/info.cc new file mode 100644 index 0000000..fc10fe5 --- /dev/null +++ b/src/core/info.cc @@ -0,0 +1,183 @@ +/* + core/info.xx + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "auxiliary/functions.h" +#include "core/info.h" +#include "sys/sys.h" + +namespace core +{ + +Info::Registry Info::registry; + +Info::Info(const std::string & label) +{ + info_label.assign(label); + aux::to_lowercase(info_label); + aux::strip_quotes(info_label); + info_timestamp = 0; +} + +void Info::set_name(const std::string & name) +{ + info_name.assign(name); +} + +void Info::set_name(const char *name) +{ + info_name.assign(name); +} + +void Info::set_modelname(const std::string & modelname) +{ + info_modelname.assign(modelname); +} + +void Info::set_modelname(const char *modelname) +{ + info_modelname.assign(modelname); +} + +void Info::set_timestamp(const unsigned long timestamp) +{ + info_timestamp = timestamp; +} + +void Info::clear_timestamp() +{ + info_timestamp = 0; +} + +void Info::add_text(const char *text) +{ + std::string str(text); + aux::strip_quotes(str); + info_text.push_back(str); +} + +void Info::add_text(const std::string & text) +{ + add_text(text.c_str()); +} + +void Info::clear_text() +{ + info_text.clear(); +} + +void Info::serialize_server_update(std::ostream & os) const +{ + os << '"' << name() << "\" \"" << modelname() << "\" " << info_text.size() << " "; + + for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) { + if (it != info_text.begin()) + os << ' '; + + os << '"' << (*it) << '"'; + } +} + +void Info::receive_server_update(std::istream &is) +{ + std::string n; + char c; + + // read name + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + info_name.assign(n); + + // read model name + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + + info_modelname.assign(n); + + // read info text + size_t s; + info_text.clear(); + is >> s; + for (size_t i = 0; (i < s) && is.good(); i++) { + n.clear(); + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + + info_text.push_back(n); + } +} + +Info::~Info() +{ + info_text.clear(); + info_modelname.clear(); + info_text.clear(); +} + +void Info::print() const +{ + con_print << "label: ^B" << label() << " ^Nname: ^B" << name() << "^N" << std::endl; + + for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) { + con_print << " " << (*it) << std::endl; + } +} + +/* ---- static info registry --------------------------------------- */ + +void Info::add(Info *info) +{ + if (find(info->label())) + return; + + registry[info->label()] = info; +} + +Info *Info::find(const char *label) +{ + for (Registry::iterator it = registry.begin(); it != registry.end(); it++) { + Info *info = (*it).second; + if (info->label().compare(label) == 0) { + return info; + } + } + return 0; +} + +Info *Info::find(const std::string & label) +{ + for (Registry::iterator it = registry.begin(); it != registry.end(); it++) { + Info *info = (*it).second; + if (info->label().compare(label) == 0) { + return info; + } + } + return 0; +} + +void Info::clear() +{ + for (Registry::iterator it = registry.begin(); it != registry.end(); it++) { + Info *info = (*it).second;; + delete info; + } + registry.clear(); +} + +void Info::list() +{ + for (Registry::iterator it = registry.begin(); it != registry.end(); it++) { + Info *info = (*it).second;; + con_print << info->label() << std::endl; + } + con_print << registry.size() << " registered infos" << std::endl; +} + +} + diff --git a/src/core/info.h b/src/core/info.h new file mode 100644 index 0000000..cc480bf --- /dev/null +++ b/src/core/info.h @@ -0,0 +1,108 @@ +/* + core/info.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_INFO_H__ +#define __INCLUDED_CORE_INFO_H__ + +#include +#include +#include +#include + +#include "model/model.h" + +namespace core +{ + +/** + * an information record + */ +class Info +{ +public: + /// create a new labeled information record + Info(const std::string & label); + /// delete the information record + ~Info(); + + typedef std::deque Text; + + inline const std::string & label() const { return info_label; } + + inline const std::string & name() const { return info_name; } + + inline const std::string & modelname() const { return info_modelname; } + + inline const unsigned long ×tamp() const { return info_timestamp; } + + inline Text & text() { return info_text; } + + void set_name(const std::string & name); + + void set_name(const char *name); + + void set_modelname(const std::string & modelname); + + void set_modelname(const char *modelname); + + /// set the timestamp + void set_timestamp(const unsigned long timestamp); + + /// clear the timestamp + void clear_timestamp(); + + /// add a line of info text + void add_text(const char *text); + + /// add a line of info text + void add_text(const std::string & text); + + /// clear the info text + void clear_text(); + + /// print info to sys::con_out + void print() const; + + /// serialize a server-to-client update on a stream + void serialize_server_update(std::ostream & os) const; + + /// receive a server-to-client update from a stream + void receive_server_update(std::istream &is); + +/* ---- static info registry --------------------------------------- */ + + typedef std::map Registry; + + /// add an item to the info regsitry + static void add(Info *info); + + /// search the info registry for a labeled item + static Info *find(const char * label); + + /// search the info registry for a labeled item + static Info *find(const std::string & label); + + /// clear the info registry + static void clear(); + + /// list the info registry + static void list(); + +private: + std::string info_label; + std::string info_name; + std::string info_modelname; + Text info_text; + + long info_credits; + static Registry registry; + + unsigned long info_timestamp; +}; + +} +#endif // __INCLUDED_CORE_INFO_H__ + diff --git a/src/core/net.h b/src/core/net.h index 887bf1b..01c15be 100644 --- a/src/core/net.h +++ b/src/core/net.h @@ -11,7 +11,7 @@ namespace core { /// network protocol version -const unsigned int PROTOCOLVERSION = 15; +const unsigned int PROTOCOLVERSION = 16; /// maximum lenght of a (compressed) network message block const unsigned int FRAMESIZE = 1152; diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index fc2b6bf..dfb86f7 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -410,6 +410,16 @@ void NetConnection::send_ping_reply(unsigned long timestamp) this->send_raw(msg.str()); } +// send an info record request +void NetConnection::send_info_request(Info *info) +{ + std::ostringstream msg; + msg << "inf " << '"' << info->label() << '"' << '\n'; + this->send_raw(msg.str()); + + info->set_timestamp(application()->timestamp()); +} + // parse incoming client messages /** * The following incoming messages are parsed; @@ -674,7 +684,28 @@ void NetConnection::parse_incoming_message(const std::string & message) return; } } - + } else if (command == "inf") { + + // incoming info record + std::string label; + char c; + + while ( (msgstream.get(c)) && (c != '"')); + while ( (msgstream.get(c)) && (c != '"')) + label += c; + + if (label.size()) { + Info *info = Info::find(label); + if (!info) { + info = new Info(label); + Info::add(info); + } + + info->receive_server_update(msgstream); + info->clear_timestamp(); + } else { + con_warn << "Received empty information record!" << std::endl; + } } else if (command == "sup") { if (connection_state == Connected) { diff --git a/src/core/netconnection.h b/src/core/netconnection.h index a17b35d..7c0b23d 100644 --- a/src/core/netconnection.h +++ b/src/core/netconnection.h @@ -30,6 +30,7 @@ #include "core/entity.h" #include "core/net.h" +#include "core/info.h" namespace core { @@ -71,6 +72,9 @@ public: /// send a console command to the remote server void send_rcon(std::string const &cmdline); + /// send an info request + void send_info_request(Info *info); + /// transmit messages in the send queue to the remote server void transmit(); diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 62d9b74..5b3a991 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -525,7 +525,7 @@ void NetServer::send_player_update(NetClient *client) void NetServer::send_player_update(NetClient *client, Player *player) { std::ostringstream msg; - msg << "pif " << player->id() << " "; + msg << "pif " << player->id() << ' '; client->player()->serialize_server_update(msg); msg << '\n'; client->send_raw(msg.str()); @@ -539,6 +539,16 @@ void NetServer::send_player_disconnect_info(NetClient *client, Player *player) client->send_raw(msg.str()); } +// send a "inf" info record +void NetServer::send_info_update(NetClient *client, Info *info) +{ + std::ostringstream msg; + msg << "inf " << '"' << info->label() << '"' << ' '; + info->serialize_server_update(msg); + msg << '\n'; + client->send_raw(msg.str()); +} + // parse incoming client messages /** @@ -547,6 +557,7 @@ void NetServer::send_player_disconnect_info(NetClient *client, Player *player) * disconnect * cmd * cup + * inf * pif * ping * say @@ -641,6 +652,23 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } + if (command == "inf") { + std::string n; + char c; + + while ( (msgstream.get(c)) && (c != '"')); + while ( (msgstream.get(c)) && (c != '"')) + n += c; + + if (n.size()) { + Info *info = Info::find(n); + if (info) { + send_info_update(client, info); + client->transmit(); + } + } + } + if (command == "rcon") { if ((message.size() > command.size()+1) && Cvar::sv_password->str().size()) { if ((Cvar::sv_password->str().compare(client->player()->rconpassword()) == 0)) { diff --git a/src/core/netserver.h b/src/core/netserver.h index 9ef0f0d..e54ea2e 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -19,6 +19,7 @@ #include "core/net.h" #include "core/netclient.h" #include "core/player.h" +#include "core/info.h" namespace core { @@ -92,6 +93,9 @@ protected: /// send player disconnect information message void send_player_disconnect_info(NetClient *client, Player *player); + /// send player an info record + void send_info_update(NetClient *client, Info *info); + /// set the error state void abort(); diff --git a/src/game/base/collision.cc b/src/game/base/collision.cc index 728ad14..215764c 100644 --- a/src/game/base/collision.cc +++ b/src/game/base/collision.cc @@ -8,26 +8,27 @@ #include "base/game.h" #include "core/zone.h" #include "math/functions.h" -#include "math/vector3f.h" +#include "math/vector3f.h" -namespace game { +namespace game +{ void Collision::distance_test(core::EntityControlable *first, core::Entity *second) { if (!first->owner()) return; - + if (first->state() == core::Entity::Docked) return; - + // FIXME - use distancesquared const float d = math::distance(first->location(), second->location()); const float r = first->radius() + second->radius(); - + if (second->type() == core::Entity::Globe) { // collision with a star or a planet - - if ( (d-r) < 0.0f) { + + if ((d-r) < 0.0f) { // crash zone if ((first->moduletype() == ship_enttype) && (first->state() != core::Entity::Destroyed)) { first->owner()->send_warning("^RBOOM!^N"); @@ -36,9 +37,9 @@ void Collision::distance_test(core::EntityControlable *first, core::Entity *seco } } else if (first->owner()->last_warning() + 5.0f < core::application()->time()) { // warning zone: star corona or planet atmosphere - if ((second->moduletype() == star_enttype) && (d-r < 50.0f)) { + if ((second->moduletype() == star_enttype) && (d-r < 50.0f)) { first->owner()->send_warning("^3Warning: entering star corona!^N"); - } else if ((second->moduletype() == planet_enttype) && (d-r < 15.0f)) { + } else if ((second->moduletype() == planet_enttype) && (d-r < 15.0f)) { first->owner()->send_warning("^3Warning: entering planet gravity well!^N"); } } @@ -49,14 +50,14 @@ void Collision::frame_zone(core::Zone *zone) { core::Zone::Content::iterator first; core::Zone::Content::iterator second; - + for (first = zone->content().begin(); first != zone->content().end(); first ++) { second = first; for (second++; second != zone->content().end(); second++) { - if ( (*first)->type() == core::Entity::Controlable) { + if ((*first)->type() == core::Entity::Controlable) { distance_test(static_cast((*first)), (*second)); } else if ((*second)->type() == core::Entity::Controlable) { - distance_test(static_cast ((*second)), (*first)); + distance_test(static_cast((*second)), (*first)); } } } diff --git a/src/game/base/game.cc b/src/game/base/game.cc index b2824f5..da060b9 100644 --- a/src/game/base/game.cc +++ b/src/game/base/game.cc @@ -191,6 +191,26 @@ void Game::func_dock(core::Player *player, core::Entity *entity) } } +// buy request from a player +void Game::func_buy(core::Player *player, const std::string &args) +{ + std::istringstream is(args); + std::string itemtype; + if (!(is >> itemtype)) { + player->send("usage: buy [string] [string] buy type of item, name of item"); + return; + } + + aux::to_label(itemtype); + if ((args.size() > 5 ) && (itemtype.compare("ship") == 0)) { + ShipDealer::func_buy(player, args.substr(5, args.size() - 5)); + } else { + player->send("unkown item type '" + itemtype + "'"); + } + + return; +} + // launch request void Game::func_launch(core::Player *player, std::string const &args) { @@ -308,8 +328,8 @@ Game::Game() : core::Module("Project::OSiRiON", true) func = core::Func::add("spectate", Game::func_spectate); func->set_info("leave the game and spectate"); - func = core::Func::add("buy", ShipDealer::func_buy); - func->set_info("buy a ship"); + func = core::Func::add("buy", Game::func_buy); + func->set_info("[string] [string] buy type of item, name of item"); func = core::Func::add("jump", Game::func_jump); func->set_info("[string] activate or deactivate hyperspace jump drive"); @@ -697,7 +717,7 @@ bool Game::load_menus(core::Entity *entity, const std::string &menufilename) if (model) { button = new ButtonDescription(); button->set_text("buy " + model->name()); - button->set_command("buy " + model->label()); + button->set_command("buy ship " + model->label(), ButtonDescription::CommandMenu); button->set_modelname(model->modelname()); button->set_alignment(ButtonDescription::Left); menu_dealer->add_button(button); @@ -719,14 +739,14 @@ bool Game::load_menus(core::Entity *entity, const std::string &menufilename) button = new ButtonDescription(); button->set_text("Launch"); - button->set_command("launch"); + button->set_command("launch", ButtonDescription::CommandGame); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); if (menu_dealer) { button = new ButtonDescription(); button->set_text("Return"); - button->set_command("menu view main"); + button->set_command("main", ButtonDescription::CommandMenu); button->set_alignment(ButtonDescription::Center); menu_dealer->add_button(button); @@ -734,7 +754,7 @@ bool Game::load_menus(core::Entity *entity, const std::string &menufilename) button = new ButtonDescription(); button->set_text("Ship dealer"); - button->set_command("menu view dealer"); + button->set_command("dealer", ButtonDescription::CommandMenu); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); } @@ -763,6 +783,7 @@ bool Game::load_ships() ShipModel *shipmodel = 0; std::string label; + std::string infostr; long l; float f; @@ -776,6 +797,9 @@ bool Game::load_ships() continue; } else if (shipsini.got_key_string("name",shipmodel->shipmodel_name)) { continue; + } else if (shipsini.got_key_string("info", infostr)) { + shipmodel->shipmodel_infotext.push_back(infostr); + continue; } else if (shipsini.got_key_string("model", shipmodel->shipmodel_modelname)) { continue; } else if (shipsini.got_key_long("price", l)) { @@ -814,6 +838,14 @@ bool Game::load_ships() if (shipmodel && !ShipModel::find(shipmodel)) delete shipmodel; + // add shipmodel infos + for (ShipModel::iterator it = ShipModel::registry.begin(); it != ShipModel::registry.end(); it++) { + ShipModel *shipmodel = (*it).second; + core::Info *info = new core::Info("ship/" + shipmodel->label()); + shipmodel->generate_info(info); + core::Info::add(info); + } + con_debug << " " << shipsini.name() << " " << ShipModel::registry.size() << " ship models" << std::endl; return true; diff --git a/src/game/base/game.h b/src/game/base/game.h index 36de8dc..1f16a9f 100644 --- a/src/game/base/game.h +++ b/src/game/base/game.h @@ -103,6 +103,7 @@ private: static void func_launch(core::Player *player, std::string const &args); static void func_respawn(core::Player *player, std::string const &args); static void func_goto(core::Player *player, const std::string &args); + static void func_buy(core::Player *player, std::string const &args); }; /// factory function diff --git a/src/game/base/shipmodel.cc b/src/game/base/shipmodel.cc index 78dc125..fc4781a 100644 --- a/src/game/base/shipmodel.cc +++ b/src/game/base/shipmodel.cc @@ -43,11 +43,42 @@ void ShipModel::clear() void ShipModel::print() { con_print << "label: ^B" << label() << " ^Nname: ^B" << name() << std::endl; + con_print << " price: ^B" << price() << std::endl; con_print << " acceleration: ^B" << acceleration() << std::endl; con_print << " turnspeed: ^B" << turnspeed() << std::endl; con_print << " max speed: ^B" << maxspeed() << std::endl; - con_print << " max cargo: ^B" << maxcargo() << std::endl; - con_print << " price: ^B" << price() << std::endl; + con_print << " cargo: ^B" << maxcargo() << std::endl; +} + +void ShipModel::generate_info(core::Info *info) +{ + info->clear_text(); + + info->set_name(name()); + info->set_modelname(modelname()); + + // info text form ships.ini + for (core::Info::Text::iterator it = shipmodel_infotext.begin(); it != shipmodel_infotext.end(); it++) { + info->add_text((*it)); + } + + info->add_text(""); + info->add_text("^BSpecifications:^N"); + std::stringstream str; + str << "price: ^B" << price() << " ^Ncredits"; + info->add_text(str.str()); str.str(""); + + str << "cargo hold: ^B" << 0.1f * maxcargo() << " ^Nmetric tonnes"; + info->add_text(str.str()); str.str(""); + + str << "top speed: ^B" << 100.0f * maxspeed() << " ^Nmps"; + info->add_text(str.str()); str.str(""); + + str << "response: ^B" << turnspeed() << " ^Ndps"; + info->add_text(str.str()); str.str(""); + + str << "acceleration: ^B" << acceleration() << " ^Nstandard"; + info->add_text(str.str()); str.str(""); } void ShipModel::list() diff --git a/src/game/base/shipmodel.h b/src/game/base/shipmodel.h index c194dc0..9bdf4f5 100644 --- a/src/game/base/shipmodel.h +++ b/src/game/base/shipmodel.h @@ -10,6 +10,8 @@ #include #include +#include "core/info.h" + namespace game { /// ship model specifications @@ -60,6 +62,9 @@ public: /// set size of the cargo hold inline void set_maxcargo(const float maxcargo) { shipmodel_maxcargo = maxcargo; } + /// generate an info object for this shipmodel + void generate_info(core::Info *info); + /// indicates of this model can be equiped with a jump drive bool shipmodel_jumpdrive; @@ -67,6 +72,9 @@ public: std::string shipmodel_name; std::string shipmodel_modelname; + /// info text + core::Info::Text shipmodel_infotext; + /* ---- static registry ------------------------------------ */ typedef std::map::iterator iterator; diff --git a/src/ui/modelview.cc b/src/ui/modelview.cc index 4239294..a07922a 100755 --- a/src/ui/modelview.cc +++ b/src/ui/modelview.cc @@ -55,6 +55,11 @@ void ModelView::draw() if (!modelview_modelname.size()) return; + math ::Vector2f center(global_location()); + + center.x += width() * 0.5f; + center.y += height() * 0.5f; + //model::Model *model = model::Model::find(modelview_modelname); // gl 3d mode -- cgit v1.2.3