Преглед изворни кода

Fix 981 reset hero on hiring after retreat/surrender

Vadim Markovtsev пре 9 година
родитељ
комит
7772b6de74
4 измењених фајлова са 65 додато и 42 уклоњено
  1. 9 2
      lib/NetPacksLib.cpp
  2. 26 25
      lib/mapObjects/CGHeroInstance.cpp
  3. 15 7
      lib/mapObjects/CGHeroInstance.h
  4. 15 8
      server/CGameHandler.cpp

+ 9 - 2
lib/NetPacksLib.cpp

@@ -594,7 +594,11 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
 
 	h->setOwner(player);
 	h->pos = tile;
-	h->movement =  h->maxMovePoints(true);
+	bool fresh = !h->isInitialized();
+	if(fresh)
+	{ // this is a fresh hero who hasn't appeared yet
+		h->movement = h->maxMovePoints(true);
+	}
 
 	gs->hpool.heroesPool.erase(hid);
 	if(h->id == ObjectInstanceID())
@@ -608,7 +612,10 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
 	gs->map->heroesOnMap.push_back(h);
 	p->heroes.push_back(h);
 	h->attachTo(p);
-	h->initObj();
+	if(fresh)
+	{
+		h->initObj();
+	}
 	gs->map->addBlockVisTiles(h);
 
 	if(t)

+ 26 - 25
lib/mapObjects/CGHeroInstance.cpp

@@ -220,7 +220,9 @@ CGHeroInstance::CGHeroInstance()
 	setNodeType(HERO);
 	ID = Obj::HERO;
 	tacticFormationEnabled = inTownGarrison = false;
-	mana = movement = portrait = -1;
+	mana = UNINITIALIZED_MANA;
+	movement = UNINITIALIZED_MOVEMENT;
+	portrait = UNINITIALIZED_PORTRAIT;
 	isStanding = true;
 	moveDir = 4;
 	level = 1;
@@ -868,18 +870,18 @@ TExpType CGHeroInstance::calculateXp(TExpType exp) const
 ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const
 {
 	si16 skill = -1; //skill level
-	
+
 	spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop)
 	{
 		int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill),	valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
-		if(thisSchool > skill)									
-		{														
-			skill = thisSchool;									
-			if(outSelectedSchool)								
-				*outSelectedSchool = (ui8)cnf.id;				
-		}																
+		if(thisSchool > skill)
+		{
+			skill = thisSchool;
+			if(outSelectedSchool)
+				*outSelectedSchool = (ui8)cnf.id;
+		}
 	});
-	
+
 	vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
 	vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
 
@@ -904,7 +906,7 @@ ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack
 	if (affectedStack && affectedStack->getCreature()->level) //Hero specials like Solmyr, Deemer
 		base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV,  spell->id.toEnum()) * level) / affectedStack->getCreature()->level)) / 100.0;
 
-	return base;	
+	return base;
 }
 
 int CGHeroInstance::getEffectLevel(const CSpell * spell) const
@@ -912,7 +914,7 @@ int CGHeroInstance::getEffectLevel(const CSpell * spell) const
 	if(hasBonusOfType(Bonus::MAXED_SPELL, spell->id))
 		return 3;//todo: recheck specialty from where this bonus is. possible bug
 	else
-		return getSpellSchoolLevel(spell);		
+		return getSpellSchoolLevel(spell);
 }
 
 int CGHeroInstance::getEffectPower(const CSpell * spell) const
@@ -922,7 +924,7 @@ int CGHeroInstance::getEffectPower(const CSpell * spell) const
 
 int CGHeroInstance::getEnchantPower(const CSpell * spell) const
 {
-	return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION);	
+	return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION);
 }
 
 int CGHeroInstance::getEffectValue(const CSpell * spell) const
@@ -1107,8 +1109,8 @@ void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
 {
 	// FIXME: Offsets need to be fixed once we get rid of convertPosition
 	// Check issue 515 for details
-	offsets = 
-	{ 
+	offsets =
+	{
 		int3(-1,1,0), int3(-1,-1,0), int3(-2,0,0), int3(0,0,0), int3(0,1,0), int3(-2,1,0), int3(0,-1,0), int3(-2,-1,0)
 	};
 }
@@ -1436,20 +1438,19 @@ void CGHeroInstance::levelUpAutomatically()
 bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const
 {
 	//VISIONS spell support
-	
-	const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype)); 
-	
+
+	const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype));
+
 	const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached);
-	
+
 	int visionsRange =  visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER);
-		
-	if (visionsMultiplier > 0) 	
+
+	if (visionsMultiplier > 0)
 		vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present
-	
+
 	const int distance = target->pos.dist2d(getPosition(false));
-	
+
 	//logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
-	
-	return (distance < visionsRange) && (target->pos.z == pos.z);	
-}
 
+	return (distance < visionsRange) && (target->pos.z == pos.z);
+}

+ 15 - 7
lib/mapObjects/CGHeroInstance.h

@@ -64,6 +64,9 @@ public:
 	ConstTransitivePtr<CCommanderInstance> commander;
 	const CGBoat *boat; //set to CGBoat when sailing
 
+	static constexpr ui32 UNINITIALIZED_PORTRAIT = -1;
+	static constexpr ui32 UNINITIALIZED_MANA = -1;
+	static constexpr ui32 UNINITIALIZED_MOVEMENT = -1;
 
 	//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
 	//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
@@ -124,6 +127,11 @@ public:
 		}
 	} skillsInfo;
 
+	inline bool isInitialized() const
+	{ // has this hero been on the map at least once?
+		return movement == UNINITIALIZED_MOVEMENT && mana == UNINITIALIZED_MANA;
+	}
+
 	//int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
 	int getSightRadious() const override; //sight distance (should be used if player-owned structure)
 	//////////////////////////////////////////////////////////////////////////
@@ -177,7 +185,7 @@ public:
 	double getHeroStrength() const; // includes fighting and magic strength
 	ui64 getTotalStrength() const; // includes fighting strength and army strength
 	TExpType calculateXp(TExpType exp) const; //apply learning skill
-	
+
 	bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
 	CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
 	void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
@@ -201,23 +209,23 @@ public:
 	void Updatespecialty();
 	void recreateSecondarySkillsBonuses();
 	void updateSkill(SecondarySkill which, int val);
-	
+
 	bool hasVisions(const CGObjectInstance * target, const int subtype) const;
 
 	CGHeroInstance();
 	virtual ~CGHeroInstance();
-	
+
 	///ArtBearer
 	ArtBearer::ArtBearer bearerType() const override;
 
 	///IBonusBearer
 	CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
 	std::string nodeName() const override;
-	
+
 	///ISpellCaster
 	ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
 	ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
-	
+
 	///default spell school level for effect calculation
 	int getEffectLevel(const CSpell * spell) const override;
 
@@ -229,9 +237,9 @@ public:
 
 	///damage/heal override(ignores spell configuration, effect level and effect power)
 	int getEffectValue(const CSpell * spell) const override;
-	
+
 	const PlayerColor getOwner() const override;
-	
+
 	void deserializationFix();
 
 	void initObj() override;

+ 15 - 8
server/CGameHandler.cpp

@@ -3328,23 +3328,27 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
 //		|| (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!")))
 	if((p->resources.at(Res::GOLD) < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!"))
 		|| ((!t) && (getHeroCount(player, false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER && complain("Cannot hire hero, too many wandering heroes already!")))
-			|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) )
-
-	return false;
+		|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) )
+	{
+		return false;
+	}
 
 	if(t) //tavern in town
 	{
-		if(    (!t->hasBuilt(BuildingID::TAVERN)  && complain("No tavern!"))
-			|| (t->visitingHero  && complain("There is visiting hero - no place!")))
+		if((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
+			 || (t->visitingHero  && complain("There is visiting hero - no place!")))
+		{
 			return false;
+		}
 	}
 	else if(obj->ID == Obj::TAVERN)
 	{
-		if(getTile(obj->visitablePos())->visitableObjects.back() != obj  &&  complain("Tavern entry must be unoccupied!"))
+		if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!"))
+		{
 			return false;
+		}
 	}
 
-
 	const CGHeroInstance *nh = p->availableHeroes.at(hid);
 	if (!nh)
 	{
@@ -3359,13 +3363,14 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
 	hr.tile = obj->visitablePos() + nh->getVisitableOffset();
 	sendAndApply(&hr);
 
-
 	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->unusedHeroesFromPool();
 
 	const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid);
 	const CGHeroInstance *newHero = nullptr;
 	if (theOtherHero) //on XXL maps all heroes can be imprisoned :(
+	{
 		newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass);
+	}
 
 	SetAvailableHeroes sah;
 	sah.player = player;
@@ -3377,7 +3382,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
 		sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
 	}
 	else
+	{
 		sah.hid[hid] = -1;
+	}
 
 	sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
 	sendAndApply(&sah);