2015-10-27 02:34:47 +02:00
# pragma once
# include "VCMI_Lib.h"
# include "mapping/CMap.h"
# include "IGameCallback.h"
# include "int3.h"
2015-11-07 20:11:07 +02:00
# include <boost/heap/priority_queue.hpp>
2015-10-27 02:34:47 +02:00
/*
* CPathfinder . h , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
class CGHeroInstance ;
class CGObjectInstance ;
struct TerrainTile ;
2015-11-10 13:26:45 +02:00
class CPathfinderHelper ;
2015-10-27 02:34:47 +02:00
struct DLL_LINKAGE CGPathNode
{
2015-11-08 07:27:51 +02:00
typedef EPathfindingLayer ELayer ;
2015-11-19 02:08:57 +02:00
enum ENodeAction : ui8
2015-11-07 23:26:41 +02:00
{
2015-11-19 02:08:57 +02:00
UNKNOWN = 0 ,
2015-11-07 23:26:41 +02:00
EMBARK = 1 ,
2015-11-19 02:08:57 +02:00
DISEMBARK ,
NORMAL ,
BATTLE ,
VISIT ,
BLOCKING_VISIT
2015-11-07 23:26:41 +02:00
} ;
2015-11-19 02:08:57 +02:00
enum EAccessibility : ui8
2015-10-27 02:34:47 +02:00
{
NOT_SET = 0 ,
ACCESSIBLE = 1 , //tile can be entered and passed
VISITABLE , //tile can be entered as the last tile in path
BLOCKVIS , //visitable from neighbouring tile but not passable
2015-11-17 02:59:02 +02:00
FLYABLE , //can only be accessed in air layer
2015-10-27 02:34:47 +02:00
BLOCKED //tile can't be entered nor visited
} ;
CGPathNode * theNodeBefore ;
int3 coord ; //coordinates
2015-11-19 02:08:57 +02:00
ui32 moveRemains ; //remaining tiles after hero reaches the tile
ui8 turns ; //how many turns we have to wait before reachng the tile - 0 means current turn
2015-11-08 07:27:51 +02:00
ELayer layer ;
2015-11-19 02:08:57 +02:00
EAccessibility accessible ;
2015-11-07 23:26:41 +02:00
ENodeAction action ;
2015-11-19 02:08:57 +02:00
bool locked ;
2015-10-27 02:34:47 +02:00
2015-11-11 23:05:20 +02:00
CGPathNode ( ) ;
2015-11-07 21:16:45 +02:00
void reset ( ) ;
2015-11-11 23:05:20 +02:00
void update ( const int3 & Coord , const ELayer Layer , const EAccessibility Accessible ) ;
2015-10-27 02:34:47 +02:00
bool reachable ( ) const ;
} ;
struct DLL_LINKAGE CGPath
{
std : : vector < CGPathNode > nodes ; //just get node by node
int3 startPos ( ) const ; // start point
int3 endPos ( ) const ; //destination point
void convert ( ui8 mode ) ; //mode=0 -> from 'manifest' to 'object'
} ;
struct DLL_LINKAGE CPathsInfo
{
2015-11-08 07:27:51 +02:00
typedef EPathfindingLayer ELayer ;
2015-10-27 02:34:47 +02:00
mutable boost : : mutex pathMx ;
2015-11-11 21:08:15 +02:00
const CGHeroInstance * hero ;
2015-10-27 02:34:47 +02:00
int3 hpos ;
int3 sizes ;
2015-11-11 23:05:20 +02:00
boost : : multi_array < CGPathNode , 4 > nodes ; //[w][h][level][layer]
2015-10-27 02:34:47 +02:00
2015-11-11 21:08:15 +02:00
CPathsInfo ( const int3 & Sizes ) ;
2015-10-27 02:34:47 +02:00
~ CPathsInfo ( ) ;
2015-11-11 23:05:20 +02:00
const CGPathNode * getPathInfo ( const int3 & tile ) const ;
bool getPath ( CGPath & out , const int3 & dst ) const ;
int getDistance ( const int3 & tile ) const ;
const CGPathNode * getNode ( const int3 & coord ) const ;
CGPathNode * getNode ( const int3 & coord , const ELayer layer ) ;
2015-10-27 02:34:47 +02:00
} ;
class CPathfinder : private CGameInfoCallback
{
public :
2015-11-16 17:43:02 +02:00
friend class CPathfinderHelper ;
2015-11-11 21:08:15 +02:00
CPathfinder ( CPathsInfo & _out , CGameState * _gs , const CGHeroInstance * _hero ) ;
2015-10-27 02:34:47 +02:00
void calculatePaths ( ) ; //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
private :
2015-11-08 07:27:51 +02:00
typedef EPathfindingLayer ELayer ;
2015-10-27 02:34:47 +02:00
struct PathfinderOptions
{
bool useFlying ;
bool useWaterWalking ;
bool useEmbarkAndDisembark ;
bool useTeleportTwoWay ; // Two-way monoliths and Subterranean Gate
bool useTeleportOneWay ; // One-way monoliths with one known exit only
bool useTeleportOneWayRandom ; // One-way monoliths with more than one known exit
bool useTeleportWhirlpool ; // Force enabled if hero protected or unaffected (have one stack of one creature)
2015-11-08 09:06:24 +02:00
/// TODO: Find out with client and server code, merge with normal teleporters.
/// Likely proper implementation would require some refactoring of CGTeleport.
/// So for now this is unfinished and disabled by default.
bool useCastleGate ;
2015-11-04 14:05:22 +02:00
/// If true transition into air layer only possible from initial node.
/// This is drastically decrease path calculation complexity (and time).
/// Downside is less MP effective paths calculation.
2015-11-17 03:33:21 +02:00
///
/// TODO: If this option end up useful for slow devices it's can be improved:
/// - Allow transition into air layer not only from initial position, but also from teleporters.
/// Movement into air can be also allowed when hero disembarked.
/// - Other idea is to allow transition into air within certain radius of N tiles around hero.
/// Patrol support need similar functionality so it's won't be ton of useless code.
/// Such limitation could be useful as it's can be scaled depend on device performance.
2015-11-04 14:05:22 +02:00
bool lightweightFlyingMode ;
2015-11-08 06:44:00 +02:00
/// This option enable one turn limitation for flying and water walking.
/// So if we're out of MP while cp is blocked or water tile we won't add dest tile to queue.
///
/// Following imitation is default H3 mechanics, but someone may want to disable it in mods.
/// After all this limit should benefit performance on maps with tons of water or blocked tiles.
bool oneTurnSpecialLayersLimit ;
2015-11-10 20:07:27 +02:00
/// VCMI have different movement rules to solve flaws original engine has.
/// If this option enabled you'll able to do following things in fly:
/// - Move from blocked tiles to visitable one
/// - Move from guarded tiles to blockvis tiles without being attacked
/// - Move from guarded tiles to guarded visitable tiles with being attacked after
2015-11-17 03:33:21 +02:00
/// TODO:
/// - Option should also allow same tile land <-> air layer transitions.
/// Current implementation only allow go into (from) air layer only to neighbour tiles.
/// I find it's reasonable limitation, but it's will make some movements more expensive than in H3.
2015-11-10 20:07:27 +02:00
bool originalMovementRules ;
2015-10-27 02:34:47 +02:00
PathfinderOptions ( ) ;
2015-10-27 02:50:38 +02:00
} options ;
2015-10-27 02:34:47 +02:00
2015-11-11 21:08:15 +02:00
CPathsInfo & out ;
const CGHeroInstance * hero ;
2015-11-10 18:15:59 +02:00
unique_ptr < CPathfinderHelper > hlp ;
2015-10-27 02:34:47 +02:00
2015-11-07 20:11:07 +02:00
struct NodeComparer
{
bool operator ( ) ( const CGPathNode * lhs , const CGPathNode * rhs ) const
{
if ( rhs - > turns > lhs - > turns )
return false ;
else if ( rhs - > turns = = lhs - > turns & & rhs - > moveRemains < lhs - > moveRemains )
return false ;
return true ;
}
} ;
boost : : heap : : priority_queue < CGPathNode * , boost : : heap : : compare < NodeComparer > > pq ;
2015-10-27 02:34:47 +02:00
std : : vector < int3 > neighbours ;
2015-11-11 21:08:15 +02:00
CGPathNode * cp ; //current (source) path node -> we took it from the queue
CGPathNode * dp ; //destination node -> it's a neighbour of cp that we consider
const TerrainTile * ct , * dt ; //tile info for both nodes
2015-11-16 16:41:23 +02:00
const CGObjectInstance * ctObj , * dtObj ;
2015-11-07 23:26:41 +02:00
CGPathNode : : ENodeAction destAction ;
2015-10-27 02:34:47 +02:00
2015-11-16 18:36:15 +02:00
void addNeighbours ( ) ;
2015-11-16 18:14:18 +02:00
void addTeleportExits ( ) ;
2015-10-27 02:34:47 +02:00
2015-11-08 07:39:00 +02:00
bool isLayerTransitionPossible ( ) const ;
2015-11-11 18:51:08 +02:00
bool isMovementToDestPossible ( ) const ;
2015-11-08 07:39:00 +02:00
bool isMovementAfterDestPossible ( ) const ;
2015-11-11 18:51:08 +02:00
CGPathNode : : ENodeAction getDestAction ( ) const ;
2015-10-27 02:34:47 +02:00
2015-11-08 07:39:00 +02:00
bool isSourceInitialPosition ( ) const ;
2015-11-16 16:36:58 +02:00
bool isSourceVisitableObj ( ) const ;
2015-11-08 07:39:00 +02:00
bool isSourceGuarded ( ) const ;
2015-11-17 06:09:01 +02:00
bool isDestVisitableObj ( ) const ;
2015-11-08 07:39:00 +02:00
bool isDestinationGuarded ( const bool ignoreAccessibility = true ) const ;
bool isDestinationGuardian ( ) const ;
2015-10-27 02:34:47 +02:00
void initializeGraph ( ) ;
2015-11-17 02:59:02 +02:00
CGPathNode : : EAccessibility evaluateAccessibility ( const int3 & pos , const TerrainTile * tinfo , const ELayer layer ) const ;
2015-11-17 06:09:01 +02:00
bool isVisitableObj ( const CGObjectInstance * obj , const ELayer layer ) const ;
bool canSeeObj ( const CGObjectInstance * obj ) const ;
2015-11-11 21:08:15 +02:00
bool canMoveBetween ( const int3 & a , const int3 & b ) const ; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
2015-10-27 02:34:47 +02:00
2015-11-16 18:14:18 +02:00
bool isAllowedTeleportEntrance ( const CGTeleport * obj ) const ;
2015-10-27 02:34:47 +02:00
bool addTeleportTwoWay ( const CGTeleport * obj ) const ;
bool addTeleportOneWay ( const CGTeleport * obj ) const ;
bool addTeleportOneWayRandom ( const CGTeleport * obj ) const ;
bool addTeleportWhirlpool ( const CGWhirlpool * obj ) const ;
2015-11-02 10:06:06 +02:00
2015-10-27 02:34:47 +02:00
} ;
2015-11-10 01:15:27 +02:00
2015-11-12 13:04:33 +02:00
struct DLL_LINKAGE TurnInfo
2015-11-10 13:26:45 +02:00
{
2015-11-21 09:00:09 +02:00
/// This is certainly not the best design ever and certainly can be improved
/// Unfortunately for pathfinder that do hundreds of thousands calls onus system add too big overhead
struct BonusCache {
std : : vector < bool > noTerrainPenalty ;
bool freeShipBoarding ;
bool flyingMovement ;
int flyingMovementVal ;
bool waterWalking ;
int waterWalkingVal ;
BonusCache ( TBonusListPtr bonusList ) ;
} ;
unique_ptr < BonusCache > bonusCache ;
2015-11-12 13:04:33 +02:00
const CGHeroInstance * hero ;
TBonusListPtr bonuses ;
mutable int maxMovePointsLand ;
mutable int maxMovePointsWater ;
2015-11-21 12:30:39 +02:00
int nativeTerrain ;
2015-11-12 04:20:32 +02:00
2015-11-12 13:04:33 +02:00
TurnInfo ( const CGHeroInstance * Hero , const int Turn = 0 ) ;
2015-11-12 13:39:22 +02:00
bool isLayerAvailable ( const EPathfindingLayer layer ) const ;
2015-11-12 13:04:33 +02:00
bool hasBonusOfType ( const Bonus : : BonusType type , const int subtype = - 1 ) const ;
int valOfBonuses ( const Bonus : : BonusType type , const int subtype = - 1 ) const ;
2015-11-12 04:20:32 +02:00
int getMaxMovePoints ( const EPathfindingLayer layer ) const ;
2015-11-10 13:26:45 +02:00
} ;
2015-11-10 01:15:27 +02:00
class DLL_LINKAGE CPathfinderHelper
{
public :
2015-11-16 17:43:02 +02:00
CPathfinderHelper ( const CGHeroInstance * Hero , const CPathfinder : : PathfinderOptions & Options ) ;
2015-11-11 21:08:15 +02:00
void updateTurnInfo ( const int turn = 0 ) ;
2015-11-12 13:39:22 +02:00
bool isLayerAvailable ( const EPathfindingLayer layer ) const ;
2015-11-12 13:04:33 +02:00
const TurnInfo * getTurnInfo ( ) const ;
bool hasBonusOfType ( const Bonus : : BonusType type , const int subtype = - 1 ) const ;
int getMaxMovePoints ( const EPathfindingLayer layer ) const ;
2015-11-10 13:26:45 +02:00
2015-11-16 20:22:11 +02:00
static void getNeighbours ( const CMap * map , const TerrainTile & srct , const int3 & tile , std : : vector < int3 > & vec , const boost : : logic : : tribool & onLand , const bool limitCoastSailing ) ;
2015-11-10 01:15:27 +02:00
2015-11-21 13:31:30 +02:00
static int getMovementCost ( const CGHeroInstance * h , const int3 & src , const int3 & dst , const TerrainTile * ct , const TerrainTile * dt , const int remainingMovePoints = - 1 , const TurnInfo * ti = nullptr , const bool checkLast = true ) ;
2015-11-11 21:08:15 +02:00
static int getMovementCost ( const CGHeroInstance * h , const int3 & dst ) ;
2015-11-10 13:26:45 +02:00
private :
2015-11-12 13:39:22 +02:00
int turn ;
const CGHeroInstance * hero ;
2015-11-10 13:26:45 +02:00
std : : vector < TurnInfo * > turnsInfo ;
2015-11-16 17:43:02 +02:00
const CPathfinder : : PathfinderOptions & options ;
2015-11-10 01:15:27 +02:00
} ;