Merge lp:~widelands-dev/widelands/bug-1687100-reveal_fields into lp:widelands
- bug-1687100-reveal_fields
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 8540 |
Proposed branch: | lp:~widelands-dev/widelands/bug-1687100-reveal_fields |
Merge into: | lp:widelands |
Diff against target: |
652 lines (+237/-137) 8 files modified
src/logic/CMakeLists.txt (+1/-0) src/logic/player.cc (+58/-23) src/logic/player.h (+17/-18) src/logic/see_unsee_node.h (+26/-0) src/map_io/map_players_view_packet.cc (+35/-2) src/scripting/lua_game.cc (+15/-18) test/maps/lua_testsuite.wmf/scripting/gplayer.lua (+0/-76) test/maps/plain.wmf/scripting/test_player_vision.lua (+85/-0) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/bug-1687100-reveal_fields |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
kaputtnik (community) | testing | Approve | |
GunChleoc | Needs Resubmitting | ||
Review via email: mp+323721@code.launchpad.net |
Commit message
When the hide_completely option is not selected, LuaPlayer:
Also, removed unused bool view_changed_ from player.h and removed compatibility code for border_file in the player vision packet.
Description of the change
This was actually a long-standing bug - the function wasn't doing what the documentation described in the first place.
Can be tested by hacking the seafaring tutorial:
=== modified file 'data/campaigns
--- data/campaigns/
+++ data/campaigns/
@@ -5,6 +5,14 @@
function introduction()
additional_
sleep(1000)
+
+plr:reveal_
+plr:hide_
+
+plr:hide_
+plr:reveal_
+sleep(600000)
+
message_
sleep(300)
message_
bunnybot (widelandsofficial) wrote : | # |
kaputtnik (franku) wrote : | # |
Doesn't work for me... add just
plr:
sleep(6000000)
to the mission_thread.lua and you see that every field is visible.
kaputtnik (franku) wrote : | # |
Maybe a better branch for testing is the branch where i have added already some reveal/hide animations to the first tutorials. This is also the branch where i had encountered this bug.
https:/
GunChleoc (gunchleoc) wrote : | # |
Next try.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2221. State: failed. Details: https:/
Appveyor build 2056. State: success. Details: https:/
kaputtnik (franku) wrote : | # |
Looks good now :-)
One small nit: Since hide_completely and reveal_completely are optional it should be clarified which boolean (true/false) will take an effect at all.
I want to make some more tests, before approval of testing. In the meantime someone else could look at the logic...
GunChleoc (gunchleoc) wrote : | # |
How is the documentation for you now?
kaputtnik (franku) wrote : | # |
> How is the documentation for you now?
Perfect :-) Thanks
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2230. State: passed. Details: https:/
Appveyor build 2065. State: success. Details: https:/
kaputtnik (franku) wrote : | # |
Hmmm... this do not work. From my understanding the function is_seeing() restricts the seen fields to MapObjectType:
So all nodes (fields) which are not in the area a Warehouse sees, behaves as before: E.g. the census and statistics disappear for a short time.
You could see this when playing this branch with the datadir of the reveal_
I think at least the military buildings must be added to set the seen fields.
Another approach is maybe to make the function sees_field() (https:/
kaputtnik (franku) wrote : | # |
Oh i didn't understood the code... is_seen() is only used to hide fields. But partly not working is revealing fields.
I am not familiar with the code, but i am asking myself why the function hide_fields() is now much complicated than the one in trunk ( http://
kaputtnik (franku) wrote : | # |
I have just seen that the mentioned behavior (disappearing census and statistics) do also happen in current trunk when playing the seafaring tutorial.
I check this in build 19... (must first compile from scratch)
kaputtnik (franku) wrote : | # |
Sorry, wrong alert :S
I accidentally edited the seafaring tutorial in trunk ... So trunk does NOT show this behavior.
Seems i must limit the things i am working on... Sorry for the noise.
GunChleoc (gunchleoc) wrote : | # |
I have dug some more into the vision stuff and completely reimplemented the thing. The new implementation should be safe for multiplayer too.
Could you please test again, and if still it doesn't work as expected, copy over some of your failing scripting code so I will have some real data to test with?
It would also be interesting to see if we can't just always hide completely, this would make the code a bit simpler.
I still need to implement saveloading, but I'll wait with that until it works.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2262. State: failed. Details: https:/
Appveyor build 2097. State: success. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
I guess the bug in the military scenario is that the soldiers see where they are going - do you want that switched off?
kaputtnik (franku) wrote : | # |
> do you want that switched off?
Lets say: If it doesn't make much work it would be fine to have the possibility to switch that off :-) Otherwise i could live with the current state.
The bug with the disappearing census and statistics is gone now.
Thank you very much for working on this.
You can always test your code if you run widelands with the --datadir= option pointing the data folder of my branch. Doesn't that work for you?
GunChleoc (gunchleoc) wrote : | # |
Well, it turns out that it makes insanely complicated work, so worker vision will have to stay as it is.
Duh, I never thought of the datadir option!
BTW you don't need the boolean now when revealing fields, as the necessary steps are detected automatically. We still need the 2 modes for hiding though, e.g. in the Barbarian campaign scenario 2 when the tracks are discovered, we don't want to black this out.
Now on to saveloading...
kaputtnik (franku) wrote : | # |
Sorry Gun,
with your last changes the prior problem appear again: The census and statistics overlay for some buildings suddenly disappear in the seafaring tutorial.
I have reverted to revision 8358 and then it works: The census and statistics overlays keep alive.
GunChleoc (gunchleoc) wrote : | # |
Thanks for testing again. I thought the new changes would make the code more correct, but I was obviously wrong, since the test suite also blew up.
GunChleoc (gunchleoc) wrote : | # |
I found the error in the test suite: we need to sleep after calls now, because the playercommands are causing delays.
I deleted the commit that broke things for you from the branch's history, so you will need to pull the branch with the option --overwrite.
kaputtnik (franku) wrote : | # |
I have a fresh branch and compiled from scratch.
Works again :-)
GunChleoc (gunchleoc) wrote : | # |
Saveloading is implemented, so this is ready for review again.
kaputtnik (franku) wrote : | # |
Sorry, isn't working. Now it is the economy tutorial which does not work. Please run the branch with the --datadir option pointing to my branch and open first the seafaring tutorial, and afterwards the economy tutorial. Within the seafaring tutorial you see the 'reveal_randomly' code is working, whereas in the economy tutorial the same code isn't working (most areas get not hidden prior revealing). Maybe i have made a mistake in my code, so i would be glad if you can take a look at it.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2284. State: errored. Details: https:/
Appveyor build 2119. State: success. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
The difference is the headquarters in the economy tutorial - seems that it is creating vision no matter what we do.
kaputtnik (franku) wrote : | # |
Hm.. that's strange, because this works also for the first tutorial, where a headquarter is used. The first campaign "A Place to Call Home" works also nicely with a headquarter.
GunChleoc (gunchleoc) wrote : | # |
Yep, really strange. It very obviously is the vision or conquer radius of the headquarters though. Your code doesn't look any different to the Seafaring tutorial code, so I don't think that's it either.
GunChleoc (gunchleoc) wrote : | # |
I have found the culprit - some of the roads are messing this up. So, I have moved them into a separate function and add them after the land has been revealed.
We can surely still pretty this up, but I need to create a debug build first so that I can see the coordinates.
GunChleoc (gunchleoc) wrote : | # |
OK, done with my work on the other branch. The problem happens when we connect mines and constructionsites to the road network - there is nothing in the vision code that makes mines special as far as I can see, so I haven't the foggiest what's causing this. I'd say let's leave it as it is and live with the workaround that I coded for the Economy tutorial.
kaputtnik (franku) wrote : | # |
Wow, thanks a lot :-)
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2287. State: passed. Details: https:/
Appveyor build 2122. State: success. Details: https:/
kaputtnik (franku) wrote : | # |
I think this is working now :-)
Klaus Halfmann (klaus-halfmann) wrote : | # |
Testing hint:
use this datadir this will use the functions
https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2504. State: errored. Details: https:/
Appveyor build 2328. State: success. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2528. State: passed. Details: https:/
Appveyor build 2352. State: success. Details: https:/
SirVer (sirver) wrote : | # |
I starting looking through the code and there is a lot to digest here. I have a few questions:
- Why do we require the player command? It seems that we only want to mess with the normal rules of vision through scripting which has to run in parallel on each machine anyways, i.e. should not require a player command. Using a player command will be more fragile, as for example the scripting code has no guarantee when the see/unsee actually happens (can be seconds if the network connection is bad).
- The semantis of hidden_fields_ is unclear to me. What happens in this case: Field f is seen by two buildings and a worker, i.e. f.vision = 4 (1 (has been seen before) + 3 viewers). It is now hidden through scripting. The worker will walk away eventually, so 'f.vision = 3'. What happens if the field is now 'revealed'?
GunChleoc (gunchleoc) wrote : | # |
> I starting looking through the code and there is a lot to digest here. I have a few questions:
>
> - Why do we require the player command? It seems that we only want to mess with the normal rules of vision through scripting which has to run in parallel on each machine anyways, i.e. should not require a player command. Using a player command will be more fragile, as for example the scripting code has no guarantee when the see/unsee actually happens (can be seconds if the network connection is bad).
I wasn't sure whether we needed one or not and decided to be rather safe
than sorry. I don't have a problem with removing it.
> - The semantis of hidden_fields_ is unclear to me.
Join the club. I arrived at the solution pretty much through trial and
error. Since I wrote this a long time ago, I'd need to completely
reanalzye the code myself - let me know if you need me to do so and I'll
do that next month.
SirVer (sirver) wrote : | # |
> I don't have a problem with removing it.
please do and ping me for another round of reviews here.
GunChleoc (gunchleoc) wrote : | # |
I have removed the playercommand now.
Here's what I remember of the vision code:
Vision state of a field is an integer. Everything in sight of a building with vision is always visible. Every time a worker walks past an area, the vision on the field is incremented. It then slowly gets decremented until the player loses vision. Then the fields are darkened and not updated any longer, but never blacked out.
What we needed in the hide/reveal code were 2 versions of hiding fields from the player:
1. Just the darkened state
2. A permanent black state that can be undone by scripting, field by field
Buildings and workers will interfere with the hiding state, so implementing this became a bit tricky and also needed some extra data in the savegame.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2731. State: passed. Details: https:/
Appveyor build 2543. State: failed. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2778. State: passed. Details: https:/
Appveyor build 2590. State: success. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
This branch has been sitting here well tested for half a year now, so let's have it before we get more code duplication in from the next campaign scenario.
@bunnybot merge
Preview Diff
1 | === modified file 'src/logic/CMakeLists.txt' |
2 | --- src/logic/CMakeLists.txt 2017-11-20 13:50:51 +0000 |
3 | +++ src/logic/CMakeLists.txt 2017-12-19 07:13:00 +0000 |
4 | @@ -164,6 +164,7 @@ |
5 | roadtype.h |
6 | save_handler.cc |
7 | save_handler.h |
8 | + see_unsee_node.h |
9 | trade_agreement.h |
10 | widelands_geometry_io.cc |
11 | widelands_geometry_io.h |
12 | |
13 | === modified file 'src/logic/player.cc' |
14 | --- src/logic/player.cc 2017-12-02 12:43:38 +0000 |
15 | +++ src/logic/player.cc 2017-12-19 07:13:00 +0000 |
16 | @@ -173,7 +173,7 @@ |
17 | field_terrain_changed_subscriber_ = Notifications::subscribe<NoteFieldTerrainChanged>( |
18 | [this](const NoteFieldTerrainChanged& note) { |
19 | if (vision(note.map_index) > 1) { |
20 | - rediscover_node(egbase().map(), egbase().map()[0], note.fc); |
21 | + rediscover_node(egbase().map(), note.fc); |
22 | } |
23 | }); |
24 | |
25 | @@ -923,16 +923,15 @@ |
26 | } |
27 | } |
28 | |
29 | -void Player::rediscover_node(const Map& map, |
30 | - const Widelands::Field& first_map_field, |
31 | - const FCoords& f) { |
32 | +void Player::rediscover_node(const Map& map, const FCoords& f) { |
33 | |
34 | assert(0 <= f.x); |
35 | assert(f.x < map.get_width()); |
36 | assert(0 <= f.y); |
37 | assert(f.y < map.get_height()); |
38 | - assert(&map[0] <= f.field); |
39 | - assert(f.field < &map[0] + map.max_index()); |
40 | + const Widelands::Field& first_map_field = map[0]; |
41 | + assert(&first_map_field <= f.field); |
42 | + assert(f.field < &first_map_field + map.max_index()); |
43 | |
44 | Field& field = fields_[f.field - &first_map_field]; |
45 | |
46 | @@ -1031,8 +1030,8 @@ |
47 | } |
48 | } |
49 | |
50 | -void Player::see_node(const Map& map, |
51 | - const Widelands::Field& first_map_field, |
52 | +/// Returns the resulting vision. |
53 | +Vision Player::see_node(const Map& map, |
54 | const FCoords& f, |
55 | Time const gametime, |
56 | bool const forward) { |
57 | @@ -1040,7 +1039,8 @@ |
58 | assert(f.x < map.get_width()); |
59 | assert(0 <= f.y); |
60 | assert(f.y < map.get_height()); |
61 | - assert(&map[0] <= f.field); |
62 | + const Widelands::Field& first_map_field = map[0]; |
63 | + assert(&first_map_field <= f.field); |
64 | assert(f.field < &first_map_field + map.max_index()); |
65 | |
66 | // If this is not already a forwarded call, we should inform allied players |
67 | @@ -1049,31 +1049,34 @@ |
68 | update_team_players(); |
69 | if (!forward && !team_player_.empty()) { |
70 | for (uint8_t j = 0; j < team_player_.size(); ++j) |
71 | - team_player_[j]->see_node(map, first_map_field, f, gametime, true); |
72 | + team_player_[j]->see_node(map, f, gametime, true); |
73 | } |
74 | |
75 | Field& field = fields_[f.field - &first_map_field]; |
76 | assert(fields_ <= &field); |
77 | assert(&field < fields_ + map.max_index()); |
78 | - Vision fvision = field.vision; |
79 | - if (fvision == 0) |
80 | - fvision = 1; |
81 | - if (fvision == 1) |
82 | - rediscover_node(map, first_map_field, f); |
83 | - ++fvision; |
84 | - field.vision = fvision; |
85 | + |
86 | + if (field.vision == 0) { |
87 | + field.vision = 1; |
88 | + } |
89 | + if (field.vision == 1) { |
90 | + rediscover_node(map, f); |
91 | + } |
92 | + return ++field.vision; |
93 | } |
94 | |
95 | /// If 'mode' = UnseeMode::kUnexplore, fields will be marked as unexplored. Else, player no longer |
96 | -/// sees what's currently going on. |
97 | -void Player::unsee_node(MapIndex const i, |
98 | +/// sees what's currently going on. Returns the vision that this node had before it was hidden. |
99 | +Vision Player::unsee_node(MapIndex const i, |
100 | Time const gametime, |
101 | - const UnseeNodeMode mode, |
102 | + const SeeUnseeNode mode, |
103 | bool const forward) { |
104 | Field& field = fields_[i]; |
105 | - if ((mode == UnseeNodeMode::kUnsee && field.vision <= 1) || |
106 | + if ((mode == SeeUnseeNode::kUnsee && field.vision <= 1) || |
107 | field.vision < 1) // Already does not see this |
108 | - return; |
109 | + return field.vision; |
110 | + |
111 | + const Vision original_vision = field.vision; |
112 | |
113 | // If this is not already a forwarded call, we should inform allied players |
114 | // as well of this change. |
115 | @@ -1084,7 +1087,7 @@ |
116 | team_player_[j]->unsee_node(i, gametime, mode, true); |
117 | } |
118 | |
119 | - if (mode == UnseeNodeMode::kUnexplore) { |
120 | + if (mode == SeeUnseeNode::kUnexplore) { |
121 | field.vision = 0; |
122 | } else { |
123 | --field.vision; |
124 | @@ -1093,6 +1096,38 @@ |
125 | if (field.vision < 2) { |
126 | field.time_node_last_unseen = gametime; |
127 | } |
128 | + return original_vision; |
129 | +} |
130 | + |
131 | +void Player::hide_or_reveal_field(const uint32_t gametime, const Coords& coords, SeeUnseeNode mode) { |
132 | + const Map& map = egbase().map(); |
133 | + FCoords fcoords = map.get_fcoords(coords); |
134 | + const Widelands::MapIndex index = fcoords.field - &map[0]; |
135 | + |
136 | + switch (mode) { |
137 | + // Reveal field |
138 | + case SeeUnseeNode::kReveal: { |
139 | + Widelands::Vision new_vision = see_node(map, fcoords, gametime); |
140 | + // If the field was manually hidden, restore the original vision |
141 | + if (hidden_fields_.count(index) == 1) { |
142 | + auto iter = hidden_fields_.find(index); |
143 | + Vision original_vision = iter->second; |
144 | + while (new_vision < original_vision) { |
145 | + new_vision = see_node(map, fcoords, gametime); |
146 | + } |
147 | + hidden_fields_.erase(iter); |
148 | + } |
149 | + } break; |
150 | + // Hide field |
151 | + case SeeUnseeNode::kUnsee: |
152 | + case SeeUnseeNode::kUnexplore: { |
153 | + const Widelands::Vision new_vision = unsee_node(index, gametime, mode); |
154 | + // Remember the original vision so that we can unhide the fields again |
155 | + if (hidden_fields_.count(index) != 1) { |
156 | + hidden_fields_.insert(std::make_pair(index, new_vision)); |
157 | + } |
158 | + } break; |
159 | + } |
160 | } |
161 | |
162 | /** |
163 | |
164 | === modified file 'src/logic/player.h' |
165 | --- src/logic/player.h 2017-11-20 07:54:19 +0000 |
166 | +++ src/logic/player.h 2017-12-19 07:13:00 +0000 |
167 | @@ -34,6 +34,7 @@ |
168 | #include "logic/map_objects/tribes/warehouse.h" |
169 | #include "logic/mapregion.h" |
170 | #include "logic/message_queue.h" |
171 | +#include "logic/see_unsee_node.h" |
172 | #include "logic/widelands.h" |
173 | |
174 | class Node; |
175 | @@ -146,7 +147,6 @@ |
176 | // For cheating |
177 | void set_see_all(bool const t) { |
178 | see_all_ = t; |
179 | - view_changed_ = true; |
180 | } |
181 | bool see_all() const { |
182 | return see_all_; |
183 | @@ -433,37 +433,28 @@ |
184 | return (see_all_ ? 2 : 0) + fields_[i].vision; |
185 | } |
186 | |
187 | - bool has_view_changed() { |
188 | - bool t = view_changed_; |
189 | - view_changed_ = false; |
190 | - return t; |
191 | - } |
192 | - |
193 | /** |
194 | * Update this player's information about this node and the surrounding |
195 | * triangles and edges. |
196 | */ |
197 | - void see_node(const Map&, |
198 | - const Widelands::Field& first_map_field, |
199 | + Vision see_node(const Map&, |
200 | const FCoords&, |
201 | const Time, |
202 | const bool forward = false); |
203 | |
204 | /// Decrement this player's vision for a node. |
205 | - enum class UnseeNodeMode { kUnsee, kUnexplore }; |
206 | - void |
207 | - unsee_node(MapIndex, Time, UnseeNodeMode mode = UnseeNodeMode::kUnsee, bool forward = false); |
208 | + |
209 | + Vision |
210 | + unsee_node(MapIndex, Time, SeeUnseeNode mode = SeeUnseeNode::kUnsee, bool forward = false); |
211 | |
212 | /// Call see_node for each node in the area. |
213 | void see_area(const Area<FCoords>& area) { |
214 | const Time gametime = egbase().get_gametime(); |
215 | const Map& map = egbase().map(); |
216 | - const Widelands::Field& first_map_field = map[0]; |
217 | MapRegion<Area<FCoords>> mr(map, area); |
218 | do { |
219 | - see_node(map, first_map_field, mr.location(), gametime); |
220 | + see_node(map, mr.location(), gametime); |
221 | } while (mr.advance(map)); |
222 | - view_changed_ = true; |
223 | } |
224 | |
225 | /// Decrement this player's vision for each node in an area. |
226 | @@ -475,9 +466,15 @@ |
227 | do |
228 | unsee_node(mr.location().field - &first_map_field, gametime); |
229 | while (mr.advance(map)); |
230 | - view_changed_ = true; |
231 | } |
232 | |
233 | + /// Explicitly hide or reveal the field at 'c'. The modes are as follows: |
234 | + /// - kUnsee: Decrement the field's vision |
235 | + /// - kUnexplore: Set the field's vision to 0 |
236 | + /// - kReveal: If the field was hidden previously, restore the vision to the value it had |
237 | + /// at the time of hiding. Otherwise, increment the vision. |
238 | + void hide_or_reveal_field(const uint32_t gametime, const Coords& c, SeeUnseeNode mode); |
239 | + |
240 | MilitaryInfluence military_influence(MapIndex const i) const { |
241 | return fields_[i].military_influence; |
242 | } |
243 | @@ -618,7 +615,7 @@ |
244 | // Called when a node becomes seen or has changed. Discovers the node and |
245 | // those of the 6 surrounding edges/triangles that are not seen from another |
246 | // node. |
247 | - void rediscover_node(const Map&, const Widelands::Field&, const FCoords&); |
248 | + void rediscover_node(const Map&, const FCoords&); |
249 | |
250 | std::unique_ptr<Notifications::Subscriber<NoteImmovable>> immovable_subscriber_; |
251 | std::unique_ptr<Notifications::Subscriber<NoteFieldTerrainChanged>> |
252 | @@ -634,7 +631,6 @@ |
253 | std::vector<Player*> team_player_; |
254 | bool team_player_uptodate_; |
255 | bool see_all_; |
256 | - bool view_changed_; |
257 | const PlayerNumber player_number_; |
258 | const TribeDescr& tribe_; // buildings, wares, workers, sciences |
259 | uint32_t casualties_, kills_; |
260 | @@ -649,6 +645,9 @@ |
261 | std::string name_; // Player name |
262 | std::string ai_; /**< Name of preferred AI implementation */ |
263 | |
264 | + // Fields that were explicitly hidden, with their vision at the time of hiding |
265 | + std::map<MapIndex, Widelands::Vision> hidden_fields_; |
266 | + |
267 | /** |
268 | * Wares produced (by ware id) since the last call to @ref sample_statistics |
269 | */ |
270 | |
271 | === added file 'src/logic/see_unsee_node.h' |
272 | --- src/logic/see_unsee_node.h 1970-01-01 00:00:00 +0000 |
273 | +++ src/logic/see_unsee_node.h 2017-12-19 07:13:00 +0000 |
274 | @@ -0,0 +1,26 @@ |
275 | +/* |
276 | + * Copyright (C) 2017 by the Widelands Development Team |
277 | + * |
278 | + * This program is free software; you can redistribute it and/or |
279 | + * modify it under the terms of the GNU General Public License |
280 | + * as published by the Free Software Foundation; either version 2 |
281 | + * of the License, or (at your option) any later version. |
282 | + * |
283 | + * This program is distributed in the hope that it will be useful, |
284 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
285 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
286 | + * GNU General Public License for more details. |
287 | + * |
288 | + * You should have received a copy of the GNU General Public License |
289 | + * along with this program; if not, write to the Free Software |
290 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
291 | + * |
292 | + */ |
293 | + |
294 | +#ifndef WL_LOGIC_SEE_UNSEE_NODE_H |
295 | +#define WL_LOGIC_SEE_UNSEE_NODE_H |
296 | + |
297 | +namespace Widelands { |
298 | + enum class SeeUnseeNode { kUnsee, kUnexplore, kReveal }; |
299 | +} |
300 | +#endif // end of include guard: WL_LOGIC_SEE_UNSEE_NODE_H |
301 | |
302 | === modified file 'src/map_io/map_players_view_packet.cc' |
303 | --- src/map_io/map_players_view_packet.cc 2017-09-03 11:19:21 +0000 |
304 | +++ src/map_io/map_players_view_packet.cc 2017-12-19 07:13:00 +0000 |
305 | @@ -72,6 +72,9 @@ |
306 | constexpr uint8_t kCurrentPacketVersionSurveyTimes = 1; |
307 | #define SURVEY_TIMES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/survey_times_%u" |
308 | |
309 | +constexpr uint8_t kCurrentPacketVersionHidden = 1; |
310 | +#define HIDDEN_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/hidden_%u" |
311 | + |
312 | constexpr uint8_t kCurrentPacketVersionVision = 1; |
313 | #define VISION_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/vision_%u" |
314 | |
315 | @@ -292,7 +295,7 @@ |
316 | const uint16_t mapheight = map.get_height(); |
317 | Field& first_field = map[0]; |
318 | const PlayerNumber nr_players = map.get_nrplayers(); |
319 | - iterate_players_existing_const(plnum, nr_players, egbase, player) { |
320 | + iterate_players_existing(plnum, nr_players, egbase, player) { |
321 | Player::Field* const player_fields = player->fields_; |
322 | uint32_t const gametime = egbase.get_gametime(); |
323 | |
324 | @@ -466,10 +469,14 @@ |
325 | OPEN_INPUT_FILE(FileRead, survey_times_file, survey_times_filename, |
326 | SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes); |
327 | |
328 | - OPEN_INPUT_FILE_NEW_VERSION_SILENT(FileRead, border_file, border_filename, |
329 | + OPEN_INPUT_FILE_NEW_VERSION(FileRead, border_file, border_filename, |
330 | border_file_version, BORDER_FILENAME_TEMPLATE, |
331 | kCurrentPacketVersionBorder); |
332 | |
333 | + OPEN_INPUT_FILE_NEW_VERSION_SILENT(FileRead, hidden_file, hidden_filename, |
334 | + hidden_file_version, HIDDEN_FILENAME_TEMPLATE, |
335 | + kCurrentPacketVersionHidden); |
336 | + |
337 | for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight; |
338 | ++first_in_row.y, first_in_row.field += mapwidth) { |
339 | FCoords r = first_in_row, br = map.bl_n(r); |
340 | @@ -777,6 +784,21 @@ |
341 | } |
342 | } while (r.x); |
343 | } |
344 | + |
345 | + // Read the number of explicitly hidden fields and then loop through them |
346 | + if (hidden_file_version == kCurrentPacketVersionHidden) { |
347 | + const uint32_t no_of_hidden_fields = hidden_file.unsigned_32(); |
348 | + for (uint32_t i = 0; i < no_of_hidden_fields; ++i) { |
349 | + player->hidden_fields_.insert(std::make_pair(hidden_file.unsigned_32(), hidden_file.unsigned_16())); |
350 | + } |
351 | + } else if (hidden_file_version < 0) { |
352 | + // TODO(GunChleoc): Savegame compatibility - remove after Build 20 |
353 | + log("MapPlayersViewPacket - No hidden fields to read for Player %d - probably an old save file\n", plnum); |
354 | + } else { |
355 | + throw UnhandledVersionError("MapPlayersViewPacket - Hidden fields file", |
356 | + hidden_file_version, kCurrentPacketVersionHidden); |
357 | + } |
358 | + |
359 | CHECK_TRAILING_BYTES(unseen_times_file, unseen_times_filename); |
360 | CHECK_TRAILING_BYTES(node_immovable_kinds_file, node_immovable_kinds_filename); |
361 | CHECK_TRAILING_BYTES(node_immovables_file, node_immovables_filename); |
362 | @@ -788,6 +810,8 @@ |
363 | CHECK_TRAILING_BYTES(surveys_file, surveys_filename); |
364 | CHECK_TRAILING_BYTES(survey_amounts_file, survey_amounts_filename); |
365 | CHECK_TRAILING_BYTES(survey_times_file, survey_times_filename); |
366 | + CHECK_TRAILING_BYTES(border_file, border_filename); |
367 | + CHECK_TRAILING_BYTES(hidden_file, hidden_filename); |
368 | } |
369 | } |
370 | |
371 | @@ -864,6 +888,7 @@ |
372 | FileWrite surveys_file; |
373 | FileWrite survey_amounts_file; |
374 | FileWrite survey_times_file; |
375 | + FileWrite hidden_file; |
376 | FileWrite vision_file; |
377 | FileWrite border_file; |
378 | for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight; |
379 | @@ -970,6 +995,12 @@ |
380 | } |
381 | } while (r.x); |
382 | } |
383 | + // Write the number of explicitly hidden fields and then loop through them |
384 | + hidden_file.unsigned_32(player->hidden_fields_.size()); |
385 | + for (const auto& hidden : player->hidden_fields_) { |
386 | + hidden_file.unsigned_32(hidden.first); |
387 | + hidden_file.unsigned_16(hidden.second); |
388 | + } |
389 | |
390 | char filename[FILENAME_SIZE]; |
391 | |
392 | @@ -1005,6 +1036,8 @@ |
393 | |
394 | WRITE(survey_times_file, SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes); |
395 | |
396 | + WRITE(hidden_file, HIDDEN_FILENAME_TEMPLATE, kCurrentPacketVersionHidden); |
397 | + |
398 | WRITE(vision_file, VISION_FILENAME_TEMPLATE, kCurrentPacketVersionVision); |
399 | |
400 | WRITE(border_file, BORDER_FILENAME_TEMPLATE, kCurrentPacketVersionBorder); |
401 | |
402 | === modified file 'src/scripting/lua_game.cc' |
403 | --- src/scripting/lua_game.cc 2017-12-17 03:57:22 +0000 |
404 | +++ src/scripting/lua_game.cc 2017-12-19 07:13:00 +0000 |
405 | @@ -564,25 +564,23 @@ |
406 | /* RST |
407 | .. method:: reveal_fields(fields) |
408 | |
409 | - Make these fields visible for the current player. The fields will remain |
410 | - visible until they are hidden again. |
411 | + Make these fields visible for the current player. If the fields were previously hidden via |
412 | + `hide_fields`, restores the exact vision that they had before that was done. |
413 | |
414 | - :arg fields: The fields to show |
415 | + :arg fields: The fields to reveal |
416 | :type fields: :class:`array` of :class:`wl.map.Fields` |
417 | |
418 | :returns: :const:`nil` |
419 | */ |
420 | int LuaPlayer::reveal_fields(lua_State* L) { |
421 | - EditorGameBase& egbase = get_egbase(L); |
422 | - Player& p = get(L, egbase); |
423 | - const Map& map = egbase.map(); |
424 | + Game& game = get_game(L); |
425 | + Player& p = get(L, game); |
426 | |
427 | luaL_checktype(L, 2, LUA_TTABLE); |
428 | |
429 | lua_pushnil(L); /* first key */ |
430 | while (lua_next(L, 2) != 0) { |
431 | - p.see_node( |
432 | - map, map[0], (*get_user_class<LuaField>(L, -1))->fcoords(L), egbase.get_gametime()); |
433 | + p.hide_or_reveal_field(game.get_gametime(), (*get_user_class<LuaField>(L, -1))->coords(), SeeUnseeNode::kReveal); |
434 | lua_pop(L, 1); |
435 | } |
436 | |
437 | @@ -592,29 +590,28 @@ |
438 | /* RST |
439 | .. method:: hide_fields(fields) |
440 | |
441 | - Make these fields hidden for the current player if they are not |
442 | - seen by a military building. |
443 | + Make these fields hidden for the current player. |
444 | |
445 | :arg fields: The fields to hide |
446 | :type fields: :class:`array` of :class:`wl.map.Fields` |
447 | |
448 | - :arg hide_completely: *Optional*. The fields will be marked as unexplored. |
449 | - :type hide_completely: :class:`boolean` |
450 | + :arg unexplore: *Optional*. If `true`, the fields will be marked as completely unexplored. |
451 | + :type unexplore: :class:`boolean` |
452 | |
453 | :returns: :const:`nil` |
454 | */ |
455 | int LuaPlayer::hide_fields(lua_State* L) { |
456 | - EditorGameBase& egbase = get_egbase(L); |
457 | - Player& p = get(L, egbase); |
458 | + Game& game = get_game(L); |
459 | + Player& p = get(L, game); |
460 | |
461 | luaL_checktype(L, 2, LUA_TTABLE); |
462 | - const bool mode = !lua_isnone(L, 3) && luaL_checkboolean(L, 3); |
463 | + const SeeUnseeNode mode = (!lua_isnone(L, 3) && luaL_checkboolean(L, 3)) ? |
464 | + SeeUnseeNode::kUnexplore : |
465 | + SeeUnseeNode::kUnsee; |
466 | |
467 | lua_pushnil(L); /* first key */ |
468 | while (lua_next(L, 2) != 0) { |
469 | - p.unsee_node((*get_user_class<LuaField>(L, -1))->fcoords(L).field - &egbase.map()[0], |
470 | - egbase.get_gametime(), |
471 | - mode ? Player::UnseeNodeMode::kUnexplore : Player::UnseeNodeMode::kUnsee); |
472 | + p.hide_or_reveal_field(game.get_gametime(), (*get_user_class<LuaField>(L, -1))->coords(), mode); |
473 | lua_pop(L, 1); |
474 | } |
475 | |
476 | |
477 | === modified file 'test/maps/lua_testsuite.wmf/scripting/gplayer.lua' |
478 | --- test/maps/lua_testsuite.wmf/scripting/gplayer.lua 2017-03-24 08:31:08 +0000 |
479 | +++ test/maps/lua_testsuite.wmf/scripting/gplayer.lua 2017-12-19 07:13:00 +0000 |
480 | @@ -8,82 +8,6 @@ |
481 | assert_equal("Awesome Atlantean", egbase.players[3].name) |
482 | end |
483 | |
484 | --- -- ======================================================================= |
485 | --- -- See Fields/Hide Fields |
486 | --- -- ======================================================================= |
487 | -player_vision_tests = lunit.TestCase("Player vision tests") |
488 | -function player_vision_tests:setup() |
489 | - self.f = map:get_field(50, 20) |
490 | - player1.see_all = false |
491 | -end |
492 | -function player_vision_tests:teardown() |
493 | - player1:hide_fields(self.f:region(1)) |
494 | - player1.see_all = false |
495 | -end |
496 | --- This test must appear as the very first |
497 | -function player_vision_tests:test_seen_field() |
498 | - assert_equal(false, player1:sees_field(self.f)) |
499 | - assert_equal(false, player1:seen_field(self.f)) |
500 | - player1:reveal_fields(self.f:region(1)) |
501 | - player1:hide_fields(self.f:region(1)) |
502 | - assert_equal(false, player1:sees_field(self.f)) |
503 | - assert_equal(true, player1:seen_field(self.f)) |
504 | -end |
505 | - |
506 | -function player_vision_tests:test_sees_field() |
507 | - assert_equal(false, player1:sees_field(self.f)) |
508 | - player1:reveal_fields(self.f:region(1)) |
509 | - assert_equal(true, player1:sees_field(self.f)) |
510 | - player1:hide_fields(self.f:region(1)) |
511 | - assert_equal(false, player1:sees_field(self.f)) |
512 | -end |
513 | -function player_vision_tests:test_see_all() |
514 | - assert_equal(false, player1:sees_field(self.f)) |
515 | - player1.see_all = true |
516 | - assert_equal(true, player1.see_all) |
517 | - assert_equal(true, player1:sees_field(self.f)) |
518 | - player1.see_all = false |
519 | - assert_equal(false, player1:sees_field(self.f)) |
520 | -end |
521 | -function player_vision_tests:test_sees_field_see_all_hide() |
522 | - player1.see_all = true |
523 | - assert_equal(true, player1:sees_field(self.f)) |
524 | - player1:hide_fields(self.f:region(1)) |
525 | - assert_equal(true, player1:sees_field(self.f)) |
526 | - player1.see_all = false |
527 | - assert_equal(false, player1:sees_field(self.f)) |
528 | - |
529 | - player1:reveal_fields(self.f:region(1)) |
530 | - assert_equal(true, player1:sees_field(self.f)) |
531 | - player1.see_all = false |
532 | - assert_equal(true, player1:sees_field(self.f)) |
533 | -end |
534 | - |
535 | --- This test must go last, because we change the state of player1:seen_field. |
536 | -function player_vision_tests:test_hide_completely() |
537 | - player1.see_all = true |
538 | - assert_equal(true, player1:sees_field(self.f)) |
539 | - player1.see_all = false |
540 | - assert_equal(false, player1:sees_field(self.f)) |
541 | - |
542 | - player1:hide_fields(self.f:region(1), false) |
543 | - game.desired_speed = 0; |
544 | - assert_equal(false, player1:sees_field(self.f)) |
545 | - assert_equal(true, player1:seen_field(self.f)) |
546 | - |
547 | - player1.see_all = true |
548 | - assert_equal(true, player1:sees_field(self.f)) |
549 | - player1.see_all = false |
550 | - assert_equal(false, player1:sees_field(self.f)) |
551 | - |
552 | - player1:hide_fields(self.f:region(1), true) |
553 | - game.desired_speed = 0; |
554 | - assert_equal(false, player1:sees_field(self.f)) |
555 | - assert_equal(false, player1:seen_field(self.f)) |
556 | - player1:reveal_fields(self.f:region(1)) |
557 | -end |
558 | - |
559 | - |
560 | -- ========================= |
561 | -- Forbid & Allow buildings |
562 | -- ========================= |
563 | |
564 | === added file 'test/maps/plain.wmf/scripting/test_player_vision.lua' |
565 | --- test/maps/plain.wmf/scripting/test_player_vision.lua 1970-01-01 00:00:00 +0000 |
566 | +++ test/maps/plain.wmf/scripting/test_player_vision.lua 2017-12-19 07:13:00 +0000 |
567 | @@ -0,0 +1,85 @@ |
568 | +-- -- ======================================================================= |
569 | +-- -- See Fields/Hide Fields |
570 | +-- -- ======================================================================= |
571 | + |
572 | +run(function() |
573 | + function cleanup(field) |
574 | + p1:hide_fields(field:region(1)) |
575 | + p1.see_all = false |
576 | + end |
577 | + |
578 | + sleep(5000) |
579 | + local field = wl.Game().map:get_field(50, 20) |
580 | + cleanup(field) |
581 | + |
582 | + -- Seen field |
583 | + assert_equal(false, p1:sees_field(field)) |
584 | + assert_equal(false, p1:seen_field(field)) |
585 | + p1:reveal_fields(field:region(1)) |
586 | + sleep(1000) |
587 | + p1:hide_fields(field:region(1)) |
588 | + sleep(1000) |
589 | + assert_equal(false, p1:sees_field(field)) |
590 | + assert_equal(true, p1:seen_field(field)) |
591 | + cleanup(field) |
592 | + |
593 | + -- Sees field |
594 | + assert_equal(false, p1:sees_field(field)) |
595 | + p1:reveal_fields(field:region(1)) |
596 | + sleep(1000) |
597 | + assert_equal(true, p1:sees_field(field)) |
598 | + p1:hide_fields(field:region(1)) |
599 | + sleep(1000) |
600 | + assert_equal(false, p1:sees_field(field)) |
601 | + cleanup(field) |
602 | + |
603 | + -- See all |
604 | + assert_equal(false, p1:sees_field(field)) |
605 | + p1.see_all = true |
606 | + assert_equal(true, p1.see_all) |
607 | + assert_equal(true, p1:sees_field(field)) |
608 | + p1.see_all = false |
609 | + assert_equal(false, p1:sees_field(field)) |
610 | + cleanup(field) |
611 | + |
612 | + -- Sees field see all hide |
613 | + p1.see_all = true |
614 | + assert_equal(true, p1:sees_field(field)) |
615 | + p1:hide_fields(field:region(1)) |
616 | + sleep(1000) |
617 | + assert_equal(true, p1:sees_field(field)) |
618 | + p1.see_all = false |
619 | + assert_equal(false, p1:sees_field(field)) |
620 | + cleanup(field) |
621 | + |
622 | + p1:reveal_fields(field:region(1)) |
623 | + sleep(1000) |
624 | + assert_equal(true, p1:sees_field(field)) |
625 | + p1.see_all = false |
626 | + assert_equal(true, p1:sees_field(field)) |
627 | + cleanup(field) |
628 | + |
629 | + -- Unexplore |
630 | + p1:hide_fields(field:region(1), false) |
631 | + sleep(1000) |
632 | + assert_equal(false, p1:sees_field(field)) |
633 | + assert_equal(true, p1:seen_field(field)) |
634 | + |
635 | + p1.see_all = true |
636 | + assert_equal(true, p1:sees_field(field)) |
637 | + p1.see_all = false |
638 | + assert_equal(false, p1:sees_field(field)) |
639 | + |
640 | + p1:hide_fields(field:region(1), true) |
641 | + sleep(1000) |
642 | + assert_equal(false, p1:sees_field(field)) |
643 | + assert_equal(false, p1:seen_field(field)) |
644 | + p1:reveal_fields(field:region(1)) |
645 | + sleep(1000) |
646 | + assert_equal(true, p1:sees_field(field)) |
647 | + assert_equal(true, p1:seen_field(field)) |
648 | + cleanup(field) |
649 | + |
650 | + print("# All Tests passed.") |
651 | + wl.ui.MapView():close() |
652 | +end) |
Continuous integration builds have changed state:
Travis build 2154. State: passed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 229623909. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ bug_1687100_ reveal_ fields- 1989.
Appveyor build 1989. State: success. Details: https:/