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