From af2c4633adc652e2aa7e31f7f74f38967db30fd2 Mon Sep 17 00:00:00 2001 From: mateuszb Date: Wed, 22 Dec 2010 20:14:40 +0000 Subject: [PATCH] * partially done duel mode * program options parsing via boost::program_options * Stupid AI - a stupid battle-only AI * precompiled headers for server and Stupid AI on MSVC --- CCallback.cpp | 84 ++-- CCallback.h | 126 +++--- CGameInterface.cpp | 23 +- CGameInterface.h | 58 +-- StartInfo.h | 2 +- StupidAI/StupidAI.cpp | 34 ++ StupidAI/StupidAI.h | 14 + StupidAI/StupidAI.vcxproj | 87 ++++ StupidAI/main.cpp | 30 ++ StupidAI/stdafx.cpp | 1 + StupidAI/stdafx.h | 2 + client/CMT.cpp | 86 ++-- client/Client.cpp | 48 ++- client/Client.h | 2 + global.h | 1 + lib/CGameState.cpp | 387 ++++++++++++++++++ lib/CGameState.h | 4 +- server/CGameHandler.cpp | 810 ++++++++++++-------------------------- server/CGameHandler.h | 5 +- server/CVCMIServer.cpp | 9 +- server/NetPacksServer.cpp | 1 + server/stdafx.cpp | 1 + server/stdafx.h | 24 ++ 23 files changed, 1088 insertions(+), 751 deletions(-) create mode 100644 StupidAI/StupidAI.cpp create mode 100644 StupidAI/StupidAI.h create mode 100644 StupidAI/StupidAI.vcxproj create mode 100644 StupidAI/main.cpp create mode 100644 StupidAI/stdafx.cpp create mode 100644 StupidAI/stdafx.h create mode 100644 server/stdafx.cpp create mode 100644 server/stdafx.h diff --git a/CCallback.cpp b/CCallback.cpp index b34c3cff0..0ecd26c33 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -473,19 +473,19 @@ bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID) return true; } -int CCallback::battleGetBattlefieldType() +int CBattleCallback::battleGetBattlefieldType() { boost::shared_lock lock(*gs->mx); return gs->battleGetBattlefieldType(); } -int CCallback::battleGetObstaclesAtTile(int tile) //returns bitfield +int CBattleCallback::battleGetObstaclesAtTile(int tile) //returns bitfield { //TODO - write return -1; } -std::vector CCallback::battleGetAllObstacles() +std::vector CBattleCallback::battleGetAllObstacles() { boost::shared_lock lock(*gs->mx); if(gs->curB) @@ -494,14 +494,14 @@ std::vector CCallback::battleGetAllObstacles() return std::vector(); } -const CStack* CCallback::battleGetStackByID(int ID, bool onlyAlive) +const CStack* CBattleCallback::battleGetStackByID(int ID, bool onlyAlive) { boost::shared_lock lock(*gs->mx); if(!gs->curB) return NULL; return gs->curB->getStack(ID, onlyAlive); } -int CCallback::battleMakeAction(BattleAction* action) +int CBattleCallback::battleMakeAction(BattleAction* action) { assert(action->actionType == BattleAction::HERO_SPELL); MakeCustomAction mca(*action); @@ -509,13 +509,13 @@ int CCallback::battleMakeAction(BattleAction* action) return 0; } -const CStack* CCallback::battleGetStackByPos(int pos, bool onlyAlive) +const CStack* CBattleCallback::battleGetStackByPos(int pos, bool onlyAlive) { boost::shared_lock lock(*gs->mx); return battleGetStackByID(gs->battleGetStack(pos, onlyAlive), onlyAlive); } -int CCallback::battleGetPos(int stack) +int CBattleCallback::battleGetPos(int stack) { boost::shared_lock lock(*gs->mx); if(!gs->curB) @@ -531,7 +531,7 @@ int CCallback::battleGetPos(int stack) return -1; } -std::vector CCallback::battleGetStacks() +std::vector CBattleCallback::battleGetStacks() { boost::shared_lock lock(*gs->mx); std::vector ret; @@ -547,7 +547,7 @@ std::vector CCallback::battleGetStacks() return ret; } -void CCallback::getStackQueue( std::vector &out, int howMany ) +void CBattleCallback::getStackQueue( std::vector &out, int howMany ) { if(!gs->curB) { @@ -557,7 +557,7 @@ void CCallback::getStackQueue( std::vector &out, int howMany ) gs->curB->getStackQueue(out, howMany); } -std::vector CCallback::battleGetAvailableHexes(int ID, bool addOccupiable) +std::vector CBattleCallback::battleGetAvailableHexes(int ID, bool addOccupiable) { boost::shared_lock lock(*gs->mx); if(!gs->curB) @@ -569,7 +569,7 @@ std::vector CCallback::battleGetAvailableHexes(int ID, bool addOccupiable) //return gs->battleGetRange(ID); } -bool CCallback::battleCanShoot(int ID, int dest) +bool CBattleCallback::battleCanShoot(int ID, int dest) { boost::shared_lock lock(*gs->mx); @@ -578,7 +578,7 @@ bool CCallback::battleCanShoot(int ID, int dest) return gs->battleCanShoot(ID, dest); } -bool CCallback::battleCanCastSpell() +bool CBattleCallback::battleCanCastSpell() { if(!gs->curB) //there is no battle return false; @@ -589,12 +589,12 @@ bool CCallback::battleCanCastSpell() return gs->curB->castSpells[1] == 0 && gs->curB->heroes[1] && gs->curB->heroes[1]->getArt(17); } -bool CCallback::battleCanFlee() +bool CBattleCallback::battleCanFlee() { return gs->battleCanFlee(player); } -const CGTownInstance *CCallback::battleGetDefendedTown() +const CGTownInstance *CBattleCallback::battleGetDefendedTown() { if(!gs->curB || gs->curB->tid == -1) return NULL; @@ -602,7 +602,7 @@ const CGTownInstance *CCallback::battleGetDefendedTown() return static_cast(gs->map->objects[gs->curB->tid].get()); } -ui8 CCallback::battleGetWallState(int partOfWall) +ui8 CBattleCallback::battleGetWallState(int partOfWall) { if(!gs->curB || gs->curB->siege == 0) { @@ -611,7 +611,7 @@ ui8 CCallback::battleGetWallState(int partOfWall) return gs->curB->si.wallState[partOfWall]; } -int CCallback::battleGetWallUnderHex(int hex) +int CBattleCallback::battleGetWallUnderHex(int hex) { if(!gs->curB || gs->curB->siege == 0) { @@ -620,7 +620,7 @@ int CCallback::battleGetWallUnderHex(int hex) return gs->curB->hexToWallPart(hex); } -std::pair CCallback::battleEstimateDamage(int attackerID, int defenderID) +std::pair CBattleCallback::battleEstimateDamage(int attackerID, int defenderID) { if(!gs->curB) return std::make_pair(0, 0); @@ -644,7 +644,7 @@ std::pair CCallback::battleEstimateDamage(int attackerID, int defend return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0, false); } -ui8 CCallback::battleGetSiegeLevel() +ui8 CBattleCallback::battleGetSiegeLevel() { if(!gs->curB) return 0; @@ -652,7 +652,7 @@ ui8 CCallback::battleGetSiegeLevel() return gs->curB->siege; } -const CGHeroInstance * CCallback::battleGetFightingHero(ui8 side) const +const CGHeroInstance * CBattleCallback::battleGetFightingHero(ui8 side) const { if(!gs->curB) return 0; @@ -660,6 +660,19 @@ const CGHeroInstance * CCallback::battleGetFightingHero(ui8 side) const return gs->curB->heroes[side]; } +template +void CBattleCallback::sendRequest(const T* request) +{ + //TODO? should be part of CClient but it would have to be very tricky cause template/serialization issues + if(waitTillRealize) + cl->waitingRequest.set(true); + + *cl->serv << request; + + if(waitTillRealize) + cl->waitingRequest.waitWhileTrue(); +} + void CCallback::swapGarrisonHero( const CGTownInstance *town ) { if(town->tempOwner != player) return; @@ -822,21 +835,8 @@ void CCallback::buildBoat( const IShipyard *obj ) sendRequest(&bb); } -template -void CCallback::sendRequest(const T* request) -{ - //TODO? should be part of CClient but it would have to be very tricky cause template/serialization issues - if(waitTillRealize) - cl->waitingRequest.set(true); - - *cl->serv << request; - - if(waitTillRealize) - cl->waitingRequest.waitWhileTrue(); -} - CCallback::CCallback( CGameState * GS, int Player, CClient *C ) - :gs(GS), cl(C), player(Player) + :CBattleCallback(GS, Player, C) { waitTillRealize = false; } @@ -909,17 +909,17 @@ bool CCallback::hasAccess(int playerId) const return gs->getPlayerRelations( playerId, player ) || player < 0; } -si8 CCallback::battleHasDistancePenalty( int stackID, int destHex ) +si8 CBattleCallback::battleHasDistancePenalty( int stackID, int destHex ) { return gs->curB->hasDistancePenalty(stackID, destHex); } -si8 CCallback::battleHasWallPenalty( int stackID, int destHex ) +si8 CBattleCallback::battleHasWallPenalty( int stackID, int destHex ) { return gs->curB->hasWallPenalty(stackID, destHex); } -si8 CCallback::battleCanTeleportTo(int stackID, int destHex, int telportLevel) +si8 CBattleCallback::battleCanTeleportTo(int stackID, int destHex, int telportLevel) { return gs->curB->canTeleportTo(stackID, destHex, telportLevel); } @@ -1019,4 +1019,16 @@ void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed) details->goldIncome = -1; details->hallLevel = -1; } +} + +bool CBattleCallback::hasAccess( int playerId ) const +{ + return playerId == player || player < 0; +} + +CBattleCallback::CBattleCallback(CGameState *GS, int Player, CClient *C ) +{ + gs = GS; + player = Player; + cl = C; } \ No newline at end of file diff --git a/CCallback.h b/CCallback.h index bfba0af28..e26c8d5c8 100644 --- a/CCallback.h +++ b/CCallback.h @@ -69,10 +69,37 @@ struct InfoAboutTown void initFromGarrison(const CGGarrison *garr, bool detailed); }; -class ICallback +class IBattleCallback { public: bool waitTillRealize; //if true, request functions will return after they are realized by server + //battle + virtual int battleGetBattlefieldType()=0; // 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 + virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield + virtual std::vector battleGetAllObstacles()=0; //returns all obstacles on the battlefield + virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID + virtual const CStack * battleGetStackByPos(int pos, bool onlyAlive = true)=0; //returns stack info by given pos + virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack + virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack + virtual std::vector battleGetStacks()=0; //returns stacks on battlefield + virtual void getStackQueue( std::vector &out, int howMany )=0; //returns vector of stack in order of their move sequence + virtual std::vector battleGetAvailableHexes(int ID, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID + virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest + virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell + virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle + virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead + virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle + virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found + virtual std::pair battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle + virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender) + virtual si8 battleHasDistancePenalty(int stackID, int destHex) =0; //checks if given stack has distance penalty + virtual si8 battleHasWallPenalty(int stackID, int destHex) =0; //checks if given stack has wall penalty +}; + +class ICallback : public virtual IBattleCallback +{ +public: //hero virtual bool moveHero(const CGHeroInstance *h, int3 dst) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile) virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly @@ -156,45 +183,59 @@ public: virtual int3 getMapSize() const =0; //returns size of map - z is 1 for one - level map and 2 for two level map virtual const TerrainTile * getTileInfo(int3 tile) const = 0; virtual int getPlayerRelations(ui8 color1, ui8 color2) const =0;// 0 = enemy, 1 = ally, 2 = same player - -//battle - virtual int battleGetBattlefieldType()=0; // 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 - virtual int battleGetObstaclesAtTile(int tile)=0; //returns bitfield - virtual std::vector battleGetAllObstacles()=0; //returns all obstacles on the battlefield - virtual const CStack * battleGetStackByID(int ID, bool onlyAlive = true)=0; //returns stack info by given ID - virtual const CStack * battleGetStackByPos(int pos, bool onlyAlive = true)=0; //returns stack info by given pos - virtual int battleGetPos(int stack)=0; //returns position (tile ID) of stack - virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack - virtual std::vector battleGetStacks()=0; //returns stacks on battlefield - virtual void getStackQueue( std::vector &out, int howMany )=0; //returns vector of stack in order of their move sequence - virtual std::vector battleGetAvailableHexes(int ID, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID - virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest - virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell - virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle - virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead - virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle - virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found - virtual std::pair battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle - virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender) - virtual si8 battleHasDistancePenalty(int stackID, int destHex) =0; //checks if given stack has distance penalty - virtual si8 battleHasWallPenalty(int stackID, int destHex) =0; //checks if given stack has wall penalty }; -class CCallback : public ICallback +class CBattleCallback : public virtual IBattleCallback { private: - CCallback(CGameState * GS, int Player, CClient *C);; CGameState * gs; - CClient *cl; - bool isVisible(int3 pos, int Player) const; - bool isVisible(const CGObjectInstance *obj, int Player) const; - template void sendRequest(const T*request); + CBattleCallback(CGameState *GS, int Player, CClient *C); + protected: - bool hasAccess(int playerId) const; + template void sendRequest(const T*request); + CClient *cl; + virtual bool hasAccess(int playerId) const; int player; +public: + //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 + int battleGetObstaclesAtTile(int tile); //returns bitfield + std::vector battleGetAllObstacles(); //returns all obstacles on the battlefield + const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID + const CStack * battleGetStackByPos(int pos, bool onlyAlive = true); //returns stack info by given pos + int battleGetPos(int stack); //returns position (tile ID) of stack + int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack + std::vector battleGetStacks(); //returns stacks on battlefield + void getStackQueue( std::vector &out, int howMany ); //returns vector of stack in order of their move sequence + std::vector battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID + bool battleCanShoot(int ID, int dest); //returns true if unit with id ID can shoot to dest + bool battleCanCastSpell(); //returns true, if caller can cast a spell + bool battleCanFlee(); //returns true if caller can flee from the battle + const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead + ui8 battleGetWallState(int partOfWall); //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle + int battleGetWallUnderHex(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found + std::pair battleEstimateDamage(int attackerID, int defenderID); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle + const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender) + si8 battleHasDistancePenalty(int stackID, int destHex); //checks if given stack has distance penalty + si8 battleHasWallPenalty(int stackID, int destHex); //checks if given stack has wall penalty + si8 battleCanTeleportTo(int stackID, int destHex, int telportLevel); //checks if teleportation of given stack to given position can take place + + friend CCallback; + friend CClient; +}; + +class CCallback : public ICallback, public CBattleCallback +{ +private: + CCallback(CGameState * GS, int Player, CClient *C); + bool isVisible(int3 pos, int Player) const; + bool isVisible(const CGObjectInstance *obj, int Player) const; +protected: + virtual bool hasAccess(int playerId) const OVERRIDE; + public: //commands bool moveHero(const CGHeroInstance *h, int3 dst); //dst must be free, neighbouring tile (this function can move hero only by one tile) @@ -272,29 +313,6 @@ public: int getPlayerStatus(int player) const; int getPlayerRelations(ui8 color1, ui8 color2) const;// 0 = enemy, 1 = ally, 2 = same player - //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 - int battleGetObstaclesAtTile(int tile); //returns bitfield - std::vector battleGetAllObstacles(); //returns all obstacles on the battlefield - const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID - const CStack * battleGetStackByPos(int pos, bool onlyAlive = true); //returns stack info by given pos - int battleGetPos(int stack); //returns position (tile ID) of stack - int battleMakeAction(BattleAction* action);//for casting spells by hero - DO NOT use it for moving active stack - std::vector battleGetStacks(); //returns stacks on battlefield - void getStackQueue( std::vector &out, int howMany ); //returns vector of stack in order of their move sequence - std::vector battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID - bool battleCanShoot(int ID, int dest); //returns true if unit with id ID can shoot to dest - bool battleCanCastSpell(); //returns true, if caller can cast a spell - bool battleCanFlee(); //returns true if caller can flee from the battle - const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead - ui8 battleGetWallState(int partOfWall); //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle - int battleGetWallUnderHex(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found - std::pair battleEstimateDamage(int attackerID, int defenderID); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle - const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender) - si8 battleHasDistancePenalty(int stackID, int destHex); //checks if given stack has distance penalty - si8 battleHasWallPenalty(int stackID, int destHex); //checks if given stack has wall penalty - si8 battleCanTeleportTo(int stackID, int destHex, int telportLevel); //checks if teleportation of given stack to given position can take place //XXX hmmm _tmain on _GNUC_ wtf? //friends diff --git a/CGameInterface.cpp b/CGameInterface.cpp index 513ef9c0e..ced9c862b 100644 --- a/CGameInterface.cpp +++ b/CGameInterface.cpp @@ -18,17 +18,18 @@ * */ -CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname) +template +rett * createAnyAI(CCallback * cb, std::string dllname, std::string methodName) { char temp[50]; - CGlobalAI * ret=NULL; - CGlobalAI*(*getAI)(); + rett * ret=NULL; + rett*(*getAI)(); void(*getName)(char*); std::string dllPath; #ifdef _WIN32 - dllPath = "AI/"+dllname+".dll"; + dllPath = LIB_DIR "/" +dllname+".dll"; HINSTANCE dll = LoadLibraryA(dllPath.c_str()); if (!dll) { @@ -37,7 +38,7 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname) } //int len = dllname.size()+1; getName = (void(*)(char*))GetProcAddress(dll,"GetAiName"); - getAI = (CGlobalAI*(*)())GetProcAddress(dll,"GetNewAI"); + getAI = (rett*(*)())GetProcAddress(dll,methodName.c_str()); #else dllPath = LIB_DIR "/" + dllname + ".so"; void *dll = dlopen(dllPath.c_str(), RTLD_LOCAL | RTLD_LAZY); @@ -47,7 +48,7 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname) throw new std::string("Cannot open AI library"); } getName = (void(*)(char*))dlsym(dll,"GetAiName"); - getAI = (CGlobalAI*(*)())dlsym(dll,"GetNewAI"); + getAI = (rett*(*)())dlsym(dll,methodName.c_str()); #endif getName(temp); tlog0 << "Loaded AI named " << temp << std::endl; @@ -59,3 +60,13 @@ CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname) ret->dllName = dllname; return ret; } + +CGlobalAI * CAIHandler::getNewAI(CCallback * cb, std::string dllname) +{ + return createAnyAI(cb, dllname, "GetNewAI"); +} + +CBattleGameInterface * CAIHandler::getNewBattleAI( CCallback * cb, std::string dllname ) +{ + return createAnyAI(cb, dllname, "GetNewBattleAI"); +} diff --git a/CGameInterface.h b/CGameInterface.h index 8eb2e60d1..9075d4ef0 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -18,6 +18,7 @@ using namespace boost::logic; class CCallback; +class IBattleCallback; class ICallback; class CGlobalAI; struct Component; @@ -47,18 +48,46 @@ class CStackInstance; class CCreature; class CLoadFile; class CSaveFile; -typedef TQuantity; +typedef si32 TQuantity; template class CISer; template class COSer; -class CGameInterface +class CBattleGameInterface { public: bool human; int playerID; std::string dllName; - virtual ~CGameInterface() {}; + virtual ~CBattleGameInterface() {}; + + virtual void init(IBattleCallback * CB){}; + + //battle call-ins + virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero + virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero + virtual BattleAction activeStack(int stackID)=0; //called when it's turn of that stack + virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack + virtual void battleStacksAttacked(const std::vector & bsa){}; //called when stack receives damage (after battleAttack()) + virtual void battleEnd(const BattleResult *br){}; + virtual void battleResultsApplied(){}; //called when all effects of last battle are applied + virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied; + virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn + virtual void battleStackMoved(int ID, int dest, int distance, bool end){}; + virtual void battleSpellCast(const BattleSpellCast *sc){}; + virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks + virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right + //virtual void battlefieldPrepared(int battlefieldType, std::vector obstacles){}; //called when battlefield is prepared, prior the battle beginning + virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp + virtual void battleNewStackAppeared(int stackID){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned + virtual void battleObstaclesRemoved(const std::set & removedObstacles){}; //called when a certain set of obstacles is removed from batlefield; IDs of them are given + virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack + virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield +}; + +class CGameInterface : public CBattleGameInterface +{ +public: virtual void buildChanged(const CGTownInstance *town, int buildingID, int what){}; //what: 1 - built, 2 - demolished //garrison operations @@ -113,32 +142,13 @@ public: virtual void gameOver(ui8 player, bool victory){}; //player lost or won the game virtual void serialize(COSer &h, const int version){}; //saving virtual void serialize(CISer &h, const int version){}; //loading - - //battle call-ins - virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero - virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero - virtual BattleAction activeStack(int stackID)=0; //called when it's turn of that stack - virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack - virtual void battleStacksAttacked(const std::vector & bsa){}; //called when stack receives damage (after battleAttack()) - virtual void battleEnd(const BattleResult *br){}; - virtual void battleResultsApplied(){}; //called when all effects of last battle are applied - virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied; - virtual void battleNewRound(int round){}; //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn - virtual void battleStackMoved(int ID, int dest, int distance, bool end){}; - virtual void battleSpellCast(const BattleSpellCast *sc){}; - virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks - virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right - //virtual void battlefieldPrepared(int battlefieldType, std::vector obstacles){}; //called when battlefield is prepared, prior the battle beginning - virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp - virtual void battleNewStackAppeared(int stackID){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned - virtual void battleObstaclesRemoved(const std::set & removedObstacles){}; //called when a certain set of obstacles is removed from batlefield; IDs of them are given - virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack - virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield }; + class CAIHandler { public: static CGlobalAI * getNewAI(CCallback * cb, std::string dllname); + static CBattleGameInterface * getNewBattleAI(CCallback * cb, std::string dllname); }; class CGlobalAI : public CGameInterface // AI class (to derivate) { diff --git a/StartInfo.h b/StartInfo.h index 8d9b80854..26cb72b2e 100644 --- a/StartInfo.h +++ b/StartInfo.h @@ -52,7 +52,7 @@ struct PlayerSettings struct StartInfo { - enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, INVALID = 255}; + enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, DUEL, INVALID = 255}; ui8 mode; //uses EMode enum ui8 difficulty; //0=easy; 4=impossible diff --git a/StupidAI/StupidAI.cpp b/StupidAI/StupidAI.cpp new file mode 100644 index 000000000..854dad265 --- /dev/null +++ b/StupidAI/StupidAI.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "StupidAI.h" + +CStupidAI::CStupidAI(void) +{ +} + + +CStupidAI::~CStupidAI(void) +{ +} + +void CStupidAI::init( IBattleCallback * CB ) +{ + +} + +void CStupidAI::actionFinished( const BattleAction *action ) +{ + +} + +void CStupidAI::actionStarted( const BattleAction *action ) +{ + +} + +BattleAction CStupidAI::activeStack( int stackID ) +{ + BattleAction ba; + ba.DEFEND; + ba.stackNumber = stackID; + return ba; +} \ No newline at end of file diff --git a/StupidAI/StupidAI.h b/StupidAI/StupidAI.h new file mode 100644 index 000000000..b750995ba --- /dev/null +++ b/StupidAI/StupidAI.h @@ -0,0 +1,14 @@ +#pragma once + +class CStupidAI : public CBattleGameInterface +{ +public: + CStupidAI(void); + ~CStupidAI(void); + + void init(IBattleCallback * CB) OVERRIDE; + void actionFinished(const BattleAction *action) OVERRIDE;//occurs AFTER every action taken by any stack or by the hero + void actionStarted(const BattleAction *action) OVERRIDE;//occurs BEFORE every action taken by any stack or by the hero + BattleAction activeStack(int stackID) OVERRIDE; //called when it's turn of that stack +}; + diff --git a/StupidAI/StupidAI.vcxproj b/StupidAI/StupidAI.vcxproj new file mode 100644 index 000000000..8d0d13ab9 --- /dev/null +++ b/StupidAI/StupidAI.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {15DABC90-234A-4B6B-9EEB-777C4768B82B} + StupidAI + + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + E:\C++\Lua_includes;E:\vcmi\rep - assembla\trunk\lua src;E:\C++\boost_1_43_0;E:\C++\SDL_mixer-1.2.7\include;E:\C++\SDL_ttf-2.0.8\include;E:\C++\zlib 1.2.3 binaries\include;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\include;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\include;%(AdditionalIncludeDirectories)options> + Use + stdafx.h + + + true + VCMI_lib.lib;%(AdditionalDependencies) + E:\vcmi\rep - assembla\trunk;E:\C++\lua bin;E:\C++\boost_1_43_0\lib;E:\C++\SDL_mixer-1.2.7\lib;E:\C++\SDL_ttf-2.0.8\lib;E:\C++\zlib 1.2.3 binaries\lib;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\lib;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + MaxSpeed + true + true + E:\C++\Lua_includes;E:\vcmi\rep - assembla\trunk\lua src;E:\C++\boost_1_43_0;E:\C++\SDL_mixer-1.2.7\include;E:\C++\SDL_ttf-2.0.8\include;E:\C++\zlib 1.2.3 binaries\include;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\include;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\include;%(AdditionalIncludeDirectories)options> + Use + stdafx.h + + + true + true + true + VCMI_lib.lib;%(AdditionalDependencies) + E:\vcmi\rep - assembla\trunk;E:\C++\lua bin;E:\C++\boost_1_43_0\lib;E:\C++\SDL_mixer-1.2.7\lib;E:\C++\SDL_ttf-2.0.8\lib;E:\C++\zlib 1.2.3 binaries\lib;E:\C++\SDL-1.2.11\devlibs - visual\SDL-1.2.11\lib;E:\C++\SDL_Image 1.2.5\SDL_image-1.2.5\lib;%(AdditionalLibraryDirectories) + + + + + + Create + Create + + + + + + + + + + + \ No newline at end of file diff --git a/StupidAI/main.cpp b/StupidAI/main.cpp new file mode 100644 index 000000000..39cf37b1a --- /dev/null +++ b/StupidAI/main.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "StupidAI.h" + +const char *g_cszAiName = "Stupid AI 0.1"; + +extern "C" DLL_F_EXPORT int GetGlobalAiVersion() +{ + return AI_INTERFACE_VER; +} + +extern "C" DLL_F_EXPORT void GetAiName(char* name) +{ + strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName); +} + +extern "C" DLL_F_EXPORT char* GetAiNameS() +{ + // need to be defined + return NULL; +} + +extern "C" DLL_F_EXPORT CBattleGameInterface* GetNewBattleAI() +{ + return new CStupidAI(); +} + +extern "C" DLL_F_EXPORT void ReleaseBattleAI(CBattleGameInterface* i) +{ + delete (CStupidAI*)i; +} \ No newline at end of file diff --git a/StupidAI/stdafx.cpp b/StupidAI/stdafx.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/StupidAI/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/StupidAI/stdafx.h b/StupidAI/stdafx.h new file mode 100644 index 000000000..efff23f15 --- /dev/null +++ b/StupidAI/stdafx.h @@ -0,0 +1,2 @@ +#pragma once +#include "../../AI_Base.h" \ No newline at end of file diff --git a/client/CMT.cpp b/client/CMT.cpp index b006cc0e8..c93d46829 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -43,6 +43,7 @@ #include "../lib/NetPacks.h" #include "CMessage.h" #include "../lib/CObjectHandler.h" +#include #ifdef _WIN32 #include "SDL_syswm.h" @@ -54,6 +55,8 @@ #undef main #endif +namespace po = boost::program_options; + /* * CMT.cpp, part of VCMI engine * @@ -89,6 +92,7 @@ void dispose(); void playIntro(); static void listenForEvents(); void requestChangingResolution(); +void startGame(StartInfo * options, CConnection *serv = NULL); #ifndef _WIN32 #ifndef _GNU_SOURCE @@ -160,7 +164,6 @@ void init() //tlog0<<"Initialization CPreGame (together): "< 1) + { + try + { + po::store(po::parse_command_line(argc, argv, opts), vm); + } + catch(std::exception &e) + { + tlog1 << "Failure during parsing command-line options:\n" << e.what() << std::endl; } } - if (optind < argc) { - printf ("Extra arguments: %s\n", argv[optind++]); - return 1; + po::notify(vm); + if(vm.count("help")) + { + prog_help(0); + return 0; + } + if(vm.count("version")) + { + prog_version(); + return 0; } -#endif //Set environment vars to make window centered. Sometimes work, sometimes not. :/ putenv("SDL_VIDEO_WINDOW_POS"); putenv("SDL_VIDEO_CENTERED=1"); - tlog0 << "Starting... " << std::endl; timeHandler total, pomtime; std::cout.flags(std::ios::unitbuf); logfile = new std::ofstream("VCMI_Client_log.txt"); @@ -267,15 +261,26 @@ int main(int argc, char** argv) //we can properly play intro only in the main thread, so we have to move loading to the separate thread boost::thread loading(init); - playIntro(); + + if(!vm.count("battle")) + playIntro(); + SDL_FillRect(screen,NULL,0); SDL_Flip(screen); loading.join(); tlog0<<"Initialization of VCMI (together): "<musich->playMusic(musicBase::mainMenu, -1); - - GH.curInt = new CGPreGame; //will set CGP pointer to itself + if(!vm.count("battle")) + { + CCS->musich->playMusic(musicBase::mainMenu, -1); + GH.curInt = new CGPreGame; //will set CGP pointer to itself + } + else + { + StartInfo *si = new StartInfo(); + si->mode = StartInfo::DUEL; + startGame(si); + } mainGUIThread = new boost::thread(&CGuiHandler::run, boost::ref(GH)); listenForEvents(); @@ -667,6 +672,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/) { case StartInfo::NEW_GAME: case StartInfo::CAMPAIGN: + case StartInfo::DUEL: client->newGame(serv, options); break; case StartInfo::LOAD_GAME: diff --git a/client/Client.cpp b/client/Client.cpp index c191d9122..c7386658d 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -367,38 +367,54 @@ void CClient::newGame( CConnection *con, StartInfo *si ) gs = const_cast(CGI)->state; gs->scenarioOps = si; gs->init(si, sum, seed); - - const_cast(CGI)->mh = new CMapHandler(); tlog0 <<"Initializing GameState (together): "<mh->map = gs->map; - tlog0 <<"Creating mapHandler: "<mh->init(); - initVillagesCapitols(gs->map); - pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1)); - tlog0 <<"Initializing mapHandler (together): "<map) + { + const_cast(CGI)->mh = new CMapHandler(); + CGI->mh->map = gs->map; + tlog0 <<"Creating mapHandler: "<mh->init(); + initVillagesCapitols(gs->map); + pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel+1)); + tlog0 <<"Initializing mapHandler (together): "<::iterator it = gs->scenarioOps->playerInfos.begin(); it != gs->scenarioOps->playerInfos.end(); ++it)//initializing interfaces for players { ui8 color = it->first; + gs->currentPlayer = color; if(!vstd::contains(myPlayers, color)) continue; - CCallback *cb = new CCallback(gs,color,this); - if(!it->second.human) + if(si->mode != StartInfo::DUEL) { - playerint[color] = static_cast(CAIHandler::getNewAI(cb,conf.cc.defaultAI)); + CCallback *cb = new CCallback(gs,color,this); + if(!it->second.human) + { + playerint[color] = static_cast(CAIHandler::getNewAI(cb,conf.cc.defaultAI)); + } + else + { + playerint[color] = new CPlayerInterface(color); + humanPlayers++; + } + + playerint[color]->init(cb); } - else + else { - playerint[color] = new CPlayerInterface(color); - humanPlayers++; + CBattleCallback * cbc = new CBattleCallback(gs, color, this); + battleints[color] = CAIHandler::getNewBattleAI(cb,conf.cc.defaultAI); + battleints[color]->init(cbc); } - gs->currentPlayer = color; - playerint[color]->init(cb); } + playerint[254] = new CPlayerInterface(-1); + playerint[254]->init(new CCallback(gs, -1, this)); + serv->addStdVecItems(const_cast(CGI)->state); hotSeat = (humanPlayers > 1); diff --git a/client/Client.h b/client/Client.h index f7ffbada5..09d0faaef 100644 --- a/client/Client.h +++ b/client/Client.h @@ -18,6 +18,7 @@ * */ +class CBattleGameInterface; struct StartInfo; class CGameState; class CGameInterface; @@ -61,6 +62,7 @@ public: CCallback *cb; std::set callbacks; //callbacks given to player interfaces std::map playerint; + std::map battleints; bool hotSeat; CConnection *serv; BattleAction *curbaction; diff --git a/global.h b/global.h index fa8d026ae..d379de21a 100644 --- a/global.h +++ b/global.h @@ -41,6 +41,7 @@ extern std::string NAME_AFFIX; //client / server #define DATA_DIR "." #define USER_DIR "." #define BIN_DIR "." +#define LIB_DIR "AI" #define SERVER_NAME "VCMI_server.exe" #else #ifndef DATA_DIR diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index ea8e4ec0d..29929c578 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1456,6 +1456,364 @@ CGameState::~CGameState() capitols.clear(); } +namespace CGH +{ + using namespace std; + static void readItTo(ifstream & input, vector< vector > & dest) //reads 7 lines, i-th one containing i integers, and puts it to dest + { + for(int j=0; j<7; ++j) + { + std::vector pom; + for(int g=0; g>hlp; + pom.push_back(hlp); + } + dest.push_back(pom); + } + } +} + +BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ) +{ + CMP_stack cmpst; + BattleInfo *curB = new BattleInfo; + curB->side1 = armies[0]->tempOwner; + curB->side2 = armies[1]->tempOwner; + if(curB->side2 == 254) + curB->side2 = 255; + + std::vector & stacks = (curB->stacks); + + curB->tile = tile; + curB->belligerents[0] = const_cast(armies[0]); + curB->belligerents[1] = const_cast(armies[1]); + curB->heroes[0] = const_cast(heroes[0]); + curB->heroes[1] = const_cast(heroes[1]); + curB->round = -2; + curB->activeStack = -1; + + if(town) + { + curB->tid = town->id; + curB->siege = town->fortLevel(); + } + else + { + curB->tid = -1; + curB->siege = 0; + } + + //reading battleStartpos + std::ifstream positions; + positions.open(DATA_DIR "/config/battleStartpos.txt", std::ios_base::in|std::ios_base::binary); + if(!positions.is_open()) + { + tlog1<<"Unable to open battleStartpos.txt!"<>dump; positions>>dump; + std::vector< std::vector > attackerLoose, defenderLoose, attackerTight, defenderTight, attackerCreBank, defenderCreBank; + CGH::readItTo(positions, attackerLoose); + positions>>dump; + CGH::readItTo(positions, defenderLoose); + positions>>dump; + positions>>dump; + CGH::readItTo(positions, attackerTight); + positions>>dump; + CGH::readItTo(positions, defenderTight); + positions>>dump; + positions>>dump; + CGH::readItTo(positions, attackerCreBank); + positions>>dump; + CGH::readItTo(positions, defenderCreBank); + positions.close(); + //battleStartpos read + + int k = 0; //stack serial + for(TSlots::const_iterator i = armies[0]->Slots().begin(); i!=armies[0]->Slots().end(); i++, k++) + { + int pos; + if(creatureBank) + pos = attackerCreBank[armies[0]->stacksCount()-1][k]; + else if(armies[0]->formation) + pos = attackerTight[armies[0]->stacksCount()-1][k]; + else + pos = attackerLoose[armies[0]->stacksCount()-1][k]; + + CStack * stack = curB->generateNewStack(*i->second, stacks.size(), true, i->first, pos); + stacks.push_back(stack); + } + + k = 0; + for(TSlots::const_iterator i = armies[1]->Slots().begin(); i!=armies[1]->Slots().end(); i++, k++) + { + int pos; + if(creatureBank) + pos = defenderCreBank[armies[1]->stacksCount()-1][k]; + else if(armies[1]->formation) + pos = defenderTight[armies[1]->stacksCount()-1][k]; + else + pos = defenderLoose[armies[1]->stacksCount()-1][k]; + + CStack * stack = curB->generateNewStack(*i->second, stacks.size(), false, i->first, pos); + stacks.push_back(stack); + } + + for(unsigned g=0; gposition%17)==1 && stacks[g]->doubleWide() && stacks[g]->attackerOwned) + { + stacks[g]->position += 1; + } + else if((stacks[g]->position%17)==15 && stacks[g]->doubleWide() && !stacks[g]->attackerOwned) + { + stacks[g]->position -= 1; + } + } + + //adding war machines + if(heroes[0]) + { + if(heroes[0]->getArt(13)) //ballista + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), true, 255, 52); + stacks.push_back(stack); + } + if(heroes[0]->getArt(14)) //ammo cart + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), true, 255, 18); + stacks.push_back(stack); + } + if(heroes[0]->getArt(15)) //first aid tent + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), true, 255, 154); + stacks.push_back(stack); + } + } + if(heroes[1]) + { + //defending hero shouldn't receive ballista (bug #551) + if(heroes[1]->getArt(13) && !town) //ballista + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), false, 255, 66); + stacks.push_back(stack); + } + if(heroes[1]->getArt(14)) //ammo cart + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), false, 255, 32); + stacks.push_back(stack); + } + if(heroes[1]->getArt(15)) //first aid tent + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), false, 255, 168); + stacks.push_back(stack); + } + } + if(town && heroes[0] && town->hasFort()) //catapult + { + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(145, 1), stacks.size(), true, 255, 120); + stacks.push_back(stack); + } + //war machines added + + switch(curB->siege) //adding towers + { + + case 3: //castle + {//lower tower / upper tower + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -4); + stacks.push_back(stack); + stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -3); + stacks.push_back(stack); + } + case 2: //citadel + {//main tower + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -2); + stacks.push_back(stack); + } + } + + std::stable_sort(stacks.begin(),stacks.end(),cmpst); + + //seting up siege + if(town && town->hasFort()) + { + for(int b=0; bsi.wallState); ++b) + { + curB->si.wallState[b] = 1; + } + } + + //randomize obstacles + if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank + { + bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles; + std::vector possibleObstacles; + + for(int i=0; i 12) + { + obAv[i] = false; + } + else + { + obAv[i] = true; + } + } + + for(std::map::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g) + { + if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0 + { + possibleObstacles.push_back(g->first); + } + } + + srand(time(NULL)); + if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them + { + int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles + while(toBlock>0) + { + CObstacleInstance coi; + coi.uniqueID = curB->obstacles.size(); + coi.ID = possibleObstacles[rand()%possibleObstacles.size()]; + coi.pos = rand()%BFIELD_SIZE; + std::vector block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos); + bool badObstacle = false; + for(int b=0; b= BFIELD_SIZE || !obAv[block[b]]) + { + badObstacle = true; + break; + } + } + if(badObstacle) continue; + //obstacle can be placed + curB->obstacles.push_back(coi); + for(int b=0; b= 0 && block[b] < BFIELD_SIZE) + obAv[block[b]] = false; + } + toBlock -= block.size(); + } + } + } + +// //giving building bonuses, if siege and we have harrisoned hero +// if (town) +// { +// if (heroes[1]) +// { +// for (int i=0; i<4; i++) +// { +// int val = town->defenceBonus(i); +// if (val) +// { +// GiveBonus gs; +// gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::OBJECT, val, -1, "", i); +// gs.id = heroes[1]->id; +// sendAndApply(&gs); +// } +// } +// } +// else//if we don't have hero - apply separately, if hero present - will be taken from hero bonuses +// { +// if(town->subID == 0 && vstd::contains(town->builtBuildings,22)) //castle, brotherhood of sword built +// for(int g=0; gaddNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE)); +// +// else if(vstd::contains(town->builtBuildings,5)) //tavern is built +// for(int g=0; gaddNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 1, Bonus::TOWN_STRUCTURE)); +// +// if(town->subID == 1 && vstd::contains(town->builtBuildings,21)) //rampart, fountain of fortune is present +// for(int g=0; gaddNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE)); +// } +// } + + //giving terrain overalay premies + int bonusSubtype = -1; + switch(terType) + { + case 9: //magic plains + { + bonusSubtype = 0; + } + case 14: //fiery fields + { + if(bonusSubtype == -1) bonusSubtype = 1; + } + case 15: //rock lands + { + if(bonusSubtype == -1) bonusSubtype = 8; + } + case 16: //magic clouds + { + if(bonusSubtype == -1) bonusSubtype = 2; + } + case 17: //lucid pools + { + if(bonusSubtype == -1) bonusSubtype = 4; + } + + { //common part for cases 9, 14, 15, 16, 17 + curB->addNewBonus(new Bonus(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::TERRAIN_OVERLAY, 3, -1, "", bonusSubtype)); + break; + } + + case 18: //holy ground + { + curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD))); + curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL))); + break; + } + case 19: //clover field + { //+2 luck bonus for neutral creatures + curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureFactionLimiter(-1))); + break; + } + case 20: //evil fog + { + curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD))); + curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL))); + break; + } + case 22: //cursed ground + { + curB->addNewBonus(makeFeature(Bonus::NO_MORALE, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY)); + curB->addNewBonus(makeFeature(Bonus::NO_LUCK, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY)); + curB->addNewBonus(makeFeature(Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY)); + break; + } + } + //overlay premies given + + //native terrain bonuses + if(town) //during siege always take premies for native terrain of faction + terrain = VLC->heroh->nativeTerrains[town->town->typeID]; + + ILimiter *nativeTerrain = new CreatureNativeTerrainLimiter(terrain); + curB->addNewBonus(makeFeature(Bonus::STACKS_SPEED, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); + curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::ATTACK, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); + curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::DEFENSE, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); + ////////////////////////////////////////////////////////////////////////// + + return curB; +} + +BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town) +{ + int terrain = map->getTile(tile).tertype; + int terType = battleGetBattlefieldType(tile); + return ::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town); +} + void CGameState::init( StartInfo * si, ui32 checksum, int Seed ) { struct HLP @@ -1563,6 +1921,35 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed ) map->initFromBytes((const unsigned char*)mapContent.c_str()); } break; + case StartInfo::DUEL: + { + int3 tile; + int terType = TerrainTile::grass; + int terrain = 15; + const CArmedInstance *armies[2]; + const CGHeroInstance *heroes[2]; + const CGTownInstance *town = NULL; + CGHeroInstance *h = new CGHeroInstance(); + h->subID = 1; + h->initHero(1); + h->initObj(); + //h->putStack(0, new CStackInstance(34, 5)); + + CGCreature *c = new CGCreature(); + c->putStack(0, new CStackInstance(70, 6)); + c->subID = 34; + c->initObj(); + + heroes[0] = h; + heroes[1] = NULL; + + armies[0] = h; + armies[1] = c; + + curB = ::setupBattle(tile, terrain, terType, armies, heroes, false, town); + return; + } + break; default: tlog1 << "Wrong mode: " << (int)scenarioOps->mode << std::endl; return; diff --git a/lib/CGameState.h b/lib/CGameState.h index 47367aeb5..17f77f136 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -171,7 +171,7 @@ public: template void serialize(Handler &h, const int version) { - h & players & fogOfWarMap; + h & id & players & fogOfWarMap; h & static_cast(*this); } @@ -498,6 +498,8 @@ public: bool checkForStandardLoss(ui8 player) const; //checks if given player lost the game void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild bmap > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns + BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town); + bool isVisible(int3 pos, int player); bool isVisible(const CGObjectInstance *obj, int player); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 36c2b2b4c..16cfdbbf1 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1,3 +1,4 @@ +#include "stdafx.h" #include "../lib/CCampaignHandler.h" #include "../StartInfo.h" #include "../lib/CArtHandler.h" @@ -17,16 +18,7 @@ #include "../lib/VCMIDirs.h" #include "../client/CSoundBase.h" #include "CGameHandler.h" -#include -#include //no i/o just types -#include -#include -#include -#include -#include -#include -#include -#include + /* * CGameHandler.cpp, part of VCMI engine @@ -313,237 +305,16 @@ void CGameHandler::changeSecSkill( int ID, int which, int val, bool abs/*=false* } } -void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function cb, const CGTownInstance *town) +void CGameHandler::startBattle( const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function cb, const CGTownInstance *town /*= NULL*/ ) { battleEndCallback = new boost::function(cb); - bEndArmy1 = army1; - bEndArmy2 = army2; + bEndArmy1 = armies[0]; + bEndArmy2 = armies[1]; { - BattleInfo *curB = new BattleInfo; - curB->side1 = army1->tempOwner; - curB->side2 = army2->tempOwner; - if(curB->side2 == 254) - curB->side2 = 255; - setupBattle(curB, tile, army1, army2, hero1, hero2, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces + setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces } - //TODO: pre-tactic stuff, call scripts etc. - - //tactic round - { - if( (hero1 && hero1->getSecSkillLevel(19)>0) || - ( hero2 && hero2->getSecSkillLevel(19)>0) )//someone has tactics - { - //TODO: tactic round (round -1) - NEW_ROUND; - } - } - - //spells opening battle - if (hero1 && hero1->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) - { - BonusList bl; - hero1->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL)); - BOOST_FOREACH (Bonus *b, bl) - { - handleSpellCasting(b->subtype, 3, -1, 0, hero1->tempOwner, NULL, hero2, b->val); - } - } - if (hero2 && hero2->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) - { - BonusList bl; - hero2->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL)); - BOOST_FOREACH (Bonus *b, bl) - { - handleSpellCasting(b->subtype, 3, -1, 1, hero2->tempOwner, NULL, hero1, b->val); - } - } - - //main loop - while(!battleResult.get()) //till the end of the battle ;] - { - NEW_ROUND; - std::vector & stacks = (gs->curB->stacks); - const BattleInfo & curB = *gs->curB; - - //stack loop - const CStack *next; - while(!battleResult.get() && (next = curB.getNextStack()) && next->willMove()) - { - - //check for bad morale => freeze - int nextStackMorale = next->MoraleVal(); - if( nextStackMorale < 0 && - !(NBonus::hasOfType(hero1, Bonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, Bonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses) - ) - { - if( rand()%24 < -2 * nextStackMorale) - { - //unit loses its turn - empty freeze action - BattleAction ba; - ba.actionType = BattleAction::BAD_MORALE; - ba.additionalInfo = 1; - ba.side = !next->attackerOwned; - ba.stackNumber = next->ID; - sendAndApply(&StartAction(ba)); - sendAndApply(&EndAction()); - checkForBattleEnd(stacks); //check if this "action" ended the battle (not likely but who knows...) - continue; - } - } - - if(next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk - { - std::pair attackInfo = curB.getNearestStack(next, boost::logic::indeterminate); - if(attackInfo.first != NULL) - { - BattleAction attack; - attack.actionType = BattleAction::WALK_AND_ATTACK; - attack.side = !next->attackerOwned; - attack.stackNumber = next->ID; - - attack.additionalInfo = attackInfo.first->position; - attack.destinationTile = attackInfo.second; - - makeBattleAction(attack); - - checkForBattleEnd(stacks); - } - else - { - makeStackDoNothing(next); - } - continue; - } - - const CGHeroInstance * curOwner = gs->battleGetOwner(next->ID); - - if( (next->position < 0 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //arrow turret, hero has no ballistics - || (next->getCreature()->idNumber == 146 && (!curOwner || curOwner->getSecSkillLevel(20) == 0))) //ballista, hero has no artillery - { - BattleAction attack; - attack.actionType = BattleAction::SHOOT; - attack.side = !next->attackerOwned; - attack.stackNumber = next->ID; - - for(int g=0; gcurB->stacks.size(); ++g) - { - if(gs->curB->stacks[g]->owner != next->owner && gs->curB->stacks[g]->alive()) - { - attack.destinationTile = gs->curB->stacks[g]->position; - break; - } - } - - makeBattleAction(attack); - - checkForBattleEnd(stacks); - continue; - } - - if(next->getCreature()->idNumber == 145 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //catapult, hero has no ballistics - { - BattleAction attack; - static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95}; - - attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ]; - attack.actionType = BattleAction::CATAPULT; - attack.additionalInfo = 0; - attack.side = !next->attackerOwned; - attack.stackNumber = next->ID; - - makeBattleAction(attack); - continue; - } - - if(next->getCreature()->idNumber == 147 && (!curOwner || curOwner->getSecSkillLevel(27) == 0)) //first aid tent, hero has no first aid - { - BattleAction heal; - - std::vector< const CStack * > possibleStacks; - for (int v=0; vcurB->stacks.size(); ++v) - { - const CStack * cstack = gs->curB->stacks[v]; - if (cstack->owner == next->owner && cstack->firstHPleft < cstack->MaxHealth() && cstack->alive()) //it's friendly and not fully healthy - { - possibleStacks.push_back(cstack); - } - } - - if(possibleStacks.size() == 0) - { - //nothing to heal - makeStackDoNothing(next); - - continue; - } - else - { - //heal random creature - const CStack * toBeHealed = possibleStacks[ rand()%possibleStacks.size() ]; - heal.actionType = BattleAction::STACK_HEAL; - heal.additionalInfo = 0; - heal.destinationTile = toBeHealed->position; - heal.side = !next->attackerOwned; - heal.stackNumber = next->ID; - - makeBattleAction(heal); - - } - continue; - } - - int numberOfAsks = 1; - bool breakOuter = false; - do - {//ask interface and wait for answer - if(!battleResult.get()) - { - BattleSetActiveStack sas; - sas.stack = next->ID; - sendAndApply(&sas); - boost::unique_lock lock(battleMadeAction.mx); - while(next->alive() && (!battleMadeAction.data && !battleResult.get())) //active stack hasn't made its action and battle is still going - battleMadeAction.cond.wait(lock); - battleMadeAction.data = false; - } - - if(battleResult.get()) //don't touch it, battle could be finished while waiting got action - { - breakOuter = true; - break; - } - - //we're after action, all results applied - checkForBattleEnd(stacks); //check if this action ended the battle - - //check for good morale - nextStackMorale = next->MoraleVal(); - if(!vstd::contains(next->state,HAD_MORALE) //only one extra move per turn possible - && !vstd::contains(next->state,DEFENDING) - && !vstd::contains(next->state,WAITING) - && next->alive() - && nextStackMorale > 0 - && !(NBonus::hasOfType(hero1, Bonus::BLOCK_MORALE) || NBonus::hasOfType(hero2, Bonus::BLOCK_MORALE)) //checking if heroes have (or don't have) morale blocking bonuses - ) - { - if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn - ++numberOfAsks; //move this stack once more - } - - --numberOfAsks; - } while (numberOfAsks > 0); - - if (breakOuter) - { - break; - } - - } - } - - endBattle(tile, hero1, hero2); - + runBattle(); } void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) @@ -1294,7 +1065,10 @@ void CGameHandler::run(bool resume) ui8 pom; //ui32 seed; if(!resume) - (*cc) << gs->initialOpts << gs->map->checksum << gs->seed; // gs->scenarioOps + { + ui32 sum = gs->map ? gs->map->checksum : 612; + (*cc) << gs->initialOpts << sum << gs->seed; // gs->scenarioOps + } (*cc) >> quantity; //how many players will be handled at that client @@ -1321,6 +1095,12 @@ void CGameHandler::run(bool resume) boost::thread(boost::bind(&CGameHandler::handleConnection,this,pom,boost::ref(**i))); } + if(gs->scenarioOps->mode == StartInfo::DUEL) + { + runBattle(); + return; + } + while (!end2) { if(!resume) @@ -1382,336 +1162,13 @@ namespace CGH } } -void CGameHandler::setupBattle(BattleInfo * curB, int3 tile, const CArmedInstance *army1, const CArmedInstance *army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town) +void CGameHandler::setupBattle( int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town ) { battleResult.set(NULL); - std::vector & stacks = (curB->stacks); - - curB->tile = tile; - curB->belligerents[0] = const_cast(army1); - curB->belligerents[1] = const_cast(army2); - curB->heroes[0] = const_cast(hero1); - curB->heroes[1] = const_cast(hero2); - curB->round = -2; - curB->activeStack = -1; - - if(town) - { - curB->tid = town->id; - curB->siege = town->fortLevel(); - } - else - { - curB->tid = -1; - curB->siege = 0; - } - - //reading battleStartpos - std::ifstream positions; - positions.open(DATA_DIR "/config/battleStartpos.txt", std::ios_base::in|std::ios_base::binary); - if(!positions.is_open()) - { - tlog1<<"Unable to open battleStartpos.txt!"<>dump; positions>>dump; - std::vector< std::vector > attackerLoose, defenderLoose, attackerTight, defenderTight, attackerCreBank, defenderCreBank; - CGH::readItTo(positions, attackerLoose); - positions>>dump; - CGH::readItTo(positions, defenderLoose); - positions>>dump; - positions>>dump; - CGH::readItTo(positions, attackerTight); - positions>>dump; - CGH::readItTo(positions, defenderTight); - positions>>dump; - positions>>dump; - CGH::readItTo(positions, attackerCreBank); - positions>>dump; - CGH::readItTo(positions, defenderCreBank); - positions.close(); - //battleStartpos read - - int k = 0; //stack serial - for(TSlots::const_iterator i = army1->Slots().begin(); i!=army1->Slots().end(); i++, k++) - { - int pos; - if(creatureBank) - pos = attackerCreBank[army1->stacksCount()-1][k]; - else if(army1->formation) - pos = attackerTight[army1->stacksCount()-1][k]; - else - pos = attackerLoose[army1->stacksCount()-1][k]; - - CStack * stack = curB->generateNewStack(*i->second, stacks.size(), true, i->first, pos); - stacks.push_back(stack); - } - - k = 0; - for(TSlots::const_iterator i = army2->Slots().begin(); i!=army2->Slots().end(); i++, k++) - { - int pos; - if(creatureBank) - pos = defenderCreBank[army2->stacksCount()-1][k]; - else if(army2->formation) - pos = defenderTight[army2->stacksCount()-1][k]; - else - pos = defenderLoose[army2->stacksCount()-1][k]; - - CStack * stack = curB->generateNewStack(*i->second, stacks.size(), false, i->first, pos); - stacks.push_back(stack); - } - - for(unsigned g=0; gposition%17)==1 && stacks[g]->doubleWide() && stacks[g]->attackerOwned) - { - stacks[g]->position += 1; - } - else if((stacks[g]->position%17)==15 && stacks[g]->doubleWide() && !stacks[g]->attackerOwned) - { - stacks[g]->position -= 1; - } - } - - //adding war machines - if(hero1) - { - if(hero1->getArt(13)) //ballista - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), true, 255, 52); - stacks.push_back(stack); - } - if(hero1->getArt(14)) //ammo cart - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), true, 255, 18); - stacks.push_back(stack); - } - if(hero1->getArt(15)) //first aid tent - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), true, 255, 154); - stacks.push_back(stack); - } - } - if(hero2) - { - //defending hero shouldn't receive ballista (bug #551) - if(hero2->getArt(13) && !town) //ballista - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(146, 1), stacks.size(), false, 255, 66); - stacks.push_back(stack); - } - if(hero2->getArt(14)) //ammo cart - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(148, 1), stacks.size(), false, 255, 32); - stacks.push_back(stack); - } - if(hero2->getArt(15)) //first aid tent - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(147, 1), stacks.size(), false, 255, 168); - stacks.push_back(stack); - } - } - if(town && hero1 && town->hasFort()) //catapult - { - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(145, 1), stacks.size(), true, 255, 120); - stacks.push_back(stack); - } - //war machines added - - switch(curB->siege) //adding towers - { - - case 3: //castle - {//lower tower / upper tower - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -4); - stacks.push_back(stack); - stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -3); - stacks.push_back(stack); - } - case 2: //citadel - {//main tower - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(149, 1), stacks.size(), false, 255, -2); - stacks.push_back(stack); - } - } - - std::stable_sort(stacks.begin(),stacks.end(),cmpst); - - //seting up siege - if(town && town->hasFort()) - { - for(int b=0; bsi.wallState); ++b) - { - curB->si.wallState[b] = 1; - } - } - - int terType = gs->battleGetBattlefieldType(tile); - - //randomize obstacles - if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank - { - bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles; - std::vector possibleObstacles; - - for(int i=0; i 12) - { - obAv[i] = false; - } - else - { - obAv[i] = true; - } - } - - for(std::map::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g) - { - if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0 - { - possibleObstacles.push_back(g->first); - } - } - - srand(time(NULL)); - if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them - { - int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles - while(toBlock>0) - { - CObstacleInstance coi; - coi.uniqueID = curB->obstacles.size(); - coi.ID = possibleObstacles[rand()%possibleObstacles.size()]; - coi.pos = rand()%BFIELD_SIZE; - std::vector block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos); - bool badObstacle = false; - for(int b=0; b= BFIELD_SIZE || !obAv[block[b]]) - { - badObstacle = true; - break; - } - } - if(badObstacle) continue; - //obstacle can be placed - curB->obstacles.push_back(coi); - for(int b=0; b= 0 && block[b] < BFIELD_SIZE) - obAv[block[b]] = false; - } - toBlock -= block.size(); - } - } - } - - //giving building bonuses, if siege and we have harrisoned hero - if (town) - { - if (hero2) - { - for (int i=0; i<4; i++) - { - int val = town->defenceBonus(i); - if (val) - { - GiveBonus gs; - gs.bonus = Bonus(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::OBJECT, val, -1, "", i); - gs.id = hero2->id; - sendAndApply(&gs); - } - } - } - else//if we don't have hero - apply separately, if hero present - will be taken from hero bonuses - { - if(town->subID == 0 && vstd::contains(town->builtBuildings,22)) //castle, brotherhood of sword built - for(int g=0; gaddNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE)); - - else if(vstd::contains(town->builtBuildings,5)) //tavern is built - for(int g=0; gaddNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, 1, Bonus::TOWN_STRUCTURE)); - - if(town->subID == 1 && vstd::contains(town->builtBuildings,21)) //rampart, fountain of fortune is present - for(int g=0; gaddNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, 2, Bonus::TOWN_STRUCTURE)); - } - } - - //giving terrain overalay premies - int bonusSubtype = -1; - switch(terType) - { - case 9: //magic plains - { - bonusSubtype = 0; - } - case 14: //fiery fields - { - if(bonusSubtype == -1) bonusSubtype = 1; - } - case 15: //rock lands - { - if(bonusSubtype == -1) bonusSubtype = 8; - } - case 16: //magic clouds - { - if(bonusSubtype == -1) bonusSubtype = 2; - } - case 17: //lucid pools - { - if(bonusSubtype == -1) bonusSubtype = 4; - } - - { //common part for cases 9, 14, 15, 16, 17 - curB->addNewBonus(new Bonus(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::TERRAIN_OVERLAY, 3, -1, "", bonusSubtype)); - break; - } - - case 18: //holy ground - { - curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD))); - curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL))); - break; - } - case 19: //clover field - { //+2 luck bonus for neutral creatures - curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureFactionLimiter(-1))); - break; - } - case 20: //evil fog - { - curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, -1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(GOOD))); - curB->addNewBonus(makeFeature(Bonus::MORALE, Bonus::ONE_BATTLE, 0, +1, Bonus::TERRAIN_OVERLAY)->addLimiter(new CreatureAlignmentLimiter(EVIL))); - break; - } - case 22: //cursed ground - { - curB->addNewBonus(makeFeature(Bonus::NO_MORALE, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY)); - curB->addNewBonus(makeFeature(Bonus::NO_LUCK, Bonus::ONE_BATTLE, 0, 0, Bonus::TERRAIN_OVERLAY)); - curB->addNewBonus(makeFeature(Bonus::BLOCK_SPELLS_ABOVE_LEVEL, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_OVERLAY)); - break; - } - } - //overlay premies given - - //native terrain bonuses - int terrain = this->getTile(tile)->tertype; - if(town) //during siege always take premies for native terrain of faction - terrain = VLC->heroh->nativeTerrains[town->town->typeID]; - - ILimiter *nativeTerrain = new CreatureNativeTerrainLimiter(terrain); - curB->addNewBonus(makeFeature(Bonus::STACKS_SPEED, Bonus::ONE_BATTLE, 0, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); - curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::ATTACK, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); - curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::DEFENSE, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); - ////////////////////////////////////////////////////////////////////////// //send info about battles BattleStart bs; - bs.info = curB; + bs.info = gs->setupBattle(tile, armies, heroes, creatureBank, town); sendAndApply(&bs); } @@ -2285,7 +1742,14 @@ void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstanc if(army2->tempOwner < PLAYER_LIMIT) states.setFlag(army2->tempOwner,&PlayerStatus::engagedIntoBattle,true); - boost::thread(boost::bind(&CGameHandler::startBattle, this, army1, army2, tile, hero1, hero2, creatureBank, cb, town)); + const CArmedInstance *armies[2]; + armies[0] = army1; + armies[1] = army2; + const CGHeroInstance*heroes[2]; + heroes[0] = hero1; + heroes[1] = hero2; + + boost::thread(boost::bind(&CGameHandler::startBattle, this, armies, tile, heroes, creatureBank, cb, town)); } void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function cb, bool creatureBank ) @@ -5333,6 +4797,226 @@ bool CGameHandler::swapStacks(const StackLocation &sl1, const StackLocation &sl2 } } +void CGameHandler::runBattle() +{ + assert(gs->curB); + //TODO: pre-tactic stuff, call scripts etc. + + //tactic round + { + if( (gs->curB->heroes[0] && gs->curB->heroes[0]->getSecSkillLevel(19)>0) || + ( gs->curB->heroes[1] && gs->curB->heroes[1]->getSecSkillLevel(19)>0) )//someone has tactics + { + //TODO: tactic round (round -1) + NEW_ROUND; + } + } + + //spells opening battle + if (gs->curB->heroes[0] && gs->curB->heroes[0]->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) + { + BonusList bl; + gs->curB->heroes[0]->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL)); + BOOST_FOREACH (Bonus *b, bl) + { + handleSpellCasting(b->subtype, 3, -1, 0, gs->curB->heroes[0]->tempOwner, NULL, gs->curB->heroes[1], b->val); + } + } + if (gs->curB->heroes[1] && gs->curB->heroes[1]->hasBonusOfType(Bonus::OPENING_BATTLE_SPELL)) + { + BonusList bl; + gs->curB->heroes[1]->getBonuses(bl, Selector::type(Bonus::OPENING_BATTLE_SPELL)); + BOOST_FOREACH (Bonus *b, bl) + { + handleSpellCasting(b->subtype, 3, -1, 1, gs->curB->heroes[1]->tempOwner, NULL, gs->curB->heroes[0], b->val); + } + } + + //main loop + while(!battleResult.get()) //till the end of the battle ;] + { + NEW_ROUND; + std::vector & stacks = (gs->curB->stacks); + const BattleInfo & curB = *gs->curB; + + //stack loop + const CStack *next; + while(!battleResult.get() && (next = curB.getNextStack()) && next->willMove()) + { + + //check for bad morale => freeze + int nextStackMorale = next->MoraleVal(); + if( nextStackMorale < 0 && + !(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses) + ) + { + if( rand()%24 < -2 * nextStackMorale) + { + //unit loses its turn - empty freeze action + BattleAction ba; + ba.actionType = BattleAction::BAD_MORALE; + ba.additionalInfo = 1; + ba.side = !next->attackerOwned; + ba.stackNumber = next->ID; + sendAndApply(&StartAction(ba)); + sendAndApply(&EndAction()); + checkForBattleEnd(stacks); //check if this "action" ended the battle (not likely but who knows...) + continue; + } + } + + if(next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk + { + std::pair attackInfo = curB.getNearestStack(next, boost::logic::indeterminate); + if(attackInfo.first != NULL) + { + BattleAction attack; + attack.actionType = BattleAction::WALK_AND_ATTACK; + attack.side = !next->attackerOwned; + attack.stackNumber = next->ID; + + attack.additionalInfo = attackInfo.first->position; + attack.destinationTile = attackInfo.second; + + makeBattleAction(attack); + + checkForBattleEnd(stacks); + } + else + { + makeStackDoNothing(next); + } + continue; + } + + const CGHeroInstance * curOwner = gs->battleGetOwner(next->ID); + + if( (next->position < 0 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //arrow turret, hero has no ballistics + || (next->getCreature()->idNumber == 146 && (!curOwner || curOwner->getSecSkillLevel(20) == 0))) //ballista, hero has no artillery + { + BattleAction attack; + attack.actionType = BattleAction::SHOOT; + attack.side = !next->attackerOwned; + attack.stackNumber = next->ID; + + for(int g=0; gcurB->stacks.size(); ++g) + { + if(gs->curB->stacks[g]->owner != next->owner && gs->curB->stacks[g]->alive()) + { + attack.destinationTile = gs->curB->stacks[g]->position; + break; + } + } + + makeBattleAction(attack); + + checkForBattleEnd(stacks); + continue; + } + + if(next->getCreature()->idNumber == 145 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //catapult, hero has no ballistics + { + BattleAction attack; + static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95}; + + attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ]; + attack.actionType = BattleAction::CATAPULT; + attack.additionalInfo = 0; + attack.side = !next->attackerOwned; + attack.stackNumber = next->ID; + + makeBattleAction(attack); + continue; + } + + if(next->getCreature()->idNumber == 147 && (!curOwner || curOwner->getSecSkillLevel(27) == 0)) //first aid tent, hero has no first aid + { + BattleAction heal; + + std::vector< const CStack * > possibleStacks; + for (int v=0; vcurB->stacks.size(); ++v) + { + const CStack * cstack = gs->curB->stacks[v]; + if (cstack->owner == next->owner && cstack->firstHPleft < cstack->MaxHealth() && cstack->alive()) //it's friendly and not fully healthy + { + possibleStacks.push_back(cstack); + } + } + + if(possibleStacks.size() == 0) + { + //nothing to heal + makeStackDoNothing(next); + + continue; + } + else + { + //heal random creature + const CStack * toBeHealed = possibleStacks[ rand()%possibleStacks.size() ]; + heal.actionType = BattleAction::STACK_HEAL; + heal.additionalInfo = 0; + heal.destinationTile = toBeHealed->position; + heal.side = !next->attackerOwned; + heal.stackNumber = next->ID; + + makeBattleAction(heal); + + } + continue; + } + + int numberOfAsks = 1; + bool breakOuter = false; + do + {//ask interface and wait for answer + if(!battleResult.get()) + { + BattleSetActiveStack sas; + sas.stack = next->ID; + sendAndApply(&sas); + boost::unique_lock lock(battleMadeAction.mx); + while(next->alive() && (!battleMadeAction.data && !battleResult.get())) //active stack hasn't made its action and battle is still going + battleMadeAction.cond.wait(lock); + battleMadeAction.data = false; + } + + if(battleResult.get()) //don't touch it, battle could be finished while waiting got action + { + breakOuter = true; + break; + } + + //we're after action, all results applied + checkForBattleEnd(stacks); //check if this action ended the battle + + //check for good morale + nextStackMorale = next->MoraleVal(); + if(!vstd::contains(next->state,HAD_MORALE) //only one extra move per turn possible + && !vstd::contains(next->state,DEFENDING) + && !vstd::contains(next->state,WAITING) + && next->alive() + && nextStackMorale > 0 + && !(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses + ) + { + if(rand()%24 < nextStackMorale) //this stack hasn't got morale this turn + ++numberOfAsks; //move this stack once more + } + + --numberOfAsks; + } while (numberOfAsks > 0); + + if (breakOuter) + { + break; + } + + } + } + + endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]); +} CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat) { int color = army->tempOwner; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index fb95ac5d2..5c3ec4e67 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -105,7 +105,8 @@ public: bool isAllowedExchange(int id1, int id2); void giveSpells(const CGTownInstance *t, const CGHeroInstance *h); int moveStack(int stack, int dest); //returned value - travelled distance - void startBattle(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero + void startBattle(const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero + void runBattle(); void checkLossVictory(ui8 player); void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all void getLossVicMessage(ui8 player, ui8 standard, bool victory, InfoWindow &out) const; @@ -119,7 +120,7 @@ public: void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking void prepareAttacked(BattleStackAttacked &bsa, const CStack *def); void checkForBattleEnd( std::vector &stacks ); - void setupBattle( BattleInfo * curB, int3 tile, const CArmedInstance *army1, const CArmedInstance *army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town); + void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town); CGameHandler(void); ~CGameHandler(void); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 38465e91a..32157856b 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -1,7 +1,5 @@ +#include "stdafx.h" #include "../lib/CCampaignHandler.h" -#include -#include -#include #include "../global.h" #include "../lib/Connection.h" #include "../lib/CArtHandler.h" @@ -18,9 +16,6 @@ #include #endif #include "CVCMIServer.h" -#include -#include -#include #include "../StartInfo.h" #include "../lib/map.h" #include "../lib/CLodHandler.h" @@ -28,8 +23,6 @@ #include "../lib/VCMI_Lib.h" #include "../lib/VCMIDirs.h" #include "CGameHandler.h" -#include -#include #include "../lib/CMapInfo.h" std::string NAME_AFFIX = "server"; diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index e793b2695..492dc6985 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -1,3 +1,4 @@ +#include "stdafx.h" #include "../lib/NetPacks.h" #include "CGameHandler.h" #include "../lib/CObjectHandler.h" diff --git a/server/stdafx.cpp b/server/stdafx.cpp new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/server/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/server/stdafx.h b/server/stdafx.h new file mode 100644 index 000000000..59c4b34b6 --- /dev/null +++ b/server/stdafx.h @@ -0,0 +1,24 @@ +#include +#include //no i/o just types +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../global.h" + +#include +#include +#include +#include +#include +#include +#include +#include