From d25a5a795e8fac093d2123ce2d302d0be653775e Mon Sep 17 00:00:00 2001 From: mateuszb Date: Tue, 1 Sep 2009 13:54:13 +0000 Subject: [PATCH] * next part of sieges --- CCallback.cpp | 9 ++++++ CCallback.h | 2 ++ CGameInterface.h | 2 ++ client/CBattleInterface.cpp | 41 ++++++++++++++++++------ client/CBattleInterface.h | 6 ++-- client/CPlayerInterface.cpp | 7 +++++ client/CPlayerInterface.h | 1 + client/NetPacksClient.cpp | 7 +++++ lib/CGameState.cpp | 18 +++++++++++ lib/CGameState.h | 3 +- lib/NetPacks.h | 16 ++++++++++ lib/NetPacksLib.cpp | 9 ++++++ lib/RegisterTypes.cpp | 1 + server/CGameHandler.cpp | 63 +++++++++++++++++++++++++++++++++++++ 14 files changed, 173 insertions(+), 12 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index 1485b90a1..16764dffb 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -618,6 +618,15 @@ ui8 CCallback::battleGetWallState(int partOfWall) return gs->curB->si.wallState[partOfWall]; } +int CCallback::battleGetWallUnderHex(int hex) +{ + if(!gs->curB || gs->curB->siege == 0) + { + return -1; + } + return gs->curB->hexToWallPart(hex); +} + std::pair CCallback::battleEstimateDamage(int attackerID, int defenderID) { if(!gs->curB) diff --git a/CCallback.h b/CCallback.h index 8d020a609..db35511fa 100644 --- a/CCallback.h +++ b/CCallback.h @@ -182,6 +182,7 @@ public: 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 }; @@ -289,6 +290,7 @@ public: 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 //XXX hmmm _tmain on _GNUC_ wtf? diff --git a/CGameInterface.h b/CGameInterface.h index 6c653e715..2bb99d2bc 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -40,6 +40,7 @@ struct SetStackEffect; struct HeroBonus; struct PackageApplied; struct SetObjectProperty; +struct CatapultAttack; class CLoadFile; class CSaveFile; template class CISer; @@ -121,6 +122,7 @@ public: virtual void battleStacksHealedRes(const std::vector > & healedStacks){}; //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 }; class CAIHandler { diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 72f0a20cb..2dee1c4cd 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -817,6 +817,12 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) console->whoSetAlter = 0; } } + else if( sactive->hasFeatureOfType(StackFeature::CATAPULT) && isCatapultAttackable(myNumber) ) //catapulting + { + CGI->curh->changeGraphic(1,16); + console->alterTxt = ""; + console->whoSetAlter = 0; + } else //empty unavailable tile { CGI->curh->changeGraphic(1,0); @@ -1579,6 +1585,18 @@ bool CBattleInterface::blockedByObstacle(int hex) const return vstd::contains(coveredHexes, hex); } +bool CBattleInterface::isCatapultAttackable(int hex) const +{ + if(!siegeH) + return false; + + int wallUnder = LOCPLINT->cb->battleGetWallUnderHex(hex); + if(wallUnder == -1) + return false; + + return LOCPLINT->cb->battleGetWallState(wallUnder) < 3; +} + void CBattleInterface::handleEndOfMove(int stackNumber, int destinationTile) { const CStack * movedStack = LOCPLINT->cb->battleGetStackByID(stackNumber); @@ -1652,6 +1670,7 @@ void CBattleInterface::hexLclicked(int whichOne) const CStack* dest = LOCPLINT->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one if(!dest || !dest->alive()) //no creature at that tile { + const CStack * sactive = LOCPLINT->cb->battleGetStackByID(activeStack); if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range { CGI->curh->changeGraphic(1, 6); //cursor should be changed @@ -1669,6 +1688,10 @@ void CBattleInterface::hexLclicked(int whichOne) giveCommand(2,whichOne,activeStack); } } + else if(sactive->hasFeatureOfType(StackFeature::CATAPULT) && isCatapultAttackable(whichOne)) //attacking (catapult) + { + giveCommand(9,whichOne,activeStack); + } } else if(dest->owner != attackingHeroInstance->tempOwner && LOCPLINT->cb->battleCanShoot(activeStack, whichOne) ) //shooting @@ -3219,19 +3242,14 @@ std::string CBattleInterface::SiegeHelper::townTypeInfixes[F_NUMBER] = {"CS", "R CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner) : town(siegeTown), owner(_owner) { - backWall = BitmapHandler::loadBitmap( getSiegeName(1) ); - for(int g=0; gpos.w + owner->pos.x - backWall->w, 55 + owner->pos.y); + pos = Point(owner->pos.w + owner->pos.x - walls[1]->w, 55 + owner->pos.y); break; case 2: //keep - pos = Point(owner->pos.w + owner->pos.x - walls[what-2]->w, 154 + owner->pos.y); + pos = Point(owner->pos.w + owner->pos.x - walls[2]->w, 154 + owner->pos.y); break; case 3: //bottom tower case 4: //bottom wall @@ -3314,6 +3337,6 @@ void CBattleInterface::SiegeHelper::printPartOfWall(SDL_Surface * to, int what) if(pos.x != -1) { - blitAt(walls[what-2], pos.x, pos.y, to); + blitAt(walls[what], pos.x, pos.y, to); } } diff --git a/client/CBattleInterface.h b/client/CBattleInterface.h index 9bd790b57..a142522c1 100644 --- a/client/CBattleInterface.h +++ b/client/CBattleInterface.h @@ -218,6 +218,7 @@ private: void giveCommand(ui8 action, ui16 tile, ui32 stack, si32 additional=-1); bool isTileAttackable(const int & number) const; //returns true if tile 'number' is neighbouring any tile from active stack's range or is one of these tiles bool blockedByObstacle(int hex) const; + bool isCatapultAttackable(int hex) const; //returns true if given tile can be attacked by catapult void handleEndOfMove(int stackNumber, int destinationTile); //helper function @@ -233,8 +234,7 @@ private: { private: static std::string townTypeInfixes[F_NUMBER]; //for internal use only - to build filenames - SDL_Surface * backWall; - SDL_Surface * walls[11]; + SDL_Surface * walls[13]; const CBattleInterface * owner; public: const CGTownInstance * town; //besieged town @@ -245,6 +245,8 @@ private: std::string getSiegeName(ui16 what, ui16 additInfo = 1) const; //what: 0 - background, 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall; additInfo: 1 - intact, 2 - damaged, 3 - destroyed void printPartOfWall(SDL_Surface * to, int what);//what: 1 - background wall, 2 - keep, 3 - bottom tower, 4 - bottom wall, 5 - below gate, 6 - over gate, 7 - upper wall, 8 - uppert tower, 9 - gate, 10 - gate arch, 11 - bottom static wall, 12 - upper static wall + + friend class CPlayerInterface; } * siegeH; public: CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect); //c-tor diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 177427b8a..35683f2b1 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -996,6 +996,13 @@ void CPlayerInterface::battleObstaclesRemoved(const std::set & removedObst battleInt->redrawBackgroundWithHexes(battleInt->activeStack); } +void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca) +{ + SDL_FreeSurface(battleInt->siegeH->walls[ca.attackedPartOfWall + 2]); + battleInt->siegeH->walls[ca.attackedPartOfWall + 2] = BitmapHandler::loadBitmap( + battleInt->siegeH->getSiegeName(ca.attackedPartOfWall + 2, cb->battleGetWallState(ca.attackedPartOfWall)) ); +} + void CPlayerInterface::battleNewRound(int round) //called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn { boost::unique_lock un(*pim); diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index bfe1a1d44..2dc98b01b 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -181,6 +181,7 @@ public: void battleStacksHealedRes(const std::vector > & healedStacks); //called when stacks are healed / resurrected void battleNewStackAppeared(int stackID); //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned void battleObstaclesRemoved(const std::set & removedObstacles); //called when a certain set of obstacles is removed from batlefield; IDs of them are given + void battleCatapultAttacked(const CatapultAttack & ca); //called when catapult makes an attack //-------------// void heroKilled(const CGHeroInstance* hero); diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index f89cdd9f9..ec73fa645 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -469,6 +469,13 @@ void ObstaclesRemoved::applyCl( CClient *cl ) INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleObstaclesRemoved, obstacles); } +void CatapultAttack::applyCl( CClient *cl ) +{ + //inform interfaces about catapult attack + INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleCatapultAttacked, *this); + INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleCatapultAttacked, *this); +} + CGameState* CPackForClient::GS( CClient *cl ) { return cl->gs; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 118ac93fe..475c02666 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2473,6 +2473,24 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) return ret + manaReduction; } +int BattleInfo::hexToWallPart(int hex) +{ + if(siege == 0) //there is no battle! + return -1; + + static const std::pair attackable[] = //potentially attackable parts of wall + {std::make_pair(50, 0), std::make_pair(182, 1), std::make_pair(165, 2), std::make_pair(130, 3), + std::make_pair(62, 4), std::make_pair(29, 5), std::make_pair(12, 6), std::make_pair(95, 7), std::make_pair(96, 7)}; + + for(int g = 0; g < ARRAY_COUNT(attackable); ++g) + { + if(attackable[g].first == hex) + return attackable[g].second; + } + + return -1; //not found! +} + CStack * BattleInfo::getNextStack() { CStack *current = getStack(activeStack); diff --git a/lib/CGameState.h b/lib/CGameState.h index 1cae2c4c1..3094b6e29 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -108,7 +108,7 @@ struct DLL_EXPORT CObstacleInstance //only for use in BattleInfo struct DLL_EXPORT SiegeInfo { - ui8 wallState[7]; //[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; 1 - intact, 2 - damaged, 3 - destroyed + ui8 wallState[8]; //[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; 1 - intact, 2 - damaged, 3 - destroyed template void serialize(Handler &h, const int version) { @@ -155,6 +155,7 @@ struct DLL_EXPORT BattleInfo static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster); CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position); //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster); //returns cost of given spell + int hexToWallPart(int hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found }; class DLL_EXPORT CStack diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 94c1a2714..7250bf41f 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -992,6 +992,22 @@ struct ObstaclesRemoved : public CPackForClient //3014 } }; +struct CatapultAttack : public CPackForClient //3015 +{ + CatapultAttack(){type = 3015;} + + DLL_EXPORT void applyGs(CGameState *gs); + void applyCl(CClient *cl); + + ui8 attackedPartOfWall;//[0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; + ui8 damageDealt; + + template void serialize(Handler &h, const int version) + { + h & attackedPartOfWall & damageDealt; + } +}; + struct ShowInInfobox : public CPackForClient //107 { ShowInInfobox(){type = 107;}; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index a0ab71391..507d47fc8 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1068,6 +1068,15 @@ DLL_EXPORT void ObstaclesRemoved::applyGs( CGameState *gs ) } } +DLL_EXPORT void CatapultAttack::applyGs( CGameState *gs ) +{ + if(gs->curB && gs->curB->siege != 0) //if there is a battle and it's a siege + { + gs->curB->si.wallState[attackedPartOfWall] = + std::min( gs->curB->si.wallState[attackedPartOfWall] + damageDealt, 3 ); + } +} + DLL_EXPORT void YourTurn::applyGs( CGameState *gs ) { gs->currentPlayer = player; diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index d7907c748..9c75710a8 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -106,6 +106,7 @@ void registerTypes2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9c986fee1..720d2f4e4 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2478,6 +2478,69 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) sendAndApply(&EndAction()); break; } + case 9: //catapult + { + const CGHeroInstance * attackingHero = (ba.side) ? gs->getHero(gs->curB->hero2) : gs->getHero(gs->curB->hero1); + CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(20)]; //artillery + + int attackedPart = gs->curB->hexToWallPart(ba.destinationTile); + if(attackedPart == -1) + { + complain("catapult tried to attack non-catapultable hex!"); + break; + } + for(int g=0; gcurB->si.wallState[attackedPart] == 3) //it's not destroyed + continue; + + CatapultAttack ca; //package for clients + ca.attackedPartOfWall = attackedPart; + ca.damageDealt = 0; + + int chanceForHit = 0; + int dmgChance[3] = {sbi.noDmg, sbi.oneDmg, sbi.twoDmg}; //dmgChance[i] - chance for doing i dmg when hit is successful + switch(attackedPart) + { + case 0: //keep + chanceForHit = sbi.keep; + break; + case 1: //bottom tower + case 6: //upper tower + chanceForHit = sbi.tower; + break; + case 2: //bottom wall + case 3: //below gate + case 4: //over gate + case 5: //upper wall + chanceForHit = sbi.wall; + break; + case 7: //gate + chanceForHit = sbi.gate; + break; + } + + if(rand()%100 >= chanceForHit) //hit is successful + { + int dmgRand = rand()%100; + //accumulating dmgChance + dmgChance[1] += dmgChance[0]; + dmgChance[2] += dmgChance[1]; + //calculating dealt damage + for(int v = 0; v < ARRAY_COUNT(dmgChance); ++v) + { + if(dmgRand <= dmgChance[v]) + { + ca.damageDealt = v; + break; + } + } + } + + sendAndApply(&ca); + } + break; + } } battleMadeAction.setn(true); return ok;