// ============================================================== // ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2001-2008 MartiƱo Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #include "map.h" #include #include "tileset.h" #include "unit.h" #include "resource.h" #include "logger.h" #include "tech_tree.h" #include "config.h" #include "util.h" #include "game_settings.h" #include "platform_util.h" #include "faction.h" #include "command.h" #include "map_preview.h" #include "world.h" #include "byte_order.h" #include "leak_dumper.h" using namespace Shared::Graphics; using namespace Shared::Util; using namespace Shared::Platform; namespace Glest{ namespace Game{ // ===================================================== // class Cell // ===================================================== Cell::Cell() { //game data for(int i = 0; i < fieldCount; ++i) { units[i]= NULL; unitsWithEmptyCellMap[i]=NULL; } height= 0; } // ==================== misc ==================== //returns if the cell is free //returns if the cell is free void Cell::saveGame(XmlNode *rootNode, int index) const { bool saveCell = false; //if(saveCell == false) { for(unsigned int i = 0; i < fieldCount; ++i) { if(units[i] != NULL) { saveCell = true; break; } if(unitsWithEmptyCellMap[i] != NULL) { saveCell = true; break; } } //} if(saveCell == true) { std::map mapTagReplacements; XmlNode *cellNode = rootNode->addChild("Cell" + intToStr(index)); cellNode->addAttribute("index",intToStr(index), mapTagReplacements); // Unit *units[fieldCount]; //units on this cell for(unsigned int i = 0; i < fieldCount; ++i) { if(units[i] != NULL) { XmlNode *unitsNode = cellNode->addChild("units"); unitsNode->addAttribute("field",intToStr(i), mapTagReplacements); unitsNode->addAttribute("unitid",intToStr(units[i]->getId()), mapTagReplacements); } } // Unit *unitsWithEmptyCellMap[fieldCount]; //units with an empty cellmap on this cell for(unsigned int i = 0; i < fieldCount; ++i) { if(unitsWithEmptyCellMap[i] != NULL) { XmlNode *unitsWithEmptyCellMapNode = cellNode->addChild("unitsWithEmptyCellMap"); unitsWithEmptyCellMapNode->addAttribute("field",intToStr(i), mapTagReplacements); unitsWithEmptyCellMapNode->addAttribute("unitid",intToStr(unitsWithEmptyCellMap[i]->getId()), mapTagReplacements); } } // float height; cellNode->addAttribute("height",floatToStr(getHeight(),6), mapTagReplacements); } } void Cell::loadGame(const XmlNode *rootNode, int index, World *world) { if(rootNode->hasChild("Cell" + intToStr(index)) == true) { const XmlNode *cellNode = rootNode->getChild("Cell" + intToStr(index)); unsigned int unitCount = (unsigned int)cellNode->getChildCount(); for(unsigned int i = 0; i < unitCount; ++i) { if(cellNode->hasChildAtIndex("units",i) == true) { const XmlNode *unitsNode = cellNode->getChild("units",i); int field = unitsNode->getAttribute("field")->getIntValue(); int unitId = unitsNode->getAttribute("unitid")->getIntValue(); units[field] = world->findUnitById(unitId); } if(cellNode->hasChildAtIndex("unitsWithEmptyCellMap",i) == true) { const XmlNode *unitsNode = cellNode->getChild("unitsWithEmptyCellMap",i); int field = unitsNode->getAttribute("field")->getIntValue(); int unitId = unitsNode->getAttribute("unitid")->getIntValue(); unitsWithEmptyCellMap[field] = world->findUnitById(unitId); } } } } // ===================================================== // class SurfaceCell // ===================================================== SurfaceCell::SurfaceCell() { object= NULL; vertex= Vec3f(0.f); normal= Vec3f(0.f, 1.f, 0.f); surfaceType= -1; surfaceTexture= NULL; nearSubmerged = false; cellChangedFromOriginalMapLoad = false; for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) { setVisible(index,false); setExplored(index,false); } } SurfaceCell::~SurfaceCell() { delete object; object=NULL; } void SurfaceCell::end(){ if(object!=NULL){ object->end(); } } void SurfaceCell::deleteResource() { cellChangedFromOriginalMapLoad = true; delete object; object= NULL; } void SurfaceCell::setHeight(float height, bool cellChangedFromOriginalMapLoadValue) { height = truncateDecimal(height); vertex.y= height; if(cellChangedFromOriginalMapLoadValue == true) { this->cellChangedFromOriginalMapLoad = true; } } bool SurfaceCell::decAmount(int value) { cellChangedFromOriginalMapLoad = true; return object->getResource()->decAmount(value); } void SurfaceCell::setExplored(int teamIndex, bool explored) { if(teamIndex < 0 || teamIndex >= GameConstants::maxPlayers + GameConstants::specialFactions) { char szBuf[8096]=""; snprintf(szBuf,8096,"Invalid value for teamIndex [%d]",teamIndex); printf("%s\n",szBuf); throw megaglest_runtime_error(szBuf); } this->explored[teamIndex]= explored; //printf("Setting explored to %d for teamIndex %d\n",explored,teamIndex); } void SurfaceCell::setVisible(int teamIndex, bool visible) { if(teamIndex < 0 || teamIndex >= GameConstants::maxPlayers + GameConstants::specialFactions) { char szBuf[8096]=""; snprintf(szBuf,8096,"Invalid value for teamIndex [%d]",teamIndex); printf("%s\n",szBuf); throw megaglest_runtime_error(szBuf); } this->visible[teamIndex]= visible; if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true && SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynchMax).enabled == true) { char szBuf[8096]=""; snprintf(szBuf,8096,"In setVisible() teamIndex %d visible %d",teamIndex,visible); // if(frameIndex < 0) { // unit->logSynchData(__FILE__,__LINE__,szBuf); // } // else { // unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf); // } if(Thread::isCurrentThreadMainThread()) { //unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf); SystemFlags::OutputDebug(SystemFlags::debugWorldSynch,szBuf); } else { //unit->logSynchData(__FILE__,__LINE__,szBuf); printf("%s",szBuf); } } } string SurfaceCell::isVisibleString() const { string result = "isVisibleList = "; for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) { result += string(visible[index] ? "true" : "false"); } return result; } string SurfaceCell::isExploredString() const { string result = "isExploredList = "; for(int index = 0; index < GameConstants::maxPlayers + GameConstants::specialFactions; ++index) { result += string(explored[index] ? "true" : "false"); } return result; } void SurfaceCell::saveGame(XmlNode *rootNode,int index) const { bool saveCell = (this->getCellChangedFromOriginalMapLoad() == true); if(saveCell == true) { std::map mapTagReplacements; XmlNode *surfaceCellNode = rootNode->addChild("SurfaceCell" + intToStr(index)); surfaceCellNode->addAttribute("index",intToStr(index), mapTagReplacements); // //geometry // Vec3f vertex; surfaceCellNode->addAttribute("vertex",vertex.getString(), mapTagReplacements); // Vec3f normal; //surfaceCellNode->addAttribute("normal",normal.getString(), mapTagReplacements); // Vec3f color; //surfaceCellNode->addAttribute("color",color.getString(), mapTagReplacements); // // //tex coords // Vec2f fowTexCoord; //tex coords for TEXTURE1 when multitexturing and fogOfWar //surfaceCellNode->addAttribute("fowTexCoord",fowTexCoord.getString(), mapTagReplacements); // Vec2f surfTexCoord; //tex coords for TEXTURE0 //surfaceCellNode->addAttribute("surfTexCoord",surfTexCoord.getString(), mapTagReplacements); // //surface // int surfaceType; //surfaceCellNode->addAttribute("surfaceType",intToStr(surfaceType), mapTagReplacements); // const Texture2D *surfaceTexture; // // //object & resource // Object *object; if(object != NULL) { object->saveGame(surfaceCellNode); } else { XmlNode *objectNode = surfaceCellNode->addChild("Object"); objectNode->addAttribute("isDeleted",intToStr(true), mapTagReplacements); } // //visibility // bool visible[GameConstants::maxPlayers + GameConstants::specialFactions]; // for(unsigned int i = 0; i < GameConstants::maxPlayers; ++i) { // if(visible[i] == true) { // XmlNode *visibleNode = surfaceCellNode->addChild("visible"); // visibleNode->addAttribute("index",intToStr(i), mapTagReplacements); // visibleNode->addAttribute("value",intToStr(visible[i]), mapTagReplacements); // } // } // // bool explored[GameConstants::maxPlayers + GameConstants::specialFactions]; // for(unsigned int i = 0; i < GameConstants::maxPlayers; ++i) { // if(explored[i] == true) { // XmlNode *exploredNode = surfaceCellNode->addChild("explored"); // exploredNode->addAttribute("index",intToStr(i), mapTagReplacements); // exploredNode->addAttribute("value",intToStr(explored[i]), mapTagReplacements); // } // } // //cache // bool nearSubmerged; //surfaceCellNode->addAttribute("nearSubmerged",intToStr(nearSubmerged), mapTagReplacements); } } void SurfaceCell::loadGame(const XmlNode *rootNode, int index, World *world) { if(rootNode->hasChild("SurfaceCell" + intToStr(index)) == true) { const XmlNode *surfaceCellNode = rootNode->getChild("SurfaceCell" + intToStr(index)); if(surfaceCellNode->hasAttribute("vertex") == true) { vertex = Vec3f::strToVec3(surfaceCellNode->getAttribute("vertex")->getValue()); } //int visibleCount = cellNode->getChildCount(); XmlNode *objectNode = surfaceCellNode->getChild("Object"); if(objectNode->hasAttribute("isDeleted") == true) { this->deleteResource(); } else { object->loadGame(surfaceCellNode,world->getTechTree()); } //printf("Loading game, sc index [%d][%d]\n",index,visibleCount); // for(unsigned int i = 0; i < visibleCount; ++i) { // if(cellNode->hasChildAtIndex("visible",i) == true) { // const XmlNode *visibleNode = cellNode->getChild("visible",i); // int indexCell = visibleNode->getAttribute("index")->getIntValue(); // bool value = visibleNode->getAttribute("value")->getIntValue(); // visible[indexCell] = value; // // //printf("Loading game, sc visible index [%d][%d][%d]\n",index,indexCell,value); // } // if(cellNode->hasChildAtIndex("explored",i) == true) { // const XmlNode *exploredNode = cellNode->getChild("explored",i); // int indexCell = exploredNode->getAttribute("index")->getIntValue(); // bool value = exploredNode->getAttribute("value")->getIntValue(); // explored[indexCell] = value; // // //printf("Loading game, sc explored cell index [%d] exploredIndex [%d] value [%d]\n",index,indexCell,value); // } // } } } // ===================================================== // class Map // ===================================================== // ===================== PUBLIC ======================== const int Map::cellScale= 2; const int Map::mapScale= 2; Map::Map() { cells= NULL; surfaceCells= NULL; startLocations= NULL; title=""; waterLevel=0; heightFactor=0; cliffLevel=0; cameraHeight=0; w=0; h=0; surfaceW=0; surfaceH=0; surfaceSize=(surfaceW * surfaceH); maxPlayers=0; maxMapHeight=0; } Map::~Map() { Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMapCells","",true), true); delete [] cells; cells = NULL; delete [] surfaceCells; surfaceCells = NULL; delete [] startLocations; startLocations = NULL; } void Map::end(){ if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMap","",true), true); //read heightmap for(int j = 0; j < surfaceH; ++j) { for(int i = 0; i < surfaceW; ++i) { getSurfaceCell(i, j)->end(); } } if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } Vec2i Map::getStartLocation(int locationIndex) const { if(locationIndex >= maxPlayers) { char szBuf[8096]=""; snprintf(szBuf,8096,"locationIndex >= maxPlayers [%d] [%d]",locationIndex, maxPlayers); printf("%s\n",szBuf); throw megaglest_runtime_error(szBuf); //assert(locationIndex < maxPlayers); } else if(startLocations == NULL) { throw megaglest_runtime_error("startLocations == NULL"); } return startLocations[locationIndex]; } Checksum Map::load(const string &path, TechTree *techTree, Tileset *tileset) { Checksum mapChecksum; try{ #ifdef WIN32 FILE *f= _wfopen(utf8_decode(path).c_str(), L"rb"); #else FILE *f = fopen(path.c_str(), "rb"); #endif if(f != NULL) { mapFile = path; mapChecksum.addFile(path); checksumValue.addFile(path); //read header MapFileHeader header; size_t readBytes = fread(&header, sizeof(MapFileHeader), 1, f); if(readBytes != 1) { throw megaglest_runtime_error("Invalid map header detected for file: " + path); } fromEndianMapFileHeader(header); if(next2Power(header.width) != header.width){ throw megaglest_runtime_error("Map width is not a power of 2"); } if(next2Power(header.height) != header.height){ throw megaglest_runtime_error("Map height is not a power of 2"); } heightFactor= header.heightFactor; if(heightFactor>100){ heightFactor=heightFactor/100; heightFactor = truncateDecimal(heightFactor,6); } waterLevel= static_cast((header.waterLevel-0.01f)/heightFactor); waterLevel = truncateDecimal(waterLevel,6); title= header.title; maxPlayers= header.maxFactions; surfaceW= header.width; surfaceH= header.height; surfaceSize=(surfaceW * surfaceH); w= surfaceW*cellScale; h= surfaceH*cellScale; cliffLevel = 0; cameraHeight = 0; if(header.version==1){ //desc = header.description; } else if(header.version==2){ //desc = header.version2.short_desc; if(header.version2.cliffLevel > 0 && header.version2.cliffLevel < 5000){ cliffLevel=static_cast((header.version2.cliffLevel-0.01f)/(heightFactor)); cliffLevel = truncateDecimal(cliffLevel,6); } if(header.version2.cameraHeight > 0 && header.version2.cameraHeight < 5000) { cameraHeight = header.version2.cameraHeight; } } //start locations startLocations= new Vec2i[maxPlayers]; for(int i=0; i < maxPlayers; ++i) { int x=0, y=0; readBytes = fread(&x, sizeof(int32), 1, f); if(readBytes != 1) { char szBuf[8096]=""; snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__); throw megaglest_runtime_error(szBuf); } x = ::Shared::PlatformByteOrder::fromCommonEndian(x); readBytes = fread(&y, sizeof(int32), 1, f); if(readBytes != 1) { char szBuf[8096]=""; snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__); throw megaglest_runtime_error(szBuf); } y = ::Shared::PlatformByteOrder::fromCommonEndian(y); startLocations[i]= Vec2i(x, y)*cellScale; } //cells cells= new Cell[getCellArraySize()]; surfaceCells= new SurfaceCell[getSurfaceCellArraySize()]; //read heightmap for(int j = 0; j < surfaceH; ++j) { for(int i = 0; i < surfaceW; ++i) { float32 alt=0; readBytes = fread(&alt, sizeof(float32), 1, f); if(readBytes != 1) { char szBuf[8096]=""; snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__); throw megaglest_runtime_error(szBuf); } alt = ::Shared::PlatformByteOrder::fromCommonEndian(alt); SurfaceCell *sc= getSurfaceCell(i, j); sc->setVertex(Vec3f(i*mapScale, alt / heightFactor, j*mapScale)); } } //read surfaces for(int j = 0; j < surfaceH; ++j) { for(int i = 0; i < surfaceW; ++i) { int8 surf=0; readBytes = fread(&surf, sizeof(int8), 1, f); if(readBytes != 1) { char szBuf[8096]=""; snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__); throw megaglest_runtime_error(szBuf); } surf = ::Shared::PlatformByteOrder::fromCommonEndian(surf); getSurfaceCell(i, j)->setSurfaceType(surf-1); } } //read objects and resources for(int j = 0; j < h; j += cellScale) { for(int i = 0; i < w; i += cellScale) { int8 objNumber=0; readBytes = fread(&objNumber, sizeof(int8), 1, f); if(readBytes != 1) { char szBuf[8096]=""; snprintf(szBuf,8096,"fread returned wrong size = " MG_SIZE_T_SPECIFIER " on line: %d.",readBytes,__LINE__); throw megaglest_runtime_error(szBuf); } objNumber = ::Shared::PlatformByteOrder::fromCommonEndian(objNumber); SurfaceCell *sc= getSurfaceCell(toSurfCoords(Vec2i(i, j))); if(objNumber <= 0) { sc->setObject(NULL); } else if(objNumber <= Tileset::objCount) { Object *o= new Object(tileset->getObjectType(objNumber-1), sc->getVertex(),Vec2i(i, j)); sc->setObject(o); for(int k = 0; k < techTree->getResourceTypeCount(); ++k) { const ResourceType *rt= techTree->getResourceType(k); if(rt->getClass() == rcTileset && rt->getTilesetObject() == objNumber){ o->setResource(rt, Vec2i(i, j)); } } } else{ const ResourceType *rt= techTree->getTechResourceType(objNumber - Tileset::objCount) ; Object *o= new Object(NULL, sc->getVertex(),Vec2i(i, j)); o->setResource(rt, Vec2i(i, j)); sc->setObject(o); } } } if(f) fclose(f); } else { throw megaglest_runtime_error("Can't open file"); } } catch(const exception &e){ SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,e.what()); throw megaglest_runtime_error("Error loading map: "+ path+ "\n"+ e.what()); } return mapChecksum; } void Map::init(Tileset *tileset) { Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingMap","",true), true); maxMapHeight=0.0f; smoothSurface(tileset); computeNormals(); computeInterpolatedHeights(); computeNearSubmerged(); computeCellColors(); } // ==================== is ==================== class FindBestPos { public: float distanceFromUnitNoAdjustment; float distanceFromClickNoAdjustment; Vec2i resourcePosNoAdjustment; }; //returns if there is a resource next to a unit, in "resourcePos" is stored the relative position of the resource bool Map::isResourceNear(int frameIndex,const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit, bool fallbackToPeersHarvestingSameResource, Vec2i *resourceClickPos) const { bool resourceNear = false; float distanceFromUnit=-1; float distanceFromClick=-1; if(resourceClickPos) { //printf("+++++++++ unit [%s - %d] pos = [%s] resourceClickPos [%s]\n",unit->getFullName().c_str(),unit->getId(),pos.getString().c_str(),resourceClickPos->getString().c_str()); } for(int i = -size; i <= size; ++i) { for(int j = -size; j <= size; ++j) { Vec2i resPos = Vec2i(pos.x + i, pos.y + j); if(resourceClickPos) { resPos = Vec2i(resourceClickPos->x + i, resourceClickPos->y + j); } Vec2i surfCoords = toSurfCoords(resPos); if(isInside(resPos) && isInsideSurface(surfCoords)) { Resource *r= getSurfaceCell(surfCoords)->getResource(); if(r != NULL) { if(r->getType() == rt) { if(resourceClickPos) { //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); } if(resourceClickPos == NULL || (distanceFromClick < 0 || resourceClickPos->dist(resPos) <= distanceFromClick)) { if(unit == NULL || (distanceFromUnit < 0 || unit->getCenteredPos().dist(resPos) <= distanceFromUnit)) { bool isResourceNextToUnit = (resourceClickPos == NULL); for(int i1 = -size; isResourceNextToUnit == false && i1 <= size; ++i1) { for(int j1 = -size; j1 <= size; ++j1) { Vec2i resPos1 = Vec2i(pos.x + i1, pos.y + j1); if(resPos == resPos1) { isResourceNextToUnit = true; break; } } } if(isResourceNextToUnit == true) { if(resourceClickPos != NULL) { distanceFromClick = resourceClickPos->dist(resPos); } if(unit != NULL) { distanceFromUnit = unit->getCenteredPos().dist(resPos); } resourcePos= pos + Vec2i(i,j); if(unit == NULL || unit->isBadHarvestPos(resourcePos) == false) { resourceNear = true; if(resourceClickPos) { //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); } } } } } } } } } } if(resourceNear == false) { if(fallbackToPeersHarvestingSameResource == true && unit != NULL) { // Look for another unit that is currently harvesting the same resource // type right now // Check the faction cache for a known position where we can harvest // this resource type Vec2i result = unit->getFaction()->getClosestResourceTypeTargetFromCache(unit, rt,frameIndex); if(result.x >= 0) { resourcePos = result; if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true) { char szBuf[8096]=""; snprintf(szBuf,8096,"[found peer harvest pos] pos [%s] resourcePos [%s] unit->getFaction()->getCacheResourceTargetListSize() [%d]", pos.getString().c_str(),resourcePos.getString().c_str(),unit->getFaction()->getCacheResourceTargetListSize()); if(frameIndex < 0) { unit->logSynchData(__FILE__,__LINE__,szBuf); } else { unit->logSynchDataThreaded(__FILE__,__LINE__,szBuf); } } if(unit->getPos().dist(resourcePos) <= size) { resourceNear = true; if(resourceClickPos) { //printf("###### unit [%s - %d]\n",unit->getFullName().c_str(),unit->getId()); } } } } } if(resourceNear == false && resourceClickPos != NULL) { std::vector bestPosList; //if(resourceClickPos) { //printf("^^^^^ unit [%s - %d]\n",unit->getFullName().c_str(),unit->getId()); //} for(int i = -1; i <= 1; ++i) { for(int j = -1; j <= 1; ++j) { Vec2i resPos = Vec2i(resourceClickPos->x + i, resourceClickPos->y + j); Vec2i surfCoords = toSurfCoords(resPos); if(isInside(resPos) && isInsideSurface(surfCoords)) { Resource *r= getSurfaceCell(surfCoords)->getResource(); if(r != NULL) { if(r->getType() == rt) { //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); if(unit == NULL || (distanceFromUnit < 0 || unit->getCenteredPos().dist(resPos) <= (distanceFromUnit + 2.0))) { if(resourceClickPos->dist(resPos) <= 1.0) { if(unit != NULL) { FindBestPos bestPosItem; bestPosItem.distanceFromUnitNoAdjustment = unit->getCenteredPos().dist(resPos); bestPosItem.distanceFromClickNoAdjustment =distanceFromClick = resourceClickPos->dist(resPos); bestPosItem.resourcePosNoAdjustment = resPos; bestPosList.push_back(bestPosItem); } } //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); if(distanceFromClick < 0 || resourceClickPos->dist(resPos) <= distanceFromClick) { //if(resourceClickPos != NULL) { distanceFromClick = resourceClickPos->dist(resPos); //} if(unit != NULL) { distanceFromUnit = unit->getCenteredPos().dist(resPos); } *resourceClickPos = resPos; if(unit == NULL || unit->isBadHarvestPos(*resourceClickPos) == false) { //resourceNear = true; //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); } } } } } } } } float bestUnitDist = distanceFromUnit; for(unsigned int i = 0; i < bestPosList.size(); ++i) { FindBestPos &bestPosItem = bestPosList[i]; if(bestPosItem.distanceFromUnitNoAdjustment < bestUnitDist) { bestUnitDist = bestPosItem.distanceFromUnitNoAdjustment; *resourceClickPos = bestPosItem.resourcePosNoAdjustment; if(unit == NULL || unit->isBadHarvestPos(*resourceClickPos) == false) { //printf("%%----------- unit [%s - %d] resourceClickPos [%s] bestUnitDist [%f]\n",unit->getFullName().c_str(),unit->getId(),resourceClickPos->getString().c_str(),bestUnitDist); } } } } return resourceNear; } // ==================== free cells ==================== bool Map::isFreeCell(const Vec2i &pos, Field field) const { return isInside(pos) && isInsideSurface(toSurfCoords(pos)) && getCell(pos)->isFree(field) && (field==fAir || getSurfaceCell(toSurfCoords(pos))->isFree()) && (field!=fLand || getDeepSubmerged(getCell(pos)) == false); } bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const{ if(isInside(pos)){ Cell *c= getCell(pos); if(c->getUnit(field) == unit && unit != NULL) { return true; } else{ return isFreeCell(pos, field); } } return false; } //TT: this is much more complicated compared with the old one above. I think its no more needed //bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const { // if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) { // if(unit->getCurrField() != field) { // return isFreeCell(pos, field); // } // Cell *c= getCell(pos); // if(c->getUnit(unit->getCurrField()) == unit) { // if(unit->getCurrField() == fAir) { // if(field == fAir) { // return true; // } // const SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos)); // if(sc != NULL) { // if(getDeepSubmerged(sc) == true) { // return false; // } // else if(field == fLand) { // if(sc->isFree() == false) { // return false; // } // else if(c->getUnit(field) != NULL) { // return false; // } // } // } // } // return true; // } // else{ // return isFreeCell(pos, field); // } // } // return false; //} bool Map::isAproxFreeCell(const Vec2i &pos, Field field, int teamIndex) const { if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) { const SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos)); if(sc->isVisible(teamIndex)) { return isFreeCell(pos, field); } else if(sc->isExplored(teamIndex)) { return field==fLand? sc->isFree() && !getDeepSubmerged(getCell(pos)): true; } else { return true; } } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } bool Map::isFreeCells(const Vec2i & pos, int size, Field field) const { for(int i=pos.x; igetField(); const UnitType *ut=targetUnitType; CardinalDir facing=currentUnit->getModelFacing(); if (ut->hasCellMap() && isInside(pos) && isInsideSurface(toSurfCoords(pos))) { for (int y=0; y < ut->getSize(); ++y) { for (int x=0; x < ut->getSize(); ++x) { Vec2i cellPos = pos + Vec2i(x, y); if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) { if (ut->getCellMapCell(x, y, facing)) { if (isFreeCellOrHasUnit(cellPos, field, currentUnit) == false) { return false; } } } else { return false; } } } return true; } else { return isFreeCellsOrHasUnit(pos, ut->getSize(), field,currentUnit); } } //bool Map::canOccupy(const Vec2i &pos, Field field, const UnitType *ut, CardinalDir facing) { // if (ut->hasCellMap() && isInside(pos) && isInsideSurface(toSurfCoords(pos))) { // for (int y=0; y < ut->getSize(); ++y) { // for (int x=0; x < ut->getSize(); ++x) { // Vec2i cellPos = pos + Vec2i(x, y); // if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) { // if (ut->getCellMapCell(x, y, facing)) { // if (isFreeCell(cellPos, field) == false) { // return false; // } // } // } // else { // return false; // } // } // } // return true; // } // else { // return isFreeCells(pos, ut->getSize(), field); // } //} // ==================== unit placement ==================== //checks if a unit can move from between 2 cells bool Map::canMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2, std::map > > > *lookupCache) const { int size= unit->getType()->getSize(); Field field= unit->getCurrField(); if(lookupCache != NULL) { std::map > > >::const_iterator iterFind1 = lookupCache->find(pos1); if(iterFind1 != lookupCache->end()) { std::map > >::const_iterator iterFind2 = iterFind1->second.find(pos2); if(iterFind2 != iterFind1->second.end()) { std::map >::const_iterator iterFind3 = iterFind2->second.find(size); if(iterFind3 != iterFind2->second.end()) { std::map::const_iterator iterFind4 = iterFind3->second.find(field); if(iterFind4 != iterFind3->second.end()) { // Found this result in the cache return iterFind4->second; } } } } } for(int i=pos2.x; igetUnit(field) != unit) { if(isFreeCell(Vec2i(i, j), field) == false) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][size][field]=false; } return false; } } } else { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][size][field]=false; } return false; } } } bool isBadHarvestPos = false; //if(unit != NULL) { Command *command= unit->getCurrCommand(); if(command != NULL) { const HarvestCommandType *hct = dynamic_cast(command->getCommandType()); if(hct != NULL && unit->isBadHarvestPos(pos2) == true) { isBadHarvestPos = true; } } //} if(isBadHarvestPos == true) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][size][field]=false; } return false; } if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][size][field]=true; } return true; } //checks if a unit can move from between 2 cells using only visible cells (for pathfinding) bool Map::aproxCanMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2, std::map > > > > *lookupCache) const { if(isInside(pos1) == false || isInsideSurface(toSurfCoords(pos1)) == false || isInside(pos2) == false || isInsideSurface(toSurfCoords(pos2)) == false) { //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } if(unit == NULL) { throw megaglest_runtime_error("unit == NULL"); } int size= unit->getType()->getSize(); int teamIndex= unit->getTeam(); Field field= unit->getCurrField(); if(lookupCache != NULL) { std::map > > > >::const_iterator iterFind1 = lookupCache->find(pos1); if(iterFind1 != lookupCache->end()) { std::map > > >::const_iterator iterFind2 = iterFind1->second.find(pos2); if(iterFind2 != iterFind1->second.end()) { std::map > >::const_iterator iterFind3 = iterFind2->second.find(teamIndex); if(iterFind3 != iterFind2->second.end()) { std::map >::const_iterator iterFind4 = iterFind3->second.find(size); if(iterFind4 != iterFind3->second.end()) { std::map::const_iterator iterFind5 = iterFind4->second.find(field); if(iterFind5 != iterFind4->second.end()) { // Found this result in the cache if(iterFind5->second == false) { //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); } return iterFind5->second; } } } } } } //single cell units if(size == 1) { if(isAproxFreeCell(pos2, field, teamIndex) == false) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } if(pos1.x != pos2.x && pos1.y != pos2.y) { if(isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex) == false) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //Unit *cellUnit = getCell(Vec2i(pos1.x, pos2.y))->getUnit(field); //Object * obj = getSurfaceCell(toSurfCoords(Vec2i(pos1.x, pos2.y)))->getObject(); //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)); //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } if(isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex) == false) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } } bool isBadHarvestPos = false; //if(unit != NULL) { Command *command= unit->getCurrCommand(); if(command != NULL) { const HarvestCommandType *hct = dynamic_cast(command->getCommandType()); if(hct != NULL && unit->isBadHarvestPos(pos2) == true) { isBadHarvestPos = true; } } //} if(unit == NULL || isBadHarvestPos == true) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=true; } return true; } //multi cell units else { for(int i = pos2.x; i < pos2.x + size; ++i) { for(int j = pos2.y; j < pos2.y + size; ++j) { Vec2i cellPos = Vec2i(i,j); if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) { if(getCell(cellPos)->getUnit(unit->getCurrField()) != unit) { if(isAproxFreeCell(cellPos, field, teamIndex) == false) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } } } else { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } } } bool isBadHarvestPos = false; Command *command= unit->getCurrCommand(); if(command != NULL) { const HarvestCommandType *hct = dynamic_cast(command->getCommandType()); if(hct != NULL && unit->isBadHarvestPos(pos2) == true) { isBadHarvestPos = true; } } if(isBadHarvestPos == true) { if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=false; } //printf("[%s] Line: %d returning false\n",__FUNCTION__,__LINE__); return false; } if(lookupCache != NULL) { (*lookupCache)[pos1][pos2][teamIndex][size][field]=true; } } return true; } Vec2i Map::computeRefPos(const Selection *selection) const { Vec2i total= Vec2i(0); if(selection == NULL) { throw megaglest_runtime_error("selection == NULL"); } for(int i = 0; i < selection->getCount(); ++i) { if(selection->getUnit(i) == NULL) { throw megaglest_runtime_error("selection == NULL || selection->getUnit(i) == NULL"); } total = total + selection->getUnit(i)->getPosNotThreadSafe(); } return Vec2i(total.x / selection->getCount(), total.y / selection->getCount()); } Vec2i Map::computeDestPos( const Vec2i &refUnitPos, const Vec2i &unitPos, const Vec2i &commandPos) const { Vec2i pos; // no more random needed // Vec2i posDiff = unitPos - refUnitPos; // // if(abs(posDiff.x) >= 3){ // posDiff.x = posDiff.x % 3; // } // // if(abs(posDiff.y) >= 3){ // posDiff.y = posDiff.y % 3; // } pos = commandPos; //+ posDiff; clampPos(pos); return pos; } //std::pair Map::getUnitDistanceToPos(const Unit *unit,Vec2i pos,const UnitType *ut) { // if(unit == NULL) { // throw megaglest_runtime_error("unit == NULL"); // } // // std::pair result(-1,Vec2i(0)); // //int unitId= unit->getId(); // Vec2i unitPos= computeDestPos(unit->getPosNotThreadSafe(), unit->getPosNotThreadSafe(), pos); // // Vec2i start = pos - Vec2i(1); // int unitTypeSize = 0; // if(ut != NULL) { // unitTypeSize = ut->getSize(); // } // Vec2i end = pos + Vec2i(unitTypeSize); // // for(int i = start.x; i <= end.x; ++i) { // for(int j = start.y; j <= end.y; ++j){ // Vec2i testPos(i,j); // // if(ut == NULL || isInUnitTypeCells(ut, pos,testPos) == false) { // float distance = unitPos.dist(testPos); // if(result.first < 0 || result.first > distance) { // result.first = distance; // result.second = testPos; // } // } // } // } // // return result; //} const Unit * Map::findClosestUnitToPos(const Selection *selection, Vec2i originalBuildPos, const UnitType *ut) const { const Unit *closestUnit = NULL; Vec2i refPos = computeRefPos(selection); Vec2i pos = originalBuildPos; float bestRange = -1; Vec2i start = pos - Vec2i(1); int unitTypeSize = 0; if(ut != NULL) { unitTypeSize = ut->getSize(); } Vec2i end = pos + Vec2i(unitTypeSize); for(int i = 0; i < selection->getCount(); ++i) { const Unit *unit = selection->getUnit(i); //int unitId= unit->getId(); Vec2i unitBuilderPos= computeDestPos(refPos, unit->getPosNotThreadSafe(), pos); for(int i = start.x; i <= end.x; ++i) { for(int j = start.y; j <= end.y; ++j){ Vec2i testPos(i,j); if(isInUnitTypeCells(ut, originalBuildPos,testPos) == false) { float distance = unitBuilderPos.dist(testPos); if(bestRange < 0 || bestRange > distance) { bestRange = distance; pos = testPos; closestUnit = unit; } } } } } return closestUnit; } Vec2i Map::findBestBuildApproach(const Unit *unit, Vec2i originalBuildPos,const UnitType *ut) const { if(unit == NULL) { throw megaglest_runtime_error("unit == NULL"); } if(ut == NULL) { throw megaglest_runtime_error("ut == NULL"); } Vec2i unitBuilderPos = unit->getPosNotThreadSafe(); Vec2i pos = originalBuildPos; float bestRange = -1; Vec2i start = pos - Vec2i(unit->getType()->getSize()); Vec2i end = pos + Vec2i(ut->getSize()); for(int i = start.x; i <= end.x; ++i) { for(int j = start.y; j <= end.y; ++j) { Vec2i testPos(i,j); if(isInUnitTypeCells(ut, originalBuildPos,testPos) == false) { float distance = unitBuilderPos.dist(testPos); if(bestRange < 0 || bestRange > distance) { // Check if the cell is occupied by another unit if(isFreeCellOrHasUnit(testPos, unit->getType()->getField(), unit) == true) { bestRange = distance; pos = testPos; } } } } } return pos; } bool Map::isNextToUnitTypeCells(const UnitType *ut, const Vec2i &pos, const Vec2i &testPos) const { bool isInsideDestUnitCells = isInUnitTypeCells(ut, pos,testPos); if(isInsideDestUnitCells == false) { //Cell *testCell = getCell(testPos); for(int i=-1; i <= ut->getSize(); ++i){ for(int j = -1; j <= ut->getSize(); ++j) { Vec2i currPos = pos + Vec2i(i, j); if(isInside(currPos) == true && isInsideSurface(toSurfCoords(currPos)) == true) { //Cell *unitCell = getCell(currPos); //if(unitCell == testCell) { if(isNextTo(testPos,currPos) == true) { return true; } } } } } return false; } // is testPos in the cells of unitType where unitType's position is pos bool Map::isInUnitTypeCells(const UnitType *ut, const Vec2i &pos, const Vec2i &testPos) const { assert(ut != NULL); if(ut == NULL) { throw megaglest_runtime_error("ut == NULL"); } if(isInside(testPos) && isInsideSurface(toSurfCoords(testPos))) { Cell *testCell = getCell(testPos); for(int i=0; i < ut->getSize(); ++i){ for(int j = 0; j < ut->getSize(); ++j) { Vec2i currPos = pos + Vec2i(i, j); if(isInside(currPos) && isInsideSurface(toSurfCoords(currPos))) { Cell *unitCell = getCell(currPos); if(unitCell == testCell) { return true; } } } } } return false; } //put a units into the cells void Map::putUnitCells(Unit *unit, const Vec2i &pos, bool ignoreSkill, bool threaded) { assert(unit != NULL); if(unit == NULL) { throw megaglest_runtime_error("ut == NULL"); } putUnitCellsPrivate(unit, pos, unit->getType(), false, threaded); // block space for morphing units if(ignoreSkill==false && unit->getCurrSkill() != NULL && unit->getCurrSkill()->getClass() == scMorph) { Command *command= unit->getCurrCommand(); if(command != NULL && command->getCommandType()->commandTypeClass == ccMorph){ const MorphCommandType *mct= static_cast(command->getCommandType()); putUnitCellsPrivate(unit, pos, mct->getMorphUnit(),true, threaded); unit->setMorphFieldsBlocked(true); } } } void Map::putUnitCellsPrivate(Unit *unit, const Vec2i &pos, const UnitType *ut, bool isMorph, bool threaded) { assert(unit != NULL); if(unit == NULL) { throw megaglest_runtime_error("ut == NULL"); } bool canPutInCell = true; Field field=ut->getField(); for(int i = 0; i < ut->getSize(); ++i) { for(int j = 0; j < ut->getSize(); ++j) { Vec2i currPos= pos + Vec2i(i, j); assert(isInside(currPos)); if(isInside(currPos) == false) { throw megaglest_runtime_error("isInside(currPos) == false"); } if( ut->hasCellMap() == false || ut->getCellMapCell(i, j, unit->getModelFacing())) { if(getCell(currPos)->getUnit(field) != NULL && getCell(currPos)->getUnit(field) != unit) { // TT: is this ok ? // If unit tries to move into a cell where another unit resides // cancel the move command if(unit->getCurrSkill() != NULL && unit->getCurrSkill()->getClass() == scMove) { canPutInCell = false; //unit->setCurrSkill(scStop); //unit->finishCommand(); //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", // __FILE__,__FUNCTION__,__LINE__, // currPos.getString().c_str(), // unit->toString().c_str(), // getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str()); } //TT: Nonsens? // else { // // If the unit trying to move into the cell is not in the moving state // // it is likely being created or morphed so we will will log the error // canPutInCell = false; // // throw megaglest_runtime_error("getCell(currPos)->getUnit(unit->getCurrField()) != NULL"); // SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] ERROR [getCell(currPos)->getUnit(unit->getCurrField()) != NULL] currPos [%s] unit [%s] cell unit [%s]\n", // __FILE__,__FUNCTION__,__LINE__, // currPos.getString().c_str(), // unit->toString().c_str(), // getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str()); // } } if(getCell(currPos)->getUnit(field) == NULL || getCell(currPos)->getUnit(field) == unit) { if(isMorph) { // unit is beeing morphed to another unit with maybe other field. getCell(currPos)->setUnit(field, unit); canPutInCell = false; } if(canPutInCell == true) { getCell(currPos)->setUnit(unit->getCurrField(), unit); } } else if(canPutInCell == true) { char szBuf[8096]=""; 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()); throw megaglest_runtime_error(szBuf); } } else if(ut->hasCellMap() == true && ut->getAllowEmptyCellMap() == true && ut->hasEmptyCellMap() == true) { getCell(currPos)->setUnitWithEmptyCellMap(unit->getCurrField(), unit); //SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] currPos = %s unit = %s\n", // __FILE__,__FUNCTION__,__LINE__, // currPos.getString().c_str(), // unit->toString().c_str()); } } } if(canPutInCell == true) { unit->setPos(pos, false, threaded); } } //removes a unit from cells void Map::clearUnitCells(Unit *unit, const Vec2i &pos, bool ignoreSkill) { assert(unit != NULL); if(unit == NULL) { throw megaglest_runtime_error("unit == NULL"); } const UnitType *ut= unit->getType(); Field currentField=unit->getCurrField(); if(ignoreSkill==false && unit->getCurrSkill() != NULL && unit->getCurrSkill()->getClass() == scMorph && unit->getMorphFieldsBlocked() == true) { Command *command= unit->getCurrCommand(); const MorphCommandType *mct= static_cast(command->getCommandType()); if(unit->getType()->getSize()<=mct->getMorphUnit()->getSize()){ ut=mct->getMorphUnit(); currentField=ut->getField(); unit->setMorphFieldsBlocked(false); } } for(int i=0; igetSize(); ++i){ for(int j=0; jgetSize(); ++j){ Vec2i currPos= pos + Vec2i(i, j); assert(isInside(currPos)); if(isInside(currPos) == false) { throw megaglest_runtime_error("isInside(currPos) == false"); } if(ut->hasCellMap() == false || ut->getCellMapCell(i, j, unit->getModelFacing())) { // This seems to be a bad assert since you can clear the cell // for many reasons including a unit dieing. //assert(getCell(currPos)->getUnit(unit->getCurrField()) == unit || getCell(currPos)->getUnit(unit->getCurrField()) == NULL); //if(getCell(currPos)->getUnit(unit->getCurrField()) != unit && getCell(currPos)->getUnit(unit->getCurrField()) != NULL) { // throw megaglest_runtime_error("getCell(currPos)->getUnit(unit->getCurrField()) != unit"); //SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] ERROR [getCell(currPos)->getUnit(unit->getCurrField()) != unit] currPos [%s] unit [%s] cell unit [%s]\n", // __FILE__,__FUNCTION__,__LINE__, // currPos.getString().c_str(), // unit->toString().c_str(), // (getCell(currPos)->getUnit(unit->getCurrField()) != NULL ? getCell(currPos)->getUnit(unit->getCurrField())->toString().c_str() : "NULL")); //} // Only clear the cell if its the unit we expect to clear out of it if(getCell(currPos)->getUnit(currentField) == unit) { getCell(currPos)->setUnit(currentField, NULL); } } else if(ut->hasCellMap() == true && ut->getAllowEmptyCellMap() == true && ut->hasEmptyCellMap() == true) { getCell(currPos)->setUnitWithEmptyCellMap(currentField, NULL); //SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] currPos = %s unit = %s\n", // __FILE__,__FUNCTION__,__LINE__, // currPos.getString().c_str(), // unit->toString().c_str()); } } } } // ==================== misc ==================== //return if unit is next to pos bool Map::isNextTo(const Vec2i &pos, const Unit *unit) const { for(int i=-1; i<=1; ++i) { for(int j=-1; j<=1; ++j) { if(isInside(pos.x+i, pos.y+j) && isInsideSurface(toSurfCoords(Vec2i(pos.x+i, pos.y+j)))) { if(getCell(pos.x+i, pos.y+j)->getUnit(fLand) == unit) { return true; } else if(getCell(pos.x+i, pos.y+j)->getUnitWithEmptyCellMap(fLand) == unit) { return true; } } } } return false; } //return if unit is next to pos bool Map::isNextTo(const Unit *unit1, const Unit *unit2) const { Vec2i pos = unit1->getPosNotThreadSafe(); const UnitType *ut = unit1->getType(); for (int y=-1; y < ut->getSize()+1; ++y) { for (int x=-1; x < ut->getSize()+1; ++x) { Vec2i cellPos = pos + Vec2i(x, y); if(isInside(cellPos) && isInsideSurface(toSurfCoords(cellPos))) { if(getCell(cellPos)->getUnit(fLand) == unit2) { return true; } else if(getCell(cellPos)->getUnitWithEmptyCellMap(fLand) == unit2) { return true; } } } } return false; } //return if unit is next to pos bool Map::isNextTo(const Vec2i &pos, const Vec2i &nextToPos) const { for(int i=-1; i<=1; ++i) { for(int j=-1; j<=1; ++j) { if(isInside(pos.x+i, pos.y+j) && isInsideSurface(toSurfCoords(Vec2i(pos.x+i, pos.y+j)))) { if(getCell(pos.x+i, pos.y+j) == getCell(nextToPos.x,nextToPos.y)) { return true; } } } } return false; } void Map::clampPos(Vec2i &pos) const{ if(pos.x<0){ pos.x=0; } if(pos.y<0){ pos.y=0; } if(pos.x>=w){ pos.x=w-1; } if(pos.y>=h){ pos.y=h-1; } } void Map::prepareTerrain(const Unit *unit) { Chrono chrono; if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start(); flatternTerrain(unit); 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()); computeNormals(); 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()); computeInterpolatedHeights(); 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()); } // ==================== PRIVATE ==================== // ==================== compute ==================== void Map::flatternTerrain(const Unit *unit){ float refHeight= getSurfaceCell(toSurfCoords(unit->getCenteredPos()))->getHeight(); for(int i=-1; i<=unit->getType()->getSize(); ++i){ for(int j=-1; j<=unit->getType()->getSize(); ++j){ Vec2i pos= unit->getPosNotThreadSafe()+Vec2i(i, j); if(isInside(pos) && isInsideSurface(toSurfCoords(pos))) { Cell *c= getCell(pos); SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos)); //we change height if pos is inside world, if its free or ocupied by the currenty building if(sc->getObject() == NULL && (c->getUnit(fLand)==NULL || c->getUnit(fLand)==unit)) { sc->setHeight(refHeight,true); } } } } } //compute normals void Map::computeNormals(){ //compute center normals for(int i=1; isetNormal( getSurfaceCell(i, j)->getVertex().normal(getSurfaceCell(i, j-1)->getVertex(), getSurfaceCell(i+1, j)->getVertex(), getSurfaceCell(i, j+1)->getVertex(), getSurfaceCell(i-1, j)->getVertex())); } } } void Map::computeInterpolatedHeights(){ for(int i=0; isetHeight(getSurfaceCell(toSurfCoords(Vec2i(i, j)))->getHeight()); } } for(int i=1; isetHeight(getSurfaceCell(i, j)->getHeight()); } else if(k!=0 && l==0){ getCell(i*cellScale+k, j*cellScale)->setHeight(( getSurfaceCell(i, j)->getHeight()+ getSurfaceCell(i+1, j)->getHeight())/2.f); } else if(l!=0 && k==0){ getCell(i*cellScale, j*cellScale+l)->setHeight(( getSurfaceCell(i, j)->getHeight()+ getSurfaceCell(i, j+1)->getHeight())/2.f); } else{ getCell(i*cellScale+k, j*cellScale+l)->setHeight(( getSurfaceCell(i, j)->getHeight()+ getSurfaceCell(i, j+1)->getHeight()+ getSurfaceCell(i+1, j)->getHeight()+ getSurfaceCell(i+1, j+1)->getHeight())/4.f); } } } } } } void Map::smoothSurface(Tileset *tileset) { float *oldHeights = new float[getSurfaceCellArraySize()]; //int arraySize=getSurfaceCellArraySize(); for (int i = 0; i < getSurfaceCellArraySize(); ++i) { oldHeights[i] = surfaceCells[i].getHeight(); } for (int i = 1; i < surfaceW - 1; ++i) { for (int j = 1; j < surfaceH - 1; ++j) { float height = 0.f; float numUsedToSmooth = 0.f; for (int k = -1; k <= 1; ++k) { for (int l = -1; l <= 1; ++l) { #ifdef USE_STREFLOP if (cliffLevel<=0.1f || cliffLevel > streflop::fabs(static_cast(oldHeights[(j) * surfaceW + (i)] - oldHeights[(j + k) * surfaceW + (i + l)]))) { #else if (cliffLevel<=0.1f || cliffLevel > fabs(oldHeights[(j) * surfaceW + (i)] - oldHeights[(j + k) * surfaceW + (i + l)])) { #endif height += oldHeights[(j + k) * surfaceW + (i + l)]; numUsedToSmooth++; } else { // we have something which should not be smoothed! // This is a cliff and must be textured -> set cliff texture getSurfaceCell(i, j)->setSurfaceType(5); //set invisible blocking object and replace resource objects //and non blocking objects with invisible blocker too Object *formerObject = getSurfaceCell(i, j)->getObject(); if (formerObject != NULL) { if (formerObject->getWalkable() || formerObject->getResource() != NULL) { delete formerObject; formerObject = NULL; } } if (formerObject == NULL) { Object *o = new Object(tileset->getObjectType(9), getSurfaceCell(i, j)->getVertex(), Vec2i(i,j)); getSurfaceCell(i, j)->setObject(o); } } } } height /= numUsedToSmooth; if(maxMapHeightsetHeight(height); Object *object = getSurfaceCell(i, j)->getObject(); if (object != NULL) { object->setHeight(height); } } } delete[] oldHeights; } void Map::computeNearSubmerged(){ for(int i=0; isetNearSubmerged(anySubmerged); } } } void Map::computeCellColors(){ for(int i=0; igetHeight()*1.5f, 1.f, 1.5f); sc->setColor(Vec3f(1.0f, 1.0f, 1.0f)/factor); } else{ sc->setColor(Vec3f(1.0f, 1.0f, 1.0f)); } } } } void Map::saveGame(XmlNode *rootNode) const { std::map mapTagReplacements; XmlNode *mapNode = rootNode->addChild("Map"); // string title; mapNode->addAttribute("title",title, mapTagReplacements); // float waterLevel; mapNode->addAttribute("waterLevel",floatToStr(waterLevel,6), mapTagReplacements); // float heightFactor; mapNode->addAttribute("heightFactor",floatToStr(heightFactor,6), mapTagReplacements); // float cliffLevel; mapNode->addAttribute("cliffLevel",floatToStr(cliffLevel,6), mapTagReplacements); // int cameraHeight; mapNode->addAttribute("cameraHeight",intToStr(cameraHeight), mapTagReplacements); // int w; mapNode->addAttribute("w",intToStr(w), mapTagReplacements); // int h; mapNode->addAttribute("h",intToStr(h), mapTagReplacements); // int surfaceW; mapNode->addAttribute("surfaceW",intToStr(surfaceW), mapTagReplacements); // int surfaceH; mapNode->addAttribute("surfaceH",intToStr(surfaceH), mapTagReplacements); // int maxPlayers; mapNode->addAttribute("maxPlayers",intToStr(maxPlayers), mapTagReplacements); // Cell *cells; //printf("getCellArraySize() = %d\n",getCellArraySize()); // for(unsigned int i = 0; i < getCellArraySize(); ++i) { // Cell &cell = cells[i]; // cell.saveGame(mapNode,i); // } // SurfaceCell *surfaceCells; //printf("getSurfaceCellArraySize() = %d\n",getSurfaceCellArraySize()); string exploredList = ""; string visibleList = ""; for(unsigned int i = 0; i < (unsigned int)getSurfaceCellArraySize(); ++i) { SurfaceCell &surfaceCell = surfaceCells[i]; if(exploredList != "") { exploredList += ","; } for(unsigned int j = 0; j < (unsigned int)GameConstants::maxPlayers; ++j) { if(j > 0) { exploredList += "|"; } exploredList += intToStr(surfaceCell.isExplored(j)); } if(visibleList != "") { visibleList += ","; } for(unsigned int j = 0; j < (unsigned int)GameConstants::maxPlayers; ++j) { if(j > 0) { visibleList += "|"; } visibleList += intToStr(surfaceCell.isVisible(j)); } surfaceCell.saveGame(mapNode,i); if(i > 0 && i % 100 == 0) { XmlNode *surfaceCellNode = mapNode->addChild("SurfaceCell"); surfaceCellNode->addAttribute("batchIndex",intToStr(i), mapTagReplacements); surfaceCellNode->addAttribute("exploredList",exploredList, mapTagReplacements); surfaceCellNode->addAttribute("visibleList",visibleList, mapTagReplacements); exploredList = ""; visibleList = ""; } } if(exploredList != "") { XmlNode *surfaceCellNode = mapNode->addChild("SurfaceCell"); surfaceCellNode->addAttribute("batchIndex",intToStr(getSurfaceCellArraySize()), mapTagReplacements); surfaceCellNode->addAttribute("exploredList",exploredList, mapTagReplacements); surfaceCellNode->addAttribute("visibleList",visibleList, mapTagReplacements); } // Vec2i *startLocations; for(unsigned int i = 0; i < (unsigned int)maxPlayers; ++i) { XmlNode *startLocationsNode = mapNode->addChild("startLocations"); startLocationsNode->addAttribute("location",startLocations[i].getString(), mapTagReplacements); } // Checksum checksumValue; // mapNode->addAttribute("checksumValue",intToStr(checksumValue.getSum()), mapTagReplacements); // float maxMapHeight; mapNode->addAttribute("maxMapHeight",floatToStr(maxMapHeight,6), mapTagReplacements); // string mapFile; mapNode->addAttribute("mapFile",mapFile, mapTagReplacements); } void Map::loadGame(const XmlNode *rootNode, World *world) { const XmlNode *mapNode = rootNode->getChild("Map"); //description = gameSettingsNode->getAttribute("description")->getValue(); // for(unsigned int i = 0; i < getCellArraySize(); ++i) { // Cell &cell = cells[i]; // cell.saveGame(mapNode,i); // } // for(unsigned int i = 0; i < getSurfaceCellArraySize(); ++i) { // SurfaceCell &surfaceCell = surfaceCells[i]; // surfaceCell.saveGame(mapNode,i); // } // printf("getCellArraySize() = %d\n",getCellArraySize()); // for(unsigned int i = 0; i < getCellArraySize(); ++i) { // Cell &cell = cells[i]; // cell.loadGame(mapNode,i,world); // } // printf("getSurfaceCellArraySize() = %d\n",getSurfaceCellArraySize()); for(unsigned int i = 0; i < (unsigned int)getSurfaceCellArraySize(); ++i) { SurfaceCell &surfaceCell = surfaceCells[i]; surfaceCell.loadGame(mapNode,i,world); } int surfaceCellIndexExplored = 0; int surfaceCellIndexVisible = 0; vector surfaceCellNodeList = mapNode->getChildList("SurfaceCell"); for(unsigned int i = 0; i < surfaceCellNodeList.size(); ++i) { XmlNode *surfaceCellNode = surfaceCellNodeList[i]; //XmlNode *surfaceCellNode = mapNode->getChild("SurfaceCell"); string exploredList = surfaceCellNode->getAttribute("exploredList")->getValue(); string visibleList = surfaceCellNode->getAttribute("visibleList")->getValue(); //int batchIndex = surfaceCellNode->getAttribute("batchIndex")->getIntValue(); vector tokensExplored; Tokenize(exploredList,tokensExplored,","); //printf("=====================\nNew batchIndex = %d batchsize = %d\n",batchIndex,tokensExplored.size()); //for(unsigned int j = 0; j < tokensExplored.size(); ++j) { //string valueList = tokensExplored[j]; //printf("valueList [%s]\n",valueList.c_str()); //} for(unsigned int j = 0; j < tokensExplored.size(); ++j) { string valueList = tokensExplored[j]; //int surfaceCellIndex = (i * tokensExplored.size()) + j; //printf("Loading sc = %d batchIndex = %d\n",surfaceCellIndexExplored,batchIndex); SurfaceCell &surfaceCell = surfaceCells[surfaceCellIndexExplored]; vector tokensExploredValue; Tokenize(valueList,tokensExploredValue,"|"); // if(tokensExploredValue.size() != GameConstants::maxPlayers) { // for(unsigned int k = 0; k < tokensExploredValue.size(); ++k) { // string value = tokensExploredValue[k]; // printf("k = %d [%s]\n",k,value.c_str()); // } // throw megaglest_runtime_error("tokensExploredValue.size() [" + intToStr(tokensExploredValue.size()) + "] != GameConstants::maxPlayers"); // } for(unsigned int k = 0; k < tokensExploredValue.size(); ++k) { string value = tokensExploredValue[k]; surfaceCell.setExplored(k,strToInt(value) != 0); //if(surfaceCell.isExplored(k) == true) { // printf("Setting cell at index: %d for team: %d to: %d [%s]\n",surfaceCellIndexExplored,k,surfaceCell.isExplored(k),value.c_str()); //} } surfaceCellIndexExplored++; } vector tokensVisible; Tokenize(visibleList,tokensVisible,","); for(unsigned int j = 0; j < tokensVisible.size(); ++j) { string valueList = tokensVisible[j]; //int surfaceCellIndex = (i * tokensVisible.size()) + j; SurfaceCell &surfaceCell = surfaceCells[surfaceCellIndexVisible]; vector tokensVisibleValue; Tokenize(valueList,tokensVisibleValue,"|"); // if(tokensVisibleValue.size() != GameConstants::maxPlayers) { // throw megaglest_runtime_error("tokensVisibleValue.size() [" + intToStr(tokensVisibleValue.size()) + "] != GameConstants::maxPlayers"); // } for(unsigned int k = 0; k < tokensVisibleValue.size(); ++k) { string value = tokensVisibleValue[k]; surfaceCell.setVisible(k,strToInt(value) != 0); } surfaceCellIndexVisible++; } } computeNormals(); computeInterpolatedHeights(); } // ===================================================== // class PosCircularIterator // ===================================================== PosCircularIterator::PosCircularIterator(const Map *map, const Vec2i ¢er, int radius){ this->map= map; this->radius= radius; this->center= center; pos= center - Vec2i(radius, radius); pos.x-= 1; } bool PosCircularIterator::next(){ //iterate while dont find a cell that is inside the world //and at less or equal distance that the radius do{ pos.x++; if(pos.x > center.x+radius){ pos.x= center.x-radius; pos.y++; } if(pos.y>center.y+radius) return false; } #ifdef USE_STREFLOP while(streflop::floor(static_cast(pos.dist(center))) >= (radius+1) || !map->isInside(pos) || !map->isInsideSurface(map->toSurfCoords(pos)) ); #else while(floor(pos.dist(center)) >= (radius+1) || !map->isInside(pos) || !map->isInsideSurface(map->toSurfCoords(pos)) ); #endif return true; } const Vec2i &PosCircularIterator::getPos(){ return pos; } // ===================================================== // class PosQuadIterator // ===================================================== PosQuadIterator::PosQuadIterator(const Map *map,const Quad2i &quad, int step) { this->map = map; this->quad= quad; this->boundingRect= quad.computeBoundingRect(); this->step= step; pos= boundingRect.p[0]; --pos.x; pos.x= (pos.x/step)*step; pos.y= (pos.y/step)*step; //map->clampPos(pos); } bool PosQuadIterator::next() { do { pos.x += step; if(pos.x > boundingRect.p[1].x) { pos.x = (boundingRect.p[0].x / step) * step; pos.y += step; } if(pos.y > boundingRect.p[1].y) { return false; } //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()); } while(!quad.isInside(pos)); return true; } //void PosQuadIterator::skipX() { // pos.x+= step; //} const Vec2i &PosQuadIterator::getPos(){ return pos; } }}//end namespace