Merge lp:~widelands-dev/widelands/bug-1718745-allows-seafaring into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8530
Proposed branch: lp:~widelands-dev/widelands/bug-1718745-allows-seafaring
Merge into: lp:widelands
Diff against target: 1153 lines (+324/-174)
39 files modified
src/ai/defaultai.cc (+11/-13)
src/ai/defaultai.h (+3/-1)
src/ai/defaultai_seafaring.cc (+3/-6)
src/editor/tools/set_port_space_tool.cc (+2/-2)
src/editor/ui_menus/main_menu_save_map.cc (+1/-0)
src/logic/map.cc (+66/-50)
src/logic/map.h (+21/-9)
src/logic/map_objects/tribes/production_program.cc (+2/-2)
src/map_io/map_port_spaces_packet.cc (+2/-4)
src/map_io/s2map.cc (+24/-38)
src/map_io/s2map.h (+2/-1)
src/scripting/lua_map.cc (+54/-1)
src/scripting/lua_map.h (+3/-0)
src/wui/building_statistics_menu.cc (+2/-2)
src/wui/fieldaction.cc (+4/-2)
test/maps/expedition.wmf/scripting/init.lua (+5/-16)
test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua (+1/-1)
test/maps/expedition.wmf/scripting/test_ship_movement_controls.lua (+1/-1)
test/maps/expedition.wmf/scripting/test_starting_wait_a_while_cancel.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/init.lua (+1/-12)
test/maps/ship_transportation.wmf/scripting/test_many_ships.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_ware_in_portdock.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_worker_in_portdock.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_ware_in_transit.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_worker_in_transit.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_ware_in_portdock.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua (+1/-1)
test/maps/ship_transportation.wmf/scripting/test_rip_ship_before_picking_up_transporting_ware.lua (+2/-2)
test/maps/ship_transportation.wmf/scripting/test_rip_ship_while_transporting_ware.lua (+2/-2)
test/maps/two_ponds.wmf/elemental (+12/-0)
test/maps/two_ponds.wmf/objective (+4/-0)
test/maps/two_ponds.wmf/player_names (+10/-0)
test/maps/two_ponds.wmf/player_position (+5/-0)
test/maps/two_ponds.wmf/port_spaces (+9/-0)
test/maps/two_ponds.wmf/scripting/init.lua (+6/-0)
test/maps/two_ponds.wmf/scripting/test_seafaring.lua (+33/-0)
test/maps/two_ponds.wmf/version (+11/-0)
test/scripting/stable_save.lua (+13/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/bug-1718745-allows-seafaring
Reviewer Review Type Date Requested Status
GunChleoc Needs Resubmitting
Review via email: mp+333233@code.launchpad.net

Commit message

Split new function cleanup_portspaces() from allows_seafaring(), and refactored port spaces checks. allows_seafaring() is now const and used more widely. It also had a use-after-free error that has been fixed now. Exposed 3 new functions to LuaMap:

- get_allows_seafaring
- get_number_of_port_spaces
- set_port_space

To post a comment you must log in.
Revision history for this message
TiborB (tiborb95) wrote :

So If I understand correctly, get_allows_seafaring will be LUA-only, so not usable by AI?

Revision history for this message
GunChleoc (gunchleoc) wrote :

allows_seafaring is already usable by the AI. And now you don't even need a mutable_map - a const map& will do.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2766. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/297640330.
Appveyor build 2578. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2578.

Revision history for this message
GunChleoc (gunchleoc) wrote :

New version - I decided to get a bit more fancy with the checks.

It would be good to have a replacement for the Buildcaps check, because it fails once a tree gets in the way. This is why we need the "force" parameter. Maybe check the terrain height in a certain radius?

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

Continuous integration builds have changed state:

Travis build 2774. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/298027831.
Appveyor build 2586. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2586.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Well, the solution was staring me right in the face in the S2 map loading code that I had deleted... So, we now have a port space check that ignores immovables.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2779. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/298472005.
Appveyor build 2591. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2591.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2914. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/310475159.
Appveyor build 2723. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2723.

Revision history for this message
TiborB (tiborb95) wrote :

See comment in the code

Revision history for this message
GunChleoc (gunchleoc) wrote :

I completely removed the probability for the check - we can afford to run the function every 5 seconds.

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

Continuous integration builds have changed state:

Travis build 2934. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/312284476.
Appveyor build 2743. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2743.

Revision history for this message
TiborB (tiborb95) wrote :

But this might be too much - if we have 16 players, this could mean 3x per second....

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have now added a static variable to control the check frequency. So, it's now globally checked every 5 seconds max.

Revision history for this message
TiborB (tiborb95) wrote :

See comment in diff

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2936. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/312567600.
Appveyor build 2745. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1718745_allows_seafaring-2745.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Duh - thanks for having my back!

Revision history for this message
TiborB (tiborb95) wrote :

Also I cannot find an initialization of the variable :)

Revision history for this message
GunChleoc (gunchleoc) wrote :

Line 8 in the diff: uint32_t DefaultAI::last_seafaring_check_ = 0;

This has to be initialized in the .cc file - at least that has been my experience with static variables that are not constexpr.

Revision history for this message
TiborB (tiborb95) wrote :

But why not few lines below inside the constructor of DefaultAI object?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Because the compiler will not allow initializing it within the constructor.

Revision history for this message
TiborB (tiborb95) wrote :

Because it is static? Do you mean it is shared among all AIs?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Exactly, that is what static means - there is only 1 instance of the variable for all objects. This is also the reason why it can't be initialized in the constructor, because there are no individual copies of the variable, and it can be initialized only once.

Since whether a map is seafaring does not depend on which AI is doing the check, having a global check triggered about ever 5 seconds is sufficient. So, it won't cost extra load when we have lots of AIs, because only one of them will check for everybody.

I'm now wondering if that actually works, since seafaring_economy is not static. It does some check for if there is a ship being built somewhere else in the AI code - is this check actually needed now?

Revision history for this message
TiborB (tiborb95) wrote :

OK, I expected something like this. And this looks like very useful type of variable.

As for seafaring economy, this one can be as well changed into static I think... and/or renamed a bit...

Revision history for this message
TiborB (tiborb95) wrote :

Maybe you could rename swim_coords and visited_positions to more obvious names. Swim coords are coords reachable from previous ports and visited_positions are the one reachable from current port.
It is not easy to come up with short names, but something like this: previous_ports_reachable and current_port_reachable? I know they are long and dont sound good, but...

+ 1 comment in diff

Revision history for this message
GunChleoc (gunchleoc) wrote :

Excellent suggestions, I have implemented them. I also added a simple check for number of port spaces up front, this will speed things up even further on non-seafaring maps.

And ASan found a bug, that's now fixed too :)

review: Needs Resubmitting
Revision history for this message
TiborB (tiborb95) wrote :

I would rename the variable to reachable_from_previous_ports (plural), if there are multiple distinct waters it might scan multiple ports before running into one that can be connected to one of previous ports

Revision history for this message
GunChleoc (gunchleoc) wrote :

Done. Thanks for the review, this branch really would have been a mess without it!

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Error merging this proposal:

Output:
stdout:

stderr:
bzr: ERROR: Parameter 'Merged lp:~widelands-dev/widelands/bug-1718745-allows-seafaring:\nSplit new function cleanup_portspaces() from allows_seafaring(), and refactored port spaces checks. allows_seafaring() is now const and used more widely. It also had a use-after-free error that has been fixed now. Exposed 3 new functions to LuaMap:\n\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0- get_allows_seafaring\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0- get_number_of_port_spaces\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0- set_port_space' encoding is unsupported by ascii application locale.

Revision history for this message
GunChleoc (gunchleoc) wrote :

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ai/defaultai.cc'
2--- src/ai/defaultai.cc 2017-12-03 08:23:27 +0000
3+++ src/ai/defaultai.cc 2017-12-11 06:56:51 +0000
4@@ -82,6 +82,9 @@
5 DefaultAI::WeakImpl DefaultAI::weak_impl;
6 DefaultAI::VeryWeakImpl DefaultAI::very_weak_impl;
7
8+uint32_t DefaultAI::last_seafaring_check_ = 0;
9+bool DefaultAI::map_allows_seafaring_ = false;
10+
11 /// Constructor of DefaultAI
12 DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, Widelands::AiType const t)
13 : ComputerPlayer(ggame, pid),
14@@ -109,7 +112,6 @@
15 enemysites_check_delay_(30),
16 resource_necessity_water_needed_(false),
17 highest_nonmil_prio_(0),
18- seafaring_economy(false),
19 expedition_ship_(kNoShip) {
20
21 // Subscribe to NoteFieldPossession.
22@@ -1951,11 +1953,9 @@
23
24 const Map& map = game().map();
25
26- if (gametime % 5 == 0) {
27- // TODO(unknown): Counting port spaces is very primitive way for this
28- // there should be better alternative f.e. like map::allows_seafaring()
29- // function but simplier
30- seafaring_economy = map.get_port_spaces().size() >= 2;
31+ if (gametime > last_seafaring_check_ + 20000U) {
32+ map_allows_seafaring_ = map.allows_seafaring();
33+ last_seafaring_check_ = gametime;
34 }
35
36 for (int32_t i = 0; i < 4; ++i)
37@@ -2663,7 +2663,7 @@
38 assert(!bo.is(BuildingAttribute::kShipyard));
39 } else if (bo.is(BuildingAttribute::kShipyard)) {
40 assert(bo.new_building == BuildingNecessity::kAllowed);
41- if (!seafaring_economy) {
42+ if (!map_allows_seafaring_) {
43 continue;
44 }
45 } else {
46@@ -2702,12 +2702,10 @@
47 bf->unowned_mines_spots_nearby) { // not close to mountains
48 prio -= std::abs(management_data.get_military_number_at(104)) / 5;
49 }
50- }
51-
52- else if (bo.is(BuildingAttribute::kShipyard)) {
53+ } else if (bo.is(BuildingAttribute::kShipyard)) {
54 // for now AI builds only one shipyard
55 assert(bo.total_count() == 0);
56- if (bf->open_water_nearby > 3 && seafaring_economy) {
57+ if (bf->open_water_nearby > 3 && map_allows_seafaring_) {
58 prio += productionsites.size() * 5 +
59 bf->open_water_nearby *
60 std::abs(management_data.get_military_number_at(109)) / 10;
61@@ -4253,7 +4251,7 @@
62 return BuildingNecessity::kForbidden;
63 }
64
65- if (bo.is(BuildingAttribute::kPort) && !seafaring_economy) {
66+ if (bo.is(BuildingAttribute::kPort) && !map_allows_seafaring_) {
67 bo.new_building_overdue = 0;
68 bo.primary_priority = 0;
69 return BuildingNecessity::kForbidden;
70@@ -4331,7 +4329,7 @@
71 }
72
73 // Perhaps buildings are not allowed because the map is no seafaring
74- if (purpose == PerfEvaluation::kForConstruction && !seafaring_economy &&
75+ if (purpose == PerfEvaluation::kForConstruction && !map_allows_seafaring_ &&
76 bo.is(BuildingAttribute::kNeedsSeafaring)) {
77 return BuildingNecessity::kForbidden;
78 }
79
80=== modified file 'src/ai/defaultai.h'
81--- src/ai/defaultai.h 2017-11-27 21:21:06 +0000
82+++ src/ai/defaultai.h 2017-12-11 06:56:51 +0000
83@@ -385,7 +385,9 @@
84
85 // seafaring related
86 enum { kReprioritize, kStopShipyard, kStapShipyard };
87- bool seafaring_economy; // false by default, until first port space is found
88+ static uint32_t last_seafaring_check_;
89+ // False by default, until Map::allows_seafaring() is true
90+ static bool map_allows_seafaring_;
91 uint32_t expedition_ship_;
92 uint32_t expedition_max_duration;
93 std::vector<int16_t> marine_task_queue;
94
95=== modified file 'src/ai/defaultai_seafaring.cc'
96--- src/ai/defaultai_seafaring.cc 2017-11-20 07:54:19 +0000
97+++ src/ai/defaultai_seafaring.cc 2017-12-11 06:56:51 +0000
98@@ -104,8 +104,7 @@
99 // - build a ship
100 // - start preparation for expedition
101 bool DefaultAI::marine_main_decisions() {
102-
103- if (!seafaring_economy) {
104+ if (!map_allows_seafaring_) {
105 set_taskpool_task_time(kNever, SchedulerTaskId::KMarineDecisions);
106 return false;
107 }
108@@ -226,9 +225,8 @@
109
110 // This identifies ships that are waiting for command
111 bool DefaultAI::check_ships(uint32_t const gametime) {
112-
113- if (!seafaring_economy) {
114- set_taskpool_task_time(std::numeric_limits<int32_t>::max(), SchedulerTaskId::kCheckShips);
115+ if (!map_allows_seafaring_) {
116+ set_taskpool_task_time(kNever, SchedulerTaskId::kCheckShips);
117 return false;
118 }
119
120@@ -402,7 +400,6 @@
121 if (type == NewShip::kBuilt) {
122 marine_task_queue.push_back(kStopShipyard);
123 } else {
124- seafaring_economy = true;
125 if (ship.state_is_expedition()) {
126 if (expedition_ship_ == kNoShip) {
127 // OK, this ship is in expedition
128
129=== modified file 'src/editor/tools/set_port_space_tool.cc'
130--- src/editor/tools/set_port_space_tool.cc 2017-09-15 19:18:08 +0000
131+++ src/editor/tools/set_port_space_tool.cc 2017-12-11 06:56:51 +0000
132@@ -65,7 +65,7 @@
133 do {
134 // check if field is valid
135 if (port_tool_nodecaps(mr.location(), *map) != NodeCaps::CAPS_NONE) {
136- map->set_port_space(mr.location(), true);
137+ map->set_port_space(world, mr.location(), true);
138 Area<FCoords> a(mr.location(), 0);
139 map->recalc_for_field_area(world, a);
140 ++nr;
141@@ -106,7 +106,7 @@
142 do {
143 // check if field is valid
144 if (port_tool_nodecaps(mr.location(), *map)) {
145- map->set_port_space(mr.location(), false);
146+ map->set_port_space(world, mr.location(), false);
147 Area<FCoords> a(mr.location(), 0);
148 map->recalc_for_field_area(world, a);
149 ++nr;
150
151=== modified file 'src/editor/ui_menus/main_menu_save_map.cc'
152--- src/editor/ui_menus/main_menu_save_map.cc 2017-11-24 21:34:17 +0000
153+++ src/editor/ui_menus/main_menu_save_map.cc 2017-12-11 06:56:51 +0000
154@@ -280,6 +280,7 @@
155 g_fs->create_sub_file_system(tmp_name, binary ? FileSystem::ZIP : FileSystem::DIR));
156
157 // Recompute seafaring tag
158+ map->cleanup_port_spaces(egbase.world());
159 if (map->allows_seafaring()) {
160 map->add_tag("seafaring");
161 } else {
162
163=== modified file 'src/logic/map.cc'
164--- src/logic/map.cc 2017-11-05 19:59:33 +0000
165+++ src/logic/map.cc 2017-12-11 06:56:51 +0000
166@@ -546,7 +546,7 @@
167 }
168 }
169
170-NodeCaps Map::get_max_nodecaps(const World& world, const FCoords& fc) {
171+NodeCaps Map::get_max_nodecaps(const World& world, const FCoords& fc) const {
172 NodeCaps caps = calc_nodecaps_pass1(world, fc, false);
173 caps = calc_nodecaps_pass2(world, fc, false, caps);
174 return caps;
175@@ -937,7 +937,7 @@
176 f.field->caps = calc_nodecaps_pass1(world, f, true);
177 }
178
179-NodeCaps Map::calc_nodecaps_pass1(const World& world, const FCoords& f, bool consider_mobs) {
180+NodeCaps Map::calc_nodecaps_pass1(const World& world, const FCoords& f, bool consider_mobs) const {
181 uint8_t caps = CAPS_NONE;
182
183 // 1a) Get all the neighbours to make life easier
184@@ -1058,7 +1058,7 @@
185 NodeCaps Map::calc_nodecaps_pass2(const World& world,
186 const FCoords& f,
187 bool consider_mobs,
188- NodeCaps initcaps) {
189+ NodeCaps initcaps) const {
190 uint8_t caps = consider_mobs ? f.field->caps : static_cast<uint8_t>(initcaps);
191
192 // NOTE This dependency on the bottom-right neighbour is the reason
193@@ -1166,7 +1166,7 @@
194 bool avoidnature,
195 bool* ismine,
196 bool consider_mobs,
197- NodeCaps initcaps) {
198+ NodeCaps initcaps) const {
199 if (consider_mobs) {
200 if (!(f.field->get_caps() & MOVECAPS_WALK))
201 return BaseImmovable::NONE;
202@@ -1240,7 +1240,7 @@
203 * The array \p dirs must have length \p length, where \p length is
204 * the length of the cycle.
205 */
206-bool Map::is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs) {
207+bool Map::is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs) const {
208 FCoords f = start;
209 bool prev_walkable = start.field->get_caps() & MOVECAPS_WALK;
210 uint32_t alternations = 0;
211@@ -1304,18 +1304,27 @@
212 return portdock;
213 }
214
215+bool Map::is_port_space_allowed(const World& world, const FCoords& fc) const {
216+ return (get_max_nodecaps(world, fc) & BUILDCAPS_SIZEMASK) == BUILDCAPS_BIG && !find_portdock(fc).empty();
217+}
218+
219 /// \returns true, if Coordinates are in port space list
220 bool Map::is_port_space(const Coords& c) const {
221 return port_spaces_.count(c);
222 }
223
224-/// Set or unset a space as port space
225-void Map::set_port_space(Coords c, bool allowed) {
226- if (allowed) {
227- port_spaces_.insert(c);
228+bool Map::set_port_space(const World& world, const Coords& c, bool set, bool force) {
229+ bool success = false;
230+ if (set) {
231+ success = force || is_port_space_allowed(world, get_fcoords(c));
232+ if (success) {
233+ port_spaces_.insert(c);
234+ }
235 } else {
236 port_spaces_.erase(c);
237+ success = true;
238 }
239+ return success;
240 }
241
242 /**
243@@ -1950,60 +1959,67 @@
244 check_neighbour_heights(n[i], area);
245 }
246
247-/*
248-===========
249-Map::allows_seafaring()
250-
251-This function checks if there are two ports that are reachable
252-for each other - then the map is seafaring.
253-=============
254-*/
255-bool Map::allows_seafaring() {
256- Map::PortSpacesSet port_spaces = get_port_spaces();
257- std::vector<Coords> portdocks;
258- std::set<Coords> swim_coords;
259-
260- for (const Coords& c : port_spaces) {
261- std::queue<Coords> q_positions;
262- std::set<Coords> visited_positions;
263+bool Map::allows_seafaring() const {
264+
265+ // There need to be at least 2 port spaces for seafaring to make sense
266+ if (get_port_spaces().size() < 2) {
267+ return false;
268+ }
269+
270+ std::set<Coords> reachable_from_previous_ports;
271+
272+ for (const Coords& c : get_port_spaces()) {
273+ std::queue<Coords> positions_to_check;
274+ std::set<Coords> reachable_from_current_port;
275 FCoords fc = get_fcoords(c);
276- portdocks = find_portdock(fc);
277-
278- /* remove the port space if it is not longer valid port space */
279- if ((fc.field->get_caps() & BUILDCAPS_SIZEMASK) != BUILDCAPS_BIG || portdocks.empty()) {
280- set_port_space(c, false);
281- continue;
282- }
283-
284- for (const Coords& portdock : portdocks) {
285- visited_positions.insert(portdock);
286- q_positions.push(portdock);
287- }
288-
289- while (!q_positions.empty()) {
290- const Coords& swim_coord = q_positions.front();
291- q_positions.pop();
292+
293+ // Get portdock slots for this port
294+ for (const Coords& portdock : find_portdock(fc)) {
295+ reachable_from_current_port.insert(portdock);
296+ positions_to_check.push(portdock);
297+ }
298+
299+ // Pick up all positions that can be reached from the current port
300+ while (!positions_to_check.empty()) {
301+ // Take a copy, because we'll pop it
302+ const Coords current_position = positions_to_check.front();
303+ positions_to_check.pop();
304+
305+ // Found one
306+ if (reachable_from_previous_ports.count(current_position) > 0) {
307+ return true;
308+ }
309+
310+ // Adding the neighbors to the list
311 for (uint8_t i = 1; i <= 6; ++i) {
312 FCoords neighbour;
313- get_neighbour(get_fcoords(swim_coord), i, &neighbour);
314+ get_neighbour(get_fcoords(current_position), i, &neighbour);
315 if ((neighbour.field->get_caps() & (MOVECAPS_SWIM | MOVECAPS_WALK)) == MOVECAPS_SWIM) {
316- if (visited_positions.count(neighbour) == 0) {
317- visited_positions.insert(neighbour);
318- q_positions.push(neighbour);
319+ if (reachable_from_current_port.count(neighbour) == 0) {
320+ reachable_from_current_port.insert(neighbour);
321+ positions_to_check.push(neighbour);
322 }
323 }
324 }
325 }
326
327- for (const Coords& swim_coord : visited_positions)
328- if (swim_coords.count(swim_coord) == 0)
329- swim_coords.insert(swim_coord);
330- else
331- return true;
332+ // Couldn't connect to another port, so we add our reachable nodes to the list
333+ for (const Coords& reachable_coord : reachable_from_current_port) {
334+ reachable_from_previous_ports.insert(reachable_coord);
335+ }
336 }
337 return false;
338 }
339
340+void Map::cleanup_port_spaces(const World& world) {
341+ for (const Coords& c : get_port_spaces()) {
342+ if (!is_port_space_allowed(world, get_fcoords(c))) {
343+ set_port_space(world, c, false);
344+ continue;
345+ }
346+ }
347+}
348+
349 bool Map::has_artifacts() {
350 for (MapIndex i = 0; i < max_index(); ++i) {
351 if (upcast(Immovable, immovable, fields_[i].get_immovable())) {
352
353=== modified file 'src/logic/map.h'
354--- src/logic/map.h 2017-11-26 14:44:23 +0000
355+++ src/logic/map.h 2017-12-11 06:56:51 +0000
356@@ -270,8 +270,8 @@
357 void set_scenario_player_ai(PlayerNumber, const std::string&);
358 void set_scenario_player_closeable(PlayerNumber, bool);
359
360- /// \returns the maximum theoretical possible nodecaps (no blocking bobs, etc.)
361- NodeCaps get_max_nodecaps(const World& world, const FCoords&);
362+ /// \returns the maximum theoretical possible nodecaps (no blocking bobs, immovables etc.)
363+ NodeCaps get_max_nodecaps(const World& world, const FCoords&) const;
364
365 BaseImmovable* get_immovable(const Coords&) const;
366 uint32_t find_bobs(const Area<FCoords>,
367@@ -438,14 +438,26 @@
368 /// Translate the whole map so that the given point becomes the new origin.
369 void set_origin(const Coords&);
370
371- /// Port space specific functions
372+ // Port space specific functions
373+
374+ /// Checks whether the maximum theoretical possible NodeCap of the field is big,
375+ /// and there is room for a port space
376+ bool is_port_space_allowed(const World& world, const FCoords& fc) const;
377 bool is_port_space(const Coords& c) const;
378- void set_port_space(Coords c, bool allowed);
379+
380+ /// If 'set', set the space at 'c' as port space, otherwise unset.
381+ /// 'force' sets the port space even if it isn't viable, and is to be used for map loading only.
382+ /// Returns whether the port space was set/unset successfully.
383+ bool set_port_space(const World& world, const Widelands::Coords& c, bool set, bool force = false);
384 const PortSpacesSet& get_port_spaces() const {
385 return port_spaces_;
386 }
387 std::vector<Coords> find_portdock(const Widelands::Coords& c) const;
388- bool allows_seafaring();
389+
390+ /// Check whether there are at least 2 port spaces that can be reached from each other by water
391+ bool allows_seafaring() const;
392+ /// Remove all port spaces that are not valid (Buildcap < big or not enough space for a portdock).
393+ void cleanup_port_spaces(const World& world);
394
395 /// Checks whether there are any artifacts on the map
396 bool has_artifacts();
397@@ -458,19 +470,19 @@
398 void recalc_brightness(const FCoords&);
399 void recalc_nodecaps_pass1(const World& world, const FCoords&);
400 void recalc_nodecaps_pass2(const World& world, const FCoords& f);
401- NodeCaps calc_nodecaps_pass1(const World& world, const FCoords&, bool consider_mobs = true);
402+ NodeCaps calc_nodecaps_pass1(const World& world, const FCoords&, bool consider_mobs = true) const;
403 NodeCaps calc_nodecaps_pass2(const World& world,
404 const FCoords&,
405 bool consider_mobs = true,
406- NodeCaps initcaps = CAPS_NONE);
407+ NodeCaps initcaps = CAPS_NONE) const;
408 void check_neighbour_heights(FCoords, uint32_t& radius);
409 int calc_buildsize(const World& world,
410 const FCoords& f,
411 bool avoidnature,
412 bool* ismine = nullptr,
413 bool consider_mobs = true,
414- NodeCaps initcaps = CAPS_NONE);
415- bool is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs);
416+ NodeCaps initcaps = CAPS_NONE) const;
417+ bool is_cycle_connected(const FCoords& start, uint32_t length, const WalkingDir* dirs) const;
418 template <typename functorT>
419 void find_reachable(const Area<FCoords>&, const CheckStep&, functorT&) const;
420 template <typename functorT> void find(const Area<FCoords>&, functorT&) const;
421
422=== modified file 'src/logic/map_objects/tribes/production_program.cc'
423--- src/logic/map_objects/tribes/production_program.cc 2017-11-24 21:34:17 +0000
424+++ src/logic/map_objects/tribes/production_program.cc 2017-12-11 06:56:51 +0000
425@@ -742,9 +742,9 @@
426 void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const {
427 switch (feature_) {
428 case SEAFARING: {
429- if (game.map().get_port_spaces().size() > 1)
430+ if (game.map().allows_seafaring()) {
431 return ps.program_step(game, 0);
432- else {
433+ } else {
434 ps.set_production_result(_("No use for ships on this map!"));
435 return ps.program_end(game, Failed);
436 }
437
438=== modified file 'src/map_io/map_port_spaces_packet.cc'
439--- src/map_io/map_port_spaces_packet.cc 2017-08-19 22:22:20 +0000
440+++ src/map_io/map_port_spaces_packet.cc 2017-12-11 06:56:51 +0000
441@@ -51,8 +51,7 @@
442
443 Section& s2 = prof.get_safe_section("port_spaces");
444 for (uint16_t i = 0; i < num; ++i) {
445- map->set_port_space(
446- get_safe_coords(std::to_string(static_cast<unsigned int>(i)), ext, &s2), true);
447+ map->set_port_space(egbase.world(), get_safe_coords(std::to_string(static_cast<unsigned int>(i)), ext, &s2), true, true);
448 }
449 } else {
450 throw UnhandledVersionError("MapPortSpacesPacket", packet_version, kCurrentPacketVersion);
451@@ -70,8 +69,7 @@
452 s1.set_int("packet_version", kCurrentPacketVersion);
453
454 const Map& map = egbase.map();
455- const uint16_t num = map.get_port_spaces().size();
456- s1.set_int("number_of_port_spaces", num);
457+ s1.set_int("number_of_port_spaces", map.get_port_spaces().size());
458
459 Section& s2 = prof.create_section("port_spaces");
460 int i = 0;
461
462=== modified file 'src/map_io/s2map.cc'
463--- src/map_io/s2map.cc 2017-08-19 13:02:14 +0000
464+++ src/map_io/s2map.cc 2017-12-11 06:56:51 +0000
465@@ -392,7 +392,7 @@
466
467 map_.recalc_whole_map(egbase.world());
468
469- postload_fix_conversion(egbase);
470+ postload_set_port_spaces(egbase.world());
471
472 set_state(STATE_LOADED);
473
474@@ -473,8 +473,9 @@
475 for (int16_t x = 0; x < mapwidth; ++x, ++f, ++pc) {
476 uint8_t c = *pc;
477 // Harbour buildspace & textures - Information taken from:
478- if (c & 0x40)
479- map_.set_port_space(Widelands::Coords(x, y), true);
480+ if (c & 0x40) {
481+ port_spaces_to_set_.insert(Widelands::Coords(x, y));
482+ }
483 f->set_terrain_d(terrain_converter.lookup(worldtype_, c & 0x1f));
484 }
485
486@@ -490,8 +491,9 @@
487 uint8_t c = *pc;
488 // Harbour buildspace & textures - Information taken from:
489 // http://bazaar.launchpad.net/~xaser/s25rttr/s25edit/view/head:/WLD_reference.txt
490- if (c & 0x40)
491- map_.set_port_space(Widelands::Coords(x, y), true);
492+ if (c & 0x40) {
493+ port_spaces_to_set_.insert(Widelands::Coords(x, y));
494+ }
495 f->set_terrain_r(terrain_converter.lookup(worldtype_, c & 0x1f));
496 }
497
498@@ -1041,41 +1043,25 @@
499 }
500 }
501
502-/// Try to fix data, which is incompatible between S2 and Widelands
503-void S2MapLoader::postload_fix_conversion(Widelands::EditorGameBase& egbase) {
504-
505- /*
506- * 1: Try to fix port spaces
507- */
508- const Widelands::Map::PortSpacesSet ports(map_.get_port_spaces());
509- const Widelands::World& world = egbase.world();
510-
511- // Check if port spaces are valid
512- for (const Widelands::Coords& c : ports) {
513- Widelands::FCoords fc = map_.get_fcoords(c);
514- Widelands::NodeCaps nc = map_.get_max_nodecaps(world, fc);
515- if ((nc & Widelands::BUILDCAPS_SIZEMASK) != Widelands::BUILDCAPS_BIG ||
516- map_.find_portdock(fc).empty()) {
517- log("Invalid port build space: ");
518- map_.set_port_space(c, false);
519-
520- bool fixed = false;
521+/// Try to fix data which is incompatible between S2 and Widelands.
522+/// This is only the port space locations.
523+void S2MapLoader::postload_set_port_spaces(const Widelands::World& world) {
524+ // Set port spaces near desired locations if possible
525+ for (const Widelands::Coords& coords : port_spaces_to_set_) {
526+ bool was_set = map_.set_port_space(world, coords, true);
527+ const Widelands::FCoords fc = map_.get_fcoords(coords);
528+ if (!was_set) {
529+ // Try to set a port space at alternative location
530 Widelands::MapRegion<Widelands::Area<Widelands::FCoords>> mr(
531- map_, Widelands::Area<Widelands::FCoords>(fc, 3));
532+ map_, Widelands::Area<Widelands::FCoords>(fc, 3));
533 do {
534- // Check whether the maximum theoretical possible NodeCap of the field is big + port
535- Widelands::NodeCaps nc2 =
536- map_.get_max_nodecaps(world, const_cast<Widelands::FCoords&>(mr.location()));
537- if ((nc2 & Widelands::BUILDCAPS_SIZEMASK) == Widelands::BUILDCAPS_BIG &&
538- (!map_.find_portdock(mr.location()).empty())) {
539- map_.set_port_space(Widelands::Coords(mr.location().x, mr.location().y), true);
540- fixed = true;
541- }
542- } while (mr.advance(map_) && !fixed);
543- if (!fixed) {
544- log("FAILED! No alternative port buildspace for (%i, %i) found!\n", fc.x, fc.y);
545- } else
546- log("Fixed!\n");
547+ was_set = map_.set_port_space(world, Widelands::Coords(mr.location().x, mr.location().y), true);
548+ } while (!was_set && mr.advance(map_));
549+ }
550+ if (!was_set) {
551+ log("FAILED! No port buildspace for (%i, %i) found!\n", fc.x, fc.y);
552+ } else {
553+ log("SUCCESS! Port buildspace set for (%i, %i) \n", fc.x, fc.y);
554 }
555 }
556 }
557
558=== modified file 'src/map_io/s2map.h'
559--- src/map_io/s2map.h 2017-06-24 10:38:19 +0000
560+++ src/map_io/s2map.h 2017-12-11 06:56:51 +0000
561@@ -42,10 +42,11 @@
562 private:
563 const std::string filename_;
564 WorldType worldtype_;
565+ std::set<Widelands::Coords> port_spaces_to_set_;
566
567 void load_s2mf_header(FileRead&);
568 void load_s2mf(Widelands::EditorGameBase&);
569- void postload_fix_conversion(Widelands::EditorGameBase&);
570+ void postload_set_port_spaces(const Widelands::World& world);
571 };
572
573 #endif // end of include guard: WL_MAP_IO_S2MAP_H
574
575=== modified file 'src/scripting/lua_map.cc'
576--- src/scripting/lua_map.cc 2017-11-23 23:32:40 +0000
577+++ src/scripting/lua_map.cc 2017-12-11 06:56:51 +0000
578@@ -1138,7 +1138,7 @@
579
580 .. class:: Map
581
582- Access to the map and it's objects. You cannot instantiate this directly,
583+ Access to the map and its objects. You cannot instantiate this directly,
584 instead access it via :attr:`wl.Game.map`.
585 */
586 const char LuaMap::className[] = "Map";
587@@ -1146,9 +1146,12 @@
588 METHOD(LuaMap, place_immovable),
589 METHOD(LuaMap, get_field),
590 METHOD(LuaMap, recalculate),
591+ METHOD(LuaMap, set_port_space),
592 {nullptr, nullptr},
593 };
594 const PropertyType<LuaMap> LuaMap::Properties[] = {
595+ PROP_RO(LuaMap, allows_seafaring),
596+ PROP_RO(LuaMap, number_of_port_spaces),
597 PROP_RO(LuaMap, width),
598 PROP_RO(LuaMap, height),
599 PROP_RO(LuaMap, player_slots),
600@@ -1167,6 +1170,31 @@
601 ==========================================================
602 */
603 /* RST
604+ .. attribute:: allows_seafaring
605+
606+ (RO) Whether the map currently allows seafaring. This will calculate a path between port spaces,
607+ so it's more accurate but less efficient than :any:`number_of_port_spaces`.
608+
609+ :returns: True if there are at least two port spaces that can be reached from each other.
610+*/
611+int LuaMap::get_allows_seafaring(lua_State* L) {
612+ lua_pushboolean(L, get_egbase(L).map().allows_seafaring());
613+ return 1;
614+}
615+/* RST
616+ .. attribute:: number_of_port_spaces
617+
618+ (RO) The amount of port spaces on the map. If this is >= 2, one can assume that the map
619+ allows seafaring. This is checked very quickly and is more efficient than :any:`allows_seafaring`,
620+ but it won't detect whether the port spaces can be reached from each other, so it's less accurate.
621+
622+ :returns: An integer with the number of port spaces.
623+*/
624+int LuaMap::get_number_of_port_spaces(lua_State* L) {
625+ lua_pushuint32(L, get_egbase(L).map().get_port_spaces().size());
626+ return 1;
627+}
628+/* RST
629 .. attribute:: width
630
631 (RO) The width of the map in fields.
632@@ -1297,6 +1325,31 @@
633 return 0;
634 }
635
636+/* RST
637+ .. method:: set_port_space(x, y, allowed)
638+
639+ Sets whether a port space is allowed at the coordinates (x, y).
640+ Returns false if the port space couldn't be set.
641+
642+ :arg x: The x coordinate of the port space to set/unset.
643+ :type x: :class:`int`
644+ :arg y: The y coordinate of the port space to set/unset.
645+ :type y: :class:`int`
646+ :arg allowed: Whether building a port will be allowed here.
647+ :type allowed: :class:`bool`
648+
649+ :returns: :const:`true` on success, or :const:`false` otherwise
650+ :rtype: :class:`bool`
651+*/
652+int LuaMap::set_port_space(lua_State* L) {
653+ const int x = luaL_checkint32(L, 2);
654+ const int y = luaL_checkint32(L, 3);
655+ const bool allowed = luaL_checkboolean(L, 4);
656+ const bool success = get_egbase(L).mutable_map()->set_port_space(get_egbase(L).world(), Widelands::Coords(x, y), allowed);
657+ lua_pushboolean(L, success);
658+ return 1;
659+}
660+
661 /*
662 ==========================================================
663 C METHODS
664
665=== modified file 'src/scripting/lua_map.h'
666--- src/scripting/lua_map.h 2017-11-28 08:57:52 +0000
667+++ src/scripting/lua_map.h 2017-12-11 06:56:51 +0000
668@@ -86,6 +86,8 @@
669 /*
670 * Properties
671 */
672+ int get_allows_seafaring(lua_State*);
673+ int get_number_of_port_spaces(lua_State*);
674 int get_width(lua_State*);
675 int get_height(lua_State*);
676 int get_player_slots(lua_State*);
677@@ -96,6 +98,7 @@
678 int place_immovable(lua_State*);
679 int get_field(lua_State*);
680 int recalculate(lua_State*);
681+ int set_port_space(lua_State*);
682
683 /*
684 * C methods
685
686=== modified file 'src/wui/building_statistics_menu.cc'
687--- src/wui/building_statistics_menu.cc 2017-11-11 10:08:54 +0000
688+++ src/wui/building_statistics_menu.cc 2017-12-11 06:56:51 +0000
689@@ -138,8 +138,8 @@
690 g_gr->images().get("images/wui/fieldaction/menu_tab_buildmine.png"),
691 tabs_[BuildingTab::Mines], _("Mines"));
692
693- // Hide the ports tab for non-seafaring maps
694- if (iplayer().game().map().get_port_spaces().size() > 1) {
695+ // Only show the ports tab for seafaring maps
696+ if (iplayer().game().map().allows_seafaring()) {
697 tab_panel_.add("building_stats_ports",
698 g_gr->images().get("images/wui/fieldaction/menu_tab_buildport.png"),
699 tabs_[BuildingTab::Ports], _("Ports"));
700
701=== modified file 'src/wui/fieldaction.cc'
702--- src/wui/fieldaction.cc 2017-11-27 08:21:32 +0000
703+++ src/wui/fieldaction.cc 2017-12-11 06:56:51 +0000
704@@ -416,11 +416,13 @@
705 // Some building types cannot be built (i.e. construction site) and not
706 // allowed buildings.
707 if (dynamic_cast<const Game*>(&ibase().egbase())) {
708- if (!building_descr->is_buildable() || !player_->is_building_type_allowed(building_index))
709+ if (!building_descr->is_buildable() || !player_->is_building_type_allowed(building_index)) {
710 continue;
711+ }
712 if (building_descr->needs_seafaring() &&
713- ibase().egbase().map().get_port_spaces().size() < 2)
714+ !ibase().egbase().map().allows_seafaring()) {
715 continue;
716+ }
717 } else if (!building_descr->is_buildable() && !building_descr->is_enhanced())
718 continue;
719
720
721=== modified file 'test/maps/expedition.wmf/scripting/init.lua'
722--- test/maps/expedition.wmf/scripting/init.lua 2017-01-17 20:57:35 +0000
723+++ test/maps/expedition.wmf/scripting/init.lua 2017-12-11 06:56:51 +0000
724@@ -2,6 +2,7 @@
725 include "scripting/coroutine.lua"
726 include "scripting/infrastructure.lua"
727 include "scripting/ui.lua"
728+include "test/scripting/stable_save.lua"
729
730 -- This is a test case for bug 1234058: there is constant demand for logs,
731 -- so the expedition initially never got any.
732@@ -53,18 +54,6 @@
733 first_ship = nil
734 second_ship = nil
735
736--- Save the game so that reloading does not skip
737-function stable_save(safename)
738- local old_speed = game.desired_speed
739- game.desired_speed = 1000
740- sleep(100)
741- game:save(safename)
742- game.desired_speed = 1000
743- sleep(2000) -- Give the loaded game a chance to catch up
744- game.desired_speed = old_speed
745- sleep(1000)
746-end
747-
748 function click_on_ship(which_ship)
749 local mv = wl.ui.MapView()
750 for x=0,map.width-1 do
751@@ -211,7 +200,7 @@
752 game.desired_speed = 10 * 1000
753 sleep(10000)
754
755- stable_save("ready_to_sail")
756+ stable_save(game, "ready_to_sail")
757
758 sleep(10000)
759 assert_equal(1, p1:get_workers("barbarians_builder"))
760@@ -255,7 +244,7 @@
761 assert_equal("ccw",expedition_ship.island_explore_direction)
762 sleep(6000)
763
764- stable_save("sailing")
765+ stable_save(game, "sailing")
766 assert_equal(1, p1:get_workers("barbarians_builder"))
767
768 cancel_expedition_in_shipwindow(expedition_ship)
769@@ -289,7 +278,7 @@
770 sleep(500)
771 assert_equal(1, p1:get_workers("barbarians_builder"))
772
773- stable_save("reached_port_space")
774+ stable_save(game, "reached_port_space")
775 sleep(5000)
776 ships = p1:get_ships()
777 --ships table should contain 1-2 items (1-2 ships)
778@@ -342,7 +331,7 @@
779 sleep(500)
780 assert_equal(1, p1:get_workers("barbarians_builder"))
781
782- stable_save("port_done")
783+ stable_save(game, "port_done")
784 game.desired_speed = 25 * 1000
785
786 -- build a lumberjack and see if the ship starts transporting stuff
787
788=== modified file 'test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua'
789--- test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua 2015-11-04 16:48:56 +0000
790+++ test/maps/expedition.wmf/scripting/test_cancel_when_port_space_was_reached_two_ships.lua 2017-12-11 06:56:51 +0000
791@@ -27,7 +27,7 @@
792 sleep(500)
793 assert_equal(1, p1:get_workers("barbarians_builder"))
794
795- stable_save("reached_port_space")
796+ stable_save(game, "reached_port_space")
797 assert_equal(1, p1:get_workers("barbarians_builder"))
798
799 cancel_expedition_in_shipwindow(expedition_ship)
800
801=== modified file 'test/maps/expedition.wmf/scripting/test_ship_movement_controls.lua'
802--- test/maps/expedition.wmf/scripting/test_ship_movement_controls.lua 2015-12-03 21:12:12 +0000
803+++ test/maps/expedition.wmf/scripting/test_ship_movement_controls.lua 2017-12-11 06:56:51 +0000
804@@ -105,7 +105,7 @@
805 sleep(500)
806 assert_equal("exp_colonizing", ships[1].state)
807 sleep(15000)
808- stable_save("port_in_constr")
809+ stable_save(game, "port_in_constr")
810
811 -- while unfinished yet, removing it
812 new_port=map:get_field(16,2).immovable
813
814=== modified file 'test/maps/expedition.wmf/scripting/test_starting_wait_a_while_cancel.lua'
815--- test/maps/expedition.wmf/scripting/test_starting_wait_a_while_cancel.lua 2015-10-31 12:11:44 +0000
816+++ test/maps/expedition.wmf/scripting/test_starting_wait_a_while_cancel.lua 2017-12-11 06:56:51 +0000
817@@ -6,7 +6,7 @@
818 -- gives the builder enough time to walk over.
819 port:start_expedition()
820 sleep(50000)
821- stable_save("cancel_in_port")
822+ stable_save(game, "cancel_in_port")
823 assert_equal(1, p1:get_workers("barbarians_builder"))
824
825 port:cancel_expedition()
826
827=== modified file 'test/maps/ship_transportation.wmf/scripting/init.lua'
828--- test/maps/ship_transportation.wmf/scripting/init.lua 2016-01-28 05:24:34 +0000
829+++ test/maps/ship_transportation.wmf/scripting/init.lua 2017-12-11 06:56:51 +0000
830@@ -1,6 +1,7 @@
831 include "scripting/lunit.lua"
832 include "scripting/coroutine.lua"
833 include "scripting/infrastructure.lua"
834+include "test/scripting/stable_save.lua"
835
836 game = wl.Game()
837 map = game.map
838@@ -49,15 +50,3 @@
839
840 ship = p1:place_ship(map:get_field(10, 10))
841 p1.see_all = true
842-
843--- Save the game so that reloading does not skip
844-function stable_save(safename)
845- local old_speed = game.desired_speed
846- game.desired_speed = 1000
847- sleep(100)
848- game:save(safename)
849- game.desired_speed = 1000
850- sleep(2000) -- Give the loaded game a chance to catch up
851- game.desired_speed = old_speed
852- sleep(1000)
853-end
854
855=== modified file 'test/maps/ship_transportation.wmf/scripting/test_many_ships.lua'
856--- test/maps/ship_transportation.wmf/scripting/test_many_ships.lua 2016-01-31 21:03:15 +0000
857+++ test/maps/ship_transportation.wmf/scripting/test_many_ships.lua 2017-12-11 06:56:51 +0000
858@@ -22,7 +22,7 @@
859 sleep(2000)
860 end
861
862- stable_save("20_ships")
863+ stable_save(game, "20_ships")
864
865 i = 0
866 while i < 10 do
867
868=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_ware_in_portdock.lua'
869--- test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_ware_in_portdock.lua 2015-11-03 18:52:00 +0000
870+++ test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_ware_in_portdock.lua 2017-12-11 06:56:51 +0000
871@@ -24,7 +24,7 @@
872
873 sleep(100)
874 assert_equal(p1:get_wares("blackwood"), 0)
875- stable_save("port1_just_removed")
876+ stable_save(game, "port1_just_removed")
877
878 sleep(5000)
879
880
881=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_worker_in_portdock.lua'
882--- test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_worker_in_portdock.lua 2015-11-03 18:52:00 +0000
883+++ test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_worker_in_portdock.lua 2017-12-11 06:56:51 +0000
884@@ -20,7 +20,7 @@
885
886 southern_port():remove()
887 sleep(100)
888- stable_save("port1_just_removed")
889+ stable_save(game, "port1_just_removed")
890
891 -- Wait till the worker runs to the warehouse.
892 while wh:get_workers("barbarians_builder") == 0 do
893
894=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua'
895--- test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua 2015-11-21 11:47:05 +0000
896+++ test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua 2017-12-11 06:56:51 +0000
897@@ -29,7 +29,7 @@
898
899 local flag_oversea = northern_port().flag
900
901- stable_save("restored_port")
902+ stable_save(game, "restored_port")
903
904 -- remove the portdock while the blackwood is in transit.
905 north_port_portdock=northern_port().portdock
906
907=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_ware_in_transit.lua'
908--- test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_ware_in_transit.lua 2015-11-03 18:52:00 +0000
909+++ test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_ware_in_transit.lua 2017-12-11 06:56:51 +0000
910@@ -28,7 +28,7 @@
911 southern_port():remove()
912 sleep(1000)
913
914- stable_save("no_ports")
915+ stable_save(game, "no_ports")
916
917 -- There are no more ports, therefore also no fleet. The wares on the ship
918 -- are not accessible and should therefore not show up in the stock anymore.
919
920=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_worker_in_transit.lua'
921--- test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_worker_in_transit.lua 2015-11-03 18:52:00 +0000
922+++ test/maps/ship_transportation.wmf/scripting/test_rip_ports_with_worker_in_transit.lua 2017-12-11 06:56:51 +0000
923@@ -28,7 +28,7 @@
924 southern_port():remove()
925 sleep(1000)
926
927- stable_save("no_ports")
928+ stable_save(game, "no_ports")
929
930 -- There are no more ports, therefore also no fleet. The workers on the ship
931 -- are not accessible and should therefore not show up in the stock anymore.
932
933=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_ware_in_portdock.lua'
934--- test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_ware_in_portdock.lua 2015-11-03 18:52:00 +0000
935+++ test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_ware_in_portdock.lua 2017-12-11 06:56:51 +0000
936@@ -29,7 +29,7 @@
937 northern_port():remove()
938 sleep(100)
939
940- stable_save("ware_in_portdock")
941+ stable_save(game, "ware_in_portdock")
942
943 --ship has to get to the place of former upper port and then return back to the bottom port
944 sleep(30000)
945
946=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua'
947--- test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua 2015-11-21 11:47:05 +0000
948+++ test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua 2017-12-11 06:56:51 +0000
949@@ -38,7 +38,7 @@
950
951 sleep(100)
952
953- stable_save("worker_in_portdock")
954+ stable_save(game, "worker_in_portdock")
955
956 -- Wait till a ship unloads a worked at the souther port
957 while (ship:get_workers("builder") == 1) do
958
959=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_ship_before_picking_up_transporting_ware.lua'
960--- test/maps/ship_transportation.wmf/scripting/test_rip_ship_before_picking_up_transporting_ware.lua 2015-11-03 18:52:00 +0000
961+++ test/maps/ship_transportation.wmf/scripting/test_rip_ship_before_picking_up_transporting_ware.lua 2017-12-11 06:56:51 +0000
962@@ -16,10 +16,10 @@
963 sleep(12000)
964
965 -- remove the ship while the ware is in transit.
966- stable_save("0_before_removing_ship")
967+ stable_save(game, "0_before_removing_ship")
968 ship:remove()
969 sleep(1000)
970- stable_save("1_no_more_ship")
971+ stable_save(game, "1_no_more_ship")
972
973 assert_equal(1, p1:get_wares("blackwood"))
974 -- It is not in the port (still in the dock)
975
976=== modified file 'test/maps/ship_transportation.wmf/scripting/test_rip_ship_while_transporting_ware.lua'
977--- test/maps/ship_transportation.wmf/scripting/test_rip_ship_while_transporting_ware.lua 2015-11-03 18:52:00 +0000
978+++ test/maps/ship_transportation.wmf/scripting/test_rip_ship_while_transporting_ware.lua 2017-12-11 06:56:51 +0000
979@@ -18,10 +18,10 @@
980 assert_equal(p1:get_wares("blackwood"), 1)
981
982 -- remove the ship while the ware is in transit.
983- stable_save("0_before_removing_ship")
984+ stable_save(game, "0_before_removing_ship")
985 ship:remove()
986 sleep(1000)
987- stable_save("1_no_more_ship")
988+ stable_save(game, "1_no_more_ship")
989
990 -- No more blackwood.
991 assert_equal(p1:get_wares("blackwood"), 0)
992
993=== added directory 'test/maps/two_ponds.wmf'
994=== added directory 'test/maps/two_ponds.wmf/binary'
995=== added file 'test/maps/two_ponds.wmf/binary/heights'
996Binary files test/maps/two_ponds.wmf/binary/heights 1970-01-01 00:00:00 +0000 and test/maps/two_ponds.wmf/binary/heights 2017-12-11 06:56:51 +0000 differ
997=== added file 'test/maps/two_ponds.wmf/binary/mapobjects'
998Binary files test/maps/two_ponds.wmf/binary/mapobjects 1970-01-01 00:00:00 +0000 and test/maps/two_ponds.wmf/binary/mapobjects 2017-12-11 06:56:51 +0000 differ
999=== added file 'test/maps/two_ponds.wmf/binary/resource'
1000Binary files test/maps/two_ponds.wmf/binary/resource 1970-01-01 00:00:00 +0000 and test/maps/two_ponds.wmf/binary/resource 2017-12-11 06:56:51 +0000 differ
1001=== added file 'test/maps/two_ponds.wmf/binary/terrain'
1002Binary files test/maps/two_ponds.wmf/binary/terrain 1970-01-01 00:00:00 +0000 and test/maps/two_ponds.wmf/binary/terrain 2017-12-11 06:56:51 +0000 differ
1003=== added file 'test/maps/two_ponds.wmf/elemental'
1004--- test/maps/two_ponds.wmf/elemental 1970-01-01 00:00:00 +0000
1005+++ test/maps/two_ponds.wmf/elemental 2017-12-11 06:56:51 +0000
1006@@ -0,0 +1,12 @@
1007+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1008+
1009+[global]
1010+packet_version="1"
1011+map_w="64"
1012+map_h="64"
1013+nr_players="1"
1014+name="Two Ponds"
1015+author="Unknown"
1016+descr="Test map with 2 bodies of water, with 1 port space each."
1017+hint=
1018+tags=
1019
1020=== added file 'test/maps/two_ponds.wmf/minimap.png'
1021Binary files test/maps/two_ponds.wmf/minimap.png 1970-01-01 00:00:00 +0000 and test/maps/two_ponds.wmf/minimap.png 2017-12-11 06:56:51 +0000 differ
1022=== added file 'test/maps/two_ponds.wmf/objective'
1023--- test/maps/two_ponds.wmf/objective 1970-01-01 00:00:00 +0000
1024+++ test/maps/two_ponds.wmf/objective 2017-12-11 06:56:51 +0000
1025@@ -0,0 +1,4 @@
1026+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1027+
1028+[global]
1029+packet_version="2"
1030
1031=== added file 'test/maps/two_ponds.wmf/player_names'
1032--- test/maps/two_ponds.wmf/player_names 1970-01-01 00:00:00 +0000
1033+++ test/maps/two_ponds.wmf/player_names 2017-12-11 06:56:51 +0000
1034@@ -0,0 +1,10 @@
1035+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1036+
1037+[global]
1038+packet_version="2"
1039+
1040+[player_1]
1041+name="Cluicheadair 1"
1042+tribe="barbarians"
1043+ai=
1044+closeable="false"
1045
1046=== added file 'test/maps/two_ponds.wmf/player_position'
1047--- test/maps/two_ponds.wmf/player_position 1970-01-01 00:00:00 +0000
1048+++ test/maps/two_ponds.wmf/player_position 2017-12-11 06:56:51 +0000
1049@@ -0,0 +1,5 @@
1050+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1051+
1052+[global]
1053+packet_version="2"
1054+player_1="5 58"
1055
1056=== added file 'test/maps/two_ponds.wmf/port_spaces'
1057--- test/maps/two_ponds.wmf/port_spaces 1970-01-01 00:00:00 +0000
1058+++ test/maps/two_ponds.wmf/port_spaces 2017-12-11 06:56:51 +0000
1059@@ -0,0 +1,9 @@
1060+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1061+
1062+[global]
1063+packet_version="1"
1064+number_of_port_spaces="2"
1065+
1066+[port_spaces]
1067+0="7 2"
1068+1="12 24"
1069
1070=== added directory 'test/maps/two_ponds.wmf/scripting'
1071=== added file 'test/maps/two_ponds.wmf/scripting/init.lua'
1072--- test/maps/two_ponds.wmf/scripting/init.lua 1970-01-01 00:00:00 +0000
1073+++ test/maps/two_ponds.wmf/scripting/init.lua 2017-12-11 06:56:51 +0000
1074@@ -0,0 +1,6 @@
1075+include "scripting/coroutine.lua"
1076+include "scripting/lunit.lua"
1077+include "test/scripting/stable_save.lua"
1078+
1079+game = wl.Game()
1080+map = game.map
1081
1082=== added file 'test/maps/two_ponds.wmf/scripting/test_seafaring.lua'
1083--- test/maps/two_ponds.wmf/scripting/test_seafaring.lua 1970-01-01 00:00:00 +0000
1084+++ test/maps/two_ponds.wmf/scripting/test_seafaring.lua 2017-12-11 06:56:51 +0000
1085@@ -0,0 +1,33 @@
1086+run(function()
1087+ -- The map in its initial state has 2 unconnected port spaces, so it should
1088+ -- not allow seafaring. One of the port spaces has trees on top of it.
1089+ assert_equal(2, map.number_of_port_spaces)
1090+ assert_equal(false, map.allows_seafaring)
1091+
1092+ -- Now try to add a port space on a medium buildcap, it should fail
1093+ assert_equal(false, map:set_port_space(11, 9, true))
1094+ assert_equal(2, map.number_of_port_spaces)
1095+ assert_equal(false, map.allows_seafaring)
1096+
1097+ -- Now try to add a port space away from water, it should fail
1098+ assert_equal(false, map:set_port_space(18, 9, true))
1099+ assert_equal(2, map.number_of_port_spaces)
1100+ assert_equal(false, map.allows_seafaring)
1101+
1102+ -- Now add a connecting port space - it should succeed and we should have seafaring then
1103+ assert_equal(true, map:set_port_space(0, 2, true))
1104+ assert_equal(3, map.number_of_port_spaces)
1105+ assert_equal(true, map.allows_seafaring)
1106+
1107+ stable_save(game, "port_spaces")
1108+ assert_equal(3, map.number_of_port_spaces)
1109+ assert_equal(true, map.allows_seafaring)
1110+
1111+ -- Remove the port space again
1112+ assert_equal(true, map:set_port_space(0, 2, false))
1113+ assert_equal(2, map.number_of_port_spaces)
1114+ assert_equal(false, map.allows_seafaring)
1115+
1116+ print("# All Tests passed.")
1117+ wl.ui.MapView():close()
1118+end)
1119
1120=== added file 'test/maps/two_ponds.wmf/version'
1121--- test/maps/two_ponds.wmf/version 1970-01-01 00:00:00 +0000
1122+++ test/maps/two_ponds.wmf/version 2017-12-11 06:56:51 +0000
1123@@ -0,0 +1,11 @@
1124+# Automatically created by Widelands bzr8483[bug-1718745-allows-seafaring] (Release)
1125+
1126+[global]
1127+map_source_url=
1128+map_release=
1129+map_creator_version="bzr8483[bug-1718745-allows-seafaring]"
1130+map_version_major="0"
1131+map_version_minor="1"
1132+map_version_timestamp="1510038973"
1133+packet_version="1"
1134+packet_compatibility="1"
1135
1136=== added directory 'test/scripting'
1137=== added file 'test/scripting/stable_save.lua'
1138--- test/scripting/stable_save.lua 1970-01-01 00:00:00 +0000
1139+++ test/scripting/stable_save.lua 2017-12-11 06:56:51 +0000
1140@@ -0,0 +1,13 @@
1141+include "scripting/coroutine.lua"
1142+
1143+-- Save the game so that reloading does not skip
1144+function stable_save(game, savename)
1145+ local old_speed = game.desired_speed
1146+ game.desired_speed = 1000
1147+ sleep(100)
1148+ game:save(savename)
1149+ game.desired_speed = 1000
1150+ sleep(2000) -- Give the loaded game a chance to catch up
1151+ game.desired_speed = old_speed
1152+ sleep(1000)
1153+end

Subscribers

People subscribed via source and target branches

to status/vote changes: