1 #include <boost/algorithm/string/split.hpp>
2 #include <boost/algorithm/string.hpp>
3 #include <sstream>
4 
5 #include "asserts.hpp"
6 #include "foreach.hpp"
7 #include "formula.hpp"
8 #include "hex_map.hpp"
9 #include "hex_object.hpp"
10 #include "hex_tile.hpp"
11 #include "json_parser.hpp"
12 #include "level.hpp"
13 #include "module.hpp"
14 #include "variant_utils.hpp"
15 
16 namespace hex {
17 
18 static const int HexTileSize = 72;
19 
hex_map(variant node)20 hex_map::hex_map(variant node)
21 	: zorder_(node["zorder"].as_int(-1000)),
22 	x_(node["x"].as_int(0)),
23 	y_(node["y"].as_int(0)), width_(0), height_(0)
24 {
25 	std::vector<std::string> result;
26 	std::string tile_str = node["tiles"].as_string();
27 	boost::algorithm::erase_all(tile_str, "\t");
28 	boost::algorithm::erase_all(tile_str, "\v");
29 	boost::algorithm::erase_all(tile_str, " ");
30 	boost::algorithm::erase_all(tile_str, "\r");
31 	boost::algorithm::split(result, tile_str, std::bind2nd(std::equal_to<char>(), '\n'));
32 	int x = x_;
33 	int y = y_;
34 	foreach(const std::string& row, result) {
35 		if(row.empty()) {
36 			y++;
37 			x = x_;
38 			continue;
39 		}
40 		std::vector<std::string> row_res;
41 		boost::algorithm::split(row_res, row, std::bind2nd(std::equal_to<char>(), ','));
42 		std::vector<hex_object_ptr> new_row;
43 		foreach(const std::string& col, row_res) {
44 			new_row.push_back(hex_object_ptr(new hex_object(col, x, y, this)));
45 			x++;
46 		}
47 		y++;
48 		x = x_;
49 		tiles_.push_back(new_row);
50 		width_ = std::max<size_t>(width_, tiles_.back().size());
51 	}
52 	height_ = tiles_.size();
53 
54 #ifdef USE_GLES2
55 	if(node.has_key("shader")) {
56 		shader_.reset(new gles2::shader_program(node["shader"]));
57 	} else {
58 		shader_.reset();
59 	}
60 #endif
61 }
62 
draw() const63 void hex_map::draw() const
64 {
65 #if defined(USE_GLES2)
66 #ifndef NO_EDITOR
67 	try {
68 #endif
69 		gles2::manager manager(shader_);
70 #endif
71 		foreach(const hex_tile_row& row, tiles_) {
72 			foreach(const hex_object_ptr& col, row) {
73 				col->draw();
74 			}
75 		}
76 #if defined(USE_GLES2) && !defined(NO_EDITOR)
77 	} catch(validation_failure_exception& e) {
78 		gles2::shader::set_runtime_error("HEX MAP SHADER ERROR: " + e.msg);
79 	}
80 #endif
81 }
82 
build()83 void hex_map::build()
84 {
85 	foreach(const std::string& rule, hex_object::get_rules()) {
86 		foreach(hex_tile_row& row, tiles_) {
87 			foreach(hex_object_ptr& col, row) {
88 				col->apply_rules(rule);
89 			}
90 		}
91 	}
92 }
93 
make_tile_string() const94 std::string hex_map::make_tile_string() const
95 {
96 	std::ostringstream tiles;
97 	foreach(const hex_tile_row& row, tiles_) {
98 		for(hex_tile_row::const_iterator i = row.begin(); i != row.end(); ++i) {
99 			tiles << (*i)->type() << ((i+1) == row.end() ? "\n" : ",");
100 		}
101 	}
102 	return tiles.str();
103 }
104 
write() const105 variant hex_map::write() const
106 {
107 	variant_builder res;
108 	res.add("x", x_);
109 	res.add("y", y_);
110 	res.add("zorder", zorder_);
111 
112 	res.add("tiles", make_tile_string());
113 
114 #if defined(USE_GLES2)
115 	if(shader_) {
116 		res.add("shader", shader_->write());
117 	}
118 #endif
119 	return res.build();
120 }
121 
get_hex_tile(direction d,int x,int y) const122 hex_object_ptr hex_map::get_hex_tile(direction d, int x, int y) const
123 {
124 	int ox = x;
125 	int oy = y;
126 	x -= x_;
127 	y -= y_;
128 	if(d == NORTH) {
129 		y -= 1;
130 	} else if(d == SOUTH) {
131 		y += 1;
132 	} else if(d == NORTH_WEST) {
133 		y -= (abs(ox)%2==0) ? 1 : 0;
134 		x -= 1;
135 	} else if(d == NORTH_EAST) {
136 		y -= (abs(ox)%2==0) ? 1 : 0;
137 		x += 1;
138 	} else if(d == SOUTH_WEST) {
139 		y += (abs(ox)%2) ? 1 : 0;
140 		x -= 1;
141 	} else if(d == SOUTH_EAST) {
142 		y += (abs(ox)%2) ? 1 : 0;
143 		x += 1;
144 	} else {
145 		ASSERT_LOG(false, "Unrecognised direction: " << d);
146 	}
147 	if(x < 0 || y < 0 || size_t(y) >= tiles_.size()) {
148 		return hex_object_ptr();
149 	}
150 	if(size_t(x) >= tiles_[y].size()) {
151 		return hex_object_ptr();
152 	}
153 	return tiles_[y][x];
154 }
155 
get_value(const std::string & key) const156 variant hex_map::get_value(const std::string& key) const
157 {
158 	if(key == "x_size") {
159 		return variant(width());
160 	} else if(key == "y_size") {
161 		return variant(height());
162 	} else if(key == "size") {
163 		std::vector<variant> v;
164 		v.push_back(variant(width()));
165 		v.push_back(variant(height()));
166 		return variant(&v);
167 	} else if(key == "map") {
168 		std::vector<variant> list;
169 		foreach(const hex_tile_row& row, tiles_) {
170 			std::vector<variant> rrow;
171 			for(hex_tile_row::const_iterator i = row.begin(); i != row.end(); ++i) {
172 				rrow.push_back(variant((*i).get()));
173 			}
174 			list.push_back(variant(&rrow));
175 		}
176 		return variant(&list);
177 	} else if(key == "mapstring") {
178 		return variant(make_tile_string());
179 	} else if(key == "maplist") {
180 		std::vector<variant> list;
181 		foreach(const hex_tile_row& row, tiles_) {
182 			std::vector<variant> rrow;
183 			for(hex_tile_row::const_iterator i = row.begin(); i != row.end(); ++i) {
184 				rrow.push_back(variant((*i)->type()));
185 			}
186 			list.push_back(variant(&rrow));
187 		}
188 		return variant(&list);
189 	}
190 	return variant();
191 }
192 
set_value(const std::string & key,const variant & value)193 void hex_map::set_value(const std::string& key, const variant& value)
194 {
195 }
196 
get_tile_pos_from_pixel_pos(int mx,int my)197 point hex_map::get_tile_pos_from_pixel_pos(int mx, int my)
198 {
199 	const int tesselation_x_size = (3*HexTileSize)/2;
200 	const int tesselation_y_size = HexTileSize;
201 	const int x_base = (mx>=0) ? mx / tesselation_x_size * 2 : mx / tesselation_x_size * 2 - 2;
202 	const int x_mod  = (mx>=0) ? mx % tesselation_x_size : tesselation_x_size + (mx % tesselation_x_size);
203 	const int y_base = (my>=0) ? my / tesselation_y_size : my / tesselation_y_size - 1;
204 	const int y_mod  = (my>=0) ? my % tesselation_y_size : tesselation_y_size + (my % tesselation_y_size);
205 	const int m = 2;
206 
207 	int x_modifier = 0;
208 	int y_modifier = 0;
209 
210 	if(y_mod < tesselation_y_size / 2) {
211 		if((x_mod * m + y_mod) < (HexTileSize / 2)) {
212 			x_modifier = -1;
213 			y_modifier = -1;
214 		} else if ((x_mod * m - y_mod) < (HexTileSize * 3 / 2)) {
215 			x_modifier = 0;
216 			y_modifier = 0;
217 		} else {
218 			x_modifier = 1;
219 			y_modifier = -1;
220 		}
221 
222 	} else {
223 		if((x_mod * m - (y_mod - HexTileSize / 2)) < 0) {
224 			x_modifier = -1;
225 			y_modifier = 0;
226 		} else if((x_mod * m + (y_mod - HexTileSize / 2)) < HexTileSize * 2) {
227 			x_modifier = 0;
228 			y_modifier = 0;
229 		} else {
230 			x_modifier = 1;
231 			y_modifier = 0;
232 		}
233 	}
234 	return point(x_base + x_modifier, y_base + y_modifier);
235 }
236 
get_tile_from_pixel_pos(int mx,int my) const237 hex_object_ptr hex_map::get_tile_from_pixel_pos(int mx, int my) const
238 {
239 	point p = get_tile_pos_from_pixel_pos(mx, my);
240 	return get_tile_at(p.x, p.y);
241 }
242 
get_pixel_pos_from_tile_pos(int x,int y)243 point hex_map::get_pixel_pos_from_tile_pos(int x, int y)
244 {
245 	const int HexTileSizeHalf = HexTileSize/2;
246 	const int HexTileSizeThreeQuarters = (HexTileSize*3)/4;
247 	const int tx = x*HexTileSizeThreeQuarters;
248 	const int ty = HexTileSize*y + (abs(x)%2)*HexTileSizeHalf;
249 	return point(tx, ty);
250 }
251 
get_tile_at(int x,int y) const252 hex_object_ptr hex_map::get_tile_at(int x, int y) const
253 {
254 	x -= x_;
255 	y -= y_;
256 	if(x < 0 || y < 0 || size_t(y) >= tiles_.size()) {
257 		return hex_object_ptr();
258 	}
259 	if(size_t(x) >= tiles_[y].size()) {
260 		return hex_object_ptr();
261 	}
262 	return tiles_[y][x];
263 }
264 
set_tile(int xx,int yy,const std::string & tile)265 bool hex_map::set_tile(int xx, int yy, const std::string& tile)
266 {
267 	point p = get_tile_pos_from_pixel_pos(xx, yy);
268 
269 	// New tile position is outside current bounds, so enlarge.
270 	if(p.y < y()) {
271 		int needed_rows = y() - p.y;
272 		y_ = p.y;
273 		std::vector<hex_object_ptr> r;
274 		tiles_.insert(tiles_.begin(), r);
275 	}
276 	if(p.x < x()) {
277 		size_t needed_cols = x() - p.x;
278 		int n = x_;
279 		x_ = p.x;
280 		for(size_t j = 0; j < tiles_.size(); ++j) {
281 			for(size_t i = 0; i < needed_cols; ++i) {
282 				tiles_[j].insert(tiles_[j].begin(), new hex_object("Xv", n + x(), j + y(), this));
283 				--n;
284 			}
285 		}
286 	}
287 
288 	const int tx = p.x - x();
289 	const int ty = p.y - y();
290 	bool changed = false;
291 
292 	int needed_rows = int(size_t(ty)+1 - tiles_.size());
293 	changed |= (needed_rows > 0 );
294 	while(needed_rows-- > 0) {
295 		std::vector<hex_object_ptr> r;
296 		tiles_.push_back(r);
297 	}
298 	int needed_cols = int(size_t(tx)+1 - tiles_[ty].size());
299 	changed |= (needed_cols > 0 );
300 	int n = tx;
301 	while(needed_cols-- > 0) {
302 		// Add Void
303 		tiles_[ty].push_back(hex_object_ptr(new hex_object("Xv", n + x(), ty + y(), this)));
304 		++n;
305 	}
306 	if(tiles_[ty][tx] == NULL || tiles_[ty][tx]->type() != tile) {
307 		tiles_[ty][tx].reset(new hex_object(tile, tx + x(), ty + y(), this));
308 		changed = true;
309 	}
310 	return changed;
311 }
312 
loc_in_dir(int x,int y,direction d)313 point hex_map::loc_in_dir(int x, int y, direction d)
314 {
315 	int ox = x;
316 	int oy = y;
317 	if(d == NORTH) {
318 		y -= 1;
319 	} else if(d == SOUTH) {
320 		y += 1;
321 	} else if(d == NORTH_WEST) {
322 		y -= (abs(ox)%2==0) ? 1 : 0;
323 		x -= 1;
324 	} else if(d == NORTH_EAST) {
325 		y -= (abs(ox)%2==0) ? 1 : 0;
326 		x += 1;
327 	} else if(d == SOUTH_WEST) {
328 		y += (abs(ox)%2) ? 1 : 0;
329 		x -= 1;
330 	} else if(d == SOUTH_EAST) {
331 		y += (abs(ox)%2) ? 1 : 0;
332 		x += 1;
333 	} else {
334 		ASSERT_LOG(false, "Unrecognised direction: " << d);
335 	}
336 	return point(x, y);
337 }
338 
loc_in_dir(int x,int y,const std::string & s)339 point hex_map::loc_in_dir(int x, int y, const std::string& s)
340 {
341 	if(s == "north" || s == "n") {
342 		return loc_in_dir(x, y, NORTH);
343 	} else if(s == "south" || s == "s") {
344 		return loc_in_dir(x, y, SOUTH);
345 	} else if(s == "north_west" || s == "nw" || s == "northwest") {
346 		return loc_in_dir(x, y, NORTH_WEST);
347 	} else if(s == "north_east" || s == "ne" || s == "northeast") {
348 		return loc_in_dir(x, y, NORTH_EAST);
349 	} else if(s == "south_west" || s == "sw" || s == "southwest") {
350 		return loc_in_dir(x, y, SOUTH_WEST);
351 	} else if(s == "south_east" || s == "se" || s == "southeast") {
352 		return loc_in_dir(x, y, SOUTH_EAST);
353 	}
354 	ASSERT_LOG(false, "Unreognised direction " << s);
355 	return point();
356 }
357 
create_formula(const variant & v)358 game_logic::formula_ptr hex_map::create_formula(const variant& v)
359 {
360 	return game_logic::formula_ptr(new game_logic::formula(v));
361 }
362 
execute_command(const variant & var)363 bool hex_map::execute_command(const variant& var)
364 {
365 	bool result = true;
366 	if(var.is_null()) {
367 		return result;
368 	}
369 
370 	if(var.is_list()) {
371 		const int num_elements = var.num_elements();
372 		for(int n = 0; n != num_elements; ++n) {
373 			if(var[n].is_null() == false) {
374 				result = execute_command(var[n]) && result;
375 			}
376 		}
377 	} else {
378 		game_logic::command_callable* cmd = var.try_convert<game_logic::command_callable>();
379 		if(cmd != NULL) {
380 			cmd->execute(*this);
381 		}
382 	}
383 	return result;
384 }
385 
386 }
387