Ver Fonte

* next part of sieges

mateuszb há 16 anos atrás
pai
commit
05b0d82769

+ 2 - 0
CGameInterface.h

@@ -41,6 +41,7 @@ struct HeroBonus;
 struct PackageApplied;
 struct SetObjectProperty;
 struct CatapultAttack;
+struct BattleStacksRemoved;
 class CLoadFile;
 class CSaveFile;
 template <typename Serializer> class CISer;
@@ -123,6 +124,7 @@ public:
 	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
+	virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield
 };
 class CAIHandler
 {

+ 37 - 25
client/CBattleInterface.cpp

@@ -109,8 +109,10 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 			SDL_Surface * moat = BitmapHandler::loadBitmap( siegeH->getSiegeName(13) ),
 				* mlip = BitmapHandler::loadBitmap( siegeH->getSiegeName(14) );
 
-			blitAt(moat, 410, background->h - moat->h, background);
-			blitAt(mlip, 410, background->h - mlip->h, background);
+			if(moat) //eg. tower has no moat
+				blitAt(moat, 410, background->h - moat->h, background);
+			if(mlip) //eg. tower has no mlip
+				blitAt(mlip, 410, background->h - mlip->h, background);
 
 			SDL_FreeSurface(moat);
 			SDL_FreeSurface(mlip);
@@ -211,9 +213,10 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	//loading projectiles for units
 	for(std::map<int, CStack>::iterator g = stacks.begin(); g != stacks.end(); ++g)
 	{
-		if(g->second.creature->isShooting() && CGI->creh->idToProjectile[g->second.creature->idNumber] != std::string())
-		{
-			idToProjectile[g->second.creature->idNumber] = CDefHandler::giveDef(CGI->creh->idToProjectile[g->second.creature->idNumber]);
+		int creID = (g->second.creature->idNumber == 149) ? CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] : g->second.creature->idNumber; //id of creature whose shots should be loaded
+		if(g->second.creature->isShooting() && CGI->creh->idToProjectile[creID] != std::string())
+		{	
+			idToProjectile[g->second.creature->idNumber] = CDefHandler::giveDef(CGI->creh->idToProjectile[creID]);
 
 			if(idToProjectile[g->second.creature->idNumber]->ourImages.size() > 2) //add symmetric images
 			{
@@ -1115,21 +1118,23 @@ void CBattleInterface::newStack(int stackID)
 
 	if(newStack->position < 0) //turret
 	{
-		static const int townToTurretAnim[] = {2, 18, 34, 44, 64, 76, 88, 100, 127};
+		const CCreature & turretCreature = CGI->creh->creatures[ CGI->creh->factionToTurretCreature[siegeH->town->town->typeID] ];
+
+		int xShift = turretCreature.isDoubleWide() ? 44 : 0;
 
 		switch(newStack->position)
 		{
 		case -2: //keep
-			coords = std::make_pair(505, -66);
+			coords = std::make_pair(505 + xShift, -66);
 			break;
 		case -3: //lower turret
-			coords = std::make_pair(368, 304);
+			coords = std::make_pair(368 + xShift, 304);
 			break;
 		case -4: //upper turret
-			coords = std::make_pair(339, -192);
+			coords = std::make_pair(339 + xShift, -192);
 			break;	
 		}
-		creAnims[stackID] = new CCreatureAnimation(CGI->creh->creatures[ townToTurretAnim[siegeH->town->town->typeID] ].animDefName);	
+		creAnims[stackID] = new CCreatureAnimation(turretCreature.animDefName);	
 	}
 	else
 	{
@@ -1145,6 +1150,7 @@ void CBattleInterface::stackRemoved(int stackID)
 {
 	delete creAnims[stackID];
 	creAnims.erase(stackID);
+	creDir.erase(stackID);
 }
 
 void CBattleInterface::stackActivated(int number)
@@ -1666,7 +1672,10 @@ void CBattleInterface::handleEndOfMove(int stackNumber, int destinationTile)
 
 void CBattleInterface::hexLclicked(int whichOne)
 {
-	if((whichOne%BFIELD_WIDTH)!=0 && (whichOne%BFIELD_WIDTH)!=(BFIELD_WIDTH-1)) //if player is trying to attack enemey unit or move creature stack
+	const CStack * actSt = LOCPLINT->cb->battleGetStackByID(activeStack);
+	if( ((whichOne%BFIELD_WIDTH)!=0 && (whichOne%BFIELD_WIDTH)!=(BFIELD_WIDTH-1)) //if player is trying to attack enemey unit or move creature stack
+		|| (actSt->hasFeatureOfType(StackFeature::CATAPULT) && !spellDestSelectMode )
+		)
 	{
 		if(!myTurn)
 			return; //we are not permit to do anything
@@ -2541,20 +2550,24 @@ void CBattleInterface::showPieceOfWall(SDL_Surface * to, int hex, const std::map
 					break;
 				}
 			}
-			showAliveStack(ID, stacks, to);
-			//blitting creature cover
-			switch(posToSeek)
+			if(ID != -1)
 			{
-			case -3: //bottom turret
-				siegeH->printPartOfWall(to, 16);
-				break;
-			case -4: //upper turret
-				siegeH->printPartOfWall(to, 17);
-				break;
-			case -2: //keep
-				siegeH->printPartOfWall(to, 15);
-				break;
+				showAliveStack(ID, stacks, to);
+				//blitting creature cover
+				switch(posToSeek)
+				{
+				case -3: //bottom turret
+					siegeH->printPartOfWall(to, 16);
+					break;
+				case -4: //upper turret
+					siegeH->printPartOfWall(to, 17);
+					break;
+				case -2: //keep
+					siegeH->printPartOfWall(to, 15);
+					break;
+				}
 			}
+
 		}
 	}
 
@@ -3340,8 +3353,7 @@ std::string CBattleInterface::SiegeHelper::getSiegeName(ui16 what, ui16 additInf
 {
 	if(what == 2 || what == 3 || what == 8)
 	{
-		if(additInfo == 2) additInfo = 1;
-		else if(additInfo == 3) additInfo = 2;
+		if(additInfo == 3) additInfo = 2;
 	}
 	char buf[100];
 	SDL_itoa(additInfo, buf, 10);

+ 8 - 0
client/CPlayerInterface.cpp

@@ -1003,6 +1003,14 @@ void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
 		battleInt->siegeH->getSiegeName(ca.attackedPartOfWall + 2, cb->battleGetWallState(ca.attackedPartOfWall)) );
 }
 
+void CPlayerInterface::battleStacksRemoved(const BattleStacksRemoved & bsr)
+{
+	for(std::set<ui32>::const_iterator it = bsr.stackIDs.begin(); it != bsr.stackIDs.end(); ++it) //for each removed stack
+	{
+		battleInt->stackRemoved(*it);
+	}
+}
+
 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

@@ -182,6 +182,7 @@ public:
 	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 battleStacksRemoved(const BattleStacksRemoved & bsr); //called when certain stack is completely removed from battlefield
 
 	//-------------//
 	void heroKilled(const CGHeroInstance* hero);

+ 7 - 0
client/NetPacksClient.cpp

@@ -476,6 +476,13 @@ void CatapultAttack::applyCl( CClient *cl )
 	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleCatapultAttacked, *this);
 }
 
+void BattleStacksRemoved::applyCl( CClient *cl )
+{
+	//inform interfaces about removed stacks
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side1, battleStacksRemoved, *this);
+	INTERFACE_CALL_IF_PRESENT(GS(cl)->curB->side2, battleStacksRemoved, *this);
+}
+
 CGameState* CPackForClient::GS( CClient *cl )
 {
 	return cl->gs;

+ 1 - 0
config/cr_abils.txt

@@ -165,6 +165,7 @@
 + 144 FULL_HP_REGENERATION 0 0 0 			//troll
 + 147 NOT_ACTIVE 0 0 0	   	 		//First Aid Tent //TODO: remove when support is added
 + 148 NOT_ACTIVE 0 0 0				//Ammo Cart
++ 149 SHOOTER 0 0 0					//arrow turret
 -  46 FLYING		  				//hell hound doesn't fly
 -  47 FLYING			  			//cerberus doesn't fly
 - 120 DOUBLE_WIDE					//psychic elemental

+ 11 - 0
config/cr_to_turret.txt

@@ -0,0 +1,11 @@
+//describes_which_creature's_animation_should_be_used_to_dispaly_creature_in_turret_while_siege
+//i-th_line_-_i-th_faction
+2
+18
+34
+44
+64
+76
+88
+100
+127

+ 12 - 0
hch/CCreatureHandler.cpp

@@ -525,6 +525,18 @@ void CCreatureHandler::loadCreatures()
 		idToProjectileSpin[id] = spin;
 	}
 	inp2.close();
+
+	//reading factionToTurretCreature
+
+	tlog5 << "\t\tReading config/cr_to_turret.txt" << std::endl;
+	std::ifstream inp3("config" PATHSEPARATOR "cr_to_turret.txt", std::ios::in | std::ios::binary); //this file is not in lod
+	std::string dump2;
+	inp3 >> dump2 >> dump2;
+	for(int g=0; g<F_NUMBER; ++g)
+	{
+		inp3 >> factionToTurretCreature[g];
+	}
+	inp3.close();
 }
 
 void CCreatureHandler::loadAnimationInfo()

+ 3 - 2
hch/CCreatureHandler.h

@@ -102,7 +102,8 @@ public:
 	std::map<std::string,int> nameToID;
 	std::map<int,std::string> idToProjectile;
 	std::map<int,bool> idToProjectileSpin; //if true, appropriate projectile is spinning during flight
-	std::vector<bool> factionAlignments; //1 for good, 0 for neutral and -1 for evil with faction ID as index
+	std::vector<si8> factionAlignments; //1 for good, 0 for neutral and -1 for evil with faction ID as index
+	int factionToTurretCreature[F_NUMBER]; //which creature's animation should be used to dispaly creature in turret while siege
 
 	void loadCreatures();
 	void loadAnimationInfo();
@@ -117,7 +118,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		//TODO: should be optimized, not all these informations needs to be serialized (same for ccreature)
-		h & notUsedMonsters & creatures & nameToID & idToProjectile & idToProjectileSpin;
+		h & notUsedMonsters & creatures & nameToID & idToProjectile & idToProjectileSpin & factionToTurretCreature;
 
 		if(!h.saving)
 		{

+ 13 - 3
lib/CGameState.cpp

@@ -1564,6 +1564,16 @@ int CGameState::battleGetBattlefieldType(int3 tile)
 	}
 }
 
+const CGHeroInstance * CGameState::battleGetOwner(int stackID)
+{
+	if(!curB)
+		return NULL;
+
+	si32 ourHero = curB->getStack(stackID)->attackerOwned ? curB->hero1 : curB->hero2;
+	return getHero(ourHero);
+
+}
+
 UpgradeInfo CGameState::getUpgradeInfo(CArmedInstance *obj, int stackPos)
 {
 	UpgradeInfo ret;
@@ -2133,7 +2143,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
 		}
 	}
 
-	if(attacker->hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //any siege weapon, but only ballista can attack
+	if(attacker->hasFeatureOfType(StackFeature::SIEGE_WEAPON) && attacker->creature->idNumber != 149) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
 	{ //minDmg and maxDmg are multiplied by hero attack + 1
 		minDmg *= attackerHero->getPrimSkillLevel(0) + 1; 
 		maxDmg *= attackerHero->getPrimSkillLevel(0) + 1; 
@@ -2599,7 +2609,7 @@ bool CGameState::battleCanShoot(int ID, int dest)
 
 	if(!our || !dst) return false;
 
-	int ourHero = our->attackerOwned ? curB->hero1 : curB->hero2;
+	const CGHeroInstance * ourHero = battleGetOwner(our->ID);
 
 	if(our->hasFeatureOfType(StackFeature::FORGETFULL)) //forgetfulness
 		return false;
@@ -2608,7 +2618,7 @@ bool CGameState::battleCanShoot(int ID, int dest)
 		&& our->owner != dst->owner
 		&& dst->alive()
 		&& (!curB->isStackBlocked(ID) || 
-			( getHero(ourHero) && getHero(ourHero)->hasBonusOfType(HeroBonus::FREE_SHOOTING) ) )
+			( ourHero && ourHero->hasBonusOfType(HeroBonus::FREE_SHOOTING) ) )
 		&& our->shots
 		)
 		return true;

+ 1 - 0
lib/CGameState.h

@@ -330,6 +330,7 @@ public:
 	bool battleCanFlee(int player); //returns true if player can flee from the battle
 	int battleGetStack(int pos, bool onlyAlive); //returns ID of stack at given tile
 	int battleGetBattlefieldType(int3 tile = int3());//   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
+	const CGHeroInstance * battleGetOwner(int stackID); //returns hero that owns given stack; NULL if none
 	si8 battleMaxSpellLevel(); //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned
 	bool battleCanShoot(int ID, int dest); //determines if stack with given ID shoot at the selected destination
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);

+ 15 - 0
lib/NetPacks.h

@@ -1008,6 +1008,21 @@ struct CatapultAttack : public CPackForClient //3015
 	}
 };
 
+struct BattleStacksRemoved : public CPackForClient //3016
+{
+	BattleStacksRemoved(){type = 3016;}
+
+	DLL_EXPORT void applyGs(CGameState *gs);
+	void applyCl(CClient *cl);
+
+	std::set<ui32> stackIDs; //IDs of removed stacks
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & stackIDs;
+	}
+};
+
 struct ShowInInfobox : public CPackForClient //107
 {
 	ShowInInfobox(){type = 107;};

+ 18 - 0
lib/NetPacksLib.cpp

@@ -1080,6 +1080,24 @@ DLL_EXPORT void CatapultAttack::applyGs( CGameState *gs )
 	}
 }
 
+DLL_EXPORT void BattleStacksRemoved::applyGs( CGameState *gs )
+{
+	if(!gs->curB)
+		return;
+
+	for(std::set<ui32>::const_iterator it = stackIDs.begin(); it != stackIDs.end(); ++it) //for each removed stack
+	{
+		for(int b=0; b<gs->curB->stacks.size(); ++b) //find it in vector of stacks
+		{
+			if(gs->curB->stacks[b]->ID == *it) //if found
+			{
+				gs->curB->stacks.erase(gs->curB->stacks.begin() + b); //remove
+				break;
+			}
+		}
+	}
+}
+
 DLL_EXPORT void YourTurn::applyGs( CGameState *gs )
 {
 	gs->currentPlayer = player;

+ 1 - 0
lib/RegisterTypes.cpp

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

+ 55 - 3
server/CGameHandler.cpp

@@ -396,6 +396,30 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 				continue;
 			}
 
+			const CGHeroInstance * curOwner = gs->battleGetOwner(next->ID);
+
+			if(next->position < 0 && (!curOwner || curOwner->getSecSkillLevel(10) == 0)) //arrow turret, hero has no ballistics
+			{
+				BattleAction attack;
+				attack.actionType = 7;
+				attack.side = !next->attackerOwned;
+				attack.stackNumber = next->ID;
+
+				for(int g=0; g<gs->curB->stacks.size(); ++g)
+				{
+					if(gs->curB->stacks[g]->attackerOwned)
+					{
+						attack.destinationTile = gs->curB->stacks[g]->position;
+						break;
+					}
+				}
+
+				makeBattleAction(attack);
+
+				checkForBattleEnd(stacks);
+				continue;
+			}
+
 askInterfaceForMove:
 			//ask interface and wait for answer
 			if(!battleResult.get())
@@ -536,8 +560,8 @@ void CGameHandler::prepareAttack(BattleAttack &bat, CStack *att, CStack *def, in
 		BattleStackAttacked *bsa = &*i;
 	#endif
 
-	bsa->stackAttacked = def->ID;
-	bsa->damageAmount = BattleInfo::calculateDmg(att, def, gs->getHero(att->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), gs->getHero(def->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), bat.shot(), distance);//counting dealt damage
+		bsa->stackAttacked = def->ID;
+	bsa->damageAmount = BattleInfo::calculateDmg(att, def, gs->battleGetOwner(att->ID), gs->battleGetOwner(def->ID), bat.shot(), distance);//counting dealt damage
 	if(att->Luck() > 0  &&  rand()%24 < att->Luck())
 	{
 		bsa->damageAmount *= 2;
@@ -2552,7 +2576,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 					break;
 				}
 
-				if(rand()%100 >= chanceForHit) //hit is successful
+				if(rand()%100 <= chanceForHit) //hit is successful
 				{
 					int dmgRand = rand()%100;
 					//accumulating dmgChance
@@ -2567,6 +2591,34 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 							break;
 						}
 					}
+					if(ca.damageDealt > 0 && (attackedPart == 0 || attackedPart == 1 || attackedPart == 6))
+					{
+						int posRemove = -1;
+						switch(attackedPart)
+						{
+						case 0: //keep
+							posRemove = -2;
+							break;
+						case 1: //bottom tower
+							posRemove = -3;
+							break;
+						case 6: //upper tower
+							posRemove = -4;
+							break;
+						}
+
+						BattleStacksRemoved bsr;
+						for(int g=0; g<gs->curB->stacks.size(); ++g)
+						{
+							if(gs->curB->stacks[g]->position == posRemove)
+							{
+								bsr.stackIDs.insert( gs->curB->stacks[g]->ID );
+								break;
+							}
+						}
+
+						sendAndApply(&bsr);
+					}
 				}
 
 				sendAndApply(&ca);