- removed pathfinder from GAE and all references

This commit is contained in:
Mark Vejvoda 2012-09-22 21:39:13 +00:00
parent 871b6c4a6e
commit 8e88b36723
30 changed files with 2 additions and 5952 deletions

View File

@ -1,379 +0,0 @@
// ==============================================================
// This file is part of The Glest Advanced Engine
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================//
// File: annotated_map.cpp
//
// Annotated Map, for use in pathfinding.
//
#include "annotated_map.h"
#include "world.h"
#include "pos_iterator.h"
#include "cartographer.h"
#include "cluster_map.h"
#include "profiler.h"
namespace Glest { namespace Game {
/** Construct AnnotatedMap object, 'g_map' must be constructed and loaded
* @param master true if this is the master map, false for a foggy map (default true)
*/
AnnotatedMap::AnnotatedMap(World *world)
: world(world)
, cellMap(NULL) {
//_PROFILE_FUNCTION();
assert(world && world->getMap());
cellMap = world->getMap();
width = cellMap->getW();
height = cellMap->getH();
metrics.init(width, height);
for (int f = fLand; f < fieldCount; ++f) {
maxClearance[f] = 0;
}
const int &ftCount = world->getTechTree()->getTypeCount();
const FactionType *factionType;
for (int i = 0; i < ftCount; ++i) {
factionType = world->getTechTree()->getType(i);
const UnitType *unitType;
for (int j = 0; j < factionType->getUnitTypeCount(); ++j) {
unitType = factionType->getUnitType(j);
if (unitType->isMobile()) {
if (unitType->getSize() > maxClearance[unitType->getField()]) {
maxClearance[unitType->getField()] = unitType->getSize();
}
}
}
}
initMapMetrics();
}
AnnotatedMap::~AnnotatedMap() {
}
/** Initialise clearance data for a master map. */
void AnnotatedMap::initMapMetrics() {
// _PROFILE_FUNCTION();
Util::ReverseRectIterator iter(Vec2i(0,0), Vec2i(width - 1, height - 1));
while (iter.more()) {
computeClearances(iter.next());
}
}
#define LOG_CLUSTER_DIRTYING(x) {}
//#define LOG_CLUSTER_DIRTYING(x) {cout << x;}
struct MudFlinger {
ClusterMap *cm;
inline void setDirty(const Vec2i &pos) {
Vec2i cluster = ClusterMap::cellToCluster(pos);
cm->setClusterDirty(cluster);
LOG_CLUSTER_DIRTYING( "MapMetrics changed @ pos = " << pos << endl )
LOG_CLUSTER_DIRTYING( cout << "\tCluster = " << cluster << " dirty\n" )
int ymod = pos.y % GameConstants::clusterSize;
if (ymod == 0) {
cm->setNorthBorderDirty(cluster);
LOG_CLUSTER_DIRTYING( "\tNorth border dirty\n" )
} else if (ymod == GameConstants::clusterSize - 1) {
cm->setNorthBorderDirty(Vec2i(cluster.x, cluster.y + 1));
LOG_CLUSTER_DIRTYING( "\tSouth border dirty\n" )
}
int xmod = pos.x % GameConstants::clusterSize;
if (xmod == 0) {
cm->setWestBorderDirty(cluster);
LOG_CLUSTER_DIRTYING( "\tWest border dirty\n" )
} else if (xmod == GameConstants::clusterSize - 1) {
cm->setWestBorderDirty(Vec2i(cluster.x + 1, cluster.y));
LOG_CLUSTER_DIRTYING( "\tEast border dirty\n" )
}
}
} mudFlinger;
/** Update clearance data, when an obstactle is placed or removed from the map *
* @param pos the cell co-ordinates of the obstacle added/removed *
* @param size the size of the obstacle */
void AnnotatedMap::updateMapMetrics(const Vec2i &pos, const int size) {
assert(cellMap->isInside(pos));
assert(cellMap->isInside(pos.x + size - 1, pos.y + size - 1));
//_PROFILE_FUNCTION();
// need to throw mud on the ClusterMap
mudFlinger.cm = world->getCartographer()->getClusterMap();
// 1. re-evaluate the cells occupied (or formerly occupied)
for (int i = size - 1; i >= 0 ; --i) {
for (int j = size - 1; j >= 0; --j) {
Vec2i occPos = pos;
occPos.x += i; occPos.y += j;
CellMetrics old = metrics[occPos];
computeClearances(occPos);
if (old != metrics[occPos]) {
mudFlinger.setDirty(occPos);
}
}
}
// 2. propegate changes...
cascadingUpdate(pos, size);
}
/** Perform a 'cascading update' of clearance metrics having just changed clearances *
* @param pos the cell co-ordinates of the obstacle added/removed *
* @param size the size of the obstacle *
* @param field the field to update (temporary), or fieldCount to update all fields (permanent) */
void AnnotatedMap::cascadingUpdate(const Vec2i &pos, const int size, const Field field) {
list<Vec2i> *leftList, *aboveList, leftList1, leftList2, aboveList1, aboveList2;
leftList = &leftList1;
aboveList = &aboveList1;
// both the left and above lists need to be sorted, bigger values first (right->left, bottom->top)
for (int i = size - 1; i >= 0; --i) {
// Check if positions are on map, (the '+i' components are along the sides of the building/object,
// so we assume they are ok). If so, list them
if (pos.x-1 >= 0) {
leftList->push_back(Vec2i(pos.x-1,pos.y+i));
}
if (pos.y-1 >= 0) {
aboveList->push_back(Vec2i(pos.x+i,pos.y-1));
}
}
// the cell to the nothwest...
Vec2i *corner = NULL;
Vec2i cornerHolder(pos.x - 1, pos.y - 1);
if (pos.x - 1 >= 0 && pos.y - 1 >= 0) {
corner = &cornerHolder;
}
while (!leftList->empty() || !aboveList->empty() || corner) {
// the left and above lists for the next loop iteration
list<Vec2i> *newLeftList, *newAboveList;
newLeftList = leftList == &leftList1 ? &leftList2 : &leftList1;
newAboveList = aboveList == &aboveList1 ? &aboveList2 : &aboveList1;
if (!leftList->empty()) {
for (VLIt it = leftList->begin(); it != leftList->end(); ++it) {
if (updateCell(*it, field) && it->x - 1 >= 0) {
// if we updated and there is a cell to the left, add it to
newLeftList->push_back(Vec2i(it->x-1,it->y)); // the new left list
}
}
}
if (!aboveList->empty()) {
for (VLIt it = aboveList->begin(); it != aboveList->end(); ++it) {
if (updateCell(*it, field) && it->y - 1 >= 0) {
newAboveList->push_back(Vec2i(it->x,it->y-1));
}
}
}
if (corner) {
// Deal with the corner...
if (updateCell(*corner, field)) {
int x = corner->x, y = corner->y;
if (x - 1 >= 0) {
newLeftList->push_back(Vec2i(x-1,y));
if (y - 1 >= 0) {
*corner = Vec2i(x-1,y-1);
} else {
corner = NULL;
}
} else {
corner = NULL;
}
if (y - 1 >= 0) {
newAboveList->push_back(Vec2i(x,y-1));
}
} else {
corner = NULL;
}
}
leftList->clear();
leftList = newLeftList;
aboveList->clear();
aboveList = newAboveList;
}// end while
}
/** cascadingUpdate() helper */
bool AnnotatedMap::updateCell(const Vec2i &pos, const Field field) {
if (field == fieldCount) { // permanent annotation, update all
//if (eMap && !eMap->isExplored(Map::toTileCoords(pos))) {
// if not master map, stop if cells are unexplored
// return false;
//}
CellMetrics old = metrics[pos];
computeClearances(pos);
if (old != metrics[pos]) {
mudFlinger.setDirty(pos);
return true;
}
} else { // local annotation, only check field, store original clearances
uint32 old = metrics[pos].get(field);
if (old) {
computeClearance(pos, field);
if (old > metrics[pos].get(field)) {
if (localAnnt.find(pos) == localAnnt.end()) {
localAnnt[pos] = old; // was original clearance
}
return true;
}
}
}
return false;
}
/** Compute clearances (all fields) for a location
* @param pos the cell co-ordinates
*/
void AnnotatedMap::computeClearances(const Vec2i &pos) {
assert(cellMap->isInside(pos));
if (pos.x >= cellMap->getW() - 2 || pos.y >= cellMap->getH() - 2) {
metrics[pos].setAll(0);
return;
}
Cell *cell = cellMap->getCell(pos);
// is there a building here, or an object on the tile ??
bool surfaceBlocked = (cell->getUnit(fLand) && !cell->getUnit(fLand)->getType()->isMobile())
|| !cellMap->getSurfaceCell(cellMap->toSurfCoords(pos))->isFree();
// Walkable
if (surfaceBlocked || cellMap->getDeepSubmerged(cell))
metrics[pos].set(fLand, 0);
else
computeClearance(pos, fLand);
/*
// Any Water
if ( surfaceBlocked || !cell->isSubmerged() || !maxClearance[Field::ANY_WATER] )
metrics[pos].set(Field::ANY_WATER, 0);
else
computeClearance(pos, Field::ANY_WATER);
// Deep Water
if ( surfaceBlocked || !cell->isDeepSubmerged() || !maxClearance[Field::DEEP_WATER] )
metrics[pos].set(Field::DEEP_WATER, 0);
else
computeClearance(pos, Field::DEEP_WATER);
// Amphibious:
if ( surfaceBlocked || !maxClearance[Field::AMPHIBIOUS] )
metrics[pos].set(Field::AMPHIBIOUS, 0);
else
computeClearance(pos, Field::AMPHIBIOUS);
*/
// Air
computeClearance(pos, fAir);
}
/** Computes clearance based on metrics to the south and east.
* Does NOT check if this cell is an obstactle, assumes metrics of cells to
* the south, south-east & east are correct
* @param pos the co-ordinates of the cell
* @param field the field to update
*/
uint32 AnnotatedMap::computeClearance( const Vec2i &pos, Field f ) {
uint32 clear = metrics[Vec2i(pos.x, pos.y + 1)].get(f);
if ( clear > metrics[Vec2i(pos.x + 1, pos.y + 1)].get(f) ) {
clear = metrics[Vec2i(pos.x + 1, pos.y + 1)].get(f);
}
if ( clear > metrics[Vec2i(pos.x + 1, pos.y)].get(f) ) {
clear = metrics[Vec2i(pos.x + 1, pos.y)].get(f);
}
clear ++;
if ( clear > maxClearance[f] ) {
clear = maxClearance[f];
}
metrics[pos].set(f, clear);
return clear;
}
/** Perform 'local annotations', annotate the map to treat other mobile units in
* the vincinity of unit as obstacles
* @param unit the unit about to perform a search
* @param field the field that the unit is about to search in
*/
void AnnotatedMap::annotateLocal(const Unit *unit) {
//_PROFILE_FUNCTION();
const Field &field = unit->getCurrField();
const Vec2i &pos = unit->getPos();
const int &size = unit->getType()->getSize();
assert(cellMap->isInside(pos));
assert(cellMap->isInside(pos.x + size - 1, pos.y + size - 1));
const int dist = 3;
set<Unit*> annotate;
// find surrounding units
for ( int y = pos.y - dist; y < pos.y + size + dist; ++y ) {
for ( int x = pos.x - dist; x < pos.x + size + dist; ++x ) {
if ( cellMap->isInside(x, y) && metrics[Vec2i(x, y)].get(field) ) { // clearance != 0
Unit *u = cellMap->getCell(x, y)->getUnit(field);
if ( u && u != unit ) { // the set will take care of duplicates for us
annotate.insert(u);
}
}
}
}
// annotate map for each nearby unit
for ( set<Unit*>::iterator it = annotate.begin(); it != annotate.end(); ++it ) {
annotateUnit(*it, field);
}
}
/** Temporarily annotate the map, to treat unit as an obstacle
* @param unit the unit to treat as an obstacle
* @param field the field to annotate
*/
void AnnotatedMap::annotateUnit(const Unit *unit, const Field field) {
const int size = unit->getType()->getSize();
const Vec2i &pos = unit->getPos();
assert(cellMap->isInside(pos));
assert(cellMap->isInside(pos.x + size - 1, pos.y + size - 1));
// first, re-evaluate the cells occupied
for (int i = size - 1; i >= 0 ; --i) {
for (int j = size - 1; j >= 0; --j) {
Vec2i occPos = pos;
occPos.x += i; occPos.y += j;
if (!unit->getType()->hasCellMap() || unit->getType()->getCellMapCell(i, j, unit->getModelFacing())) {
if (localAnnt.find(occPos) == localAnnt.end()) {
localAnnt[occPos] = metrics[occPos].get(field);
}
metrics[occPos].set(field, 0);
} else {
uint32 old = metrics[occPos].get(field);
computeClearance(occPos, field);
if (old != metrics[occPos].get(field) && localAnnt.find(occPos) == localAnnt.end()) {
localAnnt[occPos] = old;
}
}
}
}
// propegate changes to left and above
cascadingUpdate(pos, size, field);
}
/** Clear all local annotations *
* @param field the field annotations were applied to */
void AnnotatedMap::clearLocalAnnotations(const Unit *unit) {
//_PROFILE_FUNCTION();
const Field &field = unit->getCurrField();
for ( map<Vec2i,uint32>::iterator it = localAnnt.begin(); it != localAnnt.end(); ++ it ) {
assert(it->second <= maxClearance[field]);
assert(cellMap->isInside(it->first));
metrics[it->first].set(field, it->second);
}
localAnnt.clear();
}
#if _GAE_DEBUG_EDITION_
list<pair<Vec2i,uint32> >* AnnotatedMap::getLocalAnnotations() {
list<pair<Vec2i,uint32> > *ret = new list<pair<Vec2i,uint32> >();
for ( map<Vec2i,uint32>::iterator it = localAnnt.begin(); it != localAnnt.end(); ++ it )
ret->push_back(pair<Vec2i,uint32>(it->first,metrics[it->first].get(fLand)));
return ret;
}
#endif
}}

View File

@ -1,201 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
//
// File: annotated_map.h
//
// Annotated Map, for use in pathfinding.
//
#ifndef _GLEST_GAME_ANNOTATED_MAP_H_
#define _GLEST_GAME_ANNOTATED_MAP_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "vec.h"
#include "map.h"
namespace Glest { namespace Game {
using Shared::Platform::int64;
typedef list<Vec2i>::iterator VLIt;
typedef list<Vec2i>::const_iterator VLConIt;
typedef list<Vec2i>::reverse_iterator VLRevIt;
typedef list<Vec2i>::const_reverse_iterator VLConRevIt;
class World;
// =====================================================
// struct CellMetrics
// =====================================================
/** Stores clearance metrics for a cell.
* 3 bits are used per field, allowing for a maximum moveable unit size of 7.
* The left over bit is used for per team 'foggy' maps, the dirty bit is set when
* an obstacle has been placed or removed in an area the team cannot currently see.
* The team's annotated map is thus not updated until that cell becomes visible again.
*/
struct CellMetrics {
CellMetrics() { memset(this, 0, sizeof(*this)); }
uint16 get(const Field field) const { /**< get metrics for field */
switch (field) {
case fLand: return field0;
case fAir: return field1;
//case Field::ANY_WATER: return field2;
//case Field::DEEP_WATER: return field3;
//case Field::AMPHIBIOUS: return field4;
default: throw megaglest_runtime_error("Unknown Field passed to CellMetrics::get()");
}
}
void set(const Field field, uint16 val) { /**< set metrics for field */
switch (field) {
case fLand: field0 = val; return;
case fAir: field1 = val; return;
//case Field::ANY_WATER: field2 = val; return;
//case Field::DEEP_WATER: field3 = val; return;
//case Field::AMPHIBIOUS: field4 = val; return;
default: throw megaglest_runtime_error("Unknown Field passed to CellMetrics::set()");
}
}
void setAll(uint16 val) { /**< set clearance of all fields to val */
field0 = field1 = field2 = field3 = field4 = val;
}
bool operator!=(CellMetrics &that) const { /**< comparison, ignoring dirty bit */
if (field0 == that.field0 && field1 == that.field1
&& field2 == that.field2 && field3 == that.field3 && field4 == that.field4) {
return false;
}
return true;
}
bool isDirty() const { return dirty; } /**< is this cell dirty */
void setDirty(const bool val) { dirty = val; } /**< set dirty flag */
private:
uint16 field0 : 3; /**< fLand = land + shallow water */
uint16 field1 : 3; /**< fAir = air */
uint16 field2 : 3; /**< Field::ANY_WATER = shallow + deep water */
uint16 field3 : 3; /**< Field::DEEP_WATER = deep water */
uint16 field4 : 3; /**< Field::AMPHIBIOUS = land + shallow + deep water */
uint16 dirty : 1; /**< used in 'team' maps as a 'dirty bit' (clearances have changed
* but team hasn't seen that change yet). */
};
// =====================================================
// class MetricMap
// =====================================================
/** A wrapper class for the array of CellMetrics
*/
class MetricMap {
private:
CellMetrics *metrics;
int width,height;
public:
MetricMap() : metrics(NULL), width(0), height(0) { }
~MetricMap() { delete [] metrics; }
void init(int w, int h) {
assert ( w > 0 && h > 0);
width = w;
height = h;
metrics = new CellMetrics[w * h];
}
void zero() { memset(metrics, 0, sizeof(CellMetrics) * width * height); }
CellMetrics& operator[](const Vec2i &pos) const { return metrics[pos.y * width + pos.x]; }
};
class PathFinderTextureCallback;
// =====================================================
// class AnnotatedMap
// =====================================================
/** A 'search' map, annotated with clearance data and explored status
* <p>A compact representation of the map with clearance information for each cell.
* The clearance values are stored for each cell & field, and represent the
* clearance to the south and east of the cell. That is, the maximum size unit
* that can be 'positioned' in this cell (with units in Glest always using the
* north-west most cell they occupy as their 'position').</p>
*/
//TODO: pretty pictures for the doco...
class AnnotatedMap {
friend class ClusterMap;
friend class PathFinderTextureCallback;
private:
int width, height;
World *world;
Map *cellMap;
public:
AnnotatedMap(World *world);
~AnnotatedMap();
int getWidth() { return width; }
int getHeight() { return height; }
/** Maximum clearance allowed by the game. Hence, also maximum moveable unit size supported. */
static const int maxClearanceValue = 7; // don't change me without also changing CellMetrics
int maxClearance[fieldCount]; // maximum clearances needed for this world
void initMapMetrics();
void revealTile(const Vec2i &pos);
void updateMapMetrics(const Vec2i &pos, const int size);
/** Interface to the clearance metrics, can a unit of size occupy a cell(s)
* @param pos position agent wishes to occupy
* @param size size of agent
* @param field field agent moves in
* @return true if position can be occupied, else false
*/
bool canOccupy(const Vec2i &pos, int size, Field field) const {
assert(cellMap->isInside(pos));
return metrics[pos].get(field) >= size ? true : false;
}
bool isDirty(const Vec2i &pos) const { return metrics[pos].isDirty(); }
void setDirty(const Vec2i &pos, const bool val) { metrics[pos].setDirty(val); }
void annotateLocal(const Unit *unit);
void clearLocalAnnotations(const Unit *unit);
private:
// for initMetrics() and updateMapMetrics ()
void computeClearances(const Vec2i &);
uint32 computeClearance(const Vec2i &, Field);
void cascadingUpdate(const Vec2i &pos, const int size, const Field field = fieldCount);
void annotateUnit(const Unit *unit, const Field field);
bool updateCell(const Vec2i &pos, const Field field);
/** the original values of locations that have had local annotations applied */
std::map<Vec2i,uint32> localAnnt;
/** The metrics */
MetricMap metrics;
};
}}
#endif

View File

@ -1,297 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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 <algorithm>
#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<rt_ptr> 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<const HarvestCommandType *>(ct);
for (vector<rt_ptr>::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<ResourceMapKey>::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<Vec2i>::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<ResourceMapKey>::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();
}
}
}
}}

View File

@ -1,173 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifndef _GLEST_GAME_CARTOGRAPHER_H_
#define _GLEST_GAME_CARTOGRAPHER_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "game_constants.h"
#include "influence_map.h"
#include "annotated_map.h"
#include "cluster_map.h"
#include "world.h"
#include "config.h"
#include "search_engine.h"
#include "resource_type.h"
namespace Glest { namespace Game {
using std::make_pair;
class RoutePlanner;
struct ResourceMapKey {
const ResourceType *resourceType;
Field workerField;
int workerSize;
ResourceMapKey(const ResourceType *type, Field f, int s)
: resourceType(type), workerField(f), workerSize(s) {}
bool operator<(const ResourceMapKey &that) const {
return (memcmp(this, &that, sizeof(ResourceMapKey)) < 0);
}
};
struct StoreMapKey {
const Unit *storeUnit;
Field workerField;
int workerSize;
StoreMapKey(const Unit *store, Field f, int s)
: storeUnit(store), workerField(f), workerSize(s) {}
bool operator<(const StoreMapKey &that) const {
return (memcmp(this, &that, sizeof(StoreMapKey)) < 0);
}
};
struct BuildSiteMapKey {
const UnitType *buildingType;
Vec2i buildingPosition;
CardinalDir buildingFacing;
Field workerField;
int workerSize;
BuildSiteMapKey(const UnitType *type, const Vec2i &pos, CardinalDir facing, Field f, int s)
: buildingType(type), buildingPosition(pos), buildingFacing(facing)
, workerField(f), workerSize(s) {}
bool operator<(const BuildSiteMapKey &that) const {
return (memcmp(this, &that, sizeof(BuildSiteMapKey)) < 0);
}
};
//
// Cartographer: 'Map' Manager
//
class Cartographer {
private:
/** Master annotated map, always correct */
AnnotatedMap *masterMap;
/** The ClusterMap (Hierarchical map abstraction) */
ClusterMap *clusterMap;
typedef const ResourceType* rt_ptr;
typedef vector<Vec2i> V2iList;
typedef map<ResourceMapKey, PatchMap<1>*> ResourceMaps; // goal maps for harvester path searches to resourecs
typedef map<StoreMapKey, PatchMap<1>*> StoreMaps; // goal maps for harvester path searches to store
typedef map<BuildSiteMapKey,PatchMap<1>*> SiteMaps; // goal maps for building sites.
typedef list<pair<rt_ptr, Vec2i> > ResourcePosList;
typedef map<rt_ptr, V2iList> ResourcePosMap;
// Resources
/** The locations of each and every resource on the map */
ResourcePosMap resourceLocations;
set<ResourceMapKey> resourceMapKeys;
/** areas where resources have been depleted and updates are required */
ResourcePosMap resDirtyAreas;
ResourceMaps resourceMaps; /**< Goal Maps for each tech & tileset resource */
StoreMaps storeMaps; /**< goal maps for resource stores */
SiteMaps siteMaps; /**< goal maps for building sites */
World *world;
Map *cellMap;
RoutePlanner *routePlanner;
void initResourceMap(ResourceMapKey key, PatchMap<1> *pMap);
void fixupResourceMaps(const ResourceType *rt, const Vec2i &pos);
PatchMap<1>* buildAdjacencyMap(const UnitType *uType, const Vec2i &pos, CardinalDir facing, Field f, int sz);
PatchMap<1>* buildStoreMap(StoreMapKey key) {
return (storeMaps[key] =
buildAdjacencyMap(key.storeUnit->getType(), key.storeUnit->getPos(),
key.storeUnit->getModelFacing(), key.workerField, key.workerSize));
}
PatchMap<1>* buildSiteMap(BuildSiteMapKey key);
# ifdef DEBUG_RENDERING_ENABLED
void debugAddBuildSiteMap(PatchMap<1>*);
# endif
public:
Cartographer(World *world);
virtual ~Cartographer();
RoutePlanner* getRoutePlanner() { return routePlanner; }
/** Update the annotated maps when an obstacle has been added or removed from the map.
* @param pos position (north-west most cell) of obstacle
* @param size size of obstacle */
void updateMapMetrics(const Vec2i &pos, const int size) {
masterMap->updateMapMetrics(pos, size);
}
void onResourceDepleted(Vec2i pos, const ResourceType *rt);
void onStoreDestroyed(Unit *unit);
void tick();
PatchMap<1>* getResourceMap(ResourceMapKey key);
PatchMap<1>* getStoreMap(StoreMapKey key, bool build=true);
PatchMap<1>* getStoreMap(const Unit *store, const Unit *worker);
PatchMap<1>* getSiteMap(BuildSiteMapKey key);
PatchMap<1>* getSiteMap(const UnitType *ut, const Vec2i &pos, CardinalDir facing, Unit *worker);
void adjustGlestimalMap(Field f, TypeMap<float> &iMap, const Vec2i &pos, float range);
void buildGlestimalMap(Field f, V2iList &positions);
ClusterMap* getClusterMap() const { return clusterMap; }
AnnotatedMap* getMasterMap() const { return masterMap; }
AnnotatedMap* getAnnotatedMap(int team ) { return masterMap;/*teamMaps[team];*/ }
AnnotatedMap* getAnnotatedMap(const Faction *faction) { return getAnnotatedMap(faction->getTeam()); }
AnnotatedMap* getAnnotatedMap(const Unit *unit) { return getAnnotatedMap(unit->getTeam()); }
};
}}
#endif

View File

@ -1,670 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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 <algorithm>
#include "cluster_map.h"
#include "node_pool.h"
#include "route_planner.h"
#include "line.h"
using std::min;
using Shared::Util::line;
#define _USE_LINE_PATH_ 1
#if DEBUG_RENDERING_ENABLED
# include "debug_renderer.h"
#endif
namespace Glest { namespace Game {
int Edge::numEdges[fieldCount];
int Transition::numTransitions[fieldCount];
void Edge::zeroCounters() {
for (int f = 0; f < fieldCount; ++f) {
numEdges[f] = 0;
}
}
void Transition::zeroCounters() {
for (int f = 0; f < fieldCount; ++f) {
numTransitions[f] = 0;
}
}
ClusterMap::ClusterMap(AnnotatedMap *aMap, Cartographer *carto)
: carto(carto), aMap(aMap), dirty(false) {
//_PROFILE_FUNCTION();
w = aMap->getWidth() / GameConstants::clusterSize;
h = aMap->getHeight() / GameConstants::clusterSize;
vertBorders = new ClusterBorder[(w-1)*h];
horizBorders = new ClusterBorder[w*(h-1)];
Edge::zeroCounters();
Transition::zeroCounters();
// init Borders (and hence inter-cluster edges) & evaluate clusters (intra-cluster edges)
for (int i = h - 1; i >= 0; --i) {
for (int j = w - 1; j >= 0; --j) {
Vec2i cluster(j, i);
initCluster(cluster);
evalCluster(cluster);
//g_logger.clusterInit();
}
}
}
ClusterMap::~ClusterMap() {
delete [] vertBorders;
delete [] horizBorders;
for (int f = 0; f < fieldCount; ++f) {
assert(Edge::NumEdges(Field(f)) == 0);
assert(Transition::NumTransitions(Field(f)) == 0);
if (Edge::NumEdges(Field(f)) != 0 || Transition::NumTransitions(Field(f)) != 0) {
throw megaglest_runtime_error("memory leak");
}
}
}
#define LOG(x) {}
void ClusterMap::assertValid() {
//bool valid[fieldCount];
bool inUse[fieldCount];
int numNodes[fieldCount];
int numEdges[fieldCount];
for (int f = 0; f < fieldCount; ++f) {
typedef set<const Transition *> TSet;
TSet tSet;
typedef pair<Vec2i, bool> TKey;
typedef map<TKey, const Transition *> TKMap;
TKMap tkMap;
//valid[f] = true;
numNodes[f] = 0;
numEdges[f] = 0;
inUse[f] = aMap->maxClearance[f] != 0;
if (f == fAir) inUse[f] = false;
if (!inUse[f]) {
continue;
}
// collect all transitions, checking for membership in tSet and tkMap (indicating an error)
// and filling tSet and tkMap
for (int i=0; i < (w - 1) * h; ++i) { // vertical borders
ClusterBorder *b = &vertBorders[i];
for (int j=0; j < b->transitions[f].n; ++j) {
const Transition *t = b->transitions[f].transitions[j];
if (tSet.find(t) != tSet.end()) {
LOG("single transition on multiple borders.\n");
//valid[f] = false;
} else {
tSet.insert(t);
TKey key(t->nwPos, t->vertical);
if (tkMap.find(key) != tkMap.end()) {
LOG("seperate transitions of same orientation on same cell.\n");
//valid[f] = false;
} else {
tkMap[key] = t;
}
}
++numNodes[f];
}
}
for (int i=0; i < w * (h - 1); ++i) { // horizontal borders
ClusterBorder *b = &horizBorders[i];
for (int j=0; j < b->transitions[f].n; ++j) {
const Transition *t = b->transitions[f].transitions[j];
if (tSet.find(t) != tSet.end()) {
LOG("single transition on multiple borders.\n");
//valid[f] = false;
} else {
tSet.insert(t);
TKey key(t->nwPos, t->vertical);
if (tkMap.find(key) != tkMap.end()) {
LOG("seperate transitions of same orientation on same cell.\n");
//valid[f] = false;
} else {
tkMap[key] = t;
}
}
++numNodes[f];
}
}
// with a complete collection, iterate, check all dest transitions
for (TSet::iterator it = tSet.begin(); it != tSet.end(); ++it) {
const Edges &edges = (*it)->edges;
for (Edges::const_iterator eit = edges.begin(); eit != edges.end(); ++eit) {
TSet::iterator it2 = tSet.find((*eit)->transition());
if (it2 == tSet.end()) {
LOG("Invalid edge.\n");
//valid[f] = false;
} else {
if (*it == *it2) {
LOG("self referential transition.\n");
//valid[f] = false;
}
}
++numEdges[f];
}
}
}
LOG("\nClusterMap::assertValid()");
LOG("\n=========================\n");
for (int f = 0; f < fieldCount; ++f) {
if (!inUse[f]) {
LOG("Field::" << FieldNames[f] << " not in use.\n");
} else {
LOG("Field::" << FieldNames[f] << " in use and " << (!valid[f]? "NOT " : "") << "valid.\n");
LOG("\t" << numNodes[f] << " transitions inspected.\n");
LOG("\t" << numEdges[f] << " edges inspected.\n");
}
}
}
/** Entrance init helper class */
class InsideOutIterator {
private:
int centre, incr, end;
bool flip;
public:
InsideOutIterator(int low, int high)
: flip(false) {
centre = low + (high - low) / 2;
incr = 0;
end = ((high - low) % 2 != 0) ? low - 1 : high + 1;
}
int operator*() const {
return centre + (flip ? incr : -incr);
}
void operator++() {
flip = !flip;
if (flip) ++incr;
}
bool more() {
return **this != end;
}
};
void ClusterMap::addBorderTransition(EntranceInfo &info) {
assert(info.max_clear > 0 && info.startPos != -1 && info.endPos != -1);
if (info.run < 12) {
// find central most pos with max clearance
InsideOutIterator it(info.endPos, info.startPos);
while (it.more()) {
if (eClear[info.startPos - *it] == info.max_clear) {
Transition *t;
if (info.vert) {
t = new Transition(Vec2i(info.pos, *it), info.max_clear, true, info.f);
} else {
t = new Transition(Vec2i(*it, info.pos), info.max_clear, false, info.f);
}
info.cb->transitions[info.f].add(t);
return;
}
++it;
}
assert(false);
} else {
// look for two, as close as possible to 1/4 and 3/4 accross
int l1 = info.endPos;
int h1 = info.endPos + (info.startPos - info.endPos) / 2 - 1;
int l2 = info.endPos + (info.startPos - info.endPos) / 2;
int h2 = info.startPos;
InsideOutIterator it(l1, h1);
int first_at = -1;
while (it.more()) {
if (eClear[info.startPos - *it] == info.max_clear) {
first_at = *it;
break;
}
++it;
}
if (first_at != -1) {
it = InsideOutIterator(l2, h2);
int next_at = -1;
while (it.more()) {
if (eClear[info.startPos - *it] == info.max_clear) {
next_at = *it;
break;
}
++it;
}
if (next_at != -1) {
Transition *t1, *t2;
if (info.vert) {
t1 = new Transition(Vec2i(info.pos, first_at), info.max_clear, true, info.f);
t2 = new Transition(Vec2i(info.pos, next_at), info.max_clear, true, info.f);
} else {
t1 = new Transition(Vec2i(first_at, info.pos), info.max_clear, false, info.f);
t2 = new Transition(Vec2i(next_at, info.pos), info.max_clear, false, info.f);
}
info.cb->transitions[info.f].add(t1);
info.cb->transitions[info.f].add(t2);
return;
}
}
// failed to find two, just add one...
it = InsideOutIterator(info.endPos, info.startPos);
while (it.more()) {
if (eClear[info.startPos - *it] == info.max_clear) {
Transition *t;
if (info.vert) {
t = new Transition(Vec2i(info.pos, *it), info.max_clear, true, info.f);
} else {
t = new Transition(Vec2i(*it, info.pos), info.max_clear, false, info.f);
}
info.cb->transitions[info.f].add(t);
return;
}
++it;
}
assert(false);
}
}
void ClusterMap::initClusterBorder(const Vec2i &cluster, bool north) {
//_PROFILE_FUNCTION();
ClusterBorder *cb = north ? getNorthBorder(cluster) : getWestBorder(cluster);
EntranceInfo inf;
inf.cb = cb;
inf.vert = !north;
if (cb != &sentinel) {
int pos = north ? cluster.y * GameConstants::clusterSize - 1
: cluster.x * GameConstants::clusterSize - 1;
inf.pos = pos;
int pos2 = pos + 1;
bool clear = false; // true while evaluating a Transition, false when obstacle hit
inf.max_clear = -1; // max clearance seen for current Transition
inf.startPos = -1; // start position of entrance
inf.endPos = -1; // end position of entrance
inf.run = 0; // to count entrance 'width'
for (int f = 0; f < fieldCount; ++f) {
if (!aMap->maxClearance[f] || f == fAir) continue;
# if DEBUG_RENDERING_ENABLED
if (f == fLand) {
for (int i=0; i < cb->transitions[f].n; ++i) {
getDebugRenderer().getCMOverlay().entranceCells.erase(
cb->transitions[f].transitions[i]->nwPos
);
}
}
# endif
cb->transitions[f].clear();
clear = false;
inf.f = Field(f);
inf.max_clear = -1;
for (int i=0; i < GameConstants::clusterSize; ++i) {
int clear1, clear2;
if (north) {
clear1 = aMap->metrics[Vec2i(POS_X,pos)].get(inf.f);
clear2 = aMap->metrics[Vec2i(POS_X,pos2)].get(inf.f);
} else {
clear1 = aMap->metrics[Vec2i(pos, POS_Y)].get(Field(f));
clear2 = aMap->metrics[Vec2i(pos2, POS_Y)].get(Field(f));
}
int local = min(clear1, clear2);
if (local) {
if (!clear) {
clear = true;
inf.startPos = north ? POS_X : POS_Y;
}
eClear[inf.run++] = local;
inf.endPos = north ? POS_X : POS_Y;
if (local > inf.max_clear) {
inf.max_clear = local;
}
} else {
if (clear) {
addBorderTransition(inf);
inf.run = 0;
inf.startPos = inf.endPos = inf.max_clear = -1;
clear = false;
}
}
} // for i < clusterSize
if (clear) {
addBorderTransition(inf);
inf.run = 0;
inf.startPos = inf.endPos = inf.max_clear = -1;
clear = false;
}
}// for each Field
# if DEBUG_RENDERING_ENABLED
for (int i=0; i < cb->transitions[fLand].n; ++i) {
getDebugRenderer().getCMOverlay().entranceCells.insert(
cb->transitions[fLand].transitions[i]->nwPos
);
}
# endif
} // if not sentinel
}
/** function object for line alg. 'visit' */
struct Visitor {
vector<Vec2i> &results;
Visitor(vector<Vec2i> &res) : results(res) {}
void operator()(int x, int y) {
results.push_back(Vec2i(x, y));
}
};
/** compute path length using midpoint line algorithm, @return infinite if path not possible, else cost */
float ClusterMap::linePathLength(Field f, int size, const Vec2i &start, const Vec2i &dest) {
//_PROFILE_FUNCTION();
if (start == dest) {
return 0.f;
}
vector<Vec2i> linePath;
Visitor visitor(linePath);
line(start.x, start.y, dest.x, dest.y, visitor);
assert(linePath.size() >= 2);
MoveCost costFunc(f, size, aMap);
vector<Vec2i>::iterator it = linePath.begin();
vector<Vec2i>::iterator nIt = it + 1;
float cost = 0.f;
while (nIt != linePath.end()) {
float add = costFunc(*it++, *nIt++);
if (add != -1.f) {
cost += add;
} else {
return -1.f;
}
}
return cost;
}
/** compute path length using A* (with node limit), @return infinite if path not possible, else cost */
float ClusterMap::aStarPathLength(Field f, int size, const Vec2i &start, const Vec2i &dest) {
//_PROFILE_FUNCTION();
if (start == dest) {
return 0.f;
}
SearchEngine<NodePool> *se = carto->getRoutePlanner()->getSearchEngine();
MoveCost costFunc(f, size, aMap);
DiagonalDistance dd(dest);
se->setNodeLimit(GameConstants::clusterSize * GameConstants::clusterSize);
se->setStart(start, dd(start));
PosGoal goal(dest);
AStarResult res = se->aStar<PosGoal,MoveCost,DiagonalDistance>(goal, costFunc, dd);
Vec2i goalPos = se->getGoalPos();
if (res != asrComplete || goalPos != dest) {
return -1.f;
}
return se->getCostTo(goalPos);
}
void ClusterMap::getTransitions(const Vec2i &cluster, Field f, Transitions &t) {
ClusterBorder *b = getNorthBorder(cluster);
for (int i=0; i < b->transitions[f].n; ++i) {
t.push_back(b->transitions[f].transitions[i]);
}
b = getEastBorder(cluster);
for (int i=0; i < b->transitions[f].n; ++i) {
t.push_back(b->transitions[f].transitions[i]);
}
b = getSouthBorder(cluster);
for (int i=0; i < b->transitions[f].n; ++i) {
t.push_back(b->transitions[f].transitions[i]);
}
b = getWestBorder(cluster);
for (int i=0; i < b->transitions[f].n; ++i) {
t.push_back(b->transitions[f].transitions[i]);
}
}
void ClusterMap::disconnectCluster(const Vec2i &cluster) {
//cout << "Disconnecting cluster " << cluster << endl;
for (int f = 0; f < fieldCount; ++f) {
if (!aMap->maxClearance[f] || f == fAir) continue;
Transitions t;
getTransitions(cluster, Field(f), t);
set<const Transition*> tset;
for (Transitions::iterator it = t.begin(); it != t.end(); ++it) {
tset.insert(*it);
}
//int del = 0;
for (Transitions::iterator it = t.begin(); it != t.end(); ++it) {
Transition *t = const_cast<Transition*>(*it);
Edges::iterator eit = t->edges.begin();
while (eit != t->edges.end()) {
if (tset.find((*eit)->transition()) != tset.end()) {
delete *eit;
eit = t->edges.erase(eit);
//++del;
} else {
++eit;
}
}
}
//cout << "\tDeleted " << del << " edges in Field = " << FieldNames[f] << ".\n";
}
}
void ClusterMap::update() {
//_PROFILE_FUNCTION();
//cout << "ClusterMap::update()" << endl;
for (set<Vec2i>::iterator it = dirtyNorthBorders.begin(); it != dirtyNorthBorders.end(); ++it) {
if (it->y > 0 && it->y < h) {
dirtyClusters.insert(Vec2i(it->x, it->y));
dirtyClusters.insert(Vec2i(it->x, it->y - 1));
}
}
for (set<Vec2i>::iterator it = dirtyWestBorders.begin(); it != dirtyWestBorders.end(); ++it) {
if (it->x > 0 && it->x < w) {
dirtyClusters.insert(Vec2i(it->x, it->y));
dirtyClusters.insert(Vec2i(it->x - 1, it->y));
}
}
for (set<Vec2i>::iterator it = dirtyClusters.begin(); it != dirtyClusters.end(); ++it) {
//cout << "cluster " << *it << " dirty." << endl;
disconnectCluster(*it);
}
for (set<Vec2i>::iterator it = dirtyNorthBorders.begin(); it != dirtyNorthBorders.end(); ++it) {
//cout << "cluster " << *it << " north border dirty." << endl;
initClusterBorder(*it, true);
}
for (set<Vec2i>::iterator it = dirtyWestBorders.begin(); it != dirtyWestBorders.end(); ++it) {
//cout << "cluster " << *it << " west border dirty." << endl;
initClusterBorder(*it, false);
}
for (set<Vec2i>::iterator it = dirtyClusters.begin(); it != dirtyClusters.end(); ++it) {
evalCluster(*it);
}
dirtyClusters.clear();
dirtyNorthBorders.clear();
dirtyWestBorders.clear();
dirty = false;
}
/** compute intra-cluster path lengths */
void ClusterMap::evalCluster(const Vec2i &cluster) {
//_PROFILE_FUNCTION();
//int linePathSuccess = 0, linePathFail = 0;
SearchEngine<NodePool> *se = carto->getRoutePlanner()->getSearchEngine();
se->getNeighbourFunc().setSearchCluster(cluster);
Transitions transitions;
for (int f = 0; f < fieldCount; ++f) {
if (!aMap->maxClearance[f] || f == fAir) continue;
transitions.clear();
getTransitions(cluster, Field(f), transitions);
Transitions::iterator it = transitions.begin();
for ( ; it != transitions.end(); ++it) { // foreach transition
Transition *t = const_cast<Transition*>(*it);
Vec2i start = t->nwPos;
Transitions::iterator it2 = transitions.begin();
for ( ; it2 != transitions.end(); ++it2) { // foreach other transition
const Transition* &t2 = *it2;
if (t == t2) continue;
Vec2i dest = t2->nwPos;
# if _USE_LINE_PATH_
float cost = linePathLength(Field(f), 1, start, dest);
if (cost == -1.f) {
cost = aStarPathLength(Field(f), 1, start, dest);
}
# else
float cost = aStarPathLength(Field(f), 1, start, dest);
# endif
if (cost == -1.f) continue;
Edge *e = new Edge(t2, Field(f));
t->edges.push_back(e);
e->addWeight(cost);
int size = 2;
int maxClear = t->clearance > t2->clearance ? t2->clearance : t->clearance;
while (size <= maxClear) {
# if _USE_LINE_PATH_
cost = linePathLength(Field(f), 1, start, dest);
if (cost == -1.f) {
cost = aStarPathLength(Field(f), size, start, dest);
}
# else
float cost = aStarPathLength(Field(f), size, start, dest);
# endif
if (cost == -1.f) {
break;
}
e->addWeight(cost);
assert(size == e->maxClear());
++size;
}
} // for each other transition
} // for each transition
} // for each Field
se->getNeighbourFunc().setSearchSpace(ssCellMap);
}
// ========================================================
// class TransitionNodeStorage
// ========================================================
TransitionAStarNode* TransitionNodeStore::getNode() {
if (nodeCount == size) {
//assert(false);
return NULL;
}
return &stock[nodeCount++];
}
void TransitionNodeStore::insertIntoOpen(TransitionAStarNode *node) {
if (openList.empty()) {
openList.push_front(node);
return;
}
list<TransitionAStarNode*>::iterator it = openList.begin();
while (it != openList.end() && (*it)->est() <= node->est()) {
++it;
}
openList.insert(it, node);
}
bool TransitionNodeStore::assertOpen() {
if (openList.size() < 2) {
return true;
}
set<const Transition*> seen;
list<TransitionAStarNode*>::iterator it1, it2 = openList.begin();
it1 = it2;
seen.insert((*it1)->pos);
for (++it2; it2 != openList.end(); ++it2) {
if (seen.find((*it2)->pos) != seen.end()) {
LOG("open list has cycle... that's bad.");
return false;
}
seen.insert((*it2)->pos);
if ((*it1)->est() > (*it2)->est() + 0.0001f) { // stupid inaccurate fp
LOG("Open list corrupt: it1.est() == " << (*it1)->est()
<< " > it2.est() == " << (*it2)->est());
return false;
}
}
set<const Transition*>::iterator it = open.begin();
for ( ; it != open.end(); ++it) {
if (seen.find(*it) == seen.end()) {
LOG("node marked open not on open list.");
return false;
}
}
it = seen.begin();
for ( ; it != seen.end(); ++it) {
if (open.find(*it) == open.end()) {
LOG("node on open list not marked open.");
return false;
}
}
return true;
}
Transition* TransitionNodeStore::getBestSeen() {
assert(false);
return NULL;
}
bool TransitionNodeStore::setOpen(const Transition* pos, const Transition* prev, float h, float d) {
assert(open.find(pos) == open.end());
assert(closed.find(pos) == closed.end());
TransitionAStarNode *node = getNode();
if (!node) return false;
node->pos = pos;
node->prev = prev;
node->distToHere = d;
node->heuristic = h;
open.insert(pos);
insertIntoOpen(node);
listed[pos] = node;
return true;
}
void TransitionNodeStore::updateOpen(const Transition* pos, const Transition* &prev, const float cost) {
assert(open.find(pos) != open.end());
assert(closed.find(prev) != closed.end());
TransitionAStarNode *prevNode = listed[prev];
TransitionAStarNode *posNode = listed[pos];
if (prevNode->distToHere + cost < posNode->distToHere) {
openList.remove(posNode);
posNode->prev = prev;
posNode->distToHere = prevNode->distToHere + cost;
insertIntoOpen(posNode);
}
}
const Transition* TransitionNodeStore::getBestCandidate() {
if (openList.empty()) return NULL;
TransitionAStarNode *node = openList.front();
const Transition *best = node->pos;
openList.pop_front();
open.erase(open.find(best));
closed.insert(best);
return best;
}
}}

View File

@ -1,324 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifndef _GLEST_GAME_CLUSTER_MAP_H_
#define _GLEST_GAME_CLUSTER_MAP_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "util.h"
#include "game_constants.h"
#include "skill_type.h"
#include "math_util.h"
#include <map>
#include <set>
#include <list>
namespace Glest { namespace Game {
using std::set;
using std::map;
using std::list;
using Shared::Util::deleteValues;
class AnnotatedMap;
class Cartographer;
struct Transition;
/** uni-directional edge, is owned by the source transition, contains pointer to dest */
struct Edge {
private:
static int numEdges[fieldCount];
const Transition *dest;
vector<float> weights;
Field f; // for diagnostics... remove this one day
public:
Edge(const Transition *t, Field f) : f(f) {
dest = t;
++numEdges[f];
}
~Edge() {
--numEdges[f];
}
void addWeight(const float w) { weights.push_back(w); }
const Transition* transition() const { return dest; }
float cost(int size) const { return weights[size-1]; }
int maxClear() { return weights.size(); }
static int NumEdges(Field f) { return numEdges[f]; }
static void zeroCounters();
};
typedef list<Edge*> Edges;
/** */
struct Transition {
private:
static int numTransitions[fieldCount];
Field f;
public:
int clearance;
Vec2i nwPos;
bool vertical;
Edges edges;
Transition(Vec2i pos, int clear, bool vert, Field f)
: f(f), clearance(clear), nwPos(pos), vertical(vert) {
++numTransitions[f];
}
~Transition() {
deleteValues(edges.begin(), edges.end());
--numTransitions[f];
}
static int NumTransitions(Field f) { return numTransitions[f]; }
static void zeroCounters();
};
typedef vector<const Transition*> Transitions;
struct TransitionCollection {
Transition *transitions[GameConstants::clusterSize / 2];
unsigned n;
TransitionCollection() {
n = 0;
memset(transitions, 0, sizeof(Transition*) * GameConstants::clusterSize / 2);
}
void add(Transition *t) {
assert(n < GameConstants::clusterSize / 2);
transitions[n++] = t;
}
void clear() {
for (int i=0; i < n; ++i) {
delete transitions[i];
}
n = 0;
}
};
struct ClusterBorder {
TransitionCollection transitions[fieldCount];
~ClusterBorder() {
for (int f = 0; f < fieldCount; ++f) {
transitions[f].clear();
}
}
};
/** NeighbourFunc, describes abstract search space */
class TransitionNeighbours {
public:
void operator()(const Transition* pos, vector<const Transition*> &neighbours) const {
for (Edges::const_iterator it = pos->edges.begin(); it != pos->edges.end(); ++it) {
neighbours.push_back((*it)->transition());
}
}
};
/** cost function for searching cluster map */
class TransitionCost {
Field field;
int size;
public:
TransitionCost(Field f, int s) : field(f), size(s) {}
float operator()(const Transition *t, const Transition *t2) const {
Edges::const_iterator it = t->edges.begin();
for ( ; it != t->edges.end(); ++it) {
if ((*it)->transition() == t2) {
break;
}
}
if (it == t->edges.end()) {
throw megaglest_runtime_error("bad connection in ClusterMap.");
}
if ((*it)->maxClear() >= size) {
return (*it)->cost(size);
}
return -1.f;
}
};
/** goal function for search on cluster map */
class TransitionGoal {
set<const Transition*> goals;
public:
TransitionGoal() {}
set<const Transition*>& goalTransitions() {return goals;}
bool operator()(const Transition *t, const float d) const {
return goals.find(t) != goals.end();
}
};
#define POS_X ((cluster.x + 1) * GameConstants::clusterSize - i - 1)
#define POS_Y ((cluster.y + 1) * GameConstants::clusterSize - i - 1)
class ClusterMap {
#if _GAE_DEBUG_EDITION_
friend class DebugRenderer;
#endif
private:
int w, h;
ClusterBorder *vertBorders, *horizBorders, sentinel;
Cartographer *carto;
AnnotatedMap *aMap;
set<Vec2i> dirtyClusters;
set<Vec2i> dirtyNorthBorders;
set<Vec2i> dirtyWestBorders;
bool dirty;
int eClear[GameConstants::clusterSize];
public:
ClusterMap(AnnotatedMap *aMap, Cartographer *carto);
~ClusterMap();
int getWidth() const { return w; }
int getHeight() const { return h; }
static Vec2i cellToCluster (const Vec2i &cellPos) {
return Vec2i(cellPos.x / GameConstants::clusterSize, cellPos.y / GameConstants::clusterSize);
}
// ClusterBorder getters
ClusterBorder* getNorthBorder(Vec2i cluster) {
return ( cluster.y == 0 ) ? &sentinel : &horizBorders[(cluster.y - 1) * w + cluster.x ];
}
ClusterBorder* getEastBorder(Vec2i cluster) {
return ( cluster.x == w - 1 ) ? &sentinel : &vertBorders[cluster.y * (w - 1) + cluster.x ];
}
ClusterBorder* getSouthBorder(Vec2i cluster) {
return ( cluster.y == h - 1 ) ? &sentinel : &horizBorders[cluster.y * w + cluster.x];
}
ClusterBorder* getWestBorder(Vec2i cluster) {
return ( cluster.x == 0 ) ? &sentinel : &vertBorders[cluster.y * (w - 1) + cluster.x - 1];
}
ClusterBorder* getBorder(Vec2i cluster, CardinalDir dir) {
switch (dir) {
case CardinalDir::NORTH:
return getNorthBorder(cluster);
case CardinalDir::EAST:
return getEastBorder(cluster);
case CardinalDir::SOUTH:
return getSouthBorder(cluster);
case CardinalDir::WEST:
return getWestBorder(cluster);
default:
throw megaglest_runtime_error("ClusterMap::getBorder() passed dodgey direction");
}
return 0; // keep compiler quiet
}
void getTransitions(const Vec2i &cluster, Field f, Transitions &t);
bool isDirty() { return dirty; }
void update();
void setClusterDirty(const Vec2i &cluster) { dirty = true; dirtyClusters.insert(cluster); }
void setNorthBorderDirty(const Vec2i &cluster) { dirty = true; dirtyNorthBorders.insert(cluster); }
void setWestBorderDirty(const Vec2i &cluster) { dirty = true; dirtyWestBorders.insert(cluster); }
void assertValid();
private:
struct EntranceInfo {
ClusterBorder *cb;
Field f;
bool vert;
int pos, max_clear, startPos, endPos, run;
};
void addBorderTransition(EntranceInfo &info);
void initClusterBorder(const Vec2i &cluster, bool north);
/** initialise/re-initialise cluster (evaluates north and west borders) */
void initCluster(const Vec2i &cluster) {
initClusterBorder(cluster, true);
initClusterBorder(cluster, false);
}
void evalCluster(const Vec2i &cluster);
float linePathLength(Field f, int size, const Vec2i &start, const Vec2i &dest);
float aStarPathLength(Field f, int size, const Vec2i &start, const Vec2i &dest);
void disconnectCluster(const Vec2i &cluster);
};
struct TransitionAStarNode {
const Transition *pos, *prev;
float heuristic; /**< estimate of distance to goal */
float distToHere; /**< cost from origin to this node */
float est() const { /**< estimate, costToHere + heuristic */
return distToHere + heuristic;
}
};
// ========================================================
// class TransitionNodeStorage
// ========================================================
// NodeStorage template interface
class TransitionNodeStore {
private:
list<TransitionAStarNode*> openList;
set<const Transition*> open;
set<const Transition*> closed;
map<const Transition*, TransitionAStarNode*> listed;
int size, nodeCount;
TransitionAStarNode *stock;
TransitionAStarNode* getNode();
void insertIntoOpen(TransitionAStarNode *node);
bool assertOpen();
public:
TransitionNodeStore(int size) : size(size), stock(NULL) {
stock = new TransitionAStarNode[size];
reset();
}
~TransitionNodeStore() {
delete [] stock;
}
void reset() { nodeCount = 0; open.clear(); closed.clear(); openList.clear(); listed.clear(); }
void setMaxNodes(int limit) { }
bool isOpen(const Transition* pos) { return open.find(pos) != open.end(); }
bool isClosed(const Transition* pos) { return closed.find(pos) != closed.end(); }
bool setOpen(const Transition* pos, const Transition* prev, float h, float d);
void updateOpen(const Transition* pos, const Transition* &prev, const float cost);
const Transition* getBestCandidate();
Transition* getBestSeen();
float getHeuristicAt(const Transition* &pos) { return listed[pos]->heuristic; }
float getCostTo(const Transition* pos) { return listed[pos]->distToHere; }
float getEstimateFor(const Transition* pos) { return listed[pos]->est(); }
const Transition* getBestTo(const Transition* pos) { return listed[pos]->prev; }
};
}}
#endif

View File

@ -1,257 +0,0 @@
// ==============================================================
// 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
// ==============================================================
#ifndef _GAME_SEARCH_INLUENCE_MAP_H_
#define _GAME_SEARCH_INLUENCE_MAP_H_
#include "vec.h"
#include "data_types.h"
#include <algorithm>
namespace Glest { namespace Game {
using Shared::Platform::uint32;
typedef Shared::Graphics::Vec2i Point;
struct Rectangle {
Rectangle() : x(0), y(0), w(0), h(0) {}
Rectangle(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {}
int x, y, w, h;
};
const Point invalidPos(-1,-1);
// =====================================================
// class InfluenceMap
// =====================================================
template<typename iType, typename MapType> class InfluenceMap {
public:
InfluenceMap(Rectangle b, iType def) : def(def), x(b.x), y(b.y), w(b.w), h(b.h) {}
iType getInfluence(Point pos) {
pos = translate(pos);
if (pos != invalidPos) {
return static_cast<MapType*>(this)->get(pos);
}
return def;
}
bool setInfluence(Point pos, iType val) {
pos = translate(pos);
if (pos != invalidPos) {
static_cast<MapType*>(this)->set(pos, val);
return true;
}
return false;
}
Point translate(Point p) {
const int nx = p.x - x;
const int ny = p.y - y;
if (nx >= 0 && ny >=0 && nx < w && ny < h) {
return Point(nx, ny);
}
return invalidPos;
}
Point getPos() const { return Point(x, y); }
Rectangle getBounds() const { return Rectangle(x, y, w, h); }
protected:
iType def; // defualt value for positions not within (this) maps dimensions
int x , y, w, h; // Dimensions of this map.
};
// =====================================================
// class TypeMap
// =====================================================
template<typename type = float> class TypeMap : public InfluenceMap<type, TypeMap<type> > {
friend class InfluenceMap<type, TypeMap<type> >;
public:
TypeMap(Rectangle b, type def) : InfluenceMap<type, TypeMap<type> >(b,def) {
data = new type[b.w*b.h];
}
~TypeMap() { delete [] data; }
void zeroMap() { memset(data, 0, sizeof(type) * this->w * this->h); }
void clearMap(type val) { std::fill_n(data, this->w * this->h, val); }
private:
type get(Point p) { return data[p.y * this->w + p.x]; }
void set(Point p, type v) { data[p.y * this->w + p.x] = v; }
type *data;
};
// =====================================================
// class PatchMap
// =====================================================
/** bit packed InfluenceMap, values are bit packed into 32 bit 'sections' (possibly padded)
* Not usefull for bits >= 7, just use TypeMap<uint8>, TypeMap<uint16> etc...
* bit wastage:
* bits == 1, 2 or 4: no wastage in full sections
* bits == 3, 5 or 6: 2 bits wasted per full section
*/
template<int bits> class PatchMap : public InfluenceMap<uint32, PatchMap<bits> > {
friend class InfluenceMap<uint32, PatchMap<bits> >;
public:
PatchMap(Rectangle b, uint32 def) : InfluenceMap<uint32,PatchMap<bits> >(b,def) {
//cout << "PatchMap<" << bits << "> max_value = "<< max_value <<", [width = " << b.w << " height = " << b.h << "]\n";
sectionsPerRow = b.w / sectionSize + 1;
//cout << "section size = " << sectionSize << ", sections per row = " << sectionsPerRow << endl;
data = new uint32[b.h * sectionsPerRow];
}
//only for infuence_map_test.cpp:249
PatchMap<bits> &operator=(const PatchMap<bits> &op){
if(&op != this) {
//FIXME: better when moved to InfluenceMap::operator=...
this->def = op.def;
this->x = op.x; this->y = op.y; this->w = op.w; this->h = op.h;
//
sectionsPerRow = op.sectionsPerRow;
delete[] data;
data = new uint32[op.h * sectionsPerRow];
memcpy(data, op.data, op.h * sectionsPerRow);
}
return *this;
}
~PatchMap() { delete [] data; }
void zeroMap() { memset(data, 0, sizeof(uint32) * sectionsPerRow * this->h); }
void clearMap(uint32 val) {
// assert(val < max_value);
data[0] = 0;
for ( int i=0; i < sectionSize - 1; ++i) {
data[0] |= val;
data[0] <<= bits;
}
data[0] |= val;
for ( int i=1; i < sectionsPerRow; ++i ) {
data[i] = data[0];
}
const int rowSize = sectionsPerRow * sizeof(uint32);
for ( int i=1; i < this->h; ++i ) {
uint32 *ptr = &data[i * sectionsPerRow];
memcpy(ptr, data, rowSize);
}
}
private:
uint32 get(Point p) {
int sectionNdx = p.y * sectionsPerRow + p.x / sectionSize;
int subSectionNdx = p.x % sectionSize;
uint32 val = (data[sectionNdx] >> (subSectionNdx * bits)) & max_value;
//cout << "get(" << p.x << "," << p.y << "). section=" << sectionNdx
// << ", subsection=" << subSectionNdx << " = " << val <<endl;
return val;
}
void set(Point p, uint32 v) {
if (v > max_value) { //clamp?
v = max_value;
}
int sectionNdx = p.y * sectionsPerRow + p.x / sectionSize;
int subSectionNdx = p.x % sectionSize;
uint32 val = v << bits * subSectionNdx;
uint32 mask = max_value << bits * subSectionNdx;
data[sectionNdx] &= ~mask; // blot out old value
data[sectionNdx] |= val; // put in the new value
}/*
void logSection(int s) {
cout << "[";
for ( int j = 31; j >= 0; --j ) {
uint32 bitmask = 1 << j;
if ( data[s] & bitmask ) cout << "1";
else cout << "0";
if ( j && j % bits == 0 ) cout << " ";
}
cout << "]" << endl;
}*/
static const uint32 max_value = (1 << bits) - 1;
static const int sectionSize = 32 / bits;
int sectionsPerRow;
uint32 *data;
};
// =====================================================
// class FlowMap
// =====================================================
/** bit packed 'offset' map, where offset is of the form -1 <= x <= 1 && -1 <= y <= 1 */
class FlowMap : public InfluenceMap<Point, FlowMap> {
friend class InfluenceMap<Point, FlowMap>;
private:
Point expand(uint32 v) {
Point res(0,0);
if ( v & 8 ) { res.x = -1; }
else if ( v & 4 ) { res.x = 1; }
if ( v & 2 ) { res.y = -1; }
else if ( v & 1 ) { res.y = 1; }
return res;
}
uint32 pack(Point v) {
uint32 res = 0;
if ( v.x ) { res = 1 << (v.x > 0 ? 2 : 3); }
if ( v.y ) { res |= 1 << (v.y > 0 ? 0 : 1); }
return res;
}
public:
FlowMap(Rectangle b, Point def) : InfluenceMap<Point,FlowMap>(b,def) {
blocksPerRow = b.w / 8 + 1;
data = new uint32[b.h * blocksPerRow];
}
~FlowMap() { delete [] data; }
void zeroMap() { memset(data, 0, sizeof(uint32) * blocksPerRow * h); }
void clearMap(Point val) {
uint32 v = pack(val);
data[0] = 0;
for ( int i=0; i < 7; ++i ) {
data[0] |= v;
data[0] <<= 4;
}
data[0] |= v;
for ( int i=1; i < blocksPerRow; ++i ) {
data[i] = data[0];
}
const int rowSize = blocksPerRow * sizeof(uint32);
for ( int i=1; i < h; ++i ) {
uint32 *ptr = &data[i * blocksPerRow];
memcpy(ptr, data, rowSize);
}
}
private:
Point get(Point p) {
const int blockNdx = p.y * blocksPerRow + p.x / 8;
const int subBlockNdx = p.x % 8;
uint32 val = (data[blockNdx] >> (subBlockNdx * 4)) & 15;
return expand(val);
}
void set(Point p, Point v) {
int blockNdx = p.y * blocksPerRow + p.x / 8;
int subBlockNdx = p.x % 8;
uint32 val = pack(v) << 4 * subBlockNdx;
uint32 mask = 15 << 4 * subBlockNdx;
data[blockNdx] &= ~mask; // blot out old value
data[blockNdx] |= val; // put in the new value
}/*
void logSection(int s) {
cout << "[";
for ( int j = 31; j >= 0; --j ) {
uint32 bitmask = 1 << j;
if ( data[s] & bitmask ) cout << "1";
else cout << "0";
if ( j && j % 4 == 0 ) cout << " ";
}
cout << "]" << endl;
}*/
int blocksPerRow;
uint32 *data; // 8 values each
};
}}
#endif

View File

@ -1,134 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
//
// File: node_pool.cpp
//
#include "node_pool.h"
#include "world.h"
#include "map.h"
namespace Glest { namespace Game {
// =====================================================
// class NodePool
// =====================================================
NodePool::NodePool(int w, int h)
: counter(0)
, leastH(NULL)
, numNodes(0)
, tmpMaxNodes(size)
, markerArray(w, h) {
stock = new AStarNode[size];
}
NodePool::~NodePool() {
delete [] stock;
}
/** reset the node pool for a new search (resets tmpMaxNodes too) */
void NodePool::reset() {
numNodes = 0;
counter = 0;
tmpMaxNodes = size;
leastH = NULL;
markerArray.newSearch();
openHeap.clear();
//IF_DEBUG_EDITION( listedNodes.clear(); )
}
/** set a maximum number of nodes to expand */
void NodePool::setMaxNodes(const int max) {
assert(max >= 32 && max <= size); // reasonable number ?
assert(!numNodes); // can't do this after we've started using it.
tmpMaxNodes = max;
}
/** marks an unvisited position as open
* @param pos the position to open
* @param prev the best known path to pos is from
* @param h the heuristic for pos
* @param d the costSoFar for pos
* @return true if added, false if node limit reached */
bool NodePool::setOpen(const Vec2i &pos, const Vec2i &prev, float h, float d) {
assert(!isOpen(pos));
AStarNode *node = newNode();
if (!node) { // NodePool exhausted
return false;
}
//IF_DEBUG_EDITION( listedNodes.push_back(pos); )
node->posOff = pos;
if (prev.x >= 0) {
node->posOff.ox = prev.x - pos.x;
node->posOff.oy = prev.y - pos.y;
} else {
node->posOff.ox = 0;
node->posOff.oy = 0;
}
node->distToHere = d;
node->heuristic = h;
addOpenNode(node);
if (!numNodes || h < leastH->heuristic) {
leastH = node;
}
numNodes++;
return true;
}
/** add a new node to the open list @param node pointer to the node to add */
void NodePool::addOpenNode(AStarNode *node) {
assert(!isOpen(node->pos()));
markerArray.setOpen(node->pos());
markerArray.set(node->pos(), node);
openHeap.insert(node);
}
/** conditionally update a node on the open list. Tests if a path through a new nieghbour
* is better than the existing known best path to pos, updates if so.
* @param pos the open postion to test
* @param prev the new path from
* @param cost the cost to here from prev */
void NodePool::updateOpen(const Vec2i &pos, const Vec2i &prev, const float cost) {
assert(isClosed(prev));
assert(isOpen(pos));
AStarNode *posNode, *prevNode;
posNode = markerArray.get(pos);
prevNode = markerArray.get(prev);
if (prevNode->distToHere + cost < posNode->distToHere) {
posNode->posOff.ox = prev.x - pos.x;
posNode->posOff.oy = prev.y - pos.y;
posNode->distToHere = prevNode->distToHere + cost;
openHeap.promote(posNode);
}
}
#if _GAE_DEBUG_EDITION_
list<Vec2i>* NodePool::getOpenNodes() {
list<Vec2i> *ret = new list<Vec2i>();
list<Vec2i>::iterator it = listedNodes.begin();
for ( ; it != listedNodes.end (); ++it) {
if (isOpen(*it)) ret->push_back(*it);
}
return ret;
}
list<Vec2i>* NodePool::getClosedNodes() {
list<Vec2i> *ret = new list<Vec2i>();
list<Vec2i>::iterator it = listedNodes.begin();
for ( ; it != listedNodes.end(); ++it) {
if (isClosed(*it)) ret->push_back(*it);
}
return ret;
}
#endif // _GAE_DEBUG_EDITION_
}}

View File

@ -1,198 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
//
// File: node_pool.h
//
#ifndef _GLEST_GAME_PATHFINDER_NODE_POOL_H_
#define _GLEST_GAME_PATHFINDER_NODE_POOL_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "vec.h"
#include "game_constants.h"
#include "heap.h"
#include "data_types.h"
#include <algorithm>
#include <set>
#include <list>
//#include <limits>
namespace Glest { namespace Game {
using Shared::Util::MinHeap;
using Shared::Graphics::Vec2i;
using namespace Shared::Platform;
#pragma pack(push, 4)
struct PosOff { /**< A bit packed position (Vec2i) and offset (direction) pair */
PosOff() : x(0), y(0), ox(0), oy(0) {} /**< Construct a PosOff [0,0] */
PosOff(Vec2i pos) : ox(0),oy(0) { *this = pos; } /**< Construct a PosOff [pos.x, pos.y] */
PosOff(int x, int y) : x(x) ,y(y) ,ox(0), oy(0) {} /**< Construct a PosOff [x,y] */
PosOff& operator=(Vec2i pos) { /**< Assign from Vec2i */
assert(pos.x <= 8191 && pos.y <= 8191);
x = pos.x; y = pos.y; return *this;
}
bool operator==(PosOff &p) const
{ return x == p.x && y == p.y; } /**< compare position components only */
Vec2i getPos() const { return Vec2i(x, y); } /**< this packed pos as Vec2i */
Vec2i getPrev() const { return Vec2i(x + ox, y + oy); } /**< return pos + offset */
Vec2i getOffset() const { return Vec2i(ox, oy); } /**< return offset */
bool hasOffset() const { return ox || oy; } /**< has an offset */
bool valid() const { return x >= 0 && y >= 0; } /**< is this position valid */
int32 x : 14; /**< x coordinate */
int32 y : 14; /**< y coordinate */
int32 ox : 2; /**< x offset */
int32 oy : 2; /**< y offset */
};
#pragma pack(pop)
// =====================================================
// struct AStarNode
// =====================================================
#pragma pack(push, 2)
struct AStarNode { /**< A node structure for A* with NodePool */
PosOff posOff; /**< position of this node, and direction of best path to it */
float heuristic; /**< estimate of distance to goal */
float distToHere; /**< cost from origin to this node */
float est() const { return distToHere + heuristic;} /**< estimate, costToHere + heuristic */
Vec2i pos() const { return posOff.getPos(); } /**< position of this node */
Vec2i prev() const { return posOff.getPrev(); } /**< best path to this node is from */
bool hasPrev() const{ return posOff.hasOffset(); } /**< has valid previous 'pointer' */
int32 heap_ndx;
void setHeapIndex(int ndx) { heap_ndx = ndx; }
int getHeapIndex() const { return heap_ndx; }
bool operator<(const AStarNode &that) const {
const float diff = est() - that.est();
if (diff < 0.f) return true;
if (diff > 0.f) return false;
// tie, prefer closer to goal...
const float h_diff = heuristic - that.heuristic;
if (h_diff < 0.f) return true;
if (h_diff > 0.f) return false;
// still tied... just distinguish them somehow...
return pos() < that.pos();
}
}; // == 128 bits (16 bytes)
#pragma pack(pop)
// ========================================================
// class NodePool
// ========================================================
class NodePool { /**< A NodeStorage class (template interface) for A* */
private:
static const int size = 512; /**< total number of AStarNodes in each pool */
AStarNode *stock; /**< The block of nodes */
int counter; /**< current counter */
// =====================================================
// struct MarkerArray
// =====================================================
/** An Marker & Pointer Array supporting two mark types, open and closed. */
///@todo replace pointers with indices, interleave mark and index arrays
struct MarkerArray {
private:
int stride; /**< stride of array */
unsigned int counter; /**< the counter */
unsigned int *marker; /**< the mark array */
AStarNode **pArray; /**< the pointer array */
public:
MarkerArray(int w, int h) : stride(w), counter(0) {
marker = new unsigned int[w * h];
memset(marker, 0, w * h * sizeof(unsigned int));
pArray = new AStarNode*[w * h];
memset(pArray, 0, w * h * sizeof(AStarNode*));
}
~MarkerArray() { delete [] marker; delete [] pArray; }
inline void newSearch() { counter += 2; }
inline void setOpen(const Vec2i &pos) { marker[pos.y * stride + pos.x] = counter; }
inline void setClosed(const Vec2i &pos) { marker[pos.y * stride + pos.x] = counter + 1; }
inline bool isOpen(const Vec2i &pos) { return marker[pos.y * stride + pos.x] == counter; }
inline bool isClosed(const Vec2i &pos) { return marker[pos.y * stride + pos.x] == counter + 1; }
inline bool isListed(const Vec2i &pos) { return marker[pos.y * stride + pos.x] >= counter; } /**< @deprecated not needed? */
inline void setNeither(const Vec2i &pos){ marker[pos.y * stride + pos.x] = 0; } /**< @deprecated not needed? */
inline void set(const Vec2i &pos, AStarNode *ptr) { pArray[pos.y * stride + pos.x] = ptr; } /**< set the pointer for pos to ptr */
inline AStarNode* get(const Vec2i &pos) { return pArray[pos.y * stride + pos.x]; } /**< get the pointer for pos */
};
private:
AStarNode *leastH; /**< The 'best' node seen so far this search */
int numNodes; /**< number of nodes used so far this search */
int tmpMaxNodes; /**< a temporary maximum number of nodes to use */
MarkerArray markerArray; /**< An array the size of the map, indicating node status (unvisited, open, closed) */
MinHeap<AStarNode> openHeap; /**< the open list, binary heap with index aware nodes */
public:
NodePool(int w, int h);
~NodePool();
// NodeStorage template interface
//
void setMaxNodes(const int max);
void reset();
bool isOpen(const Vec2i &pos) { return markerArray.isOpen(pos); } /**< test if a position is open */
bool isClosed(const Vec2i &pos) { return markerArray.isClosed(pos); } /**< test if a position is closed */
bool isListed(const Vec2i &pos) { return markerArray.isListed(pos); } /**< @deprecated needed for canPathOut() */
bool setOpen(const Vec2i &pos, const Vec2i &prev, float h, float d);
void updateOpen(const Vec2i &pos, const Vec2i &prev, const float cost);
/** get the best candidate from the open list, and close it.
* @return the lowest estimate node from the open list, or -1,-1 if open list empty */
Vec2i getBestCandidate() {
if (openHeap.empty()) {
return Vec2i(-1);
}
AStarNode *ptr = openHeap.extract();
markerArray.setClosed(ptr->pos());
return ptr->pos();
}
/** get the best heuristic node seen this search */
Vec2i getBestSeen() { return leastH->pos(); }
/** get the heuristic of the node at pos [known to be visited] */
float getHeuristicAt(const Vec2i &pos) { return markerArray.get(pos)->heuristic; }
/** get the cost to the node at pos [known to be visited] */
float getCostTo(const Vec2i &pos) { return markerArray.get(pos)->distToHere; }
/** get the estimate for the node at pos [known to be visited] */
float getEstimateFor(const Vec2i &pos) { return markerArray.get(pos)->est(); }
/** get the best path to the node at pos [known to be closed] */
Vec2i getBestTo(const Vec2i &pos) {
AStarNode *ptr = markerArray.get(pos);
assert(ptr);
return ptr->hasPrev() ? ptr->prev() : Vec2i(-1);
}
private:
void addOpenNode(AStarNode *node);
AStarNode* newNode() { return ( counter < size ? &stock[counter++] : NULL ); }
#if _GAE_DEBUG_EDITION_
public:
// interface to support debugging textures
list<Vec2i>* getOpenNodes();
list<Vec2i>* getClosedNodes();
list<Vec2i> listedNodes;
#endif
};
}}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
// 2009-2010 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
// ==============================================================
#ifndef _GLEST_GAME_ROUTEPLANNER_H_
#define _GLEST_GAME_ROUTEPLANNER_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "game_constants.h"
#include "influence_map.h"
#include "annotated_map.h"
#include "cluster_map.h"
#include "config.h"
#include "profiler.h"
#include "search_engine.h"
#include "cartographer.h"
#include "node_pool.h"
#include "world.h"
#include "data_types.h"
#define PF_TRACE() SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__)
using Shared::Graphics::Vec2i;
using Shared::Platform::uint32;
namespace Glest { namespace Game {
/** maximum radius to search for a free position from a target position */
const int maxFreeSearchRadius = 10;
/** @deprecated not in use */
const int pathFindNodesMax = 2048;
typedef SearchEngine<TransitionNodeStore,TransitionNeighbours,const Transition*> TransitionSearchEngine;
class PMap1Goal {
protected:
PatchMap<1> *pMap;
public:
PMap1Goal(PatchMap<1> *pMap) : pMap(pMap) {}
bool operator()(const Vec2i &pos, const float) const {
if (pMap->getInfluence(pos)) {
return true;
}
return false;
}
};
/** gets the two 'diagonal' cells to check for obstacles when a unit is moving diagonally
* @param s start pos
* @param d destination pos
* @param size size of unit
* @return d1 & d2, the two cells to check
* @warning assumes s & d are indeed diagonal, abs(s.x - d.x) == 1 && abs(s.y - d.y) == 1
*/
__inline void getDiags(const Vec2i &s, const Vec2i &d, const int size, Vec2i &d1, Vec2i &d2) {
# define _SEARCH_ENGINE_GET_DIAGS_DEFINED_
assert(abs(s.x - d.x) == 1 && abs(s.y - d.y) == 1);
if (size == 1) {
d1.x = s.x; d1.y = d.y;
d2.x = d.x; d2.y = s.y;
return;
}
if (d.x > s.x) { // travelling east
if (d.y > s.y) { // se
d1.x = d.x + size - 1; d1.y = s.y;
d2.x = s.x; d2.y = d.y + size - 1;
} else { // ne
d1.x = s.x; d1.y = d.y;
d2.x = d.x + size - 1; d2.y = s.y + size - 1;
}
} else { // travelling west
if (d.y > s.y) { // sw
d1.x = d.x; d1.y = s.y;
d2.x = s.x + size - 1; d2.y = d.y + size - 1;
} else { // nw
d1.x = d.x; d1.y = s.y + size - 1;
d2.x = s.x + size - 1; d2.y = d.y;
}
}
}
/** The movement cost function */
class MoveCost {
private:
const int size; /**< size of agent */
const Field field; /**< field to search in */
const AnnotatedMap *aMap; /**< map to search on */
public:
MoveCost(const Unit *unit, const AnnotatedMap *aMap)
: size(unit->getType()->getSize()), field(unit->getCurrField()), aMap(aMap) {}
MoveCost(const Field field, const int size, const AnnotatedMap *aMap )
: size(size), field(field), aMap(aMap) {}
/** The cost function @param p1 position 1 @param p2 position 2 ('adjacent' p1)
* @return cost of move, possibly infinite */
float operator()(const Vec2i &p1, const Vec2i &p2) const {
assert(p1.dist(p2) < 1.5 && p1 != p2);
//assert(g_map.isInside(p2));
if (!aMap->canOccupy(p2, size, field)) {
return -1.f;
}
if (p1.x != p2.x && p1.y != p2.y) {
Vec2i d1, d2;
getDiags(p1, p2, size, d1, d2);
//assert(g_map.isInside(d1) && g_map.isInside(d2));
if (!aMap->canOccupy(d1, 1, field) || !aMap->canOccupy(d2, 1, field) ) {
return -1.f;
}
return SQRT2;
}
return 1.f;
// todo... height
}
};
enum HAAStarResult {
hsrFailed,
hsrComplete,
hsrStartTrap,
hsrGoalTrap
};
// =====================================================
// class RoutePlanner
// =====================================================
/** Finds paths for units using SearchEngine<>::aStar<>() */
class RoutePlanner {
public:
RoutePlanner(World *world);
~RoutePlanner();
TravelState findPathToLocation(Unit *unit, const Vec2i &finalPos);
/** @see findPathToLocation() */
TravelState findPath(Unit *unit, const Vec2i &finalPos) {
return findPathToLocation(unit, finalPos);
}
TravelState findPathToResource(Unit *unit, const Vec2i &targetPos, const ResourceType *rt);
TravelState findPathToStore(Unit *unit, const Unit *store);
TravelState findPathToBuildSite(Unit *unit, const UnitType *bType, const Vec2i &bPos, CardinalDir bFacing);
bool isLegalMove(Unit *unit, const Vec2i &pos) const;
SearchEngine<NodePool>* getSearchEngine() { return nsgSearchEngine; }
private:
bool repairPath(Unit *unit);
TravelState findAerialPath(Unit *unit, const Vec2i &targetPos);
TravelState doRouteCache(Unit *unit);
TravelState doQuickPathSearch(Unit *unit, const Vec2i &target);
TravelState findPathToGoal(Unit *unit, PMap1Goal &goal, const Vec2i &targetPos);
TravelState customGoalSearch(PMap1Goal &goal, Unit *unit, const Vec2i &target);
float quickSearch(Field field, int Size, const Vec2i &start, const Vec2i &dest);
bool refinePath(Unit *unit);
void smoothPath(Unit *unit);
HAAStarResult setupHierarchicalOpenList(Unit *unit, const Vec2i &target);
HAAStarResult setupHierarchicalSearch(Unit *unit, const Vec2i &dest, TransitionGoal &goalFunc);
HAAStarResult findWaypointPath(Unit *unit, const Vec2i &dest, WaypointPath &waypoints);
HAAStarResult findWaypointPathUnExplored(Unit *unit, const Vec2i &dest, WaypointPath &waypoints);
World *world;
SearchEngine<NodePool> *nsgSearchEngine;
NodePool *nodeStore;
TransitionSearchEngine *tSearchEngine;
TransitionNodeStore *tNodeStore;
Vec2i computeNearestFreePos(const Unit *unit, const Vec2i &targetPos);
bool attemptMove(Unit *unit) const {
UnitPathInterface *path = unit->getPath();
UnitPath *advPath = dynamic_cast<UnitPath *>(path);
if(advPath == NULL) {
throw megaglest_runtime_error("Invalid or NULL unit path pointer!");
}
assert(advPath->isEmpty() == false);
Vec2i pos = advPath->peek();
if (isLegalMove(unit, pos)) {
unit->setTargetPos(pos);
advPath->pop();
return true;
}
return false;
}
#if _GAE_DEBUG_EDITION_
TravelState doFullLowLevelAStar(Unit *unit, const Vec2i &dest);
#endif
#if DEBUG_SEARCH_TEXTURES
public:
enum { SHOW_PATH_ONLY, SHOW_OPEN_CLOSED_SETS, SHOW_LOCAL_ANNOTATIONS } debug_texture_action;
#endif
}; // class RoutePlanner
/** Diaginal Distance Heuristic */
class DiagonalDistance {
public:
DiagonalDistance(const Vec2i &target) : target(target) {}
/** search target */
Vec2i target;
/** The heuristic function. @param pos the position to calculate the heuristic for
* @return an estimate of the cost to target */
float operator()(const Vec2i &pos) const {
float dx = (float)abs(pos.x - target.x),
dy = (float)abs(pos.y - target.y);
float diag = dx < dy ? dx : dy;
float straight = dx + dy - 2 * diag;
return 1.4 * diag + straight;
}
};
/** Goal function for 'normal' search */
class PosGoal {
private:
Vec2i target; /**< search target */
public:
PosGoal(const Vec2i &target) : target(target) {}
/** The goal function @param pos position to test
* @param costSoFar the cost of the shortest path to pos
* @return true if pos is target, else false */
bool operator()(const Vec2i &pos, const float costSoFar) const {
return pos == target;
}
};
//TODO: put these somewhere sensible
class TransitionHeuristic {
DiagonalDistance dd;
public:
TransitionHeuristic(const Vec2i &target) : dd(target) {}
bool operator()(const Transition *t) const {
return (dd(t->nwPos) != 0);
}
};
#if 0 == 1
//
// just use DiagonalDistance to waypoint ??
class AbstractAssistedHeuristic {
public:
AbstractAssistedHeuristic(const Vec2i &target, const Vec2i &waypoint, float wpCost)
: target(target), waypoint(waypoint), wpCost(wpCost) {}
/** search target */
Vec2i target, waypoint;
float wpCost;
/** The heuristic function.
* @param pos the position to calculate the heuristic for
* @return an estimate of the cost to target
*/
float operator()(const Vec2i &pos) const {
float dx = (float)abs(pos.x - waypoint.x),
dy = (float)abs(pos.y - waypoint.y);
float diag = dx < dy ? dx : dy;
float straight = dx + dy - 2 * diag;
return 1.4 * diag + straight + wpCost;
}
};
#endif
}} // namespace
#endif

View File

@ -1,290 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
//
// search_engine.h
#ifndef _GLEST_GAME_SEARCH_ENGINE_
#define _GLEST_GAME_SEARCH_ENGINE_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "math_util.h"
#include "game_constants.h"
/** Home of the templated A* algorithm and some of the bits that plug into it.*/
namespace Glest { namespace Game {
using Shared::Graphics::Vec2i;
static const float SQRT2 = Shared::Graphics::sqrt2;
enum OrdinalDir {
odNorth, odNorthEast, odEast, odSouthEast, odSouth, odSouthWest, odWest, odNorthWest, odCount
};
const Vec2i OrdinalOffsets[odCount] = {
Vec2i( 0, -1), // n
Vec2i( 1, -1), // ne
Vec2i( 1, 0), // e
Vec2i( 1, 1), // se
Vec2i( 0, 1), // s
Vec2i(-1, 1), // sw
Vec2i(-1, 0), // w
Vec2i(-1, -1) // nw
};
/*
// NodeStorage template interface
//
class NodeStorage {
public:
void reset();
void setMaxNode( int limit );
bool isOpen( const Vec2i &pos );
bool isClosed( const Vec2i &pos );
bool setOpen( const Vec2i &pos, const Vec2i &prev, float h, float d );
void updateOpen( const Vec2i &pos, const Vec2i &prev, const float cost );
Vec2i getBestCandidate();
Vec2i getBestSeen();
float getHeuristicAt( const Vec2i &pos );
float getCostTo( const Vec2i &pos );
float getEstimateFor( const Vec2i &pos );
Vec2i getBestTo( const Vec2i &pos );
};
*/
enum SearchSpace { ssCellMap, ssTileMap };
/** Neighbour function for 'octile grid map' as search domain */
class GridNeighbours {
private:
int x, y;
int width, height;
const int cellWidth, cellHeight;
public:
GridNeighbours(int cellWidth, int cellHeight)
: x(0), y(0)
, width(cellWidth)
, height(cellHeight)
, cellWidth(cellWidth)
, cellHeight(cellHeight) {
assert(cellWidth % GameConstants::cellScale == 0);
assert(cellHeight % GameConstants::cellScale == 0);
}
void operator()(Vec2i &pos, vector<Vec2i> &neighbours) const {
for (int i = 0; i < odCount; ++i) {
Vec2i nPos = pos + OrdinalOffsets[i];
if (nPos.x >= x && nPos.x < x + width && nPos.y >= y && nPos.y < y + height) {
neighbours.push_back(nPos);
}
}
}
/** Kludge to search on Cellmap or Tilemap... templated search domain should deprecate this */
void setSearchSpace(SearchSpace s) {
if (s == ssCellMap) {
x = y = 0;
width = cellWidth;
height = cellHeight;
} else if (s == ssTileMap) {
x = y = 0;
width = cellWidth / GameConstants::cellScale;
height= cellHeight / GameConstants::cellScale;
}
}
/** more kludgy search restriction stuff... */
void setSearchCluster(Vec2i cluster) {
x = cluster.x * GameConstants::clusterSize - 1;
if (x < 0) x = 0;
y = cluster.y * GameConstants::clusterSize - 1;
if (y < 0) y = 0;
width = GameConstants::clusterSize + 1;
height = GameConstants::clusterSize + 1;
}
};
enum AStarResult {
asrFailed, asrComplete, asrNodeLimit, asrTimeLimit
};
// ========================================================
// class SearchEngine
// ========================================================
/** Generic (template) A*
* @param NodeStorage NodeStorage class to use, must conform to implicit interface, see elsewhere
* @param NeighbourFunc the function to generate neighbours for a search domain
* @param DomainKey the key type of the search domain
*/
template< typename NodeStorage, typename NeighbourFunc = GridNeighbours, typename DomainKey = Vec2i >
class SearchEngine {
private:
NodeStorage *nodeStorage;/**< NodeStorage for this SearchEngine */
DomainKey goalPos; /**< The goal pos (the 'result') from the last A* search */
DomainKey invalidKey; /**< The DomainKey value indicating an invalid 'position' */
int expandLimit, /**< limit on number of nodes to expand */
nodeLimit, /**< limit on number of nodes to use */
expanded; /**< number of nodes expanded this/last run */
bool ownStore; /**< wether or not this SearchEngine 'owns' its storage */
NeighbourFunc neighbourFunc;
public:
/** construct & initialise NodeStorage
* @param store NodeStorage to use
* @param own wether the SearchEngine should own the storage
*/
SearchEngine(NeighbourFunc neighbourFunc, NodeStorage *store = 0, bool own=false)
: nodeStorage(store)
, expandLimit(-1)
, nodeLimit(-1)
, expanded(0)
, ownStore(own)
, neighbourFunc(neighbourFunc) {
}
/** Detruct, will delete storage if ownStore was set true */
~SearchEngine() {
if (ownStore) {
delete nodeStorage;
}
}
/** Attach NodeStorage
* @param store NodeStorage to use
* @param own wether the SearchEngine should own the storage
*/
void setStorage(NodeStorage *store, bool own=false) {
nodeStorage = store;
ownStore = own;
}
/** Set the DomainKey value indicating an invalid position, this must be set before use!
* @param key the invalid DomainKey value
*/
void setInvalidKey(DomainKey key) { invalidKey = key; }
/** @return a pointer to this engine's node storage */
NodeStorage* getStorage() { return nodeStorage; }
NeighbourFunc& getNeighbourFunc() { return neighbourFunc; }
/** Reset the node storage */
void reset() { nodeStorage->reset(); nodeStorage->setMaxNodes(nodeLimit > 0 ? nodeLimit : -1); }
/** Add a position to the open set with 'd' cost to here and invalid prev (a start position)
* @param pos position to set as open
* @param h heuristc, estimate to goal from pos
* @param d distance or cost to node (optional, defaults to 0)
*/
void setOpen(DomainKey pos, float h, float d = 0.f) {
nodeStorage->setOpen(pos, invalidKey, h, d);
}
/** Reset the node storage and add pos to open
* @param pos position to set as open
* @param h heuristc, estimate to goal from pos
* @param d distance or cost to node (optional, defaults to 0)
*/
void setStart(DomainKey pos, float h, float d = 0.f) {
nodeStorage->reset();
if ( nodeLimit > 0 ) {
nodeStorage->setMaxNodes(nodeLimit);
}
nodeStorage->setOpen(pos, invalidKey, h, d);
}
/** Retrieve the goal of last search, position of last goal reached */
DomainKey getGoalPos() { return goalPos; }
/** Best path to pos is from */
DomainKey getPreviousPos(const DomainKey &pos) { return nodeStorage->getBestTo(pos); }
/** limit search to use at most limit nodes */
void setNodeLimit(int limit) { nodeLimit = limit > 0 ? limit : -1; }
/** set an 'expanded nodes' limit, for a resumable search */
void setTimeLimit(int limit) { expandLimit = limit > 0 ? limit : -1; }
/** How many nodes were expanded last search */
int getExpandedLastRun() { return expanded; }
/** Retrieves cost to the node at pos (known to be visited) */
float getCostTo(const DomainKey &pos) {
assert(nodeStorage->isOpen(pos) || nodeStorage->isClosed(pos));
return nodeStorage->getCostTo(pos);
}
/** A* Algorithm (Just the loop, does not do any setup or post-processing)
* @param GoalFunc <b>template parameter</b> Goal function class
* @param CostFunc <b>template parameter</b> Cost function class
* @param Heuristic <b>template parameter</b> Heuristic function class
* @param goalFunc goal function object
* @param costFunc cost function object
* @param heuristic heuristic function object
*/
template< typename GoalFunc, typename CostFunc, typename Heuristic >
AStarResult aStar(GoalFunc &goalFunc, CostFunc &costFunc, Heuristic &heuristic) {
expanded = 0;
DomainKey minPos(invalidKey);
vector<DomainKey> neighbours;
while (true) {
// get best open
minPos = nodeStorage->getBestCandidate();
if (minPos == invalidKey) { // failure
goalPos = invalidKey;
return asrFailed;
}
if (goalFunc(minPos, nodeStorage->getCostTo(minPos))) { // success
goalPos = minPos;
return asrComplete;
}
// expand it...
neighbourFunc(minPos, neighbours);
while (!neighbours.empty()) {
DomainKey nPos = neighbours.back();
neighbours.pop_back();
if (nodeStorage->isClosed(nPos)) {
continue;
}
float cost = costFunc(minPos, nPos);
if (cost == -1.f) {
continue;
}
if (nodeStorage->isOpen(nPos)) {
nodeStorage->updateOpen(nPos, minPos, cost);
} else {
const float &costToMin = nodeStorage->getCostTo(minPos);
if (!nodeStorage->setOpen(nPos, minPos, heuristic(nPos), costToMin + cost)) {
goalPos = nodeStorage->getBestSeen();
return asrNodeLimit;
}
}
}
expanded++;
if (expanded == expandLimit) { // run limit
goalPos = invalidKey;
return asrTimeLimit;
}
}
return asrFailed; // impossible... just keeping the compiler from complaining
}
};
}}
#endif

View File

@ -1,57 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009-2010 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 "pos_iterator.h"
#include "leak_dumper.h"
namespace Glest { namespace Util {
Vec2i PerimeterIterator::next() {
Vec2i n(cx, cy);
switch (state) {
case 0: // top edge, left->right
if (cx == ex) {
state = 1;
++cy;
} else {
++cx;
}
break;
case 1: // right edge, top->bottom
if (cy == sy) {
state = 2;
--cx;
} else {
++cy;
}
break;
case 2:
if (cx == wx) {
state = 3;
--cy;
} else {
--cx;
}
break;
case 3:
if (cy == ny + 1) {
state = 4;
} else {
--cy;
}
break;
}
return n;
}
}}//end namespace

View File

@ -1,125 +0,0 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2009-2010 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
// ==============================================================
#ifndef _GLEST_GAME_UTIL_POSITERATOR_H_
#define _GLEST_GAME_UTIL_POSITERATOR_H_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include <cassert>
#include <stdexcept>
#include "math_util.h"
namespace Glest { namespace Util {
using std::runtime_error;
using Shared::Graphics::Vec2i;
using Shared::Graphics::Rect2i;
class RectIteratorBase {
protected:
int ex, wx, sy, ny;
public:
RectIteratorBase(const Vec2i &p1, const Vec2i &p2) {
if (p1.x > p2.x) {
ex = p1.x; wx = p2.x;
} else {
ex = p2.x; wx = p1.x;
}
if (p1.y > p2.y) {
sy = p1.y; ny = p2.y;
} else {
sy = p2.y; ny = p1.y;
}
}
};
/** An iterator over a rectangular region that starts at the 'top-left' and proceeds left
* to right, top to bottom. */
class RectIterator : public RectIteratorBase {
private:
int cx, cy;
public:
RectIterator(const Rect2i &rect)
: RectIteratorBase(rect.p[0], rect.p[1]) {
cx = wx;
cy = ny;
}
RectIterator(const Vec2i &p1, const Vec2i &p2)
: RectIteratorBase(p1, p2) {
cx = wx;
cy = ny;
}
bool more() const { return cy <= sy; }
Vec2i next() {
Vec2i n(cx, cy);
if (cx == ex) {
cx = wx; ++cy;
} else {
++cx;
}
return n;
}
};
/** An iterator over a rectangular region that starts at the 'bottom-right' and proceeds right
* to left, bottom to top. */
class ReverseRectIterator : public RectIteratorBase {
private:
int cx, cy;
public:
ReverseRectIterator(const Vec2i &p1, const Vec2i &p2)
: RectIteratorBase(p1, p2) {
cx = ex;
cy = sy;
}
bool more() const { return cy >= ny; }
Vec2i next() {
Vec2i n(cx,cy);
if (cx == wx) {
cx = ex; cy--;
} else {
cx--;
}
return n;
}
};
class PerimeterIterator : public RectIteratorBase {
private:
int cx, cy;
int state;
public:
PerimeterIterator(const Vec2i &p1, const Vec2i &p2)
: RectIteratorBase(p1, p2), state(0) {
cx = wx;
cy = ny;
}
bool more() const { return state != 4; }
Vec2i next();
};
}} // namespace Glest::Util
#endif // _GLEST_GAME_UTIL_POSITERATOR_H_

View File

@ -1241,8 +1241,6 @@ void Game::init(bool initForPreviewOnly) {
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"================ STARTING GAME ================\n");
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled) SystemFlags::OutputDebug(SystemFlags::debugPathFinder,"================ STARTING GAME ================\n");
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled) SystemFlags::OutputDebug(SystemFlags::debugPathFinder,"PathFinderType: %s\n", (getGameSettings()->getPathFinderType() ? "RoutePlanner" : "PathFinder"));
setupPopupMenus(false);
for(int i=0; i < world.getFactionCount(); ++i) {

View File

@ -73,8 +73,7 @@ const Vec4f WHITE(1.0f, 1.0f, 1.0f, 1.0f);
const Vec4f ORANGE(1.0f, 0.7f, 0.0f, 1.0f);
enum PathFinderType {
pfBasic,
pfRoutePlanner
pfBasic
};
enum TravelState {

View File

@ -1,577 +0,0 @@
// ==============================================================
// This file is part of The Glest Advanced Engine
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifdef DEBUG_RENDERING_ENABLED
#include "debug_renderer.h"
#include "route_planner.h"
#include "influence_map.h"
#include "cartographer.h"
#include "renderer.h"
using namespace Shared::Graphics;
using namespace Shared::Graphics::Gl;
using namespace Shared::Util;
namespace Glest { namespace Game {
// texture loading helper
void _load_debug_tex(Texture2D* &texPtr, const char *fileName) {
texPtr = g_renderer.newTexture2D(rsGame);
texPtr->setMipmap(false);
texPtr->getPixmap()->load(fileName);
}
// =====================================================
// class PathFinderTextureCallback
// =====================================================
PathFinderTextureCallback::PathFinderTextureCallback()
: debugField(fLand) {
reset();
}
void PathFinderTextureCallback::reset() {
pathSet.clear();
openSet.clear();
closedSet.clear();
pathStart = Vec2i(-1);
pathDest = Vec2i(-1);
localAnnotations.clear();
debugField = fLand;
memset(PFDebugTextures, 0, sizeof(PFDebugTextures));
}
void PathFinderTextureCallback::loadTextures() {
# define _load_tex(i,f) _load_debug_tex(PFDebugTextures[i],f)
char buff[128];
for (int i=0; i < 8; ++i) {
sprintf(buff, "data/core/misc_textures/g%02d.bmp", i);
_load_tex(i, buff);
}
_load_tex(9, "data/core/misc_textures/path_start.bmp");
_load_tex(10, "data/core/misc_textures/path_dest.bmp");
//_load_tex(11, "data/core/misc_textures/path_both.bmp");
//_load_tex(12, "data/core/misc_textures/path_return.bmp");
//_load_tex(13, "data/core/misc_textures/path.bmp");
_load_tex(14, "data/core/misc_textures/path_node.bmp");
_load_tex(15, "data/core/misc_textures/open_node.bmp");
_load_tex(16, "data/core/misc_textures/closed_node.bmp");
for (int i=17; i < 17+8; ++i) {
sprintf(buff, "data/core/misc_textures/l%02d.bmp", i-17);
_load_tex(i, buff);
}
# undef _load_tex
}
Texture2DGl* PathFinderTextureCallback::operator()(const Vec2i &cell) {
int ndx = -1;
if (pathStart == cell) ndx = 9;
else if (pathDest == cell) ndx = 10;
else if (pathSet.find(cell) != pathSet.end()) ndx = 14; // on path
else if (closedSet.find(cell) != closedSet.end()) ndx = 16; // closed nodes
else if (openSet.find(cell) != openSet.end()) ndx = 15; // open nodes
else if (localAnnotations.find(cell) != localAnnotations.end()) // local annotation
ndx = 17 + localAnnotations.find(cell)->second;
else ndx = g_world.getCartographer()->getMasterMap()->metrics[cell].get(debugField); // else use cell metric for debug field
return (Texture2DGl*)PFDebugTextures[ndx];
}
// =====================================================
// class GridTextureCallback
// =====================================================
void GridTextureCallback::loadTextures() {
_load_debug_tex(tex, "data/core/misc_textures/grid.bmp");
}
bool ResourceMapOverlay::operator()(const Vec2i &cell, Vec4f &colour) {
ResourceMapKey mapKey(rt, fLand, 1);
PatchMap<1> *pMap = g_world.getCartographer()->getResourceMap(mapKey);
if (pMap && pMap->getInfluence(cell)) {
colour = Vec4f(1.f, 1.f, 0.f, 0.7f);
return true;
}
return false;
}
bool StoreMapOverlay::operator()(const Vec2i &cell, Vec4f &colour) {
for (KeyList::iterator it = storeMaps.begin(); it != storeMaps.end(); ++it) {
PatchMap<1> *pMap = g_world.getCartographer()->getStoreMap(*it, false);
if (pMap && pMap->getInfluence(cell)) {
colour = Vec4f(0.f, 1.f, 0.3f, 0.7f);
return true;
}
}
return false;
}
// =====================================================
// class DebugRender
// =====================================================
DebugRenderer::DebugRenderer() {
// defaults, listed for ease of maintenance. [Note: these can be set from Lua now, use debugSet()]
showVisibleQuad =
captureVisibleQuad =
regionHilights =
storeMapOverlay =
showFrustum =
captureFrustum =
gridTextures =
influenceMap =
buildSiteMaps =
resourceMapOverlay =
false;
AAStarTextures =
HAAStarOverlay =
true;
}
const ResourceType* findResourceMapRes(const string &res) {
const int &n = g_world.getTechTree()->getResourceTypeCount();
for (int i=0; i < n; ++i) {
const ResourceType *rt = g_world.getTechTree()->getResourceType(i);
if (rt->getName() == res) {
return rt;
}
}
return 0;
}
void DebugRenderer::init() {
pfCallback.reset();
pfCallback.loadTextures();
gtCallback.reset();
gtCallback.loadTextures();
rhCallback.reset();
vqCallback.reset();
cmOverlay.reset();
rmOverlay.reset();
smOverlay.reset();
bsOverlay.reset();
if (resourceMapOverlay) {
rmOverlay.rt = findResourceMapRes(string("gold"));
} else {
rmOverlay.reset();
}
}
void DebugRenderer::commandLine(string &line) {
string key, val;
size_t n = line.find('=');
if ( n != string::npos ) {
key = line.substr(0, n);
val = line.substr(n+1);
} else {
key = line;
}
if ( key == "AStarTextures" ) {
if ( val == "" ) { // no val supplied, toggle
AAStarTextures = !AAStarTextures;
} else {
if ( val == "on" || val == "On" ) {
AAStarTextures = true;
} else {
AAStarTextures = false;
}
}
} else if ( key == "GridTextures" ) {
if ( val == "" ) { // no val supplied, toggle
gridTextures = !gridTextures;
} else {
if ( val == "on" || val == "On" ) {
gridTextures = true;
} else {
gridTextures = false;
}
}
} else if ( key == "ClusterOverlay" ) {
if ( val == "" ) { // no val supplied, toggle
HAAStarOverlay = !HAAStarOverlay;
} else {
if ( val == "on" || val == "On" ) {
HAAStarOverlay = true;
} else {
HAAStarOverlay = false;
}
}
} else if ( key == "CaptuereQuad" ) {
captureVisibleQuad = true;
} else if ( key == "RegionColouring" ) {
if ( val == "" ) { // no val supplied, toggle
regionHilights = !regionHilights;
} else {
if ( val == "on" || val == "On" ) {
regionHilights = true;
} else {
regionHilights = false;
}
}
} else if ( key == "DebugField" ) {
Field f = fLand;
if (val == "air") {
f = fAir;
}
pfCallback.debugField = f;
} else if (key == "Frustum") {
if (val == "capture" || val == "Capture") {
captureFrustum = true;
} else if (val == "off" || val == "Off") {
showFrustum = false;
}
} else if (key == "ResourceMap") {
if ( val == "" ) { // no val supplied, toggle
resourceMapOverlay = !resourceMapOverlay;
} else {
const ResourceType *rt = 0;
if ( val == "on" || val == "On" ) {
resourceMapOverlay = true;
} else if (val == "off" || val == "Off") {
resourceMapOverlay = false;
} else {
// else find resource
if (!( rt = findResourceMapRes(val))) {
g_console.addLine("Error: value='" + val + "' not valid.");
resourceMapOverlay = false;
}
resourceMapOverlay = true;
rmOverlay.rt = rt;
}
}
} else if (key == "StoreMap") {
n = val.find(',');
if (n == string::npos) {
g_console.addLine("Error: value='" + val + "' not valid");
return;
}
storeMapOverlay = false;
string idString = val.substr(0, n);
++n;
while (val[n] == ' ') ++n;
string szString = val.substr(n);
int id, sz;
try {
id = strToInt(idString);
sz = strToInt(szString);
} catch (runtime_error &e) {
g_console.addLine("Error: value='" + val + "' not valid: expected id, size (two integers)");
return;
}
Unit *store = g_world.findUnitById(id);
if (!store) {
g_console.addLine("Error: unit id " + idString + " not found");
return;
}
StoreMapKey smkey(store, fLand, sz);
PatchMap<1> *pMap = g_world.getCartographer()->getStoreMap(smkey, false);
if (pMap) {
smOverlay.storeMaps.push_back(smkey);
storeMapOverlay = true;
} else {
g_console.addLine("Error: no StoreMap found for unit " + idString
+ " in fLand with size " + szString);
}
} else if (key == "AssertClusterMap") {
g_world.getCartographer()->getClusterMap()->assertValid();
} else if (key == "TransitionEdges") {
if (val == "clear") {
clusterEdgesNorth.clear();
clusterEdgesWest.clear();
} else {
n = val.find(',');
if (n == string::npos) {
g_console.addLine("Error: value=" + val + "not valid");
return;
}
string xs = val.substr(0, n);
val = val.substr(n + 1);
int x = atoi(xs.c_str());
n = val.find(':');
if (n == string::npos) {
g_console.addLine("Error: value=" + val + "not valid");
return;
}
string ys = val.substr(0, n);
val = val.substr(n + 1);
int y = atoi(ys.c_str());
if (val == "north") {
clusterEdgesNorth.insert(Vec2i(x, y));
} else if ( val == "west") {
clusterEdgesWest.insert(Vec2i(x, y));
} else if ( val == "south") {
clusterEdgesNorth.insert(Vec2i(x, y + 1));
} else if ( val == "east") {
clusterEdgesWest.insert(Vec2i(x + 1, y));
} else if ( val == "all") {
clusterEdgesNorth.insert(Vec2i(x, y));
clusterEdgesNorth.insert(Vec2i(x, y + 1));
clusterEdgesWest.insert(Vec2i(x, y));
clusterEdgesWest.insert(Vec2i(x + 1, y));
} else {
g_console.addLine("Error: value=" + val + "not valid");
}
}
}
}
void DebugRenderer::renderCellTextured(const Texture2DGl *tex, const Vec3f &norm, const Vec3f &v0,
const Vec3f &v1, const Vec3f &v2, const Vec3f &v3) {
glBindTexture( GL_TEXTURE_2D, tex->getHandle() );
glBegin( GL_TRIANGLE_FAN );
glTexCoord2f( 0.f, 1.f );
glNormal3fv( norm.ptr() );
glVertex3fv( v0.ptr() );
glTexCoord2f( 1.f, 1.f );
glNormal3fv( norm.ptr() );
glVertex3fv( v1.ptr() );
glTexCoord2f( 1.f, 0.f );
glNormal3fv( norm.ptr() );
glVertex3fv( v2.ptr() );
glTexCoord2f( 0.f, 0.f );
glNormal3fv( norm.ptr() );
glVertex3fv( v3.ptr() );
glEnd ();
}
void DebugRenderer::renderCellOverlay(const Vec4f colour, const Vec3f &norm,
const Vec3f &v0, const Vec3f &v1, const Vec3f &v2, const Vec3f &v3) {
glBegin ( GL_TRIANGLE_FAN );
glNormal3fv(norm.ptr());
glColor4fv( colour.ptr() );
glVertex3fv(v0.ptr());
glNormal3fv(norm.ptr());
glColor4fv( colour.ptr() );
glVertex3fv(v1.ptr());
glNormal3fv(norm.ptr());
glColor4fv( colour.ptr() );
glVertex3fv(v2.ptr());
glNormal3fv(norm.ptr());
glColor4fv( colour.ptr() );
glVertex3fv(v3.ptr());
glEnd ();
}
void DebugRenderer::renderArrow(
const Vec3f &pos1, const Vec3f &_pos2, const Vec3f &color, float width) {
const int tesselation = 3;
const float arrowEndSize = 0.5f;
Vec3f dir = Vec3f(_pos2 - pos1);
//float len = dir.length();
float alphaFactor = 0.3f;
dir.normalize();
Vec3f pos2 = _pos2 - dir;
Vec3f normal = dir.cross(Vec3f(0, 1, 0));
Vec3f pos2Left = pos2 + normal * (width - 0.05f) - dir * arrowEndSize * width;
Vec3f pos2Right = pos2 - normal * (width - 0.05f) - dir * arrowEndSize * width;
Vec3f pos1Left = pos1 + normal * (width + 0.02f);
Vec3f pos1Right = pos1 - normal * (width + 0.02f);
//arrow body
glBegin(GL_TRIANGLE_STRIP);
for(int i=0; i<=tesselation; ++i){
float t= static_cast<float>(i)/tesselation;
Vec3f a= pos1Left.lerp(t, pos2Left);
Vec3f b= pos1Right.lerp(t, pos2Right);
Vec4f c= Vec4f(color, t*0.25f*alphaFactor);
glColor4fv(c.ptr());
glVertex3fv(a.ptr());
glVertex3fv(b.ptr());
}
glEnd();
//arrow end
glBegin(GL_TRIANGLES);
glVertex3fv((pos2Left + normal*(arrowEndSize-0.1f)).ptr());
glVertex3fv((pos2Right - normal*(arrowEndSize-0.1f)).ptr());
glVertex3fv((pos2 + dir*(arrowEndSize-0.1f)).ptr());
glEnd();
}
void DebugRenderer::renderPathOverlay() {
//return;
Vec3f one, two;
if ( waypoints.size() < 2 ) return;
assertGl();
glPushAttrib( GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
glEnable( GL_COLOR_MATERIAL );
glDisable( GL_ALPHA_TEST );
glDepthFunc(GL_ALWAYS);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glLineWidth(2.f);
glActiveTexture( GL_TEXTURE0 );
glDisable( GL_TEXTURE_2D );
list<Vec3f>::iterator it = waypoints.begin();
one = *it;
++it;
two = *it;
while ( true ) {
renderArrow(one,two,Vec3f(1.0f, 1.0f, 0.f), 0.15f);
one = two;
++it;
if ( it == waypoints.end() ) break;
two = *it;
}
//Restore
glPopAttrib();
}
void DebugRenderer::renderIntraClusterEdges(const Vec2i &cluster, CardinalDir dir) {
ClusterMap *cm = g_world.getCartographer()->getClusterMap();
const Map *map = g_world.getMap();
if (cluster.x < 0 || cluster.x >= cm->getWidth()
|| cluster.y < 0 || cluster.y >= cm->getHeight()) {
return;
}
Transitions transitions;
if (dir != CardinalDir::COUNT) {
TransitionCollection &tc = cm->getBorder(cluster, dir)->transitions[fLand];
for (int i=0; i < tc.n; ++i) {
transitions.push_back(tc.transitions[i]);
}
} else {
cm->getTransitions(cluster, fLand, transitions);
}
assertGl();
glPushAttrib( GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT );
glEnable( GL_COLOR_MATERIAL );
glDisable( GL_ALPHA_TEST );
glDepthFunc(GL_ALWAYS);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glLineWidth(2.f);
glActiveTexture( GL_TEXTURE0 );
glDisable( GL_TEXTURE_2D );
for (Transitions::iterator ti = transitions.begin(); ti != transitions.end(); ++ti) {
const Transition* &t = *ti;
float h = map->getCell(t->nwPos)->getHeight();
Vec3f t1Pos(t->nwPos.x + 0.5f, h + 0.1f, t->nwPos.y + 0.5f);
for (Edges::const_iterator ei = t->edges.begin(); ei != t->edges.end(); ++ei) {
Edge * const &e = *ei;
//if (e->cost(1) != numeric_limits<float>::infinity()) {
const Transition* t2 = e->transition();
h = map->getCell(t2->nwPos)->getHeight();
Vec3f t2Pos(t2->nwPos.x + 0.5f, h + 0.1f, t2->nwPos.y + 0.5f);
renderArrow(t1Pos, t2Pos, Vec3f(1.f, 0.f, 1.f), 0.2f);
//}
}
}
//Restore
glPopAttrib();
}
void DebugRenderer::renderFrustum() const {
glPushAttrib( GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT );
glEnable( GL_BLEND );
glEnable( GL_COLOR_MATERIAL );
glDisable( GL_ALPHA_TEST );
glActiveTexture( GL_TEXTURE0 );
glDisable( GL_TEXTURE_2D );
glPointSize(5);
glColor3f(1.f, 0.2f, 0.2f);
glBegin(GL_POINTS);
for (int i=0; i < 8; ++i) glVertex3fv(frstmPoints[i].ptr());
glEnd();
glLineWidth(2);
glColor3f(0.1f, 0.5f, 0.1f); // near
glBegin(GL_LINE_LOOP);
for (int i=0; i < 4; ++i) glVertex3fv(frstmPoints[i].ptr());
glEnd();
glColor3f(0.1f, 0.1f, 0.5f); // far
glBegin(GL_LINE_LOOP);
for (int i=4; i < 8; ++i) glVertex3fv(frstmPoints[i].ptr());
glEnd();
glColor3f(0.1f, 0.5f, 0.5f);
glBegin(GL_LINES);
for (int i=0; i < 4; ++i) {
glVertex3fv(frstmPoints[i].ptr()); // near
glVertex3fv(frstmPoints[i+4].ptr()); // far
}
glEnd();
glPopAttrib();
}
void DebugRenderer::setInfluenceMap(TypeMap<float> *iMap, Vec3f colour, float max) {
imOverlay.iMap = iMap;
imOverlay.baseColour = colour;
imOverlay.max = max;
influenceMap = true;
}
void DebugRenderer::renderEffects(Quad2i &quad) {
if (regionHilights && !rhCallback.empty()) {
renderCellOverlay(quad, rhCallback);
}
if (showVisibleQuad) {
renderCellOverlay(quad, vqCallback);
}
if (HAAStarOverlay) {
renderCellOverlay(quad, cmOverlay);
renderPathOverlay();
set<Vec2i>::iterator it;
for (it = clusterEdgesWest.begin(); it != clusterEdgesWest.end(); ++it) {
renderIntraClusterEdges(*it, CardinalDir::WEST);
}
for (it = clusterEdgesNorth.begin(); it != clusterEdgesNorth.end(); ++it) {
renderIntraClusterEdges(*it, CardinalDir::NORTH);
}
}
if (resourceMapOverlay && rmOverlay.rt) {
renderCellOverlay(quad, rmOverlay);
}
if (storeMapOverlay && !smOverlay.storeMaps.empty()) {
renderCellOverlay(quad, smOverlay);
}
if (buildSiteMaps && !bsOverlay.cells.empty()) {
renderCellOverlay(quad, bsOverlay);
}
//if (showFrustum) {
// renderFrustum();
//}
if (influenceMap) {
renderCellOverlay(quad, imOverlay);
}
}
DebugRenderer& getDebugRenderer() {
static DebugRenderer debugRenderer;
return debugRenderer;
}
}} // end namespace Glest::Debug
#endif // _GAE_DEBUG_EDITION_

View File

@ -1,408 +0,0 @@
// ==============================================================
// This file is part of The Glest Advanced Engine
//
// Copyright (C) 2009 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifndef DEBUG_RENDERING_ENABLED
# error debug_renderer.h included without DEBUG_RENDERING_ENABLED defined
#endif
#ifndef _GLEST_GAME_DEBUG_RENDERER_
#define _GLEST_GAME_DEBUG_RENDERER_
#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#endif
#include "vec.h"
#include "math_util.h"
#include "pixmap.h"
#include "texture.h"
#include "graphics_factory_gl.h"
#include "route_planner.h"
#include "influence_map.h"
#include "cartographer.h"
#include "cluster_map.h"
#include "game.h"
#define g_renderer (Renderer::getInstance())
#define g_world (*static_cast<Game*>(Program::getInstance()->getState())->getWorld())
#define g_console (*static_cast<Game*>(Program::getInstance()->getState())->getConsole())
using namespace Shared::Graphics;
using namespace Shared::Graphics::Gl;
using namespace Shared::Util;
namespace Glest { namespace Game {
class PathFinderTextureCallback {
public:
set<Vec2i> pathSet, openSet, closedSet;
Vec2i pathStart, pathDest;
map<Vec2i,uint32> localAnnotations;
Field debugField;
Texture2D *PFDebugTextures[26];
PathFinderTextureCallback();
void reset();
void loadTextures();
Texture2DGl* operator()(const Vec2i &cell);
};
class GridTextureCallback {
public:
Texture2D *tex;
void reset() { tex = 0; }
void loadTextures();
GridTextureCallback() : tex(0) {}
Texture2DGl* operator()(const Vec2i &cell) {
return (Texture2DGl*)tex;
}
};
enum HighlightColour { hcBlue, hcGreen, hcCount };
class CellHighlightOverlay {
public:
typedef map<Vec2i, HighlightColour> CellColours;
CellColours cells;
Vec4f highlightColours[hcCount];
CellHighlightOverlay() {
highlightColours[hcBlue] = Vec4f(0.f, 0.f, 1.f, 0.6f);
highlightColours[hcGreen] = Vec4f(0.f, 1.f, 0.f, 0.6f);
}
void reset() {
cells.clear();
}
bool empty() const { return cells.empty(); }
bool operator()(const Vec2i &cell, Vec4f &colour) {
CellColours::iterator it = cells.find(cell);
if (it != cells.end()) {
colour = highlightColours[it->second];
return true;
}
return false;
}
};
class InfluenceMapOverlay {
public:
TypeMap<float> *iMap;
Vec3f baseColour;
float max;
bool operator()(const Vec2i &cell, Vec4f &colour) {
const float influence = iMap->getInfluence(cell);
if (influence != 0.f) {
colour = Vec4f(baseColour, clamp(influence / max, 0.f, 1.f));
return true;
}
return false;
}
};
class VisibleAreaOverlay {
public:
set<Vec2i> quadSet;
Vec4f colour;
void reset() {
colour = Vec4f(0.f, 1.f, 0.f, 0.5f);
quadSet.clear();
}
bool operator()(const Vec2i &cell, Vec4f &colour) {
if (quadSet.find(cell) == quadSet.end()) {
return false;
}
colour = this->colour;
return true;
}
};
class TeamSightOverlay {
public:
bool operator()(const Vec2i &cell, Vec4f &colour);
};
class ClusterMapOverlay {
public:
set<Vec2i> entranceCells;
set<Vec2i> pathCells;
void reset() {
entranceCells.clear();
pathCells.clear();
}
bool operator()(const Vec2i &cell, Vec4f &colour) {
const int &clusterSize = GameConstants::clusterSize;
if ( cell.x % clusterSize == clusterSize - 1
|| cell.y % clusterSize == clusterSize - 1 ) {
if ( entranceCells.find(cell) != entranceCells.end() ) {
colour = Vec4f(0.f, 1.f, 0.f, 0.7f); // entrance
} else {
colour = Vec4f(1.f, 0.f, 0.f, 0.7f); // border
}
} else if ( pathCells.find(cell) != pathCells.end() ) { // intra-cluster edge
colour = Vec4f(0.f, 0.f, 1.f, 0.7f);
} else {
return false; // nothing interesting
}
return true;
}
};
class ResourceMapOverlay {
public:
const ResourceType *rt;
ResourceMapOverlay() : rt(0) {}
void reset() { rt = 0; }
bool operator()(const Vec2i &cell, Vec4f &colour);
};
class StoreMapOverlay {
public:
typedef vector<StoreMapKey> KeyList;
KeyList storeMaps;
void reset() { storeMaps.clear(); }
bool operator()(const Vec2i &cell, Vec4f &colour);
};
class BuildSiteMapOverlay {
public:
set<Vec2i> cells;
void reset() { cells.clear(); }
bool operator()(const Vec2i &cell, Vec4f &colour) {
if (cells.find(cell) != cells.end()) {
colour = Vec4f(0.f, 1.f, 0.3f, 0.7f);
return true;
}
return false;
}
};
// =====================================================
// class DebugRender
//
/// Helper class compiled with _GAE_DEBUG_EDITION_ only
// =====================================================
class DebugRenderer {
private:
set<Vec2i> clusterEdgesWest;
set<Vec2i> clusterEdgesNorth;
Vec3f frstmPoints[8];
PathFinderTextureCallback pfCallback;
GridTextureCallback gtCallback;
CellHighlightOverlay rhCallback;
VisibleAreaOverlay vqCallback;
ClusterMapOverlay cmOverlay;
ResourceMapOverlay rmOverlay;
StoreMapOverlay smOverlay;
BuildSiteMapOverlay bsOverlay;
InfluenceMapOverlay imOverlay;
public:
DebugRenderer();
void init();
void commandLine(string &line);
bool gridTextures, // show cell grid
AAStarTextures, // AA* search space and results of last low-level search
HAAStarOverlay, // HAA* search space and results of last hierarchical search
showVisibleQuad, // set to show visualisation of last captured scene cull
captureVisibleQuad, // set to trigger a capture of the next scene cull
captureFrustum, // set to trigger a capture of the view frustum
showFrustum, // set to show visualisation of captured view frustum
regionHilights, // show hilighted cells, are, and can further be, used for various things
resourceMapOverlay, // show resource goal map overlay
storeMapOverlay, // show store goal map overlay
buildSiteMaps, // show building site goal maps
influenceMap; // visualise an inluence map, [TypeMap<float> only]
void addCellHighlight(const Vec2i &pos, HighlightColour c = hcBlue) {
rhCallback.cells[pos] = c;
}
void clearCellHilights() {
rhCallback.cells.clear();
}
void addBuildSiteCell(const Vec2i &pos) {
bsOverlay.cells.insert(pos);
}
void setInfluenceMap(TypeMap<float> *iMap, Vec3f colour, float max);
PathFinderTextureCallback& getPFCallback() { return pfCallback; }
ClusterMapOverlay& getCMOverlay() { return cmOverlay; }
private:
/***/
template<typename TextureCallback>
void renderCellTextures(Quad2i &quad, TextureCallback callback) {
const Map *map = g_world.getMap();
const Rect2i mapBounds(0, 0, map->getSurfaceW() - 1, map->getSurfaceH() - 1);
float coordStep = g_world.getTileset()->getSurfaceAtlas()->getCoordStep();
assertGl();
glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT);
glEnable(GL_BLEND);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_ALPHA_TEST);
glActiveTexture( GL_TEXTURE0 );
PosQuadIterator pqi(map, quad);
while (pqi.next()) {
const Vec2i &pos = pqi.getPos();
int cx, cy;
cx = pos.x * 2;
cy = pos.y * 2;
if (mapBounds.isInside(pos)) {
SurfaceCell *tc00 = map->getSurfaceCell(pos.x, pos.y), *tc10 = map->getSurfaceCell(pos.x+1, pos.y),
*tc01 = map->getSurfaceCell(pos.x, pos.y+1), *tc11 = map->getSurfaceCell(pos.x+1, pos.y+1);
Vec3f tl = tc00->getVertex (), tr = tc10->getVertex (),
bl = tc01->getVertex (), br = tc11->getVertex ();
Vec3f tc = tl + (tr - tl) / 2, ml = tl + (bl - tl) / 2,
mr = tr + (br - tr) / 2, mc = ml + (mr - ml) / 2, bc = bl + (br - bl) / 2;
Vec2i cPos(cx, cy);
const Texture2DGl *tex = callback(cPos);
renderCellTextured(tex, tc00->getNormal(), tl, tc, mc, ml);
cPos = Vec2i(cx + 1, cy);
tex = callback(cPos);
renderCellTextured(tex, tc00->getNormal(), tc, tr, mr, mc);
cPos = Vec2i(cx, cy + 1 );
tex = callback(cPos);
renderCellTextured(tex, tc00->getNormal(), ml, mc, bc, bl);
cPos = Vec2i(cx + 1, cy + 1);
tex = callback(cPos);
renderCellTextured(tex, tc00->getNormal(), mc, mr, br, bc);
}
}
//Restore
glPopAttrib();
//assert
glGetError(); //remove when first mtex problem solved
assertGl();
} // renderCellTextures ()
/***/
template< typename ColourCallback >
void renderCellOverlay(Quad2i &quad, ColourCallback callback) {
const Map *map = g_world.getMap();
const Rect2i mapBounds(0, 0, map->getSurfaceW() - 1, map->getSurfaceH() - 1);
float coordStep = g_world.getTileset()->getSurfaceAtlas()->getCoordStep();
Vec4f colour;
assertGl();
glPushAttrib( GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT );
glEnable( GL_BLEND );
glEnable( GL_COLOR_MATERIAL );
glDisable( GL_ALPHA_TEST );
glActiveTexture( GL_TEXTURE0 );
glDisable( GL_TEXTURE_2D );
PosQuadIterator pqi(map, quad);
while(pqi.next()){
const Vec2i &pos= pqi.getPos();
int cx, cy;
cx = pos.x * 2;
cy = pos.y * 2;
if ( mapBounds.isInside( pos ) ) {
SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y), *tc10= map->getSurfaceCell(pos.x+1, pos.y),
*tc01= map->getSurfaceCell(pos.x, pos.y+1), *tc11= map->getSurfaceCell(pos.x+1, pos.y+1);
Vec3f tl = tc00->getVertex(), tr = tc10->getVertex(),
bl = tc01->getVertex(), br = tc11->getVertex();
tl.y += 0.1f; tr.y += 0.1f; bl.y += 0.1f; br.y += 0.1f;
Vec3f tc = tl + (tr - tl) / 2, ml = tl + (bl - tl) / 2, mr = tr + (br - tr) / 2,
mc = ml + (mr - ml) / 2, bc = bl + (br - bl) / 2;
if (callback(Vec2i(cx,cy), colour)) {
renderCellOverlay(colour, tc00->getNormal(), tl, tc, mc, ml);
}
if (callback(Vec2i(cx+1, cy), colour)) {
renderCellOverlay(colour, tc00->getNormal(), tc, tr, mr, mc);
}
if (callback(Vec2i(cx, cy + 1), colour)) {
renderCellOverlay(colour, tc00->getNormal(), ml, mc, bc, bl);
}
if (callback(Vec2i(cx + 1, cy + 1), colour)) {
renderCellOverlay(colour, tc00->getNormal(), mc, mr, br, bc);
}
}
}
//Restore
glPopAttrib();
assertGl();
}
/***/
void renderCellTextured(const Texture2DGl *tex, const Vec3f &norm, const Vec3f &v0,
const Vec3f &v1, const Vec3f &v2, const Vec3f &v3);
/***/
void renderCellOverlay(const Vec4f colour, const Vec3f &norm, const Vec3f &v0,
const Vec3f &v1, const Vec3f &v2, const Vec3f &v3);
/***/
void renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width);
/***/
void renderPathOverlay();
/***/
void renderIntraClusterEdges(const Vec2i &cluster, CardinalDir dir = CardinalDir::COUNT);
/***/
void renderFrustum() const;
list<Vec3f> waypoints;
public:
void clearWaypoints() { waypoints.clear(); }
void addWaypoint(Vec3f v) { waypoints.push_back(v); }
bool willRenderSurface() const { return AAStarTextures || gridTextures; }
void renderSurface(Quad2i &quad) {
if (AAStarTextures) {
if (gridTextures) gridTextures = false;
renderCellTextures(quad, pfCallback);
} else if (gridTextures) {
renderCellTextures(quad, gtCallback);
}
}
void renderEffects(Quad2i &quad);
};
DebugRenderer& getDebugRenderer();
}}
#endif

View File

@ -233,9 +233,6 @@ MenuStateConnectedGame::MenuStateConnectedGame(Program *program, MainMenu *mainM
listBoxPathFinderType.registerGraphicComponent(containerName,"listBoxPathFinderType");
listBoxPathFinderType.init(xoffset+650, aPos, 150);
listBoxPathFinderType.pushBackItem(lang.get("PathFinderTypeRegular"));
if(config.getBool("EnableRoutePlannerPathfinder","false") == true) {
listBoxPathFinderType.pushBackItem(lang.get("PathFinderTypeRoutePlanner"));
}
listBoxPathFinderType.setSelectedItemIndex(0);
listBoxPathFinderType.setEditable(false);
@ -598,9 +595,6 @@ void MenuStateConnectedGame::reloadUI() {
vector<string> pathfinderItems;
pathfinderItems.push_back(lang.get("PathFinderTypeRegular"));
if(config.getBool("EnableRoutePlannerPathfinder","false") == true) {
pathfinderItems.push_back(lang.get("PathFinderTypeRoutePlanner"));
}
listBoxPathFinderType.setItems(pathfinderItems);
labelMap.setText(lang.get("Map"));

View File

@ -345,9 +345,6 @@ MenuStateCustomGame::MenuStateCustomGame(Program *program, MainMenu *mainMenu,
listBoxPathFinderType.registerGraphicComponent(containerName,"listBoxPathFinderType");
listBoxPathFinderType.init(xoffset+650, aPos, 150);
listBoxPathFinderType.pushBackItem(lang.get("PathFinderTypeRegular"));
if(config.getBool("EnableRoutePlannerPathfinder","false") == true) {
listBoxPathFinderType.pushBackItem(lang.get("PathFinderTypeRoutePlanner"));
}
listBoxPathFinderType.setSelectedItemIndex(0);
// Advanced Options
@ -781,9 +778,6 @@ void MenuStateCustomGame::reloadUI() {
listBoxData.clear();
listBoxData.push_back(lang.get("PathFinderTypeRegular"));
if(config.getBool("EnableRoutePlannerPathfinder","false") == true) {
listBoxData.push_back(lang.get("PathFinderTypeRoutePlanner"));
}
listBoxPathFinderType.setItems(listBoxData);
// Advanced Options

View File

@ -3876,9 +3876,6 @@ Unit * Unit::loadGame(const XmlNode *rootNode, GameSettings *settings, Faction *
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}

View File

@ -245,7 +245,7 @@ public:
Vec2i peek() {return back();} /**< peek at the next position */
void pop() {this->pop_back();} /**< pop the next position off the path */
#else
// new style, for the new RoutePlanner
// new style
Vec2i peek() {return front();} /**< peek at the next position */
//virtual Vec2i pop() { Vec2i p= front(); erase(begin()); return p; } /**< pop the next position off the path */
void pop() { erase(begin()); } /**< pop the next position off the path */

View File

@ -23,7 +23,6 @@
#include "util.h"
#include "game_settings.h"
#include "platform_util.h"
#include "pos_iterator.h"
#include "faction.h"
#include "command.h"
#include "map_preview.h"
@ -661,24 +660,6 @@ bool Map::isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resour
return resourceNear;
}
//returns if there is a resource next to a unit, in "resourcePos" is stored the relative position of the resource
bool Map::isResourceNear(const Vec2i &pos, int size, const ResourceType *rt, Vec2i &resourcePos) const {
Vec2i p1 = pos + Vec2i(-size);
Vec2i p2 = pos + Vec2i(size);
Util::PerimeterIterator iter(p1, p2);
while (iter.more()) {
Vec2i cur = iter.next();
if (isInside(cur) && isInsideSurface(toSurfCoords(cur))) {
Resource *r = getSurfaceCell(toSurfCoords(cur))->getResource();
if (r && r->getType() == rt) {
resourcePos = cur;
return true;
}
}
}
return false;
}
// ==================== free cells ====================
bool Map::isFreeCell(const Vec2i &pos, Field field) const {

View File

@ -316,7 +316,6 @@ public:
return isInsideSurface(sPos.x, sPos.y);
}
bool isResourceNear(const Vec2i &pos, const ResourceType *rt, Vec2i &resourcePos, int size, Unit *unit=NULL,bool fallbackToPeersHarvestingSameResource=false,Vec2i *resourceClickPos=NULL) const;
bool isResourceNear(const Vec2i &pos, int size, const ResourceType *rt, Vec2i &resourcePos) const;
//free cells
bool isFreeCell(const Vec2i &pos, Field field) const;

View File

@ -14,7 +14,6 @@
#include <algorithm>
#include <cassert>
#include "cartographer.h"
#include "core_data.h"
#include "config.h"
#include "game.h"
@ -24,7 +23,6 @@
#include "particle_type.h"
#include "path_finder.h"
#include "renderer.h"
#include "route_planner.h"
#include "sound.h"
#include "sound_renderer.h"
#include "upgrade.h"
@ -51,7 +49,6 @@ UnitUpdater::UnitUpdater() {
this->map= NULL;
this->console= NULL;
this->scriptManager= NULL;
this->routePlanner = NULL;
this->pathFinder = NULL;
//UnitRangeCellsLookupItemCacheTimerCount = 0;
attackWarnRange=0;
@ -66,7 +63,6 @@ void UnitUpdater::init(Game *game){
this->map= world->getMap();
this->console= game->getConsole();
this->scriptManager= game->getScriptManager();
this->routePlanner = NULL;
this->pathFinder = NULL;
attackWarnRange=Config::getInstance().getFloat("AttackWarnRange","50.0");
//UnitRangeCellsLookupItemCacheTimerCount = 0;
@ -76,9 +72,6 @@ void UnitUpdater::init(Game *game){
pathFinder = new PathFinder();
pathFinder->init(map);
break;
case pfRoutePlanner:
routePlanner = world->getRoutePlanner();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -226,9 +219,6 @@ void UnitUpdater::updateUnit(Unit *unit) {
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -440,9 +430,6 @@ void UnitUpdater::updateMove(Unit *unit, int frameIndex) {
case pfBasic:
tsValue = pathFinder->findPath(unit, pos, NULL, frameIndex);
break;
case pfRoutePlanner:
tsValue = routePlanner->findPath(unit, pos);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -531,9 +518,6 @@ void UnitUpdater::updateAttack(Unit *unit, int frameIndex) {
case pfBasic:
tsValue = pathFinder->findPath(unit, pos, NULL, frameIndex);
break;
case pfRoutePlanner:
tsValue = routePlanner->findPath(unit, pos);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -740,9 +724,6 @@ void UnitUpdater::updateBuild(Unit *unit, int frameIndex) {
tsValue = pathFinder->findPath(unit, buildPos, NULL, frameIndex);
}
break;
case pfRoutePlanner:
tsValue = routePlanner->findPathToBuildSite(unit, ut, command->getPos(), command->getFacing());
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -773,9 +754,6 @@ void UnitUpdater::updateBuild(Unit *unit, int frameIndex) {
if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d] tsArrived about to call map->isFreeCells() for command->getPos() = %s, ut->getSize() = %d\n",__FILE__,__FUNCTION__,__LINE__,command->getPos().getString().c_str(),ut->getSize());
canOccupyCell = map->isFreeCells(command->getPos(), ut->getSize(), fLand);
break;
case pfRoutePlanner:
canOccupyCell = map->canOccupy(command->getPos(), ut->getField(), ut, command->getFacing());
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -791,9 +769,6 @@ void UnitUpdater::updateBuild(Unit *unit, int frameIndex) {
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -820,9 +795,6 @@ void UnitUpdater::updateBuild(Unit *unit, int frameIndex) {
switch(this->game->getGameSettings()->getPathFinderType()) {
case pfBasic:
break;
case pfRoutePlanner:
world->getCartographer()->updateMapMetrics(builtUnit->getPos(), builtUnit->getType()->getSight());
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -973,9 +945,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
}
}
break;
case pfRoutePlanner:
canHarvestDestPos = map->isResourceNear(unit->getPos(), unit->getType()->getSize(), r->getType(), targetPos);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1002,9 +971,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
case pfBasic:
unit->setLoadType(r->getType());
break;
case pfRoutePlanner:
unit->setLoadType(r->getType());
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1036,12 +1002,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
unit->setCurrSkill(hct->getMoveSkillType());
}
break;
case pfRoutePlanner:
tsValue = routePlanner->findPathToResource(unit, command->getPos(), r->getType());
if (tsValue == tsMoving && frameIndex < 0) {
unit->setCurrSkill(hct->getMoveSkillType());
}
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1062,9 +1022,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
}
}
break;
case pfRoutePlanner:
canHarvestDestPos = map->isResourceNear(unit->getPos(), unit->getType()->getSize(), r->getType(), targetPos);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1088,9 +1045,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
case pfBasic:
unit->setLoadType(r->getType());
break;
case pfRoutePlanner:
unit->setLoadType(r->getType());
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1125,13 +1079,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
command->setPos(targetPos);
}
break;
case pfRoutePlanner:
tsValue = routePlanner->findPathToResource(unit, targetPos, r->getType());
if (tsValue == tsMoving && frameIndex < 0) {
unit->setCurrSkill(hct->getMoveSkillType());
command->setPos(targetPos);
}
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1181,9 +1128,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
case pfBasic:
tsValue = pathFinder->findPath(unit, store->getCenteredPos(), NULL, frameIndex);
break;
case pfRoutePlanner:
tsValue = routePlanner->findPathToStore(unit, store);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1310,9 +1254,6 @@ void UnitUpdater::updateHarvest(Unit *unit, int frameIndex) {
switch(this->game->getGameSettings()->getPathFinderType()) {
case pfBasic:
break;
case pfRoutePlanner:
world->getCartographer()->onResourceDepleted(Map::toSurfCoords(unit->getTargetPos()), rt);
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1669,16 +1610,6 @@ void UnitUpdater::updateRepair(Unit *unit, int frameIndex) {
ts = pathFinder->findPath(unit, repairPos, NULL, frameIndex);
break;
case pfRoutePlanner:
if(SystemFlags::getSystemSettingType(SystemFlags::debugUnitCommands).enabled) SystemFlags::OutputDebug(SystemFlags::debugUnitCommands,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
if (repaired && !repaired->getType()->isMobile()) {
ts = routePlanner->findPathToBuildSite(unit, repaired->getType(), repaired->getPos(), repaired->getModelFacing());
}
else {
ts = routePlanner->findPath(unit, repairPos);
}
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1812,9 +1743,6 @@ void UnitUpdater::updateProduce(Unit *unit, int frameIndex) {
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1921,10 +1849,6 @@ void UnitUpdater::updateMorph(Unit *unit, int frameIndex) {
switch(this->game->getGameSettings()->getPathFinderType()) {
case pfBasic:
break;
case pfRoutePlanner:
oldSize = unit->getType()->getSize();
needMapUpdate = unit->getType()->isMobile() != mct->getMorphUnit()->isMobile();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1938,12 +1862,6 @@ void UnitUpdater::updateMorph(Unit *unit, int frameIndex) {
switch(this->game->getGameSettings()->getPathFinderType()) {
case pfBasic:
break;
case pfRoutePlanner:
if (needMapUpdate) {
int size = max(oldSize, unit->getType()->getSize());
world->getCartographer()->updateMapMetrics(unit->getPos(), size);
}
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1967,57 +1885,8 @@ void UnitUpdater::updateMorph(Unit *unit, int frameIndex) {
// ==================== updateMove ====================
void UnitUpdater::updateSwitchTeam(Unit *unit, int frameIndex) {
// Chrono chrono;
// if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
//
// Command *command= unit->getCurrCommand();
// const MoveCommandType *mct= static_cast<const MoveCommandType*>(command->getCommandType());
//
// Vec2i pos= command->getUnit()!=NULL? command->getUnit()->getCenteredPos(): command->getPos();
//
// if(SystemFlags::getSystemSettingType(SystemFlags::debugWorldSynch).enabled == true && frameIndex < 0) {
// char szBuf[4096]="";
// sprintf(szBuf,"[updateMove] pos [%s] unit [%d - %s] cmd [%s]",pos.getString().c_str(),unit->getId(),unit->getFullName().c_str(),command->toString().c_str());
// unit->logSynchData(__FILE__,__LINE__,szBuf);
// }
//
// 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());
//
// TravelState tsValue = tsImpossible;
// switch(this->game->getGameSettings()->getPathFinderType()) {
// case pfBasic:
// tsValue = pathFinder->findPath(unit, pos, NULL, frameIndex);
// break;
// case pfRoutePlanner:
// tsValue = routePlanner->findPath(unit, pos);
// break;
// default:
// throw megaglest_runtime_error("detected unsupported pathfinder type!");
// }
//
// 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());
//
// if(frameIndex < 0) {
// switch (tsValue) {
// case tsMoving:
// unit->setCurrSkill(mct->getMoveSkillType());
// break;
//
// case tsBlocked:
// unit->setCurrSkill(scStop);
// if(unit->getPath()->isBlocked()){
// unit->finishCommand();
// }
// break;
//
// default:
// unit->finishCommand();
// }
// }
// if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld --------------------------- [END OF METHOD] ---------------------------\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
}
// ==================== updateAttack ====================
// ==================== PRIVATE ====================
@ -2094,11 +1963,6 @@ void UnitUpdater::damage(Unit *attacker, const AttackSkillType* ast, Unit *attac
switch(this->game->getGameSettings()->getPathFinderType()) {
case pfBasic:
break;
case pfRoutePlanner:
if (!attacked->getType()->isMobile()) {
world->getCartographer()->updateMapMetrics(attacked->getPos(), attacked->getType()->getSize());
}
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -2627,7 +2491,6 @@ void UnitUpdater::saveGame(XmlNode *rootNode) {
// ScriptManager *scriptManager;
// PathFinder *pathFinder;
pathFinder->saveGame(unitupdaterNode);
// RoutePlanner *routePlanner;
// Game *game;
// RandomGen random;
//unitupdaterNode->addAttribute("random",intToStr(random.getLastNumber()), mapTagReplacements);

View File

@ -32,7 +32,6 @@ class Unit;
class Map;
class ScriptManager;
class PathFinder;
class RoutePlanner;
// =====================================================
// class UnitUpdater
@ -79,7 +78,6 @@ private:
Console *console;
ScriptManager *scriptManager;
PathFinder *pathFinder;
RoutePlanner *routePlanner;
Game *game;
//RandomGen random;
float attackWarnRange;

View File

@ -22,7 +22,6 @@
#include "sound_renderer.h"
#include "game_settings.h"
#include "cache_manager.h"
#include "route_planner.h"
#include <iostream>
#include "sound.h"
#include "sound_renderer.h"
@ -69,9 +68,6 @@ World::World() {
MaxExploredCellsLookupItemCache= config.getInt("MaxExploredCellsLookupItemCache",intToStr(MaxExploredCellsLookupItemCache).c_str());
//MaxExploredCellsLookupItemCache = 0;
routePlanner = 0;
cartographer = 0;
frameCount= 0;
//nextUnitId= 0;
@ -122,9 +118,6 @@ void World::cleanup() {
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
delete routePlanner;
routePlanner = 0;
for(std::map<string,StaticSound *>::iterator iterMap = staticSoundList.begin();
iterMap != staticSoundList.end(); ++iterMap) {
delete iterMap->second;
@ -139,8 +132,6 @@ void World::cleanup() {
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
delete cartographer;
cartographer = 0;
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
@ -244,12 +235,6 @@ void World::init(Game *game, bool createUnits, bool initFactions){
initMap();
initSplattedTextures();
// must be done after initMap()
if(gs->getPathFinderType() != pfBasic) {
routePlanner = new RoutePlanner(this);
cartographer = new Cartographer(this);
}
unitUpdater.init(game);
if(loadWorldNode != NULL) {
unitUpdater.loadGame(loadWorldNode);
@ -709,10 +694,6 @@ void World::tick() {
}
}
}
if(cartographer != NULL) {
cartographer->tick();
}
}
Unit* World::findUnitById(int id) const {
@ -941,9 +922,6 @@ void World::createUnit(const string &unitName, int factionIndex, const Vec2i &po
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -1605,9 +1583,6 @@ void World::initUnitsForScenario() {
if (unit->getType()->hasSkillClass(scBeBuilt)) {
map.flatternTerrain(unit);
if(cartographer != NULL) {
cartographer->updateMapMetrics(unit->getPos(), unit->getType()->getSize());
}
}
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] unit created for unit [%s]\n",__FILE__,__FUNCTION__,__LINE__,unit->toString().c_str());
}
@ -1640,9 +1615,6 @@ void World::placeUnitAtLocation(const Vec2i &location, int radius, Unit *unit, b
}
if (unit->getType()->hasSkillClass(scBeBuilt)) {
map.flatternTerrain(unit);
if(cartographer != NULL) {
cartographer->updateMapMetrics(unit->getPos(), unit->getType()->getSize());
}
}
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] unit created for unit [%s]\n",__FILE__,__FUNCTION__,__LINE__,unit->toString().c_str());
@ -1673,9 +1645,6 @@ void World::initUnits() {
case pfBasic:
newpath = new UnitPathBasic();
break;
case pfRoutePlanner:
newpath = new UnitPath();
break;
default:
throw megaglest_runtime_error("detected unsupported pathfinder type!");
}
@ -2325,8 +2294,6 @@ void World::saveGame(XmlNode *rootNode) {
// RandomGen random;
worldNode->addAttribute("random",intToStr(random.getLastNumber()), mapTagReplacements);
// ScriptManager* scriptManager;
// Cartographer *cartographer;
// RoutePlanner *routePlanner;
//
// int thisFactionIndex;
worldNode->addAttribute("thisFactionIndex",intToStr(thisFactionIndex), mapTagReplacements);

View File

@ -49,8 +49,6 @@ class Config;
class Game;
class GameSettings;
class ScriptManager;
class Cartographer;
class RoutePlanner;
class StaticSound;
class StrSound;
@ -123,8 +121,6 @@ private:
RandomGen random;
ScriptManager* scriptManager;
Cartographer *cartographer;
RoutePlanner *routePlanner;
int thisFactionIndex;
int thisTeamIndex;
@ -181,8 +177,6 @@ public:
inline const TimeFlow *getTimeFlow() const {return &timeFlow;}
inline Tileset *getTileset() {return &tileset;}
inline Map *getMap() {return &map;}
Cartographer* getCartographer() {return cartographer;}
RoutePlanner* getRoutePlanner() {return routePlanner;}
inline const Faction *getFaction(int i) const {return factions[i];}
inline Faction *getFaction(int i) {return factions[i];}
inline const Minimap *getMinimap() const {return &minimap;}

View File

@ -1,119 +0,0 @@
// ==============================================================
// This file is part of the Glest Advanced Engine
//
// Copyright (C) 2010 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifndef _MIN_HEAP_INCLUDED_
#define _MIN_HEAP_INCLUDED_
#include <cassert>
namespace Shared { namespace Util {
/** 'Nodes' nead to implement this implicit interface:
struct SomeNode {
void setHeapIndex(int ndx); // typically, { heap_ndx = ndx; }
int getHeapIndex() const; // typically, { return heap_ndx; }
bool operator<(const SomeNode &that) const;
};
*/
/** (Min) Heap, supporting node 'index awareness'.
* stores pointers to Nodes, user needs to supply the actual nodes, preferably in single block
* of memory, and preferably with as compact a node structure as is possible (to the point that the
* int 'heap_ndx' should be a bitfield using as few bits as you can get away with).
*/
template<typename Node> class MinHeap {
private:
Node** data;
int counter;
int capacity;
public:
/** Construct MinHeap with a given capacity */
MinHeap(int capacity = 1024) : counter(0), capacity(capacity) {
data = new Node*[capacity];
}
~MinHeap() {
delete [] data;
}
/** add a new node to the min heap */
bool insert(Node *node) {
if (counter == capacity) {
return false;
}
data[counter] = node;
data[counter]->setHeapIndex(counter);
promoteNode(counter++);
return true;
}
/** pop the best node off the min heap */
Node* extract() {
assert(counter);
Node *res = data[0];
if (--counter) {
data[0] = data[counter];
data[0]->setHeapIndex(0);
demoteNode();
}
return res;
}
/** indicate a node has had its key decreased */
void promote(Node *node) {
assert(data[node->getHeapIndex()] == node);
promoteNode(node->getHeapIndex());
}
int size() const { return counter; }
void clear() { counter = 0; }
bool empty() const { return !counter; }
private:
inline int parent(int ndx) const { return (ndx - 1) / 2; }
inline int left(int ndx) const { return (ndx * 2) + 1; }
void promoteNode(int ndx) {
assert(ndx >= 0 && ndx < counter);
while (ndx > 0 && *data[ndx] < *data[parent(ndx)]) {
Node *tmp = data[parent(ndx)];
data[parent(ndx)] = data[ndx];
data[ndx] = tmp;
data[ndx]->setHeapIndex(ndx);
ndx = parent(ndx);
data[ndx]->setHeapIndex(ndx);
}
}
void demoteNode(int ndx = 0) {
assert(counter);
while (true) {
int cndx = left(ndx); // child index
int sndx = ndx; // smallest (priority) of data[ndx] and any children
if (cndx < counter && *data[cndx] < *data[ndx]) sndx = cndx;
if (++cndx < counter && *data[cndx] < *data[sndx]) sndx = cndx;
if (sndx == ndx) return;
Node *tmp = data[sndx];
data[sndx] = data[ndx];
data[ndx] = tmp;
data[ndx]->setHeapIndex(ndx);
ndx = sndx;
data[ndx]->setHeapIndex(ndx);
}
}
};
}} // end namespace Shared::Util
#endif // _MIN_HEAP_INCLUDED_

View File

@ -1,83 +0,0 @@
// ==============================================================
// This file is part of the Glest Advanced Engine
//
// Copyright (C) 2010 James McCulloch <silnarm at gmail>
//
// 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
// ==============================================================
#ifndef _LINE_ALGORITHM_INCLUDED_
#define _LINE_ALGORITHM_INCLUDED_
#include <cassert>
namespace Shared { namespace Util {
/** midpoint line algorithm, 'Visit' specifies the 'pixel visit' function, *
* and must take two int params (x & y co-ords) */
template<typename VisitFunc> void line(int x0, int y0, int x1, int y1, VisitFunc visitor) {
bool mirror_x, mirror_y;
int pivot_x, pivot_y;
if (x0 > x1) {
mirror_x = true;
pivot_x = x0;
x1 = (x0 << 1) - x1;
} else {
mirror_x = false;
}
if (y0 > y1) {
mirror_y = true;
pivot_y = y0;
y1 = (y0 << 1) - y1;
} else {
mirror_y = false;
}
// Visit(x,y) => Visit(mirror_x ? (pivot_x << 1) - x : x, mirror_y ? (pivot_y << 1) - y : y);
assert(y0 <= y1 && x0 <= x1);
int dx = x1 - x0,
dy = y1 - y0;
int x = x0,
y = y0;
if (dx == 0) {
while (y <= y1) {
visitor(mirror_x ? (pivot_x << 1) - x : x, mirror_y ? (pivot_y << 1) - y : y);
++y;
}
} else if (dy == 0) {
while (x <= x1) {
visitor(mirror_x ? (pivot_x << 1) - x : x, mirror_y ? (pivot_y << 1) - y : y);
++x;
}
} else if (dy > dx) {
int d = 2 * dx - dy;
int incrS = 2 * dx;
int incrSE = 2 * (dx - dy);
do {
visitor(mirror_x ? (pivot_x << 1) - x : x, mirror_y ? (pivot_y << 1) - y : y);
if (d <= 0) {
d += incrS; ++y;
} else {
d += incrSE; ++x; ++y;
}
} while (y <= y1);
} else {
int d = 2 * dy - dx;
int incrE = 2 * dy;
int incrSE = 2 * (dy - dx);
do {
visitor(mirror_x ? (pivot_x << 1) - x : x, mirror_y ? (pivot_y << 1) - y : y);
if (d <= 0) {
d += incrE; ++x;
} else {
d += incrSE; ++x; ++y;
}
} while (x <= x1);
}
}
}} // end namespace Shared::Util
#endif // !def _LINE_ALGORITHM_INCLUDED_