2
0
mateuszb 16 жил өмнө
parent
commit
d25a5a795e

+ 9 - 0
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<ui32, ui32> CCallback::battleEstimateDamage(int attackerID, int defenderID)
 {
 	if(!gs->curB)

+ 2 - 0
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<ui32, ui32> 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 <min dmg, max dmg>
 };
 
@@ -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<ui32, ui32> 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 <min dmg, max dmg>
 
 //XXX hmmm _tmain on _GNUC_ wtf?

+ 2 - 0
CGameInterface.h

@@ -40,6 +40,7 @@ struct SetStackEffect;
 struct HeroBonus;
 struct PackageApplied;
 struct SetObjectProperty;
+struct CatapultAttack;
 class CLoadFile;
 class CSaveFile;
 template <typename Serializer> class CISer;
@@ -121,6 +122,7 @@ public:
 	virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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<si32> & 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
 {

+ 32 - 9
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; g<ARRAY_COUNT(walls); ++g)
 	{
-		walls[g] = BitmapHandler::loadBitmap( getSiegeName(g + 2) );
+		walls[g] = BitmapHandler::loadBitmap( getSiegeName(g) );
 	}
 }
 
 CBattleInterface::SiegeHelper::~SiegeHelper()
 {
-	if(backWall)
-		SDL_FreeSurface(backWall);
-
 	for(int g=0; g<ARRAY_COUNT(walls); ++g)
 	{
 		SDL_FreeSurface(walls[g]);
@@ -3240,6 +3258,11 @@ CBattleInterface::SiegeHelper::~SiegeHelper()
 
 std::string CBattleInterface::SiegeHelper::getSiegeName(ui16 what, ui16 additInfo) const
 {
+	if(what == 2 || what == 3 || what == 8)
+	{
+		if(additInfo == 2) additInfo = 1;
+		else if(additInfo == 3) additInfo = 2;
+	}
 	char buf[100];
 	SDL_itoa(additInfo, buf, 10);
 	std::string addit(buf);
@@ -3292,10 +3315,10 @@ void CBattleInterface::SiegeHelper::printPartOfWall(SDL_Surface * to, int what)
 	switch(what)
 	{
 	case 1: //background wall
-		pos = Point(owner->pos.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);
 	}
 }

+ 4 - 2
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

+ 7 - 0
client/CPlayerInterface.cpp

@@ -996,6 +996,13 @@ void CPlayerInterface::battleObstaclesRemoved(const std::set<si32> & 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<boost::recursive_mutex> un(*pim);

+ 1 - 0
client/CPlayerInterface.h

@@ -181,6 +181,7 @@ public:
 	void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & 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<si32> & 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);

+ 7 - 0
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;

+ 18 - 0
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<int, int> 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);

+ 2 - 1
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 <typename Handler> 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

+ 16 - 0
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 <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & attackedPartOfWall & damageDealt;
+	}
+};
+
 struct ShowInInfobox : public CPackForClient //107
 {
 	ShowInInfobox(){type = 107;};

+ 9 - 0
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;

+ 1 - 0
lib/RegisterTypes.cpp

@@ -106,6 +106,7 @@ void registerTypes2(Serializer &s)
 	s.template registerType<BattleResultsApplied>();
 	s.template registerType<StacksHealedOrResurrected>();
 	s.template registerType<ObstaclesRemoved>();
+	s.template registerType<CatapultAttack>();
 	s.template registerType<ShowInInfobox>();
 	s.template registerType<OpenWindow>();
 	s.template registerType<NewObject>();

+ 63 - 0
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; g<sbi.shots; ++g)
+			{
+				if(gs->curB->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;