Merge lp:~widelands-dev/widelands/ship_scheduling into lp:widelands
- ship_scheduling
- Merge into trunk
Proposed by
TiborB
Status: | Merged |
---|---|
Merged at revision: | 7515 |
Proposed branch: | lp:~widelands-dev/widelands/ship_scheduling |
Merge into: | lp:widelands |
Diff against target: |
794 lines (+456/-96) 11 files modified
src/economy/fleet.cc (+261/-60) src/economy/fleet.h (+7/-0) src/economy/portdock.cc (+9/-1) src/economy/portdock.h (+1/-0) src/logic/ship.cc (+43/-21) src/logic/ship.h (+2/-0) src/logic/warehouse.cc (+20/-2) test/maps/expedition.wmf/scripting/init.lua (+65/-8) test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_one_ship.lua (+2/-1) test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_two_ships.lua (+3/-2) test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua (+43/-1) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/ship_scheduling |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
SirVer | Approve | ||
Review via email: mp+266626@code.launchpad.net |
Commit message
Description of the change
This is rework of ship transportation - with goal to make it more effective (f.e. eliminate unexplained moves of ships and so on).
But I had to modify also regression tests, because in some situation ships behave differently, f.e. in current code in some situation first ship in m_ships is sent, or ship is sent to first port in m_ports, not considering distance or so.
I think there will have to be some discussion about this design :)
Oh, also debug window for port was improved, but still debug window for ship is missing, or rather not accessible in normal way... Once I had a ship on coast and was able to open debug window of ship. Obviously land and sea has bit different logic for opening debug wndows...
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/economy/fleet.cc' |
2 | --- src/economy/fleet.cc 2015-02-05 12:11:20 +0000 |
3 | +++ src/economy/fleet.cc 2015-08-30 19:39:04 +0000 |
4 | @@ -337,6 +337,28 @@ |
5 | return true; |
6 | } |
7 | |
8 | +uint32_t Fleet::count_ships(){ |
9 | + return m_ships.size(); |
10 | +} |
11 | + |
12 | +uint32_t Fleet::count_ships_heading_here(EditorGameBase & egbase, PortDock * port){ |
13 | + uint32_t ships_on_way = 0; |
14 | + for (uint16_t s = 0; s < m_ships.size(); s += 1){ |
15 | + if (m_ships[s]->get_destination(egbase) == port){ |
16 | + ships_on_way += 1; |
17 | + } |
18 | + } |
19 | + |
20 | + return ships_on_way; |
21 | +} |
22 | + |
23 | +uint32_t Fleet::count_ports(){ |
24 | + return m_ports.size(); |
25 | +} |
26 | +bool Fleet::get_act_pending(){ |
27 | + return m_act_pending; |
28 | +} |
29 | + |
30 | void Fleet::add_neighbours(PortDock & pd, std::vector<RoutingNodeNeighbour> & neighbours) |
31 | { |
32 | uint32_t idx = std::find(m_ports.begin(), m_ports.end(), &pd) - m_ports.begin(); |
33 | @@ -576,6 +598,25 @@ |
34 | } |
35 | |
36 | /** |
37 | + * Search among the docks of the fleet for the one that has matches given coordinates. |
38 | + * Intended for a ship querying in what portdock it is now. |
39 | + * |
40 | + * @return the dock, or 0 if not found. |
41 | + */ |
42 | +PortDock * Fleet::get_dock(EditorGameBase & egbase, Coords field_coords) const |
43 | +{ |
44 | + for (PortDock * temp_port : m_ports) { |
45 | + for (Coords tmp_coords : temp_port->get_positions(egbase)) { |
46 | + if (tmp_coords == field_coords){ |
47 | + return temp_port; |
48 | + } |
49 | + } |
50 | + } |
51 | + |
52 | + return nullptr; |
53 | +} |
54 | + |
55 | +/** |
56 | * @return an arbitrary dock of the fleet, or 0 if the fleet has no docks |
57 | */ |
58 | PortDock * Fleet::get_arbitrary_dock() const |
59 | @@ -590,8 +631,9 @@ |
60 | */ |
61 | void Fleet::update(EditorGameBase & egbase) |
62 | { |
63 | - if (m_act_pending) |
64 | + if (m_act_pending){ |
65 | return; |
66 | + } |
67 | |
68 | if (upcast(Game, game, &egbase)) { |
69 | schedule_act(*game, 100); |
70 | @@ -608,6 +650,7 @@ |
71 | void Fleet::act(Game & game, uint32_t /* data */) |
72 | { |
73 | m_act_pending = false; |
74 | + |
75 | if (!active()) { |
76 | // If we are here, most likely act() was called by a port with waiting wares or an expedition ready |
77 | // although there are still no ships. We can't handle it now, so we reschedule the act() |
78 | @@ -618,63 +661,222 @@ |
79 | |
80 | molog("Fleet::act\n"); |
81 | |
82 | - for (Ship * temp_ship : m_ships) { |
83 | - Ship & ship = *temp_ship; |
84 | - if (ship.get_nritems() > 0 && !ship.get_destination(game)) { |
85 | - molog("Ship %u has items\n", ship.serial()); |
86 | - bool found_dst = false; |
87 | - for (ShippingItem& temp_item : ship.m_items) { |
88 | - PortDock * dst = temp_item.get_destination(game); |
89 | - if (dst) { |
90 | - molog("... sending to portdock %u\n", dst->serial()); |
91 | - ship.set_destination(game, *dst); |
92 | - found_dst = true; |
93 | - break; |
94 | - } |
95 | - } |
96 | - // If we end here, we just send the ship to the first port - maybe the old port got destroyed |
97 | - if (!found_dst) { |
98 | - assert(!m_ports.empty()); |
99 | - ship.set_destination(game, *m_ports[0]); |
100 | - } |
101 | - } |
102 | - } |
103 | - |
104 | - for (uint32_t i = 0; i < m_ports.size(); ++i) { |
105 | - PortDock & pd = *m_ports[i]; |
106 | - |
107 | - if (pd.get_need_ship()) { |
108 | - molog("Port %u needs ship\n", pd.serial()); |
109 | - |
110 | - bool success = false; |
111 | - for (Ship * temp_ship : m_ships) { |
112 | - Ship & ship = *temp_ship; |
113 | - // Check whether ship is in TRANSPORT state |
114 | - if (ship.get_ship_state() != Ship::TRANSPORT) |
115 | - continue; |
116 | - |
117 | - PortDock * dst = ship.get_destination(game); |
118 | - // Check if ship has currently a different destination |
119 | - if (dst && dst != &pd) |
120 | - continue; |
121 | - if (ship.get_nritems() >= ship.descr().get_capacity()) |
122 | - continue; |
123 | - |
124 | - molog("... ship %u takes care of it\n", ship.serial()); |
125 | - |
126 | - if (!dst) |
127 | - ship.set_destination(game, pd); |
128 | - |
129 | - success = true; |
130 | - break; |
131 | - } |
132 | - |
133 | - if (!success) { |
134 | - schedule_act(game, 5000); // retry in the next time |
135 | - m_act_pending = true; |
136 | - break; |
137 | - } |
138 | - } |
139 | + // we need to calculate what ship is to be send to which port |
140 | + // for this we will have temporary data structure with format |
141 | + // <<ship,port>,score> |
142 | + // where ship and port are not objects but positions in m_ports and m_ships |
143 | + // this is to allow native hashing |
144 | + std::map<std::pair<uint16_t, uint16_t>, uint16_t> scores; |
145 | + |
146 | + // so we will identify all pairs: idle ship : ports, and score all such |
147 | + // pairs. We consider |
148 | + // - count of wares onboard, first ware (oldest) is counted as 8 (prioritization) |
149 | + // (counting wares for particular port only) |
150 | + // - count wares waiting at the port/3 |
151 | + // - distance between ship and a port (0-10 points, the closer the more points) |
152 | + // - is another ship heading there right now? |
153 | + |
154 | + // at the end we must know if requrests of all ports asking for ship were addressed |
155 | + // if any unsatisfied, we must schedule new run of this function |
156 | + // when we send a ship there, the port is removed from list |
157 | + std::list<uint16_t> waiting_ports; |
158 | + |
159 | + // this is just helper - first member of scores map |
160 | + std::pair<uint16_t, uint16_t> mapping; //ship number, port number |
161 | + |
162 | + // first we go over ships - idle ones (=without destination) |
163 | + // then over wares on these ships and create first ship-port |
164 | + // pairs with score |
165 | + for (uint16_t s = 0; s < m_ships.size(); s += 1){ |
166 | + if (m_ships[s]->get_destination(game)) { |
167 | + continue; |
168 | + } |
169 | + if (m_ships[s]->get_ship_state() != Ship::TRANSPORT) { |
170 | + continue; // in expedition obviously |
171 | + } |
172 | + |
173 | + for (uint16_t i = 0; i < m_ships[s]->get_nritems(); i += 1){ |
174 | + PortDock * dst = m_ships[s]->m_items[i].get_destination(game); |
175 | + if (!dst) { |
176 | + // if wares without destination on ship without destination |
177 | + // such ship can be send to any port, and should be sent |
178 | + // to some port, so we add 1 point to score for each port |
179 | + for (uint16_t p = 0; p < m_ports.size(); p += 1){ |
180 | + mapping.first = s; |
181 | + mapping.second = p; |
182 | + scores[mapping] += 1; |
183 | + } |
184 | + continue; |
185 | + } |
186 | + |
187 | + bool destination_found = false; //just a functional check |
188 | + for (uint16_t p = 0; p < m_ports.size(); p += 1){ |
189 | + if (m_ports[p] == m_ships[s]->m_items[i].get_destination(game)){ |
190 | + mapping.first = s; |
191 | + mapping.second = p; |
192 | + scores[mapping] += (i == 0)?8:1; |
193 | + destination_found = true; |
194 | + } |
195 | + } |
196 | + if (!destination_found){ |
197 | + // Perhaps the throw here is too strong |
198 | + // we can still remove it before stable release if it proves too much |
199 | + // during my testing this situation never happened |
200 | + throw wexception("A ware with destination that does not match any of player's" |
201 | + " ports, ship %u, ware's destination: %u", |
202 | + m_ships[s]->serial(), |
203 | + m_ships[s]->m_items[i].get_destination(game)->serial()); |
204 | + } |
205 | + } |
206 | + } |
207 | + |
208 | + // now opposite aproach - we go over ports to find out those that have wares |
209 | + // waiting for ship then find candidate ships to satisfy the requests |
210 | + for (uint16_t p = 0; p < m_ports.size(); p += 1){ |
211 | + PortDock & pd = *m_ports[p]; |
212 | + if (!pd.get_need_ship()){ |
213 | + continue; |
214 | + } |
215 | + |
216 | + // general stategy is "one ship for port is enough", but sometimes |
217 | + // amount of ware waiting for ship is too high |
218 | + if (count_ships_heading_here(game, &pd) * 25 > pd.count_waiting()) { |
219 | + continue; |
220 | + } |
221 | + |
222 | + waiting_ports.push_back(p); |
223 | + |
224 | + // scoring and entering the pair into scores (or increasing existing |
225 | + // score if the pair is already there) |
226 | + for (uint16_t s = 0; s < m_ships.size(); s += 1){ |
227 | + |
228 | + if (m_ships[s]->get_destination(game)) { |
229 | + continue; // already has destination |
230 | + } |
231 | + |
232 | + if (m_ships[s]->get_ship_state() != Ship::TRANSPORT) { |
233 | + continue; // in expedition obviously |
234 | + } |
235 | + |
236 | + mapping.first = s; |
237 | + mapping.second = p; |
238 | + // folowing aproximately considers free capacity of a ship |
239 | + scores[mapping] += ((m_ships[s]->get_nritems() > 15)?1:3) |
240 | + + |
241 | + std::min( |
242 | + m_ships[s]->descr().get_capacity() - m_ships[s]->get_nritems(), |
243 | + m_ports[p]->count_waiting()) / 3; |
244 | + } |
245 | + } |
246 | + |
247 | + //now adding score for distance |
248 | + for (std::pair<std::pair<uint16_t, uint16_t>, uint16_t> ship_port_relation : scores) { |
249 | + |
250 | + // here we get distance ship->port |
251 | + // possibilities are: |
252 | + // - we are in port and it is the same as target port |
253 | + // - we are in other port, then we use get_dock() function to fetch precalculated path |
254 | + // - if above fails, we calculate path "manually" |
255 | + int16_t route_length = -1; |
256 | + |
257 | + PortDock * current_portdock = get_dock(game, m_ships[ship_port_relation.first.first]->get_position()); |
258 | + |
259 | + if (current_portdock) { // we try to use precalculated paths of game |
260 | + |
261 | + // we are in the same portdock |
262 | + if (current_portdock == m_ports[ship_port_relation.first.second]) { |
263 | + route_length = 0; |
264 | + } else { // it is different portdock then |
265 | + Path tmp_path; |
266 | + if (get_path(*current_portdock, *m_ports[ship_port_relation.first.second], tmp_path)) { |
267 | + route_length = tmp_path.get_nsteps(); |
268 | + } |
269 | + } |
270 | + } |
271 | + |
272 | + // most probably the ship is not in a portdock (should not happen frequently) |
273 | + if (route_length == -1) { |
274 | + route_length = m_ships[ship_port_relation.first.first]->calculate_sea_route |
275 | + (game, *m_ports[ship_port_relation.first.second]); |
276 | + } |
277 | + |
278 | + // now we have length of route, so we need to calculate score |
279 | + int16_t score_for_distance = 0; |
280 | + if (route_length < 3) { |
281 | + score_for_distance = 10; |
282 | + } else { |
283 | + score_for_distance = 8 - route_length / 50; |
284 | + } |
285 | + // must not be negative |
286 | + score_for_distance = (score_for_distance < 0)?0:score_for_distance; |
287 | + |
288 | + scores[ship_port_relation.first] += score_for_distance; |
289 | + } |
290 | + |
291 | + // looking for best scores and sending ships accordingly |
292 | + uint16_t best_ship = 0; |
293 | + uint16_t best_port = 0; |
294 | + uint16_t best_score; |
295 | + |
296 | + // after sending a ship we will remove one or more items from scores |
297 | + while (!scores.empty()){ |
298 | + best_score = 0; |
299 | + |
300 | + // searching for combination with highest score |
301 | + for (std::pair<std::pair<uint16_t, uint16_t>, uint16_t> combination : scores) { |
302 | + if (combination.second > best_score){ |
303 | + best_score = combination.second; |
304 | + best_ship = combination.first.first; |
305 | + best_port = combination.first.second; |
306 | + } |
307 | + } |
308 | + if (best_score == 0){ |
309 | + // this is check of correctnes of this algorithm, this should not happen |
310 | + throw wexception("Fleet::act(): No port-destination pair selected or its score is zero"); |
311 | + } |
312 | + |
313 | + // making sure the winner has no destination set |
314 | + assert(!m_ships[best_ship]->get_destination(game)); |
315 | + |
316 | + // now actual setting destination for "best ship" |
317 | + m_ships[best_ship]->set_destination(game, *m_ports[best_port]); |
318 | + molog("... ship %u sent to port %u, wares onboard: %2d, the port is asking for a ship: %s\n", |
319 | + m_ships[best_ship]->serial(), |
320 | + m_ports[best_port]->serial(), |
321 | + m_ships[best_ship]->get_nritems(), |
322 | + (m_ports[best_port]->get_need_ship())?"yes":"no"); |
323 | + |
324 | + // pruning the scores table |
325 | + // the ship that was just sent somewhere cannot be send elsewhere :) |
326 | + for (auto it = scores.cbegin(); it != scores.cend();){ |
327 | + |
328 | + // decreasing score for target port as there was a ship just sent there |
329 | + if (it->first.second == best_port) { |
330 | + mapping.first = it->first.first; |
331 | + mapping.second = it->first.second; |
332 | + scores[mapping] /= 2; |
333 | + // just make sure it is nonzero |
334 | + scores[mapping] = (scores[mapping] == 0)?1:scores[mapping]; |
335 | + } |
336 | + |
337 | + // but removing all pairs where best ship is participating as it is not available anymore |
338 | + // (because it was sent to "best port") |
339 | + if (it->first.first == best_ship) { |
340 | + scores.erase(it++); |
341 | + } else { |
342 | + ++it; |
343 | + } |
344 | + } |
345 | + |
346 | + // also removing the port from waiting_ports |
347 | + waiting_ports.remove(best_port); |
348 | + } |
349 | + |
350 | + if (!waiting_ports.empty()) { |
351 | + molog("... there are %" PRIuS " ports requesting ship(s) we cannot satisfy yet\n", |
352 | + waiting_ports.size()); |
353 | + schedule_act(game, 5000); // retry next time |
354 | + m_act_pending = true; |
355 | } |
356 | } |
357 | |
358 | @@ -682,8 +884,7 @@ |
359 | { |
360 | MapObject::log_general_info(egbase); |
361 | |
362 | - molog |
363 | - ("%" PRIuS " ships and %" PRIuS " ports\n", m_ships.size(), m_ports.size()); |
364 | + molog ("%" PRIuS " ships and %" PRIuS " ports\n", m_ships.size(), m_ports.size()); |
365 | } |
366 | |
367 | #define FLEET_SAVEGAME_VERSION 4 |
368 | |
369 | === modified file 'src/economy/fleet.h' |
370 | --- src/economy/fleet.h 2014-09-10 08:55:04 +0000 |
371 | +++ src/economy/fleet.h 2015-08-30 19:39:04 +0000 |
372 | @@ -24,6 +24,7 @@ |
373 | |
374 | #include "base/macros.h" |
375 | #include "logic/instances.h" |
376 | +#include "logic/widelands_geometry.h" |
377 | |
378 | namespace Widelands { |
379 | |
380 | @@ -79,6 +80,7 @@ |
381 | Player & owner() const {return m_owner;} |
382 | |
383 | PortDock * get_dock(Flag & flag) const; |
384 | + PortDock * get_dock(EditorGameBase &, Coords) const; |
385 | PortDock * get_arbitrary_dock() const; |
386 | void set_economy(Economy * e); |
387 | |
388 | @@ -98,6 +100,11 @@ |
389 | bool get_path(PortDock & start, PortDock & end, Path & path); |
390 | void add_neighbours(PortDock & pd, std::vector<RoutingNodeNeighbour> & neighbours); |
391 | |
392 | + uint32_t count_ships(); |
393 | + uint32_t count_ships_heading_here(EditorGameBase & egbase, PortDock * port); |
394 | + uint32_t count_ports(); |
395 | + bool get_act_pending(); |
396 | + |
397 | protected: |
398 | void act(Game &, uint32_t data) override; |
399 | |
400 | |
401 | === modified file 'src/economy/portdock.cc' |
402 | --- src/economy/portdock.cc 2015-05-05 20:00:21 +0000 |
403 | +++ src/economy/portdock.cc 2015-08-30 19:39:04 +0000 |
404 | @@ -364,8 +364,9 @@ |
405 | m_waiting.pop_back(); |
406 | } |
407 | |
408 | - if (m_waiting.empty()) |
409 | + if (m_waiting.empty()){ |
410 | set_need_ship(game, false); |
411 | + } |
412 | } |
413 | |
414 | m_fleet->update(game); |
415 | @@ -408,6 +409,13 @@ |
416 | return count; |
417 | } |
418 | |
419 | +/** |
420 | + * Return the number of wares or workers waiting at the dock. |
421 | + */ |
422 | +uint32_t PortDock::count_waiting() { |
423 | + return m_waiting.size(); |
424 | +} |
425 | + |
426 | /// \returns whether an expedition was started or is even ready |
427 | bool PortDock::expedition_started() { |
428 | return (m_expedition_bootstrap.get() != nullptr) || m_expedition_ready; |
429 | |
430 | === modified file 'src/economy/portdock.h' |
431 | --- src/economy/portdock.h 2014-09-10 10:18:46 +0000 |
432 | +++ src/economy/portdock.h 2015-08-30 19:39:04 +0000 |
433 | @@ -113,6 +113,7 @@ |
434 | void log_general_info(const EditorGameBase &) override; |
435 | |
436 | uint32_t count_waiting(WareWorker waretype, WareIndex wareindex); |
437 | + uint32_t count_waiting(); |
438 | |
439 | // Returns true if a expedition is started or ready to be send out. |
440 | bool expedition_started(); |
441 | |
442 | === modified file 'src/logic/ship.cc' |
443 | --- src/logic/ship.cc 2015-04-07 20:56:02 +0000 |
444 | +++ src/logic/ship.cc 2015-08-30 19:39:04 +0000 |
445 | @@ -242,18 +242,8 @@ |
446 | |
447 | PortDock* dst = get_destination(game); |
448 | if (!dst) { |
449 | - molog("ship_update: No destination anymore.\n"); |
450 | - if (m_items.empty()) |
451 | - return false; |
452 | - molog("but it has wares....\n"); |
453 | - pop_task(game); |
454 | - PortDock* other_dock = m_fleet->get_arbitrary_dock(); |
455 | - // TODO(sirver): What happens if there is no port anymore? |
456 | - if (other_dock) { |
457 | - set_destination(game, *other_dock); |
458 | - } else { |
459 | - start_task_idle(game, descr().main_animation(), 2000); |
460 | - } |
461 | + //here we just do nothing, this is usually OK |
462 | + start_task_idle(game, descr().main_animation(), 10000); |
463 | return true; |
464 | } |
465 | |
466 | @@ -729,7 +719,7 @@ |
467 | * @note This is supposed to be called only from the scheduling code of @ref Fleet. |
468 | */ |
469 | void Ship::set_destination(Game& game, PortDock& pd) { |
470 | - molog("set_destination to %u (currently %" PRIuS " items)\n", pd.serial(), m_items.size()); |
471 | + molog("set_destination / sending to portdock %u (carrying %" PRIuS " items)\n", pd.serial(), m_items.size()); |
472 | m_destination = &pd; |
473 | send_signal(game, "wakeup"); |
474 | } |
475 | @@ -755,9 +745,9 @@ |
476 | } |
477 | |
478 | /** |
479 | - * Find a path to the dock @p pd and follow it without using precomputed paths. |
480 | + * Find a path to the dock @p pd, returns its length, and the path optionally. |
481 | */ |
482 | -void Ship::start_task_movetodock(Game& game, PortDock& pd) { |
483 | +uint32_t Ship::calculate_sea_route(Game& game, PortDock& pd, Path* finalpath){ |
484 | Map& map = game.map(); |
485 | StepEvalAStar se(pd.get_warehouse()->get_position()); |
486 | se.m_swim = true; |
487 | @@ -772,15 +762,47 @@ |
488 | FCoords cur; |
489 | while (astar.step(cur, cost)) { |
490 | if (cur.field->get_immovable() == &pd) { |
491 | - Path path; |
492 | - astar.pathto(cur, path); |
493 | - start_task_movepath(game, path, descr().get_sail_anims()); |
494 | - return; |
495 | + if (finalpath){ |
496 | + astar.pathto(cur, *finalpath); |
497 | + return finalpath->get_nsteps(); |
498 | + } else { |
499 | + Path path; |
500 | + astar.pathto(cur, path); |
501 | + return path.get_nsteps(); |
502 | + } |
503 | } |
504 | } |
505 | |
506 | - molog("start_task_movedock: Failed to find path!\n"); |
507 | - start_task_idle(game, descr().main_animation(), 5000); |
508 | + molog(" calculate_sea_distance: Failed to find path!\n"); |
509 | + return std::numeric_limits<uint32_t>::max(); |
510 | + |
511 | +} |
512 | + |
513 | +/** |
514 | + * Find a path to the dock @p pd and follow it without using precomputed paths. |
515 | + */ |
516 | +void Ship::start_task_movetodock(Game& game, PortDock& pd) { |
517 | + Path path; |
518 | + |
519 | + uint32_t const distance = calculate_sea_route(game, pd, &path); |
520 | + |
521 | + // if we get a meaningfull result |
522 | + if (distance < std::numeric_limits<uint32_t>::max()) { |
523 | + start_task_movepath(game, path, descr().get_sail_anims()); |
524 | + return; |
525 | + } else { |
526 | + log("start_task_movedock: Failed to find a path: ship at %3dx%3d to port at: %3dx%3d\n", |
527 | + get_position().x, |
528 | + get_position().y, |
529 | + pd.get_positions(game)[0].x, |
530 | + pd.get_positions(game)[0].y); |
531 | + //this should not happen, but in theory there could be some inconstinency |
532 | + //I (tiborb) failed to invoke this situation when testing so |
533 | + //I am not sure if following line behaves allright |
534 | + get_fleet()->update(game); |
535 | + start_task_idle(game, descr().main_animation(), 5000); |
536 | + } |
537 | + |
538 | } |
539 | |
540 | /// Prepare everything for the coming exploration |
541 | |
542 | === modified file 'src/logic/ship.h' |
543 | --- src/logic/ship.h 2015-06-10 06:46:40 +0000 |
544 | +++ src/logic/ship.h 2015-08-30 19:39:04 +0000 |
545 | @@ -115,6 +115,8 @@ |
546 | void start_task_movetodock(Game &, PortDock &); |
547 | void start_task_expedition(Game &); |
548 | |
549 | + uint32_t calculate_sea_route(Game& game, PortDock& pd, Path* finalpath = nullptr); |
550 | + |
551 | void log_general_info(const EditorGameBase &) override; |
552 | |
553 | uint32_t get_nritems() const {return m_items.size();} |
554 | |
555 | === modified file 'src/logic/warehouse.cc' |
556 | --- src/logic/warehouse.cc 2015-02-13 22:39:34 +0000 |
557 | +++ src/logic/warehouse.cc 2015-08-30 19:39:04 +0000 |
558 | @@ -28,6 +28,7 @@ |
559 | #include "base/wexception.h" |
560 | #include "economy/economy.h" |
561 | #include "economy/flag.h" |
562 | +#include "economy/fleet.h" |
563 | #include "economy/portdock.h" |
564 | #include "economy/request.h" |
565 | #include "economy/ware_instance.h" |
566 | @@ -1427,8 +1428,25 @@ |
567 | { |
568 | Building::log_general_info(egbase); |
569 | |
570 | - if (descr().get_isport()) |
571 | - molog("Port dock: %u\n", m_portdock ? m_portdock->serial() : 0); |
572 | + if (descr().get_isport()){ |
573 | + PortDock* pd_tmp = m_portdock; |
574 | + if (pd_tmp){ |
575 | + molog("Port dock: %u\n", pd_tmp->serial()); |
576 | + molog("port needs ship: %s\n", (pd_tmp->get_need_ship())?"true":"false"); |
577 | + molog("wares and workers waiting: %u\n", pd_tmp->count_waiting()); |
578 | + molog("exped. in progr.: %s\n", (pd_tmp->expedition_started())?"true":"false"); |
579 | + Fleet* fleet = pd_tmp->get_fleet(); |
580 | + if (fleet) { |
581 | + molog("* fleet: %u\n", fleet->serial()); |
582 | + molog(" ships: %u, ports: %u\n", fleet->count_ships(), fleet->count_ports()); |
583 | + molog(" m_act_pending: %s\n", (fleet->get_act_pending())?"true":"false"); |
584 | + } else { |
585 | + molog("No fleet?!\n"); |
586 | + } |
587 | + } else { |
588 | + molog ("No port dock!?\n"); |
589 | + } |
590 | + } |
591 | } |
592 | |
593 | |
594 | |
595 | === modified file 'test/maps/expedition.wmf/scripting/init.lua' |
596 | --- test/maps/expedition.wmf/scripting/init.lua 2015-06-29 13:49:35 +0000 |
597 | +++ test/maps/expedition.wmf/scripting/init.lua 2015-08-30 19:39:04 +0000 |
598 | @@ -103,6 +103,36 @@ |
599 | sleep(100) |
600 | end |
601 | |
602 | +--function cancel_expedition_or_sink_in_shipwindow() |
603 | + --if second_ship then |
604 | + --ship_to_click=second_ship |
605 | + --elseif first_ship then |
606 | + --ship_to_click=first_ship |
607 | + --else |
608 | + --assert(false) |
609 | + --end |
610 | + |
611 | + --click_on_ship(ship_to_click) |
612 | + --if click_button("cancel_expedition") then |
613 | + --sleep(100) |
614 | + --assert_true(click_button("ok")) |
615 | + --sleep(100) |
616 | + --close_windows() |
617 | + --sleep(100) |
618 | + --print (" DEBUG expedition cancelled") |
619 | + --else |
620 | + --click_on_ship(ship_to_click) |
621 | + --assert_true(click_button("sink")) |
622 | + --sleep(100) |
623 | + --assert_true(click_button("ok")) |
624 | + --sleep(100) |
625 | + --close_windows() |
626 | + --sleep(100) |
627 | + --print (" DEBUG ship sunk") |
628 | + --end |
629 | +--end |
630 | + |
631 | + |
632 | function dismantle_hardener() |
633 | assert_true(click_building(p1, "hardener")) |
634 | assert_true(click_button("dismantle")) |
635 | @@ -151,18 +181,28 @@ |
636 | first_ship = p1:place_bob("ship", map:get_field(10, 10)) |
637 | end |
638 | |
639 | +function create_second_ship() |
640 | + second_ship = p1:place_bob("ship", map:get_field(14, 10)) |
641 | +end |
642 | + |
643 | function create_two_ships() |
644 | create_one_ship() |
645 | - second_ship = p1:place_bob("ship", map:get_field(14, 10)) |
646 | + create_second_ship() |
647 | end |
648 | |
649 | -function test_cancel_started_expedition_on_ship() |
650 | +function test_cancel_started_expedition_on_ship(needs_second_ship) |
651 | sleep(100) |
652 | game.desired_speed = 10 * 1000 |
653 | |
654 | -- Start a new expedition. |
655 | port:start_expedition() |
656 | wait_for_message("Expedition Ready") |
657 | + |
658 | + --if current test requires second ship... |
659 | + if needs_second_ship then |
660 | + create_second_ship() |
661 | + end |
662 | + |
663 | game.desired_speed = 10 * 1000 |
664 | sleep(10000) |
665 | |
666 | @@ -197,15 +237,23 @@ |
667 | game.desired_speed = 10 * 1000 |
668 | sleep(10000) |
669 | |
670 | - first_ship.island_explore_direction="ccw" |
671 | + if first_ship.state=="exp_waiting" then |
672 | + expedition_ship=first_ship |
673 | + elseif second_ship.state=="exp_waiting" then |
674 | + expedition_ship=second_ship |
675 | + else |
676 | + assert(false) |
677 | + end |
678 | + |
679 | + expedition_ship.island_explore_direction="ccw" |
680 | sleep(2000) |
681 | - assert_equal("ccw",first_ship.island_explore_direction) |
682 | + assert_equal("ccw",expedition_ship.island_explore_direction) |
683 | sleep(6000) |
684 | |
685 | stable_save("sailing") |
686 | assert_equal(1, p1:get_workers("builder")) |
687 | |
688 | - cancel_expedition_in_shipwindow(first_ship) |
689 | + cancel_expedition_in_shipwindow(expedition_ship) |
690 | sleep(20000) |
691 | assert_equal(1, p1:get_workers("builder")) |
692 | check_wares_in_port_are_all_there() |
693 | @@ -263,11 +311,20 @@ |
694 | |
695 | port:start_expedition() |
696 | wait_for_message("Expedition Ready") |
697 | - first_ship.island_explore_direction="ccw" |
698 | + |
699 | + if first_ship.state=="exp_waiting" then |
700 | + expedition_ship=first_ship |
701 | + elseif second_ship.state=="exp_waiting" then |
702 | + expedition_ship=second_ship |
703 | + else |
704 | + assert(false) |
705 | + end |
706 | + |
707 | + expedition_ship.island_explore_direction="ccw" |
708 | sleep(2000) |
709 | - assert_equal("ccw",first_ship.island_explore_direction) |
710 | + assert_equal("ccw",expedition_ship.island_explore_direction) |
711 | wait_for_message("Port Space Found") |
712 | - first_ship:build_colonization_port() |
713 | + expedition_ship:build_colonization_port() |
714 | sleep(500) |
715 | assert_equal(1, p1:get_workers("builder")) |
716 | wait_for_message("Port") |
717 | |
718 | === modified file 'test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_one_ship.lua' |
719 | --- test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_one_ship.lua 2013-10-29 20:22:08 +0000 |
720 | +++ test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_one_ship.lua 2015-08-30 19:39:04 +0000 |
721 | @@ -1,5 +1,6 @@ |
722 | run(function() |
723 | create_one_ship() |
724 | |
725 | - test_cancel_started_expedition_on_ship() |
726 | + --false indicates that second ship is not to be created |
727 | + test_cancel_started_expedition_on_ship(false) |
728 | end) |
729 | |
730 | === modified file 'test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_two_ships.lua' |
731 | --- test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_two_ships.lua 2013-10-29 20:22:08 +0000 |
732 | +++ test/maps/expedition.wmf/scripting/test_cancel_started_expedition_on_ship_two_ships.lua 2015-08-30 19:39:04 +0000 |
733 | @@ -1,5 +1,6 @@ |
734 | run(function() |
735 | - create_two_ships() |
736 | + create_one_ship() |
737 | |
738 | - test_cancel_started_expedition_on_ship() |
739 | + --true indicates that second ship is to be created |
740 | + test_cancel_started_expedition_on_ship(true) |
741 | end) |
742 | |
743 | === modified file 'test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua' |
744 | --- test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua 2013-10-29 20:22:08 +0000 |
745 | +++ test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua 2015-08-30 19:39:04 +0000 |
746 | @@ -1,5 +1,47 @@ |
747 | run(function() |
748 | create_two_ships() |
749 | |
750 | - test_cancel_when_port_space_was_reached() |
751 | + |
752 | + sleep(100) |
753 | + game.desired_speed = 10 * 1000 |
754 | + |
755 | + -- Send expedition to port space. |
756 | + port:start_expedition() |
757 | + wait_for_message("Expedition Ready") |
758 | + assert_equal(1, p1:get_workers("builder")) |
759 | + sleep(500) |
760 | + |
761 | + |
762 | + if first_ship.state=="exp_waiting" then |
763 | + expedition_ship=first_ship |
764 | + elseif second_ship.state=="exp_waiting" then |
765 | + expedition_ship=second_ship |
766 | + else |
767 | + assert(false) |
768 | + end |
769 | + |
770 | + expedition_ship.island_explore_direction="ccw" |
771 | + sleep(2000) |
772 | + assert_equal("ccw",expedition_ship.island_explore_direction) |
773 | + wait_for_message("Port Space Found") |
774 | + sleep(500) |
775 | + assert_equal(1, p1:get_workers("builder")) |
776 | + |
777 | + stable_save("reached_port_space") |
778 | + assert_equal(1, p1:get_workers("builder")) |
779 | + |
780 | + cancel_expedition_in_shipwindow(expedition_ship) |
781 | + sleep(20000) |
782 | + assert_equal(1, p1:get_workers("builder")) |
783 | + check_wares_in_port_are_all_there() |
784 | + |
785 | + -- Dismantle the hardener to make sure that the builder is able to do his work. |
786 | + game.desired_speed = 50 * 1000 |
787 | + dismantle_hardener() |
788 | + |
789 | + print("# All Tests passed.") |
790 | + wl.ui.MapView():close() |
791 | + |
792 | + |
793 | + |
794 | end) |
lgtm.