Merge lp:~widelands-dev/widelands/ship_scheduling_2 into lp:widelands

Proposed by ypopezios
Status: Superseded
Proposed branch: lp:~widelands-dev/widelands/ship_scheduling_2
Merge into: lp:widelands
Diff against target: 1132 lines (+417/-335)
8 files modified
src/economy/fleet.cc (+195/-219)
src/economy/fleet.h (+6/-1)
src/economy/portdock.cc (+125/-71)
src/economy/portdock.h (+9/-7)
src/economy/shippingitem.cc (+1/-1)
src/economy/shippingitem.h (+1/-1)
src/logic/map_objects/tribes/ship.cc (+74/-32)
src/logic/map_objects/tribes/ship.h (+6/-3)
To merge this branch: bzr merge lp:~widelands-dev/widelands/ship_scheduling_2
Reviewer Review Type Date Requested Status
GunChleoc Needs Fixing
Review via email: mp+354019@code.launchpad.net

This proposal supersedes a proposal from 2018-08-04.

This proposal has been superseded by a proposal from 2018-09-21.

Commit message

Improved ware/worker routing algorithm for ships and portdocks.

Description of the change

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote : Posted in a previous version of this proposal

Continuous integration builds have changed state:

Travis build 3754. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/412020649.
Appveyor build 3554. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ship_scheduling_2-3554.

Revision history for this message
bunnybot (widelandsofficial) wrote : Posted in a previous version of this proposal

Continuous integration builds have changed state:

Travis build 3758. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/412187264.
Appveyor build 3558. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ship_scheduling_2-3558.

Revision history for this message
bunnybot (widelandsofficial) wrote : Posted in a previous version of this proposal

Continuous integration builds have changed state:

Travis build 3855. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/422224908.
Appveyor build 3653. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ship_scheduling_2-3653.

Revision history for this message
ypopezios (ypopezios) wrote :

AppVeyor seems broken. Otherwise, this branch is now mature and big enough. Not going to add anything more into it, so as to keep it easier for reviewers. If people test it, it can make it into build 20.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

Reason for appveyor not working anymore currently is that the glbinding package has been upgraded in the repo of MSYS from 2.1.4 to 3.0.2.1 which seems to have issues with our code now.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have pushed a commit with a code review and some small tweaks. Please have a look at the NOCOM comments.

Not tested yet.

Revision history for this message
ypopezios (ypopezios) wrote :

Added inline comments concerning the NOCOM comments.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3961. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/428035209.
Appveyor build 3759. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_ship_scheduling_2-3759.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I had actually programmed a really nice endless loop with those iterators... all fixed now. I have also improved the savegame compatibility, so it's only calculated once during game loading.

Thanks for this great feature! :)

@bunnybot merge

review: Approve
Revision history for this message
GunChleoc (gunchleoc) wrote :

One of the tests in our test suite has an endless loop in it:

test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_worker_in_transit.lua

This already happened with older versions of this branch, so I don't think that it's anything I did.

review: Needs Fixing
Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 3977. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/428723984.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/economy/fleet.cc'
--- src/economy/fleet.cc 2018-09-05 06:42:21 +0000
+++ src/economy/fleet.cc 2018-09-21 19:37:16 +0000
@@ -300,7 +300,7 @@
300 *300 *
301 * @return true if successful, or false if the docks are not actually part of the fleet.301 * @return true if successful, or false if the docks are not actually part of the fleet.
302 */302 */
303bool Fleet::get_path(PortDock& start, PortDock& end, Path& path) {303bool Fleet::get_path(const PortDock& start, const PortDock& end, Path& path) {
304 uint32_t startidx = std::find(ports_.begin(), ports_.end(), &start) - ports_.begin();304 uint32_t startidx = std::find(ports_.begin(), ports_.end(), &start) - ports_.begin();
305 uint32_t endidx = std::find(ports_.begin(), ports_.end(), &end) - ports_.begin();305 uint32_t endidx = std::find(ports_.begin(), ports_.end(), &end) - ports_.begin();
306306
@@ -628,8 +628,7 @@
628}628}
629629
630/**630/**
631 * Act callback updates ship scheduling. All decisions about where transport ships631 * Act callback updates ship scheduling of idle ships.
632 * are supposed to go are made via this function.
633 *632 *
634 * @note Do not call this directly; instead, trigger it via @ref update633 * @note Do not call this directly; instead, trigger it via @ref update
635 */634 */
@@ -637,230 +636,207 @@
637 act_pending_ = false;636 act_pending_ = false;
638637
639 if (!active()) {638 if (!active()) {
640 // If we are here, most likely act() was called by a port with waiting wares or an expedition639 // If we are here, most likely act() was called by a port with waiting wares or
641 // ready640 // with an expedition ready, although there are still no ships.
642 // although there are still no ships. We can't handle it now, so we reschedule the act()641 // We can't handle it now, so we reschedule the act()
643 schedule_act(game, 5000); // retry in the next time642 schedule_act(game, kFleetInterval); // retry in the next time
644 act_pending_ = true;643 act_pending_ = true;
645 return;644 return;
646 }645 }
647646
648 molog("Fleet::act\n");647 molog("Fleet::act\n");
649648
650 // we need to calculate what ship is to be send to which port649 // For each waiting port, try to find idle ships and send to it the closest one.
651 // for this we will have temporary data structure with format650 uint16_t waiting_ports = ports_.size();
652 // <<ship,port>,score>651 for (PortDock* p : ports_) {
653 // where ship and port are not objects but positions in ports_ and ships_652 if (p->get_need_ship() == 0) {
654 // this is to allow native hashing653 --waiting_ports;
655 std::map<std::pair<uint16_t, uint16_t>, uint16_t> scores;654 continue;
656655 }
657 // so we will identify all pairs: idle ship : ports, and score all such656
658 // pairs. We consider657 Ship* closest_ship = nullptr;
659 // - count of wares onboard, first ware (oldest) is counted as 8 (prioritization)658 uint32_t shortest_dist = kRouteNotCalculated;
660 // (counting wares for particular port only)659 bool waiting = true;
661 // - count wares waiting at the port/3660
662 // - distance between ship and a port (0-10 points, the closer the more points)661 for (Ship* s : ships_) {
663 // - is another ship heading there right now?662 if (s->get_destination(game)) {
664663 if (s->get_destination(game) == p) {
665 // at the end we must know if requrests of all ports asking for ship were addressed664 waiting = false;
666 // if any unsatisfied, we must schedule new run of this function665 --waiting_ports;
667 // when we send a ship there, the port is removed from list666 break;
668 std::list<uint16_t> waiting_ports;667 }
669668 continue; // The ship already has a destination
670 // this is just helper - first member of scores map669 }
671 std::pair<uint16_t, uint16_t> mapping; // ship number, port number670 if (s->get_ship_state() != Ship::ShipStates::kTransport) {
672671 continue; // Ship is not available, e.g. in expedition
673 // first we go over ships - idle ones (=without destination)672 }
674 // then over wares on these ships and create first ship-port673
675 // pairs with score674 // Here we get distance ship->port
676 for (uint16_t s = 0; s < ships_.size(); s += 1) {675 uint32_t route_length = kRouteNotCalculated;
677 if (ships_[s]->get_destination(game)) {676
678 continue;677 // Get precalculated distance for ships available at ports
679 }678 {
680 if (ships_[s]->get_ship_state() != Ship::ShipStates::kTransport) {679 PortDock* cur_port = get_dock(game, s->get_position());
681 continue; // in expedition obviously680 if (cur_port) { // Ship is at a port
682 }681 if (cur_port == p) { // Same port
683682 route_length = 0;
684 for (uint16_t i = 0; i < ships_[s]->get_nritems(); i += 1) {683 } else { // Different port
685 PortDock* dst = ships_[s]->items_[i].get_destination(game);684 Path precalculated_path;
686 if (!dst) {685 if (get_path(*cur_port, *p, precalculated_path)) {
687 // if wares without destination on ship without destination686 route_length = precalculated_path.get_nsteps();
688 // such ship can be send to any port, and should be sent687 }
689 // to some port, so we add 1 point to score for each port688 }
690 for (uint16_t p = 0; p < ports_.size(); p += 1) {689 }
691 mapping.first = s;690 }
692 mapping.second = p;691
693 scores[mapping] += 1;692 // Get distance for ships available but not at a port (should not happen frequently)
694 }693 if (route_length == kRouteNotCalculated) {
695 continue;694 route_length = s->calculate_sea_route(game, *p);
696 }695 }
697696
698 bool destination_found = false; // Just a functional check697 if (route_length < shortest_dist) {
699 for (uint16_t p = 0; p < ports_.size(); p += 1) {698 shortest_dist = route_length;
700 if (ports_[p] == ships_[s]->items_[i].get_destination(game)) {699 closest_ship = s;
701 mapping.first = s;700 }
702 mapping.second = p;701 }
703 scores[mapping] += (i == 0) ? 8 : 1;702
704 destination_found = true;703 if (waiting && closest_ship) {
705 }704 --waiting_ports;
706 }705 closest_ship->set_destination(p);
707 if (!destination_found) {706 closest_ship->send_signal(game, "wakeup");
708 // Perhaps the throw here is too strong707 }
709 // we can still remove it before stable release if it proves too much708 }
710 // during my testing this situation never happened709
711 throw wexception("A ware with destination that does not match any of player's"710 if (waiting_ports > 0) {
712 " ports, ship %u, ware's destination: %u",711 molog("... there are %u ports requesting ship(s) we cannot satisfy yet\n", waiting_ports);
713 ships_[s]->serial(),712 schedule_act(game, kFleetInterval); // retry next time
714 ships_[s]->items_[i].get_destination(game)->serial());
715 }
716 }
717 }
718
719 // now opposite aproach - we go over ports to find out those that have wares
720 // waiting for ship then find candidate ships to satisfy the requests
721 for (uint16_t p = 0; p < ports_.size(); p += 1) {
722 PortDock& pd = *ports_[p];
723 if (!pd.get_need_ship()) {
724 continue;
725 }
726
727 // general stategy is "one ship for port is enough", but sometimes
728 // amount of ware waiting for ship is too high
729 if (count_ships_heading_here(game, &pd) * 25 > pd.count_waiting()) {
730 continue;
731 }
732
733 waiting_ports.push_back(p);
734
735 // scoring and entering the pair into scores (or increasing existing
736 // score if the pair is already there)
737 for (uint16_t s = 0; s < ships_.size(); s += 1) {
738
739 if (ships_[s]->get_destination(game)) {
740 continue; // already has destination
741 }
742
743 if (ships_[s]->get_ship_state() != Ship::ShipStates::kTransport) {
744 continue; // in expedition obviously
745 }
746
747 mapping.first = s;
748 mapping.second = p;
749 // following aproximately considers free capacity of a ship
750 scores[mapping] += ((ships_[s]->get_nritems() > 15) ? 1 : 3) +
751 std::min(ships_[s]->descr().get_capacity() - ships_[s]->get_nritems(),
752 ports_[p]->count_waiting()) /
753 3;
754 }
755 }
756
757 // Now adding score for distance
758 for (auto ship_port_relation : scores) {
759
760 // here we get distance ship->port
761 // possibilities are:
762 // - we are in port and it is the same as target port
763 // - we are in other port, then we use get_dock() function to fetch precalculated path
764 // - if above fails, we calculate path "manually"
765 int16_t route_length = -1;
766
767 PortDock* current_portdock =
768 get_dock(game, ships_[ship_port_relation.first.first]->get_position());
769
770 if (current_portdock) { // we try to use precalculated paths of game
771
772 // we are in the same portdock
773 if (current_portdock == ports_[ship_port_relation.first.second]) {
774 route_length = 0;
775 } else { // it is different portdock then
776 Path tmp_path;
777 if (get_path(*current_portdock, *ports_[ship_port_relation.first.second], tmp_path)) {
778 route_length = tmp_path.get_nsteps();
779 }
780 }
781 }
782
783 // most probably the ship is not in a portdock (should not happen frequently)
784 if (route_length == -1) {
785 route_length = ships_[ship_port_relation.first.first]->calculate_sea_route(
786 game, *ports_[ship_port_relation.first.second]);
787 }
788
789 // now we have length of route, so we need to calculate score
790 int16_t score_for_distance = 0;
791 if (route_length < 3) {
792 score_for_distance = 10;
793 } else {
794 score_for_distance = 8 - route_length / 50;
795 }
796 // must not be negative
797 score_for_distance = (score_for_distance < 0) ? 0 : score_for_distance;
798
799 scores[ship_port_relation.first] += score_for_distance;
800 }
801
802 // looking for best scores and sending ships accordingly
803 uint16_t best_ship = 0;
804 uint16_t best_port = 0;
805
806 // after sending a ship we will remove one or more items from scores
807 while (!scores.empty()) {
808 uint16_t best_score = 0;
809
810 // searching for combination with highest score
811 for (const auto& combination : scores) {
812 if (combination.second > best_score) {
813 best_score = combination.second;
814 best_ship = combination.first.first;
815 best_port = combination.first.second;
816 }
817 }
818 if (best_score == 0) {
819 // this is check of correctnes of this algorithm, this should not happen
820 throw wexception("Fleet::act(): No port-destination pair selected or its score is zero");
821 }
822
823 // making sure the winner has no destination set
824 assert(!ships_[best_ship]->get_destination(game));
825
826 // now actual setting destination for "best ship"
827 ships_[best_ship]->set_destination(game, *ports_[best_port]);
828 molog("... ship %u sent to port %u, wares onboard: %2d, the port is asking for a ship: %s\n",
829 ships_[best_ship]->serial(), ports_[best_port]->serial(),
830 ships_[best_ship]->get_nritems(), (ports_[best_port]->get_need_ship()) ? "yes" : "no");
831
832 // pruning the scores table
833 // the ship that was just sent somewhere cannot be send elsewhere :)
834 for (auto it = scores.cbegin(); it != scores.cend();) {
835
836 // decreasing score for target port as there was a ship just sent there
837 if (it->first.second == best_port) {
838 mapping.first = it->first.first;
839 mapping.second = it->first.second;
840 scores[mapping] /= 2;
841 // just make sure it is nonzero
842 scores[mapping] = (scores[mapping] == 0) ? 1 : scores[mapping];
843 }
844
845 // but removing all pairs where best ship is participating as it is not available anymore
846 // (because it was sent to "best port")
847 if (it->first.first == best_ship) {
848 scores.erase(it++);
849 } else {
850 ++it;
851 }
852 }
853
854 // also removing the port from waiting_ports
855 waiting_ports.remove(best_port);
856 }
857
858 if (!waiting_ports.empty()) {
859 molog("... there are %" PRIuS " ports requesting ship(s) we cannot satisfy yet\n",
860 waiting_ports.size());
861 schedule_act(game, 5000); // retry next time
862 act_pending_ = true;713 act_pending_ = true;
863 }714 }
715
716 // Deal with edge-case of losing destination before reaching it
717 for (Ship* s : ships_) {
718 if (s->get_destination(game)) {
719 continue; // The ship has a destination
720 }
721 if (s->get_ship_state() != Ship::ShipStates::kTransport) {
722 continue; // Ship is not available, e.g. in expedition
723 }
724 if (s->items_.empty()) {
725 continue; // No pending wares/workers
726 }
727
728 // Send ship to the closest port
729 PortDock* closest_port = nullptr;
730 uint32_t shortest_dist = kRouteNotCalculated;
731
732 for (PortDock* p : ports_) {
733 uint32_t route_length = s->calculate_sea_route(game, *p);
734 if (route_length < shortest_dist) {
735 shortest_dist = route_length;
736 closest_port = p;
737 }
738 }
739
740 if (closest_port) {
741 s->set_destination(closest_port);
742 s->send_signal(game, "wakeup");
743 }
744 }
745}
746
747/**
748 * For the given three consecutive ports, decide if their path is favourable or not.
749 * \return true if the path from start to finish >= the path from middle to finish
750 */
751bool Fleet::is_path_favourable(const PortDock& start, const PortDock& middle, const PortDock& finish) {
752 if (&middle != &finish) {
753 Path path_start_to_finish;
754 Path path_middle_to_finish;
755#ifndef NDEBUG
756 assert(get_path(start, finish, path_start_to_finish));
757#else
758 get_path(start, finish, path_start_to_finish);
759#endif
760 if (get_path(middle, finish, path_middle_to_finish)) {
761 if (path_middle_to_finish.get_nsteps() > path_start_to_finish.get_nsteps()) {
762 return false;
763 }
764 }
765 }
766 return true; // default
767}
768
769/**
770 * For the given ship, go through all ports of this fleet
771 * and find the one with the best score.
772 * \return that port
773 */
774PortDock* Fleet::find_next_dest(Game& game, const Ship& ship, const PortDock& from_port) {
775 PortDock* best_port = nullptr;
776 float best_score = 0.0f;
777
778 for (PortDock* p : ports_) {
779 if (p == &from_port) {
780 continue; // same port
781 }
782
783 float score = 0.0f;
784 WareInstance* ware;
785 Worker* worker;
786
787 // Score for wares/workers onboard that ship for that port
788 for (const ShippingItem& si : ship.items_) {
789 if (si.get_destination(game) == p) {
790 si.get(game, &ware, &worker);
791 if (ware) {
792 score += 1; // TODO(ypopezios): increase by ware's importance
793 } else { // worker
794 score += 4;
795 }
796 }
797 }
798
799 // Score for wares/workers waiting at that port
800 for (const ShippingItem& si : from_port.waiting_) {
801 if (si.get_destination(game) == p) {
802 si.get(game, &ware, &worker);
803 if (ware) {
804 score += 1; // TODO(ypopezios): increase by ware's importance
805 } else { // worker
806 score += 4;
807 }
808 }
809 }
810
811 if (score == 0.0f && p->get_need_ship() == 0) {
812 continue; // empty ship to empty port
813 }
814
815 // Here we get distance ship->port
816 uint32_t route_length = kRouteNotCalculated;
817
818 // Get precalculated distance if the ship is at a port
819 {
820 Path precalculated_path;
821 if (get_path(from_port, *p, precalculated_path)) { // try to use precalculated path
822 route_length = precalculated_path.get_nsteps();
823 }
824 }
825
826 // Get distance for when the ship is not at a port (should not happen frequently)
827 if (route_length == kRouteNotCalculated) {
828 route_length = ship.calculate_sea_route(game, *p);
829 }
830
831 score = (score + 1.0f) * (score + p->get_need_ship());
832 score = score * (1.0f - route_length / (score + route_length));
833 if (score > best_score) {
834 best_score = score;
835 best_port = p;
836 }
837 }
838
839 return best_port;
864}840}
865841
866void Fleet::log_general_info(const EditorGameBase& egbase) const {842void Fleet::log_general_info(const EditorGameBase& egbase) const {
867843
=== modified file 'src/economy/fleet.h'
--- src/economy/fleet.h 2018-09-04 15:48:47 +0000
+++ src/economy/fleet.h 2018-09-21 19:37:16 +0000
@@ -46,6 +46,9 @@
46 DISALLOW_COPY_AND_ASSIGN(FleetDescr);46 DISALLOW_COPY_AND_ASSIGN(FleetDescr);
47};47};
4848
49constexpr int32_t kFleetInterval = 5000;
50constexpr uint32_t kRouteNotCalculated = std::numeric_limits<uint32_t>::max();
51
49/**52/**
50 * Manage all ships and ports of a player that are connected53 * Manage all ships and ports of a player that are connected
51 * by ocean.54 * by ocean.
@@ -96,7 +99,7 @@
9699
97 void log_general_info(const EditorGameBase&) const override;100 void log_general_info(const EditorGameBase&) const override;
98101
99 bool get_path(PortDock& start, PortDock& end, Path& path);102 bool get_path(const PortDock& start, const PortDock& end, Path& path);
100 void add_neighbours(PortDock& pd, std::vector<RoutingNodeNeighbour>& neighbours);103 void add_neighbours(PortDock& pd, std::vector<RoutingNodeNeighbour>& neighbours);
101104
102 uint32_t count_ships() const;105 uint32_t count_ships() const;
@@ -152,6 +155,8 @@
152 void save(EditorGameBase&, MapObjectSaver&, FileWrite&) override;155 void save(EditorGameBase&, MapObjectSaver&, FileWrite&) override;
153156
154 static MapObject::Loader* load(EditorGameBase&, MapObjectLoader&, FileRead&);157 static MapObject::Loader* load(EditorGameBase&, MapObjectLoader&, FileRead&);
158 bool is_path_favourable(const PortDock& start, const PortDock& middle, const PortDock& finish);
159 PortDock* find_next_dest(Game&, const Ship&, const PortDock& from_port);
155};160};
156161
157} // namespace Widelands162} // namespace Widelands
158163
=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc 2018-09-04 15:48:47 +0000
+++ src/economy/portdock.cc 2018-09-21 19:37:16 +0000
@@ -34,6 +34,7 @@
34#include "logic/game_data_error.h"34#include "logic/game_data_error.h"
35#include "logic/map_objects/tribes/ship.h"35#include "logic/map_objects/tribes/ship.h"
36#include "logic/map_objects/tribes/warehouse.h"36#include "logic/map_objects/tribes/warehouse.h"
37#include "logic/path.h"
37#include "logic/player.h"38#include "logic/player.h"
38#include "logic/widelands_geometry_io.h"39#include "logic/widelands_geometry_io.h"
39#include "map_io/map_object_loader.h"40#include "map_io/map_object_loader.h"
@@ -55,7 +56,7 @@
55 : PlayerImmovable(g_portdock_descr),56 : PlayerImmovable(g_portdock_descr),
56 fleet_(nullptr),57 fleet_(nullptr),
57 warehouse_(wh),58 warehouse_(wh),
58 need_ship_(false),59 ships_coming_(0),
59 expedition_ready_(false) {60 expedition_ready_(false) {
60}61}
6162
@@ -116,6 +117,10 @@
116 return nullptr;117 return nullptr;
117}118}
118119
120uint32_t PortDock::get_need_ship() const {
121 return (waiting_.size() + (expedition_ready_ ? 20 : 0)) / (ships_coming_ + 1);
122}
123
119/**124/**
120 * Signal to the dock that it now belongs to the given economy.125 * Signal to the dock that it now belongs to the given economy.
121 *126 *
@@ -247,7 +252,7 @@
247 * its route.252 * its route.
248 */253 */
249void PortDock::update_shippingitem(Game& game, WareInstance& ware) {254void PortDock::update_shippingitem(Game& game, WareInstance& ware) {
250 for (std::vector<ShippingItem>::iterator item_iter = waiting_.begin();255 for (auto item_iter = waiting_.begin();
251 item_iter != waiting_.end(); ++item_iter) {256 item_iter != waiting_.end(); ++item_iter) {
252257
253 if (item_iter->object_.serial() == ware.serial()) {258 if (item_iter->object_.serial() == ware.serial()) {
@@ -271,7 +276,7 @@
271 * updated its route.276 * updated its route.
272 */277 */
273void PortDock::update_shippingitem(Game& game, Worker& worker) {278void PortDock::update_shippingitem(Game& game, Worker& worker) {
274 for (std::vector<ShippingItem>::iterator item_iter = waiting_.begin();279 for (auto item_iter = waiting_.begin();
275 item_iter != waiting_.end(); ++item_iter) {280 item_iter != waiting_.end(); ++item_iter) {
276281
277 if (item_iter->object_.serial() == worker.serial()) {282 if (item_iter->object_.serial() == worker.serial()) {
@@ -281,44 +286,66 @@
281 }286 }
282}287}
283288
284void PortDock::update_shippingitem(Game& game, std::vector<ShippingItem>::iterator it) {289void PortDock::update_shippingitem(Game& game, std::list<ShippingItem>::iterator it) {
285 it->update_destination(game, *this);290 it->update_destination(game, *this);
286291
287 PortDock* dst = it->get_destination(game);292 const PortDock* dst = it->get_destination(game);
288 assert(dst != this);293 assert(dst != this);
289294
290 // Destination might have vanished or be in another economy altogether.295 // Destination might have vanished or be in another economy altogether.
291 if (dst && dst->get_economy() == get_economy()) {296 if (dst && dst->get_economy() == get_economy()) {
292 set_need_ship(game, true);297 if (ships_coming_ <= 0) {
298 set_need_ship(game, true);
299 }
293 } else {300 } else {
294 it->set_location(game, warehouse_);301 it->set_location(game, warehouse_);
295 it->end_shipping(game);302 it->end_shipping(game);
296 *it = waiting_.back();303 *it = waiting_.back();
297 waiting_.pop_back();304 waiting_.pop_back();
298305 }
299 if (waiting_.empty())306}
300 set_need_ship(game, false);307
301 }308/**
302}309 * Receive shipping item from unloading ship.
303310 * Called by ship code.
304/**311 */
305 * A ship has arrived at the dock. Clear all items designated for this dock,312void PortDock::shipping_item_arrived(Game& game, ShippingItem& si) {
306 * and load the ship.313 si.set_location(game, warehouse_);
314 si.end_shipping(game);
315}
316
317/**
318 * Receive shipping item from departing ship.
319 * Called by ship code.
320 */
321void PortDock::shipping_item_returned(Game& game, ShippingItem& si) {
322 si.set_location(game, this);
323 waiting_.push_back(si);
324}
325
326/**
327 * A ship changed destination and is now coming to the dock. Increase counter for need_ship.
328 */
329void PortDock::ship_coming(bool affirmative) {
330 if (affirmative) {
331 ++ships_coming_;
332 } else {
333 --ships_coming_;
334 }
335}
336
337/**
338 * A ship has arrived at the dock. Set its next destination and load it accordingly.
307 */339 */
308void PortDock::ship_arrived(Game& game, Ship& ship) {340void PortDock::ship_arrived(Game& game, Ship& ship) {
309 std::vector<ShippingItem> items_brought_by_ship;341 ship_coming(false);
310 ship.withdraw_items(game, *this, items_brought_by_ship);
311
312 for (ShippingItem& shipping_item : items_brought_by_ship) {
313 shipping_item.set_location(game, warehouse_);
314 shipping_item.end_shipping(game);
315 }
316342
317 if (expedition_ready_) {343 if (expedition_ready_) {
318 assert(expedition_bootstrap_ != nullptr);344 assert(expedition_bootstrap_ != nullptr);
319345
320 // Only use an empty ship.346 // Only use an empty ship.
321 if (ship.get_nritems() < 1) {347 if (ship.get_nritems() < 1) {
348 ship.set_destination(nullptr);
322 // Load the ship349 // Load the ship
323 std::vector<Worker*> workers;350 std::vector<Worker*> workers;
324 std::vector<WareInstance*> wares;351 std::vector<WareInstance*> wares;
@@ -337,46 +364,61 @@
337 // The expedition goods are now on the ship, so from now on it is independent from the port364 // The expedition goods are now on the ship, so from now on it is independent from the port
338 // and thus we switch the port to normal, so we could even start a new expedition,365 // and thus we switch the port to normal, so we could even start a new expedition,
339 cancel_expedition(game);366 cancel_expedition(game);
340 return fleet_->update(game);367 fleet_->update(game);
341 }368 return;
342 }369 }
343370 }
344 if (ship.get_nritems() < ship.descr().get_capacity() && !waiting_.empty()) {371
345 uint32_t nrload =372 // Decide where the arrived ship will go next
346 std::min<uint32_t>(waiting_.size(), ship.descr().get_capacity() - ship.get_nritems());373 PortDock* next_port = fleet_->find_next_dest(game, ship, *this);
347374 if (!next_port) {
348 while (nrload--) {375 ship.set_destination(next_port);
349 // Check if the item has still a valid destination376 return; // no need to load anything
350 if (waiting_.back().get_destination(game)) {377 }
351 // Destination is valid, so we load the item onto the ship378
352 ship.add_item(game, waiting_.back());379 // Unload any wares/workers onboard the departing ship which are not favored by next dest
353 } else {380 ship.unload_unfit_items(game, *this, *next_port);
354 // The item has no valid destination anymore, so we just carry it381
355 // back in the warehouse382 // Then load the remaining capacity of the departing ship with relevant items
356 waiting_.back().set_location(game, warehouse_);383 uint32_t remaining_capacity = ship.descr().get_capacity() - ship.get_nritems();
357 waiting_.back().end_shipping(game);384
358 }385 // Firstly load the items which go to chosen destination, while also checking for items with invalid destination
359 waiting_.pop_back();386 for (auto si_it = waiting_.begin(); si_it != waiting_.end(); ++si_it) {
360 }387 const PortDock* itemdest = si_it->get_destination(game);
361388 if (itemdest) {
362 if (waiting_.empty()) {389 // Valid destination. Only load the item if it matches the ship's destination and the ship still has room for it
363 set_need_ship(game, false);390 if (remaining_capacity > 0 && itemdest == next_port) {
364 }391 ship.add_item(game, *si_it); // load it
365 }392 si_it = waiting_.erase(si_it);
366393 --remaining_capacity;
367 fleet_->update(game);394 }
395 } else {
396 // Invalid destination. Carry the item back into the warehouse
397 si_it->set_location(game, warehouse_);
398 si_it->end_shipping(game);
399 si_it = waiting_.erase(si_it);
400 }
401 }
402
403 if (remaining_capacity > 0) {
404 // There is still come capacity left. Load any items favored by the chosen destination on the ship.
405 for (auto si_it = waiting_.begin(); si_it != waiting_.end() && remaining_capacity > 0; ++si_it) {
406 assert(si_it->get_destination(game) != nullptr);
407 if (fleet_->is_path_favourable(*this, *next_port, *si_it->get_destination(game))) {
408 ship.add_item(game, *si_it);
409 si_it = waiting_.erase(si_it);
410 --remaining_capacity;
411 }
412 }
413 }
414
415 ship.set_destination(next_port);
416 set_need_ship(game, !waiting_.empty());
368}417}
369418
370void PortDock::set_need_ship(Game& game, bool need) {419void PortDock::set_need_ship(Game& game, bool need) {
371 molog("set_need_ship(%s)\n", need ? "true" : "false");420 if (need && fleet_) {
372421 molog("trigger fleet update\n");
373 if (need == need_ship_)
374 return;
375
376 need_ship_ = need;
377
378 if (fleet_) {
379 molog("... trigger fleet update\n");
380 fleet_->update(game);422 fleet_->update(game);
381 }423 }
382}424}
@@ -445,13 +487,13 @@
445487
446 if (warehouse_) {488 if (warehouse_) {
447 Coords pos(warehouse_->get_position());489 Coords pos(warehouse_->get_position());
448 molog("PortDock for warehouse %u (at %i,%i) in fleet %u, need_ship: %s, waiting: %" PRIuS490 molog("PortDock for warehouse %u (at %i,%i) in fleet %u, expedition_ready: %s, waiting: %" PRIuS
449 "\n",491 "\n",
450 warehouse_->serial(), pos.x, pos.y, fleet_ ? fleet_->serial() : 0,492 warehouse_->serial(), pos.x, pos.y, fleet_ ? fleet_->serial() : 0,
451 need_ship_ ? "true" : "false", waiting_.size());493 expedition_ready_ ? "true" : "false", waiting_.size());
452 } else {494 } else {
453 molog("PortDock without a warehouse in fleet %u, need_ship: %s, waiting: %" PRIuS "\n",495 molog("PortDock without a warehouse in fleet %u, expedition_ready: %s, waiting: %" PRIuS "\n",
454 fleet_ ? fleet_->serial() : 0, need_ship_ ? "true" : "false", waiting_.size());496 fleet_ ? fleet_->serial() : 0, expedition_ready_ ? "true" : "false", waiting_.size());
455 }497 }
456498
457 for (const ShippingItem& shipping_item : waiting_) {499 for (const ShippingItem& shipping_item : waiting_) {
@@ -460,12 +502,12 @@
460 }502 }
461}503}
462504
463constexpr uint8_t kCurrentPacketVersion = 3;505constexpr uint8_t kCurrentPacketVersion = 4;
464506
465PortDock::Loader::Loader() : warehouse_(0) {507PortDock::Loader::Loader() : warehouse_(0) {
466}508}
467509
468void PortDock::Loader::load(FileRead& fr) {510void PortDock::Loader::load(FileRead& fr, uint8_t packet_version) {
469 PlayerImmovable::Loader::load(fr);511 PlayerImmovable::Loader::load(fr);
470512
471 PortDock& pd = get<PortDock>();513 PortDock& pd = get<PortDock>();
@@ -479,7 +521,18 @@
479 pd.set_position(egbase(), pd.dockpoints_[i]);521 pd.set_position(egbase(), pd.dockpoints_[i]);
480 }522 }
481523
482 pd.need_ship_ = fr.unsigned_8();524 pd.ships_coming_ = fr.unsigned_8();
525
526 // TODO(GunChleoc): Savegame compatibility Build 20
527 if (packet_version < 4) {
528 pd.ships_coming_ = 0;
529 for (const Serial ship_serial : pd.owner().ships()) {
530 Ship* ship = dynamic_cast<Ship*>(egbase().objects().get_object(ship_serial));
531 if (ship->get_destination(egbase())->serial() == pd.serial()) {
532 ++pd.ships_coming_;
533 }
534 }
535 }
483536
484 waiting_.resize(fr.unsigned_32());537 waiting_.resize(fr.unsigned_32());
485 for (ShippingItem::Loader& shipping_loader : waiting_) {538 for (ShippingItem::Loader& shipping_loader : waiting_) {
@@ -499,10 +552,10 @@
499 PortDock& pd = get<PortDock>();552 PortDock& pd = get<PortDock>();
500 pd.warehouse_ = &mol().get<Warehouse>(warehouse_);553 pd.warehouse_ = &mol().get<Warehouse>(warehouse_);
501554
502 pd.waiting_.resize(waiting_.size());
503 for (uint32_t i = 0; i < waiting_.size(); ++i) {555 for (uint32_t i = 0; i < waiting_.size(); ++i) {
504 pd.waiting_[i] = waiting_[i].get(mol());556 pd.waiting_.push_back(waiting_[i].get(mol()));
505 }557 }
558 assert(pd.waiting_.size() == waiting_.size());
506}559}
507560
508void PortDock::Loader::load_finish() {561void PortDock::Loader::load_finish() {
@@ -527,10 +580,11 @@
527 try {580 try {
528 // The header has been peeled away by the caller581 // The header has been peeled away by the caller
529582
583 // TODO(GunChleoc): Savegame compatibility Build 20
530 uint8_t const packet_version = fr.unsigned_8();584 uint8_t const packet_version = fr.unsigned_8();
531 if (packet_version == kCurrentPacketVersion) {585 if (packet_version >= 3 && packet_version <= kCurrentPacketVersion) {
532 loader->init(egbase, mol, *new PortDock(nullptr));586 loader->init(egbase, mol, *new PortDock(nullptr));
533 loader->load(fr);587 loader->load(fr, packet_version);
534 } else {588 } else {
535 throw UnhandledVersionError("PortDock", packet_version, kCurrentPacketVersion);589 throw UnhandledVersionError("PortDock", packet_version, kCurrentPacketVersion);
536 }590 }
@@ -553,7 +607,7 @@
553 write_coords_32(&fw, coords);607 write_coords_32(&fw, coords);
554 }608 }
555609
556 fw.unsigned_8(need_ship_);610 fw.unsigned_8(ships_coming_);
557611
558 fw.unsigned_32(waiting_.size());612 fw.unsigned_32(waiting_.size());
559 for (ShippingItem& shipping_item : waiting_) {613 for (ShippingItem& shipping_item : waiting_) {
560614
=== modified file 'src/economy/portdock.h'
--- src/economy/portdock.h 2018-09-04 15:48:47 +0000
+++ src/economy/portdock.h 2018-09-21 19:37:16 +0000
@@ -20,6 +20,7 @@
20#ifndef WL_ECONOMY_PORTDOCK_H20#ifndef WL_ECONOMY_PORTDOCK_H
21#define WL_ECONOMY_PORTDOCK_H21#define WL_ECONOMY_PORTDOCK_H
2222
23#include <list>
23#include <memory>24#include <memory>
2425
25#include "base/macros.h"26#include "base/macros.h"
@@ -85,9 +86,7 @@
85 return fleet_;86 return fleet_;
86 }87 }
87 PortDock* get_dock(Flag& flag) const;88 PortDock* get_dock(Flag& flag) const;
88 bool get_need_ship() const {89 uint32_t get_need_ship() const;
89 return need_ship_ || expedition_ready_;
90 }
9190
92 void set_economy(Economy*) override;91 void set_economy(Economy*) override;
9392
@@ -113,6 +112,9 @@
113 void add_shippingitem(Game&, Worker&);112 void add_shippingitem(Game&, Worker&);
114 void update_shippingitem(Game&, Worker&);113 void update_shippingitem(Game&, Worker&);
115114
115 void shipping_item_arrived(Game&, ShippingItem&);
116 void shipping_item_returned(Game&, ShippingItem&);
117 void ship_coming(bool affirmative);
116 void ship_arrived(Game&, Ship&);118 void ship_arrived(Game&, Ship&);
117119
118 void log_general_info(const EditorGameBase&) const override;120 void log_general_info(const EditorGameBase&) const override;
@@ -139,14 +141,14 @@
139141
140 void init_fleet(EditorGameBase& egbase);142 void init_fleet(EditorGameBase& egbase);
141 void set_fleet(Fleet* fleet);143 void set_fleet(Fleet* fleet);
142 void update_shippingitem(Game&, std::vector<ShippingItem>::iterator);144 void update_shippingitem(Game&, std::list<ShippingItem>::iterator);
143 void set_need_ship(Game&, bool need);145 void set_need_ship(Game&, bool need);
144146
145 Fleet* fleet_;147 Fleet* fleet_;
146 Warehouse* warehouse_;148 Warehouse* warehouse_;
147 PositionList dockpoints_;149 PositionList dockpoints_;
148 std::vector<ShippingItem> waiting_;150 std::list<ShippingItem> waiting_;
149 bool need_ship_;151 uint8_t ships_coming_;
150 bool expedition_ready_;152 bool expedition_ready_;
151153
152 std::unique_ptr<ExpeditionBootstrap> expedition_bootstrap_;154 std::unique_ptr<ExpeditionBootstrap> expedition_bootstrap_;
@@ -157,7 +159,7 @@
157 public:159 public:
158 Loader();160 Loader();
159161
160 void load(FileRead&);162 void load(FileRead&, uint8_t packet_version);
161 void load_pointers() override;163 void load_pointers() override;
162 void load_finish() override;164 void load_finish() override;
163165
164166
=== modified file 'src/economy/shippingitem.cc'
--- src/economy/shippingitem.cc 2018-04-07 16:59:00 +0000
+++ src/economy/shippingitem.cc 2018-09-21 19:37:16 +0000
@@ -105,7 +105,7 @@
105 worker->end_shipping(game);105 worker->end_shipping(game);
106}106}
107107
108PortDock* ShippingItem::get_destination(Game& game) {108const PortDock* ShippingItem::get_destination(Game& game) const {
109 return destination_dock_.get(game);109 return destination_dock_.get(game);
110}110}
111111
112112
=== modified file 'src/economy/shippingitem.h'
--- src/economy/shippingitem.h 2018-04-07 16:59:00 +0000
+++ src/economy/shippingitem.h 2018-09-21 19:37:16 +0000
@@ -53,7 +53,7 @@
53 void get(const EditorGameBase& game, WareInstance** ware, Worker** worker) const;53 void get(const EditorGameBase& game, WareInstance** ware, Worker** worker) const;
5454
55 void set_economy(Game&, Economy* e);55 void set_economy(Game&, Economy* e);
56 PortDock* get_destination(Game&);56 const PortDock* get_destination(Game&) const;
5757
58 void remove(EditorGameBase&);58 void remove(EditorGameBase&);
5959
6060
=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc 2018-09-11 16:58:16 +0000
+++ src/logic/map_objects/tribes/ship.cc 2018-09-21 19:37:16 +0000
@@ -303,12 +303,22 @@
303303
304 FCoords position = map.get_fcoords(get_position());304 FCoords position = map.get_fcoords(get_position());
305 if (position.field->get_immovable() == dst) {305 if (position.field->get_immovable() == dst) {
306 molog("ship_update: Arrived at dock %u\n", dst->serial());306 if (lastdock_ != dst) {
307 lastdock_ = dst;307 molog("ship_update: Arrived at dock %u\n", dst->serial());
308 destination_ = nullptr;308 lastdock_ = dst;
309 dst->ship_arrived(game, *this);309 }
310 start_task_idle(game, descr().main_animation(), 250);310 if (withdraw_item(game, *dst)) {
311 Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged));311 schedule_act(game, kShipInterval);
312 return true;
313 }
314
315 dst->ship_arrived(game, *this); // This will also set the destination
316 dst = get_destination(game);
317 if (dst) {
318 start_task_movetodock(game, *dst);
319 } else {
320 start_task_idle(game, descr().main_animation(), 250);
321 }
312 return true;322 return true;
313 }323 }
314324
@@ -484,7 +494,7 @@
484 }494 }
485495
486 if (totalprob == 0) {496 if (totalprob == 0) {
487 start_task_idle(game, descr().main_animation(), 1500);497 start_task_idle(game, descr().main_animation(), kShipInterval);
488 return;498 return;
489 }499 }
490500
@@ -496,13 +506,13 @@
496 }506 }
497507
498 if (dir == 0 || dir > LAST_DIRECTION) {508 if (dir == 0 || dir > LAST_DIRECTION) {
499 start_task_idle(game, descr().main_animation(), 1500);509 start_task_idle(game, descr().main_animation(), kShipInterval);
500 return;510 return;
501 }511 }
502512
503 FCoords neighbour = map.get_neighbour(position, dir);513 FCoords neighbour = map.get_neighbour(position, dir);
504 if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) {514 if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) {
505 start_task_idle(game, descr().main_animation(), 1500);515 start_task_idle(game, descr().main_animation(), kShipInterval);
506 return;516 return;
507 }517 }
508518
@@ -542,7 +552,7 @@
542 pgettext("ship", "Waiting"), _("Island Circumnavigated"),552 pgettext("ship", "Waiting"), _("Island Circumnavigated"),
543 _("An expedition ship sailed around its island without any events."),553 _("An expedition ship sailed around its island without any events."),
544 "images/wui/ship/ship_explore_island_cw.png");554 "images/wui/ship/ship_explore_island_cw.png");
545 return start_task_idle(game, descr().main_animation(), 1500);555 return start_task_idle(game, descr().main_animation(), kShipInterval);
546 }556 }
547 }557 }
548 // The ship is supposed to follow the coast as close as possible, therefore the check558 // The ship is supposed to follow the coast as close as possible, therefore the check
@@ -585,7 +595,7 @@
585 shipname_.c_str());595 shipname_.c_str());
586 set_ship_state_and_notify(596 set_ship_state_and_notify(
587 ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);597 ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);
588 start_task_idle(game, descr().main_animation(), 1500);598 start_task_idle(game, descr().main_animation(), kShipInterval);
589 return;599 return;
590 }600 }
591 } else { // scouting towards a specific direction601 } else { // scouting towards a specific direction
@@ -598,7 +608,7 @@
598 // coast reached608 // coast reached
599 set_ship_state_and_notify(609 set_ship_state_and_notify(
600 ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);610 ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand);
601 start_task_idle(game, descr().main_animation(), 1500);611 start_task_idle(game, descr().main_animation(), kShipInterval);
602 // Send a message to the player, that a new coast was reached612 // Send a message to the player, that a new coast was reached
603 send_message(game,613 send_message(game,
604 /** TRANSLATORS: A ship has discovered land */614 /** TRANSLATORS: A ship has discovered land */
@@ -692,14 +702,14 @@
692 }702 }
693703
694 expedition_.reset(nullptr);704 expedition_.reset(nullptr);
695 return start_task_idle(game, descr().main_animation(), 1500);705 return start_task_idle(game, descr().main_animation(), kShipInterval);
696 }706 }
697 }707 }
698 FALLS_THROUGH;708 FALLS_THROUGH;
699 case ShipStates::kExpeditionWaiting:709 case ShipStates::kExpeditionWaiting:
700 case ShipStates::kExpeditionPortspaceFound: {710 case ShipStates::kExpeditionPortspaceFound: {
701 // wait for input711 // wait for input
702 start_task_idle(game, descr().main_animation(), 1500);712 start_task_idle(game, descr().main_animation(), kShipInterval);
703 return;713 return;
704 }714 }
705 FALLS_THROUGH;715 FALLS_THROUGH;
@@ -729,14 +739,17 @@
729739
730/**740/**
731 * Enter a new destination port for the ship.741 * Enter a new destination port for the ship.
732 *742 * Call this after (un)loading the ship, for proper logging.
733 * @note This is supposed to be called only from the scheduling code of @ref Fleet.
734 */743 */
735void Ship::set_destination(Game& game, PortDock& pd) {744void Ship::set_destination(PortDock* pd) {
736 molog("set_destination / sending to portdock %u (carrying %" PRIuS " items)\n", pd.serial(),745 destination_ = pd;
737 items_.size());746 if (pd) {
738 destination_ = &pd;747 molog("set_destination / sending to portdock %u (carrying %" PRIuS " items)\n", pd->serial(),
739 send_signal(game, "wakeup");748 items_.size());
749 pd->ship_coming(true);
750 } else {
751 molog("set_destination / none\n");
752 }
740 Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged));753 Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged));
741}754}
742755
@@ -747,14 +760,39 @@
747 items_.back().set_location(game, this);760 items_.back().set_location(game, this);
748}761}
749762
750void Ship::withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items) {763/**
751 uint32_t dst = 0;764 * Unload one item designated for given dock or for no dock.
752 for (uint32_t src = 0; src < items_.size(); ++src) {765 * \return true if item unloaded.
753 PortDock* destination = items_[src].get_destination(game);766 */
754 if (!destination || destination == &pd) {767bool Ship::withdraw_item(Game& game, PortDock& pd) {
755 items.push_back(items_[src]);768 bool unloaded = false;
769 size_t dst = 0;
770 for (ShippingItem& si : items_) {
771 if (!unloaded) {
772 const PortDock* itemdest = si.get_destination(game);
773 if (!itemdest || itemdest == &pd) {
774 pd.shipping_item_arrived(game, si);
775 unloaded = true;
776 continue;
777 }
778 }
779 items_[dst++] = si;
780 }
781 items_.resize(dst);
782 return unloaded;
783}
784
785/**
786 * Unload all items not favored by given next dest.
787 * Assert all items for current portdock have already been unloaded.
788 */
789void Ship::unload_unfit_items(Game& game, PortDock& here, const PortDock& nextdest) {
790 size_t dst = 0;
791 for (ShippingItem& si : items_) {
792 if (fleet_->is_path_favourable(here, nextdest, *si.get_destination(game))) {
793 items_[dst++] = si;
756 } else {794 } else {
757 items_[dst++] = items_[src];795 here.shipping_item_returned(game, si);
758 }796 }
759 }797 }
760 items_.resize(dst);798 items_.resize(dst);
@@ -813,7 +851,7 @@
813 // I (tiborb) failed to invoke this situation when testing so851 // I (tiborb) failed to invoke this situation when testing so
814 // I am not sure if following line behaves allright852 // I am not sure if following line behaves allright
815 get_fleet()->update(game);853 get_fleet()->update(game);
816 start_task_idle(game, descr().main_animation(), 5000);854 start_task_idle(game, descr().main_animation(), kFleetInterval);
817 }855 }
818}856}
819857
@@ -837,10 +875,10 @@
837875
838 set_economy(game, expedition_->economy);876 set_economy(game, expedition_->economy);
839877
840 for (int i = items_.size() - 1; i >= 0; --i) {878 for (const ShippingItem& si : items_) {
841 WareInstance* ware;879 WareInstance* ware;
842 Worker* worker;880 Worker* worker;
843 items_.at(i).get(game, &ware, &worker);881 si.get(game, &ware, &worker);
844 if (worker) {882 if (worker) {
845 worker->reset_tasks(game);883 worker->reset_tasks(game);
846 worker->start_task_idle(game, 0, -1);884 worker->start_task_idle(game, 0, -1);
@@ -971,8 +1009,12 @@
971/// @note only called via player command1009/// @note only called via player command
972void Ship::sink_ship(Game& game) {1010void Ship::sink_ship(Game& game) {
973 // Running colonization has the highest priority + a sink request is only valid once1011 // Running colonization has the highest priority + a sink request is only valid once
974 if (!state_is_sinkable())1012 if (!state_is_sinkable()) {
975 return;1013 return;
1014 }
1015 if (destination_.is_set()) {
1016 destination_.get(game)->ship_coming(false);
1017 }
976 ship_state_ = ShipStates::kSinkRequest;1018 ship_state_ = ShipStates::kSinkRequest;
977 // Make sure the ship is active and close possible open windows1019 // Make sure the ship is active and close possible open windows
978 ship_wakeup(game);1020 ship_wakeup(game);
9791021
=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h 2018-09-05 06:42:21 +0000
+++ src/logic/map_objects/tribes/ship.h 2018-09-21 19:37:16 +0000
@@ -78,6 +78,8 @@
78 DISALLOW_COPY_AND_ASSIGN(ShipDescr);78 DISALLOW_COPY_AND_ASSIGN(ShipDescr);
79};79};
8080
81constexpr int32_t kShipInterval = 1500;
82
81/**83/**
82 * Ships belong to a player and to an economy. The usually are in a (unique)84 * Ships belong to a player and to an economy. The usually are in a (unique)
83 * fleet for a player, but only if they are on standard duty. Exploration ships85 * fleet for a player, but only if they are on standard duty. Exploration ships
@@ -104,7 +106,7 @@
104 return economy_;106 return economy_;
105 }107 }
106 void set_economy(Game&, Economy* e);108 void set_economy(Game&, Economy* e);
107 void set_destination(Game&, PortDock&);109 void set_destination(PortDock*);
108110
109 void init_auto_task(Game&) override;111 void init_auto_task(Game&) override;
110112
@@ -126,8 +128,9 @@
126 return items_[idx];128 return items_[idx];
127 }129 }
128130
129 void withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items);131 void add_item(Game&, const ShippingItem&);
130 void add_item(Game&, const ShippingItem& item);132 bool withdraw_item(Game&, PortDock&);
133 void unload_unfit_items(Game&, PortDock& here, const PortDock& nextdest);
131134
132 // A ship with task expedition can be in four states: kExpeditionWaiting, kExpeditionScouting,135 // A ship with task expedition can be in four states: kExpeditionWaiting, kExpeditionScouting,
133 // kExpeditionPortspaceFound or kExpeditionColonizing in the first states, the owning player of136 // kExpeditionPortspaceFound or kExpeditionColonizing in the first states, the owning player of

Subscribers

People subscribed via source and target branches

to status/vote changes: