// ============================================================== // ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2001-2008 Martio 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 "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; iisPutrefacting(); } // ===================================================== // class SurfaceCell // ===================================================== SurfaceCell::SurfaceCell(){ object= NULL; vertex= Vec3f(0.f); normal= Vec3f(0.f, 1.f, 0.f); surfaceType= -1; surfaceTexture= NULL; } SurfaceCell::~SurfaceCell(){ delete object; } bool SurfaceCell::isFree() const{ return object==NULL || object->getWalkable(); } void SurfaceCell::deleteResource(){ delete object; object= NULL; } void SurfaceCell::setExplored(int teamIndex, bool explored){ this->explored[teamIndex]= explored; } void SurfaceCell::setVisible(int teamIndex, bool visible){ this->visible[teamIndex]= visible; } // ===================================================== // class Map // ===================================================== // ===================== PUBLIC ======================== const int Map::cellScale= 2; const int Map::mapScale= 2; Map::Map(){ cells= NULL; surfaceCells= NULL; startLocations= NULL; } Map::~Map(){ Logger::getInstance().add("Cells", true); delete [] cells; delete [] surfaceCells; delete [] startLocations; } void Map::load(const string &path, TechTree *techTree, Tileset *tileset){ struct MapFileHeader{ int32 version; int32 maxPlayers; int32 width; int32 height; int32 altFactor; int32 waterLevel; int8 title[128]; int8 author[128]; int8 description[256]; }; try{ FILE *f= fopen(path.c_str(), "rb"); if(f!=NULL){ //read header MapFileHeader header; size_t readBytes = fread(&header, sizeof(MapFileHeader), 1, f); if(next2Power(header.width) != header.width){ throw runtime_error("Map width is not a power of 2"); } if(next2Power(header.height) != header.height){ throw runtime_error("Map height is not a power of 2"); } heightFactor= header.altFactor; waterLevel= static_cast((header.waterLevel-0.01f)/heightFactor); title= header.title; maxPlayers= header.maxPlayers; surfaceW= header.width; surfaceH= header.height; w= surfaceW*cellScale; h= surfaceH*cellScale; //start locations startLocations= new Vec2i[maxPlayers]; for(int i=0; isetVertex(Vec3f(i*mapScale, alt / heightFactor, j*mapScale)); } } //read surfaces for(int j=0; jsetSurfaceType(surf-1); } } //read objects and resources for(int j=0; jsetObject(NULL); } else if(objNumber <= Tileset::objCount){ Object *o= new Object(tileset->getObjectType(objNumber-1), sc->getVertex()); sc->setObject(o); for(int k=0; kgetResourceTypeCount(); ++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()); o->setResource(rt, Vec2i(i, j)); sc->setObject(o); } } } } else{ throw runtime_error("Can't open file"); } fclose(f); } catch(const exception &e){ throw runtime_error("Error loading map: "+ path+ "\n"+ e.what()); } } void Map::init(){ Logger::getInstance().add("Heightmap computations", true); smoothSurface(); computeNormals(); computeInterpolatedHeights(); computeNearSubmerged(); computeCellColors(); } // ==================== is ==================== bool Map::isInside(int x, int y) const{ return x>=0 && y>=0 && x=0 && sy>=0 && sxgetResource(); if(r!=NULL){ if(r->getType()==rt){ resourcePos= pos + Vec2i(i,j); return true; } } } } } return false; } // ==================== free cells ==================== bool Map::isFreeCell(const Vec2i &pos, Field field) const{ return isInside(pos) && getCell(pos)->isFree(field) && (field==fAir || getSurfaceCell(toSurfCoords(pos))->isFree()) && (field!=fLand || !getDeepSubmerged(getCell(pos))); } bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const{ if(isInside(pos)){ Cell *c= getCell(pos); if(c->getUnit(unit->getCurrField())==unit){ return true; } else{ return isFreeCell(pos, field); } } return false; } bool Map::isAproxFreeCell(const Vec2i &pos, Field field, int teamIndex) const{ if(isInside(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; } } return false; } bool Map::isFreeCells(const Vec2i & pos, int size, Field field) const{ for(int i=pos.x; igetType()->getSize(); for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ if(!isFreeCell(Vec2i(i, j), unit->getCurrField())){ return false; } } } else{ return false; } } } 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) const{ int size= unit->getType()->getSize(); int teamIndex= unit->getTeam(); Field field= unit->getCurrField(); //single cell units if(size==1){ if(!isAproxFreeCell(pos2, field, teamIndex)){ return false; } if(pos1.x!=pos2.x && pos1.y!=pos2.y){ if(!isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex)){ return false; } if(!isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex)){ return false; } } return true; } //multi cell units else{ for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ if(!isAproxFreeCell(Vec2i(i, j), field, teamIndex)){ return false; } } } else{ return false; } } } return true; } } //put a units into the cells void Map::putUnitCells(Unit *unit, const Vec2i &pos){ assert(unit!=NULL); const UnitType *ut= unit->getType(); for(int i=0; igetSize(); ++i){ for(int j=0; jgetSize(); ++j){ Vec2i currPos= pos + Vec2i(i, j); assert(isInside(currPos)); if(!ut->hasCellMap() || ut->getCellMapCell(i, j, unit->getModelFacing())){ assert(getCell(currPos)->getUnit(unit->getCurrField())==NULL); getCell(currPos)->setUnit(unit->getCurrField(), unit); } } } unit->setPos(pos); } //removes a unit from cells void Map::clearUnitCells(Unit *unit, const Vec2i &pos){ assert(unit!=NULL); const UnitType *ut= unit->getType(); for(int i=0; igetSize(); ++i){ for(int j=0; jgetSize(); ++j){ Vec2i currPos= pos + Vec2i(i, j); assert(isInside(currPos)); if(!ut->hasCellMap() || ut->getCellMapCell(i, j, unit->getModelFacing())){ assert(getCell(currPos)->getUnit(unit->getCurrField())==unit); getCell(currPos)->setUnit(unit->getCurrField(), NULL); } } } } // ==================== misc ==================== //returnis 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)) { if(getCell(pos.x+i, pos.y+j)->getUnit(fLand)==unit){ 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){ flatternTerrain(unit); computeNormals(); computeInterpolatedHeights(); } // ==================== 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->getPos()+Vec2i(i, j); 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(isInside(pos) && sc->getObject()==NULL && (c->getUnit(fLand)==NULL || c->getUnit(fLand)==unit)){ sc->setHeight(refHeight); } } } } //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(){ float *oldHeights= new float[surfaceW*surfaceH]; for(int i=0; isetHeight(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)); } } } } // static string Map::getMapPath(const string &mapName, string scenarioDir) { Config &config = Config::getInstance(); vector pathList = config.getPathListForType(ptMaps,scenarioDir); for(int idx = 0; idx < pathList.size(); idx++) { const string &map_path = pathList[idx]; const string mega = map_path + "/" + mapName + ".mgm"; const string glest = map_path + "/" + mapName + ".gbm"; if (fileExists(mega)) { return mega; } else if (fileExists(glest)) { return glest; } } throw runtime_error("Map [" + mapName + "] not found, scenarioDir [" + scenarioDir + "]"); } // ===================================================== // 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; } while(floor(pos.dist(center)) >= (radius+1) || !map->isInside(pos)); //while(!(pos.dist(center) <= radius && map->isInside(pos))); 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; } 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; } while(!quad.isInside(pos)); return true; } void PosQuadIterator::skipX(){ pos.x+= step; } const Vec2i &PosQuadIterator::getPos(){ return pos; } }}//end namespace