1 /*
2 * Copyright (C) 2008-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "logic/map_objects/checkstep.h"
21
22 #include "economy/flag.h"
23 #include "economy/road.h"
24 #include "logic/editor_game_base.h"
25 #include "logic/map.h"
26 #include "logic/map_objects/world/terrain_description.h"
27 #include "logic/map_objects/world/world.h"
28 #include "logic/player.h"
29
30 namespace Widelands {
31
32 /**
33 * The default constructor creates a functor that always returns \c false
34 * on all checks.
35 */
CheckStep()36 CheckStep::CheckStep() : capsule(always_false().capsule) {
37 }
38
39 struct CheckStepAlwaysFalse {
allowedWidelands::CheckStepAlwaysFalse40 bool allowed(const Map&, const FCoords&, const FCoords&, int32_t, CheckStep::StepId) const {
41 return false;
42 }
reachable_destWidelands::CheckStepAlwaysFalse43 bool reachable_dest(const Map&, const FCoords&) const {
44 return false;
45 }
46 };
47
always_false()48 const CheckStep& CheckStep::always_false() {
49 static const CheckStep cstep = CheckStep(CheckStepAlwaysFalse());
50 return cstep;
51 }
52
add(const CheckStep & sub)53 void CheckStepAnd::add(const CheckStep& sub) {
54 subs.push_back(sub);
55 }
56
allowed(const Map & map,const FCoords & start,const FCoords & end,int32_t const dir,CheckStep::StepId const id) const57 bool CheckStepAnd::allowed(const Map& map,
58 const FCoords& start,
59 const FCoords& end,
60 int32_t const dir,
61 CheckStep::StepId const id) const {
62 for (const CheckStep& checkstep : subs) {
63 if (!checkstep.allowed(map, start, end, dir, id)) {
64 return false;
65 }
66 }
67 return true;
68 }
69
reachable_dest(const Map & map,const FCoords & dest) const70 bool CheckStepAnd::reachable_dest(const Map& map, const FCoords& dest) const {
71 for (const CheckStep& checkstep : subs) {
72 if (!checkstep.reachable_dest(map, dest)) {
73 return false;
74 }
75 }
76 return true;
77 }
78
79 /*
80 ===============
81 CheckStepDefault
82 ===============
83 */
allowed(const Map &,const FCoords & start,const FCoords & end,int32_t,CheckStep::StepId) const84 bool CheckStepDefault::allowed(
85 const Map&, const FCoords& start, const FCoords& end, int32_t, CheckStep::StepId) const {
86 NodeCaps const endcaps = end.field->nodecaps();
87
88 if (endcaps & movecaps_)
89 return true;
90
91 // Swimming bobs are allowed to move from a water field to a shore field
92 NodeCaps const startcaps = start.field->nodecaps();
93
94 if ((endcaps & MOVECAPS_WALK) && (startcaps & movecaps_ & MOVECAPS_SWIM))
95 return true;
96
97 return false;
98 }
99
reachable_dest(const Map & map,const FCoords & dest) const100 bool CheckStepDefault::reachable_dest(const Map& map, const FCoords& dest) const {
101 NodeCaps const caps = dest.field->nodecaps();
102
103 if (!(caps & movecaps_)) {
104 if (!((movecaps_ & MOVECAPS_SWIM) && (caps & MOVECAPS_WALK)))
105 return false;
106
107 if (!map.can_reach_by_water(dest))
108 return false;
109 }
110
111 return true;
112 }
113
114 /*
115 ===============
116 CheckStepFerry
117 ===============
118 */
allowed(const Map & map,const FCoords & from,const FCoords & to,int32_t dir,CheckStep::StepId) const119 bool CheckStepFerry::allowed(
120 const Map& map, const FCoords& from, const FCoords& to, int32_t dir, CheckStep::StepId) const {
121 if (MOVECAPS_SWIM & (from.field->nodecaps() | to.field->nodecaps())) {
122 return true;
123 }
124 FCoords fd, fr;
125 switch (dir) {
126 case WALK_NE:
127 fd = to;
128 fr = map.tl_n(from);
129 break;
130 case WALK_SW:
131 fd = from;
132 fr = map.l_n(from);
133 break;
134 case WALK_NW:
135 fd = fr = to;
136 break;
137 case WALK_SE:
138 fd = fr = from;
139 break;
140 case WALK_E:
141 fd = map.tr_n(from);
142 fr = from;
143 break;
144 case WALK_W:
145 fd = map.tl_n(from);
146 fr = to;
147 break;
148 }
149 const World& world = egbase_.world();
150 return (world.terrain_descr(fd.field->terrain_d()).get_is() & TerrainDescription::Is::kWater) &&
151 (world.terrain_descr(fr.field->terrain_r()).get_is() & TerrainDescription::Is::kWater);
152 }
153
reachable_dest(const Map & map,const FCoords & dest) const154 bool CheckStepFerry::reachable_dest(const Map& map, const FCoords& dest) const {
155 for (int i = 1; i <= 6; ++i) {
156 if (allowed(map, dest, map.get_neighbour(dest, i), i, CheckStep::StepId::stepNormal)) {
157 return true;
158 }
159 }
160 return false;
161 }
162
163 /*
164 ===============
165 CheckStepWalkOn
166 ===============
167 */
allowed(const Map &,const FCoords & start,const FCoords & end,int32_t,CheckStep::StepId const id) const168 bool CheckStepWalkOn::allowed(const Map&,
169 const FCoords& start,
170 const FCoords& end,
171 int32_t,
172 CheckStep::StepId const id) const {
173 NodeCaps const startcaps = start.field->nodecaps();
174 NodeCaps const endcaps = end.field->nodecaps();
175
176 // Make sure to not find paths where we walk onto an unwalkable node, then
177 // then back onto a walkable node.
178 if (!onlyend_ && id != CheckStep::stepFirst && !(startcaps & movecaps_))
179 return false;
180
181 if (endcaps & movecaps_)
182 return true;
183
184 // We can't move onto the node using normal rules.
185 // If onlyend is true, exception rules only apply for the last step.
186 if (onlyend_ && id != CheckStep::stepLast)
187 return false;
188
189 // If the previous field was walkable, we can move onto this one
190 if (startcaps & movecaps_)
191 return true;
192
193 return false;
194 }
195
reachable_dest(const Map &,FCoords) const196 bool CheckStepWalkOn::reachable_dest(const Map&, FCoords) const {
197 // Don't bother solving this.
198 return true;
199 }
200
allowed(const Map & map,const FCoords & start,const FCoords & end,int32_t,CheckStep::StepId const id) const201 bool CheckStepRoad::allowed(const Map& map,
202 const FCoords& start,
203 const FCoords& end,
204 int32_t,
205 CheckStep::StepId const id) const {
206 uint8_t const endcaps = player_.get_buildcaps(end);
207
208 // Calculate cost and passability
209 if (!(endcaps & movecaps_) &&
210 !((endcaps & MOVECAPS_WALK) && (player_.get_buildcaps(start) & movecaps_ & MOVECAPS_SWIM)))
211 return false;
212
213 // Check for blocking immovables
214 if (BaseImmovable const* const imm = map.get_immovable(end))
215 if (imm->get_size() >= BaseImmovable::SMALL) {
216 if (id != CheckStep::stepLast)
217 return false;
218
219 return dynamic_cast<Flag const*>(imm) ||
220 (dynamic_cast<Road const*>(imm) && (endcaps & BUILDCAPS_FLAG));
221 }
222
223 return true;
224 }
225
reachable_dest(const Map & map,const FCoords & dest) const226 bool CheckStepRoad::reachable_dest(const Map& map, const FCoords& dest) const {
227 NodeCaps const caps = dest.field->nodecaps();
228
229 if (!(caps & movecaps_)) {
230 if (!((movecaps_ & MOVECAPS_SWIM) && (caps & MOVECAPS_WALK)))
231 return false;
232
233 if (!map.can_reach_by_water(dest))
234 return false;
235 }
236
237 return true;
238 }
239
allowed(const Map &,const FCoords &,const FCoords & end,int32_t,CheckStep::StepId) const240 bool CheckStepLimited::allowed(
241 const Map&, const FCoords&, const FCoords& end, int32_t, CheckStep::StepId) const {
242 return allowed_locations_.find(end) != allowed_locations_.end();
243 }
244
reachable_dest(const Map &,FCoords) const245 bool CheckStepLimited::reachable_dest(const Map&, FCoords) const {
246 return true;
247 }
248 } // namespace Widelands
249