mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	* canceling of casting a spell by pressing Escape or R-click (R-click on a creatures does not cancel a spell)
* new spells: - frost ring - fireball - inferno - meteor shower - death ripple - destroy undead * spellbook button is inactive when hero cannot cast any spell * obstacles will be placed more properly when resolution is different than 800x600 * minor changes
This commit is contained in:
		| @@ -344,6 +344,7 @@ void CBattleInterface::activate() | ||||
| { | ||||
| 	KeyInterested::activate(); | ||||
| 	MotionInterested::activate(); | ||||
| 	ClickableR::activate(); | ||||
| 	subInt = NULL; | ||||
| 	bOptions->activate(); | ||||
| 	bSurrender->activate(); | ||||
| @@ -370,6 +371,7 @@ void CBattleInterface::deactivate() | ||||
| { | ||||
| 	KeyInterested::deactivate(); | ||||
| 	MotionInterested::deactivate(); | ||||
| 	ClickableR::deactivate(); | ||||
| 	bOptions->deactivate(); | ||||
| 	bSurrender->deactivate(); | ||||
| 	bFlee->deactivate(); | ||||
| @@ -482,7 +484,7 @@ void CBattleInterface::show(SDL_Surface * to) | ||||
| 	bConsoleUp->show(to); | ||||
| 	bConsoleDown->show(to); | ||||
|  | ||||
| 	 | ||||
| 	//prevents blitting outside this window | ||||
| 	SDL_GetClipRect(to, &buf); | ||||
| 	SDL_SetClipRect(to, &pos); | ||||
|  | ||||
| @@ -490,9 +492,9 @@ void CBattleInterface::show(SDL_Surface * to) | ||||
| 	std::vector<CObstacleInstance> obstacles = LOCPLINT->cb->battleGetAllObstacles(); | ||||
| 	for(int b=0; b<obstacles.size(); ++b) | ||||
| 	{ | ||||
| 		int x = ((obstacles[b].pos/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(obstacles[b].pos%BFIELD_WIDTH); | ||||
| 		int y = 86 + 42 * (obstacles[b].pos/BFIELD_WIDTH); | ||||
| 		std::vector<Cimage> &images = idToObstacle[obstacles[b].ID]->ourImages; | ||||
| 		int x = ((obstacles[b].pos/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(obstacles[b].pos%BFIELD_WIDTH) + pos.x; | ||||
| 		int y = 86 + 42 * (obstacles[b].pos/BFIELD_WIDTH) + pos.y; | ||||
| 		std::vector<Cimage> &images = idToObstacle[obstacles[b].ID]->ourImages; //reference to animation of obstacle | ||||
| 		blitAt(images[((animCount+1)/(4/settings.animSpeed))%images.size()].bitmap, x, y, to); | ||||
| 	} | ||||
|  | ||||
| @@ -711,6 +713,10 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key) | ||||
| 	{ | ||||
| 		showStackQueue = key.state==SDL_PRESSED; | ||||
| 	} | ||||
| 	else if(key.keysym.sym == SDLK_ESCAPE && spellDestSelectMode) | ||||
| 	{ | ||||
| 		endCastingSpell(); | ||||
| 	} | ||||
| } | ||||
| void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) | ||||
| { | ||||
| @@ -928,6 +934,14 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBattleInterface::clickRight(boost::logic::tribool down) | ||||
| { | ||||
| 	if(!down && spellDestSelectMode) | ||||
| 	{ | ||||
| 		endCastingSpell(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool CBattleInterface::reverseCreature(int number, int hex, bool wideTrick) | ||||
| { | ||||
| 	if(creAnims[number]==NULL) | ||||
| @@ -1076,11 +1090,13 @@ void CBattleInterface::stackActivated(int number) | ||||
| 	//block cast spell button if hero doesn't have a spellbook | ||||
| 	if(attackingHeroInstance && attackingHeroInstance->tempOwner==LOCPLINT->cb->battleGetStackByID(number)->owner) | ||||
| 	{ | ||||
| 		bSpell->block(!attackingHeroInstance->getArt(17)); | ||||
| 		if(!attackingHeroInstance->getArt(17)) //don't unlock if already locked | ||||
| 			bSpell->block(!attackingHeroInstance->getArt(17)); | ||||
| 	} | ||||
| 	else if(defendingHeroInstance && defendingHeroInstance->tempOwner==LOCPLINT->cb->battleGetStackByID(number)->owner) | ||||
| 	{ | ||||
| 		bSpell->block(!defendingHeroInstance->getArt(17)); | ||||
| 		if(!defendingHeroInstance->getArt(17)) //don't unlock if already locked | ||||
| 			bSpell->block(!defendingHeroInstance->getArt(17)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1293,6 +1309,7 @@ void CBattleInterface::stacksAreAttacked(std::vector<CBattleInterface::SStackAtt | ||||
| 	{ | ||||
| 		show(screen); | ||||
| 		CSDL_Ext::update(screen); | ||||
| 		SDL_Delay(5); | ||||
| 		SDL_framerateDelay(LOCPLINT->mainFPSmng); | ||||
| 		for(size_t g=0; g<attackedInfos.size(); ++g) | ||||
| 		{ | ||||
| @@ -1492,6 +1509,9 @@ void CBattleInterface::stackAttacking(int ID, int dest) | ||||
| void CBattleInterface::newRound(int number) | ||||
| { | ||||
| 	console->addText(CGI->generaltexth->allTexts[412]); | ||||
|  | ||||
| 	//unlock spellbook | ||||
| 	bSpell->block(!LOCPLINT->cb->battleCanCastSpell()); | ||||
| } | ||||
|  | ||||
| void CBattleInterface::giveCommand(ui8 action, ui16 tile, ui32 stack, si32 additional) | ||||
| @@ -1549,10 +1569,7 @@ void CBattleInterface::hexLclicked(int whichOne) | ||||
| 			{ | ||||
| 				spellToCast->destinationTile = whichOne; | ||||
| 				LOCPLINT->cb->battleMakeAction(spellToCast); | ||||
| 				delete spellToCast; | ||||
| 				spellToCast = NULL; | ||||
| 				spellDestSelectMode = false; | ||||
| 				CGI->curh->changeGraphic(1, 6); | ||||
| 				endCastingSpell(); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| @@ -1979,6 +1996,16 @@ float CBattleInterface::getAnimSpeedMultiplier() const | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBattleInterface::endCastingSpell() | ||||
| { | ||||
| 	assert(spellDestSelectMode); | ||||
|  | ||||
| 	delete spellToCast; | ||||
| 	spellToCast = NULL; | ||||
| 	spellDestSelectMode = false; | ||||
| 	CGI->curh->changeGraphic(1, 6); | ||||
| } | ||||
|  | ||||
| void CBattleInterface::attackingShowHelper() | ||||
| { | ||||
| 	if(attackingInfo && !attackingInfo->reversing) | ||||
|   | ||||
| @@ -151,7 +151,7 @@ struct BattleSettings | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class CBattleInterface : public CMainInterface, public MotionInterested, public KeyInterested | ||||
| class CBattleInterface : public CMainInterface, public MotionInterested, public KeyInterested, public ClickableR | ||||
| { | ||||
| private: | ||||
| 	SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes; | ||||
| @@ -178,6 +178,7 @@ private: | ||||
| 	bool spellDestSelectMode; //if true, player is choosing destination for his spell | ||||
| 	int spellSelMode; //0 - any location, 1 - any firendly creature, 2 - any hostile creature, 3 - any creature, 4 - obstacle, -1 - no location | ||||
| 	BattleAction * spellToCast; //spell for which player is choosing destination | ||||
| 	void endCastingSpell(); //ends casting spell (eg. when spell has been casted or cancelled) | ||||
|  | ||||
| 	class CAttHelper | ||||
| 	{ | ||||
| @@ -259,6 +260,8 @@ public: | ||||
| 	void show(SDL_Surface * to); | ||||
| 	void keyPressed(const SDL_KeyboardEvent & key); | ||||
| 	void mouseMoved(const SDL_MouseMotionEvent &sEvent); | ||||
| 	void clickRight(boost::logic::tribool down); | ||||
|  | ||||
| 	bool reverseCreature(int number, int hex, bool wideTrick = false); //reverses animation of given creature playing animation of reversing | ||||
| 	void handleStartMoving(int number); //animation of starting move; some units don't have this animation (ie. halberdier) | ||||
|  | ||||
|   | ||||
| @@ -570,6 +570,17 @@ bool CCallback::battleCanShoot(int ID, int dest) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool CCallback::battleCanCastSpell() | ||||
| { | ||||
| 	if(!gs->curB) //there is no battle | ||||
| 		return false; | ||||
|  | ||||
| 	if(gs->curB->side1 == player) | ||||
| 		return gs->curB->castedSpells[0] == 0 && gs->getHero(gs->curB->hero1)->getArt(17); | ||||
| 	else | ||||
| 		return gs->curB->castedSpells[1] == 0 && gs->getHero(gs->curB->hero2)->getArt(17); | ||||
| } | ||||
|  | ||||
| void CCallback::swapGarrisonHero( const CGTownInstance *town ) | ||||
| { | ||||
| 	if(town->tempOwner != player) return; | ||||
|   | ||||
| @@ -105,6 +105,7 @@ public: | ||||
| 	virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //reutrns numbers of hexes reachable by creature with id ID | ||||
| 	virtual bool battleIsStackMine(int ID)=0; //returns true if stack with id ID belongs to caller | ||||
| 	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 | ||||
| }; | ||||
|  | ||||
| struct HeroMoveDetails | ||||
| @@ -197,7 +198,7 @@ public: | ||||
| 	std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID | ||||
| 	bool battleIsStackMine(int ID); //returns true if stack with id ID belongs to caller | ||||
| 	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 | ||||
|  | ||||
| //XXX hmmm _tmain on _GNUC_ wtf? | ||||
| //friends | ||||
|   | ||||
| @@ -107,7 +107,7 @@ struct DLL_EXPORT CObstacleInstance | ||||
|  | ||||
| struct DLL_EXPORT BattleInfo | ||||
| { | ||||
| 	ui8 side1, side2; | ||||
| 	ui8 side1, side2; //side1 - attacker, side2 - defender | ||||
| 	si32 round, activeStack; | ||||
| 	ui8 siege; //    = 0 ordinary battle    = 1 a siege with a Fort    = 2 a siege with a Citadel    = 3 a siege with a Castle | ||||
| 	int3 tile; //for background and bonuses | ||||
| @@ -115,7 +115,7 @@ struct DLL_EXPORT BattleInfo | ||||
| 	CCreatureSet army1, army2; | ||||
| 	std::vector<CStack*> stacks; | ||||
| 	std::vector<CObstacleInstance> obstacles; | ||||
| 	ui8 castedSpells[2]; | ||||
| 	ui8 castedSpells[2]; //[0] - attacker, [1] - defender | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
|   | ||||
| @@ -63,6 +63,10 @@ bool CCreature::isShooting() const | ||||
| { | ||||
| 	return vstd::contains(abilities,SHOOTER); | ||||
| } | ||||
| bool CCreature::isUndead() const | ||||
| { | ||||
| 	return vstd::contains(abilities,UNDEAD); | ||||
| } | ||||
| si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought | ||||
| { | ||||
| 	int ret = 2147483645; | ||||
| @@ -331,6 +335,8 @@ void CCreatureHandler::loadCreatures() | ||||
| 			ncre.abilities.insert(TWICE_ATTACK); | ||||
| 		if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack")) | ||||
| 			ncre.abilities.insert(NO_ENEMY_RETALIATION); | ||||
| 		if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD")) | ||||
| 			ncre.abilities.insert(UNDEAD); | ||||
| 		if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string("")) | ||||
| 		{ | ||||
| 			ncre.idNumber = creatures.size(); | ||||
|   | ||||
| @@ -45,6 +45,7 @@ public: | ||||
| 	bool isDoubleWide() const; //returns true if unit is double wide on battlefield | ||||
| 	bool isFlying() const; //returns true if it is a flying unit | ||||
| 	bool isShooting() const; //returns true if unit can shoot | ||||
| 	bool isUndead() const; //returns true if unit is undead | ||||
| 	si32 maxAmount(const std::vector<si32> &res) const; //how many creatures can be bought | ||||
| 	static int getQuantityID(const int & quantity); //0 - a few, 1 - several, 2 - pack, 3 - lots, 4 - horde, 5 - throng, 6 - swarm, 7 - zounds, 8 - legion | ||||
|  | ||||
|   | ||||
| @@ -519,6 +519,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c) | ||||
| handleConEnd: | ||||
| 	tlog1 << "Ended handling connection\n"; | ||||
| #undef SPELL_CAST_TEMPLATE_1 | ||||
| #undef SPELL_CAST_TEMPLATE_2 | ||||
| } | ||||
| void CGameHandler::moveStack(int stack, int dest) | ||||
| {							 | ||||
| @@ -2306,6 +2307,28 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) | ||||
| 				sendAndApply(&sse); \ | ||||
| 			} | ||||
|  | ||||
| #define SPELL_CAST_TEMPLATE_2(EFFECT_ID, DAMAGE) std::set<ui16> attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s)); \ | ||||
| 					std::set<CStack*> attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/ \ | ||||
| 					for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it) \ | ||||
| 					{ \ | ||||
| 						CStack * st = gs->curB->getStackT(*it); \ | ||||
| 						if(st) \ | ||||
| 							attackedCres.insert(st); \ | ||||
| 					} \ | ||||
| 					if(attackedCres.size() == 0) break; \ | ||||
| 					StacksInjured si; \ | ||||
| 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) \ | ||||
| 					{ \ | ||||
| 						BattleStackAttacked bsa; \ | ||||
| 						bsa.flags |= 2; \ | ||||
| 						bsa.effect = EFFECT_ID; \ | ||||
| 						bsa.damageAmount = DAMAGE;  \ | ||||
| 						bsa.stackAttacked = (*it)->ID; \ | ||||
| 						prepareAttacked(bsa,*it); \ | ||||
| 						si.stacks.insert(bsa); \ | ||||
| 					} \ | ||||
| 					sendAndApply(&si); \ | ||||
|  | ||||
| 			SpellCasted sc; | ||||
| 			sc.side = ba.side; | ||||
| 			sc.id = ba.additionalInfo; | ||||
| @@ -2316,67 +2339,90 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) | ||||
| 			{ | ||||
| 			case 15: //magic arrow | ||||
| 				{ | ||||
| 					CStack * attacked = gs->curB->getStackT(ba.destinationTile); | ||||
| 					if(!attacked) break; | ||||
| 					BattleStackAttacked bsa; | ||||
| 					bsa.flags |= 2; | ||||
| 					bsa.effect = 64; | ||||
| 					bsa.damageAmount = h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 					bsa.stackAttacked = attacked->ID; | ||||
| 					prepareAttacked(bsa,attacked); | ||||
| 					sendAndApply(&bsa); | ||||
| 					SPELL_CAST_TEMPLATE_2(64, h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 16: //ice bolt | ||||
| 				{ | ||||
| 					CStack * attacked = gs->curB->getStackT(ba.destinationTile); | ||||
| 					if(!attacked) break; | ||||
| 					BattleStackAttacked bsa; | ||||
| 					bsa.flags |= 2; | ||||
| 					bsa.effect = 46; | ||||
| 					bsa.damageAmount = h->getPrimSkillLevel(2) * 20  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 					bsa.stackAttacked = attacked->ID; | ||||
| 					prepareAttacked(bsa,attacked); | ||||
| 					sendAndApply(&bsa); | ||||
| 					SPELL_CAST_TEMPLATE_2(46, h->getPrimSkillLevel(2) * 20  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 17: //lightning bolt | ||||
| 				{ | ||||
| 					CStack * attacked = gs->curB->getStackT(ba.destinationTile); | ||||
| 					if(!attacked) break; | ||||
| 					BattleStackAttacked bsa; | ||||
| 					bsa.flags |= 2; | ||||
| 					bsa.effect = 38; | ||||
| 					bsa.damageAmount = h->getPrimSkillLevel(2) * 25  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 					bsa.stackAttacked = attacked->ID; | ||||
| 					prepareAttacked(bsa,attacked); | ||||
| 					sendAndApply(&bsa); | ||||
| 					SPELL_CAST_TEMPLATE_2(38, h->getPrimSkillLevel(2) * 25  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 18: //implosion | ||||
| 				{ | ||||
| 					CStack * attacked = gs->curB->getStackT(ba.destinationTile); | ||||
| 					if(!attacked) break; | ||||
| 					BattleStackAttacked bsa; | ||||
| 					bsa.flags |= 2; | ||||
| 					bsa.effect = 10; | ||||
| 					bsa.damageAmount = h->getPrimSkillLevel(2) * 75  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 					bsa.stackAttacked = attacked->ID; | ||||
| 					prepareAttacked(bsa,attacked); | ||||
| 					sendAndApply(&bsa); | ||||
| 					SPELL_CAST_TEMPLATE_2(10, h->getPrimSkillLevel(2) * 75  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 20: //frost ring | ||||
| 				{ | ||||
| 					SPELL_CAST_TEMPLATE_2(45, h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 21: //fireball | ||||
| 				{ | ||||
| 					std::set<ui16> attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s)); | ||||
| 					std::set<CStack*> attackedCres; //set to exclude multiple occurences of two hex creatures | ||||
| 					for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it) | ||||
| 					{ | ||||
| 						attackedCres.insert(gs->curB->getStackT(*it)); | ||||
| 					} | ||||
| 					SPELL_CAST_TEMPLATE_2(53, h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 22: //inferno | ||||
| 				{ | ||||
| 					SPELL_CAST_TEMPLATE_2(9, h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 23: //meteor shower | ||||
| 				{ | ||||
| 					SPELL_CAST_TEMPLATE_2(16, h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)]); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 24: //death ripple | ||||
| 				{ | ||||
| 					std::set<CStack*> attackedCres; | ||||
| 					 | ||||
| 					if(attackedCres.size()) break; | ||||
| 					//TODO: the rest of it | ||||
| 					for(int it=0; it<gs->curB->stacks.size(); ++it) | ||||
| 					{ | ||||
| 						if(!gs->curB->stacks[it]->creature->isUndead()) | ||||
| 							attackedCres.insert(gs->curB->stacks[it]); | ||||
| 					} | ||||
| 					if(attackedCres.size() == 0) break; | ||||
| 					StacksInjured si; | ||||
| 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) | ||||
| 					{ | ||||
| 						BattleStackAttacked bsa; | ||||
| 						bsa.flags |= 2; | ||||
| 						bsa.effect = 8; | ||||
| 						bsa.damageAmount = h->getPrimSkillLevel(2) * 5  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 						bsa.stackAttacked = (*it)->ID; | ||||
| 						prepareAttacked(bsa,*it); | ||||
| 						si.stacks.insert(bsa); | ||||
| 					} | ||||
| 					sendAndApply(&si); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 25: //destroy undead | ||||
| 				{ | ||||
| 					std::set<CStack*> attackedCres; | ||||
| 					 | ||||
| 					for(int it=0; it<gs->curB->stacks.size(); ++it) | ||||
| 					{ | ||||
| 						if(gs->curB->stacks[it]->creature->isUndead()) | ||||
| 							attackedCres.insert(gs->curB->stacks[it]); | ||||
| 					} | ||||
| 					if(attackedCres.size() == 0) break; | ||||
| 					StacksInjured si; | ||||
| 					for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) | ||||
| 					{ | ||||
| 						BattleStackAttacked bsa; | ||||
| 						bsa.flags |= 2; | ||||
| 						bsa.effect = 29; | ||||
| 						bsa.damageAmount = h->getPrimSkillLevel(2) * 10  +  s->powers[h->getSpellSchoolLevel(s)];  | ||||
| 						bsa.stackAttacked = (*it)->ID; | ||||
| 						prepareAttacked(bsa,*it); | ||||
| 						si.stacks.insert(bsa); | ||||
| 					} | ||||
| 					sendAndApply(&si); | ||||
| 					break; | ||||
| 				} | ||||
| 			case 27: //shield  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user