From cc616616c18918e8ce6757abd62c90b19399c618 Mon Sep 17 00:00:00 2001 From: OnionKnight Date: Thu, 6 May 2010 12:13:31 +0000 Subject: [PATCH] * Creatures now guard surrounding tiles. --- CCallback.cpp | 5 +++ CCallback.h | 1 + client/CAdvmapInterface.cpp | 31 ++++++++++-------- client/CPlayerInterface.cpp | 24 +++++++++++++- lib/CGameState.cpp | 65 +++++++++++++++++++++++++++++++++++-- lib/CGameState.h | 1 + lib/NetPacks.h | 5 +-- lib/NetPacksLib.cpp | 3 +- server/CGameHandler.cpp | 20 +++++++++--- 9 files changed, 130 insertions(+), 25 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index b77559519..dc4761402 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -188,6 +188,11 @@ bool CCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown &dest ) return true; } +int3 CCallback::guardingCreaturePosition (int3 pos) const +{ + return gs->guardingCreaturePosition(pos); +} + int CCallback::howManyHeroes(bool includeGarrisoned) const { boost::shared_lock lock(*gs->mx); diff --git a/CCallback.h b/CCallback.h index 204bcf3ce..c15fff23b 100644 --- a/CCallback.h +++ b/CCallback.h @@ -278,6 +278,7 @@ public: void recalculatePaths(); //updates pathfinder info (should be called when moving hero is over) bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const; bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const; + int3 guardingCreaturePosition (int3 pos) const; //battle int battleGetBattlefieldType(); // 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index 30aa23897..3247fbf60 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -1854,6 +1854,8 @@ void CAdvMapInt::tileHovered(const int3 &tile) } } + const bool guardingCreature = CGI->mh->map->isInTheMap(LOCPLINT->cb->guardingCreaturePosition(tile)); + if(selection->ID == TOWNI_TYPE) { if(objAtTile && objAtTile->tempOwner == LOCPLINT->playerID) @@ -1917,13 +1919,6 @@ void CAdvMapInt::tileHovered(const int3 &tile) CGI->curh->changeGraphic(0, 3); } } - else if(objAtTile->ID == 54) //monster - { - if(accessible) - CGI->curh->changeGraphic(0, 5 + turns*6); - else - CGI->curh->changeGraphic(0, 0); - } else if(objAtTile->ID == 8) //boat { if(accessible) @@ -1946,6 +1941,10 @@ void CAdvMapInt::tileHovered(const int3 &tile) else CGI->curh->changeGraphic(0, 0); } + else if (guardingCreature && accessible) //(objAtTile->ID == 54) //monster + { + CGI->curh->changeGraphic(0, 5 + turns*6); + } else { if(accessible) @@ -1963,15 +1962,19 @@ void CAdvMapInt::tileHovered(const int3 &tile) { if(accessible) { - if(pnode->land) - { - if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water) - CGI->curh->changeGraphic(0, 4 + turns*6); + if (guardingCreature) { + CGI->curh->changeGraphic(0, 5 + turns*6); + } else { + if(pnode->land) + { + if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water) + CGI->curh->changeGraphic(0, 4 + turns*6); + else + CGI->curh->changeGraphic(0, 7 + turns*6); //anchor + } else - CGI->curh->changeGraphic(0, 7 + turns*6); //anchor + CGI->curh->changeGraphic(0, 6 + turns*6); } - else - CGI->curh->changeGraphic(0, 6 + turns*6); } else CGI->curh->changeGraphic(0, 0); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index c9ea3f984..f3e836ab5 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -237,6 +237,9 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) adventureInt->minimap.draw(screen2); adventureInt->heroList.draw(screen2); + bool directlyAttackingCreature = + CGI->mh->map->isInTheMap(details.attackedFrom) + && adventureInt->terrain.currentPath->nodes.size() == 3; if(makingTurn && ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path { @@ -248,7 +251,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) //TODO: smooth disappear / appear effect } - if (details.result != TryMoveHero::SUCCESS && details.result != TryMoveHero::FAILED) //hero didn't change tile but visit succeeded + if (details.result != TryMoveHero::SUCCESS && details.result != TryMoveHero::FAILED //hero didn't change tile but visit succeeded + || directlyAttackingCreature) // or creature was attacked from endangering tile. { eraseCurrentPathOf(ho); } @@ -317,6 +321,18 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) if(stillMoveHero.get() == WAITING_MOVE) stillMoveHero.setn(DURING_MOVE); + // Hero attacked creature directly, set direction to face it. + if (directlyAttackingCreature) { + // Get direction to attacker. + int3 posOffset = details.attackedFrom - details.end + int3(2, 1, 0); + const ui8 dirLookup[3][3] = { + 1, 2, 3, + 8, 0, 4, + 7, 6, 5 + }; + // FIXME: Avoid const_cast, make moveDir mutable in some other way? + const_cast(ho)->moveDir = dirLookup[posOffset.y][posOffset.x]; + } } void CPlayerInterface::heroKilled(const CGHeroInstance* hero) { @@ -976,6 +992,7 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) stillMoveHero.data = STOP_MOVE; break; } + // Start a new sound for the hero movement or let the existing one carry on. #if 0 // TODO @@ -995,12 +1012,17 @@ bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) stillMoveHero.data = WAITING_MOVE; int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z); + bool guarded = CGI->mh->map->isInTheMap(cb->guardingCreaturePosition(endpos - int3(1, 0, 0))); + cb->moveHero(h,endpos); eventsM.unlock(); while(stillMoveHero.data != STOP_MOVE && stillMoveHero.data != CONTINUE_MOVE) stillMoveHero.cond.wait(un); eventsM.lock(); + + if (guarded) // Abort movement if a guard was fought. + break; } CGI->soundh->stopSound(sh); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index e0f85a345..e52c9b1a8 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2124,6 +2124,12 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int } } } + else if (map->isInTheMap(guardingCreaturePosition(int3(i, j, k))) + && tinfo->blockingObjects.size() == 0) + { + // Monster close by; blocked visit for battle. + node.accessible = CGPathNode::BLOCKVIS; + } if(onLand && !node.land) //hero can walk only on land and tile lays on the water { @@ -2197,15 +2203,25 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int remains = moveAtNextTile - cost; } - if(dp.turns==0xff //we haven't been here before + const bool guardedPosition = guardingCreaturePosition(cp->coord) != int3(-1, -1, -1); + const bool neighborIsGuard = guardingCreaturePosition(cp->coord) == dp.coord; + + if((dp.turns==0xff //we haven't been here before || dp.turns > turnAtNextTile || (dp.turns >= turnAtNextTile && dp.moveRemains < remains)) //this route is faster + && (!guardedPosition || neighborIsGuard)) // Can step into tile of guard { + assert(&dp != cp->theNodeBefore); //two tiles can't point to each other dp.moveRemains = remains; dp.turns = turnAtNextTile; dp.theNodeBefore = cp; - if(dp.accessible == CGPathNode::ACCESSIBLE) + + const bool guardedNeighbor = guardingCreaturePosition(dp.coord) != int3(-1, -1, -1); + const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord; + + if (dp.accessible == CGPathNode::ACCESSIBLE + || (guardedNeighbor && !positionIsGuard)) // Can step into a hostile tile once. { mq.push(&dp); } @@ -2214,6 +2230,51 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int } //queue loop } +/** + * Tells if the tile is guarded by a monster as well as the position + * of the monster that will attack on it. + * + * @return int3(-1, -1, -1) if the tile is unguarded, or the position of + * the monster guarding the tile. + */ +int3 CGameState::guardingCreaturePosition (int3 pos) const +{ + // Give monster at position priority. + if (!map->isInTheMap(pos)) + return int3(-1, -1, -1); + const TerrainTile &posTile = map->terrain[pos.x][pos.y][pos.z]; + if (posTile.visitable) { + BOOST_FOREACH (CGObjectInstance* obj, posTile.visitableObjects) { + if (obj->ID == 54) { // Monster + return pos; + } + } + } + + // See if there are any monsters adjacent. + pos -= int3(1, 1, 0); // Start with top left. + for (int dx = 0; dx < 3; dx++) { + for (int dy = 0; dy < 3; dy++) { + if (map->isInTheMap(pos)) { + TerrainTile &tile = map->terrain[pos.x][pos.y][pos.z]; + if (tile.visitable) { + BOOST_FOREACH (CGObjectInstance* obj, tile.visitableObjects) { + if (obj->ID == 54) { // Monster + return pos; + } + } + } + } + + pos.y++; + } + pos.y -= 3; + pos.x++; + } + + return int3(-1, -1, -1); +} + bool CGameState::isVisible(int3 pos, int player) { if(player == 255) //neutral player diff --git a/lib/CGameState.h b/lib/CGameState.h index fa2e23755..58f380448 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -400,6 +400,7 @@ public: bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile bool getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret); //calculates path between src and dest; returns pointer to newly allocated CPath or NULL if path does not exists void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or NULL if path does not exists + int3 guardingCreaturePosition (int3 pos) const; int victoryCheck(ui8 player) const; //checks if given player is winner; -1 if std victory, 1 if special victory, 0 else int lossCheck(ui8 player) const; //checks if given player is loser; -1 if std loss, 1 if special, 0 else ui8 checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 if no winner diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 9a90b2858..b7a0dab8e 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -466,7 +466,7 @@ struct RemoveObject : public CPackForClient //500 }; struct TryMoveHero : public CPackForClient //501 { - TryMoveHero(){type = 501;humanKnows=false;}; + TryMoveHero(){type = 501;humanKnows=false; attackedFrom = int3(-1, -1, -1);}; void applyFirstCl(CClient *cl); void applyCl(CClient *cl); void applyGs(CGameState *gs); @@ -480,12 +480,13 @@ struct TryMoveHero : public CPackForClient //501 ui8 result; //uses EResult int3 start, end; //h3m format std::set fowRevealed; //revealed tiles + int3 attackedFrom; // Set when stepping into endangered tile. bool humanKnows; //used locally during applying to client template void serialize(Handler &h, const int version) { - h & id & result & start & end & movePoints & fowRevealed; + h & id & result & start & end & movePoints & fowRevealed & attackedFrom; } }; struct SetGarrisons : public CPackForClient //502 diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index c6e2e074a..4359fd3d1 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -342,8 +342,9 @@ void TryMoveHero::applyGs( CGameState *gs ) CGHeroInstance *h = gs->getHero(id); h->movement = movePoints; - if((result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK || result == DISEMBARK) && start != end) + if((result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK || result == DISEMBARK) && start != end) { h->moveDir = getDir(start,end); + } if(result == EMBARK) //hero enters boat at dest tile { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7e7bb19a0..4b1417db6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1562,7 +1562,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* if(!gs->map->isInTheMap(hmpos)) { - tlog1 << "Destination tile os out of the map!\n"; + tlog1 << "Destination tile is outside the map!\n"; return false; } @@ -1663,13 +1663,16 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* } else //normal move { - tmh.result = TryMoveHero::SUCCESS; - BOOST_FOREACH(CGObjectInstance *obj, gs->map->terrain[h->pos.x-1][h->pos.y][h->pos.z].visitableObjects) { obj->onHeroLeave(h); } getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1); + + int3 guardPos = gs->guardingCreaturePosition(int3(hmpos.x, hmpos.y, hmpos.z)); + + tmh.result = TryMoveHero::SUCCESS; + tmh.attackedFrom = guardPos; sendAndApply(&tmh); tlog5 << "Moved to " <map->isInTheMap(guardPos)) { + const TerrainTile &guardTile = gs->map->terrain[guardPos.x][guardPos.y][guardPos.z]; + objectVisited(guardTile.visitableObjects.back(), h); + } + + tlog5 << "Movement end!\n"; + return true; } - tlog5 << "Movement end!\n"; - return true; } else //instant move - teleportation {