Browse Source

* partial support for arrow turrets

mateuszb 16 years ago
parent
commit
a4df8e8831
7 changed files with 199 additions and 73 deletions
  1. 9 0
      AI/GeniusAI/BattleLogic.cpp
  2. 4 23
      CCallback.cpp
  3. 110 26
      client/CBattleInterface.cpp
  4. 4 4
      client/CBattleInterface.h
  5. 51 6
      lib/CGameState.cpp
  6. 2 1
      lib/CGameState.h
  7. 19 13
      server/CGameHandler.cpp

+ 9 - 0
AI/GeniusAI/BattleLogic.cpp

@@ -71,6 +71,10 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 	typedef std::map<int, CStack> map_stacks;
 	map_stacks allStacks = m_cb->battleGetStacks();
 	const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
+	if(currentStack->position < 0) //turret
+	{
+		return;
+	}
 	/*
 	// find all creatures belong to the enemy
 	std::for_each(allStacks.begin(), allStacks.end(),
@@ -249,6 +253,11 @@ void CBattleLogic::MakeStatistics(int currentCreatureId)
 
 BattleAction CBattleLogic::MakeDecision(int stackID)
 {
+	const CStack *currentStack = m_cb->battleGetStackByID(stackID);
+	if(currentStack->position < 0) //turret
+	{
+		return MakeDefend(stackID);
+	}
 	MakeStatistics(stackID);
 
 	list<int> creatures;

+ 4 - 23
CCallback.cpp

@@ -555,34 +555,15 @@ bool CCallback::battleIsStackMine(int ID)
 			return gs->curB->stacks[h]->owner == player;
 	}
 	return false;
-}	
+}
+
 bool CCallback::battleCanShoot(int ID, int dest)
 {
 	boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
-	const CStack *our = battleGetStackByID(ID), *dst = battleGetStackByPos(dest);
-
-	if(!our || !dst || !gs->curB) return false;
-	
-	int ourHero = our->attackerOwned ? gs->curB->hero1 : gs->curB->hero2;
-
-	//for(size_t g=0; g<our->effects.size(); ++g)
-	//{
-	//	if(61 == our->effects[g].id) //forgetfulness
-	//		return false;
-	//}
-	if(our->hasFeatureOfType(StackFeature::FORGETFULL)) //forgetfulness
-		return false;
 
+	if(!gs->curB) return false;
 
-	if(our->hasFeatureOfType(StackFeature::SHOOTER)//it's shooter
-		&& our->owner != dst->owner
-		&& dst->alive()
-		&& (!gs->curB->isStackBlocked(ID) || 
-			( gs->getHero(ourHero) && gs->getHero(ourHero)->hasBonusOfType(HeroBonus::FREE_SHOOTING) ) )
-		&& our->shots
-		)
-		return true;
-	return false;
+	return gs->battleCanShoot(ID, dest);
 }
 
 bool CCallback::battleCanCastSpell()

+ 110 - 26
client/CBattleInterface.cpp

@@ -81,6 +81,14 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	pos = myRect;
 	strongInterest = true;
 	givenCommand = new CondSh<BattleAction *>(NULL);
+	
+	//preparing siege info
+	const CGTownInstance * town = LOCPLINT->cb->battleGetDefendedTown();
+	if(town)
+	{
+		siegeH = new SiegeHelper(town, this);
+	}
+
 	//initializing armies
 	this->army1 = army1;
 	this->army2 = army2;
@@ -90,14 +98,6 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 		newStack(b->second.ID);
 	}
 
-	
-	//preparing siege info
-	const CGTownInstance * town = LOCPLINT->cb->battleGetDefendedTown();
-	if(town)
-	{
-		siegeH = new SiegeHelper(town, this);
-	}
-
 	//preparing menu background and terrain
 	if(siegeH)
 	{
@@ -204,7 +204,8 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	//locking occupied positions on batlefield
 	for(std::map<int, CStack>::iterator it = stacks.begin(); it!=stacks.end(); ++it) //stacks gained at top of this function
 	{
-		bfield[it->second.position].accesible = false;
+		if(it->second.position >= 0) //turrets have position < 0
+			bfield[it->second.position].accesible = false;
 	}
 
 	//loading projectiles for units
@@ -460,10 +461,6 @@ void CBattleInterface::show(SDL_Surface * to)
 	
 	SDL_SetClipRect(to, &buf); //restoring previous clip_rect
 
-	//showing menu background and console
-	blitAt(menu, pos.x, 556 + pos.y, to);
-	console->show(to);
-
 	//showing buttons
 	bOptions->show(to);
 	bSurrender->show(to);
@@ -501,7 +498,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	//double loop because dead stacks should be printed first
 	for(std::map<int, CStack>::iterator j=stacks.begin(); j!=stacks.end(); ++j)
 	{
-		if(j->second.alive())
+		if(j->second.alive() && j->second.position >= 0) //don't show turrets here
 			stackAliveByHex[j->second.position].push_back(j->second.ID);
 	}
 	std::vector<int> stackDeadByHex[BFIELD_SIZE];
@@ -529,7 +526,7 @@ void CBattleInterface::show(SDL_Surface * to)
 			showAliveStack(stackAliveByHex[b][v], stacks, to);
 		}
 
-		showPieceOfWall(to, b);
+		showPieceOfWall(to, b, stacks);
 	}
 	//units shown
 	projectileShowHelper(to);//showing projectiles
@@ -608,13 +605,17 @@ void CBattleInterface::show(SDL_Surface * to)
 
 	SDL_SetClipRect(to, &buf); //restoring previous clip_rect
 
+	//showing menu background and console
+	blitAt(menu, pos.x, 556 + pos.y, to);
+	console->show(to);
+
 	//showing window with result of battle
 	if(resWindow)
 	{
 		resWindow->show(to);
 	}
 
-	//showing in-gmae console
+	//showing in-game console
 	LOCPLINT->cingconsole->show(to);
 
 	//printing border around interface
@@ -1110,11 +1111,34 @@ void CBattleInterface::newStack(int stackID)
 {
 	const CStack * newStack = LOCPLINT->cb->battleGetStackByID(stackID);
 
-	std::pair <int, int> coords = CBattleHex::getXYUnitAnim(newStack->position, newStack->owner == attackingHeroInstance->tempOwner, newStack);
-	creAnims[stackID] = (new CCreatureAnimation(newStack->creature->animDefName));
+	std::pair <int, int> coords;
+
+	if(newStack->position < 0) //turret
+	{
+		static const int townToTurretAnim[] = {2, 18, 34, 44, 64, 76, 88, 100, 127};
+
+		switch(newStack->position)
+		{
+		case -2: //keep
+			coords = std::make_pair(505, -66);
+			break;
+		case -3: //lower turret
+			coords = std::make_pair(368, 304);
+			break;
+		case -4: //upper turret
+			coords = std::make_pair(339, -192);
+			break;	
+		}
+		creAnims[stackID] = new CCreatureAnimation(CGI->creh->creatures[ townToTurretAnim[siegeH->town->town->typeID] ].animDefName);	
+	}
+	else
+	{
+		coords = CBattleHex::getXYUnitAnim(newStack->position, newStack->owner == attackingHeroInstance->tempOwner, newStack);
+		creAnims[stackID] = new CCreatureAnimation(newStack->creature->animDefName);	
+	}
 	creAnims[stackID]->setType(2);
 	creAnims[stackID]->pos = genRect(creAnims[newStack->ID]->fullHeight, creAnims[newStack->ID]->fullWidth, coords.first, coords.second);
-	creDir[stackID] = newStack->owner == attackingHeroInstance->tempOwner;
+	creDir[stackID] = newStack->attackerOwned;
 }
 
 void CBattleInterface::stackRemoved(int stackID)
@@ -2473,24 +2497,67 @@ void CBattleInterface::showAliveStack(int ID, const std::map<int, CStack> & stac
 	}
 }
 
-void CBattleInterface::showPieceOfWall(SDL_Surface * to, int hex)
+void CBattleInterface::showPieceOfWall(SDL_Surface * to, int hex, const std::map<int, CStack> & stacks)
 {
 	if(!siegeH)
 		return;
 
-	static const std::map<int, int> hexToPart = boost::assign::map_list_of(12, 8)(16, 1)(29, 7)(50, 2)(62, 12)(78, 6)(112, 10)(147, 5)(165, 11)(182, 4);
+	static const std::map<int, int> hexToPart = boost::assign::map_list_of(12, 8)(16, 1)(29, 7)(50, 2)(62, 12)(78, 6)(112, 10)(147, 5)(165, 11)(182, 3)(186, 0);
+	
+	//additionally print bottom wall
+	if(hex == 182)
+	{
+		siegeH->printPartOfWall(to, 4);
+	}
 
 	std::map<int, int>::const_iterator it = hexToPart.find(hex);
 	if(it != hexToPart.end())
 	{
 		siegeH->printPartOfWall(to, it->second);
-	}
 
-	//additionally print lower tower
-	if(hex == 182)
-	{
-		siegeH->printPartOfWall(to, 3);
+		//print creature in turret
+		int posToSeek = -1;
+		switch(it->second)
+		{
+		case 3: //bottom turret
+			posToSeek = -3;
+			break;
+		case 8: //upper turret
+			posToSeek = -4;
+			break;
+		case 2: //keep
+			posToSeek = -2;
+			break;
+		}
+
+		if(posToSeek != -1)
+		{
+			int ID = -1;
+			for(std::map<int, CStack>::const_iterator it = stacks.begin(); it != stacks.end(); ++it)
+			{
+				if(it->second.position == posToSeek)
+				{
+					ID = it->second.ID;
+					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;
+			}
+		}
 	}
+
 }
 
 void CBattleInterface::redrawBackgroundWithHexes(int activeStack)
@@ -3321,6 +3388,12 @@ std::string CBattleInterface::SiegeHelper::getSiegeName(ui16 what, ui16 additInf
 		return "SG" + townTypeInfixes[town->town->typeID] + "MOAT.BMP";
 	case 14: //mlip
 		return "SG" + townTypeInfixes[town->town->typeID] + "MLIP.BMP";
+	case 15: //keep creature cover
+		return "SG" + townTypeInfixes[town->town->typeID] + "MANC.BMP";
+	case 16: //bottom turret creature cover
+		return "SG" + townTypeInfixes[town->town->typeID] + "TW1C.BMP";
+	case 17: //upper turret creature cover
+		return "SG" + townTypeInfixes[town->town->typeID] + "TW2C.BMP";
 	default:
 		return "";
 	}
@@ -3350,6 +3423,17 @@ void CBattleInterface::SiegeHelper::printPartOfWall(SDL_Surface * to, int what)
 		pos.x = CGI->heroh->wallPositions[town->town->typeID][what - 3].first + owner->pos.x;
 		pos.y = CGI->heroh->wallPositions[town->town->typeID][what - 3].second + owner->pos.y;
 		break;
+	case 15: //keep creature cover
+		pos = Point(owner->pos.w + owner->pos.x - walls[2]->w, 154 + owner->pos.y);
+		break;
+	case 16: //bottom turret creature cover
+		pos.x = CGI->heroh->wallPositions[town->town->typeID][0].first + owner->pos.x;
+		pos.y = CGI->heroh->wallPositions[town->town->typeID][0].second + owner->pos.y;
+		break;
+	case 17: //upper turret creature cover
+		pos.x = CGI->heroh->wallPositions[town->town->typeID][5].first + owner->pos.x;
+		pos.y = CGI->heroh->wallPositions[town->town->typeID][5].second + owner->pos.y;
+		break;
 	};
 
 	if(pos.x != -1)

+ 4 - 4
client/CBattleInterface.h

@@ -198,7 +198,7 @@ private:
 	} * attackingInfo;
 	void attackingShowHelper();
 	void showAliveStack(int ID, const std::map<int, CStack> & stacks, SDL_Surface * to); //helper function for function show
-	void showPieceOfWall(SDL_Surface * to, int hex); //helper function for show
+	void showPieceOfWall(SDL_Surface * to, int hex, const std::map<int, CStack> & stacks); //helper function for show
 	void redrawBackgroundWithHexes(int activeStack);
 	void printConsoleAttacked(int ID, int dmg, int killed, int IDby);
 
@@ -234,7 +234,7 @@ private:
 	{
 	private:
 		static std::string townTypeInfixes[F_NUMBER]; //for internal use only - to build filenames
-		SDL_Surface * walls[13];
+		SDL_Surface * walls[18];
 		const CBattleInterface * owner;
 	public:
 		const CGTownInstance * town; //besieged town
@@ -242,9 +242,9 @@ private:
 		~SiegeHelper(); //d-tor
 
 		//filename getters
-		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, 13 - moat, 14 - mlip; additInfo: 1 - intact, 2 - damaged, 3 - destroyed
+		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, 13 - moat, 14 - mlip, 15 - keep creature cover, 16 - bottom turret creature cover, 17 - upper turret creature cover; 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
+		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, 15 - keep creature cover, 16 - bottom turret creature cover, 17 - upper turret creature cover
 
 		friend class CPlayerInterface;
 	} * siegeH;

+ 51 - 6
lib/CGameState.cpp

@@ -307,7 +307,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 
 	for(unsigned int g=0; g<stacks.size(); ++g)
 	{
-		if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit) //we don't want to lock position of this stack
+		if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit || stacks[g]->position < 0) //we don't want to lock position of this stack (eg. if it's a turret)
 			continue;
 
 		accessibility[stacks[g]->position] = false;
@@ -433,6 +433,10 @@ std::vector<int> BattleInfo::getAccessibility(int stackID, bool addOccupiable) c
 	std::vector<int> ret;
 	bool ac[BFIELD_SIZE];
 	const CStack *s = getStack(stackID);
+
+	if(s->position < 0) //turrets
+		return std::vector<int>();
+
 	std::set<int> occupyable;
 
 	getAccessibilityMap(ac, s->hasFeatureOfType(StackFeature::DOUBLE_WIDE), s->attackerOwned, addOccupiable, occupyable, s->hasFeatureOfType(StackFeature::FLYING), stackID);
@@ -713,7 +717,7 @@ si32 CStack::Attack() const
 
 	if(hasFeatureOfType(StackFeature::IN_FRENZY)) //frenzy for attacker
 	{
-		ret += (VLC->spellh->spells[56].powers[getEffect(56)->level]/100.0) * Defense(false);
+		ret += si32(VLC->spellh->spells[56].powers[getEffect(56)->level]/100.0) * Defense(false);
 	}
 
 	ret += valOfFeatures(StackFeature::ATTACK_BONUS);
@@ -2110,10 +2114,25 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
 }
 std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge)
 {
-	int attackDefenseBonus,
+	float attackDefenseBonus,
 		minDmg = attacker->creature->damageMin * attacker->amount, 
 		maxDmg = attacker->creature->damageMax * attacker->amount;
 
+	if(attacker->creature->idNumber == 149) //arrow turret
+	{
+		switch(attacker->position)
+		{
+		case -2: //keep
+			minDmg = 15;
+			maxDmg = 15;
+			break;
+		case -3: case -4: //turrets
+			minDmg = 7.5f;
+			maxDmg = 7.5f;
+			break;
+		}
+	}
+
 	if(attacker->hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //any siege weapon, but only ballista can attack
 	{ //minDmg and maxDmg are multiplied by hero attack + 1
 		minDmg *= attackerHero->getPrimSkillLevel(0) + 1; 
@@ -2305,16 +2324,16 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
 	if(attacker->getEffect(42)) //curse handling (rest)
 	{
 		minDmg -= VLC->spellh->spells[42].powers[attacker->getEffect(42)->level];
-		return std::make_pair(minDmg, minDmg);
+		return std::make_pair(int(minDmg), int(minDmg));
 	}
 	else if(attacker->getEffect(41)) //bless handling
 	{
 		maxDmg += VLC->spellh->spells[41].powers[attacker->getEffect(41)->level];
-		return std::make_pair(maxDmg, maxDmg);
+		return std::make_pair(int(maxDmg), int(maxDmg));
 	}
 	else
 	{
-		return std::make_pair(minDmg, maxDmg);
+		return std::make_pair(int(minDmg), int(maxDmg));
 	}
 
 	tlog1 << "We are too far in calculateDmg...\n";
@@ -2570,6 +2589,32 @@ std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closes
 	return std::make_pair<const CStack * , int>(NULL, -1);
 }
 
+bool CGameState::battleCanShoot(int ID, int dest)
+{
+	if(!curB)
+		return false;
+
+	const CStack *our = curB->getStack(ID),
+		*dst = curB->getStackT(dest);
+
+	if(!our || !dst) return false;
+
+	int ourHero = our->attackerOwned ? curB->hero1 : curB->hero2;
+
+	if(our->hasFeatureOfType(StackFeature::FORGETFULL)) //forgetfulness
+		return false;
+
+	if(our->hasFeatureOfType(StackFeature::SHOOTER)//it's shooter
+		&& our->owner != dst->owner
+		&& dst->alive()
+		&& (!curB->isStackBlocked(ID) || 
+			( getHero(ourHero) && getHero(ourHero)->hasBonusOfType(HeroBonus::FREE_SHOOTING) ) )
+		&& our->shots
+		)
+		return true;
+	return false;
+}
+
 CStack * BattleInfo::getNextStack()
 {
 	CStack *current = getStack(activeStack);

+ 2 - 1
lib/CGameState.h

@@ -170,7 +170,7 @@ public:
 	ui32 firstHPleft; //HP of first creature in stack
 	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
 	ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
-	ui16 position; //position on battlefield
+	si16 position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
 	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
 	si16 shots; //how many shots left
 	si8 morale, luck; //base stack luck/morale
@@ -331,6 +331,7 @@ public:
 	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
 	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);
 	float getMarketEfficiency(int player, int mode=0);
 	int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements

+ 19 - 13
server/CGameHandler.cpp

@@ -1047,6 +1047,24 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 		stacks.push_back(stack);
 	}
 	//war machines added
+
+	switch(curB->siege) //adding towers
+	{
+		
+	case 3: //castle
+		{//lower tower / upper tower
+			CStack * stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -4);
+			stacks.push_back(stack);
+			stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -3);
+			stacks.push_back(stack);
+		}
+	case 2: //citadel
+		{//main tower
+			CStack * stack = curB->generateNewStack(hero2, 149, 1, stacks.size(), false, 255, gs->map->terrain[tile.x][tile.y][tile.z].tertype, -2);
+			stacks.push_back(stack);
+		}
+	}
+
 	std::stable_sort(stacks.begin(),stacks.end(),cmpst);
 
 	//seting up siege
@@ -2468,19 +2486,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 		{
 			CStack *curStack = gs->curB->getStack(ba.stackNumber),
 				*destStack= gs->curB->getStackT(ba.destinationTile);
-			if(!curStack //our stack exists
-				|| !destStack //there is a stack at destination tile
-				|| !curStack->shots //stack has shots
-				|| gs->curB->isStackBlocked(curStack->ID) //we are not blocked
-				|| !curStack->hasFeatureOfType(StackFeature::SHOOTER) //our stack is shooting unit
-				)
-				break;
-			//for(int g=0; g<curStack->effects.size(); ++g)
-			//{
-			//	if(61 == curStack->effects[g].id) //forgetfulness
-			//		break;
-			//}
-			if(curStack->hasFeatureOfType(StackFeature::FORGETFULL)) //forgetfulness
+			if( !gs->battleCanShoot(ba.stackNumber, ba.destinationTile) )
 				break;
 
 			sendAndApply(&StartAction(ba)); //start shooting