Merge lp:~widelands-dev/widelands/ship_scheduling_2 into lp:widelands
- ship_scheduling_2
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~widelands-dev/widelands/ship_scheduling_2 |
Merge into: | lp:widelands |
Diff against target: |
950 lines (+366/-314) 6 files modified
src/economy/fleet.cc (+163/-216) src/economy/fleet.h (+4/-0) src/economy/portdock.cc (+115/-61) src/economy/portdock.h (+5/-4) src/logic/map_objects/tribes/ship.cc (+73/-30) src/logic/map_objects/tribes/ship.h (+6/-3) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/ship_scheduling_2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Widelands Developers | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2018-08-30.
Commit message
Description of the change
See description of the branch:
https:/
Get windows builds and ask for testing - do not review yet
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
bunnybot (widelandsofficial) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 3758. State: errored. Details: https:/
Appveyor build 3558. State: success. Details: https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 3855. State: errored. Details: https:/
Appveyor build 3653. State: failed. Details: https:/
Preview Diff
1 | === modified file 'src/economy/fleet.cc' | |||
2 | --- src/economy/fleet.cc 2018-04-07 16:59:00 +0000 | |||
3 | +++ src/economy/fleet.cc 2018-08-29 19:19:29 +0000 | |||
4 | @@ -628,8 +628,7 @@ | |||
5 | 628 | } | 628 | } |
6 | 629 | 629 | ||
7 | 630 | /** | 630 | /** |
10 | 631 | * Act callback updates ship scheduling. All decisions about where transport ships | 631 | * Act callback updates ship scheduling of idle ships. |
9 | 632 | * are supposed to go are made via this function. | ||
11 | 633 | * | 632 | * |
12 | 634 | * @note Do not call this directly; instead, trigger it via @ref update | 633 | * @note Do not call this directly; instead, trigger it via @ref update |
13 | 635 | */ | 634 | */ |
14 | @@ -637,230 +636,178 @@ | |||
15 | 637 | act_pending_ = false; | 636 | act_pending_ = false; |
16 | 638 | 637 | ||
17 | 639 | if (!active()) { | 638 | if (!active()) { |
22 | 640 | // If we are here, most likely act() was called by a port with waiting wares or an expedition | 639 | // If we are here, most likely act() was called by a port with waiting wares or |
23 | 641 | // ready | 640 | // with an expedition ready, although there are still no ships. |
24 | 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() |
25 | 643 | schedule_act(game, 5000); // retry in the next time | 642 | schedule_act(game, kFleetInterval); // retry in the next time |
26 | 644 | act_pending_ = true; | 643 | act_pending_ = true; |
27 | 645 | return; | 644 | return; |
28 | 646 | } | 645 | } |
29 | 647 | 646 | ||
30 | 648 | molog("Fleet::act\n"); | 647 | molog("Fleet::act\n"); |
31 | 649 | 648 | ||
141 | 650 | // we need to calculate what ship is to be send to which port | 649 | // For each waiting port, try to find idle ships and send to it the closest one. |
142 | 651 | // for this we will have temporary data structure with format | 650 | uint16_t waiting_ports = ports_.size(); |
143 | 652 | // <<ship,port>,score> | 651 | for (PortDock* p : ports_) { |
144 | 653 | // where ship and port are not objects but positions in ports_ and ships_ | 652 | if (p->get_need_ship() == 0) { |
145 | 654 | // this is to allow native hashing | 653 | --waiting_ports; |
146 | 655 | std::map<std::pair<uint16_t, uint16_t>, uint16_t> scores; | 654 | continue; |
147 | 656 | 655 | } | |
148 | 657 | // so we will identify all pairs: idle ship : ports, and score all such | 656 | |
149 | 658 | // pairs. We consider | 657 | Ship* closest_ship = nullptr; |
150 | 659 | // - count of wares onboard, first ware (oldest) is counted as 8 (prioritization) | 658 | float shortest_dist = 10000; |
151 | 660 | // (counting wares for particular port only) | 659 | bool waiting = true; |
152 | 661 | // - count wares waiting at the port/3 | 660 | |
153 | 662 | // - distance between ship and a port (0-10 points, the closer the more points) | 661 | for (Ship* s : ships_) { |
154 | 663 | // - is another ship heading there right now? | 662 | if (s->get_destination(game)) { |
155 | 664 | 663 | if (s->get_destination(game) == p) { | |
156 | 665 | // at the end we must know if requrests of all ports asking for ship were addressed | 664 | waiting = false; |
157 | 666 | // if any unsatisfied, we must schedule new run of this function | 665 | --waiting_ports; |
158 | 667 | // when we send a ship there, the port is removed from list | 666 | break; |
159 | 668 | std::list<uint16_t> waiting_ports; | 667 | } |
160 | 669 | 668 | continue; // has already destination | |
161 | 670 | // this is just helper - first member of scores map | 669 | } |
162 | 671 | std::pair<uint16_t, uint16_t> mapping; // ship number, port number | 670 | if (s->get_ship_state() != Ship::ShipStates::kTransport) { |
163 | 672 | 671 | continue; // in expedition obviously | |
164 | 673 | // first we go over ships - idle ones (=without destination) | 672 | } |
165 | 674 | // then over wares on these ships and create first ship-port | 673 | |
166 | 675 | // pairs with score | 674 | // here we get distance ship->port |
167 | 676 | for (uint16_t s = 0; s < ships_.size(); s += 1) { | 675 | int16_t route_length = 10000; |
168 | 677 | if (ships_[s]->get_destination(game)) { | 676 | |
169 | 678 | continue; | 677 | PortDock* cur_port = get_dock(game, s->get_position()); |
170 | 679 | } | 678 | if (cur_port) { // we are in a port |
171 | 680 | if (ships_[s]->get_ship_state() != Ship::ShipStates::kTransport) { | 679 | if (cur_port == p) { // same port |
172 | 681 | continue; // in expedition obviously | 680 | route_length = 0; |
173 | 682 | } | 681 | } else { // different port |
174 | 683 | 682 | Path tmp_path; | |
175 | 684 | for (uint16_t i = 0; i < ships_[s]->get_nritems(); i += 1) { | 683 | if (get_path(*cur_port, *p, tmp_path)) { // try to use precalculated path |
176 | 685 | PortDock* dst = ships_[s]->items_[i].get_destination(game); | 684 | route_length = tmp_path.get_nsteps(); |
177 | 686 | if (!dst) { | 685 | } |
178 | 687 | // if wares without destination on ship without destination | 686 | } |
179 | 688 | // such ship can be send to any port, and should be sent | 687 | } |
180 | 689 | // to some port, so we add 1 point to score for each port | 688 | |
181 | 690 | for (uint16_t p = 0; p < ports_.size(); p += 1) { | 689 | if (route_length == 10000) { // above failed, so we calculate path "manually" |
182 | 691 | mapping.first = s; | 690 | // most probably the ship is not in a port (should not happen frequently) |
183 | 692 | mapping.second = p; | 691 | route_length = s->calculate_sea_route(game, *p); |
184 | 693 | scores[mapping] += 1; | 692 | } |
185 | 694 | } | 693 | |
186 | 695 | continue; | 694 | if (route_length < shortest_dist) { |
187 | 696 | } | 695 | shortest_dist = route_length; |
188 | 697 | 696 | closest_ship = s; | |
189 | 698 | bool destination_found = false; // Just a functional check | 697 | } |
190 | 699 | for (uint16_t p = 0; p < ports_.size(); p += 1) { | 698 | } |
191 | 700 | if (ports_[p] == ships_[s]->items_[i].get_destination(game)) { | 699 | |
192 | 701 | mapping.first = s; | 700 | if (waiting && closest_ship) { |
193 | 702 | mapping.second = p; | 701 | --waiting_ports; |
194 | 703 | scores[mapping] += (i == 0) ? 8 : 1; | 702 | closest_ship->set_destination(p); |
195 | 704 | destination_found = true; | 703 | closest_ship->send_signal(game, "wakeup"); |
196 | 705 | } | 704 | } |
197 | 706 | } | 705 | } |
198 | 707 | if (!destination_found) { | 706 | |
199 | 708 | // Perhaps the throw here is too strong | 707 | if (waiting_ports > 0) { |
200 | 709 | // we can still remove it before stable release if it proves too much | 708 | molog("... there are %u ports requesting ship(s) we cannot satisfy yet\n", waiting_ports); |
201 | 710 | // during my testing this situation never happened | 709 | schedule_act(game, kFleetInterval); // retry next time |
202 | 711 | throw wexception("A ware with destination that does not match any of player's" | 710 | act_pending_ = true; |
203 | 712 | " ports, ship %u, ware's destination: %u", | 711 | } |
204 | 713 | ships_[s]->serial(), | 712 | } |
205 | 714 | ships_[s]->items_[i].get_destination(game)->serial()); | 713 | |
206 | 715 | } | 714 | /** |
207 | 716 | } | 715 | * For the given three consecutive ports, decide if their path is favourable or not. |
208 | 717 | } | 716 | * \return true/false for yes/no |
209 | 718 | 717 | */ | |
210 | 719 | // now opposite aproach - we go over ports to find out those that have wares | 718 | bool Fleet::is_path_favourable(PortDock& start, PortDock& middle, PortDock& finish) { |
211 | 720 | // waiting for ship then find candidate ships to satisfy the requests | 719 | if (&middle != &finish) { |
212 | 721 | for (uint16_t p = 0; p < ports_.size(); p += 1) { | 720 | Path path_start_to_finish; |
213 | 722 | PortDock& pd = *ports_[p]; | 721 | Path path_middle_to_finish; |
214 | 723 | if (!pd.get_need_ship()) { | 722 | |
215 | 724 | continue; | 723 | assert(get_path(start, finish, path_start_to_finish)); |
216 | 725 | } | 724 | if (get_path(middle, finish, path_middle_to_finish)) { |
217 | 726 | 725 | if (path_middle_to_finish.get_nsteps() > path_start_to_finish.get_nsteps()) { | |
218 | 727 | // general stategy is "one ship for port is enough", but sometimes | 726 | return false; |
219 | 728 | // amount of ware waiting for ship is too high | 727 | } |
220 | 729 | if (count_ships_heading_here(game, &pd) * 25 > pd.count_waiting()) { | 728 | } |
221 | 730 | continue; | 729 | } |
222 | 731 | } | 730 | return true; // default |
223 | 732 | 731 | } | |
224 | 733 | waiting_ports.push_back(p); | 732 | |
225 | 734 | 733 | /** | |
226 | 735 | // scoring and entering the pair into scores (or increasing existing | 734 | * For the given ship, go through all ports of this fleet |
227 | 736 | // score if the pair is already there) | 735 | * and find the one with the best score. |
228 | 737 | for (uint16_t s = 0; s < ships_.size(); s += 1) { | 736 | * \return that port |
229 | 738 | 737 | * | |
230 | 739 | if (ships_[s]->get_destination(game)) { | 738 | * @note cur_port is never nullptr (we are always in a port), |
231 | 740 | continue; // already has destination | 739 | * but that is kept in case the design changes in the future |
232 | 741 | } | 740 | */ |
233 | 742 | 741 | PortDock* Fleet::find_next_dest(Game& game, Ship& ship, PortDock* const cur_port) { | |
234 | 743 | if (ships_[s]->get_ship_state() != Ship::ShipStates::kTransport) { | 742 | // uint32_t const max_capacity = ship.descr().get_capacity(); |
235 | 744 | continue; // in expedition obviously | 743 | PortDock* best_port = nullptr; |
236 | 745 | } | 744 | float best_score = 0; |
237 | 746 | 745 | ||
238 | 747 | mapping.first = s; | 746 | for (PortDock* p : ports_) { |
239 | 748 | mapping.second = p; | 747 | if (p == cur_port) { |
240 | 749 | // following aproximately considers free capacity of a ship | 748 | continue; // same port |
241 | 750 | scores[mapping] += ((ships_[s]->get_nritems() > 15) ? 1 : 3) + | 749 | } |
242 | 751 | std::min(ships_[s]->descr().get_capacity() - ships_[s]->get_nritems(), | 750 | |
243 | 752 | ports_[p]->count_waiting()) / | 751 | float score = 0; |
244 | 753 | 3; | 752 | WareInstance* ware; |
245 | 754 | } | 753 | Worker* worker; |
246 | 755 | } | 754 | |
247 | 756 | 755 | // score for wares/workers onboard that ship for that port | |
248 | 757 | // Now adding score for distance | 756 | for (uint16_t i = 0; i < ship.get_nritems(); ++i) { |
249 | 758 | for (auto ship_port_relation : scores) { | 757 | ShippingItem& si = ship.items_[i]; |
250 | 758 | if (si.get_destination(game) == p) { | ||
251 | 759 | si.get(game, &ware, &worker); | ||
252 | 760 | if (ware) { | ||
253 | 761 | score += 1; // TODO: increase by ware's importance | ||
254 | 762 | } else { // worker | ||
255 | 763 | score += 4; | ||
256 | 764 | } | ||
257 | 765 | } | ||
258 | 766 | } | ||
259 | 767 | |||
260 | 768 | if (cur_port) { // we are in a port | ||
261 | 769 | // score for wares/workers waiting at that port | ||
262 | 770 | for (uint16_t i = 0; i < cur_port->count_waiting(); ++i) { | ||
263 | 771 | ShippingItem& si = cur_port->waiting_[i]; | ||
264 | 772 | if (si.get_destination(game) == p) { | ||
265 | 773 | si.get(game, &ware, &worker); | ||
266 | 774 | if (ware) { | ||
267 | 775 | score += 1; // TODO: increase by ware's importance | ||
268 | 776 | } else { // worker | ||
269 | 777 | score += 4; | ||
270 | 778 | } | ||
271 | 779 | } | ||
272 | 780 | } | ||
273 | 781 | } | ||
274 | 782 | |||
275 | 783 | if (score == 0 && p->get_need_ship() == 0) { | ||
276 | 784 | continue; // empty ship to empty port | ||
277 | 785 | } | ||
278 | 759 | 786 | ||
279 | 760 | // here we get distance ship->port | 787 | // here we get distance ship->port |
280 | 761 | // possibilities are: | ||
281 | 762 | // - we are in port and it is the same as target port | ||
282 | 763 | // - we are in other port, then we use get_dock() function to fetch precalculated path | ||
283 | 764 | // - if above fails, we calculate path "manually" | ||
284 | 765 | int16_t route_length = -1; | 788 | int16_t route_length = -1; |
285 | 766 | 789 | ||
383 | 767 | PortDock* current_portdock = | 790 | if (cur_port) { // we are in a port |
384 | 768 | get_dock(game, ships_[ship_port_relation.first.first]->get_position()); | 791 | Path tmp_path; |
385 | 769 | 792 | if (get_path(*cur_port, *p, tmp_path)) { // try to use precalculated path | |
386 | 770 | if (current_portdock) { // we try to use precalculated paths of game | 793 | route_length = tmp_path.get_nsteps(); |
387 | 771 | 794 | } | |
388 | 772 | // we are in the same portdock | 795 | } |
389 | 773 | if (current_portdock == ports_[ship_port_relation.first.second]) { | 796 | |
390 | 774 | route_length = 0; | 797 | if (route_length == -1) { // above failed, so we calculate path "manually" |
391 | 775 | } else { // it is different portdock then | 798 | // most probably the ship is not in a port (should not happen frequently) |
392 | 776 | Path tmp_path; | 799 | route_length = ship.calculate_sea_route(game, *p); |
393 | 777 | if (get_path(*current_portdock, *ports_[ship_port_relation.first.second], tmp_path)) { | 800 | } |
394 | 778 | route_length = tmp_path.get_nsteps(); | 801 | |
395 | 779 | } | 802 | score = (score + 1) * (score + p->get_need_ship()); |
396 | 780 | } | 803 | score = score * (1 - route_length / (score + route_length)); |
397 | 781 | } | 804 | if (score > best_score) { |
398 | 782 | 805 | best_score = score; | |
399 | 783 | // most probably the ship is not in a portdock (should not happen frequently) | 806 | best_port = p; |
400 | 784 | if (route_length == -1) { | 807 | } |
401 | 785 | route_length = ships_[ship_port_relation.first.first]->calculate_sea_route( | 808 | } |
402 | 786 | game, *ports_[ship_port_relation.first.second]); | 809 | |
403 | 787 | } | 810 | return best_port; |
307 | 788 | |||
308 | 789 | // now we have length of route, so we need to calculate score | ||
309 | 790 | int16_t score_for_distance = 0; | ||
310 | 791 | if (route_length < 3) { | ||
311 | 792 | score_for_distance = 10; | ||
312 | 793 | } else { | ||
313 | 794 | score_for_distance = 8 - route_length / 50; | ||
314 | 795 | } | ||
315 | 796 | // must not be negative | ||
316 | 797 | score_for_distance = (score_for_distance < 0) ? 0 : score_for_distance; | ||
317 | 798 | |||
318 | 799 | scores[ship_port_relation.first] += score_for_distance; | ||
319 | 800 | } | ||
320 | 801 | |||
321 | 802 | // looking for best scores and sending ships accordingly | ||
322 | 803 | uint16_t best_ship = 0; | ||
323 | 804 | uint16_t best_port = 0; | ||
324 | 805 | |||
325 | 806 | // after sending a ship we will remove one or more items from scores | ||
326 | 807 | while (!scores.empty()) { | ||
327 | 808 | uint16_t best_score = 0; | ||
328 | 809 | |||
329 | 810 | // searching for combination with highest score | ||
330 | 811 | for (const auto& combination : scores) { | ||
331 | 812 | if (combination.second > best_score) { | ||
332 | 813 | best_score = combination.second; | ||
333 | 814 | best_ship = combination.first.first; | ||
334 | 815 | best_port = combination.first.second; | ||
335 | 816 | } | ||
336 | 817 | } | ||
337 | 818 | if (best_score == 0) { | ||
338 | 819 | // this is check of correctnes of this algorithm, this should not happen | ||
339 | 820 | throw wexception("Fleet::act(): No port-destination pair selected or its score is zero"); | ||
340 | 821 | } | ||
341 | 822 | |||
342 | 823 | // making sure the winner has no destination set | ||
343 | 824 | assert(!ships_[best_ship]->get_destination(game)); | ||
344 | 825 | |||
345 | 826 | // now actual setting destination for "best ship" | ||
346 | 827 | ships_[best_ship]->set_destination(game, *ports_[best_port]); | ||
347 | 828 | molog("... ship %u sent to port %u, wares onboard: %2d, the port is asking for a ship: %s\n", | ||
348 | 829 | ships_[best_ship]->serial(), ports_[best_port]->serial(), | ||
349 | 830 | ships_[best_ship]->get_nritems(), (ports_[best_port]->get_need_ship()) ? "yes" : "no"); | ||
350 | 831 | |||
351 | 832 | // pruning the scores table | ||
352 | 833 | // the ship that was just sent somewhere cannot be send elsewhere :) | ||
353 | 834 | for (auto it = scores.cbegin(); it != scores.cend();) { | ||
354 | 835 | |||
355 | 836 | // decreasing score for target port as there was a ship just sent there | ||
356 | 837 | if (it->first.second == best_port) { | ||
357 | 838 | mapping.first = it->first.first; | ||
358 | 839 | mapping.second = it->first.second; | ||
359 | 840 | scores[mapping] /= 2; | ||
360 | 841 | // just make sure it is nonzero | ||
361 | 842 | scores[mapping] = (scores[mapping] == 0) ? 1 : scores[mapping]; | ||
362 | 843 | } | ||
363 | 844 | |||
364 | 845 | // but removing all pairs where best ship is participating as it is not available anymore | ||
365 | 846 | // (because it was sent to "best port") | ||
366 | 847 | if (it->first.first == best_ship) { | ||
367 | 848 | scores.erase(it++); | ||
368 | 849 | } else { | ||
369 | 850 | ++it; | ||
370 | 851 | } | ||
371 | 852 | } | ||
372 | 853 | |||
373 | 854 | // also removing the port from waiting_ports | ||
374 | 855 | waiting_ports.remove(best_port); | ||
375 | 856 | } | ||
376 | 857 | |||
377 | 858 | if (!waiting_ports.empty()) { | ||
378 | 859 | molog("... there are %" PRIuS " ports requesting ship(s) we cannot satisfy yet\n", | ||
379 | 860 | waiting_ports.size()); | ||
380 | 861 | schedule_act(game, 5000); // retry next time | ||
381 | 862 | act_pending_ = true; | ||
382 | 863 | } | ||
404 | 864 | } | 811 | } |
405 | 865 | 812 | ||
406 | 866 | void Fleet::log_general_info(const EditorGameBase& egbase) { | 813 | void Fleet::log_general_info(const EditorGameBase& egbase) { |
407 | 867 | 814 | ||
408 | === modified file 'src/economy/fleet.h' | |||
409 | --- src/economy/fleet.h 2018-04-16 07:03:12 +0000 | |||
410 | +++ src/economy/fleet.h 2018-08-29 19:19:29 +0000 | |||
411 | @@ -46,6 +46,8 @@ | |||
412 | 46 | DISALLOW_COPY_AND_ASSIGN(FleetDescr); | 46 | DISALLOW_COPY_AND_ASSIGN(FleetDescr); |
413 | 47 | }; | 47 | }; |
414 | 48 | 48 | ||
415 | 49 | constexpr int32_t kFleetInterval = 5000; | ||
416 | 50 | |||
417 | 49 | /** | 51 | /** |
418 | 50 | * Manage all ships and ports of a player that are connected | 52 | * Manage all ships and ports of a player that are connected |
419 | 51 | * by ocean. | 53 | * by ocean. |
420 | @@ -152,6 +154,8 @@ | |||
421 | 152 | void save(EditorGameBase&, MapObjectSaver&, FileWrite&) override; | 154 | void save(EditorGameBase&, MapObjectSaver&, FileWrite&) override; |
422 | 153 | 155 | ||
423 | 154 | static MapObject::Loader* load(EditorGameBase&, MapObjectLoader&, FileRead&); | 156 | static MapObject::Loader* load(EditorGameBase&, MapObjectLoader&, FileRead&); |
424 | 157 | bool is_path_favourable(PortDock& start, PortDock& middle, PortDock& finish); | ||
425 | 158 | PortDock* find_next_dest(Game&, Ship&, PortDock* const cur_port); | ||
426 | 155 | }; | 159 | }; |
427 | 156 | 160 | ||
428 | 157 | } // namespace Widelands | 161 | } // namespace Widelands |
429 | 158 | 162 | ||
430 | === modified file 'src/economy/portdock.cc' | |||
431 | --- src/economy/portdock.cc 2018-04-16 07:03:12 +0000 | |||
432 | +++ src/economy/portdock.cc 2018-08-29 19:19:29 +0000 | |||
433 | @@ -34,6 +34,7 @@ | |||
434 | 34 | #include "logic/game_data_error.h" | 34 | #include "logic/game_data_error.h" |
435 | 35 | #include "logic/map_objects/tribes/ship.h" | 35 | #include "logic/map_objects/tribes/ship.h" |
436 | 36 | #include "logic/map_objects/tribes/warehouse.h" | 36 | #include "logic/map_objects/tribes/warehouse.h" |
437 | 37 | #include "logic/path.h" | ||
438 | 37 | #include "logic/player.h" | 38 | #include "logic/player.h" |
439 | 38 | #include "logic/widelands_geometry_io.h" | 39 | #include "logic/widelands_geometry_io.h" |
440 | 39 | #include "map_io/map_object_loader.h" | 40 | #include "map_io/map_object_loader.h" |
441 | @@ -55,7 +56,7 @@ | |||
442 | 55 | : PlayerImmovable(g_portdock_descr), | 56 | : PlayerImmovable(g_portdock_descr), |
443 | 56 | fleet_(nullptr), | 57 | fleet_(nullptr), |
444 | 57 | warehouse_(wh), | 58 | warehouse_(wh), |
446 | 58 | need_ship_(false), | 59 | ships_coming_(0), |
447 | 59 | expedition_ready_(false) { | 60 | expedition_ready_(false) { |
448 | 60 | } | 61 | } |
449 | 61 | 62 | ||
450 | @@ -116,6 +117,10 @@ | |||
451 | 116 | return nullptr; | 117 | return nullptr; |
452 | 117 | } | 118 | } |
453 | 118 | 119 | ||
454 | 120 | uint32_t PortDock::get_need_ship() const { | ||
455 | 121 | return (waiting_.size() + (expedition_ready_ ? 20 : 0)) / (ships_coming_ + 1); | ||
456 | 122 | } | ||
457 | 123 | |||
458 | 119 | /** | 124 | /** |
459 | 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. |
460 | 121 | * | 126 | * |
461 | @@ -289,36 +294,58 @@ | |||
462 | 289 | 294 | ||
463 | 290 | // Destination might have vanished or be in another economy altogether. | 295 | // Destination might have vanished or be in another economy altogether. |
464 | 291 | if (dst && dst->get_economy() == get_economy()) { | 296 | if (dst && dst->get_economy() == get_economy()) { |
466 | 292 | set_need_ship(game, true); | 297 | if (ships_coming_ <= 0) { |
467 | 298 | set_need_ship(game, true); | ||
468 | 299 | } | ||
469 | 293 | } else { | 300 | } else { |
470 | 294 | it->set_location(game, warehouse_); | 301 | it->set_location(game, warehouse_); |
471 | 295 | it->end_shipping(game); | 302 | it->end_shipping(game); |
472 | 296 | *it = waiting_.back(); | 303 | *it = waiting_.back(); |
473 | 297 | waiting_.pop_back(); | 304 | waiting_.pop_back(); |
483 | 298 | 305 | } | |
484 | 299 | if (waiting_.empty()) | 306 | } |
485 | 300 | set_need_ship(game, false); | 307 | |
486 | 301 | } | 308 | /** |
487 | 302 | } | 309 | * Receive shipping item from unloading ship. |
488 | 303 | 310 | * Called by ship code. | |
489 | 304 | /** | 311 | */ |
490 | 305 | * A ship has arrived at the dock. Clear all items designated for this dock, | 312 | void PortDock::shipping_item_arrived(Game& game, ShippingItem& si) { |
491 | 306 | * and load the ship. | 313 | si.set_location(game, warehouse_); |
492 | 314 | si.end_shipping(game); | ||
493 | 315 | } | ||
494 | 316 | |||
495 | 317 | /** | ||
496 | 318 | * Receive shipping item from departing ship. | ||
497 | 319 | * Called by ship code. | ||
498 | 320 | */ | ||
499 | 321 | void PortDock::shipping_item_returned(Game& game, ShippingItem& si) { | ||
500 | 322 | si.set_location(game, this); | ||
501 | 323 | waiting_.push_back(si); | ||
502 | 324 | } | ||
503 | 325 | |||
504 | 326 | /** | ||
505 | 327 | * A ship changed destination and is now coming at the dock. Increase counter for need_ship. | ||
506 | 328 | */ | ||
507 | 329 | void PortDock::ship_coming(bool affirmative) { | ||
508 | 330 | if (affirmative) { | ||
509 | 331 | ++ships_coming_; | ||
510 | 332 | } else { | ||
511 | 333 | ships_coming_ = std::max(0, ships_coming_ - 1); // max used for compatibility with old savegames | ||
512 | 334 | } | ||
513 | 335 | } | ||
514 | 336 | |||
515 | 337 | /** | ||
516 | 338 | * A ship has arrived at the dock. Set its next destination and load it accordingly. | ||
517 | 307 | */ | 339 | */ |
518 | 308 | void PortDock::ship_arrived(Game& game, Ship& ship) { | 340 | void PortDock::ship_arrived(Game& game, Ship& ship) { |
526 | 309 | std::vector<ShippingItem> items_brought_by_ship; | 341 | ship_coming(false); |
520 | 310 | ship.withdraw_items(game, *this, items_brought_by_ship); | ||
521 | 311 | |||
522 | 312 | for (ShippingItem& shipping_item : items_brought_by_ship) { | ||
523 | 313 | shipping_item.set_location(game, warehouse_); | ||
524 | 314 | shipping_item.end_shipping(game); | ||
525 | 315 | } | ||
527 | 316 | 342 | ||
528 | 317 | if (expedition_ready_) { | 343 | if (expedition_ready_) { |
529 | 318 | assert(expedition_bootstrap_ != nullptr); | 344 | assert(expedition_bootstrap_ != nullptr); |
530 | 319 | 345 | ||
531 | 320 | // Only use an empty ship. | 346 | // Only use an empty ship. |
532 | 321 | if (ship.get_nritems() < 1) { | 347 | if (ship.get_nritems() < 1) { |
533 | 348 | ship.set_destination(nullptr); | ||
534 | 322 | // Load the ship | 349 | // Load the ship |
535 | 323 | std::vector<Worker*> workers; | 350 | std::vector<Worker*> workers; |
536 | 324 | std::vector<WareInstance*> wares; | 351 | std::vector<WareInstance*> wares; |
537 | @@ -337,46 +364,73 @@ | |||
538 | 337 | // The expedition goods are now on the ship, so from now on it is independent from the port | 364 | // The expedition goods are now on the ship, so from now on it is independent from the port |
539 | 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, |
540 | 339 | cancel_expedition(game); | 366 | cancel_expedition(game); |
569 | 340 | return fleet_->update(game); | 367 | fleet_->update(game); |
570 | 341 | } | 368 | return; |
571 | 342 | } | 369 | } |
572 | 343 | 370 | } | |
573 | 344 | if (ship.get_nritems() < ship.descr().get_capacity() && !waiting_.empty()) { | 371 | |
574 | 345 | uint32_t nrload = | 372 | // decide where the arrived ship will go next |
575 | 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); |
576 | 347 | 374 | if (!next_port) { | |
577 | 348 | while (nrload--) { | 375 | ship.set_destination(next_port); |
578 | 349 | // Check if the item has still a valid destination | 376 | return; // no need to load anything |
579 | 350 | if (waiting_.back().get_destination(game)) { | 377 | } |
580 | 351 | // Destination is valid, so we load the item onto the ship | 378 | |
581 | 352 | ship.add_item(game, waiting_.back()); | 379 | // unload any wares/workers onboard the departing ship which are not favored by next dest |
582 | 353 | } else { | 380 | ship.unload_unfit_items(game, *this, *next_port); |
583 | 354 | // The item has no valid destination anymore, so we just carry it | 381 | |
584 | 355 | // back in the warehouse | 382 | // then load the remaining capacity of the departing ship with relevant items |
585 | 356 | waiting_.back().set_location(game, warehouse_); | 383 | uint32_t rest_capacity = ship.descr().get_capacity() - ship.get_nritems(); |
586 | 357 | waiting_.back().end_shipping(game); | 384 | |
587 | 358 | } | 385 | // firstly load the items which go to chosen dest, while also checking for items with invalid dest |
588 | 359 | waiting_.pop_back(); | 386 | uint32_t dst = 0; |
589 | 360 | } | 387 | for (ShippingItem& si : waiting_) { |
590 | 361 | 388 | PortDock* itemdest = si.get_destination(game); | |
591 | 362 | if (waiting_.empty()) { | 389 | if (itemdest) { // valid dest |
592 | 363 | set_need_ship(game, false); | 390 | if (rest_capacity == 0) { |
593 | 364 | } | 391 | waiting_[dst++] = si; // keep the item here |
594 | 365 | } | 392 | } else { |
595 | 366 | 393 | if (itemdest == next_port) { // the item goes to chosen dest | |
596 | 367 | fleet_->update(game); | 394 | ship.add_item(game, si); // load it |
597 | 395 | --rest_capacity; | ||
598 | 396 | } else { // different dest | ||
599 | 397 | waiting_[dst++] = si; // keep it here for now | ||
600 | 398 | } | ||
601 | 399 | } | ||
602 | 400 | } else { // invalid dest | ||
603 | 401 | // carry the item back in the warehouse | ||
604 | 402 | si.set_location(game, warehouse_); | ||
605 | 403 | si.end_shipping(game); | ||
606 | 404 | } | ||
607 | 405 | } | ||
608 | 406 | waiting_.resize(dst); | ||
609 | 407 | |||
610 | 408 | if (rest_capacity > 0) { // there is still capacity | ||
611 | 409 | // load any items favored by the chosen dest | ||
612 | 410 | dst = 0; | ||
613 | 411 | for (ShippingItem& si : waiting_) { | ||
614 | 412 | if (rest_capacity == 0) { | ||
615 | 413 | waiting_[dst++] = si; // keep the item here | ||
616 | 414 | } else { | ||
617 | 415 | if (fleet_->is_path_favourable(*this, *next_port, *si.get_destination(game))) { | ||
618 | 416 | ship.add_item(game, si); | ||
619 | 417 | --rest_capacity; | ||
620 | 418 | } else { // item is not favored by the chosen dest | ||
621 | 419 | // spare it from getting trapped inside the wrong ship | ||
622 | 420 | waiting_[dst++] = si; | ||
623 | 421 | } | ||
624 | 422 | } | ||
625 | 423 | } | ||
626 | 424 | waiting_.resize(dst); | ||
627 | 425 | } | ||
628 | 426 | ship.set_destination(next_port); | ||
629 | 427 | |||
630 | 428 | set_need_ship(game, !waiting_.empty()); | ||
631 | 368 | } | 429 | } |
632 | 369 | 430 | ||
633 | 370 | void PortDock::set_need_ship(Game& game, bool need) { | 431 | void PortDock::set_need_ship(Game& game, bool need) { |
643 | 371 | molog("set_need_ship(%s)\n", need ? "true" : "false"); | 432 | if (need && fleet_) { |
644 | 372 | 433 | molog("trigger fleet update\n"); | |
636 | 373 | if (need == need_ship_) | ||
637 | 374 | return; | ||
638 | 375 | |||
639 | 376 | need_ship_ = need; | ||
640 | 377 | |||
641 | 378 | if (fleet_) { | ||
642 | 379 | molog("... trigger fleet update\n"); | ||
645 | 380 | fleet_->update(game); | 434 | fleet_->update(game); |
646 | 381 | } | 435 | } |
647 | 382 | } | 436 | } |
648 | @@ -445,13 +499,13 @@ | |||
649 | 445 | 499 | ||
650 | 446 | if (warehouse_) { | 500 | if (warehouse_) { |
651 | 447 | Coords pos(warehouse_->get_position()); | 501 | Coords pos(warehouse_->get_position()); |
653 | 448 | molog("PortDock for warehouse %u (at %i,%i) in fleet %u, need_ship: %s, waiting: %" PRIuS | 502 | molog("PortDock for warehouse %u (at %i,%i) in fleet %u, expedition_ready: %s, waiting: %" PRIuS |
654 | 449 | "\n", | 503 | "\n", |
655 | 450 | warehouse_->serial(), pos.x, pos.y, fleet_ ? fleet_->serial() : 0, | 504 | warehouse_->serial(), pos.x, pos.y, fleet_ ? fleet_->serial() : 0, |
657 | 451 | need_ship_ ? "true" : "false", waiting_.size()); | 505 | expedition_ready_ ? "true" : "false", waiting_.size()); |
658 | 452 | } else { | 506 | } else { |
661 | 453 | molog("PortDock without a warehouse in fleet %u, need_ship: %s, waiting: %" PRIuS "\n", | 507 | molog("PortDock without a warehouse in fleet %u, expedition_ready: %s, waiting: %" PRIuS "\n", |
662 | 454 | fleet_ ? fleet_->serial() : 0, need_ship_ ? "true" : "false", waiting_.size()); | 508 | fleet_ ? fleet_->serial() : 0, expedition_ready_ ? "true" : "false", waiting_.size()); |
663 | 455 | } | 509 | } |
664 | 456 | 510 | ||
665 | 457 | for (ShippingItem& shipping_item : waiting_) { | 511 | for (ShippingItem& shipping_item : waiting_) { |
666 | @@ -479,7 +533,7 @@ | |||
667 | 479 | pd.set_position(egbase(), pd.dockpoints_[i]); | 533 | pd.set_position(egbase(), pd.dockpoints_[i]); |
668 | 480 | } | 534 | } |
669 | 481 | 535 | ||
671 | 482 | pd.need_ship_ = fr.unsigned_8(); | 536 | pd.ships_coming_ = fr.unsigned_8(); |
672 | 483 | 537 | ||
673 | 484 | waiting_.resize(fr.unsigned_32()); | 538 | waiting_.resize(fr.unsigned_32()); |
674 | 485 | for (ShippingItem::Loader& shipping_loader : waiting_) { | 539 | for (ShippingItem::Loader& shipping_loader : waiting_) { |
675 | @@ -553,7 +607,7 @@ | |||
676 | 553 | write_coords_32(&fw, coords); | 607 | write_coords_32(&fw, coords); |
677 | 554 | } | 608 | } |
678 | 555 | 609 | ||
680 | 556 | fw.unsigned_8(need_ship_); | 610 | fw.unsigned_8(ships_coming_); |
681 | 557 | 611 | ||
682 | 558 | fw.unsigned_32(waiting_.size()); | 612 | fw.unsigned_32(waiting_.size()); |
683 | 559 | for (ShippingItem& shipping_item : waiting_) { | 613 | for (ShippingItem& shipping_item : waiting_) { |
684 | 560 | 614 | ||
685 | === modified file 'src/economy/portdock.h' | |||
686 | --- src/economy/portdock.h 2018-04-07 16:59:00 +0000 | |||
687 | +++ src/economy/portdock.h 2018-08-29 19:19:29 +0000 | |||
688 | @@ -85,9 +85,7 @@ | |||
689 | 85 | return fleet_; | 85 | return fleet_; |
690 | 86 | } | 86 | } |
691 | 87 | PortDock* get_dock(Flag& flag) const; | 87 | PortDock* get_dock(Flag& flag) const; |
695 | 88 | bool get_need_ship() const { | 88 | uint32_t get_need_ship() const; |
693 | 89 | return need_ship_ || expedition_ready_; | ||
694 | 90 | } | ||
696 | 91 | 89 | ||
697 | 92 | void set_economy(Economy*) override; | 90 | void set_economy(Economy*) override; |
698 | 93 | 91 | ||
699 | @@ -113,6 +111,9 @@ | |||
700 | 113 | void add_shippingitem(Game&, Worker&); | 111 | void add_shippingitem(Game&, Worker&); |
701 | 114 | void update_shippingitem(Game&, Worker&); | 112 | void update_shippingitem(Game&, Worker&); |
702 | 115 | 113 | ||
703 | 114 | void shipping_item_arrived(Game&, ShippingItem&); | ||
704 | 115 | void shipping_item_returned(Game&, ShippingItem&); | ||
705 | 116 | void ship_coming(bool affirmative); | ||
706 | 116 | void ship_arrived(Game&, Ship&); | 117 | void ship_arrived(Game&, Ship&); |
707 | 117 | 118 | ||
708 | 118 | void log_general_info(const EditorGameBase&) override; | 119 | void log_general_info(const EditorGameBase&) override; |
709 | @@ -146,7 +147,7 @@ | |||
710 | 146 | Warehouse* warehouse_; | 147 | Warehouse* warehouse_; |
711 | 147 | PositionList dockpoints_; | 148 | PositionList dockpoints_; |
712 | 148 | std::vector<ShippingItem> waiting_; | 149 | std::vector<ShippingItem> waiting_; |
714 | 149 | bool need_ship_; | 150 | uint8_t ships_coming_; |
715 | 150 | bool expedition_ready_; | 151 | bool expedition_ready_; |
716 | 151 | 152 | ||
717 | 152 | std::unique_ptr<ExpeditionBootstrap> expedition_bootstrap_; | 153 | std::unique_ptr<ExpeditionBootstrap> expedition_bootstrap_; |
718 | 153 | 154 | ||
719 | === modified file 'src/logic/map_objects/tribes/ship.cc' | |||
720 | --- src/logic/map_objects/tribes/ship.cc 2018-07-08 15:16:16 +0000 | |||
721 | +++ src/logic/map_objects/tribes/ship.cc 2018-08-29 19:19:29 +0000 | |||
722 | @@ -303,12 +303,22 @@ | |||
723 | 303 | 303 | ||
724 | 304 | FCoords position = map.get_fcoords(get_position()); | 304 | FCoords position = map.get_fcoords(get_position()); |
725 | 305 | if (position.field->get_immovable() == dst) { | 305 | if (position.field->get_immovable() == dst) { |
732 | 306 | molog("ship_update: Arrived at dock %u\n", dst->serial()); | 306 | if (lastdock_ != dst) { |
733 | 307 | lastdock_ = dst; | 307 | molog("ship_update: Arrived at dock %u\n", dst->serial()); |
734 | 308 | destination_ = nullptr; | 308 | lastdock_ = dst; |
735 | 309 | dst->ship_arrived(game, *this); | 309 | } |
736 | 310 | start_task_idle(game, descr().main_animation(), 250); | 310 | if (withdraw_items(game, *dst)) { |
737 | 311 | Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged)); | 311 | schedule_act(game, kShipInterval); |
738 | 312 | return true; | ||
739 | 313 | } | ||
740 | 314 | |||
741 | 315 | dst->ship_arrived(game, *this); // this should call set_destination | ||
742 | 316 | dst = get_destination(game); | ||
743 | 317 | if (dst) { | ||
744 | 318 | start_task_movetodock(game, *dst); | ||
745 | 319 | } else { | ||
746 | 320 | start_task_idle(game, descr().main_animation(), 250); | ||
747 | 321 | } | ||
748 | 312 | return true; | 322 | return true; |
749 | 313 | } | 323 | } |
750 | 314 | 324 | ||
751 | @@ -484,7 +494,7 @@ | |||
752 | 484 | } | 494 | } |
753 | 485 | 495 | ||
754 | 486 | if (totalprob == 0) { | 496 | if (totalprob == 0) { |
756 | 487 | start_task_idle(game, descr().main_animation(), 1500); | 497 | start_task_idle(game, descr().main_animation(), kShipInterval); |
757 | 488 | return; | 498 | return; |
758 | 489 | } | 499 | } |
759 | 490 | 500 | ||
760 | @@ -496,13 +506,13 @@ | |||
761 | 496 | } | 506 | } |
762 | 497 | 507 | ||
763 | 498 | if (dir == 0 || dir > LAST_DIRECTION) { | 508 | if (dir == 0 || dir > LAST_DIRECTION) { |
765 | 499 | start_task_idle(game, descr().main_animation(), 1500); | 509 | start_task_idle(game, descr().main_animation(), kShipInterval); |
766 | 500 | return; | 510 | return; |
767 | 501 | } | 511 | } |
768 | 502 | 512 | ||
769 | 503 | FCoords neighbour = map.get_neighbour(position, dir); | 513 | FCoords neighbour = map.get_neighbour(position, dir); |
770 | 504 | if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { | 514 | if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { |
772 | 505 | start_task_idle(game, descr().main_animation(), 1500); | 515 | start_task_idle(game, descr().main_animation(), kShipInterval); |
773 | 506 | return; | 516 | return; |
774 | 507 | } | 517 | } |
775 | 508 | 518 | ||
776 | @@ -542,7 +552,7 @@ | |||
777 | 542 | pgettext("ship", "Waiting"), _("Island Circumnavigated"), | 552 | pgettext("ship", "Waiting"), _("Island Circumnavigated"), |
778 | 543 | _("An expedition ship sailed around its island without any events."), | 553 | _("An expedition ship sailed around its island without any events."), |
779 | 544 | "images/wui/ship/ship_explore_island_cw.png"); | 554 | "images/wui/ship/ship_explore_island_cw.png"); |
781 | 545 | return start_task_idle(game, descr().main_animation(), 1500); | 555 | return start_task_idle(game, descr().main_animation(), kShipInterval); |
782 | 546 | } | 556 | } |
783 | 547 | } | 557 | } |
784 | 548 | // The ship is supposed to follow the coast as close as possible, therefore the check | 558 | // The ship is supposed to follow the coast as close as possible, therefore the check |
785 | @@ -585,7 +595,7 @@ | |||
786 | 585 | shipname_.c_str()); | 595 | shipname_.c_str()); |
787 | 586 | set_ship_state_and_notify( | 596 | set_ship_state_and_notify( |
788 | 587 | ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand); | 597 | ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand); |
790 | 588 | start_task_idle(game, descr().main_animation(), 1500); | 598 | start_task_idle(game, descr().main_animation(), kShipInterval); |
791 | 589 | return; | 599 | return; |
792 | 590 | } | 600 | } |
793 | 591 | } else { // scouting towards a specific direction | 601 | } else { // scouting towards a specific direction |
794 | @@ -598,7 +608,7 @@ | |||
795 | 598 | // coast reached | 608 | // coast reached |
796 | 599 | set_ship_state_and_notify( | 609 | set_ship_state_and_notify( |
797 | 600 | ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand); | 610 | ShipStates::kExpeditionWaiting, NoteShip::Action::kWaitingForCommand); |
799 | 601 | start_task_idle(game, descr().main_animation(), 1500); | 611 | start_task_idle(game, descr().main_animation(), kShipInterval); |
800 | 602 | // Send a message to the player, that a new coast was reached | 612 | // Send a message to the player, that a new coast was reached |
801 | 603 | send_message(game, | 613 | send_message(game, |
802 | 604 | /** TRANSLATORS: A ship has discovered land */ | 614 | /** TRANSLATORS: A ship has discovered land */ |
803 | @@ -692,7 +702,7 @@ | |||
804 | 692 | } | 702 | } |
805 | 693 | 703 | ||
806 | 694 | expedition_.reset(nullptr); | 704 | expedition_.reset(nullptr); |
808 | 695 | return start_task_idle(game, descr().main_animation(), 1500); | 705 | return start_task_idle(game, descr().main_animation(), kShipInterval); |
809 | 696 | } | 706 | } |
810 | 697 | } | 707 | } |
811 | 698 | FALLS_THROUGH; | 708 | FALLS_THROUGH; |
812 | @@ -701,7 +711,7 @@ | |||
813 | 701 | case ShipStates::kSinkRequest: | 711 | case ShipStates::kSinkRequest: |
814 | 702 | case ShipStates::kSinkAnimation: { | 712 | case ShipStates::kSinkAnimation: { |
815 | 703 | // wait for input | 713 | // wait for input |
817 | 704 | start_task_idle(game, descr().main_animation(), 1500); | 714 | start_task_idle(game, descr().main_animation(), kShipInterval); |
818 | 705 | return; | 715 | return; |
819 | 706 | } | 716 | } |
820 | 707 | } | 717 | } |
821 | @@ -727,14 +737,17 @@ | |||
822 | 727 | 737 | ||
823 | 728 | /** | 738 | /** |
824 | 729 | * Enter a new destination port for the ship. | 739 | * Enter a new destination port for the ship. |
827 | 730 | * | 740 | * Call this after un/loading the ship, for proper logging. |
826 | 731 | * @note This is supposed to be called only from the scheduling code of @ref Fleet. | ||
828 | 732 | */ | 741 | */ |
834 | 733 | void Ship::set_destination(Game& game, PortDock& pd) { | 742 | void Ship::set_destination(PortDock* pd) { |
835 | 734 | molog("set_destination / sending to portdock %u (carrying %" PRIuS " items)\n", pd.serial(), | 743 | destination_ = pd; |
836 | 735 | items_.size()); | 744 | if (pd) { |
837 | 736 | destination_ = &pd; | 745 | molog("set_destination / sending to portdock %u (carrying %" PRIuS " items)\n", pd->serial(), |
838 | 737 | send_signal(game, "wakeup"); | 746 | items_.size()); |
839 | 747 | pd->ship_coming(true); | ||
840 | 748 | } else { | ||
841 | 749 | molog("set_destination / none\n"); | ||
842 | 750 | } | ||
843 | 738 | Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged)); | 751 | Notifications::publish(NoteShip(this, NoteShip::Action::kDestinationChanged)); |
844 | 739 | } | 752 | } |
845 | 740 | 753 | ||
846 | @@ -745,14 +758,40 @@ | |||
847 | 745 | items_.back().set_location(game, this); | 758 | items_.back().set_location(game, this); |
848 | 746 | } | 759 | } |
849 | 747 | 760 | ||
856 | 748 | void Ship::withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items) { | 761 | /** |
857 | 749 | uint32_t dst = 0; | 762 | * Unload one item designated for given dock or for no dock. |
858 | 750 | for (uint32_t src = 0; src < items_.size(); ++src) { | 763 | * \return true if item unloaded. |
859 | 751 | PortDock* destination = items_[src].get_destination(game); | 764 | */ |
860 | 752 | if (!destination || destination == &pd) { | 765 | bool Ship::withdraw_items(Game& game, PortDock& pd) { |
861 | 753 | items.push_back(items_[src]); | 766 | bool unloaded = false; |
862 | 767 | uint32_t dst = 0; | ||
863 | 768 | for (ShippingItem& si : items_) { | ||
864 | 769 | if (!unloaded) { | ||
865 | 770 | PortDock* itemdest = si.get_destination(game); | ||
866 | 771 | if (!itemdest || itemdest == &pd) { | ||
867 | 772 | pd.shipping_item_arrived(game, si); | ||
868 | 773 | unloaded = true; | ||
869 | 774 | continue; | ||
870 | 775 | } | ||
871 | 776 | } | ||
872 | 777 | |||
873 | 778 | items_[dst++] = si; | ||
874 | 779 | } | ||
875 | 780 | items_.resize(dst); | ||
876 | 781 | return unloaded; | ||
877 | 782 | } | ||
878 | 783 | |||
879 | 784 | /** | ||
880 | 785 | * Unload all items not favored by given next dest. | ||
881 | 786 | * Assert all items for current portdock have already been unloaded. | ||
882 | 787 | */ | ||
883 | 788 | void Ship::unload_unfit_items(Game& game, PortDock& here, PortDock& nextdest) { | ||
884 | 789 | uint32_t dst = 0; | ||
885 | 790 | for (ShippingItem& si : items_) { | ||
886 | 791 | if (fleet_->is_path_favourable(here, nextdest, *si.get_destination(game))) { | ||
887 | 792 | items_[dst++] = si; | ||
888 | 754 | } else { | 793 | } else { |
890 | 755 | items_[dst++] = items_[src]; | 794 | here.shipping_item_returned(game, si); |
891 | 756 | } | 795 | } |
892 | 757 | } | 796 | } |
893 | 758 | items_.resize(dst); | 797 | items_.resize(dst); |
894 | @@ -811,7 +850,7 @@ | |||
895 | 811 | // I (tiborb) failed to invoke this situation when testing so | 850 | // I (tiborb) failed to invoke this situation when testing so |
896 | 812 | // I am not sure if following line behaves allright | 851 | // I am not sure if following line behaves allright |
897 | 813 | get_fleet()->update(game); | 852 | get_fleet()->update(game); |
899 | 814 | start_task_idle(game, descr().main_animation(), 5000); | 853 | start_task_idle(game, descr().main_animation(), kFleetInterval); |
900 | 815 | } | 854 | } |
901 | 816 | } | 855 | } |
902 | 817 | 856 | ||
903 | @@ -969,8 +1008,12 @@ | |||
904 | 969 | /// @note only called via player command | 1008 | /// @note only called via player command |
905 | 970 | void Ship::sink_ship(Game& game) { | 1009 | void Ship::sink_ship(Game& game) { |
906 | 971 | // Running colonization has the highest priority + a sink request is only valid once | 1010 | // Running colonization has the highest priority + a sink request is only valid once |
908 | 972 | if (!state_is_sinkable()) | 1011 | if (!state_is_sinkable()) { |
909 | 973 | return; | 1012 | return; |
910 | 1013 | } | ||
911 | 1014 | if (destination_.is_set()) { | ||
912 | 1015 | destination_.get(game)->ship_coming(false); | ||
913 | 1016 | } | ||
914 | 974 | ship_state_ = ShipStates::kSinkRequest; | 1017 | ship_state_ = ShipStates::kSinkRequest; |
915 | 975 | // Make sure the ship is active and close possible open windows | 1018 | // Make sure the ship is active and close possible open windows |
916 | 976 | ship_wakeup(game); | 1019 | ship_wakeup(game); |
917 | 977 | 1020 | ||
918 | === modified file 'src/logic/map_objects/tribes/ship.h' | |||
919 | --- src/logic/map_objects/tribes/ship.h 2018-07-12 05:44:15 +0000 | |||
920 | +++ src/logic/map_objects/tribes/ship.h 2018-08-29 19:19:29 +0000 | |||
921 | @@ -78,6 +78,8 @@ | |||
922 | 78 | DISALLOW_COPY_AND_ASSIGN(ShipDescr); | 78 | DISALLOW_COPY_AND_ASSIGN(ShipDescr); |
923 | 79 | }; | 79 | }; |
924 | 80 | 80 | ||
925 | 81 | constexpr int32_t kShipInterval = 1500; | ||
926 | 82 | |||
927 | 81 | /** | 83 | /** |
928 | 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) |
929 | 83 | * fleet for a player, but only if they are on standard duty. Exploration ships | 85 | * fleet for a player, but only if they are on standard duty. Exploration ships |
930 | @@ -104,7 +106,7 @@ | |||
931 | 104 | return economy_; | 106 | return economy_; |
932 | 105 | } | 107 | } |
933 | 106 | void set_economy(Game&, Economy* e); | 108 | void set_economy(Game&, Economy* e); |
935 | 107 | void set_destination(Game&, PortDock&); | 109 | void set_destination(PortDock*); |
936 | 108 | 110 | ||
937 | 109 | void init_auto_task(Game&) override; | 111 | void init_auto_task(Game&) override; |
938 | 110 | 112 | ||
939 | @@ -126,8 +128,9 @@ | |||
940 | 126 | return items_[idx]; | 128 | return items_[idx]; |
941 | 127 | } | 129 | } |
942 | 128 | 130 | ||
945 | 129 | void withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items); | 131 | void add_item(Game&, const ShippingItem&); |
946 | 130 | void add_item(Game&, const ShippingItem& item); | 132 | bool withdraw_items(Game&, PortDock&); |
947 | 133 | void unload_unfit_items(Game&, PortDock& here, PortDock& nextdest); | ||
948 | 131 | 134 | ||
949 | 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, |
950 | 133 | // kExpeditionPortspaceFound or kExpeditionColonizing in the first states, the owning player of | 136 | // kExpeditionPortspaceFound or kExpeditionColonizing in the first states, the owning player of |
Continuous integration builds have changed state:
Travis build 3754. State: errored. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 412020649. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ ship_scheduling _2-3554.
Appveyor build 3554. State: failed. Details: https:/