From b2924064eef62a308225161dcf31f23247620f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Thu, 12 Feb 2009 14:44:58 +0000 Subject: [PATCH] * new movement cost calculation. * fixed crashes on loading maps with flag all mines/dwelling victory condition * minor changes --- AI/GeniusAI/CGeniusAI.cpp | 2 +- CGameState.cpp | 94 ++++++++++++++++++++++++++++++++++++++- CGameState.h | 4 +- CPathfinder.cpp | 6 +-- CPreGame.cpp | 8 +++- ChangeLog | 2 +- global.h | 2 +- hch/CObjectHandler.cpp | 92 ++++++++++++-------------------------- hch/CObjectHandler.h | 3 +- lib/Connection.h | 2 +- map.cpp | 16 ++++--- map.h | 1 + server/CGameHandler.cpp | 8 ++-- 13 files changed, 152 insertions(+), 88 deletions(-) diff --git a/AI/GeniusAI/CGeniusAI.cpp b/AI/GeniusAI/CGeniusAI.cpp index d6e3516e8..1e85100fb 100644 --- a/AI/GeniusAI/CGeniusAI.cpp +++ b/AI/GeniusAI/CGeniusAI.cpp @@ -1067,7 +1067,7 @@ void CBattleLogic::PrintBattleAction(const BattleAction &action) // for debug pu std::cout << color; #endif - std::cout << message.c_str() << std::flush; + std::cout << message.c_str() << std::endl; #ifdef _WIN32 SetConsoleTextAttribute(hConsole, csbi.wAttributes); diff --git a/CGameState.cpp b/CGameState.cpp index ba7116809..1028bb765 100644 --- a/CGameState.cpp +++ b/CGameState.cpp @@ -940,7 +940,7 @@ int CGameState::pickHero(int owner) } CGHeroInstance *CGameState::getHero(int objid) { - if(objid<0 || objid>=map->objects.size()) + if(objid<0 || objid>=map->objects.size() || map->objects[objid]->ID!=34) return NULL; return static_cast(map->objects[objid]); } @@ -1706,6 +1706,98 @@ void CGameState::setObjProperty( SetObjectProperty * p ) } } +void CGameState::getNeighbours(int3 tile, std::vector &vec, bool onLand) +{ + vec.clear(); + int3 hlp; + bool weAreOnLand = (map->getTile(tile).tertype != 8); + if(tile.x > 0) + { + hlp = int3(tile.x-1,tile.y,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.y > 0) + { + hlp = int3(tile.x,tile.y-1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.x > 0 && tile.y > 0) + { + hlp = int3(tile.x-1,tile.y-1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.x > 0 && tile.y < map->height-1) + { + hlp = int3(tile.x-1,tile.y+1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.y < map->height-1) + { + hlp = int3(tile.x,tile.y+1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.x < map->width-1) + { + hlp = int3(tile.x+1,tile.y,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.x < map->width-1 && tile.y > 0) + { + hlp = int3(tile.x+1,tile.y-1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } + if(tile.x < map->width-1 && tile.y < map->height-1) + { + hlp = int3(tile.x+1,tile.y+1,tile.z); + if((weAreOnLand == map->getTile(hlp).tertype!=8) && map->getTile(hlp).tertype!=9) + vec.push_back(hlp); + } +} + +int CGameState::getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints, bool checkLast) +{ + TerrainTile &s = map->terrain[src.x][src.y][src.z], + &d = map->terrain[dest.x][dest.y][dest.z]; + + //get basic cost + int ret = h->getTileCost(d,s); + + if(src.x!=dest.x && src.y!=dest.y) //diagonal move costs too much but normal move is possible + { + int old = ret; + ret *= 1.414; + if(ret > remainingMovePoints && remainingMovePoints > old) + { + return remainingMovePoints; + } + } + + + int left = remainingMovePoints-ret; + if(checkLast && left > 0 && remainingMovePoints-ret < 250) //it might be the last tile - if no further move possible we take all move points + { + std::vector vec; + getNeighbours(dest,vec,true); + for(size_t i=0; i < vec.size(); i++) + { + int fcost = getMovementCost(h,dest,vec[i],left,false); + if(fcost <= left) + { + return ret; + } + } + ret = remainingMovePoints; + } + return ret; +} + int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting) { int attackerAttackBonus = attacker->creature->attack + (attackerHero ? attackerHero->getPrimSkillLevel(0) : 0); diff --git a/CGameState.h b/CGameState.h index e8c9b14b1..a04b157eb 100644 --- a/CGameState.h +++ b/CGameState.h @@ -231,10 +231,8 @@ private: void randomizeObject(CGObjectInstance *cur); std::pair pickObject(CGObjectInstance *obj); int pickHero(int owner); - CGHeroInstance *getHero(int objid); CGTownInstance *getTown(int objid); - bool battleMoveCreatureStack(int ID, int dest); bool battleAttackCreatureStack(int ID, int dest); bool battleShootCreatureStack(int ID, int dest); @@ -246,6 +244,8 @@ private: public: CGameState(); ~CGameState(); + void getNeighbours(int3 tile, std::vector &vec, bool onLand); + int getMovementCost(const CGHeroInstance *h, int3 src, int3 dest, int remainingMovePoints=-1, bool checkLast=true); int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month template void serialize(Handler &h, const int version) { diff --git a/CPathfinder.cpp b/CPathfinder.cpp index 7339a08fc..43acf3397 100644 --- a/CPathfinder.cpp +++ b/CPathfinder.cpp @@ -178,11 +178,9 @@ void CPathfinder::convertPath(CPath * path, unsigned int mode) //mode=0 -> from } void CPathfinder::processNode(CPathNode & dp, const CGHeroInstance * hero, std::queue & mq, const CPathNode & cp, const int3 & src, bool diagonal) -{ +{ const TerrainTile * tinfo = CGI->mh->ttiles[dp.coord.x][dp.coord.y][src.z].tileInfo; - int cost = hero->getTileCost(tinfo->tertype, tinfo->malle, tinfo->nuine, hero->movement - cp.dist); - if(diagonal && (hero->movement - cp.dist) > 145) //second condition - workaround for strange behaviour manifested by Heroes III - cost *= std::sqrt(2.0); + int cost = CGI->state->getMovementCost(hero,cp.coord,dp.coord,hero->movement - cp.dist); if((dp.dist==-1 || (dp.dist > cp.dist + cost)) && dp.accesible && checkForVisitableDir(cp.coord, dp.coord) && checkForVisitableDir(dp.coord, cp.coord)) { dp.dist = cp.dist + cost; diff --git a/CPreGame.cpp b/CPreGame.cpp index 31265e058..c192f938b 100644 --- a/CPreGame.cpp +++ b/CPreGame.cpp @@ -1106,7 +1106,7 @@ void MapSel::processGames(const std::vector &pliczkiTemp) lf >> sign >> hlp; if(hlp != version) { - tlog3 << "\t" << pliczkiTemp[i] << " seems to be too " << ((hlp>version) ? "new" : "old") << " and will be ommited.\n"; + tlog3 << "\t\t" << pliczkiTemp[i] << " seems to be too " << ((hlp>version) ? "new" : "old") << " and will be ommited.\n"; ourGames[i] = NULL; continue; } @@ -1239,8 +1239,12 @@ void MapSel::init() processGames(pliczkiTemp); for (int i = 0; i < ourGames.size(); i++) { - ourGames[i]->date = datestemp[i]; + if(ourGames[i]) + ourGames[i]->date = datestemp[i]; } + maps = std::remove_if(ourGames.begin(),ourGames.end(),isNull); + ourGames.erase(maps,ourGames.end()); + std::sort(ourGames.begin(),ourGames.end(),mapSorter(_name)); } void MapSel::moveByOne(bool up) { diff --git a/ChangeLog b/ChangeLog index 5207bf7bc..22659a8bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,7 +19,7 @@ ADVENTURE INTERFACE: * campfire, borderguard, bordergate, questguard will be accessible from the top BATTLES: -* partial support for battle obstackles +* partial support for battle obstacles * spells not known by hero can't be casted * spell books won't be placed in War Machine slots after battle * attack is now possible when hex under cursor is not displayed diff --git a/global.h b/global.h index 50a0588e9..d7efdc3de 100644 --- a/global.h +++ b/global.h @@ -19,7 +19,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte) #define THC #endif -#define NAME_VER ("VCMI 0.7b") +#define NAME_VER ("VCMI 0.7c") #define CONSOLE_LOGGING_LEVEL 5 #define FILE_LOGGING_LEVEL 6 diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 2b1cf0df7..5fee14837 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -18,6 +18,7 @@ #include "../CGameState.h" #include "../lib/NetPacks.h" #include "../StartInfo.h" +#include "../Map.h" std::map > > CGTeleport::objs; IGameCallback * IObjectInterface::cb = NULL; @@ -196,72 +197,37 @@ int lowestSpeed(const CGHeroInstance * chi) return ret; } -unsigned int CGHeroInstance::getTileCost(const EterrainType & ttype, const Eroad & rdtype, const Eriver & rvtype, const int & remaingMP) const +unsigned int CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const { - if(remaingMP <= 100) return 100; //workaround for strange behaviour manifested by Heroes III - unsigned int ret = type->heroClass->terrCosts[ttype]; - //applying pathfinding skill - switch(getSecSkillLevel(0)) - { - case 1: //basic - switch(ttype) - { - case rough: - ret = 100; - break; - case sand: case snow: - if(ret>125) - ret = 125; - break; - case swamp: - if(ret>150) - ret = 150; - break; - default: - //TODO do something nasty here throw maybe? or some def value asing - break; - } - break; - case 2: //advanced - switch(ttype) - { - case rough: - case sand: - case snow: - ret = 100; - break; - case swamp: - if(ret>125) - ret = 125; - break; - default: - //TODO look up - break; - } - break; - case 3: //expert - ret = 100; - break; - default: - //TODO look up - break; - } + //TODO: check if all creatures are on its native terrain and change cost appropriately - //calculating road influence - switch(rdtype) + //base move cost + unsigned ret = 100; + + //if there is road both on dest and src tiles - use road movement cost + if(dest.malle && from.malle) { - case dirtRoad: - ret*=0.75; - break; - case grazvelRoad: - ret*=0.667; - break; - case cobblestoneRoad: - ret*=0.5; - break; - default: - //TODO killllll me - break; + int road = std::min(dest.malle,from.malle); //used road ID + switch(road) + { + case dirtRoad: + ret = 75; + break; + case grazvelRoad: + ret = 65; + break; + case cobblestoneRoad: + ret = 50; + break; + default: + tlog1 << "Unknown road type: " << road << "... Something wrong!\n"; + break; + } + } + else + { + ret = std::max(type->heroClass->terrCosts[dest.tertype],type->heroClass->terrCosts[from.tertype]); //take cost of worse of two tiles + ret = std::max(ret - 25*unsigned(getSecSkillLevel(0)), 100u); //reduce 25% of terrain penalty for each pathfinding level } return ret; } diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index 3f7b9bebc..4cfb4780e 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -30,6 +30,7 @@ class CGTownInstance; class CArtifact; class CGDefInfo; class CSpecObjInfo; +struct TerrainTile; class DLL_EXPORT CCastleEvent { @@ -207,7 +208,7 @@ public: const HeroBonus *getBonus(int from, int id) const; const std::string &getBiography() const; bool needsLastStack()const; - unsigned int getTileCost(const EterrainType & ttype, const Eroad & rdtype, const Eriver & rvtype, const int & remaingMP) const; + unsigned int getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling unsigned int getLowestCreatureSpeed() const; unsigned int getAdditiveMoveBonus() const; float getMultiplicativeMoveBonus() const; diff --git a/lib/Connection.h b/lib/Connection.h index cb45bdad7..114b403aa 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -18,7 +18,7 @@ #include #include -const ui32 version = 63; +const ui32 version = 703; class CConnection; namespace mpl = boost::mpl; diff --git a/map.cpp b/map.cpp index 682978813..ce3d08e45 100644 --- a/map.cpp +++ b/map.cpp @@ -359,7 +359,7 @@ void CMapHeader::loadViCLossConditions( unsigned char * bufor, int &i) { victoryCondition.ID = bufor[i+2]; victoryCondition.count = readNormalNr(bufor, i+3); - nr=5; + nr = 5; break; } case buildCity: @@ -369,7 +369,7 @@ void CMapHeader::loadViCLossConditions( unsigned char * bufor, int &i) victoryCondition.pos.z = bufor[i+4]; victoryCondition.count = bufor[i+5]; victoryCondition.ID = bufor[i+6]; - nr=5; + nr = 5; break; } case buildGrail: @@ -382,7 +382,7 @@ void CMapHeader::loadViCLossConditions( unsigned char * bufor, int &i) victoryCondition.pos.y = bufor[i+3]; victoryCondition.pos.z = bufor[i+4]; } - nr=3; + nr = 3; break; } case beatHero: @@ -392,13 +392,13 @@ void CMapHeader::loadViCLossConditions( unsigned char * bufor, int &i) victoryCondition.pos.x = bufor[i+2]; victoryCondition.pos.y = bufor[i+3]; victoryCondition.pos.z = bufor[i+4]; - nr=3; + nr = 3; break; } case takeDwellings: case takeMines: { - nr=3; + nr = 0; break; } case transportItem: @@ -407,7 +407,7 @@ void CMapHeader::loadViCLossConditions( unsigned char * bufor, int &i) victoryCondition.pos.x = bufor[i+3]; victoryCondition.pos.y = bufor[i+4]; victoryCondition.pos.z = bufor[i+5]; - nr=4; + nr = 4; break; } } @@ -1954,6 +1954,10 @@ void Mapa::loadQuest(CQuest * guard, unsigned char * bufor, int & i) guard->completedText = readString(bufor,i); } +TerrainTile & Mapa::getTile( int3 tile ) +{ + return terrain[tile.x][tile.y][tile.z]; +} void CMapInfo::countPlayers() { playerAmnt=humenPlayers=0; diff --git a/map.h b/map.h index ce19a8a3a..0febd0136 100644 --- a/map.h +++ b/map.h @@ -326,6 +326,7 @@ struct DLL_EXPORT Mapa : public CMapHeader Mapa(std::string filename); //creates map structure from .h3m file Mapa(); ~Mapa(); + TerrainTile &getTile(int3 tile); CGHeroInstance * getHero(int ID, int mode=0); bool isInTheMap(int3 pos); template void serializeObj(Handler &h, const int version, TObject ** obj) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 0f48d8ef7..53639867e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -495,9 +495,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) int3 hmpos = end + int3(-1,0,0); TerrainTile t = gs->map->terrain[hmpos.x][hmpos.y][hmpos.z]; CGHeroInstance *h = static_cast(gs->map->objects[id]); - double dist = distance(start,end); - if(h->movement <= 145) dist = 1.0f; //workaround for strange behaviour manifested by Heroes III - int cost = (int)((double)h->getTileCost(t.tertype,t.malle,t.nuine, h->movement) * dist); + int cost = gs->getMovementCost(h,start,end,h->movement); TryMoveHero tmh; tmh.id = id; @@ -508,7 +506,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) if((h->getOwner() != gs->currentPlayer) //not turn of that hero || (distance(start,end)>=1.5) //tiles are not neighouring - || (h->movement < cost) //lack of movement points + || (h->movement < cost && h->movement < 100) //lack of movement points || (t.tertype == rock) //rock || (!h->canWalkOnSea() && t.tertype == water) || (t.blocked && !t.visitable) //tile is blocked andnot visitable @@ -518,7 +516,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) //check if there is blocking visitable object blockvis = false; - tmh.movePoints = h->movement = (h->movement-cost); //take move points + tmh.movePoints = h->movement = std::max(si32(0),h->movement-cost); //take move points BOOST_FOREACH(CGObjectInstance *obj, t.visitableObjects) { if(obj->blockVisit)