Browse Source

- Fixed serialization of limiters
- Hero can now can have several separate specialty nodes
- Fixed typo (speciality->specialty)
- Fixed several crashes related to commanders
- Improvements to specialty handling, bugfixes and temporary solutions for upcoming hero specialties in mods

DjWarmonger 12 years ago
parent
commit
ce15eb37c2

+ 1 - 1
client/CCreatureWindow.cpp

@@ -553,7 +553,7 @@ void CCreatureWindow::showAll(SDL_Surface * to)
 		skillPictures[i]->showAll (to);
 	}
 
-	if (type == COMMANDER_LEVEL_UP && upgradeOptions[selectedOption] >= 100) //add frame to selected skill
+	if (upgradeOptions.size() && (type == COMMANDER_LEVEL_UP && upgradeOptions[selectedOption] >= 100)) //add frame to selected skill
 	{
 		int index = selectedOption - selectableSkills.size(); //this is screwed
 		CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b)); 

+ 1 - 1
client/CHeroWindow.h

@@ -65,7 +65,7 @@ class CHeroWindow: public CWindowObject, public CWindowWithGarrison, public CWin
 	std::vector<LRClickableAreaWTextComp *> primSkillAreas;
 	LRClickableAreaWText * expArea;
 	LRClickableAreaWText * spellPointsArea;
-	LRClickableAreaWText * specArea;//speciality
+	LRClickableAreaWText * specArea;//specialty
 	CAnimImage *specImage;
 	MoraleLuckBox * morale, * luck;
 	std::vector<LRClickableAreaWTextComp *> secSkillAreas;

+ 1 - 1
client/CKingdomInterface.cpp

@@ -570,7 +570,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
 		}
 	}
 
-	//Heroes can produce gold as well - skill, speciality or arts
+	//Heroes can produce gold as well - skill, specialty or arts
 	std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
 	for(size_t i=0; i<heroes.size(); i++)
 	{

+ 1 - 1
client/CPreGame.cpp

@@ -2872,7 +2872,7 @@ void OptionsTab::CPregameTooltipBox::genHeroWindow()
 	pos = Rect(0, 0, 292, 226);
 	genHeader();
 
-	// speciality
+	// specialty
 	new CAnimImage("UN44", CGI->heroh->heroes[settings.hero]->imageIndex, 0, pos.w / 2 - 22, 134);
 
 	new CLabel(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER,  Colors::YELLOW, CGI->generaltexth->allTexts[78]);

+ 4 - 4
client/GUIClasses.cpp

@@ -5117,10 +5117,10 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2):
 
 		portrait[b] = new CHeroArea(257 + 228*b, 13, heroInst[b]);
 
-		speciality[b] = new LRClickableAreaWText();
-		speciality[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + 45);
-		speciality[b]->hoverText = CGI->generaltexth->heroscrn[27];
-		speciality[b]->text = heroInst[b]->type->specDescr;
+		specialty[b] = new LRClickableAreaWText();
+		specialty[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + 45);
+		specialty[b]->hoverText = CGI->generaltexth->heroscrn[27];
+		specialty[b]->text = heroInst[b]->type->specDescr;
 
 		experience[b] = new LRClickableAreaWText();
 		experience[b]->pos = genRect(32, 32, pos.x + 105 + 490*b, pos.y + 45);

+ 1 - 1
client/GUIClasses.h

@@ -1019,7 +1019,7 @@ class CExchangeWindow : public CWindowObject, public CWindowWithGarrison, public
 
 	MoraleLuckBox *morale[2], *luck[2];
 
-	LRClickableAreaWText *speciality[2];
+	LRClickableAreaWText *specialty[2];
 	LRClickableAreaWText *experience[2];
 	LRClickableAreaWText *spellPoints[2];
 	CHeroArea *portrait[2];

+ 2 - 2
lib/CGameState.cpp

@@ -1764,8 +1764,8 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
 	if(stack.armyObj->ID == Obj::TOWN)
 		t = static_cast<const CGTownInstance *>(stack.armyObj);
 	else if(h)
-	{	//hero speciality
-		TBonusListPtr lista = h->speciality.getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
+	{	//hero specialty
+		TBonusListPtr lista = h->getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
 		BOOST_FOREACH(const Bonus *it, *lista)
 		{
 			ui16 nid = it->additionalInfo;

+ 1 - 1
lib/CModHandler.cpp

@@ -248,7 +248,7 @@ void CModHandler::initialize(std::vector<std::string> availableMods)
 			detectedMods.push_back(name);
 		}
 		else
-			tlog1 << "\t\t Directory " << name << " does not contains VCMI mod\n";
+			tlog3 << "\t\t Directory " << name << " does not contains VCMI mod\n";
 	}
 
 	if (!checkDependencies(detectedMods))

+ 82 - 61
lib/CObjectHandler.cpp

@@ -716,8 +716,6 @@ CGHeroInstance::CGHeroInstance()
 	commander = NULL;
 	sex = 0xff;
 	secSkills.push_back(std::make_pair(-1, -1));
-	speciality.setNodeType(CBonusSystemNode::SPECIALITY);
-	attachTo(&speciality); //do we ever need to detach it?
 }
 
 void CGHeroInstance::initHero(int SUBID)
@@ -926,23 +924,23 @@ const std::string & CGHeroInstance::getBiography() const
 void CGHeroInstance::initObj() //TODO: use bonus system
 {
 	blockVisit = true;
-	speciality.growthsWithLevel = false;
+	HeroSpecial * hs = new HeroSpecial();
 
 	if(!type)
 		return; //TODO: support prison
 
-	BOOST_FOREACH(const auto &spec, type->spec)
+	BOOST_FOREACH(const auto &spec, type->spec) //TODO: unfity with bonus system
 	{
 		Bonus *bonus = new Bonus();
 		bonus->val = spec.val;
-		bonus->sid = id; //from the hero, speciality has no unique id
+		bonus->sid = id; //from the hero, specialty has no unique id
 		bonus->duration = Bonus::PERMANENT;
 		bonus->source = Bonus::HERO_SPECIAL;
 		switch (spec.type)
 		{
-			case 1:// creature speciality
+			case 1:// creature specialty
 				{
-					speciality.growthsWithLevel = true;
+					hs->growsWithLevel = true;
 
 					const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
 
@@ -960,30 +958,29 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 
 					bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
 					bonus->type = Bonus::PRIMARY_SKILL;
-					bonus->additionalInfo = spec.additionalinfo;
 					bonus->valType = Bonus::ADDITIVE_VALUE;
 
 					bonus->subtype = PrimarySkill::ATTACK;
-					speciality.addNewBonus(bonus);
+					hs->addNewBonus(bonus);
 
 					bonus = new Bonus(*bonus);
 					bonus->subtype = PrimarySkill::DEFENSE;
-					speciality.addNewBonus(bonus);
+					hs->addNewBonus(bonus);
 					//values will be calculated later
 
 					bonus = new Bonus(*bonus);
 					bonus->type = Bonus::STACKS_SPEED;
 					bonus->val = 1; //+1 speed
-					speciality.addNewBonus(bonus);
+					hs->addNewBonus(bonus);
 				}
 				break;
 			case 2://secondary skill
-				speciality.growthsWithLevel = true;
+				hs->growsWithLevel = true;
 				bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value
 				bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value
 				bonus->subtype = spec.subtype; //skill id
 				bonus->val = spec.val; //value per level, in percent
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				bonus = new Bonus(*bonus);
 
 				switch (spec.additionalinfo)
@@ -996,12 +993,12 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 						break;
 				}
 				bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 3://spell damage bonus, level dependent but calculated elsewhere
 				bonus->type = Bonus::SPECIAL_SPELL_LEV;
 				bonus->subtype = spec.subtype;
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 4://creature stat boost
 				switch (spec.subtype)
@@ -1029,30 +1026,30 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 				}
 				bonus->valType = Bonus::ADDITIVE_VALUE;
 				bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true));
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 5://spell damage bonus in percent
 				bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE;
 				bonus->valType = Bonus::BASE_NUMBER; // current spell system is screwed
 				bonus->subtype = spec.subtype; //spell id
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 6://damage bonus for bless (Adela)
 				bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
 				bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
 				bonus->additionalInfo = spec.additionalinfo; //damage factor
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 7://maxed mastery for spell
 				bonus->type = Bonus::MAXED_SPELL;
 				bonus->subtype = spec.subtype; //spell i
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 8://peculiar spells - enchantments
 				bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT;
 				bonus->subtype = spec.subtype; //spell id
 				bonus->additionalInfo = spec.additionalinfo;//0, 1 for Coronius
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 9://upgrade creatures
 			{
@@ -1060,13 +1057,13 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 				bonus->type = Bonus::SPECIAL_UPGRADE;
 				bonus->subtype = spec.subtype; //base id
 				bonus->additionalInfo = spec.additionalinfo; //target id
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				bonus = new Bonus(*bonus);
 
 				BOOST_FOREACH(auto cre_id, creatures[spec.subtype]->upgrades)
 				{
 					bonus->subtype = cre_id; //propagate for regular upgrades of base creature
-					speciality.addNewBonus(bonus);
+					hs->addNewBonus(bonus);
 					bonus = new Bonus(*bonus);
 				}
 				vstd::clear_pointer(bonus);
@@ -1075,14 +1072,14 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 			case 10://resource generation
 				bonus->type = Bonus::GENERATE_RESOURCE;
 				bonus->subtype = spec.subtype;
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 11://starting skill with mastery (Adrienne)
 				cb->changeSecSkill(id, spec.val, spec.additionalinfo); //simply give it and forget
 				break;
 			case 12://army speed
 				bonus->type = Bonus::STACKS_SPEED;
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			case 13://Dragon bonuses (Mutare)
 				bonus->type = Bonus::PRIMARY_SKILL;
@@ -1097,62 +1094,86 @@ void CGHeroInstance::initObj() //TODO: use bonus system
 						break;
 				}
 				bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
-				speciality.addNewBonus(bonus);
+				hs->addNewBonus(bonus);
 				break;
 			default:
-				tlog2 << "Unexpected hero speciality " << type <<'\n';
+				tlog2 << "Unexpected hero specialty " << type <<'\n';
 		}
 	}
+	hs->setNodeType(CBonusSystemNode::specialty);
+	attachTo(hs); //do we ever need to detach it?
+	specialty.push_back(hs); //will it work?
+
 	//initialize bonuses
 	BOOST_FOREACH(auto skill_info, secSkills)
 		updateSkill(skill_info.first, skill_info.second);
-	UpdateSpeciality();
+	Updatespecialty();
 
 	mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
 	type->name = name;
 }
-void CGHeroInstance::UpdateSpeciality() //TODO: calculate special value of bonuses on-the-fly?
+void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
 {
-	if (speciality.growthsWithLevel)
+	BOOST_FOREACH (auto hs, specialty)
 	{
-		const auto &creatures = VLC->creh->creatures;
-
-		BOOST_FOREACH(Bonus *it, speciality.getBonusList())
+		if (hs->growsWithLevel)
 		{
-			switch (it->type)
+			const auto &creatures = VLC->creh->creatures;
+
+			BOOST_FOREACH(Bonus * b, hs->getBonusList())
 			{
-				case Bonus::SECONDARY_SKILL_PREMY:
-					it->val = (speciality.valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, it->subtype) * level);
-					break; //use only hero skills as bonuses to avoid feedback loop
-				case Bonus::PRIMARY_SKILL: //for crearures, that is
-					int creLevel = creatures[it->additionalInfo]->level;
-					if(!creLevel)
+				switch (b->type)
+				{
+					case Bonus::SECONDARY_SKILL_PREMY:
+						b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
+						break; //use only hero skills as bonuses to avoid feedback loop
+					case Bonus::PRIMARY_SKILL: //for creatures, that is
 					{
-						if(it->additionalInfo == 146)
-							creLevel = 5; //treat ballista as 5-level
-						else
+						CCreatureTypeLimiter * foundLimiter = NULL;
+						CCreature * cre = NULL;
+						int creLevel = 0;
+						TLimiterPtr limiterNode (b);
+						while (limiterNode->next)
 						{
-							tlog2 << "Warning: unknown level of " << creatures[it->additionalInfo]->namePl << std::endl;
-							continue;
+							limiterNode = limiterNode->next; //search list
+							if (foundLimiter = dynamic_cast<CCreatureTypeLimiter*>(limiterNode.get())) //TODO: more general eveluation of bonuses?
+							{
+								cre = const_cast<CCreature*>(foundLimiter->creature);
+								creLevel = cre->level;
+								break;
+							}
 						}
-					}
-
-					double primSkillModifier = (int)(level / creLevel) / 20.0;
-					int param;
-					switch (it->subtype)
-					{
-						case PrimarySkill::ATTACK:
-							param = creatures[it->additionalInfo]->Attack();
-							break;
-						case PrimarySkill::DEFENSE:
-							param = creatures[it->additionalInfo]->Defense();
+						if (!foundLimiter)
+						{
+							tlog2 << "Primary skill specialty growth supported only with creature type limiters\n";
 							break;
-						default:
-							assert(0);
-							param = 0;
+						}
+						if (cre)
+						{
+							creLevel = cre->level;
+						}
+						if(!creLevel)
+						{
+							creLevel = 5; //treat ballista as tier 5
+						}
+						double primSkillModifier = (int)(level / creLevel) / 20.0;
+						int param;
+						switch (b->subtype)
+						{
+							case PrimarySkill::ATTACK:
+								param = creatures[b->additionalInfo]->Attack();
+								break;
+							case PrimarySkill::DEFENSE:
+								param = creatures[b->additionalInfo]->Defense();
+								break;
+							default:
+								assert(0);
+								param = 0;
+						}
+						b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
+						break;
 					}
-					it->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
-					break;
+				}
 			}
 		}
 	}
@@ -1288,7 +1309,7 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
 
 	vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
 	vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id)); //given by artifact or other effect
-	if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero speciality (Daremyth, Melodia)
+	if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
 		skill = 3;
 	assert(skill >= 0 && skill <= 3);
 	return skill;

+ 11 - 6
lib/CObjectHandler.h

@@ -42,7 +42,7 @@ struct TerrainTile;
 struct InfoWindow;
 struct Component;
 struct BankConfig;
-struct UpdateHeroSpeciality;
+struct UpdateHerospecialty;
 struct NewArtifact;
 class CGBoat;
 class CArtifactSet;
@@ -328,13 +328,18 @@ public:
 
 	struct DLL_LINKAGE HeroSpecial : CBonusSystemNode
 	{
-		bool growthsWithLevel;
+		bool growsWithLevel;
+
+		HeroSpecial(){growsWithLevel = false;};
+
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
 			h & static_cast<CBonusSystemNode&>(*this);
-			h & growthsWithLevel;
+			h & growsWithLevel;
 		}
-	} speciality;
+	};
+
+	std::vector<HeroSpecial*> specialty;
 
 	//BonusList bonuses;
 	//////////////////////////////////////////////////////////////////////////
@@ -347,7 +352,7 @@ public:
 		h & exp & level & name & biography & portrait & mana & secSkills & movement
 			& sex & inTownGarrison & /*artifacts & artifWorn & */spells & patrol & moveDir;
 
-		h & type & speciality & commander;
+		h & type & specialty & commander;
 		BONUS_TREE_DESERIALIZATION_FIX
 		//visitied town pointer will be restored by map serialization method
 	}
@@ -411,7 +416,7 @@ public:
 	//void giveArtifact (ui32 aid);
 	void initHeroDefInfo();
 	void pushPrimSkill(int which, int val);
-	void UpdateSpeciality();
+	void Updatespecialty();
 	void updateSkill(int which, int val);
 
 	CGHeroInstance();

+ 11 - 3
lib/HeroBonus.h

@@ -25,7 +25,7 @@ struct BonusLimitationContext;
 struct BonusCalculationContext;
 
 typedef shared_ptr<BonusList> TBonusListPtr;
-typedef shared_ptr<ILimiter> TLimiterPtr;
+typedef shared_ptr<LimiterDecorator> TLimiterPtr;
 typedef shared_ptr<IPropagator> TPropagatorPtr;
 typedef shared_ptr<ICalculator> TCalculatorPtr;
 typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
@@ -44,6 +44,11 @@ public:
 
 	virtual ~LimiterDecorator()
 	{}
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & next;
+	}
 };
 
 #define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving && h.smartPointerSerialization) deserializationFix();
@@ -303,6 +308,7 @@ struct DLL_LINKAGE Bonus : public LimiterDecorator
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
+		h & static_cast<LimiterDecorator&>(*this);
 		h & duration & type & subtype & source & val & sid & description & additionalInfo & turnsRemain & valType & effectRange & limiter & propagator;
 	}
 
@@ -520,7 +526,9 @@ public:
 	virtual ~ILimiter();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
-	{}
+	{
+		h & static_cast<LimiterDecorator&>(*this);
+	}
 };
 
 class DLL_LINKAGE IBonusBearer
@@ -663,7 +671,7 @@ public:
 	}
 	enum ENodeTypes
 	{
-		UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
+		UNKNOWN, STACK_INSTANCE, STACK_BATTLE, specialty, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
 		TOWN_AND_VISITOR, BATTLE
 	};
 };

+ 2 - 2
lib/NetPacksLib.cpp

@@ -950,8 +950,8 @@ DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
 {
 	CGHeroInstance* h = gs->getHero(heroid);
 	h->level = level;
-	//speciality
-	h->UpdateSpeciality();
+	//specialty
+	h->Updatespecialty();
 }
 
 DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs)

+ 1 - 0
lib/RegisterTypes.h

@@ -76,6 +76,7 @@ void registerTypes1(Serializer &s)
 	s.template registerType<IPropagator>();
 	s.template registerType<CPropagatorNodeType>();
 
+	s.template registerType<LimiterDecorator>();
 	s.template registerType<ILimiter>();
 	s.template registerType<CCreatureTypeLimiter>();
 	s.template registerType<HasAnotherBonusLimiter>();

+ 2 - 2
server/CGameHandler.cpp

@@ -4063,7 +4063,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 			const Bonus * bonus = NULL;
 			if (caster)
 				bonus = caster->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, spellID));
-			//TODO does hero speciality should affects his stack casting spells?
+			//TODO does hero specialty should affects his stack casting spells?
 
 			si32 power = 0;
 			BOOST_FOREACH(const CStack *affected, attackedCres)
@@ -4073,7 +4073,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 				sse.stacks.push_back(affected->ID);
 
 				//Apply hero specials - peculiar enchants
-				const ui8 tier = affected->getCreature()->level;
+				const ui8 tier = std::max((ui8)1, affected->getCreature()->level); //don't divide by 0 for certain creatures (commanders, war machines)
 				if (bonus)
  				{
  	 				switch(bonus->additionalInfo)