// ============================================================== // 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 _GLEST_GAME_CLUSTER_MAP_H_ #define _GLEST_GAME_CLUSTER_MAP_H_ #include "util.h" #include "game_constants.h" #include "skill_type.h" #include "math_util.h" #include #include #include 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 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 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 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 &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 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 goals; public: TransitionGoal() {} set& 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 dirtyClusters; set dirtyNorthBorders; set 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 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 openList; set open; set closed; map 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