widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #13087
[Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands with lp:~widelands-dev/widelands/ships_optr as a prerequisite.
Commit message:
Adds a new Ship Statistics window.
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #1358880 in widelands: "List of ships"
https://bugs.launchpad.net/widelands/+bug/1358880
Bug #1618617 in widelands: "Idle ships shows "Shipping" in statistic mode = "s"-key"
https://bugs.launchpad.net/widelands/+bug/1618617
Bug #1749567 in widelands: "heap-use-after-free when sinking a ship"
https://bugs.launchpad.net/widelands/+bug/1749567
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1358880-ship-statistics/+merge/343293
Finally, ths ship list.
lp:~widelands-dev/widelands/ships_optr needs to go in first.
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands.
=== added file 'data/images/wui/editor/fsel_editor_set_port_space.png'
Binary files data/images/wui/editor/fsel_editor_set_port_space.png 1970-01-01 00:00:00 +0000 and data/images/wui/editor/fsel_editor_set_port_space.png 2018-04-16 08:14:47 +0000 differ
=== removed file 'data/images/wui/editor/fsel_editor_set_port_space.png'
Binary files data/images/wui/editor/fsel_editor_set_port_space.png 2018-01-05 12:07:27 +0000 and data/images/wui/editor/fsel_editor_set_port_space.png 1970-01-01 00:00:00 +0000 differ
=== added file 'data/images/wui/editor/fsel_editor_unset_port_space.png'
Binary files data/images/wui/editor/fsel_editor_unset_port_space.png 1970-01-01 00:00:00 +0000 and data/images/wui/editor/fsel_editor_unset_port_space.png 2018-04-16 08:14:47 +0000 differ
=== removed file 'data/images/wui/editor/fsel_editor_unset_port_space.png'
Binary files data/images/wui/editor/fsel_editor_unset_port_space.png 2018-01-05 12:07:27 +0000 and data/images/wui/editor/fsel_editor_unset_port_space.png 1970-01-01 00:00:00 +0000 differ
=== added file 'data/images/wui/ship/ship_construct_port_space.png'
Binary files data/images/wui/ship/ship_construct_port_space.png 1970-01-01 00:00:00 +0000 and data/images/wui/ship/ship_construct_port_space.png 2018-04-16 08:14:47 +0000 differ
=== added file 'data/images/wui/stats/ship_stats_idle.png'
Binary files data/images/wui/stats/ship_stats_idle.png 1970-01-01 00:00:00 +0000 and data/images/wui/stats/ship_stats_idle.png 2018-04-16 08:14:47 +0000 differ
=== added file 'data/images/wui/stats/ship_stats_shipping.png'
Binary files data/images/wui/stats/ship_stats_shipping.png 1970-01-01 00:00:00 +0000 and data/images/wui/stats/ship_stats_shipping.png 2018-04-16 08:14:47 +0000 differ
=== modified file 'data/tribes/scripting/help/controls.lua'
--- data/tribes/scripting/help/controls.lua 2017-12-06 08:16:46 +0000
+++ data/tribes/scripting/help/controls.lua 2018-04-16 08:14:47 +0000
@@ -21,6 +21,16 @@
-- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
dl(help_format_hotkey(pgettext("hotkey", "Ctrl + Left-click on Button")), _"Skip confirmation dialog")) ..
+ h2(_"Table Control") ..
+ h3(_"In tables that allow the selection of multiple entries, the following key combinations are available:") ..
+ p(
+ -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
+ dl(help_format_hotkey(pgettext("hotkey", "Ctrl + Click")), pgettext("table_control", "Select multiple entries")) ..
+ -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
+ dl(help_format_hotkey(pgettext("hotkey", "Shift + Click")), pgettext("table_control", "Select a range of entries")) ..
+ -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
+ dl(help_format_hotkey(pgettext("hotkey", "Ctrl + A")), pgettext("table_control", "Select all entries"))) ..
+
h2(_"Road Control") ..
p(
-- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
@@ -52,6 +62,8 @@
dl(help_format_hotkey("I"), _"Toggle stock inventory") ..
-- TRANSLATORS: This is an access key combination. The hotkey is 'b'
dl(help_format_hotkey("B"), _"Toggle building statistics") ..
+ -- TRANSLATORS: This is an access key combination. The hotkey is 'p'
+ dl(help_format_hotkey("E"), _"Toggle seafaring statistics") ..
-- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
dl(help_format_hotkey(pgettext("hotkey", "Home")), _"Center main mapview on starting location") ..
-- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
@@ -72,18 +84,8 @@
dl(help_format_hotkey(pgettext("hotkey", "F6")), _"Show the debug console (only in debug-builds)")
) ..
- h2(_"Table Control") ..
- h3(_"In tables that allow the selection of multiple entries, the following key combinations are available:") ..
- p(
- -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
- dl(help_format_hotkey(pgettext("hotkey", "Ctrl + Click")), pgettext("table_control", "Select multiple entries")) ..
- -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
- dl(help_format_hotkey(pgettext("hotkey", "Shift + Click")), pgettext("table_control", "Select a range of entries")) ..
- -- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
- dl(help_format_hotkey(pgettext("hotkey", "Ctrl + A")), pgettext("table_control", "Select all entries"))) ..
-
+ -- TRANSLATORS: Heading in "Controls" help
h2(_"Message Window") ..
- h3(_"In the message window, the following additional shortcuts are available:") ..
p(
-- TRANSLATORS: This is the helptext for an access key combination.
dl(help_format_hotkey(pgettext("hotkey", "Alt + 0")), _"Show all messages") ..
@@ -101,5 +103,28 @@
dl(help_format_hotkey("G"), _"Jump to the location corresponding to the current message") ..
-- TRANSLATORS: This is an access key combination. Localize, but do not change the key.
dl(help_format_hotkey(pgettext("hotkey", "Delete")), _"Archive/Restore the current message")
- )
+ ) ..
+
+ -- TRANSLATORS: Heading in "Controls" help
+ h2(_"Ship Statistics") ..
+ p(
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 0")), _"Show all ships") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 1")), _"Show idle ships") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 2")), _"Show ships shipping wares and workers") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 3")), _"Show waiting expeditions") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 4")), _"Show scouting expeditions") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey(pgettext("hotkey", "Alt + 5")), _"Show colonizing expeditions and expeditions with port space found") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey("G"), _"Center the map on the selected ship") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey("O"), _"Go to the selected ship and open its window") ..
+ -- TRANSLATORS: This is the helptext for an access key combination.
+ dl(help_format_hotkey("W"), _"Watch the selected ship")
+ )
}
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2018-04-08 22:33:43 +0000
+++ src/ai/defaultai.cc 2018-04-16 08:14:47 +0000
@@ -167,8 +167,8 @@
});
// Subscribe to ShipNotes.
- shipnotes_subscriber_ = Notifications::subscribe<NoteShipMessage>([this](
- const NoteShipMessage& note) {
+ shipnotes_subscriber_ = Notifications::subscribe<NoteShip>([this](
+ const NoteShip& note) {
// in a short time between start and late_initialization the player
// can get notes that can not be processed.
@@ -180,13 +180,13 @@
return;
}
- switch (note.message) {
+ switch (note.action) {
- case NoteShipMessage::Message::kGained:
+ case NoteShip::Action::kGained:
gain_ship(*note.ship, NewShip::kBuilt);
break;
- case NoteShipMessage::Message::kLost:
+ case NoteShip::Action::kLost:
for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
if (i->ship == note.ship) {
allships.erase(i);
@@ -195,15 +195,19 @@
}
break;
- case NoteShipMessage::Message::kWaitingForCommand:
+ case NoteShip::Action::kWaitingForCommand:
for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
if (i->ship == note.ship) {
i->waiting_for_command_ = true;
break;
}
}
+ default:
+ // Do nothing
+ break;
}
});
+
}
DefaultAI::~DefaultAI() {
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2018-04-08 22:33:43 +0000
+++ src/ai/defaultai.h 2018-04-16 08:14:47 +0000
@@ -414,7 +414,7 @@
outofresource_subscriber_;
std::unique_ptr<Notifications::Subscriber<Widelands::NoteTrainingSiteSoldierTrained>>
soldiertrained_subscriber_;
- std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteShip>> shipnotes_subscriber_;
};
#endif // end of include guard: WL_AI_DEFAULTAI_H
=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc 2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/ship.cc 2018-04-16 08:14:47 +0000
@@ -133,7 +133,6 @@
}
Ship::~Ship() {
- Notifications::publish(NoteShipWindow(serial(), NoteShipWindow::Action::kClose));
}
PortDock* Ship::get_destination(EditorGameBase& egbase) const {
@@ -155,12 +154,13 @@
bool Ship::init(EditorGameBase& egbase) {
Bob::init(egbase);
init_fleet(egbase);
- Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kGained));
assert(get_owner());
+ get_owner()->add_ship(serial());
// Assigning a ship name
shipname_ = get_owner()->pick_shipname();
molog("New ship: %s\n", shipname_.c_str());
+ Notifications::publish(NoteShip(this, NoteShip::Action::kGained));
return true;
}
@@ -182,12 +182,17 @@
fleet_->remove_ship(egbase, this);
}
+ Player* owner = get_owner();
+ if (owner != nullptr) {
+ owner->remove_ship(serial());
+ }
+
while (!items_.empty()) {
items_.back().remove(egbase);
items_.pop_back();
}
- Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kLost));
+ Notifications::publish(NoteShip(this, NoteShip::Action::kLost));
Bob::cleanup(egbase);
}
@@ -279,7 +284,7 @@
case ShipStates::kSinkAnimation:
// The sink animation has been played, so finally remove the ship from the map
pop_task(game);
- remove(game);
+ schedule_destroy(game);
return;
}
// if the real update function failed (e.g. nothing to transport), the ship goes idle
@@ -304,6 +309,7 @@
destination_ = nullptr;
dst->ship_arrived(game, *this);
start_task_idle(game, descr().main_animation(), 250);
+ Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged));
return true;
}
@@ -411,17 +417,13 @@
}
} while (mr.advance(*map));
+ expedition_->seen_port_buildspaces = temp_port_buildspaces;
if (new_port_space) {
- ship_state_ = ShipStates::kExpeditionPortspaceFound;
+ set_ship_state_and_notify(ShipStates::kExpeditionPortspaceFound, NoteShip::Action::kWaitingForCommand);
send_message(game, _("Port Space"), _("Port Space Found"),
_("An expedition ship found a new port build space."),
"images/wui/editor/fsel_editor_set_port_space.png");
}
- expedition_->seen_port_buildspaces = temp_port_buildspaces;
- if (new_port_space) {
- Notifications::publish(
- NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand));
- }
}
}
@@ -532,17 +534,13 @@
} else {
// Check whether the island was completely surrounded
if (get_position() == expedition_->exploration_start) {
+ set_ship_state_and_notify(ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);
send_message(game,
/** TRANSLATORS: A ship has circumnavigated an island and is waiting
for orders */
pgettext("ship", "Waiting"), _("Island Circumnavigated"),
_("An expedition ship sailed around its island without any events."),
"images/wui/ship/ship_explore_island_cw.png");
- ship_state_ = ShipStates::kExpeditionWaiting;
-
- Notifications::publish(
- NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand));
-
return start_task_idle(game, descr().main_animation(), 1500);
}
}
@@ -582,10 +580,10 @@
}
}
// if we are here, it seems something really strange happend.
- log("WARNING: ship %s was not able to start exploration. Entering WAIT mode.\n",
- shipname_.c_str());
- ship_state_ = ShipStates::kExpeditionWaiting;
- return start_task_idle(game, descr().main_animation(), 1500);
+ log("WARNING: ship %s was not able to start exploration. Entering WAIT mode.", shipname_.c_str());
+ set_ship_state_and_notify(ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);
+ start_task_idle(game, descr().main_animation(), 1500);
+ return;
}
} else { // scouting towards a specific direction
if (exp_dir_swimmable(expedition_->scouting_direction)) {
@@ -595,7 +593,7 @@
return;
}
// coast reached
- ship_state_ = ShipStates::kExpeditionWaiting;
+ set_ship_state_and_notify(ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);
start_task_idle(game, descr().main_animation(), 1500);
// Send a message to the player, that a new coast was reached
send_message(game,
@@ -603,10 +601,6 @@
_("Land Ahoy!"), _("Coast Reached"),
_("An expedition ship reached a coast and is waiting for further commands."),
"images/wui/ship/ship_scout_ne.png");
-
- Notifications::publish(
- NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand));
-
return;
}
}
@@ -710,6 +704,13 @@
NEVER_HERE();
}
+void Ship::set_ship_state_and_notify(ShipStates state, NoteShip::Action action) {
+ if (ship_state_ != state) {
+ ship_state_ = state;
+ Notifications::publish(NoteShip(this, action));
+ }
+}
+
void Ship::set_economy(Game& game, Economy* e) {
// Do not check here that the economy actually changed, because on loading
// we rely that wares really get reassigned our economy.
@@ -730,6 +731,7 @@
items_.size());
destination_ = &pd;
send_signal(game, "wakeup");
+ Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged));
}
void Ship::add_item(Game& game, const ShippingItem& item) {
@@ -847,14 +849,14 @@
pgettext("ship", "Expedition"), _("Expedition Ready"),
_("An expedition ship is waiting for your commands."),
"images/wui/buildings/start_expedition.png");
- Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand));
+ Notifications::publish(NoteShip(this, NoteShip::Action::kWaitingForCommand));
}
/// Initializes / changes the direction of scouting to @arg direction
/// @note only called via player command
void Ship::exp_scouting_direction(Game&, WalkingDir scouting_direction) {
assert(expedition_);
- ship_state_ = ShipStates::kExpeditionScouting;
+ set_ship_state_and_notify(ShipStates::kExpeditionScouting, NoteShip::Action::kDestinationChanged);
expedition_->scouting_direction = scouting_direction;
expedition_->island_exploration = false;
}
@@ -880,7 +882,7 @@
for (auto& tree : trees) {
tree.object->remove(game);
}
- ship_state_ = ShipStates::kExpeditionColonizing;
+ set_ship_state_and_notify(ShipStates::kExpeditionColonizing, NoteShip::Action::kDestinationChanged);
}
/// Initializes / changes the direction the island exploration in @arg island_explore_direction
@@ -888,7 +890,7 @@
/// @note only called via player command
void Ship::exp_explore_island(Game&, IslandExploreDirection island_explore_direction) {
assert(expedition_);
- ship_state_ = ShipStates::kExpeditionScouting;
+ set_ship_state_and_notify(ShipStates::kExpeditionScouting, NoteShip::Action::kDestinationChanged);
expedition_->island_explore_direction = island_explore_direction;
expedition_->scouting_direction = WalkingDir::IDLE;
expedition_->island_exploration = true;
@@ -945,7 +947,7 @@
}
}
- Notifications::publish(NoteShipWindow(serial(), NoteShipWindow::Action::kNoPortLeft));
+ Notifications::publish(NoteShip(this, NoteShip::Action::kNoPortLeft));
return;
}
assert(get_economy() && get_economy() != expedition_->economy.get());
@@ -962,7 +964,6 @@
// Running colonization has the highest priority + a sink request is only valid once
if (!state_is_sinkable())
return;
- Notifications::publish(NoteShipWindow(serial(), NoteShipWindow::Action::kClose));
ship_state_ = ShipStates::kSinkRequest;
// Make sure the ship is active and close possible open windows
ship_wakeup(game);
@@ -980,8 +981,13 @@
if (draw_text & TextToDraw::kStatistics) {
switch (ship_state_) {
case (ShipStates::kTransport):
- /** TRANSLATORS: This is a ship state */
- statistics_string = pgettext("ship_state", "Shipping");
+ if (destination_.is_set()) {
+ /** TRANSLATORS: This is a ship state. The ship is currently transporting wares. */
+ statistics_string = pgettext("ship_state", "Shipping");
+ } else {
+ /** TRANSLATORS: This is a ship state. The ship is ready to transport wares, but has nothing to do. */
+ statistics_string = pgettext("ship_state", "Idle");
+ }
break;
case (ShipStates::kExpeditionWaiting):
/** TRANSLATORS: This is a ship state. An expedition is waiting for your commands. */
@@ -1182,6 +1188,7 @@
// economy. Also, we might are on an expedition which means that we just now
// created the economy of this ship and must inform all wares.
ship.set_economy(dynamic_cast<Game&>(egbase()), ship.economy_);
+ ship.get_owner()->add_ship(ship.serial());
}
MapObject::Loader* Ship::load(EditorGameBase& egbase, MapObjectLoader& mol, FileRead& fr) {
=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h 2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/ship.h 2018-04-16 08:14:47 +0000
@@ -41,29 +41,16 @@
kNotSet
};
-struct NoteShipMessage {
- CAN_BE_SENT_AS_NOTE(NoteId::ShipMessage)
+struct NoteShip {
+ CAN_BE_SENT_AS_NOTE(NoteId::Ship)
Ship* ship;
- enum class Message { kLost, kGained, kWaitingForCommand };
- Message message;
-
- NoteShipMessage(Ship* const init_ship, const Message& init_message)
- : ship(init_ship), message(init_message) {
- }
-};
-
-struct NoteShipWindow {
- CAN_BE_SENT_AS_NOTE(NoteId::ShipWindow)
-
- Serial serial;
-
- enum class Action { kClose, kNoPortLeft };
- const Action action;
-
- NoteShipWindow(Serial init_serial, const Action& init_action)
- : serial(init_serial), action(init_action) {
+ enum class Action { kDestinationChanged, kWaitingForCommand, kNoPortLeft, kLost, kGained };
+ Action action;
+
+ NoteShip(Ship* const init_ship, const Action& init_action)
+ : ship(init_ship), action(init_action) {
}
};
@@ -263,6 +250,8 @@
bool ship_update_transport(Game&, State&);
void ship_update_expedition(Game&, State&);
void ship_update_idle(Game&, State&);
+ /// Set the ship's state to 'state' and if the ship state has changed, publish a notification.
+ void set_ship_state_and_notify(ShipStates state, NoteShip::Action action);
bool init_fleet(EditorGameBase&);
void set_fleet(Fleet* fleet);
=== modified file 'src/logic/player.cc'
--- src/logic/player.cc 2018-04-07 16:59:00 +0000
+++ src/logic/player.cc 2018-04-16 08:14:47 +0000
@@ -23,6 +23,7 @@
#include <memory>
#include <boost/bind.hpp>
+#include <boost/format.hpp>
#include <boost/signals2.hpp>
#include "base/i18n.h"
@@ -372,6 +373,18 @@
game->cmdqueue().enqueue(new CmdDeleteMessage(game->get_gametime(), player_number_, message_id));
}
+const std::set<Serial>& Player::ships() const {
+ return ships_;
+}
+void Player::add_ship(Serial ship) {
+ ships_.insert(ship);
+}
+void Player::remove_ship(Serial ship) {
+ if (ships_.count(ship) == 1) {
+ ships_.erase(ship);
+ }
+}
+
/*
===============
Return filtered buildcaps that take the player's territory into account.
@@ -1303,7 +1316,7 @@
remaining_shipnames_.erase(it);
return new_name;
}
- return "Ship";
+ return (boost::format(pgettext("shipname", "Ship %d")) % ships_.size()).str();
}
/**
=== modified file 'src/logic/player.h'
--- src/logic/player.h 2018-04-07 16:59:00 +0000
+++ src/logic/player.h 2018-04-16 08:14:47 +0000
@@ -111,6 +111,10 @@
get_messages()->set_message_status(id, status);
}
+ const std::set<Serial>& ships() const;
+ void add_ship(Serial ship);
+ void remove_ship(Serial ship);
+
const EditorGameBase& egbase() const {
return egbase_;
}
@@ -639,6 +643,7 @@
std::vector<bool> allowed_worker_types_;
std::vector<bool> allowed_building_types_;
Economies economies_;
+ std::set<Serial> ships_;
std::string name_; // Player name
std::string ai_; /**< Name of preferred AI implementation */
=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h 2018-04-07 16:59:00 +0000
+++ src/notifications/note_ids.h 2018-04-16 08:14:47 +0000
@@ -33,8 +33,7 @@
FieldTerrainChanged,
ProductionSiteOutOfResources,
TrainingSiteSoldierTrained,
- ShipMessage,
- ShipWindow,
+ Ship,
Building,
Economy,
GraphicResolutionChanged,
=== modified file 'src/scripting/lua_game.cc'
--- src/scripting/lua_game.cc 2018-04-07 16:59:00 +0000
+++ src/scripting/lua_game.cc 2018-04-16 08:14:47 +0000
@@ -672,30 +672,16 @@
*/
int LuaPlayer::get_ships(lua_State* L) {
EditorGameBase& egbase = get_egbase(L);
- const Map& map = egbase.map();
PlayerNumber p = (get(L, egbase)).player_number();
lua_newtable(L);
uint32_t cidx = 1;
-
- std::set<OPtr<Ship>> found_ships;
- for (int16_t y = 0; y < map.get_height(); ++y) {
- for (int16_t x = 0; x < map.get_width(); ++x) {
- FCoords f = map.get_fcoords(Coords(x, y));
- // there are too many bobs on the map so we investigate
- // only bobs on water
- if (f.field->nodecaps() & MOVECAPS_SWIM) {
- for (Bob* bob = f.field->get_first_bob(); bob; bob = bob->get_next_on_field()) {
- if (upcast(Ship, ship, bob)) {
- if (ship->get_owner()->player_number() == p && !found_ships.count(ship)) {
- found_ships.insert(ship);
- lua_pushuint32(L, cidx++);
- LuaMaps::upcasted_map_object_to_lua(L, ship);
- lua_rawset(L, -3);
- }
- }
- }
- }
- }
+ for (const auto& serial : egbase.player(p).ships()) {
+ Widelands::MapObject* obj = egbase.objects().get_object(serial);
+ assert(obj->descr().type() == Widelands::MapObjectType::SHIP);
+ upcast(Widelands::Ship, ship, obj);
+ lua_pushuint32(L, cidx++);
+ LuaMaps::upcasted_map_object_to_lua(L, ship);
+ lua_rawset(L, -3);
}
return 1;
}
=== modified file 'src/ui_basic/table.cc'
--- src/ui_basic/table.cc 2018-04-07 16:59:00 +0000
+++ src/ui_basic/table.cc 2018-04-16 08:14:47 +0000
@@ -551,6 +551,21 @@
layout();
}
+/**
+ * Remove the given table entry if it exists.
+ */
+void Table<void*>::remove_entry(const void* const entry) {
+ EntryRecord* er = find(entry);
+ if (er != nullptr) {
+ for (uint32_t i = 0; i < entry_records_.size(); ++i) {
+ if (entry_records_[i] == er) {
+ remove(i);
+ return;
+ }
+ }
+ }
+}
+
bool Table<void*>::sort_helper(uint32_t a, uint32_t b) {
if (sort_descending_) {
return columns_[sort_column_].compare(b, a);
=== modified file 'src/ui_basic/table.h'
--- src/ui_basic/table.h 2018-04-07 16:59:00 +0000
+++ src/ui_basic/table.h 2018-04-16 08:14:47 +0000
@@ -84,6 +84,7 @@
void sort(uint32_t lower_bound = 0, uint32_t upper_bound = std::numeric_limits<uint32_t>::max());
void remove(uint32_t);
+ void remove_entry(Entry);
EntryRecord& add(void* const entry, const bool select_this = false);
@@ -209,6 +210,7 @@
void sort(uint32_t lower_bound = 0, uint32_t upper_bound = std::numeric_limits<uint32_t>::max());
void remove(uint32_t);
+ void remove_entry(const void* const entry);
EntryRecord& add(void* entry = nullptr, bool select = false);
@@ -335,6 +337,10 @@
: Base(parent, x, y, w, h, button_background, rowtype) {
}
+ void remove_entry(Entry const* const entry) {
+ Base::remove_entry(const_cast<Entry*>(entry));
+ }
+
EntryRecord& add(Entry const* const entry = 0, bool const select_this = false) {
return Base::add(const_cast<Entry*>(entry), select_this);
}
@@ -365,6 +371,10 @@
: Base(parent, x, y, w, h, button_background, rowtype) {
}
+ void remove_entry(Entry const* entry) {
+ Base::remove_entry(entry);
+ }
+
EntryRecord& add(Entry* const entry = 0, bool const select_this = false) {
return Base::add(entry, select_this);
}
@@ -395,6 +405,10 @@
: Base(parent, x, y, w, h, button_background, rowtype) {
}
+ void remove_entry(const Entry& entry) {
+ Base::remove_entry(&const_cast<Entry&>(entry));
+ }
+
EntryRecord& add(const Entry& entry, bool const select_this = false) {
return Base::add(&const_cast<Entry&>(entry), select_this);
}
@@ -429,6 +443,10 @@
: Base(parent, x, y, w, h, button_background, rowtype) {
}
+ void remove_entry(Entry& entry) {
+ Base::remove_entry(&entry);
+ }
+
EntryRecord& add(Entry& entry, bool const select_this = false) {
return Base::add(&entry, select_this);
}
@@ -465,6 +483,10 @@
: Base(parent, x, y, w, h, button_background, rowtype) {
}
+ void remove_entry(uintptr_t const entry) {
+ Base::remove_entry(reinterpret_cast<void*>(entry));
+ }
+
EntryRecord& add(uintptr_t const entry, bool const select_this = false) {
return Base::add(reinterpret_cast<void*>(entry), select_this);
}
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2017-11-20 13:50:51 +0000
+++ src/wui/CMakeLists.txt 2018-04-16 08:14:47 +0000
@@ -227,6 +227,8 @@
portdockwaresdisplay.h
productionsitewindow.cc
productionsitewindow.h
+ seafaring_statistics_menu.cc
+ seafaring_statistics_menu.h
shipwindow.cc
shipwindow.h
soldiercapacitycontrol.cc
=== modified file 'src/wui/game_statistics_menu.cc'
--- src/wui/game_statistics_menu.cc 2018-04-07 16:59:00 +0000
+++ src/wui/game_statistics_menu.cc 2018-04-16 08:14:47 +0000
@@ -27,6 +27,7 @@
#include "wui/building_statistics_menu.h"
#include "wui/general_statistics_menu.h"
#include "wui/interactive_player.h"
+#include "wui/seafaring_statistics_menu.h"
#include "wui/stock_menu.h"
#include "wui/ware_statistics_menu.h"
@@ -37,6 +38,7 @@
player_(plr),
windows_(windows),
box_(this, 0, 0, UI::Box::Horizontal, 0, 0, 5) {
+ const bool is_seafaring = plr.egbase().mutable_map()->allows_seafaring();
add_button("wui/menus/menu_general_stats", "general_stats", _("General statistics"),
&windows_.general_stats);
add_button(
@@ -44,8 +46,12 @@
add_button("wui/menus/menu_building_stats", "building_stats", _("Building statistics"),
&windows_.building_stats);
add_button("wui/menus/menu_stock", "stock", _("Stock"), &windows_.stock);
+ if (is_seafaring) {
+ add_button("wui/buildings/start_expedition", "seafaring_stats", _("Seafaring Statistics"),
+ &windows_.seafaring_stats);
+ }
box_.set_pos(Vector2i(10, 10));
- box_.set_size((34 + 5) * 4, 34);
+ box_.set_size((34 + 5) * (is_seafaring ? 5 : 4), 34);
set_inner_size(box_.get_w() + 20, box_.get_h() + 20);
windows_.general_stats.open_window = [this] {
@@ -58,6 +64,11 @@
new BuildingStatisticsMenu(player_, windows_.building_stats);
};
// The stock window is defined in InteractivePlayer because of the keyboard shortcut.
+ if (is_seafaring) {
+ windows_.seafaring_stats.open_window = [this] {
+ new SeafaringStatisticsMenu(player_, windows_.seafaring_stats);
+ };
+ }
if (get_usedefaultpos())
center_to_parent();
=== modified file 'src/wui/interactive_gamebase.cc'
--- src/wui/interactive_gamebase.cc 2018-04-07 16:59:00 +0000
+++ src/wui/interactive_gamebase.cc 2018-04-16 08:14:47 +0000
@@ -233,20 +233,23 @@
for (Widelands::Bob* temp_ship : ships) {
if (upcast(Widelands::Ship, ship, temp_ship)) {
if (can_see(ship->get_owner()->player_number())) {
- UI::UniqueWindow::Registry& registry =
- unique_windows().get_registry((boost::format("ship_%d") % ship->serial()).str());
- registry.open_window = [this, ®istry, ship] {
- new ShipWindow(*this, registry, *ship);
- };
- registry.create();
+ show_ship_window(ship);
return true;
}
}
}
-
return false;
}
+void InteractiveGameBase::show_ship_window(Widelands::Ship* ship) {
+ UI::UniqueWindow::Registry& registry =
+ unique_windows().get_registry((boost::format("ship_%d") % ship->serial()).str());
+ registry.open_window = [this, ®istry, ship] {
+ new ShipWindow(*this, registry, ship);
+ };
+ registry.create();
+}
+
void InteractiveGameBase::show_game_summary() {
if (game_summary_.window) {
game_summary_.window->set_visible(true);
=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h 2018-04-07 16:59:00 +0000
+++ src/wui/interactive_gamebase.h 2018-04-16 08:14:47 +0000
@@ -48,6 +48,7 @@
GeneralStatisticsMenu::Registry general_stats;
UI::UniqueWindow::Registry ware_stats;
UI::UniqueWindow::Registry stock;
+ UI::UniqueWindow::Registry seafaring_stats;
};
InteractiveGameBase(Widelands::Game&,
@@ -86,6 +87,7 @@
bool was_minimal);
UI::UniqueWindow* show_building_window(const Widelands::Coords& coords, bool avoid_fastclick);
bool try_show_ship_window();
+ void show_ship_window(Widelands::Ship* ship);
bool is_multiplayer() {
return multiplayer_;
}
=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc 2018-04-07 16:59:00 +0000
+++ src/wui/interactive_player.cc 2018-04-16 08:14:47 +0000
@@ -51,6 +51,7 @@
#include "wui/game_options_menu.h"
#include "wui/game_statistics_menu.h"
#include "wui/general_statistics_menu.h"
+#include "wui/seafaring_statistics_menu.h"
#include "wui/stock_menu.h"
#include "wui/tribal_encyclopedia.h"
#include "wui/ware_statistics_menu.h"
@@ -459,6 +460,14 @@
}
return true;
+ case SDLK_e:
+ if (main_windows_.seafaring_stats.window == nullptr) {
+ new SeafaringStatisticsMenu(*this, main_windows_.seafaring_stats);
+ } else {
+ main_windows_.seafaring_stats.toggle();
+ }
+ return true;
+
case SDLK_s:
if (code.mod & (KMOD_LCTRL | KMOD_RCTRL))
new GameMainMenuSaveGame(*this, main_windows_.savegame);
=== added file 'src/wui/seafaring_statistics_menu.cc'
--- src/wui/seafaring_statistics_menu.cc 1970-01-01 00:00:00 +0000
+++ src/wui/seafaring_statistics_menu.cc 2018-04-16 08:14:47 +0000
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2017 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "wui/seafaring_statistics_menu.h"
+
+#include <memory>
+
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include "economy/fleet.h"
+#include "graphic/graphic.h"
+#include "logic/game.h"
+#include "logic/player.h"
+#include "logic/playercommand.h"
+#include "ui_basic/box.h"
+#include "wui/interactive_player.h"
+#include "wui/shipwindow.h"
+#include "wui/watchwindow.h"
+
+inline InteractivePlayer& SeafaringStatisticsMenu::iplayer() const {
+ return dynamic_cast<InteractivePlayer&>(*get_parent());
+}
+
+constexpr int kPadding = 5;
+constexpr int kButtonSize = 34;
+
+SeafaringStatisticsMenu::SeafaringStatisticsMenu(InteractivePlayer& plr,
+ UI::UniqueWindow::Registry& registry)
+ : UI::UniqueWindow(&plr, "seafaring_statistics", ®istry, 355, 375, _("Seafaring Statistics")),
+ main_box_(this, kPadding, kPadding, UI::Box::Vertical, get_inner_w(), get_inner_h(), kPadding),
+ filter_box_(
+ &main_box_, 0, 0, UI::Box::Horizontal, get_inner_w() - 2 * kPadding, kButtonSize, kPadding),
+ idle_btn_(&filter_box_,
+ "filter_ship_idle",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but0.png"),
+ status_to_image(ShipFilterStatus::kIdle)),
+ waiting_btn_(&filter_box_,
+ "filter_ship_waiting",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but0.png"),
+ status_to_image(ShipFilterStatus::kExpeditionWaiting)),
+ scouting_btn_(&filter_box_,
+ "filter_ship_scouting",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but0.png"),
+ status_to_image(ShipFilterStatus::kExpeditionScouting)),
+ portspace_btn_(&filter_box_,
+ "filter_ship_portspace",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but0.png"),
+ status_to_image(ShipFilterStatus::kExpeditionPortspaceFound)),
+ shipping_btn_(&filter_box_,
+ "filter_ship_transporting",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but0.png"),
+ status_to_image(ShipFilterStatus::kShipping)),
+ ship_filter_(ShipFilterStatus::kAll),
+ navigation_box_(
+ &main_box_, 0, 0, UI::Box::Horizontal, get_inner_w() - 2 * kPadding, kButtonSize, kPadding),
+ watchbtn_(&navigation_box_,
+ "seafaring_stats_watch_button",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but2.png"),
+ g_gr->images().get("images/wui/menus/menu_watch_follow.png"),
+ (boost::format(_("%1% (Hotkey: %2%)"))
+ %
+ /** TRANSLATORS: Tooltip in the seafaring statistics window */
+ _("Watch the selected ship") % pgettext("hotkey", "W"))
+ .str()),
+ openwindowbtn_(&navigation_box_,
+ "seafaring_stats_watch_button",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but2.png"),
+ g_gr->images().get("images/ui_basic/fsel.png"),
+ (boost::format(_("%1% (Hotkey: %2%)"))
+ %
+ /** TRANSLATORS: Tooltip in the seafaring statistics window */
+ _("Go to the selected ship and open its window") % pgettext("hotkey", "O"))
+ .str()),
+ centerviewbtn_(&navigation_box_,
+ "seafaring_stats_center_main_mapview_button",
+ 0,
+ 0,
+ kButtonSize,
+ kButtonSize,
+ g_gr->images().get("images/ui_basic/but2.png"),
+ g_gr->images().get("images/wui/ship/menu_ship_goto.png"),
+ (boost::format(_("%1% (Hotkey: %2%)"))
+ %
+ /** TRANSLATORS: Tooltip in the seafaring statistics window */
+ _("Center the map on the selected ship") % pgettext("hotkey", "G"))
+ .str()),
+ table_(&main_box_,
+ 0,
+ 0,
+ get_inner_w() - 2 * kPadding,
+ 100,
+ g_gr->images().get("images/ui_basic/but1.png")) {
+
+ const Widelands::TribeDescr& tribe = iplayer().player().tribe();
+ colony_icon_ = tribe.get_worker_descr(tribe.builder())->icon();
+
+ // Buttons for ship states
+ main_box_.add(&filter_box_, UI::Box::Resizing::kFullSize);
+ filter_box_.add(&idle_btn_);
+ filter_box_.add(&shipping_btn_);
+ filter_box_.add(&waiting_btn_);
+ filter_box_.add(&scouting_btn_);
+ filter_box_.add(&portspace_btn_);
+
+ main_box_.add(&table_, UI::Box::Resizing::kExpandBoth);
+
+ // Navigation buttons
+ main_box_.add(&navigation_box_, UI::Box::Resizing::kFullSize);
+ navigation_box_.add(&watchbtn_);
+ navigation_box_.add_inf_space();
+ navigation_box_.add(&openwindowbtn_);
+ navigation_box_.add(¢erviewbtn_);
+ main_box_.set_size(get_inner_w() - 2 * kPadding, get_inner_h() - 2 * kPadding);
+
+ // Configure actions
+ idle_btn_.sigclicked.connect(
+ boost::bind(&SeafaringStatisticsMenu::filter_ships, this, ShipFilterStatus::kIdle));
+ shipping_btn_.sigclicked.connect(
+ boost::bind(&SeafaringStatisticsMenu::filter_ships, this, ShipFilterStatus::kShipping));
+ waiting_btn_.sigclicked.connect(boost::bind(
+ &SeafaringStatisticsMenu::filter_ships, this, ShipFilterStatus::kExpeditionWaiting));
+ scouting_btn_.sigclicked.connect(boost::bind(
+ &SeafaringStatisticsMenu::filter_ships, this, ShipFilterStatus::kExpeditionScouting));
+ portspace_btn_.sigclicked.connect(boost::bind(
+ &SeafaringStatisticsMenu::filter_ships, this, ShipFilterStatus::kExpeditionPortspaceFound));
+ ship_filter_ = ShipFilterStatus::kAll;
+ set_filter_ships_tooltips();
+
+ watchbtn_.sigclicked.connect(boost::bind(&SeafaringStatisticsMenu::watch_ship, this));
+ openwindowbtn_.sigclicked.connect(boost::bind(&SeafaringStatisticsMenu::open_ship_window, this));
+ centerviewbtn_.sigclicked.connect(boost::bind(&SeafaringStatisticsMenu::center_view, this));
+
+ // Configure table
+ table_.selected.connect(boost::bind(&SeafaringStatisticsMenu::selected, this));
+ table_.double_clicked.connect(boost::bind(&SeafaringStatisticsMenu::double_clicked, this));
+ table_.add_column(
+ 0, pgettext("ship", "Name"), "", UI::Align::kLeft, UI::TableColumnType::kFlexible);
+ table_.add_column(200, pgettext("ship", "Status"));
+ table_.set_sort_column(ColName);
+ fill_table();
+
+ set_can_focus(true);
+ set_thinks(false);
+ table_.focus();
+
+ shipnotes_subscriber_ =
+ Notifications::subscribe<Widelands::NoteShip>([this](const Widelands::NoteShip& note) {
+ if (iplayer().get_player() == note.ship->get_owner()) {
+ switch (note.action) {
+ case Widelands::NoteShip::Action::kDestinationChanged:
+ case Widelands::NoteShip::Action::kWaitingForCommand:
+ case Widelands::NoteShip::Action::kGained:
+ update_ship(*note.ship);
+ break;
+ case Widelands::NoteShip::Action::kLost:
+ remove_ship(note.ship->serial());
+ break;
+ default:
+ NEVER_HERE();
+ }
+ }
+ });
+}
+
+const std::string
+SeafaringStatisticsMenu::status_to_string(SeafaringStatisticsMenu::ShipFilterStatus status) const {
+ switch (status) {
+ case SeafaringStatisticsMenu::ShipFilterStatus::kIdle:
+ return pgettext("ship_state", "Idle");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kShipping:
+ return pgettext("ship_state", "Shipping");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionWaiting:
+ return pgettext("ship_state", "Waiting");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionScouting:
+ return pgettext("ship_state", "Scouting");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionPortspaceFound:
+ return pgettext("ship_state", "Port Space Found");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionColonizing:
+ return pgettext("ship_state", "Founding a Colony");
+ case SeafaringStatisticsMenu::ShipFilterStatus::kAll:
+ return "All"; // The user shouldn't see this, so we don't localize
+ default:
+ NEVER_HERE();
+ }
+}
+
+const Image*
+SeafaringStatisticsMenu::status_to_image(SeafaringStatisticsMenu::ShipFilterStatus status) const {
+ std::string filename = "";
+ switch (status) {
+ case SeafaringStatisticsMenu::ShipFilterStatus::kIdle:
+ filename = "images/wui/stats/ship_stats_idle.png";
+ break;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kShipping:
+ filename = "images/wui/stats/ship_stats_shipping.png";
+ break;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionWaiting:
+ filename = "images/wui/buildings/start_expedition.png";
+ break;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionScouting:
+ filename = "images/wui/ship/ship_explore_island_cw.png";
+ break;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionPortspaceFound:
+ filename = "images/wui/ship/ship_construct_port_space.png";
+ break;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kExpeditionColonizing:
+ return colony_icon_;
+ case SeafaringStatisticsMenu::ShipFilterStatus::kAll:
+ filename = "images/wui/ship/ship_scout_ne.png";
+ break;
+ default:
+ NEVER_HERE();
+ }
+ return g_gr->images().get(filename);
+}
+
+const SeafaringStatisticsMenu::ShipInfo*
+SeafaringStatisticsMenu::create_shipinfo(const Widelands::Ship& ship) const {
+ if (&ship == nullptr) {
+ return new ShipInfo();
+ }
+ const Widelands::Ship::ShipStates state = ship.get_ship_state();
+ ShipFilterStatus status = ShipFilterStatus::kAll;
+ switch (state) {
+ case Widelands::Ship::ShipStates::kTransport:
+ if (ship.get_destination(iplayer().game()) != nullptr) {
+ status = ShipFilterStatus::kShipping;
+ } else {
+ status = ShipFilterStatus::kIdle;
+ }
+ break;
+ case Widelands::Ship::ShipStates::kExpeditionWaiting:
+ status = ShipFilterStatus::kExpeditionWaiting;
+ break;
+ case Widelands::Ship::ShipStates::kExpeditionScouting:
+ status = ShipFilterStatus::kExpeditionScouting;
+ break;
+ case Widelands::Ship::ShipStates::kExpeditionPortspaceFound:
+ status = ShipFilterStatus::kExpeditionPortspaceFound;
+ break;
+ case Widelands::Ship::ShipStates::kExpeditionColonizing:
+ status = ShipFilterStatus::kExpeditionColonizing;
+ break;
+ case Widelands::Ship::ShipStates::kSinkRequest:
+ case Widelands::Ship::ShipStates::kSinkAnimation:
+ status = ShipFilterStatus::kAll;
+ }
+ return new ShipInfo(ship.get_shipname(), status, ship.serial());
+}
+
+void SeafaringStatisticsMenu::set_entry_record(UI::Table<uintptr_t>::EntryRecord* er,
+ const ShipInfo& info) {
+ if (info.status != ShipFilterStatus::kAll) {
+ er->set_string(ColName, info.name);
+ er->set_picture(ColStatus, status_to_image(info.status), status_to_string(info.status));
+ }
+}
+
+Widelands::Ship* SeafaringStatisticsMenu::serial_to_ship(Widelands::Serial serial) const {
+ Widelands::MapObject* obj = iplayer().game().objects().get_object(serial);
+ assert(obj->descr().type() == Widelands::MapObjectType::SHIP);
+ upcast(Widelands::Ship, ship, obj);
+ return ship;
+}
+
+void SeafaringStatisticsMenu::update_ship(const Widelands::Ship& ship) {
+ assert(iplayer().get_player() == ship.get_owner());
+ const ShipInfo* info = create_shipinfo(ship);
+ // Remove ships that don't satisfy the filter
+ if (ship_filter_ != ShipFilterStatus::kAll && !satisfies_filter(*info, ship_filter_)) {
+ remove_ship(info->serial);
+ return;
+ }
+ // Try to find the ship in the table
+ if (data_.count(info->serial) == 1) {
+ const ShipInfo* old_info = data_[info->serial].get();
+ if (info->status != old_info->status) {
+ // The status has changed - we need an update
+ data_[info->serial] = std::unique_ptr<const ShipInfo>(info);
+ UI::Table<uintptr_t>::EntryRecord* er = table_.find(info->serial);
+ set_entry_record(er, *info);
+ }
+ } else {
+ // This is a new ship or it was filtered away before
+ data_.insert(std::make_pair(info->serial, std::unique_ptr<const ShipInfo>(info)));
+ UI::Table<uintptr_t>::EntryRecord& er = table_.add(info->serial);
+ set_entry_record(&er, *info);
+ }
+ table_.sort();
+ set_buttons_enabled();
+}
+
+void SeafaringStatisticsMenu::remove_ship(Widelands::Serial serial) {
+ if (data_.count(serial) == 1) {
+ table_.remove_entry(serial);
+ data_.erase(data_.find(serial));
+ if (!table_.empty() && !table_.has_selection()) {
+ table_.select(0);
+ }
+ set_buttons_enabled();
+ }
+}
+
+void SeafaringStatisticsMenu::update_entry_record(UI::Table<uintptr_t>::EntryRecord& er,
+ const ShipInfo& info) {
+ er.set_picture(ColStatus, status_to_image(info.status), status_to_string(info.status));
+}
+
+void SeafaringStatisticsMenu::selected() {
+ set_buttons_enabled();
+}
+
+void SeafaringStatisticsMenu::double_clicked() {
+ if (table_.has_selection()) {
+ center_view();
+ }
+}
+
+void SeafaringStatisticsMenu::set_buttons_enabled() {
+ centerviewbtn_.set_enabled(table_.has_selection());
+ openwindowbtn_.set_enabled(table_.has_selection());
+ watchbtn_.set_enabled(table_.has_selection());
+}
+
+bool SeafaringStatisticsMenu::handle_key(bool down, SDL_Keysym code) {
+ if (down) {
+ switch (code.sym) {
+ // Don't forget to change the tooltips if any of these get reassigned
+ case SDLK_g:
+ center_view();
+ return true;
+ case SDLK_o:
+ open_ship_window();
+ return true;
+ case SDLK_w:
+ watch_ship();
+ return true;
+ case SDLK_0:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kAll);
+ return true;
+ }
+ return false;
+ case SDLK_1:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kIdle);
+ return true;
+ }
+ return false;
+ case SDLK_2:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kShipping);
+ return true;
+ }
+ return false;
+ case SDLK_3:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kExpeditionWaiting);
+ return true;
+ }
+ return false;
+ case SDLK_4:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kExpeditionScouting);
+ return true;
+ }
+ return false;
+ case SDLK_5:
+ if (code.mod & KMOD_ALT) {
+ filter_ships(ShipFilterStatus::kExpeditionPortspaceFound);
+ return true;
+ }
+ return false;
+ case SDL_SCANCODE_KP_PERIOD:
+ case SDLK_KP_PERIOD:
+ if (code.mod & KMOD_NUM)
+ break;
+ /* no break */
+ default:
+ break; // not handled
+ }
+ }
+
+ return table_.handle_key(down, code);
+}
+
+void SeafaringStatisticsMenu::center_view() {
+ if (table_.has_selection()) {
+ Widelands::Ship* ship = serial_to_ship(table_.get_selected());
+ iplayer().map_view()->scroll_to_field(ship->get_position(), MapView::Transition::Smooth);
+ }
+}
+
+void SeafaringStatisticsMenu::watch_ship() {
+ if (table_.has_selection()) {
+ Widelands::Ship* ship = serial_to_ship(table_.get_selected());
+ WatchWindow* window = show_watch_window(iplayer(), ship->get_position());
+ window->follow(ship);
+ }
+}
+
+void SeafaringStatisticsMenu::open_ship_window() {
+ if (table_.has_selection()) {
+ center_view();
+ Widelands::Ship* ship = serial_to_ship(table_.get_selected());
+ iplayer().show_ship_window(ship);
+ }
+}
+
+void SeafaringStatisticsMenu::filter_ships(ShipFilterStatus status) {
+ switch (status) {
+ case ShipFilterStatus::kExpeditionWaiting:
+ toggle_filter_ships_button(waiting_btn_, status);
+ break;
+ case ShipFilterStatus::kExpeditionScouting:
+ toggle_filter_ships_button(scouting_btn_, status);
+ break;
+ // We're grouping the "colonizing" status with the port space.
+ case ShipFilterStatus::kExpeditionColonizing:
+ case ShipFilterStatus::kExpeditionPortspaceFound:
+ toggle_filter_ships_button(portspace_btn_, status);
+ break;
+ case ShipFilterStatus::kShipping:
+ toggle_filter_ships_button(shipping_btn_, status);
+ break;
+ case ShipFilterStatus::kIdle:
+ toggle_filter_ships_button(idle_btn_, status);
+ break;
+ case ShipFilterStatus::kAll:
+ set_filter_ships_tooltips();
+ ship_filter_ = ShipFilterStatus::kAll;
+ waiting_btn_.set_perm_pressed(false);
+ scouting_btn_.set_perm_pressed(false);
+ portspace_btn_.set_perm_pressed(false);
+ shipping_btn_.set_perm_pressed(false);
+ idle_btn_.set_perm_pressed(false);
+ break;
+ }
+ fill_table();
+}
+
+void SeafaringStatisticsMenu::toggle_filter_ships_button(UI::Button& button,
+ ShipFilterStatus status) {
+ set_filter_ships_tooltips();
+ if (button.style() == UI::Button::Style::kPermpressed) {
+ button.set_perm_pressed(false);
+ ship_filter_ = ShipFilterStatus::kAll;
+ } else {
+ waiting_btn_.set_perm_pressed(false);
+ scouting_btn_.set_perm_pressed(false);
+ portspace_btn_.set_perm_pressed(false);
+ shipping_btn_.set_perm_pressed(false);
+ idle_btn_.set_perm_pressed(false);
+ button.set_perm_pressed(true);
+ ship_filter_ = status;
+
+ /** TRANSLATORS: %1% is a tooltip, %2% is the corresponding hotkey */
+ button.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show all ships") % pgettext("hotkey", "Alt + 0"))
+ .str());
+ }
+}
+
+void SeafaringStatisticsMenu::set_filter_ships_tooltips() {
+
+ idle_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show idle ships") % pgettext("hotkey", "Alt + 1"))
+ .str());
+ shipping_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show ships shipping wares and workers") %
+ pgettext("hotkey", "Alt + 2"))
+ .str());
+ waiting_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show waiting expeditions") % pgettext("hotkey", "Alt + 3"))
+ .str());
+ scouting_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show scouting expeditions") % pgettext("hotkey", "Alt + 4"))
+ .str());
+ portspace_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
+ /** TRANSLATORS: Tooltip in the messages window */
+ % _("Show colonizing expeditions and expeditions with port space found") %
+ pgettext("hotkey", "Alt + 5"))
+ .str());
+}
+
+bool SeafaringStatisticsMenu::satisfies_filter(const ShipInfo& info, ShipFilterStatus filter) {
+ return filter == info.status ||
+ (filter == ShipFilterStatus::kExpeditionPortspaceFound
+ && info.status == ShipFilterStatus::kExpeditionColonizing);
+}
+
+void SeafaringStatisticsMenu::fill_table() {
+ const Widelands::Serial last_selection = table_.has_selection() ? table_.get_selected() : Widelands::INVALID_INDEX;
+ table_.clear();
+ data_.clear();
+ set_buttons_enabled();
+ for (const auto& serial : iplayer().player().ships()) {
+ Widelands::Ship* ship = serial_to_ship(serial);
+ assert(iplayer().get_player() == ship->get_owner());
+ const ShipInfo* info = create_shipinfo(*ship);
+ if (info->status != ShipFilterStatus::kAll) {
+ if (ship_filter_ == ShipFilterStatus::kAll || satisfies_filter(*info, ship_filter_)) {
+ data_.insert(std::make_pair(serial, std::unique_ptr<const ShipInfo>(info)));
+ UI::Table<uintptr_t const>::EntryRecord& er = table_.add(serial, serial == last_selection);
+ set_entry_record(&er, *info);
+ }
+ }
+ }
+
+ if (!table_.empty()) {
+ table_.sort();
+ if (!table_.has_selection()) {
+ table_.select(0);
+ }
+ }
+}
=== added file 'src/wui/seafaring_statistics_menu.h'
--- src/wui/seafaring_statistics_menu.h 1970-01-01 00:00:00 +0000
+++ src/wui/seafaring_statistics_menu.h 2018-04-16 08:14:47 +0000
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_WUI_SEAFARING_STATISTICS_MENU_H
+#define WL_WUI_SEAFARING_STATISTICS_MENU_H
+
+#include <memory>
+#include <unordered_map>
+
+#include "base/i18n.h"
+#include "logic/map_objects/tribes/ship.h"
+#include "notifications/notifications.h"
+#include "ui_basic/box.h"
+#include "ui_basic/button.h"
+#include "ui_basic/table.h"
+#include "ui_basic/unique_window.h"
+
+class InteractivePlayer;
+
+/// Shows a list of the ships owned by the interactive player with filtering and navigation options.
+struct SeafaringStatisticsMenu : public UI::UniqueWindow {
+ SeafaringStatisticsMenu(InteractivePlayer&, UI::UniqueWindow::Registry&);
+
+private:
+ /// For identifying the columns in the table.
+ enum Cols { ColName, ColStatus };
+ /**
+ * A list of ship status that we can filter for. This differs a bit from that the Widelands::Ship
+ * class has, so we define our own.
+ * */
+ enum class ShipFilterStatus {
+ kIdle,
+ kShipping,
+ kExpeditionWaiting,
+ kExpeditionScouting,
+ kExpeditionPortspaceFound,
+ kExpeditionColonizing,
+ kAll
+ };
+
+ /// Returns the localized strings that we use to display the 'status' in the table.
+ const std::string status_to_string(ShipFilterStatus status) const;
+ /// Returns the icon that we use to represent the 'status' in the table and on the filter
+ /// buttons.
+ const Image* status_to_image(ShipFilterStatus status) const;
+
+ /// The dataset that we need to display ships in the table.
+ struct ShipInfo {
+ ShipInfo(const std::string& init_name,
+ const ShipFilterStatus init_status,
+ const Widelands::Serial init_serial)
+ : name(init_name), status(init_status), serial(init_serial) {
+ }
+ ShipInfo() : ShipInfo("", ShipFilterStatus::kAll, 0) {
+ }
+ ShipInfo(const ShipInfo& other) : ShipInfo(other.name, other.status, other.serial) {
+ }
+ bool operator==(const ShipInfo& other) const {
+ return serial == other.serial;
+ }
+ bool operator<(const ShipInfo& other) const {
+ return serial < other.serial;
+ }
+
+ const std::string name;
+ ShipFilterStatus status;
+ const Widelands::Serial serial;
+ };
+
+ /// Creates our dataset from a 'ship'. Make sure to take ownership of the result.
+ const ShipInfo* create_shipinfo(const Widelands::Ship& ship) const;
+ /// Uses the 'serial' to identify and get a ship from the game.
+ Widelands::Ship* serial_to_ship(Widelands::Serial serial) const;
+
+ /// Convenience function to upcast the panel's parent.
+ InteractivePlayer& iplayer() const;
+
+ /// A ship was selected, so enable the navigation buttons.
+ void selected();
+ /// A ship was double clicked. Centers main view on ship.
+ void double_clicked();
+ /// Handle filter and navigation hotkeys
+ bool handle_key(bool down, SDL_Keysym code) override;
+
+ /// Enables the navigation buttons if a ship is selected, disables them otherwise.
+ void set_buttons_enabled();
+ /// Center the mapview on the currently selected ship.
+ void center_view();
+ /// Follow the selected ship in a watch window.
+ void watch_ship();
+ /// Center the mapview on the currently selected ship and open its window.
+ void open_ship_window();
+
+ /**
+ * Updates the status for the ship. If the ship is new and satisfies the 'ship_filter_',
+ * adds it to the table and data. If it doesn't satisfy the 'ship_filter_', removes the ship
+ * instead.
+ * */
+ void update_ship(const Widelands::Ship&);
+ /// If we listed the ship, remove it from table and data.
+ void remove_ship(Widelands::Serial serial);
+ /// Sets the contents for the entry record in the table.
+ void set_entry_record(UI::Table<uintptr_t>::EntryRecord*, const ShipInfo& info);
+ /// Updates the ship status display in the table.
+ void update_entry_record(UI::Table<uintptr_t>::EntryRecord& er, const ShipInfo&);
+ /// Rebuilds data and table with all ships that satisfy the current 'ship_filter_'.
+ void fill_table();
+
+ /// Show only the ships that have the given status. Toggle the appropriate buttons.
+ void filter_ships(ShipFilterStatus status);
+ /// Helper for filter_ships
+ void toggle_filter_ships_button(UI::Button&, ShipFilterStatus);
+ /// Helper for filter_ships
+ void set_filter_ships_tooltips();
+
+ /// We group colonizing status with port space found. Anything else needs to have an identical status.
+ bool satisfies_filter(const ShipInfo& info, ShipFilterStatus filter);
+
+ const Image* colony_icon_;
+ UI::Box main_box_;
+ // Buttons for ship states
+ UI::Box filter_box_;
+ UI::Button idle_btn_;
+ UI::Button waiting_btn_;
+ UI::Button scouting_btn_;
+ UI::Button portspace_btn_;
+ UI::Button shipping_btn_;
+ ShipFilterStatus ship_filter_;
+ // Navigation buttons
+ UI::Box navigation_box_;
+ UI::Button watchbtn_;
+ UI::Button openwindowbtn_;
+ UI::Button centerviewbtn_;
+
+ // Data
+ UI::Table<uintptr_t> table_;
+ std::unordered_map<Widelands::Serial, std::unique_ptr<const ShipInfo>> data_;
+
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteShip>> shipnotes_subscriber_;
+};
+
+#endif // end of include guard: WL_WUI_SEAFARING_STATISTICS_MENU_H
=== modified file 'src/wui/shipwindow.cc'
--- src/wui/shipwindow.cc 2018-04-07 16:59:00 +0000
+++ src/wui/shipwindow.cc 2018-04-16 08:14:47 +0000
@@ -46,25 +46,25 @@
static const char pic_scout_e[] = "images/wui/ship/ship_scout_e.png";
static const char pic_scout_sw[] = "images/wui/ship/ship_scout_sw.png";
static const char pic_scout_se[] = "images/wui/ship/ship_scout_se.png";
-static const char pic_construct_port[] = "images/wui/editor/fsel_editor_set_port_space.png";
+static const char pic_construct_port[] = "images/wui/ship/ship_construct_port_space.png";
constexpr int kPadding = 5;
} // namespace
using namespace Widelands;
-ShipWindow::ShipWindow(InteractiveGameBase& igb, UniqueWindow::Registry& reg, Ship& ship)
- : UniqueWindow(&igb, "shipwindow", ®, 0, 0, ship.get_shipname()),
+ShipWindow::ShipWindow(InteractiveGameBase& igb, UniqueWindow::Registry& reg, Ship* ship)
+ : UniqueWindow(&igb, "shipwindow", ®, 0, 0, ship->get_shipname()),
igbase_(igb),
ship_(ship),
vbox_(this, 0, 0, UI::Box::Vertical),
navigation_box_(&vbox_, 0, 0, UI::Box::Vertical),
navigation_box_height_(0) {
vbox_.set_inner_spacing(kPadding);
- assert(ship_.get_owner());
+ assert(ship->get_owner());
- display_ = new ItemWaresDisplay(&vbox_, *ship_.get_owner());
- display_->set_capacity(ship_.descr().get_capacity());
+ display_ = new ItemWaresDisplay(&vbox_, ship->owner());
+ display_->set_capacity(ship->descr().get_capacity());
vbox_.add(display_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
// Expedition buttons
@@ -158,30 +158,26 @@
move_out_of_the_way();
warp_mouse_to_fastclick_panel();
- shipnotes_subscriber_ = Notifications::subscribe<Widelands::NoteShipWindow>([this](
- const Widelands::NoteShipWindow& note) {
- if (note.serial == ship_.serial()) {
+ shipnotes_subscriber_ = Notifications::subscribe<Widelands::NoteShip>([this](
+ const Widelands::NoteShip& note) {
+ if (note.ship->serial() == ship_.serial()) {
switch (note.action) {
// Unable to cancel the expedition
- case Widelands::NoteShipWindow::Action::kNoPortLeft:
- if (upcast(InteractiveGameBase, igamebase, ship_.get_owner()->egbase().get_ibase())) {
- if (igamebase->can_act(ship_.get_owner()->player_number())) {
- UI::WLMessageBox messagebox(
- get_parent(),
- /** TRANSLATORS: Window label when an expedition can't be canceled */
- _("Cancel Expedition"), _("This expedition can’t be canceled, because the "
- "ship has no port to return to."),
- UI::WLMessageBox::MBoxType::kOk);
- messagebox.run<UI::Panel::Returncodes>();
- }
- }
+ case Widelands::NoteShip::Action::kNoPortLeft:
+ no_port_error_message();
break;
// The ship is no more
- case Widelands::NoteShipWindow::Action::kClose:
+ case Widelands::NoteShip::Action::kLost:
// Stop this from thinking to avoid segfaults
set_thinks(false);
die();
break;
+ // If the ship state has changed, e.g. expedition started or scouting direction changed,
+ // think() will take care of it.
+ case Widelands::NoteShip::Action::kDestinationChanged:
+ case Widelands::NoteShip::Action::kWaitingForCommand:
+ case Widelands::NoteShip::Action::kGained:
+ break;
}
}
});
@@ -195,10 +191,14 @@
}
void ShipWindow::set_button_visibility() {
- if (navigation_box_.is_visible() != ship_.state_is_expedition()) {
- navigation_box_.set_visible(ship_.state_is_expedition());
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ if (navigation_box_.is_visible() != ship->state_is_expedition()) {
+ navigation_box_.set_visible(ship->state_is_expedition());
navigation_box_.set_desired_size(
- navigation_box_.get_w(), ship_.state_is_expedition() ? navigation_box_height_ : 0);
+ navigation_box_.get_w(), ship->state_is_expedition() ? navigation_box_height_ : 0);
layout();
}
if (btn_cancel_expedition_->is_visible() != btn_cancel_expedition_->enabled()) {
@@ -206,19 +206,41 @@
}
}
+void ShipWindow::no_port_error_message() {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ if (upcast(InteractiveGameBase, igamebase, ship->get_owner()->egbase().get_ibase())) {
+ if (igamebase->can_act(ship->owner().player_number())) {
+ UI::WLMessageBox messagebox(
+ get_parent(),
+ /** TRANSLATORS: Window label when an expedition can't be canceled */
+ _("Cancel Expedition"), _("This expedition can’t be canceled, because the "
+ "ship has no port to return to."),
+ UI::WLMessageBox::MBoxType::kOk);
+ messagebox.run<UI::Panel::Returncodes>();
+ }
+ }
+}
+
void ShipWindow::think() {
UI::Window::think();
- InteractiveBase* ib = ship_.get_owner()->egbase().get_ibase();
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ InteractiveBase* ib = ship->get_owner()->egbase().get_ibase();
bool can_act = false;
if (upcast(InteractiveGameBase, igb, ib))
- can_act = igb->can_act(ship_.get_owner()->player_number());
+ can_act = igb->can_act(ship->owner().player_number());
- btn_destination_->set_enabled(ship_.get_destination(igbase_.egbase()));
+ btn_destination_->set_enabled(ship->get_destination(igbase_.egbase()));
btn_sink_->set_enabled(can_act);
display_->clear();
- for (uint32_t idx = 0; idx < ship_.get_nritems(); ++idx) {
- Widelands::ShippingItem item = ship_.get_item(idx);
+ for (uint32_t idx = 0; idx < ship->get_nritems(); ++idx) {
+ Widelands::ShippingItem item = ship->get_item(idx);
Widelands::WareInstance* ware;
Widelands::Worker* worker;
item.get(igbase_.egbase(), &ware, &worker);
@@ -231,8 +253,8 @@
}
}
- Ship::ShipStates state = ship_.get_ship_state();
- if (ship_.state_is_expedition()) {
+ Ship::ShipStates state = ship->get_ship_state();
+ if (ship->state_is_expedition()) {
/* The following rules apply:
* - The "construct port" button is only active, if the ship is waiting for commands and found
* a port
@@ -249,9 +271,9 @@
bool coast_nearby = false;
for (Direction dir = 1; dir <= LAST_DIRECTION; ++dir) {
// NOTE buttons are saved in the format DIRECTION - 1
- btn_scout_[dir - 1]->set_enabled(can_act && ship_.exp_dir_swimmable(dir) &&
+ btn_scout_[dir - 1]->set_enabled(can_act && ship->exp_dir_swimmable(dir) &&
(state != Ship::ShipStates::kExpeditionColonizing));
- coast_nearby |= !ship_.exp_dir_swimmable(dir);
+ coast_nearby |= !ship->exp_dir_swimmable(dir);
}
btn_explore_island_cw_->set_enabled(can_act && coast_nearby &&
(state != Ship::ShipStates::kExpeditionColonizing));
@@ -259,7 +281,7 @@
(state != Ship::ShipStates::kExpeditionColonizing));
btn_sink_->set_enabled(can_act && (state != Ship::ShipStates::kExpeditionColonizing));
}
- btn_cancel_expedition_->set_enabled(ship_.state_is_expedition() && can_act &&
+ btn_cancel_expedition_->set_enabled(ship->state_is_expedition() && can_act &&
(state != Ship::ShipStates::kExpeditionColonizing));
// Expedition specific buttons
set_button_visibility();
@@ -279,12 +301,20 @@
/// Move the main view towards the current ship location
void ShipWindow::act_goto() {
- igbase_.map_view()->scroll_to_field(ship_.get_position(), MapView::Transition::Smooth);
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ igbase_.map_view()->scroll_to_field(ship->get_position(), MapView::Transition::Smooth);
}
/// Move the main view towards the current destination of the ship
void ShipWindow::act_destination() {
- if (PortDock* destination = ship_.get_destination(igbase_.egbase())) {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ if (PortDock* destination = ship->get_destination(igbase_.egbase())) {
igbase_.map_view()->scroll_to_field(
destination->get_warehouse()->get_position(), MapView::Transition::Smooth);
}
@@ -292,53 +322,77 @@
/// Sink the ship if confirmed
void ShipWindow::act_sink() {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
if (SDL_GetModState() & KMOD_CTRL) {
- igbase_.game().send_player_sink_ship(ship_);
+ igbase_.game().send_player_sink_ship(*ship);
} else {
- show_ship_sink_confirm(dynamic_cast<InteractivePlayer&>(igbase_), ship_);
+ show_ship_sink_confirm(dynamic_cast<InteractivePlayer&>(igbase_), *ship);
}
}
/// Show debug info
void ShipWindow::act_debug() {
- show_mapobject_debug(igbase_, ship_);
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ show_mapobject_debug(igbase_, *ship);
}
/// Cancel expedition if confirmed
void ShipWindow::act_cancel_expedition() {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
if (SDL_GetModState() & KMOD_CTRL) {
- igbase_.game().send_player_cancel_expedition_ship(ship_);
+ igbase_.game().send_player_cancel_expedition_ship(*ship);
} else {
- show_ship_cancel_expedition_confirm(dynamic_cast<InteractivePlayer&>(igbase_), ship_);
+ show_ship_cancel_expedition_confirm(dynamic_cast<InteractivePlayer&>(igbase_), *ship);
}
}
/// Sends a player command to the ship to scout towards a specific direction
void ShipWindow::act_scout_towards(WalkingDir direction) {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
// ignore request if the direction is not swimmable at all
- if (!ship_.exp_dir_swimmable(static_cast<Direction>(direction)))
+ if (!ship->exp_dir_swimmable(static_cast<Direction>(direction)))
return;
- igbase_.game().send_player_ship_scouting_direction(ship_, direction);
+ igbase_.game().send_player_ship_scouting_direction(*ship, direction);
}
/// Constructs a port at the port build space in vision range
void ShipWindow::act_construct_port() {
- if (ship_.exp_port_spaces().empty())
- return;
- igbase_.game().send_player_ship_construct_port(ship_, ship_.exp_port_spaces().front());
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
+ if (ship->exp_port_spaces().empty())
+ return;
+ igbase_.game().send_player_ship_construct_port(*ship, ship->exp_port_spaces().front());
}
/// Explores the island cw or ccw
void ShipWindow::act_explore_island(IslandExploreDirection direction) {
+ Widelands::Ship* ship = ship_.get(igbase_.egbase());
+ if (ship == nullptr) {
+ return;
+ }
bool coast_nearby = false;
bool moveable = false;
for (Direction dir = 1; (dir <= LAST_DIRECTION) && (!coast_nearby || !moveable); ++dir) {
- if (!ship_.exp_dir_swimmable(dir))
+ if (!ship->exp_dir_swimmable(dir))
coast_nearby = true;
else
moveable = true;
}
if (!coast_nearby || !moveable)
return;
- igbase_.game().send_player_ship_explore_island(ship_, direction);
+ igbase_.game().send_player_ship_explore_island(*ship, direction);
}
=== modified file 'src/wui/shipwindow.h'
--- src/wui/shipwindow.h 2018-04-07 16:59:00 +0000
+++ src/wui/shipwindow.h 2018-04-16 08:14:47 +0000
@@ -36,7 +36,7 @@
*/
class ShipWindow : public UI::UniqueWindow {
public:
- ShipWindow(InteractiveGameBase& igb, UI::UniqueWindow::Registry& reg, Widelands::Ship& ship);
+ ShipWindow(InteractiveGameBase& igb, UI::UniqueWindow::Registry& reg, Widelands::Ship* ship);
private:
void think() override;
@@ -47,6 +47,7 @@
const std::string& picname,
boost::function<void()> callback);
void set_button_visibility();
+ void no_port_error_message();
void act_goto();
void act_destination();
@@ -58,7 +59,7 @@
void act_explore_island(Widelands::IslandExploreDirection);
InteractiveGameBase& igbase_;
- Widelands::Ship& ship_;
+ Widelands::OPtr<Widelands::Ship> ship_;
UI::Box vbox_;
UI::Box navigation_box_;
@@ -74,7 +75,7 @@
UI::Button* btn_construct_port_;
ItemWaresDisplay* display_;
int navigation_box_height_;
- std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipWindow>> shipnotes_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteShip>> shipnotes_subscriber_;
DISALLOW_COPY_AND_ASSIGN(ShipWindow);
};
=== modified file 'src/wui/watchwindow.cc'
--- src/wui/watchwindow.cc 2018-04-07 16:59:00 +0000
+++ src/wui/watchwindow.cc 2018-04-16 08:14:47 +0000
@@ -31,66 +31,20 @@
#include "logic/map_objects/bob.h"
#include "logic/player.h"
#include "profile/profile.h"
-#include "ui_basic/button.h"
-#include "ui_basic/window.h"
#include "wui/interactive_gamebase.h"
#include "wui/interactive_player.h"
-#include "wui/mapview.h"
#include "wui/mapviewpixelconstants.h"
#include "wui/mapviewpixelfunctions.h"
-#define NUM_VIEWS 5
#define REFRESH_TIME 5000
// Holds information for a view
-struct WatchWindowView {
- MapView::View view;
- Widelands::ObjectPointer tracking; // if non-null, we're tracking a Bob
-};
-
-struct WatchWindow : public UI::Window {
- WatchWindow(InteractiveGameBase& parent,
- int32_t x,
- int32_t y,
- uint32_t w,
- uint32_t h,
- bool single_window_ = false);
- ~WatchWindow() override;
-
- Widelands::Game& game() const {
- return parent_.game();
- }
-
- boost::signals2::signal<void(Vector2f)> warp_mainview;
-
- void add_view(Widelands::Coords);
- void next_view();
- void save_coords();
- void close_cur_view();
- void toggle_buttons();
-
-protected:
- void think() override;
- void stop_tracking_by_drag();
- void draw(RenderTarget&) override;
-
-private:
- void do_follow();
- void do_goto();
- void view_button_clicked(uint8_t index);
- void set_current_view(uint8_t idx, bool save_previous = true);
-
- InteractiveGameBase& parent_;
- MapView map_view_;
- uint32_t last_visit_;
- bool single_window_;
- uint8_t cur_index_;
- UI::Button* view_btns_[NUM_VIEWS];
- std::vector<WatchWindowView> views_;
-};
-
static WatchWindow* g_watch_window = nullptr;
+Widelands::Game& WatchWindow::game() const {
+ return parent_.game();
+}
+
WatchWindow::WatchWindow(InteractiveGameBase& parent,
int32_t const x,
int32_t const y,
@@ -98,8 +52,8 @@
uint32_t const h,
bool const init_single_window)
: UI::Window(&parent, "watch", x, y, w, h, _("Watch")),
- parent_(parent),
- map_view_(this, game().map(), 0, 0, 200, 166),
+ parent_(parent),
+ map_view_(this, game().map(), 0, 0, 200, 166),
last_visit_(game().get_gametime()),
single_window_(init_single_window),
cur_index_(0) {
@@ -115,7 +69,7 @@
gotobtn->sigclicked.connect(boost::bind(&WatchWindow::do_goto, this));
if (init_single_window) {
- for (uint8_t i = 0; i < NUM_VIEWS; ++i) {
+ for (uint8_t i = 0; i < kViews; ++i) {
view_btns_[i] = new UI::Button(this, "view", 74 + (17 * i), 200 - 34, 17, 34,
g_gr->images().get("images/ui_basic/but0.png"), "-");
view_btns_[i]->sigclicked.connect(boost::bind(&WatchWindow::view_button_clicked, this, i));
@@ -128,24 +82,24 @@
}
map_view_.field_clicked.connect(
- [&parent](const Widelands::NodeAndTriangle<>& node_and_triangle) {
- parent.map_view()->field_clicked(node_and_triangle);
- });
- map_view_.track_selection.connect(
- [&parent](const Widelands::NodeAndTriangle<>& node_and_triangle) {
- parent.map_view()->track_selection(node_and_triangle);
- });
- map_view_.changeview.connect([this] { stop_tracking_by_drag(); });
+ [&parent](const Widelands::NodeAndTriangle<>& node_and_triangle) {
+ parent.map_view()->field_clicked(node_and_triangle);
+ });
+ map_view_.track_selection.connect(
+ [&parent](const Widelands::NodeAndTriangle<>& node_and_triangle) {
+ parent.map_view()->track_selection(node_and_triangle);
+ });
+ map_view_.changeview.connect([this] { stop_tracking_by_drag(); });
warp_mainview.connect([&parent](const Vector2f& map_pixel) {
parent.map_view()->scroll_to_map_pixel(map_pixel, MapView::Transition::Smooth);
});
}
void WatchWindow::draw(RenderTarget& dst) {
- UI::Window::draw(dst);
- if (!is_minimal()) {
- parent_.draw_map_view(&map_view_, &dst);
- }
+ UI::Window::draw(dst);
+ if (!is_minimal()) {
+ parent_.draw_map_view(&map_view_, &dst);
+ }
}
/**
@@ -154,9 +108,9 @@
* This also resets the view cycling timer.
*/
void WatchWindow::add_view(Widelands::Coords const coords) {
- if (views_.size() >= NUM_VIEWS)
+ if (views_.size() >= kViews)
return;
- WatchWindowView view;
+ WatchWindow::View view;
map_view_.scroll_to_field(coords, MapView::Transition::Jump);
@@ -183,7 +137,7 @@
// Enables/Disables buttons for views_
void WatchWindow::toggle_buttons() {
- for (uint32_t i = 0; i < NUM_VIEWS; ++i) {
+ for (uint32_t i = 0; i < kViews; ++i) {
if (i < views_.size()) {
view_btns_[i]->set_title(std::to_string(i + 1));
view_btns_[i]->set_enabled(true);
@@ -228,7 +182,7 @@
if (upcast(Widelands::Bob, bob, views_[cur_index_].tracking.get(game()))) {
const Widelands::Map& map = game().map();
- const Vector2f field_position = MapviewPixelFunctions::to_map_pixel(map, bob->get_position());
+ const Vector2f field_position = MapviewPixelFunctions::to_map_pixel(map, bob->get_position());
const Vector2f pos = bob->calc_drawpos(game(), field_position, 1.f);
// Drop the tracking if it leaves our vision range
@@ -256,6 +210,13 @@
}
/**
+ * Track the specified bob.
+ */
+void WatchWindow::follow(Widelands::Bob* bob) {
+ views_[cur_index_].tracking = bob;
+}
+
+/**
* Called when the user clicks the "follow" button.
*
* If we are currently tracking a bob, stop tracking.
@@ -299,14 +260,14 @@
closest_dist = dist;
}
}
- views_[cur_index_].tracking = closest;
+ follow(closest);
}
}
/**
* Called when the "go to" button is clicked.
*
- * Cause the main map_view_ to jump to our current position.
+ * Cause the main mapview_ to jump to our current position.
*/
void WatchWindow::do_goto() {
warp_mainview(map_view_.view_area().rect().center());
@@ -344,14 +305,16 @@
Open a watch window.
===============
*/
-void show_watch_window(InteractiveGameBase& parent, const Widelands::Coords& coords) {
+WatchWindow* show_watch_window(InteractiveGameBase& parent, const Widelands::Coords& coords) {
if (g_options.pull_section("global").get_bool("single_watchwin", false)) {
if (!g_watch_window) {
g_watch_window = new WatchWindow(parent, 250, 150, 200, 200, true);
}
g_watch_window->add_view(coords);
+ return g_watch_window;
} else {
auto* window = new WatchWindow(parent, 250, 150, 200, 200, false);
window->add_view(coords);
+ return window;
}
}
=== modified file 'src/wui/watchwindow.h'
--- src/wui/watchwindow.h 2016-08-04 15:49:05 +0000
+++ src/wui/watchwindow.h 2018-04-16 08:14:47 +0000
@@ -22,8 +22,62 @@
#include "logic/widelands_geometry.h"
+#include "ui_basic/button.h"
+#include "ui_basic/window.h"
+#include "wui/mapview.h"
+
class InteractiveGameBase;
-
-void show_watch_window(InteractiveGameBase&, const Widelands::Coords&);
+namespace Widelands {
+class Game;
+}
+
+struct WatchWindow : public UI::Window {
+ WatchWindow(InteractiveGameBase& parent,
+ int32_t x,
+ int32_t y,
+ uint32_t w,
+ uint32_t h,
+ bool single_window_ = false);
+ ~WatchWindow();
+
+ boost::signals2::signal<void(Vector2f)> warp_mainview;
+
+ void add_view(Widelands::Coords);
+ void follow(Widelands::Bob* bob);
+
+private:
+ static constexpr size_t kViews = 5;
+
+ // Holds information for a view
+ struct View {
+ MapView::View view;
+ Widelands::ObjectPointer tracking; // if non-null, we're tracking a Bob
+ };
+
+ Widelands::Game& game() const;
+
+ void think() override;
+ void stop_tracking_by_drag();
+ void draw(RenderTarget&) override;
+ void save_coords();
+ void next_view();
+ void close_cur_view();
+ void toggle_buttons();
+
+ void do_follow();
+ void do_goto();
+ void view_button_clicked(uint8_t index);
+ void set_current_view(uint8_t idx, bool save_previous = true);
+
+ InteractiveGameBase& parent_;
+ MapView map_view_;
+ uint32_t last_visit_;
+ bool single_window_;
+ uint8_t cur_index_;
+ UI::Button* view_btns_[kViews];
+ std::vector<WatchWindow::View> views_;
+};
+
+WatchWindow* show_watch_window(InteractiveGameBase&, const Widelands::Coords&);
#endif // end of include guard: WL_WUI_WATCHWINDOW_H
Follow ups
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: kaputtnik, 2018-05-01
-
[Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: noreply, 2018-04-29
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: GunChleoc, 2018-04-29
-
[Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: bunnybot, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: GunChleoc, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: Notabilis, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: GunChleoc, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: Notabilis, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: GunChleoc, 2018-04-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: Notabilis, 2018-04-25
-
[Merge] lp:~widelands-dev/widelands/bug-1358880-ship-statistics into lp:widelands
From: bunnybot, 2018-04-16