// ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2009 James McCulloch // // 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 "cartographer.h" #include "game_constants.h" #include "route_planner.h" #include "pos_iterator.h" #include "game.h" #include "unit.h" #include "unit_type.h" //#include "profiler.h" //#include "leak_dumper.h" #include #if DEBUG_RENDERING_ENABLED # include "debug_renderer.h" #endif using namespace Shared::Graphics; using namespace Shared::Util; namespace Glest { namespace Game { /** Construct Cartographer object. Requires game settings, factions & cell map to have been loaded. */ Cartographer::Cartographer(World *world) : world(world), cellMap(0), routePlanner(0) { // _PROFILE_FUNCTION(); Logger::getInstance().add("Cartographer", true); cellMap = world->getMap(); int w = cellMap->getW(), h = cellMap->getH(); routePlanner = world->getRoutePlanner(); masterMap = new AnnotatedMap(world); clusterMap = new ClusterMap(masterMap, this); const TechTree *tt = world->getTechTree(); vector harvestResourceTypes; for (int i = 0; i < tt->getResourceTypeCount(); ++i) { rt_ptr rt = tt->getResourceType(i); if (rt->getClass() == rcTech || rt->getClass() == rcTileset) { harvestResourceTypes.push_back(rt); } } for (int i = 0; i < tt->getTypeCount(); ++i) { const FactionType *ft = tt->getType(i); for (int j = 0; j < ft->getUnitTypeCount(); ++j) { const UnitType *ut = ft->getUnitType(j); for (int k=0; k < ut->getCommandTypeCount(); ++k) { const CommandType *ct = ut->getCommandType(k); if (ct->getClass() == ccHarvest) { const HarvestCommandType *hct = static_cast(ct); for (vector::iterator it = harvestResourceTypes.begin(); it != harvestResourceTypes.end(); ++it) { if (hct->canHarvest(*it)) { ResourceMapKey key(*it, ut->getField(), ut->getSize()); resourceMapKeys.insert(key); } } } } } } // find and catalog all resources... for (int x=0; x < cellMap->getSurfaceW() - 1; ++x) { for (int y=0; y < cellMap->getSurfaceH() - 1; ++y) { const Resource * const r = cellMap->getSurfaceCell(x,y)->getResource(); if (r) { resourceLocations[r->getType()].push_back(Vec2i(x,y)); } } } Rectangle rect(0, 0, cellMap->getW() - 3, cellMap->getH() - 3); for (set::iterator it = resourceMapKeys.begin(); it != resourceMapKeys.end(); ++it) { PatchMap<1> *pMap = new PatchMap<1>(rect, 0); initResourceMap(*it, pMap); resourceMaps[*it] = pMap; } } /** Destruct */ Cartographer::~Cartographer() { delete masterMap; delete clusterMap; // Goal Maps deleteMapValues(resourceMaps.begin(), resourceMaps.end()); resourceMaps.clear(); deleteMapValues(storeMaps.begin(), storeMaps.end()); storeMaps.clear(); deleteMapValues(siteMaps.begin(), siteMaps.end()); siteMaps.clear(); } void Cartographer::initResourceMap(ResourceMapKey key, PatchMap<1> *pMap) { const int &size = key.workerSize; const Field &field = key.workerField; const Map &map = *world->getMap(); pMap->zeroMap(); for (vector::iterator it = resourceLocations[key.resourceType].begin(); it != resourceLocations[key.resourceType].end(); ++it) { Resource *r = world->getMap()->getSurfaceCell(*it)->getResource(); assert(r); Vec2i tl = *it * GameConstants::cellScale + OrdinalOffsets[odNorthWest] * size; Vec2i br(tl.x + size + 2, tl.y + size + 2); Util::PerimeterIterator iter(tl, br); while (iter.more()) { Vec2i pos = iter.next(); if (map.isInside(pos) && masterMap->canOccupy(pos, size, field)) { pMap->setInfluence(pos, 1); } } } } void Cartographer::onResourceDepleted(Vec2i pos, const ResourceType *rt) { PF_TRACE(); pos = Map::toUnitCoords(pos); updateMapMetrics(pos, GameConstants::cellScale); resDirtyAreas[rt].push_back(pos); } void Cartographer::fixupResourceMaps(const ResourceType *rt, const Vec2i &pos) { PF_TRACE(); const Map &map = *world->getMap(); Vec2i junk; for (set::iterator it = resourceMapKeys.begin(); it != resourceMapKeys.end(); ++it) { if (it->resourceType == rt) { PatchMap<1> *pMap = resourceMaps[*it]; const int &size = it->workerSize; const Field &field = it->workerField; Vec2i tl = pos + OrdinalOffsets[odNorthWest] * size; Vec2i br(tl.x + size + 2, tl.y + size + 2); Util::RectIterator iter(tl, br); while (iter.more()) { Vec2i cur = iter.next(); if (map.isInside(cur) && masterMap->canOccupy(cur, size, field) && map.isResourceNear(cur, size, rt, junk)) { pMap->setInfluence(cur, 1); } else { pMap->setInfluence(cur, 0); } } } } } PatchMap<1>* Cartographer::buildSiteMap(BuildSiteMapKey key) { PF_TRACE(); PatchMap<1> *sMap = siteMaps[key] = buildAdjacencyMap(key.buildingType, key.buildingPosition, key.buildingFacing, key.workerField, key.workerSize); # ifdef DEBUG_RENDERING_ENABLED debugAddBuildSiteMap(sMap); # endif return sMap; } PatchMap<1>* Cartographer::getResourceMap(ResourceMapKey key) { PF_TRACE(); return resourceMaps[key]; } PatchMap<1>* Cartographer::getStoreMap(StoreMapKey key, bool build) { PF_TRACE(); StoreMaps::iterator it = storeMaps.find(key); if (it != storeMaps.end()) { return it->second; } if (build) { return buildStoreMap(key); } else { return 0; } } PatchMap<1>* Cartographer::getStoreMap(const Unit *store, const Unit *worker) { PF_TRACE(); StoreMapKey key(store, worker->getCurrField(), worker->getType()->getSize()); return getStoreMap(key); } PatchMap<1>* Cartographer::getSiteMap(BuildSiteMapKey key) { PF_TRACE(); SiteMaps::iterator it = siteMaps.find(key); if (it != siteMaps.end()) { return it->second; } return buildSiteMap(key); } PatchMap<1>* Cartographer::getSiteMap(const UnitType *ut, const Vec2i &pos, CardinalDir facing, Unit *worker) { PF_TRACE(); BuildSiteMapKey key(ut, pos, facing, worker->getCurrField(), worker->getType()->getSize()); return getSiteMap(key); } void Cartographer::onStoreDestroyed(Unit *unit) { ///@todo fixme // delete storeMaps[unit]; // storeMaps.erase(unit); } PatchMap<1>* Cartographer::buildAdjacencyMap(const UnitType *uType, const Vec2i &pos, CardinalDir facing, Field f, int size) { PF_TRACE(); const Vec2i mapPos = pos + (OrdinalOffsets[odNorthWest] * size); const int sx = pos.x; const int sy = pos.y; Rectangle rect(mapPos.x, mapPos.y, uType->getSize() + 2 + size, uType->getSize() + 2 + size); PatchMap<1> *pMap = new PatchMap<1>(rect, 0); pMap->zeroMap(); PatchMap<1> tmpMap(rect, 0); tmpMap.zeroMap(); // mark cells occupied by unitType at pos (on tmpMap) Util::RectIterator rIter(pos, pos + Vec2i(uType->getSize() - 1)); while (rIter.more()) { Vec2i gpos = rIter.next(); if (!uType->hasCellMap() || uType->getCellMapCell(gpos.x - sx, gpos.y - sy, facing)) { tmpMap.setInfluence(gpos, 1); } } // mark goal cells on result map rIter = Util::RectIterator(mapPos, pos + Vec2i(uType->getSize())); while (rIter.more()) { Vec2i gpos = rIter.next(); if (tmpMap.getInfluence(gpos) || !masterMap->canOccupy(gpos, size, f)) { continue; // building or obstacle } Util::PerimeterIterator pIter(gpos - Vec2i(1), gpos + Vec2i(size)); while (pIter.more()) { if (tmpMap.getInfluence(pIter.next())) { pMap->setInfluence(gpos, 1); break; } } } return pMap; } #ifdef DEBUG_RENDERING_ENABLED void Cartographer::debugAddBuildSiteMap(PatchMap<1> *siteMap) { Rectangle mapBounds = siteMap->getBounds(); for (int ly = 0; ly < mapBounds.h; ++ly) { int y = mapBounds.y + ly; for (int lx = 0; lx < mapBounds.w; ++lx) { Vec2i pos(mapBounds.x + lx, y); if (siteMap->getInfluence(pos)) { getDebugRenderer().addBuildSiteCell(pos); } } } } #endif void Cartographer::tick() { PF_TRACE(); if (clusterMap->isDirty()) { clusterMap->update(); } for (ResourcePosMap::iterator it = resDirtyAreas.begin(); it != resDirtyAreas.end(); ++it) { if (!it->second.empty()) { for (V2iList::iterator posIt = it->second.begin(); posIt != it->second.end(); ++posIt) { fixupResourceMaps(it->first, *posIt); } it->second.clear(); } } } }}