1 // ==============================================================
2 // ==============================================================
3 //	This file is part of Glest (www.glest.org)
4 //
5 //	Copyright (C) 2001-2008 Martiño Figueroa
6 //
7 //	You can redistribute this code and/or modify it under
8 //	the terms of the GNU General Public License as published
9 //	by the Free Software Foundation; either version 2 of the
10 //	License, or (at your option) any later version
11 // ==============================================================
12 
13 #include "map.h"
14 
15 #include <cassert>
16 
17 #include "tileset.h"
18 #include "unit.h"
19 #include "resource.h"
20 #include "logger.h"
21 #include "tech_tree.h"
22 #include "config.h"
23 #include "util.h"
24 #include "game_settings.h"
25 #include "platform_util.h"
26 #include "faction.h"
27 #include "command.h"
28 #include "map_preview.h"
29 #include "world.h"
30 #include "byte_order.h"
31 #include "leak_dumper.h"
32 
33 using namespace Shared::Graphics;
34 using namespace Shared::Util;
35 using namespace Shared::Platform;
36 
37 namespace Glest{ namespace Game{
38 
39 // =====================================================
40 // 	class Cell
41 // =====================================================
42 
Cell()43 Cell::Cell() {
44 	//game data
45     for(int i = 0; i < fieldCount; ++i) {
46         units[i]= NULL;
47         unitsWithEmptyCellMap[i]=NULL;
48     }
49 	height= 0;
50 }
51 
52 // ==================== misc ====================
53 
54 //returns if the cell is free
55 
56 //returns if the cell is free
57 
saveGame(XmlNode * rootNode,int index) const58 void Cell::saveGame(XmlNode *rootNode, int index) const {
59 	bool saveCell = false;
60 	if(saveCell == false) {
61 		for(unsigned int i = 0; i < fieldCount; ++i) {
62 			if(units[i] != NULL) {
63 				saveCell = true;
64 				break;
65 			}
66 			if(unitsWithEmptyCellMap[i] != NULL) {
67 				saveCell = true;
68 				break;
69 			}
70 		}
71 	}
72 
73 	if(saveCell == true) {
74 		std::map<string,string> mapTagReplacements;
75 		XmlNode *cellNode = rootNode->addChild("Cell" + intToStr(index));
76 		cellNode->addAttribute("index",intToStr(index), mapTagReplacements);
77 
78 	//    Unit *units[fieldCount];	//units on this cell
79 		for(unsigned int i = 0; i < fieldCount; ++i) {
80 			if(units[i] != NULL) {
81 				XmlNode *unitsNode = cellNode->addChild("units");
82 				unitsNode->addAttribute("field",intToStr(i), mapTagReplacements);
83 				unitsNode->addAttribute("unitid",intToStr(units[i]->getId()), mapTagReplacements);
84 			}
85 		}
86 	//    Unit *unitsWithEmptyCellMap[fieldCount];	//units with an empty cellmap on this cell
87 		for(unsigned int i = 0; i < fieldCount; ++i) {
88 			if(unitsWithEmptyCellMap[i] != NULL) {
89 				XmlNode *unitsWithEmptyCellMapNode = cellNode->addChild("unitsWithEmptyCellMap");
90 				unitsWithEmptyCellMapNode->addAttribute("field",intToStr(i), mapTagReplacements);
91 				unitsWithEmptyCellMapNode->addAttribute("unitid",intToStr(unitsWithEmptyCellMap[i]->getId()), mapTagReplacements);
92 			}
93 		}
94 
95 	//	float height;
96 		cellNode->addAttribute("height",floatToStr(getHeight(),6), mapTagReplacements);
97 	}
98 }
99 
loadGame(const XmlNode * rootNode,int index,World * world)100 void Cell::loadGame(const XmlNode *rootNode, int index, World *world) {
101 	if(rootNode->hasChild("Cell" + intToStr(index)) == true) {
102 		const XmlNode *cellNode = rootNode->getChild("Cell" + intToStr(index));
103 
104 		unsigned int unitCount = (unsigned int)cellNode->getChildCount();
105 		for(unsigned int i = 0; i < unitCount; ++i) {
106 			if(cellNode->hasChildAtIndex("units",i) == true) {
107 				const XmlNode *unitsNode = cellNode->getChild("units",i);
108 				int field = unitsNode->getAttribute("field")->getIntValue();
109 				int unitId = unitsNode->getAttribute("unitid")->getIntValue();
110 				units[field] = world->findUnitById(unitId);
111 			}
112 			if(cellNode->hasChildAtIndex("unitsWithEmptyCellMap",i) == true) {
113 				const XmlNode *unitsNode = cellNode->getChild("unitsWithEmptyCellMap",i);
114 				int field = unitsNode->getAttribute("field")->getIntValue();
115 				int unitId = unitsNode->getAttribute("unitid")->getIntValue();
116 				unitsWithEmptyCellMap[field] = world->findUnitById(unitId);
117 			}
118 		}
119 	}
120 }
121 
122 // =====================================================
123 // 	class SurfaceCell
124 // =====================================================
125 
SurfaceCell()126 SurfaceCell::SurfaceCell() {
127 	object= NULL;
128 	vertex= Vec3f(0.f);
129 	normal= Vec3f(0.f, 1.f, 0.f);
130 	surfaceType= -1;
131 	surfaceTexture= NULL;
132 	nearSubmerged = false;
133 	cellChangedFromOriginalMapLoad = false;
134 
135 	for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) {
136 		setVisible(index,false);
137 		setExplored(index,false);
138 	}
139 }
140 
~SurfaceCell()141 SurfaceCell::~SurfaceCell() {
142 	delete object;
143 	object=NULL;
144 }
145 
end()146 void SurfaceCell::end(){
147 	if(object!=NULL){
148 		object->end();
149 	}
150 }
151 
deleteResource()152 void SurfaceCell::deleteResource() {
153 	cellChangedFromOriginalMapLoad = true;
154 
155 	delete object;
156 	object= NULL;
157 }
158 
setHeight(float height,bool cellChangedFromOriginalMapLoadValue)159 void SurfaceCell::setHeight(float height, bool cellChangedFromOriginalMapLoadValue) {
160 	height = truncateDecimal<float>(height);
161 	vertex.y= height;
162 	if(cellChangedFromOriginalMapLoadValue == true) {
163 		this->cellChangedFromOriginalMapLoad = true;
164 	}
165 }
166 
decAmount(int value)167 bool SurfaceCell::decAmount(int value) {
168 	cellChangedFromOriginalMapLoad = true;
169 
170 	return object->getResource()->decAmount(value);
171 }
setExplored(int teamIndex,bool explored)172 void SurfaceCell::setExplored(int teamIndex, bool explored) {
173 	if(teamIndex < 0 || teamIndex >= GameConstants::maxPlayers + GameConstants::specialFactions) {
174 		char szBuf[8096]="";
175 		snprintf(szBuf,8096,"Invalid value for teamIndex [%d]",teamIndex);
176 		printf("%s\n",szBuf);
177 		throw megaglest_runtime_error(szBuf);
178 	}
179 
180 	this->explored[teamIndex]= explored;
181 	//printf("Setting explored to %d for teamIndex %d\n",explored,teamIndex);
182 }
183 
setVisible(int teamIndex,bool visible)184 void SurfaceCell::setVisible(int teamIndex, bool visible) {
185 	if(teamIndex < 0 || teamIndex >= GameConstants::maxPlayers + GameConstants::specialFactions) {
186 		char szBuf[8096]="";
187 		snprintf(szBuf,8096,"Invalid value for teamIndex [%d]",teamIndex);
188 		printf("%s\n",szBuf);
189 		throw megaglest_runtime_error(szBuf);
190 	}
191 
192     this->visible[teamIndex]= visible;
193 
194 	if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true &&
195 			SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynchMax).enabled == true) {
196 		char szBuf[8096]="";
197 		snprintf(szBuf,8096,"In setVisible() teamIndex %d visible %d",teamIndex,visible);
198 
199 //		if(frameIndex < 0) {
200 //			unit->logSynchData(__FILE__,__LINE__,szBuf);
201 //		}
202 //		else {
203 //			unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf);
204 //		}
205 
206 		if(Thread::isCurrentThreadMainThread()) {
207 			//unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf);
208 			SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,szBuf);
209 		}
210 		else {
211 			//unit->logSynchData(__FILE__,__LINE__,szBuf);
212 			printf("%s",szBuf);
213 		}
214 
215 	}
216 
217 }
218 
isVisibleString() const219 string SurfaceCell::isVisibleString() const	{
220 	string result = "isVisibleList = ";
221 	for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) {
222 		result += string(visible[index] ? "true" : "false");
223 	}
224 	return result;
225 }
isExploredString() const226 string SurfaceCell::isExploredString() const {
227 	string result = "isExploredList = ";
228 	for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) {
229 		result += string(explored[index] ? "true" : "false");
230 	}
231 	return result;
232 }
233 
saveGame(XmlNode * rootNode,int index) const234 void SurfaceCell::saveGame(XmlNode *rootNode,int index) const {
235 	bool saveCell = (this->getCellChangedFromOriginalMapLoad() == true);
236 
237 	if(saveCell == true) {
238 		std::map<string,string> mapTagReplacements;
239 		XmlNode *surfaceCellNode = rootNode->addChild("SurfaceCell" + intToStr(index));
240 		surfaceCellNode->addAttribute("index",intToStr(index), mapTagReplacements);
241 
242 	//	//geometry
243 	//	Vec3f vertex;
244 		surfaceCellNode->addAttribute("vertex",vertex.getString(), mapTagReplacements);
245 	//	Vec3f normal;
246 		//surfaceCellNode->addAttribute("normal",normal.getString(), mapTagReplacements);
247 	//	Vec3f color;
248 		//surfaceCellNode->addAttribute("color",color.getString(), mapTagReplacements);
249 	//
250 	//	//tex coords
251 	//	Vec2f fowTexCoord;		//tex coords for TEXTURE1 when multitexturing and fogOfWar
252 		//surfaceCellNode->addAttribute("fowTexCoord",fowTexCoord.getString(), mapTagReplacements);
253 	//	Vec2f surfTexCoord;		//tex coords for TEXTURE0
254 		//surfaceCellNode->addAttribute("surfTexCoord",surfTexCoord.getString(), mapTagReplacements);
255 	//	//surface
256 	//	int surfaceType;
257 		//surfaceCellNode->addAttribute("surfaceType",intToStr(surfaceType), mapTagReplacements);
258 	//    const Texture2D *surfaceTexture;
259 	//
260 	//	//object & resource
261 	//	Object *object;
262 		if(object != NULL) {
263 			object->saveGame(surfaceCellNode);
264 		}
265 		else {
266 			XmlNode *objectNode = surfaceCellNode->addChild("Object");
267 			objectNode->addAttribute("isDeleted",intToStr(true), mapTagReplacements);
268 		}
269 	//	//visibility
270 	//	bool visible[GameConstants::maxPlayers + GameConstants::specialFactions];
271 //		for(unsigned int i = 0; i < GameConstants::maxPlayers; ++i) {
272 //			if(visible[i] == true) {
273 //				XmlNode *visibleNode = surfaceCellNode->addChild("visible");
274 //				visibleNode->addAttribute("index",intToStr(i), mapTagReplacements);
275 //				visibleNode->addAttribute("value",intToStr(visible[i]), mapTagReplacements);
276 //			}
277 //		}
278 //	//    bool explored[GameConstants::maxPlayers + GameConstants::specialFactions];
279 //		for(unsigned int i = 0; i < GameConstants::maxPlayers; ++i) {
280 //			if(explored[i] == true) {
281 //				XmlNode *exploredNode = surfaceCellNode->addChild("explored");
282 //				exploredNode->addAttribute("index",intToStr(i), mapTagReplacements);
283 //				exploredNode->addAttribute("value",intToStr(explored[i]), mapTagReplacements);
284 //			}
285 //		}
286 
287 	//	//cache
288 	//	bool nearSubmerged;
289 		//surfaceCellNode->addAttribute("nearSubmerged",intToStr(nearSubmerged), mapTagReplacements);
290 	}
291 }
292 
loadGame(const XmlNode * rootNode,int index,World * world)293 void SurfaceCell::loadGame(const XmlNode *rootNode, int index, World *world) {
294 	if(rootNode->hasChild("SurfaceCell" + intToStr(index)) == true) {
295 		const XmlNode *surfaceCellNode = rootNode->getChild("SurfaceCell" + intToStr(index));
296 
297 		if(surfaceCellNode->hasAttribute("vertex") == true) {
298 			vertex = Vec3f::strToVec3(surfaceCellNode->getAttribute("vertex")->getValue());
299 		}
300 
301 		//int visibleCount = cellNode->getChildCount();
302 		XmlNode *objectNode = surfaceCellNode->getChild("Object");
303 		if(objectNode->hasAttribute("isDeleted") == true) {
304 			this->deleteResource();
305 		}
306 		else {
307 			object->loadGame(surfaceCellNode,world->getTechTree());
308 		}
309 
310 		//printf("Loading game, sc index [%d][%d]\n",index,visibleCount);
311 
312 //		for(unsigned int i = 0; i < visibleCount; ++i) {
313 //			if(cellNode->hasChildAtIndex("visible",i) == true) {
314 //				const XmlNode *visibleNode = cellNode->getChild("visible",i);
315 //				int indexCell = visibleNode->getAttribute("index")->getIntValue();
316 //				bool value = visibleNode->getAttribute("value")->getIntValue();
317 //				visible[indexCell] = value;
318 //
319 //				//printf("Loading game, sc visible index [%d][%d][%d]\n",index,indexCell,value);
320 //			}
321 //			if(cellNode->hasChildAtIndex("explored",i) == true) {
322 //				const XmlNode *exploredNode = cellNode->getChild("explored",i);
323 //				int indexCell = exploredNode->getAttribute("index")->getIntValue();
324 //				bool value = exploredNode->getAttribute("value")->getIntValue();
325 //				explored[indexCell] = value;
326 //
327 //				//printf("Loading game, sc explored cell index [%d] exploredIndex [%d] value [%d]\n",index,indexCell,value);
328 //			}
329 //		}
330 	}
331 }
332 // =====================================================
333 // 	class Map
334 // =====================================================
335 
336 // ===================== PUBLIC ========================
337 
338 const int Map::cellScale= 2;
339 const int Map::mapScale= 2;
340 
Map()341 Map::Map() {
342 	cells= NULL;
343 	surfaceCells= NULL;
344 	startLocations= NULL;
345 
346 	title="";
347 	waterLevel=0;
348 	heightFactor=0;
349 	cliffLevel=0;
350 	cameraHeight=0;
351 	w=0;
352 	h=0;
353 	surfaceW=0;
354 	surfaceH=0;
355 	surfaceSize=(surfaceW * surfaceH);
356 	maxPlayers=0;
357 	maxMapHeight=0;
358 }
359 
~Map()360 Map::~Map() {
361 	Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMapCells","",true), true);
362 
363 	delete [] cells;
364 	cells = NULL;
365 	delete [] surfaceCells;
366 	surfaceCells = NULL;
367 	delete [] startLocations;
368 	startLocations = NULL;
369 }
370 
end()371 void Map::end(){
372 	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
373     Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMap","",true), true);
374 	//read heightmap
375 	for(int j = 0; j < surfaceH; ++j) {
376 		for(int i = 0; i < surfaceW; ++i) {
377 			getSurfaceCell(i, j)->end();
378 		}
379 	}
380 	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
381 }
382 
getStartLocation(int locationIndex) const383 Vec2i Map::getStartLocation(int locationIndex) const {
384 	if(locationIndex >= maxPlayers) {
385 		char szBuf[8096]="";
386 		snprintf(szBuf,8096,"locationIndex >= maxPlayers [%d] [%d]",locationIndex, maxPlayers);
387 		printf("%s\n",szBuf);
388 		throw megaglest_runtime_error(szBuf);
389 		//assert(locationIndex < maxPlayers);
390 	}
391 	else if(startLocations == NULL) {
392 		throw megaglest_runtime_error("startLocations == NULL");
393 	}
394 
395 	return startLocations[locationIndex];
396 }
397 
load(const string & path,TechTree * techTree,Tileset * tileset)398 Checksum Map::load(const string &path, TechTree *techTree, Tileset *tileset) {
399     Checksum mapChecksum;
400 	try{
401 #ifdef WIN32
402 		FILE *f= _wfopen(utf8_decode(path).c_str(), L"rb");
403 #else
404 		FILE *f = fopen(path.c_str(), "rb");
405 #endif
406 		if(f != NULL) {
407 			mapFile = path;
408 
409 		    mapChecksum.addFile(path);
410 		    checksumValue.addFile(path);
411 			//read header
412 			MapFileHeader header;
413 			size_t readBytes = fread(&header, sizeof(MapFileHeader), 1, f);
414 			if(readBytes != 1) {
415 				throw megaglest_runtime_error("Invalid map header detected for file: " + path);
416 			}
417 			fromEndianMapFileHeader(header);
418 
419 			if(next2Power(header.width) != header.width){
420 				throw megaglest_runtime_error("Map width is not a power of 2");
421 			}
422 
423 			if(next2Power(header.height) != header.height){
424 				throw megaglest_runtime_error("Map height is not a power of 2");
425 			}
426 
427 			heightFactor= header.heightFactor;
428 			if(heightFactor>100){
429 				heightFactor=heightFactor/100;
430 				heightFactor = truncateDecimal<float>(heightFactor,6);
431 			}
432 			waterLevel= static_cast<float>((header.waterLevel-0.01f)/heightFactor);
433 			waterLevel = truncateDecimal<float>(waterLevel,6);
434 			title= header.title;
435 			maxPlayers= header.maxFactions;
436 
437 			surfaceW= header.width;
438 			surfaceH= header.height;
439 			surfaceSize=(surfaceW * surfaceH);
440 
441 			w= surfaceW*cellScale;
442 			h= surfaceH*cellScale;
443 			cliffLevel = 0;
444 			cameraHeight = 0;
445 			if(header.version==1){
446 				//desc = header.description;
447 			}
448 			else if(header.version==2){
449 				//desc = header.version2.short_desc;
450 				if(header.version2.cliffLevel > 0  && header.version2.cliffLevel < 5000){
451 					cliffLevel=static_cast<float>((header.version2.cliffLevel-0.01f)/(heightFactor));
452 					cliffLevel = truncateDecimal<float>(cliffLevel,6);
453 				}
454 				if(header.version2.cameraHeight > 0 && header.version2.cameraHeight < 5000) {
455 					cameraHeight = header.version2.cameraHeight;
456 				}
457 			}
458 
459 			//start locations
460 			startLocations= new Vec2i[maxPlayers];
461 			for(int i=0; i < maxPlayers; ++i) {
462 				int x=0, y=0;
463 				readBytes = fread(&x, sizeof(int32), 1, f);
464 				if(readBytes != 1) {
465 					char szBuf[8096]="";
466 					snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__);
467 					throw megaglest_runtime_error(szBuf);
468 				}
469 				x = ::Shared::PlatformByteOrder::fromCommonEndian(x);
470 
471 				readBytes = fread(&y, sizeof(int32), 1, f);
472 				if(readBytes != 1) {
473 					char szBuf[8096]="";
474 					snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__);
475 					throw megaglest_runtime_error(szBuf);
476 				}
477 				y = ::Shared::PlatformByteOrder::fromCommonEndian(y);
478 
479 				startLocations[i]= Vec2i(x, y)*cellScale;
480 			}
481 
482 			//cells
483 			cells= new Cell[getCellArraySize()];
484 			surfaceCells= new SurfaceCell[getSurfaceCellArraySize()];
485 
486 			//read heightmap
487 			for(int j = 0; j < surfaceH; ++j) {
488 				for(int i = 0; i < surfaceW; ++i) {
489 					float32 alt=0;
490 					readBytes = fread(&alt, sizeof(float32), 1, f);
491 					if(readBytes != 1) {
492 						char szBuf[8096]="";
493 						snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__);
494 						throw megaglest_runtime_error(szBuf);
495 					}
496 					alt = ::Shared::PlatformByteOrder::fromCommonEndian(alt);
497 
498 					SurfaceCell *sc= getSurfaceCell(i, j);
499 					sc->setVertex(Vec3f(i*mapScale, alt / heightFactor, j*mapScale));
500 				}
501 			}
502 
503 			//read surfaces
504 			for(int j = 0; j < surfaceH; ++j) {
505 				for(int i = 0; i < surfaceW; ++i) {
506 					int8 surf=0;
507 					readBytes = fread(&surf, sizeof(int8), 1, f);
508 					if(readBytes != 1) {
509 						char szBuf[8096]="";
510 						snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__);
511 						throw megaglest_runtime_error(szBuf);
512 					}
513 					surf = ::Shared::PlatformByteOrder::fromCommonEndian(surf);
514 
515 					getSurfaceCell(i, j)->setSurfaceType(surf-1);
516 				}
517 			}
518 
519 			//read objects and resources
520 			for(int j = 0; j < h; j += cellScale) {
521 				for(int i = 0; i < w; i += cellScale) {
522 
523 					int8 objNumber=0;
524 					readBytes = fread(&objNumber, sizeof(int8), 1, f);
525 					if(readBytes != 1) {
526 						char szBuf[8096]="";
527 						snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__);
528 						throw megaglest_runtime_error(szBuf);
529 					}
530 					objNumber = ::Shared::PlatformByteOrder::fromCommonEndian(objNumber);
531 
532 					SurfaceCell *sc= getSurfaceCell(toSurfCoords(Vec2i(i, j)));
533 					if(objNumber <= 0) {
534 						sc->setObject(NULL);
535 					}
536 					else if(objNumber <= Tileset::objCount) {
537 						Object *o= new Object(tileset->getObjectType(objNumber-1), sc->getVertex(),Vec2i(i, j));
538 						sc->setObject(o);
539 						for(int k = 0; k < techTree->getResourceTypeCount(); ++k) {
540 							const ResourceType *rt= techTree->getResourceType(k);
541 							if(rt->getClass() == rcTileset && rt->getTilesetObject() == objNumber){
542 								o->setResource(rt, Vec2i(i, j));
543 							}
544 						}
545 					}
546 					else{
547 						const ResourceType *rt= techTree->getTechResourceType(objNumber - Tileset::objCount) ;
548 						Object *o= new Object(NULL, sc->getVertex(),Vec2i(i, j));
549 						o->setResource(rt, Vec2i(i, j));
550 						sc->setObject(o);
551 					}
552 				}
553 			}
554 			if(f) fclose(f);
555 		}
556 		else {
557 			throw megaglest_runtime_error("Can't open file");
558 		}
559 	}
560 	catch(const exception &e){
561 		SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,e.what());
562 		throw megaglest_runtime_error("Error loading map: "+ path+ "\n"+ e.what());
563 	}
564 
565 	return mapChecksum;
566 }
567 
init(Tileset * tileset)568 void Map::init(Tileset *tileset) {
569 	Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMap","",true), true);
570 	maxMapHeight=0.0f;
571 	smoothSurface(tileset);
572 	computeNormals();
573 	computeInterpolatedHeights();
574 	computeNearSubmerged();
575 	computeCellColors();
576 }
577 
578 
579 // ==================== is ====================
580 
581 class FindBestPos  {
582 public:
583 	float distanceFromUnitNoAdjustment;
584 	float distanceFromClickNoAdjustment;
585 	Vec2i resourcePosNoAdjustment;
586 };
587 
588 //returns if there is a resource next to a unit, in "resourcePos" is stored the relative position of the resource
isResourceNear(int frameIndex,const Vec2i & pos,const ResourceType * rt,Vec2i & resourcePos,int size,Unit * unit,bool fallbackToPeersHarvestingSameResource,Vec2i * resourceClickPos) const589 bool Map::isResourceNear(int frameIndex,const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos,
590 		int size, Unit *unit, bool fallbackToPeersHarvestingSameResource,
591 		Vec2i *resourceClickPos) const {
592 
593 	bool resourceNear = false;
594 	float distanceFromUnit=-1;
595 	float distanceFromClick=-1;
596 
597 	if(resourceClickPos) {
598 		//printf("+++++++++ unit [%s - %d] pos = [%s] resourceClickPos [%s]\n",unit->getFullName().c_str(),unit->getId(),pos.getString().c_str(),resourceClickPos->getString().c_str());
599 	}
600 	for(int i = -size; i <= size; ++i) {
601 		for(int j = -size; j <= size; ++j) {
602 			Vec2i resPos = Vec2i(pos.x + i, pos.y + j);
603 			if(resourceClickPos) {
604 				resPos = Vec2i(resourceClickPos->x + i, resourceClickPos->y + j);
605 			}
606 			Vec2i surfCoords = toSurfCoords(resPos);
607 
608 			if(isInside(resPos) && isInsideSurface(surfCoords)) {
609 				Resource *r= getSurfaceCell(surfCoords)->getResource();
610 				if(r != NULL) {
611 					if(r->getType() == rt) {
612 						if(resourceClickPos) {
613 							//printf("****** unit [%s - %d] resPos = [%s] resourceClickPos->dist(resPos) [%f] distanceFromClick [%f] unit->getCenteredPos().dist(resPos) [%f] distanceFromUnit [%f]\n",unit->getFullName().c_str(),unit->getId(),resPos.getString().c_str(),resourceClickPos->dist(resPos),distanceFromClick,unit->getCenteredPos().dist(resPos),distanceFromUnit);
614 						}
615 						if(resourceClickPos == NULL ||
616 							(distanceFromClick < 0 || resourceClickPos->dist(resPos) <= distanceFromClick)) {
617 							if(unit == NULL ||
618 								(distanceFromUnit < 0 || unit->getCenteredPos().dist(resPos) <= distanceFromUnit)) {
619 
620 								bool isResourceNextToUnit = (resourceClickPos == NULL);
621 								for(int i1 = -size; isResourceNextToUnit == false && i1 <= size; ++i1) {
622 									for(int j1 = -size; j1 <= size; ++j1) {
623 										Vec2i resPos1 = Vec2i(pos.x + i1, pos.y + j1);
624 										if(resPos == resPos1) {
625 											isResourceNextToUnit = true;
626 											break;
627 										}
628 									}
629 								}
630 								if(isResourceNextToUnit == true) {
631 									if(resourceClickPos != NULL) {
632 										distanceFromClick = resourceClickPos->dist(resPos);
633 									}
634 									if(unit != NULL) {
635 										distanceFromUnit = unit->getCenteredPos().dist(resPos);
636 									}
637 
638 									resourcePos= pos + Vec2i(i,j);
639 
640 									if(unit == NULL || unit->isBadHarvestPos(resourcePos) == false) {
641 										resourceNear = true;
642 
643 										if(resourceClickPos) {
644 											//printf("@@@@@@@@ unit [%s - %d] resPos = [%s] resourceClickPos->dist(resPos) [%f] distanceFromClick [%f] unit->getCenteredPos().dist(resPos) [%f] distanceFromUnit [%f]\n",unit->getFullName().c_str(),unit->getId(),resPos.getString().c_str(),resourceClickPos->dist(resPos),distanceFromClick,unit->getCenteredPos().dist(resPos),distanceFromUnit);
645 										}
646 									}
647 								}
648 							}
649 						}
650 					}
651 				}
652 			}
653 		}
654 	}
655 
656 	if(resourceNear == false) {
657 		if(fallbackToPeersHarvestingSameResource == true && unit != NULL) {
658 			// Look for another unit that is currently harvesting the same resource
659 			// type right now
660 
661 			// Check the faction cache for a known position where we can harvest
662 			// this resource type
663 			Vec2i result = unit->getFaction()->getClosestResourceTypeTargetFromCache(unit, rt,frameIndex);
664 			if(result.x >= 0) {
665 				resourcePos = result;
666 
667 				if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true) {
668 					char szBuf[8096]="";
669 					snprintf(szBuf,8096,"[found peer harvest pos] pos [%s] resourcePos [%s] unit->getFaction()->getCacheResourceTargetListSize() [%d]",
670 										pos.getString().c_str(),resourcePos.getString().c_str(),unit->getFaction()->getCacheResourceTargetListSize());
671 
672 					if(frameIndex < 0) {
673 						unit->logSynchData(__FILE__,__LINE__,szBuf);
674 					}
675 					else {
676 						unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf);
677 					}
678 				}
679 
680 				if(unit->getPos().dist(resourcePos) <= size) {
681 					resourceNear = true;
682 
683 					if(resourceClickPos) {
684 						//printf("###### unit [%s - %d]\n",unit->getFullName().c_str(),unit->getId());
685 					}
686 				}
687 			}
688 		}
689 	}
690 
691 	if(resourceNear == false && resourceClickPos != NULL) {
692 		std::vector<FindBestPos> bestPosList;
693 
694 		//if(resourceClickPos) {
695 			//printf("^^^^^ unit [%s - %d]\n",unit->getFullName().c_str(),unit->getId());
696 		//}
697 
698 		for(int i = -1; i <= 1; ++i) {
699 			for(int j = -1; j <= 1; ++j) {
700 				Vec2i resPos = Vec2i(resourceClickPos->x + i, resourceClickPos->y + j);
701 				Vec2i surfCoords = toSurfCoords(resPos);
702 
703 				if(isInside(resPos) && isInsideSurface(surfCoords)) {
704 					Resource *r= getSurfaceCell(surfCoords)->getResource();
705 					if(r != NULL) {
706 						if(r->getType() == rt) {
707 							//printf("^^^^^^ unit [%s - %d] resPos = [%s] resourceClickPos->dist(resPos) [%f] distanceFromClick [%f] unit->getCenteredPos().dist(resPos) [%f] distanceFromUnit [%f]\n",unit->getFullName().c_str(),unit->getId(),resPos.getString().c_str(),resourceClickPos->dist(resPos),distanceFromClick,unit->getCenteredPos().dist(resPos),distanceFromUnit);
708 
709 							if(unit == NULL ||
710 								(distanceFromUnit < 0 || unit->getCenteredPos().dist(resPos) <= (distanceFromUnit + 2.0))) {
711 
712 								if(resourceClickPos->dist(resPos) <= 1.0) {
713 									if(unit != NULL) {
714 										FindBestPos bestPosItem;
715 
716 										bestPosItem.distanceFromUnitNoAdjustment = unit->getCenteredPos().dist(resPos);
717 										bestPosItem.distanceFromClickNoAdjustment =distanceFromClick = resourceClickPos->dist(resPos);
718 										bestPosItem.resourcePosNoAdjustment = resPos;
719 
720 										bestPosList.push_back(bestPosItem);
721 									}
722 								}
723 
724 								//printf("!!!! unit [%s - %d] resPos = [%s] resourceClickPos->dist(resPos) [%f] distanceFromClick [%f] unit->getCenteredPos().dist(resPos) [%f] distanceFromUnit [%f]\n",unit->getFullName().c_str(),unit->getId(),resPos.getString().c_str(),resourceClickPos->dist(resPos),distanceFromClick,unit->getCenteredPos().dist(resPos),distanceFromUnit);
725 								if(distanceFromClick < 0 || resourceClickPos->dist(resPos) <= distanceFromClick) {
726 									if(resourceClickPos != NULL) {
727 										distanceFromClick = resourceClickPos->dist(resPos);
728 									}
729 									if(unit != NULL) {
730 										distanceFromUnit = unit->getCenteredPos().dist(resPos);
731 									}
732 
733 									*resourceClickPos = resPos;
734 
735 									if(unit == NULL || unit->isBadHarvestPos(*resourceClickPos) == false) {
736 										//resourceNear = true;
737 
738 										//printf("%%----------- unit [%s - %d] resPos = [%s] resourceClickPos->dist(resPos) [%f] distanceFromClick [%f] unit->getCenteredPos().dist(resPos) [%f] distanceFromUnit [%f]\n",unit->getFullName().c_str(),unit->getId(),resPos.getString().c_str(),resourceClickPos->dist(resPos),distanceFromClick,unit->getCenteredPos().dist(resPos),distanceFromUnit);
739 									}
740 								}
741 							}
742 						}
743 					}
744 				}
745 			}
746 		}
747 
748 		float bestUnitDist = distanceFromUnit;
749 		for(unsigned int i = 0; i < bestPosList.size(); ++i) {
750 			FindBestPos &bestPosItem = bestPosList[i];
751 
752 			if(bestPosItem.distanceFromUnitNoAdjustment < bestUnitDist) {
753 				bestUnitDist = bestPosItem.distanceFromUnitNoAdjustment;
754 				*resourceClickPos = bestPosItem.resourcePosNoAdjustment;
755 
756 				if(unit == NULL || unit->isBadHarvestPos(*resourceClickPos) == false) {
757 					//printf("%%----------- unit [%s - %d] resourceClickPos [%s] bestUnitDist [%f]\n",unit->getFullName().c_str(),unit->getId(),resourceClickPos->getString().c_str(),bestUnitDist);
758 				}
759 			}
760 		}
761 	}
762 
763 	return resourceNear;
764 }
765 
766 // ==================== free cells ====================
767 
isFreeCell(const Vec2i & pos,Field field) const768 bool Map::isFreeCell(const Vec2i &pos, Field field) const {
769 	return
770 		isInside(pos) &&
771 		isInsideSurface(toSurfCoords(pos)) &&
772 		getCell(pos)->isFree(field) &&
773 		(field==fAir || getSurfaceCell(toSurfCoords(pos))->isFree()) &&
774 		(field!=fLand || getDeepSubmerged(getCell(pos)) == false);
775 }
776 
777 
isFreeCellOrHasUnit(const Vec2i & pos,Field field,const Unit * unit) const778 bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const{
779 	if(isInside(pos)){
780 		Cell *c= getCell(pos);
781 		if(c->getUnit(field) == unit && unit != NULL) {
782 			return true;
783 		}
784 		else{
785 			return isFreeCell(pos, field);
786 		}
787 	}
788 	return false;
789 }
790 
791 //TT: this is much more complicated compared with the old one above. I think its no more needed
792 //bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const {
793 //	if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) {
794 //		if(unit->getCurrField() != field) {
795 //			return isFreeCell(pos, field);
796 //		}
797 //		Cell *c= getCell(pos);
798 //		if(c->getUnit(unit->getCurrField()) == unit) {
799 //			if(unit->getCurrField() == fAir) {
800 //				if(field == fAir) {
801 //					return true;
802 //				}
803 //				const SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos));
804 //				if(sc != NULL) {
805 //					if(getDeepSubmerged(sc) == true) {
806 //						return false;
807 //					}
808 //					else if(field == fLand) {
809 //						if(sc->isFree() == false) {
810 //							return false;
811 //						}
812 //						else if(c->getUnit(field) != NULL) {
813 //							return false;
814 //						}
815 //					}
816 //				}
817 //			}
818 //			return true;
819 //		}
820 //		else{
821 //			return isFreeCell(pos, field);
822 //		}
823 //	}
824 //	return false;
825 //}
826 
isAproxFreeCell(const Vec2i & pos,Field field,int teamIndex) const827 bool Map::isAproxFreeCell(const Vec2i &pos, Field field, int teamIndex) const {
828 	if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) {
829 		const SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos));
830 
831 		if(sc->isVisible(teamIndex)) {
832 			return isFreeCell(pos, field);
833 		}
834 		else if(sc->isExplored(teamIndex)) {
835 			return field==fLand? sc->isFree() && !getDeepSubmerged(getCell(pos)): true;
836 		}
837 		else {
838 			return true;
839 		}
840 	}
841 
842 	//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
843 	return false;
844 }
845 
isFreeCells(const Vec2i & pos,int size,Field field) const846 bool Map::isFreeCells(const Vec2i & pos, int size, Field field) const  {
847 	for(int i=pos.x; i<pos.x+size; ++i) {
848 		for(int j=pos.y; j<pos.y+size; ++j) {
849 			Vec2i testPos(i,j);
850 			if(isFreeCell(testPos, field) == false) {
851 				return false;
852 			}
853 		}
854 	}
855     return true;
856 }
857 
isFreeCellsOrHasUnit(const Vec2i & pos,int size,Field field,const Unit * unit) const858 bool Map::isFreeCellsOrHasUnit(const Vec2i &pos, int size, Field field,
859 		const Unit *unit) const {
860 	for(int i = pos.x; i < pos.x + size; ++i) {
861 		for(int j = pos.y; j < pos.y + size; ++j) {
862 			if(isFreeCellOrHasUnit(Vec2i(i,j), field, unit) == false) {
863 				return false;
864 			}
865 		}
866 	}
867 	return true;
868 }
869 
isAproxFreeCells(const Vec2i & pos,int size,Field field,int teamIndex) const870 bool Map::isAproxFreeCells(const Vec2i &pos, int size, Field field, int teamIndex) const {
871 	for(int i=pos.x; i<pos.x+size; ++i) {
872 		for(int j=pos.y; j<pos.y+size; ++j) {
873 			if(isAproxFreeCell(Vec2i(i, j), field, teamIndex) == false) {
874                 return false;
875 			}
876 		}
877 	}
878     return true;
879 }
880 
canMorph(const Vec2i & pos,const Unit * currentUnit,const UnitType * targetUnitType) const881 bool Map::canMorph(const Vec2i &pos,const Unit *currentUnit,const UnitType *targetUnitType ) const{
882 	Field field=targetUnitType->getField();
883 	const UnitType *ut=targetUnitType;
884 	CardinalDir facing=currentUnit->getModelFacing();
885 
886 	if (ut->hasCellMap() && isInside(pos) && isInsideSurface(toSurfCoords(pos))) {
887 		for (int y=0; y < ut->getSize(); ++y) {
888 			for (int x=0; x < ut->getSize(); ++x) {
889 				Vec2i cellPos = pos + Vec2i(x, y);
890 				if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) {
891 					if (ut->getCellMapCell(x, y, facing)) {
892 						if (isFreeCellOrHasUnit(cellPos, field, currentUnit) == false) {
893 							return false;
894 						}
895 					}
896 				}
897 				else {
898 					return false;
899 				}
900 			}
901 		}
902 		return true;
903 	}
904 	else {
905 		return isFreeCellsOrHasUnit(pos, ut->getSize(), field,currentUnit);
906 	}
907 }
908 
canOccupy(const Vec2i & pos,Field field,const UnitType * ut,CardinalDir facing)909 bool Map::canOccupy(const Vec2i &pos, Field field, const UnitType *ut, CardinalDir facing) {
910 	if (ut->hasCellMap() && isInside(pos) && isInsideSurface(toSurfCoords(pos))) {
911 		for (int y=0; y < ut->getSize(); ++y) {
912 			for (int x=0; x < ut->getSize(); ++x) {
913 				Vec2i cellPos = pos + Vec2i(x, y);
914 				if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) {
915 					if (ut->getCellMapCell(x, y, facing)) {
916 						if (isFreeCell(cellPos, field) == false) {
917 							return false;
918 						}
919 					}
920 				}
921 				else {
922 					return false;
923 				}
924 			}
925 		}
926 		return true;
927 	}
928 	else {
929 		return isFreeCells(pos, ut->getSize(), field);
930 	}
931 }
932 
933 // ==================== unit placement ====================
934 
935 //checks if a unit can move from between 2 cells
canMove(const Unit * unit,const Vec2i & pos1,const Vec2i & pos2,std::map<Vec2i,std::map<Vec2i,std::map<int,std::map<Field,bool>>>> * lookupCache) const936 bool Map::canMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2, std::map<Vec2i, std::map<Vec2i, std::map<int, std::map<Field,bool> > > > *lookupCache) const {
937 	int size= unit->getType()->getSize();
938 	Field field= unit->getCurrField();
939 
940 	if(lookupCache != NULL) {
941 		std::map<Vec2i, std::map<Vec2i, std::map<int, std::map<Field,bool> > > >::const_iterator iterFind1 = lookupCache->find(pos1);
942 		if(iterFind1 != lookupCache->end()) {
943 			std::map<Vec2i, std::map<int, std::map<Field,bool> > >::const_iterator iterFind2 = iterFind1->second.find(pos2);
944 			if(iterFind2 != iterFind1->second.end()) {
945 				std::map<int, std::map<Field,bool> >::const_iterator iterFind3 = iterFind2->second.find(size);
946 				if(iterFind3 != iterFind2->second.end()) {
947 					std::map<Field,bool>::const_iterator iterFind4 = iterFind3->second.find(field);
948 					if(iterFind4 != iterFind3->second.end()) {
949 						// Found this result in the cache
950 						return iterFind4->second;
951 					}
952 				}
953 			}
954 		}
955 	}
956 
957 	for(int i=pos2.x; i<pos2.x+size; ++i) {
958 		for(int j=pos2.y; j<pos2.y+size; ++j) {
959 			if(isInside(i, j) && isInsideSurface(toSurfCoords(Vec2i(i,j)))) {
960 				if(getCell(i, j)->getUnit(field) != unit) {
961 					if(isFreeCell(Vec2i(i, j), field) == false) {
962 						if(lookupCache != NULL) {
963 							(*lookupCache)[pos1][pos2][size][field]=false;
964 						}
965 
966 						return false;
967 					}
968 				}
969 			}
970 			else {
971 				if(lookupCache != NULL) {
972 					(*lookupCache)[pos1][pos2][size][field]=false;
973 				}
974 
975 				return false;
976 			}
977 		}
978 	}
979 
980 	bool isBadHarvestPos = false;
981 	//if(unit != NULL) {
982 		Command *command= unit->getCurrCommand();
983 		if(command != NULL) {
984 			const HarvestCommandType *hct = dynamic_cast<const HarvestCommandType*>(command->getCommandType());
985 			if(hct != NULL && unit->isBadHarvestPos(pos2) == true) {
986 				isBadHarvestPos = true;
987 			}
988 		}
989 	//}
990 
991 	if(isBadHarvestPos == true) {
992 		if(lookupCache != NULL) {
993 			(*lookupCache)[pos1][pos2][size][field]=false;
994 		}
995 
996 		return false;
997 	}
998 
999 	if(lookupCache != NULL) {
1000 		(*lookupCache)[pos1][pos2][size][field]=true;
1001 	}
1002 
1003     return true;
1004 }
1005 
1006 //checks if a unit can move from between 2 cells using only visible cells (for pathfinding)
aproxCanMove(const Unit * unit,const Vec2i & pos1,const Vec2i & pos2,std::map<Vec2i,std::map<Vec2i,std::map<int,std::map<int,std::map<Field,bool>>>>> * lookupCache) const1007 bool Map::aproxCanMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2, std::map<Vec2i, std::map<Vec2i, std::map<int, std::map<int, std::map<Field,bool> > > > > *lookupCache) const {
1008 	if(isInside(pos1) == false || isInsideSurface(toSurfCoords(pos1)) == false ||
1009 	   isInside(pos2) == false || isInsideSurface(toSurfCoords(pos2)) == false) {
1010 
1011 		//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1012 		return false;
1013 	}
1014 
1015 	if(unit == NULL) {
1016 		throw megaglest_runtime_error("unit == NULL");
1017 	}
1018 	int size= unit->getType()->getSize();
1019 	int teamIndex= unit->getTeam();
1020 	Field field= unit->getCurrField();
1021 
1022 	if(lookupCache != NULL) {
1023 		std::map<Vec2i, std::map<Vec2i, std::map<int, std::map<int, std::map<Field,bool> > > > >::const_iterator iterFind1 = lookupCache->find(pos1);
1024 		if(iterFind1 != lookupCache->end()) {
1025 			std::map<Vec2i, std::map<int, std::map<int, std::map<Field,bool> > > >::const_iterator iterFind2 = iterFind1->second.find(pos2);
1026 			if(iterFind2 != iterFind1->second.end()) {
1027 				std::map<int, std::map<int, std::map<Field,bool> > >::const_iterator iterFind3 = iterFind2->second.find(teamIndex);
1028 				if(iterFind3 != iterFind2->second.end()) {
1029 					std::map<int, std::map<Field,bool> >::const_iterator iterFind4 = iterFind3->second.find(size);
1030 					if(iterFind4 != iterFind3->second.end()) {
1031 						std::map<Field,bool>::const_iterator iterFind5 = iterFind4->second.find(field);
1032 						if(iterFind5 != iterFind4->second.end()) {
1033 							// Found this result in the cache
1034 							if(iterFind5->second == false) {
1035 								//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1036 							}
1037 							return iterFind5->second;
1038 						}
1039 					}
1040 				}
1041 			}
1042 		}
1043 	}
1044 
1045 	//single cell units
1046 	if(size == 1) {
1047 		if(isAproxFreeCell(pos2, field, teamIndex) == false) {
1048 			if(lookupCache != NULL) {
1049 				(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1050 			}
1051 
1052 			//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1053 			return false;
1054 		}
1055 		if(pos1.x != pos2.x && pos1.y != pos2.y) {
1056 			if(isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex) == false) {
1057 				if(lookupCache != NULL) {
1058 					(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1059 				}
1060 
1061 				//Unit *cellUnit = getCell(Vec2i(pos1.x, pos2.y))->getUnit(field);
1062 				//Object * obj = getSurfaceCell(toSurfCoords(Vec2i(pos1.x, pos2.y)))->getObject();
1063 
1064 				//printf("[%s] Line: %d returning false cell [%s] free [%d] cell unitid = %d object class = %d\n",__FUNCTION__,__LINE__,Vec2i(pos1.x, pos2.y).getString().c_str(),this->isFreeCell(Vec2i(pos1.x, pos2.y),field),(cellUnit != NULL ? cellUnit->getId() : -1),(obj != NULL ? obj->getType()->getClass() : -1));
1065 				//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1066 				return false;
1067 			}
1068 			if(isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex) == false) {
1069 				if(lookupCache != NULL) {
1070 					(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1071 				}
1072 
1073 				//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1074 				return false;
1075 			}
1076 		}
1077 
1078 		bool isBadHarvestPos = false;
1079 		//if(unit != NULL) {
1080 			Command *command= unit->getCurrCommand();
1081 			if(command != NULL) {
1082 				const HarvestCommandType *hct = dynamic_cast<const HarvestCommandType*>(command->getCommandType());
1083 				if(hct != NULL && unit->isBadHarvestPos(pos2) == true) {
1084 					isBadHarvestPos = true;
1085 				}
1086 			}
1087 		//}
1088 
1089 		if(unit == NULL || isBadHarvestPos == true) {
1090 			if(lookupCache != NULL) {
1091 				(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1092 			}
1093 
1094 			//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1095 			return false;
1096 		}
1097 
1098 		if(lookupCache != NULL) {
1099 			(*lookupCache)[pos1][pos2][teamIndex][size][field]=true;
1100 		}
1101 
1102 		return true;
1103 	}
1104 	//multi cell units
1105 	else {
1106 		for(int i = pos2.x; i < pos2.x + size; ++i) {
1107 			for(int j = pos2.y; j < pos2.y + size; ++j) {
1108 
1109 				Vec2i cellPos = Vec2i(i,j);
1110 				if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) {
1111 					if(getCell(cellPos)->getUnit(unit->getCurrField()) != unit) {
1112 						if(isAproxFreeCell(cellPos, field, teamIndex) == false) {
1113 							if(lookupCache != NULL) {
1114 								(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1115 							}
1116 
1117 							//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1118 							return false;
1119 						}
1120 					}
1121 				}
1122 				else {
1123 
1124 					if(lookupCache != NULL) {
1125 						(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1126 					}
1127 
1128 					//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1129 					return false;
1130 				}
1131 			}
1132 		}
1133 
1134 		bool isBadHarvestPos = false;
1135 		Command *command= unit->getCurrCommand();
1136 		if(command != NULL) {
1137 			const HarvestCommandType *hct = dynamic_cast<const HarvestCommandType*>(command->getCommandType());
1138 			if(hct != NULL && unit->isBadHarvestPos(pos2) == true) {
1139 				isBadHarvestPos = true;
1140 			}
1141 		}
1142 
1143 		if(isBadHarvestPos == true) {
1144 			if(lookupCache != NULL) {
1145 				(*lookupCache)[pos1][pos2][teamIndex][size][field]=false;
1146 			}
1147 
1148 			//printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__);
1149 			return false;
1150 		}
1151 
1152 		if(lookupCache != NULL) {
1153 			(*lookupCache)[pos1][pos2][teamIndex][size][field]=true;
1154 		}
1155 	}
1156 	return true;
1157 }
1158 
1159 
computeRefPos(const Selection * selection) const1160 Vec2i Map::computeRefPos(const Selection *selection) const {
1161     Vec2i total= Vec2i(0);
1162 
1163 	if(selection == NULL) {
1164 		throw megaglest_runtime_error("selection == NULL");
1165 	}
1166 
1167     for(int i = 0; i < selection->getCount(); ++i) {
1168     	if(selection->getUnit(i) == NULL) {
1169     		throw megaglest_runtime_error("selection == NULL || selection->getUnit(i) == NULL");
1170     	}
1171         total = total + selection->getUnit(i)->getPosNotThreadSafe();
1172     }
1173 
1174     return Vec2i(total.x / selection->getCount(), total.y / selection->getCount());
1175 }
1176 
computeDestPos(const Vec2i & refUnitPos,const Vec2i & unitPos,const Vec2i & commandPos) const1177 Vec2i Map::computeDestPos(	const Vec2i &refUnitPos, const Vec2i &unitPos,
1178 							const Vec2i &commandPos) const {
1179     Vec2i pos;
1180 // no more random needed
1181 //	Vec2i posDiff = unitPos - refUnitPos;
1182 //
1183 //	if(abs(posDiff.x) >= 3){
1184 //		posDiff.x = posDiff.x % 3;
1185 //	}
1186 //
1187 //	if(abs(posDiff.y) >= 3){
1188 //		posDiff.y = posDiff.y % 3;
1189 //	}
1190 
1191 	pos = commandPos; //+ posDiff;
1192     clampPos(pos);
1193     return pos;
1194 }
1195 
getUnitDistanceToPos(const Unit * unit,Vec2i pos,const UnitType * ut)1196 std::pair<float,Vec2i> Map::getUnitDistanceToPos(const Unit *unit,Vec2i pos,const UnitType *ut) {
1197 	if(unit == NULL) {
1198 		throw megaglest_runtime_error("unit == NULL");
1199 	}
1200 
1201 	std::pair<float,Vec2i> result(-1,Vec2i(0));
1202 	//int unitId= unit->getId();
1203 	Vec2i unitPos= computeDestPos(unit->getPosNotThreadSafe(), unit->getPosNotThreadSafe(), pos);
1204 
1205 	Vec2i start = pos - Vec2i(1);
1206 	int unitTypeSize = 0;
1207 	if(ut != NULL) {
1208 		unitTypeSize = ut->getSize();
1209 	}
1210 	Vec2i end 	= pos + Vec2i(unitTypeSize);
1211 
1212 	for(int i = start.x; i <= end.x; ++i) {
1213 		for(int j = start.y; j <= end.y; ++j){
1214 			Vec2i testPos(i,j);
1215 
1216 			if(ut == NULL || isInUnitTypeCells(ut, pos,testPos) == false) {
1217 				float distance = unitPos.dist(testPos);
1218 				if(result.first < 0 || result.first > distance) {
1219 					result.first = distance;
1220 					result.second = testPos;
1221 				}
1222 			}
1223 		}
1224 	}
1225 
1226 	return result;
1227 }
1228 
findClosestUnitToPos(const Selection * selection,Vec2i originalBuildPos,const UnitType * ut) const1229 const Unit * Map::findClosestUnitToPos(const Selection *selection, Vec2i originalBuildPos,
1230 								 const UnitType *ut) const {
1231 	const Unit *closestUnit = NULL;
1232 	Vec2i refPos = computeRefPos(selection);
1233 
1234 	Vec2i pos = originalBuildPos;
1235 
1236 	float bestRange = -1;
1237 
1238 	Vec2i start = pos - Vec2i(1);
1239 	int unitTypeSize = 0;
1240 	if(ut != NULL) {
1241 		unitTypeSize = ut->getSize();
1242 	}
1243 	Vec2i end 	= pos + Vec2i(unitTypeSize);
1244 
1245 	for(int i = 0; i < selection->getCount(); ++i) {
1246 		const Unit *unit = selection->getUnit(i);
1247 		//int unitId= unit->getId();
1248 		Vec2i unitBuilderPos= computeDestPos(refPos, unit->getPosNotThreadSafe(), pos);
1249 
1250 		for(int i = start.x; i <= end.x; ++i) {
1251 			for(int j = start.y; j <= end.y; ++j){
1252 				Vec2i testPos(i,j);
1253 				if(isInUnitTypeCells(ut, originalBuildPos,testPos) == false) {
1254 					float distance = unitBuilderPos.dist(testPos);
1255 					if(bestRange < 0 || bestRange > distance) {
1256 						bestRange = distance;
1257 						pos = testPos;
1258 						closestUnit = unit;
1259 					}
1260 				}
1261 			}
1262 		}
1263 	}
1264 
1265 	return closestUnit;
1266 }
1267 
findBestBuildApproach(const Unit * unit,Vec2i originalBuildPos,const UnitType * ut) const1268 Vec2i Map::findBestBuildApproach(const Unit *unit, Vec2i originalBuildPos,const UnitType *ut) const {
1269 	if(unit == NULL) {
1270 		throw megaglest_runtime_error("unit == NULL");
1271 	}
1272 	if(ut == NULL) {
1273 		throw megaglest_runtime_error("ut == NULL");
1274 	}
1275 
1276     Vec2i unitBuilderPos    = unit->getPosNotThreadSafe();
1277 	Vec2i pos               = originalBuildPos;
1278 
1279 	float bestRange = -1;
1280 
1281 	Vec2i start = pos - Vec2i(unit->getType()->getSize());
1282 	Vec2i end 	= pos + Vec2i(ut->getSize());
1283 
1284 	for(int i = start.x; i <= end.x; ++i) {
1285 		for(int j = start.y; j <= end.y; ++j) {
1286 			Vec2i testPos(i,j);
1287 			if(isInUnitTypeCells(ut, originalBuildPos,testPos) == false) {
1288 				float distance = unitBuilderPos.dist(testPos);
1289 				if(bestRange < 0 || bestRange > distance) {
1290 				    // Check if the cell is occupied by another unit
1291 				    if(isFreeCellOrHasUnit(testPos, unit->getType()->getField(), unit) == true) {
1292                         bestRange = distance;
1293                         pos = testPos;
1294 				    }
1295 				}
1296 			}
1297 		}
1298 	}
1299 
1300 	return pos;
1301 }
1302 
isNextToUnitTypeCells(const UnitType * ut,const Vec2i & pos,const Vec2i & testPos) const1303 bool Map::isNextToUnitTypeCells(const UnitType *ut, const Vec2i &pos,
1304 								const Vec2i &testPos) const {
1305 	bool isInsideDestUnitCells = isInUnitTypeCells(ut, pos,testPos);
1306 	if(isInsideDestUnitCells == false) {
1307 		//Cell *testCell = getCell(testPos);
1308 		for(int i=-1; i <= ut->getSize(); ++i){
1309 			for(int j = -1; j <= ut->getSize(); ++j) {
1310 				Vec2i currPos = pos + Vec2i(i, j);
1311 				if(isInside(currPos) == true && isInsideSurface(toSurfCoords(currPos)) == true) {
1312 					//Cell *unitCell = getCell(currPos);
1313 					//if(unitCell == testCell) {
1314 					if(isNextTo(testPos,currPos) == true) {
1315 						return true;
1316 					}
1317 				}
1318 			}
1319 		}
1320 	}
1321 
1322 	return false;
1323 }
1324 
1325 // is testPos in the cells of unitType where unitType's position is pos
isInUnitTypeCells(const UnitType * ut,const Vec2i & pos,const Vec2i & testPos) const1326 bool Map::isInUnitTypeCells(const UnitType *ut, const Vec2i &pos,
1327 							const Vec2i &testPos) const {
1328 	assert(ut != NULL);
1329 	if(ut == NULL) {
1330 		throw megaglest_runtime_error("ut == NULL");
1331 	}
1332 
1333 	if(isInside(testPos) && isInsideSurface(toSurfCoords(testPos))) {
1334 		Cell *testCell = getCell(testPos);
1335 		for(int i=0; i < ut->getSize(); ++i){
1336 			for(int j = 0; j < ut->getSize(); ++j) {
1337 				Vec2i currPos = pos + Vec2i(i, j);
1338 				if(isInside(currPos) && isInsideSurface(toSurfCoords(currPos))) {
1339 					Cell *unitCell = getCell(currPos);
1340 					if(unitCell == testCell) {
1341 						return true;
1342 					}
1343 				}
1344 			}
1345 		}
1346 	}
1347 	return false;
1348 }
1349 
1350 //put a units into the cells
putUnitCells(Unit * unit,const Vec2i & pos,bool ignoreSkill,bool threaded)1351 void Map::putUnitCells(Unit *unit, const Vec2i &pos, bool ignoreSkill, bool threaded) {
1352 	assert(unit != NULL);
1353 	if(unit == NULL) {
1354 		throw megaglest_runtime_error("ut == NULL");
1355 	}
1356 	putUnitCellsPrivate(unit, pos, unit->getType(), false, threaded);
1357 
1358 	// block space for morphing units
1359 	if(ignoreSkill==false &&
1360 			unit->getCurrSkill() != NULL &&
1361 	        unit->getCurrSkill()->getClass() == scMorph) {
1362 		Command *command= unit->getCurrCommand();
1363 		if(command != NULL && command->getCommandType()->commandTypeClass == ccMorph){
1364 			const MorphCommandType *mct= static_cast<const MorphCommandType*>(command->getCommandType());
1365 			putUnitCellsPrivate(unit, pos, mct->getMorphUnit(),true, threaded);
1366 			unit->setMorphFieldsBlocked(true);
1367 		}
1368 	}
1369 }
1370 
putUnitCellsPrivate(Unit * unit,const Vec2i & pos,const UnitType * ut,bool isMorph,bool threaded)1371 void Map::putUnitCellsPrivate(Unit *unit, const Vec2i &pos, const UnitType *ut, bool isMorph, bool threaded) {
1372 	assert(unit != NULL);
1373 	if(unit == NULL) {
1374 		throw megaglest_runtime_error("ut == NULL");
1375 	}
1376 
1377     bool canPutInCell = true;
1378 	Field field=ut->getField();
1379 	for(int i = 0; i < ut->getSize(); ++i) {
1380 		for(int j = 0; j < ut->getSize(); ++j) {
1381 			Vec2i currPos= pos + Vec2i(i, j);
1382 			assert(isInside(currPos));
1383 			if(isInside(currPos) == false) {
1384 				throw megaglest_runtime_error("isInside(currPos) == false");
1385 			}
1386 
1387 			if( ut->hasCellMap() == false || ut->getCellMapCell(i, j, unit->getModelFacing())) {
1388 				if(getCell(currPos)->getUnit(field) != NULL &&
1389                    getCell(currPos)->getUnit(field) != unit) {
1390 
1391 // TT: is this ok ?
1392                     // If unit tries to move into a cell where another unit resides
1393                     // cancel the move command
1394                     if(unit->getCurrSkill() != NULL &&
1395                        unit->getCurrSkill()->getClass() == scMove) {
1396                         canPutInCell = false;
1397                         //unit->setCurrSkill(scStop);
1398                         //unit->finishCommand();
1399 
1400                         //SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] POSSIBLE ERROR [getCell(currPos)->getUnit(unit->getCurrField()) != NULL] currPos [%s] unit [%s] cell unit [%s]\n",
1401                         //      __FILE__,__FUNCTION__,__LINE__,
1402                         //      currPos.getString().c_str(),
1403                         //      unit->toString().c_str(),
1404                         //      getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str());
1405                     }
1406 //TT: Nonsens?
1407 //                    else {
1408 //                        // If the unit trying to move into the cell is not in the moving state
1409 //                        // it is likely being created or morphed so we will will log the error
1410 //                        canPutInCell = false;
1411 //				        // throw megaglest_runtime_error("getCell(currPos)->getUnit(unit->getCurrField()) != NULL");
1412 //                        SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] ERROR [getCell(currPos)->getUnit(unit->getCurrField()) != NULL] currPos [%s] unit [%s] cell unit [%s]\n",
1413 //                              __FILE__,__FUNCTION__,__LINE__,
1414 //                              currPos.getString().c_str(),
1415 //                              unit->toString().c_str(),
1416 //                              getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str());
1417 //                    }
1418 				}
1419 
1420 
1421 				if(getCell(currPos)->getUnit(field) == NULL ||
1422 								   getCell(currPos)->getUnit(field) == unit) {
1423 					if(isMorph) {
1424 						// unit is beeing morphed to another unit with maybe other field.
1425 						getCell(currPos)->setUnit(field, unit);
1426 						canPutInCell = false;
1427 					}
1428 					if(canPutInCell == true) {
1429 						getCell(currPos)->setUnit(unit->getCurrField(), unit);
1430 					}
1431 				}
1432 				else if(canPutInCell == true) {
1433 					char szBuf[8096]="";
1434 					snprintf(szBuf,8096,"Trying to move unit [%d - %s] into occupied cell [%s] and field = %d, unit already in cell [%d - %s] ",unit->getId(),unit->getType()->getName(false).c_str(),pos.getString().c_str(),field,getCell(currPos)->getUnit(field)->getId(),getCell(currPos)->getUnit(field)->getType()->getName(false).c_str());
1435 					throw megaglest_runtime_error(szBuf);
1436 				}
1437 			}
1438 			else if(ut->hasCellMap() == true &&
1439 					ut->getAllowEmptyCellMap() == true &&
1440 					ut->hasEmptyCellMap() == true) {
1441 				getCell(currPos)->setUnitWithEmptyCellMap(unit->getCurrField(), unit);
1442 
1443 				//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] currPos = %s unit = %s\n",
1444                 //             __FILE__,__FUNCTION__,__LINE__,
1445                 //             currPos.getString().c_str(),
1446                 //             unit->toString().c_str());
1447 			}
1448 		}
1449 	}
1450 	if(canPutInCell == true) {
1451         unit->setPos(pos, false, threaded);
1452 	}
1453 }
1454 
1455 //removes a unit from cells
clearUnitCells(Unit * unit,const Vec2i & pos,bool ignoreSkill)1456 void Map::clearUnitCells(Unit *unit, const Vec2i &pos, bool ignoreSkill) {
1457 	assert(unit != NULL);
1458 	if(unit == NULL) {
1459 		throw megaglest_runtime_error("unit == NULL");
1460 	}
1461 
1462 	const UnitType *ut= unit->getType();
1463 	Field currentField=unit->getCurrField();
1464 
1465 	if(ignoreSkill==false &&
1466 			unit->getCurrSkill() != NULL &&
1467 	        unit->getCurrSkill()->getClass() == scMorph &&
1468 	        unit->getMorphFieldsBlocked() == true) {
1469 		Command *command= unit->getCurrCommand();
1470 		const MorphCommandType *mct= static_cast<const MorphCommandType*>(command->getCommandType());
1471 		if(unit->getType()->getSize()<=mct->getMorphUnit()->getSize()){
1472 			ut=mct->getMorphUnit();
1473 			currentField=ut->getField();
1474 			unit->setMorphFieldsBlocked(false);
1475 		}
1476 	}
1477 
1478 
1479 	for(int i=0; i<ut->getSize(); ++i){
1480 		for(int j=0; j<ut->getSize(); ++j){
1481 			Vec2i currPos= pos + Vec2i(i, j);
1482 			assert(isInside(currPos));
1483 			if(isInside(currPos) == false) {
1484 				throw megaglest_runtime_error("isInside(currPos) == false");
1485 			}
1486 
1487 			if(ut->hasCellMap() == false || ut->getCellMapCell(i, j, unit->getModelFacing())) {
1488 				// This seems to be a bad assert since you can clear the cell
1489 				// for many reasons including a unit dieing.
1490 
1491 				//assert(getCell(currPos)->getUnit(unit->getCurrField()) == unit || getCell(currPos)->getUnit(unit->getCurrField()) == NULL);
1492 				//if(getCell(currPos)->getUnit(unit->getCurrField()) != unit && getCell(currPos)->getUnit(unit->getCurrField()) != NULL) {
1493 				//	throw megaglest_runtime_error("getCell(currPos)->getUnit(unit->getCurrField()) != unit");
1494 					//SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] ERROR [getCell(currPos)->getUnit(unit->getCurrField()) != unit] currPos [%s] unit [%s] cell unit [%s]\n",
1495                     //          __FILE__,__FUNCTION__,__LINE__,
1496                     //          currPos.getString().c_str(),
1497                     //          unit->toString().c_str(),
1498                     //          (getCell(currPos)->getUnit(unit->getCurrField()) != NULL ? getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str() : "NULL"));
1499 				//}
1500 
1501                 // Only clear the cell if its the unit we expect to clear out of it
1502                 if(getCell(currPos)->getUnit(currentField) == unit) {
1503                     getCell(currPos)->setUnit(currentField, NULL);
1504                 }
1505 			}
1506 			else if(ut->hasCellMap() == true &&
1507 					ut->getAllowEmptyCellMap() == true &&
1508 					ut->hasEmptyCellMap() == true) {
1509 				getCell(currPos)->setUnitWithEmptyCellMap(currentField, NULL);
1510 
1511 				//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] currPos = %s unit = %s\n",
1512                 //             __FILE__,__FUNCTION__,__LINE__,
1513                 //             currPos.getString().c_str(),
1514                 //             unit->toString().c_str());
1515 			}
1516 		}
1517 	}
1518 }
1519 
1520 // ==================== misc ====================
1521 
1522 //return if unit is next to pos
isNextTo(const Vec2i & pos,const Unit * unit) const1523 bool Map::isNextTo(const Vec2i &pos, const Unit *unit) const {
1524 
1525 	for(int i=-1; i<=1; ++i) {
1526 		for(int j=-1; j<=1; ++j) {
1527 			if(isInside(pos.x+i, pos.y+j) && isInsideSurface(toSurfCoords(Vec2i(pos.x+i, pos.y+j)))) {
1528 				if(getCell(pos.x+i, pos.y+j)->getUnit(fLand) == unit) {
1529 					return true;
1530 				}
1531 				else if(getCell(pos.x+i, pos.y+j)->getUnitWithEmptyCellMap(fLand) == unit) {
1532 					return true;
1533 				}
1534 			}
1535 		}
1536 	}
1537     return false;
1538 }
1539 
1540 //return if unit is next to pos
isNextTo(const Unit * unit1,const Unit * unit2) const1541 bool Map::isNextTo(const Unit *unit1, const Unit *unit2) const {
1542 	Vec2i pos = unit1->getPosNotThreadSafe();
1543 	const UnitType *ut = unit1->getType();
1544 	for (int y=-1; y < ut->getSize()+1; ++y) {
1545 		for (int x=-1; x < ut->getSize()+1; ++x) {
1546 			Vec2i cellPos = pos + Vec2i(x, y);
1547 			if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) {
1548 				if(getCell(cellPos)->getUnit(fLand) == unit2) {
1549 					return true;
1550 				}
1551 				else if(getCell(cellPos)->getUnitWithEmptyCellMap(fLand) == unit2) {
1552 					return true;
1553 				}
1554 			}
1555 		}
1556 	}
1557 
1558     return false;
1559 }
1560 
1561 //return if unit is next to pos
isNextTo(const Vec2i & pos,const Vec2i & nextToPos) const1562 bool Map::isNextTo(const Vec2i &pos, const Vec2i &nextToPos) const {
1563 
1564 	for(int i=-1; i<=1; ++i) {
1565 		for(int j=-1; j<=1; ++j) {
1566 			if(isInside(pos.x+i, pos.y+j) && isInsideSurface(toSurfCoords(Vec2i(pos.x+i, pos.y+j)))) {
1567 				if(getCell(pos.x+i, pos.y+j) == getCell(nextToPos.x,nextToPos.y)) {
1568 					return true;
1569 				}
1570 			}
1571 		}
1572 	}
1573     return false;
1574 }
1575 
clampPos(Vec2i & pos) const1576 void Map::clampPos(Vec2i &pos) const{
1577 	if(pos.x<0){
1578         pos.x=0;
1579 	}
1580 	if(pos.y<0){
1581         pos.y=0;
1582 	}
1583 	if(pos.x>=w){
1584         pos.x=w-1;
1585 	}
1586 	if(pos.y>=h){
1587         pos.y=h-1;
1588 	}
1589 }
1590 
prepareTerrain(const Unit * unit)1591 void Map::prepareTerrain(const Unit *unit) {
1592 	Chrono chrono;
1593 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
1594 
1595 	flatternTerrain(unit);
1596 
1597 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1598 
1599     computeNormals();
1600 
1601     if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1602 
1603 	computeInterpolatedHeights();
1604 
1605 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1606 }
1607 
1608 // ==================== PRIVATE ====================
1609 
1610 // ==================== compute ====================
1611 
flatternTerrain(const Unit * unit)1612 void Map::flatternTerrain(const Unit *unit){
1613 	float refHeight= getSurfaceCell(toSurfCoords(unit->getCenteredPos()))->getHeight();
1614 	for(int i=-1; i<=unit->getType()->getSize(); ++i){
1615         for(int j=-1; j<=unit->getType()->getSize(); ++j){
1616             Vec2i pos= unit->getPosNotThreadSafe()+Vec2i(i, j);
1617             if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) {
1618 				Cell *c= getCell(pos);
1619 				SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos));
1620 				//we change height if pos is inside world, if its free or ocupied by the currenty building
1621 				if(sc->getObject() == NULL && (c->getUnit(fLand)==NULL || c->getUnit(fLand)==unit)) {
1622 					sc->setHeight(refHeight,true);
1623 				}
1624             }
1625         }
1626     }
1627 }
1628 
1629 //compute normals
computeNormals()1630 void Map::computeNormals(){
1631     //compute center normals
1632     for(int i=1; i<surfaceW-1; ++i){
1633         for(int j=1; j<surfaceH-1; ++j){
1634             getSurfaceCell(i, j)->setNormal(
1635 				getSurfaceCell(i, j)->getVertex().normal(getSurfaceCell(i, j-1)->getVertex(),
1636 					getSurfaceCell(i+1, j)->getVertex(),
1637 					getSurfaceCell(i, j+1)->getVertex(),
1638 					getSurfaceCell(i-1, j)->getVertex()));
1639         }
1640     }
1641 }
1642 
computeInterpolatedHeights()1643 void Map::computeInterpolatedHeights(){
1644 
1645 	for(int i=0; i<w; ++i){
1646 		for(int j=0; j<h; ++j){
1647 			getCell(i, j)->setHeight(getSurfaceCell(toSurfCoords(Vec2i(i, j)))->getHeight());
1648 		}
1649 	}
1650 
1651 	for(int i=1; i<surfaceW-1; ++i){
1652 		for(int j=1; j<surfaceH-1; ++j){
1653 			for(int k=0; k<cellScale; ++k){
1654 				for(int l=0; l<cellScale; ++l){
1655 					if(k==0 && l==0){
1656 						getCell(i*cellScale, j*cellScale)->setHeight(getSurfaceCell(i, j)->getHeight());
1657 					}
1658 					else if(k!=0 && l==0){
1659 						getCell(i*cellScale+k, j*cellScale)->setHeight((
1660 							getSurfaceCell(i, j)->getHeight()+
1661 							getSurfaceCell(i+1, j)->getHeight())/2.f);
1662 					}
1663 					else if(l!=0 && k==0){
1664 						getCell(i*cellScale, j*cellScale+l)->setHeight((
1665 							getSurfaceCell(i, j)->getHeight()+
1666 							getSurfaceCell(i, j+1)->getHeight())/2.f);
1667 					}
1668 					else{
1669 						getCell(i*cellScale+k, j*cellScale+l)->setHeight((
1670 							getSurfaceCell(i, j)->getHeight()+
1671 							getSurfaceCell(i, j+1)->getHeight()+
1672 							getSurfaceCell(i+1, j)->getHeight()+
1673 							getSurfaceCell(i+1, j+1)->getHeight())/4.f);
1674 					}
1675 				}
1676 			}
1677 		}
1678 	}
1679 }
1680 
smoothSurface(Tileset * tileset)1681 void Map::smoothSurface(Tileset *tileset) {
1682 	float *oldHeights = new float[getSurfaceCellArraySize()];
1683 	//int arraySize=getSurfaceCellArraySize();
1684 
1685 	for (int i = 0; i < getSurfaceCellArraySize(); ++i) {
1686 		oldHeights[i] = surfaceCells[i].getHeight();
1687 	}
1688 
1689 	for (int i = 1; i < surfaceW - 1; ++i) {
1690 		for (int j = 1; j < surfaceH - 1; ++j) {
1691 			float height = 0.f;
1692 			float numUsedToSmooth = 0.f;
1693 			for (int k = -1; k <= 1; ++k) {
1694 				for (int l = -1; l <= 1; ++l) {
1695 #ifdef USE_STREFLOP
1696 					if (cliffLevel<=0.1f || cliffLevel > streflop::fabs(static_cast<streflop::Simple>(oldHeights[(j) * surfaceW + (i)]
1697 							- oldHeights[(j + k) * surfaceW + (i + l)]))) {
1698 #else
1699 					if (cliffLevel<=0.1f || cliffLevel > fabs(oldHeights[(j) * surfaceW + (i)]
1700 							- oldHeights[(j + k) * surfaceW + (i + l)])) {
1701 #endif
1702 						height += oldHeights[(j + k) * surfaceW + (i + l)];
1703 						numUsedToSmooth++;
1704 					}
1705 					else {
1706 						// we have something which should not be smoothed!
1707 						// This is a cliff and must be textured -> set cliff texture
1708 						getSurfaceCell(i, j)->setSurfaceType(5);
1709 						//set invisible blocking object and replace resource objects
1710 						//and non blocking objects with invisible blocker too
1711 						Object *formerObject =
1712 								getSurfaceCell(i, j)->getObject();
1713 						if (formerObject != NULL) {
1714 							if (formerObject->getWalkable()
1715 									|| formerObject->getResource() != NULL) {
1716 								delete formerObject;
1717 								formerObject = NULL;
1718 							}
1719 						}
1720 						if (formerObject == NULL) {
1721 							Object *o = new Object(tileset->getObjectType(9),
1722 									getSurfaceCell(i, j)->getVertex(),
1723 									Vec2i(i,j));
1724 							getSurfaceCell(i, j)->setObject(o);
1725 						}
1726 					}
1727 				}
1728 			}
1729 
1730 			height /= numUsedToSmooth;
1731 			if(maxMapHeight<height){
1732 				maxMapHeight=height;
1733 			}
1734 
1735 			getSurfaceCell(i, j)->setHeight(height);
1736 			Object *object = getSurfaceCell(i, j)->getObject();
1737 			if (object != NULL) {
1738 				object->setHeight(height);
1739 			}
1740 		}
1741 	}
1742 	delete[] oldHeights;
1743 }
1744 
1745 void Map::computeNearSubmerged(){
1746 
1747 	for(int i=0; i<surfaceW-1; ++i){
1748 		for(int j=0; j<surfaceH-1; ++j){
1749 			bool anySubmerged= false;
1750 			for(int k=-1; k<=2; ++k){
1751 				for(int l=-1; l<=2; ++l){
1752 					Vec2i pos= Vec2i(i+k, j+l);
1753 					if(isInsideSurface(pos) && isInsideSurface(toSurfCoords(pos))) {
1754 						if(getSubmerged(getSurfaceCell(pos)))
1755 							anySubmerged= true;
1756 					}
1757 				}
1758 			}
1759 			getSurfaceCell(i, j)->setNearSubmerged(anySubmerged);
1760 		}
1761 	}
1762 }
1763 
1764 void Map::computeCellColors(){
1765 	for(int i=0; i<surfaceW; ++i){
1766 		for(int j=0; j<surfaceH; ++j){
1767 			SurfaceCell *sc= getSurfaceCell(i, j);
1768 			if(getDeepSubmerged(sc)){
1769 				float factor= clamp(waterLevel-sc->getHeight()*1.5f, 1.f, 1.5f);
1770 				sc->setColor(Vec3f(1.0f, 1.0f, 1.0f)/factor);
1771 			}
1772 			else{
1773 				sc->setColor(Vec3f(1.0f, 1.0f, 1.0f));
1774 			}
1775 		}
1776 	}
1777 }
1778 
1779 void Map::saveGame(XmlNode *rootNode) const {
1780 	std::map<string,string> mapTagReplacements;
1781 	XmlNode *mapNode = rootNode->addChild("Map");
1782 
1783 //	string title;
1784 	mapNode->addAttribute("title",title, mapTagReplacements);
1785 //	float waterLevel;
1786 	mapNode->addAttribute("waterLevel",floatToStr(waterLevel,6), mapTagReplacements);
1787 //	float heightFactor;
1788 	mapNode->addAttribute("heightFactor",floatToStr(heightFactor,6), mapTagReplacements);
1789 //	float cliffLevel;
1790 	mapNode->addAttribute("cliffLevel",floatToStr(cliffLevel,6), mapTagReplacements);
1791 //	int cameraHeight;
1792 	mapNode->addAttribute("cameraHeight",intToStr(cameraHeight), mapTagReplacements);
1793 //	int w;
1794 	mapNode->addAttribute("w",intToStr(w), mapTagReplacements);
1795 //	int h;
1796 	mapNode->addAttribute("h",intToStr(h), mapTagReplacements);
1797 //	int surfaceW;
1798 	mapNode->addAttribute("surfaceW",intToStr(surfaceW), mapTagReplacements);
1799 //	int surfaceH;
1800 	mapNode->addAttribute("surfaceH",intToStr(surfaceH), mapTagReplacements);
1801 //	int maxPlayers;
1802 	mapNode->addAttribute("maxPlayers",intToStr(maxPlayers), mapTagReplacements);
1803 //	Cell *cells;
1804 	//printf("getCellArraySize() = %d\n",getCellArraySize());
1805 //	for(unsigned int i = 0; i < getCellArraySize(); ++i) {
1806 //		Cell &cell = cells[i];
1807 //		cell.saveGame(mapNode,i);
1808 //	}
1809 //	SurfaceCell *surfaceCells;
1810 	//printf("getSurfaceCellArraySize() = %d\n",getSurfaceCellArraySize());
1811 
1812 	string exploredList = "";
1813 	string visibleList = "";
1814 
1815 	for(unsigned int i = 0; i < (unsigned int)getSurfaceCellArraySize(); ++i) {
1816 		SurfaceCell &surfaceCell = surfaceCells[i];
1817 
1818 		if(exploredList != "") {
1819 			exploredList += ",";
1820 		}
1821 
1822 		for(unsigned int j = 0; j < (unsigned int)GameConstants::maxPlayers; ++j) {
1823 			if(j > 0) {
1824 				exploredList += "|";
1825 			}
1826 
1827 			exploredList += intToStr(surfaceCell.isExplored(j));
1828 		}
1829 
1830 		if(visibleList != "") {
1831 			visibleList += ",";
1832 		}
1833 
1834 		for(unsigned int j = 0; j < (unsigned int)GameConstants::maxPlayers; ++j) {
1835 			if(j > 0) {
1836 				visibleList += "|";
1837 			}
1838 
1839 			visibleList += intToStr(surfaceCell.isVisible(j));
1840 		}
1841 
1842 		surfaceCell.saveGame(mapNode,i);
1843 
1844 		if(i > 0 && i % 100 == 0) {
1845 			XmlNode *surfaceCellNode = mapNode->addChild("SurfaceCell");
1846 			surfaceCellNode->addAttribute("batchIndex",intToStr(i), mapTagReplacements);
1847 			surfaceCellNode->addAttribute("exploredList",exploredList, mapTagReplacements);
1848 			surfaceCellNode->addAttribute("visibleList",visibleList, mapTagReplacements);
1849 
1850 			exploredList = "";
1851 			visibleList = "";
1852 		}
1853 	}
1854 
1855 	if(exploredList != "") {
1856 		XmlNode *surfaceCellNode = mapNode->addChild("SurfaceCell");
1857 		surfaceCellNode->addAttribute("batchIndex",intToStr(getSurfaceCellArraySize()), mapTagReplacements);
1858 		surfaceCellNode->addAttribute("exploredList",exploredList, mapTagReplacements);
1859 		surfaceCellNode->addAttribute("visibleList",visibleList, mapTagReplacements);
1860 	}
1861 
1862 //	Vec2i *startLocations;
1863 	for(unsigned int i = 0; i < (unsigned int)maxPlayers; ++i) {
1864 		XmlNode *startLocationsNode = mapNode->addChild("startLocations");
1865 		startLocationsNode->addAttribute("location",startLocations[i].getString(), mapTagReplacements);
1866 	}
1867 //	Checksum checksumValue;
1868 //	mapNode->addAttribute("checksumValue",intToStr(checksumValue.getSum()), mapTagReplacements);
1869 //	float maxMapHeight;
1870 	mapNode->addAttribute("maxMapHeight",floatToStr(maxMapHeight,6), mapTagReplacements);
1871 //	string mapFile;
1872 	mapNode->addAttribute("mapFile",mapFile, mapTagReplacements);
1873 }
1874 
1875 void Map::loadGame(const XmlNode *rootNode, World *world) {
1876 	const XmlNode *mapNode = rootNode->getChild("Map");
1877 
1878 	//description = gameSettingsNode->getAttribute("description")->getValue();
1879 
1880 //	for(unsigned int i = 0; i < getCellArraySize(); ++i) {
1881 //		Cell &cell = cells[i];
1882 //		cell.saveGame(mapNode,i);
1883 //	}
1884 //	for(unsigned int i = 0; i < getSurfaceCellArraySize(); ++i) {
1885 //		SurfaceCell &surfaceCell = surfaceCells[i];
1886 //		surfaceCell.saveGame(mapNode,i);
1887 //	}
1888 
1889 //	printf("getCellArraySize() = %d\n",getCellArraySize());
1890 //	for(unsigned int i = 0; i < getCellArraySize(); ++i) {
1891 //		Cell &cell = cells[i];
1892 //		cell.loadGame(mapNode,i,world);
1893 //	}
1894 
1895 //	printf("getSurfaceCellArraySize() = %d\n",getSurfaceCellArraySize());
1896 	for(unsigned int i = 0; i < (unsigned int)getSurfaceCellArraySize(); ++i) {
1897 		SurfaceCell &surfaceCell = surfaceCells[i];
1898 		surfaceCell.loadGame(mapNode,i,world);
1899 	}
1900 
1901 	int surfaceCellIndexExplored = 0;
1902 	int surfaceCellIndexVisible = 0;
1903 	vector<XmlNode *> surfaceCellNodeList = mapNode->getChildList("SurfaceCell");
1904 	for(unsigned int i = 0; i < surfaceCellNodeList.size(); ++i) {
1905 		XmlNode *surfaceCellNode = surfaceCellNodeList[i];
1906 
1907 		//XmlNode *surfaceCellNode = mapNode->getChild("SurfaceCell");
1908 		string exploredList = surfaceCellNode->getAttribute("exploredList")->getValue();
1909 		string visibleList = surfaceCellNode->getAttribute("visibleList")->getValue();
1910 		//int batchIndex = surfaceCellNode->getAttribute("batchIndex")->getIntValue();
1911 
1912 		vector<string> tokensExplored;
1913 		Tokenize(exploredList,tokensExplored,",");
1914 
1915 		//printf("=====================\nNew batchIndex = %d batchsize = %d\n",batchIndex,tokensExplored.size());
1916 		//for(unsigned int j = 0; j < tokensExplored.size(); ++j) {
1917 			//string valueList = tokensExplored[j];
1918 			//printf("valueList [%s]\n",valueList.c_str());
1919 		//}
1920 		for(unsigned int j = 0; j < tokensExplored.size(); ++j) {
1921 			string valueList = tokensExplored[j];
1922 
1923 			//int surfaceCellIndex = (i * tokensExplored.size()) + j;
1924 			//printf("Loading sc = %d batchIndex = %d\n",surfaceCellIndexExplored,batchIndex);
1925 			SurfaceCell &surfaceCell = surfaceCells[surfaceCellIndexExplored];
1926 
1927 			vector<string> tokensExploredValue;
1928 			Tokenize(valueList,tokensExploredValue,"|");
1929 
1930 //			if(tokensExploredValue.size() != GameConstants::maxPlayers) {
1931 //				for(unsigned int k = 0; k < tokensExploredValue.size(); ++k) {
1932 //					string value = tokensExploredValue[k];
1933 //					printf("k = %d [%s]\n",k,value.c_str());
1934 //				}
1935 //				throw megaglest_runtime_error("tokensExploredValue.size() [" + intToStr(tokensExploredValue.size()) + "] != GameConstants::maxPlayers");
1936 //			}
1937 			for(unsigned int k = 0; k < tokensExploredValue.size(); ++k) {
1938 				string value = tokensExploredValue[k];
1939 
1940 				surfaceCell.setExplored(k,strToInt(value) != 0);
1941 
1942 				//if(surfaceCell.isExplored(k) == true) {
1943 				//	printf("Setting cell at index: %d for team: %d to: %d [%s]\n",surfaceCellIndexExplored,k,surfaceCell.isExplored(k),value.c_str());
1944 				//}
1945 			}
1946 			surfaceCellIndexExplored++;
1947 		}
1948 
1949 		vector<string> tokensVisible;
1950 		Tokenize(visibleList,tokensVisible,",");
1951 		for(unsigned int j = 0; j < tokensVisible.size(); ++j) {
1952 			string valueList = tokensVisible[j];
1953 
1954 			//int surfaceCellIndex = (i * tokensVisible.size()) + j;
1955 			SurfaceCell &surfaceCell = surfaceCells[surfaceCellIndexVisible];
1956 
1957 			vector<string> tokensVisibleValue;
1958 			Tokenize(valueList,tokensVisibleValue,"|");
1959 
1960 //			if(tokensVisibleValue.size() != GameConstants::maxPlayers) {
1961 //				throw megaglest_runtime_error("tokensVisibleValue.size() [" + intToStr(tokensVisibleValue.size()) + "] != GameConstants::maxPlayers");
1962 //			}
1963 
1964 			for(unsigned int k = 0; k < tokensVisibleValue.size(); ++k) {
1965 				string value = tokensVisibleValue[k];
1966 
1967 				surfaceCell.setVisible(k,strToInt(value) != 0);
1968 			}
1969 			surfaceCellIndexVisible++;
1970 		}
1971 	}
1972 
1973     computeNormals();
1974 	computeInterpolatedHeights();
1975 }
1976 
1977 // =====================================================
1978 // 	class PosCircularIterator
1979 // =====================================================
1980 
1981 PosCircularIterator::PosCircularIterator(const Map *map, const Vec2i &center, int radius){
1982 	this->map= map;
1983 	this->radius= radius;
1984 	this->center= center;
1985 	pos= center - Vec2i(radius, radius);
1986 	pos.x-= 1;
1987 }
1988 
1989 bool PosCircularIterator::next(){
1990 
1991 	//iterate while dont find a cell that is inside the world
1992 	//and at less or equal distance that the radius
1993 	do{
1994 		pos.x++;
1995 		if(pos.x > center.x+radius){
1996 			pos.x= center.x-radius;
1997 			pos.y++;
1998 		}
1999 		if(pos.y>center.y+radius)
2000 			return false;
2001 	}
2002 #ifdef USE_STREFLOP
2003 	while(streflop::floor(static_cast<streflop::Simple>(pos.dist(center))) >= (radius+1) || !map->isInside(pos) || !map->isInsideSurface(map->toSurfCoords(pos)) );
2004 #else
2005 	while(floor(pos.dist(center)) >= (radius+1) || !map->isInside(pos) || !map->isInsideSurface(map->toSurfCoords(pos)) );
2006 #endif
2007 
2008 	return true;
2009 }
2010 
2011 const Vec2i &PosCircularIterator::getPos(){
2012 	return pos;
2013 }
2014 
2015 
2016 // =====================================================
2017 // 	class PosQuadIterator
2018 // =====================================================
2019 
2020 PosQuadIterator::PosQuadIterator(const Map *map,const Quad2i &quad, int step) {
2021 	this->map = map;
2022 
2023 	this->quad= quad;
2024 	this->boundingRect= quad.computeBoundingRect();
2025 	this->step= step;
2026 	pos= boundingRect.p[0];
2027 	--pos.x;
2028 	pos.x= (pos.x/step)*step;
2029 	pos.y= (pos.y/step)*step;
2030 	//map->clampPos(pos);
2031 }
2032 
2033 bool PosQuadIterator::next() {
2034 
2035 	do {
2036 		pos.x += step;
2037 		if(pos.x > boundingRect.p[1].x) {
2038 			pos.x = (boundingRect.p[0].x / step) * step;
2039 			pos.y += step;
2040 		}
2041 		if(pos.y > boundingRect.p[1].y) {
2042 			return false;
2043 		}
2044 
2045 		//printf("pos [%s] boundingRect.p[0] [%s] boundingRect.p[1] [%s]\n",pos.getString().c_str(),boundingRect.p[0].getString().c_str(),boundingRect.p[1].getString().c_str());
2046 	}
2047 	while(!quad.isInside(pos));
2048 
2049 	return true;
2050 }
2051 
2052 void PosQuadIterator::skipX() {
2053 	pos.x+= step;
2054 }
2055 
2056 const Vec2i &PosQuadIterator::getPos(){
2057 	return pos;
2058 }
2059 
2060 }}//end namespace
2061