Przeglądaj źródła

Moved CGCreature to a new file

Ivan Savenko 2 lat temu
rodzic
commit
6f743916db

+ 1 - 0
AI/VCAI/FuzzyHelper.cpp

@@ -17,6 +17,7 @@
 #include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
 #include "../../lib/mapObjects/CBank.h"
+#include "../../lib/mapObjects/CGCreature.h"
 #include "../../lib/mapObjects/CGDwelling.h"
 
 FuzzyHelper * fh;

+ 2 - 0
cmake_modules/VCMI_lib.cmake

@@ -77,6 +77,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 
 		${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp
 		${MAIN_LIB_DIR}/mapObjects/CBank.cpp
+		${MAIN_LIB_DIR}/mapObjects/CGCreature.cpp
 		${MAIN_LIB_DIR}/mapObjects/CGDwelling.cpp
 		${MAIN_LIB_DIR}/mapObjects/CGHeroInstance.cpp
 		${MAIN_LIB_DIR}/mapObjects/CGMarket.cpp
@@ -389,6 +390,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 
 		${MAIN_LIB_DIR}/mapObjects/CArmedInstance.h
 		${MAIN_LIB_DIR}/mapObjects/CBank.h
+		${MAIN_LIB_DIR}/mapObjects/CGCreature.h
 		${MAIN_LIB_DIR}/mapObjects/CGDwelling.h
 		${MAIN_LIB_DIR}/mapObjects/CGHeroInstance.h
 		${MAIN_LIB_DIR}/mapObjects/CGMarket.h

+ 1 - 0
lib/NetPacksLib.cpp

@@ -27,6 +27,7 @@
 #include "StartInfo.h"
 #include "CPlayerState.h"
 #include "TerrainHandler.h"
+#include "mapObjects/CGCreature.h"
 #include "mapObjects/CGMarket.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"

+ 1 - 0
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -28,6 +28,7 @@
 #include "../mapObjectConstructors/HillFortInstanceConstructor.h"
 #include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
 #include "../mapObjectConstructors/ShrineInstanceConstructor.h"
+#include "../mapObjects/CGCreature.h"
 #include "../mapObjects/CGPandoraBox.h"
 #include "../mapObjects/CQuest.h"
 #include "../mapObjects/ObjectTemplate.h"

+ 574 - 0
lib/mapObjects/CGCreature.cpp

@@ -0,0 +1,574 @@
+/*
+ * CGCreature.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "CGCreature.h"
+
+#include "../NetPacks.h"
+#include "../CGeneralTextHandler.h"
+#include "../CConfigHandler.h"
+#include "../GameSettings.h"
+#include "../IGameCallback.h"
+#include "../serializer/JsonSerializeFormat.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+std::string CGCreature::getHoverText(PlayerColor player) const
+{
+	if(stacks.empty())
+	{
+		//should not happen...
+		logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum());
+		return "INVALID_STACK";
+	}
+
+	std::string hoverName;
+	MetaString ms;
+	CCreature::CreatureQuantityId monsterQuantityId = stacks.begin()->second->getQuantityID();
+	int quantityTextIndex = 172 + 3 * (int)monsterQuantityId;
+	if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
+		ms << CCreature::getQuantityRangeStringForId(monsterQuantityId);
+	else
+		ms.addTxt(MetaString::ARRAY_TXT, quantityTextIndex);
+	ms << " " ;
+	ms.addTxt(MetaString::CRE_PL_NAMES,subID);
+	ms.toString(hoverName);
+	return hoverName;
+}
+
+std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
+{
+	std::string hoverName;
+	if(hero->hasVisions(this, 0))
+	{
+		MetaString ms;
+		ms << stacks.begin()->second->count;
+		ms << " " ;
+		ms.addTxt(MetaString::CRE_PL_NAMES,subID);
+
+		ms << "\n";
+
+		int decision = takenAction(hero, true);
+
+		switch (decision)
+		{
+		case FIGHT:
+			ms.addTxt(MetaString::GENERAL_TXT,246);
+			break;
+		case FLEE:
+			ms.addTxt(MetaString::GENERAL_TXT,245);
+			break;
+		case JOIN_FOR_FREE:
+			ms.addTxt(MetaString::GENERAL_TXT,243);
+			break;
+		default: //decision = cost in gold
+			ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
+			break;
+		}
+
+		ms.toString(hoverName);
+	}
+	else
+	{
+		hoverName = getHoverText(hero->tempOwner);
+	}
+
+	hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
+
+	int choice;
+	double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
+		 if (ratio < 0.1)  choice = 0;
+	else if (ratio < 0.25) choice = 1;
+	else if (ratio < 0.6)  choice = 2;
+	else if (ratio < 0.9)  choice = 3;
+	else if (ratio < 1.1)  choice = 4;
+	else if (ratio < 1.3)  choice = 5;
+	else if (ratio < 1.8)  choice = 6;
+	else if (ratio < 2.5)  choice = 7;
+	else if (ratio < 4)    choice = 8;
+	else if (ratio < 8)    choice = 9;
+	else if (ratio < 20)   choice = 10;
+	else                   choice = 11;
+
+	hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.levels." + std::to_string(choice));
+	return hoverName;
+}
+
+void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
+{
+	int action = takenAction(h);
+	switch( action ) //decide what we do...
+	{
+	case FIGHT:
+		fight(h);
+		break;
+	case FLEE:
+		{
+			flee(h);
+			break;
+		}
+	case JOIN_FOR_FREE: //join for free
+		{
+			BlockingDialog ynd(true,false);
+			ynd.player = h->tempOwner;
+			ynd.text.addTxt(MetaString::ADVOB_TXT, 86);
+			ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
+			cb->showBlockingDialog(&ynd);
+			break;
+		}
+	default: //join for gold
+		{
+			assert(action > 0);
+
+			//ask if player agrees to pay gold
+			BlockingDialog ynd(true,false);
+			ynd.player = h->tempOwner;
+			std::string tmp = VLC->generaltexth->advobtxt[90];
+			boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
+			boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
+			boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
+			ynd.text << tmp;
+			cb->showBlockingDialog(&ynd);
+			break;
+		}
+	}
+
+}
+
+void CGCreature::initObj(CRandomGenerator & rand)
+{
+	blockVisit = true;
+	switch(character)
+	{
+	case 0:
+		character = -4;
+		break;
+	case 1:
+		character = rand.nextInt(1, 7);
+		break;
+	case 2:
+		character = rand.nextInt(1, 10);
+		break;
+	case 3:
+		character = rand.nextInt(4, 10);
+		break;
+	case 4:
+		character = 10;
+		break;
+	}
+
+	stacks[SlotID(0)]->setType(CreatureID(subID));
+	TQuantity &amount = stacks[SlotID(0)]->count;
+	CCreature &c = *VLC->creh->objects[subID];
+	if(amount == 0)
+	{
+		amount = rand.nextInt(c.ammMin, c.ammMax);
+
+		if(amount == 0) //armies with 0 creatures are illegal
+		{
+			logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName());
+			amount = 1;
+		}
+	}
+
+	temppower = stacks[SlotID(0)]->count * static_cast<ui64>(1000);
+	refusedJoining = false;
+}
+
+void CGCreature::newTurn(CRandomGenerator & rand) const
+{//Works only for stacks of single type of size up to 2 millions
+	if (!notGrowingTeam)
+	{
+		if (stacks.begin()->second->count < VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
+		{
+			ui32 power = static_cast<ui32>(temppower * (100 + VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
+			cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
+			cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
+		}
+	}
+	if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
+		cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
+}
+void CGCreature::setPropertyDer(ui8 what, ui32 val)
+{
+	switch (what)
+	{
+		case ObjProperty::MONSTER_COUNT:
+			stacks[SlotID(0)]->count = val;
+			break;
+		case ObjProperty::MONSTER_POWER:
+			temppower = val;
+			break;
+		case ObjProperty::MONSTER_EXP:
+			giveStackExp(val);
+			break;
+		case ObjProperty::MONSTER_RESTORE_TYPE:
+			formation.basicType = val;
+			break;
+		case ObjProperty::MONSTER_REFUSED_JOIN:
+			refusedJoining = val;
+			break;
+	}
+}
+
+int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
+{
+	//calculate relative strength of hero and creatures armies
+	double relStrength = static_cast<double>(h->getTotalStrength()) / getArmyStrength();
+
+	int powerFactor;
+	if(relStrength >= 7)
+		powerFactor = 11;
+
+	else if(relStrength >= 1)
+		powerFactor = static_cast<int>(2 * (relStrength - 1));
+
+	else if(relStrength >= 0.5)
+		powerFactor = -1;
+
+	else if(relStrength >= 0.333)
+		powerFactor = -2;
+	else
+		powerFactor = -3;
+
+	std::set<CreatureID> myKindCres; //what creatures are the same kind as we
+	const CCreature * myCreature = VLC->creh->objects[subID];
+	myKindCres.insert(myCreature->getId()); //we
+	myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
+
+	for(ConstTransitivePtr<CCreature> &crea : VLC->creh->objects)
+	{
+		if(vstd::contains(crea->upgrades, myCreature->getId())) //it's our base creatures
+			myKindCres.insert(crea->getId());
+	}
+
+	int count = 0; //how many creatures of similar kind has hero
+	int totalCount = 0;
+
+	for(const auto & elem : h->Slots())
+	{
+		if(vstd::contains(myKindCres,elem.second->type->getId()))
+			count += elem.second->count;
+		totalCount += elem.second->count;
+	}
+
+	int sympathy = 0; // 0 if hero have no similar creatures
+	if(count)
+		sympathy++; // 1 - if hero have at least 1 similar creature
+	if(count*2 > totalCount)
+		sympathy++; // 2 - hero have similar creatures more that 50%
+
+	int diplomacy = h->valOfBonuses(BonusType::WANDERING_CREATURES_JOIN_BONUS);
+	int charisma = powerFactor + diplomacy + sympathy;
+
+	if(charisma < character)
+		return FIGHT;
+
+	if (allowJoin)
+	{
+		if(diplomacy + sympathy + 1 >= character)
+			return JOIN_FOR_FREE;
+
+		else if(diplomacy * 2  +  sympathy  +  1 >= character)
+			return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
+	}
+
+	//we are still here - creatures have not joined hero, flee or fight
+
+	if (charisma > character && !neverFlees)
+		return FLEE;
+	else
+		return FIGHT;
+}
+
+void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
+{
+	if(refusedJoining)
+		cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
+
+	if(pursue)
+	{
+		fight(h);
+	}
+	else
+	{
+		cb->removeObject(this);
+	}
+}
+
+void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
+{
+	if(!accept)
+	{
+		if(takenAction(h,false) == FLEE)
+		{
+			cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
+			flee(h);
+		}
+		else //they fight
+		{
+			h->showInfoDialog(87, 0, EInfoWindowMode::MODAL);//Insulted by your refusal of their offer, the monsters attack!
+			fight(h);
+		}
+	}
+	else //accepted
+	{
+		if (cb->getResource(h->tempOwner, EGameResID::GOLD) < cost) //player don't have enough gold!
+		{
+			InfoWindow iw;
+			iw.player = h->tempOwner;
+			iw.text << std::pair<ui8,ui32>(1,29);  //You don't have enough gold
+			cb->showInfoDialog(&iw);
+
+			//act as if player refused
+			joinDecision(h,cost,false);
+			return;
+		}
+
+		//take gold
+		if(cost)
+			cb->giveResource(h->tempOwner,EGameResID::GOLD,-cost);
+
+		giveReward(h);
+		cb->tryJoiningArmy(this, h, true, true);
+	}
+}
+
+void CGCreature::fight( const CGHeroInstance *h ) const
+{
+	//split stacks
+	//TODO: multiple creature types in a stack?
+	int basicType = stacks.begin()->second->type->getId();
+	cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
+
+	int stacksCount = getNumberOfStacks(h);
+	//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
+
+	int amount = getStackCount(SlotID(0));
+	int m = amount / stacksCount;
+	int b = stacksCount * (m + 1) - amount;
+	int a = stacksCount - b;
+
+	SlotID sourceSlot = stacks.begin()->first;
+	for (int slotID = 1; slotID < a; ++slotID)
+	{
+		int stackSize = m + 1;
+		cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
+	}
+	for (int slotID = a; slotID < stacksCount; ++slotID)
+	{
+		int stackSize = m;
+		if (slotID) //don't do this when a = 0 -> stack is single
+			cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
+	}
+	if (stacksCount > 1)
+	{
+		if (containsUpgradedStack()) //upgrade
+		{
+			SlotID slotID = SlotID(static_cast<si32>(std::floor(static_cast<float>(stacks.size()) / 2.0f)));
+			const auto & upgrades = getStack(slotID).type->upgrades;
+			if(!upgrades.empty())
+			{
+				auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
+				cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]);
+			}
+		}
+	}
+
+	cb->startBattleI(h, this);
+
+}
+
+void CGCreature::flee( const CGHeroInstance * h ) const
+{
+	BlockingDialog ynd(true,false);
+	ynd.player = h->tempOwner;
+	ynd.text.addTxt(MetaString::ADVOB_TXT,91);
+	ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
+	cb->showBlockingDialog(&ynd);
+}
+
+void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
+{
+	if(result.winner == 0)
+	{
+		giveReward(hero);
+		cb->removeObject(this);
+	}
+	else if(result.winner > 1) // draw
+	{
+		// guarded reward is lost forever on draw
+		cb->removeObject(this);
+	}
+	else
+	{
+		//merge stacks into one
+		TSlots::const_iterator i;
+		CCreature * cre = VLC->creh->objects[formation.basicType];
+		for(i = stacks.begin(); i != stacks.end(); i++)
+		{
+			if(cre->isMyUpgrade(i->second->type))
+			{
+				cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
+			}
+		}
+
+		//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
+		if(!hasStackAtSlot(SlotID(0)))
+			cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
+
+		while(stacks.size() > 1) //hopefully that's enough
+		{
+			// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
+			i = stacks.end();
+			i--;
+			SlotID slot = getSlotFor(i->second->type);
+			if(slot == i->first) //no reason to move stack to its own slot
+				break;
+			else
+				cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
+		}
+
+		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
+	}
+}
+
+void CGCreature::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
+{
+	auto action = takenAction(hero);
+	if(!refusedJoining && action >= JOIN_FOR_FREE) //higher means price
+		joinDecision(hero, action, answer);
+	else if(action != FIGHT)
+		fleeDecision(hero, answer);
+	else
+		assert(0);
+}
+
+bool CGCreature::containsUpgradedStack() const
+{
+	//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=830557#focus
+
+	float a = 2992.911117f;
+	float b = 14174.264968f;
+	float c = 5325.181015f;
+	float d = 32788.727920f;
+
+	int val = static_cast<int>(std::floor(a * pos.x + b * pos.y + c * pos.z + d));
+	return ((val % 32768) % 100) < 50;
+}
+
+int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
+{
+	//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266094#focus
+
+	double strengthRatio = static_cast<double>(hero->getArmyStrength()) / getArmyStrength();
+	int split = 1;
+
+	if (strengthRatio < 0.5f)
+		split = 7;
+	else if (strengthRatio < 0.67f)
+		split = 6;
+	else if (strengthRatio < 1)
+		split = 5;
+	else if (strengthRatio < 1.5f)
+		split = 4;
+	else if (strengthRatio < 2)
+		split = 3;
+	else
+		split = 2;
+
+	ui32 a = 1550811371u;
+	ui32 b = 3359066809u;
+	ui32 c = 1943276003u;
+	ui32 d = 3174620878u;
+
+	ui32 R1 = a * static_cast<ui32>(pos.x) + b * static_cast<ui32>(pos.y) + c * static_cast<ui32>(pos.z) + d;
+	ui32 R2 = (R1 >> 16) & 0x7fff;
+
+	int R4 = R2 % 100 + 1;
+
+	if (R4 <= 20)
+		split -= 1;
+	else if (R4 >= 80)
+		split += 1;
+
+	vstd::amin(split, getStack(SlotID(0)).count); //can't divide into more stacks than creatures total
+	vstd::amin(split, 7); //can't have more than 7 stacks
+
+	return split;
+}
+
+void CGCreature::giveReward(const CGHeroInstance * h) const
+{
+	InfoWindow iw;
+	iw.player = h->tempOwner;
+
+	if(!resources.empty())
+	{
+		cb->giveResources(h->tempOwner, resources);
+		for(int i = 0; i < resources.size(); i++)
+		{
+			if(resources[i] > 0)
+				iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0);
+		}
+	}
+
+	if(gainedArtifact != ArtifactID::NONE)
+	{
+		cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
+		iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0);
+	}
+
+	if(!iw.components.empty())
+	{
+		iw.type = EInfoWindowMode::AUTO;
+		iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
+		iw.text.addReplacement(h->getNameTranslated());
+		cb->showInfoDialog(&iw);
+	}
+}
+
+static const std::vector<std::string> CHARACTER_JSON  =
+{
+	"compliant", "friendly", "aggressive", "hostile", "savage"
+};
+
+void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
+{
+	handler.serializeEnum("character", character, CHARACTER_JSON);
+
+	if(handler.saving)
+	{
+		if(hasStackAtSlot(SlotID(0)))
+		{
+			si32 amount = getStack(SlotID(0)).count;
+			handler.serializeInt("amount", amount, 0);
+		}
+	}
+	else
+	{
+		si32 amount = 0;
+		handler.serializeInt("amount", amount);
+		auto * hlp = new CStackInstance();
+		hlp->count = amount;
+		//type will be set during initialization
+		putStack(SlotID(0), hlp);
+	}
+
+	resources.serializeJson(handler, "rewardResources");
+
+	handler.serializeId("rewardArtifact", gainedArtifact, ArtifactID(ArtifactID::NONE));
+
+	handler.serializeBool("noGrowing", notGrowingTeam);
+	handler.serializeBool("neverFlees", neverFlees);
+	handler.serializeString("rewardMessage", message);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 91 - 0
lib/mapObjects/CGCreature.h

@@ -0,0 +1,91 @@
+/*
+ * CGCreature.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "CArmedInstance.h"
+#include "../ResourceSet.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
+{
+public:
+	enum Action {
+		FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
+	};
+
+	enum Character {
+		COMPLIANT = 0, FRIENDLY = 1, AGRESSIVE = 2, HOSTILE = 3, SAVAGE = 4
+	};
+
+	ui32 identifier; //unique code for this monster (used in missions)
+	si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
+	std::string message; //message printed for attacking hero
+	TResources resources; // resources given to hero that has won with monsters
+	ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
+	bool neverFlees; //if true, the troops will never flee
+	bool notGrowingTeam; //if true, number of units won't grow
+	ui64 temppower; //used to handle fractional stack growth for tiny stacks
+
+	bool refusedJoining;
+
+	void onHeroVisit(const CGHeroInstance * h) const override;
+	std::string getHoverText(PlayerColor player) const override;
+	std::string getHoverText(const CGHeroInstance * hero) const override;
+	void initObj(CRandomGenerator & rand) override;
+	void newTurn(CRandomGenerator & rand) const override;
+	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
+	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
+
+	//stack formation depends on position,
+	bool containsUpgradedStack() const;
+	int getNumberOfStacks(const CGHeroInstance *hero) const;
+
+	struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
+	{
+		si32 basicType;
+		ui8 upgrade; //random seed used to determine number of stacks and is there's upgraded stack
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & basicType;
+			h & upgrade;
+		}
+	} formation;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CArmedInstance&>(*this);
+		h & identifier;
+		h & character;
+		h & message;
+		h & resources;
+		h & gainedArtifact;
+		h & neverFlees;
+		h & notGrowingTeam;
+		h & temppower;
+		h & refusedJoining;
+		h & formation;
+	}
+protected:
+	void setPropertyDer(ui8 what, ui32 val) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
+
+private:
+	void fight(const CGHeroInstance *h) const;
+	void flee( const CGHeroInstance * h ) const;
+	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
+	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
+
+	int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
+	void giveReward(const CGHeroInstance * h) const;
+
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/mapObjects/CQuest.cpp

@@ -18,7 +18,7 @@
 #include "../CSoundBase.h"
 #include "../CGeneralTextHandler.h"
 #include "../CHeroHandler.h"
-#include "MiscObjects.h"
+#include "CGCreature.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"

+ 0 - 554
lib/mapObjects/MiscObjects.cpp

@@ -15,16 +15,13 @@
 #include "../NetPacks.h"
 #include "../CGeneralTextHandler.h"
 #include "../CSoundBase.h"
-#include "../CConfigHandler.h"
 #include "../CModHandler.h"
-#include "../CHeroHandler.h"
 #include "../CSkillHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../IGameCallback.h"
 #include "../CGameState.h"
 #include "../mapping/CMap.h"
 #include "../CPlayerState.h"
-#include "../GameSettings.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
@@ -68,557 +65,6 @@ bool CTeamVisited::wasVisited(const TeamID & team) const
 	return false;
 }
 
-std::string CGCreature::getHoverText(PlayerColor player) const
-{
-	if(stacks.empty())
-	{
-		//should not happen...
-		logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum());
-		return "INVALID_STACK";
-	}
-
-	std::string hoverName;
-	MetaString ms;
-	CCreature::CreatureQuantityId monsterQuantityId = stacks.begin()->second->getQuantityID();
-	int quantityTextIndex = 172 + 3 * (int)monsterQuantityId;
-	if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
-		ms << CCreature::getQuantityRangeStringForId(monsterQuantityId);
-	else
-		ms.addTxt(MetaString::ARRAY_TXT, quantityTextIndex);
-	ms << " " ;
-	ms.addTxt(MetaString::CRE_PL_NAMES,subID);
-	ms.toString(hoverName);
-	return hoverName;
-}
-
-std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
-{
-	std::string hoverName;
-	if(hero->hasVisions(this, 0))
-	{
-		MetaString ms;
-		ms << stacks.begin()->second->count;
-		ms << " " ;
-		ms.addTxt(MetaString::CRE_PL_NAMES,subID);
-
-		ms << "\n";
-
-		int decision = takenAction(hero, true);
-
-		switch (decision)
-		{
-		case FIGHT:
-			ms.addTxt(MetaString::GENERAL_TXT,246);
-			break;
-		case FLEE:
-			ms.addTxt(MetaString::GENERAL_TXT,245);
-			break;
-		case JOIN_FOR_FREE:
-			ms.addTxt(MetaString::GENERAL_TXT,243);
-			break;
-		default: //decision = cost in gold
-			ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
-			break;
-		}
-
-		ms.toString(hoverName);
-	}
-	else
-	{
-		hoverName = getHoverText(hero->tempOwner);
-	}
-
-	hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
-
-	int choice;
-	double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
-		 if (ratio < 0.1)  choice = 0;
-	else if (ratio < 0.25) choice = 1;
-	else if (ratio < 0.6)  choice = 2;
-	else if (ratio < 0.9)  choice = 3;
-	else if (ratio < 1.1)  choice = 4;
-	else if (ratio < 1.3)  choice = 5;
-	else if (ratio < 1.8)  choice = 6;
-	else if (ratio < 2.5)  choice = 7;
-	else if (ratio < 4)    choice = 8;
-	else if (ratio < 8)    choice = 9;
-	else if (ratio < 20)   choice = 10;
-	else                   choice = 11;
-
-	hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.levels." + std::to_string(choice));
-	return hoverName;
-}
-
-void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
-{
-	int action = takenAction(h);
-	switch( action ) //decide what we do...
-	{
-	case FIGHT:
-		fight(h);
-		break;
-	case FLEE:
-		{
-			flee(h);
-			break;
-		}
-	case JOIN_FOR_FREE: //join for free
-		{
-			BlockingDialog ynd(true,false);
-			ynd.player = h->tempOwner;
-			ynd.text.addTxt(MetaString::ADVOB_TXT, 86);
-			ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
-			cb->showBlockingDialog(&ynd);
-			break;
-		}
-	default: //join for gold
-		{
-			assert(action > 0);
-
-			//ask if player agrees to pay gold
-			BlockingDialog ynd(true,false);
-			ynd.player = h->tempOwner;
-			std::string tmp = VLC->generaltexth->advobtxt[90];
-			boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
-			boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
-			boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
-			ynd.text << tmp;
-			cb->showBlockingDialog(&ynd);
-			break;
-		}
-	}
-
-}
-
-void CGCreature::initObj(CRandomGenerator & rand)
-{
-	blockVisit = true;
-	switch(character)
-	{
-	case 0:
-		character = -4;
-		break;
-	case 1:
-		character = rand.nextInt(1, 7);
-		break;
-	case 2:
-		character = rand.nextInt(1, 10);
-		break;
-	case 3:
-		character = rand.nextInt(4, 10);
-		break;
-	case 4:
-		character = 10;
-		break;
-	}
-
-	stacks[SlotID(0)]->setType(CreatureID(subID));
-	TQuantity &amount = stacks[SlotID(0)]->count;
-	CCreature &c = *VLC->creh->objects[subID];
-	if(amount == 0)
-	{
-		amount = rand.nextInt(c.ammMin, c.ammMax);
-
-		if(amount == 0) //armies with 0 creatures are illegal
-		{
-			logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName());
-			amount = 1;
-		}
-	}
-
-	temppower = stacks[SlotID(0)]->count * static_cast<ui64>(1000);
-	refusedJoining = false;
-}
-
-void CGCreature::newTurn(CRandomGenerator & rand) const
-{//Works only for stacks of single type of size up to 2 millions
-	if (!notGrowingTeam)
-	{
-		if (stacks.begin()->second->count < VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
-		{
-			ui32 power = static_cast<ui32>(temppower * (100 + VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
-			cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
-			cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
-		}
-	}
-	if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
-		cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
-}
-void CGCreature::setPropertyDer(ui8 what, ui32 val)
-{
-	switch (what)
-	{
-		case ObjProperty::MONSTER_COUNT:
-			stacks[SlotID(0)]->count = val;
-			break;
-		case ObjProperty::MONSTER_POWER:
-			temppower = val;
-			break;
-		case ObjProperty::MONSTER_EXP:
-			giveStackExp(val);
-			break;
-		case ObjProperty::MONSTER_RESTORE_TYPE:
-			formation.basicType = val;
-			break;
-		case ObjProperty::MONSTER_REFUSED_JOIN:
-			refusedJoining = val;
-			break;
-	}
-}
-
-int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
-{
-	//calculate relative strength of hero and creatures armies
-	double relStrength = static_cast<double>(h->getTotalStrength()) / getArmyStrength();
-
-	int powerFactor;
-	if(relStrength >= 7)
-		powerFactor = 11;
-
-	else if(relStrength >= 1)
-		powerFactor = static_cast<int>(2 * (relStrength - 1));
-
-	else if(relStrength >= 0.5)
-		powerFactor = -1;
-
-	else if(relStrength >= 0.333)
-		powerFactor = -2;
-	else
-		powerFactor = -3;
-
-	std::set<CreatureID> myKindCres; //what creatures are the same kind as we
-	const CCreature * myCreature = VLC->creh->objects[subID];
-	myKindCres.insert(myCreature->getId()); //we
-	myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
-
-	for(ConstTransitivePtr<CCreature> &crea : VLC->creh->objects)
-	{
-		if(vstd::contains(crea->upgrades, myCreature->getId())) //it's our base creatures
-			myKindCres.insert(crea->getId());
-	}
-
-	int count = 0; //how many creatures of similar kind has hero
-	int totalCount = 0;
-
-	for(const auto & elem : h->Slots())
-	{
-		if(vstd::contains(myKindCres,elem.second->type->getId()))
-			count += elem.second->count;
-		totalCount += elem.second->count;
-	}
-
-	int sympathy = 0; // 0 if hero have no similar creatures
-	if(count)
-		sympathy++; // 1 - if hero have at least 1 similar creature
-	if(count*2 > totalCount)
-		sympathy++; // 2 - hero have similar creatures more that 50%
-
-	int diplomacy = h->valOfBonuses(BonusType::WANDERING_CREATURES_JOIN_BONUS);
-	int charisma = powerFactor + diplomacy + sympathy;
-
-	if(charisma < character)
-		return FIGHT;
-
-	if (allowJoin)
-	{
-		if(diplomacy + sympathy + 1 >= character)
-			return JOIN_FOR_FREE;
-
-		else if(diplomacy * 2  +  sympathy  +  1 >= character)
-			return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
-	}
-
-	//we are still here - creatures have not joined hero, flee or fight
-
-	if (charisma > character && !neverFlees)
-		return FLEE;
-	else
-		return FIGHT;
-}
-
-void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
-{
-	if(refusedJoining)
-		cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
-
-	if(pursue)
-	{
-		fight(h);
-	}
-	else
-	{
-		cb->removeObject(this);
-	}
-}
-
-void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
-{
-	if(!accept)
-	{
-		if(takenAction(h,false) == FLEE)
-		{
-			cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
-			flee(h);
-		}
-		else //they fight
-		{
-			h->showInfoDialog(87, 0, EInfoWindowMode::MODAL);//Insulted by your refusal of their offer, the monsters attack!
-			fight(h);
-		}
-	}
-	else //accepted
-	{
-		if (cb->getResource(h->tempOwner, EGameResID::GOLD) < cost) //player don't have enough gold!
-		{
-			InfoWindow iw;
-			iw.player = h->tempOwner;
-			iw.text << std::pair<ui8,ui32>(1,29);  //You don't have enough gold
-			cb->showInfoDialog(&iw);
-
-			//act as if player refused
-			joinDecision(h,cost,false);
-			return;
-		}
-
-		//take gold
-		if(cost)
-			cb->giveResource(h->tempOwner,EGameResID::GOLD,-cost);
-
-		giveReward(h);
-		cb->tryJoiningArmy(this, h, true, true);
-	}
-}
-
-void CGCreature::fight( const CGHeroInstance *h ) const
-{
-	//split stacks
-	//TODO: multiple creature types in a stack?
-	int basicType = stacks.begin()->second->type->getId();
-	cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
-
-	int stacksCount = getNumberOfStacks(h);
-	//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
-
-	int amount = getStackCount(SlotID(0));
-	int m = amount / stacksCount;
-	int b = stacksCount * (m + 1) - amount;
-	int a = stacksCount - b;
-
-	SlotID sourceSlot = stacks.begin()->first;
-	for (int slotID = 1; slotID < a; ++slotID)
-	{
-		int stackSize = m + 1;
-		cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
-	}
-	for (int slotID = a; slotID < stacksCount; ++slotID)
-	{
-		int stackSize = m;
-		if (slotID) //don't do this when a = 0 -> stack is single
-			cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
-	}
-	if (stacksCount > 1)
-	{
-		if (containsUpgradedStack()) //upgrade
-		{
-			SlotID slotID = SlotID(static_cast<si32>(std::floor(static_cast<float>(stacks.size()) / 2.0f)));
-			const auto & upgrades = getStack(slotID).type->upgrades;
-			if(!upgrades.empty())
-			{
-				auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
-				cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]);
-			}
-		}
-	}
-
-	cb->startBattleI(h, this);
-
-}
-
-void CGCreature::flee( const CGHeroInstance * h ) const
-{
-	BlockingDialog ynd(true,false);
-	ynd.player = h->tempOwner;
-	ynd.text.addTxt(MetaString::ADVOB_TXT,91);
-	ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
-	cb->showBlockingDialog(&ynd);
-}
-
-void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
-{
-	if(result.winner == 0)
-	{
-		giveReward(hero);
-		cb->removeObject(this);
-	}
-	else if(result.winner > 1) // draw
-	{
-		// guarded reward is lost forever on draw
-		cb->removeObject(this);
-	}
-	else
-	{
-		//merge stacks into one
-		TSlots::const_iterator i;
-		CCreature * cre = VLC->creh->objects[formation.basicType];
-		for(i = stacks.begin(); i != stacks.end(); i++)
-		{
-			if(cre->isMyUpgrade(i->second->type))
-			{
-				cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
-			}
-		}
-
-		//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
-		if(!hasStackAtSlot(SlotID(0)))
-			cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
-
-		while(stacks.size() > 1) //hopefully that's enough
-		{
-			// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
-			i = stacks.end();
-			i--;
-			SlotID slot = getSlotFor(i->second->type);
-			if(slot == i->first) //no reason to move stack to its own slot
-				break;
-			else
-				cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
-		}
-
-		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
-	}
-}
-
-void CGCreature::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	auto action = takenAction(hero);
-	if(!refusedJoining && action >= JOIN_FOR_FREE) //higher means price
-		joinDecision(hero, action, answer);
-	else if(action != FIGHT)
-		fleeDecision(hero, answer);
-	else
-		assert(0);
-}
-
-bool CGCreature::containsUpgradedStack() const
-{
-	//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=830557#focus
-
-	float a = 2992.911117f;
-	float b = 14174.264968f;
-	float c = 5325.181015f;
-	float d = 32788.727920f;
-
-	int val = static_cast<int>(std::floor(a * pos.x + b * pos.y + c * pos.z + d));
-	return ((val % 32768) % 100) < 50;
-}
-
-int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
-{
-	//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266094#focus
-
-	double strengthRatio = static_cast<double>(hero->getArmyStrength()) / getArmyStrength();
-	int split = 1;
-
-	if (strengthRatio < 0.5f)
-		split = 7;
-	else if (strengthRatio < 0.67f)
-		split = 6;
-	else if (strengthRatio < 1)
-		split = 5;
-	else if (strengthRatio < 1.5f)
-		split = 4;
-	else if (strengthRatio < 2)
-		split = 3;
-	else
-		split = 2;
-
-	ui32 a = 1550811371u;
-	ui32 b = 3359066809u;
-	ui32 c = 1943276003u;
-	ui32 d = 3174620878u;
-
-	ui32 R1 = a * static_cast<ui32>(pos.x) + b * static_cast<ui32>(pos.y) + c * static_cast<ui32>(pos.z) + d;
-	ui32 R2 = (R1 >> 16) & 0x7fff;
-
-	int R4 = R2 % 100 + 1;
-
-	if (R4 <= 20)
-		split -= 1;
-	else if (R4 >= 80)
-		split += 1;
-
-	vstd::amin(split, getStack(SlotID(0)).count); //can't divide into more stacks than creatures total
-	vstd::amin(split, 7); //can't have more than 7 stacks
-
-	return split;
-}
-
-void CGCreature::giveReward(const CGHeroInstance * h) const
-{
-	InfoWindow iw;
-	iw.player = h->tempOwner;
-
-	if(!resources.empty())
-	{
-		cb->giveResources(h->tempOwner, resources);
-		for(int i = 0; i < resources.size(); i++)
-		{
-			if(resources[i] > 0)
-				iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0);
-		}
-	}
-
-	if(gainedArtifact != ArtifactID::NONE)
-	{
-		cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
-		iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0);
-	}
-
-	if(!iw.components.empty())
-	{
-		iw.type = EInfoWindowMode::AUTO;
-		iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
-		iw.text.addReplacement(h->getNameTranslated());
-		cb->showInfoDialog(&iw);
-	}
-}
-
-static const std::vector<std::string> CHARACTER_JSON  =
-{
-	"compliant", "friendly", "aggressive", "hostile", "savage"
-};
-
-void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
-{
-	handler.serializeEnum("character", character, CHARACTER_JSON);
-
-	if(handler.saving)
-	{
-		if(hasStackAtSlot(SlotID(0)))
-		{
-			si32 amount = getStack(SlotID(0)).count;
-			handler.serializeInt("amount", amount, 0);
-		}
-	}
-	else
-	{
-		si32 amount = 0;
-		handler.serializeInt("amount", amount);
-		auto * hlp = new CStackInstance();
-		hlp->count = amount;
-		//type will be set during initialization
-		putStack(SlotID(0), hlp);
-	}
-
-	resources.serializeJson(handler, "rewardResources");
-
-	handler.serializeId("rewardArtifact", gainedArtifact, ArtifactID(ArtifactID::NONE));
-
-	handler.serializeBool("noGrowing", notGrowingTeam);
-	handler.serializeBool("neverFlees", neverFlees);
-	handler.serializeString("rewardMessage", message);
-}
-
 //CGMine
 void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 {

+ 0 - 75
lib/mapObjects/MiscObjects.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "CArmedInstance.h"
-#include "../ResourceSet.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -40,80 +39,6 @@ public:
 	static constexpr int OBJPROP_VISITED = 10;
 };
 
-class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
-{
-public:
-	enum Action {
-		FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
-	};
-
-	enum Character {
-		COMPLIANT = 0, FRIENDLY = 1, AGRESSIVE = 2, HOSTILE = 3, SAVAGE = 4
-	};
-
-	ui32 identifier; //unique code for this monster (used in missions)
-	si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
-	std::string message; //message printed for attacking hero
-	TResources resources; // resources given to hero that has won with monsters
-	ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
-	bool neverFlees; //if true, the troops will never flee
-	bool notGrowingTeam; //if true, number of units won't grow
-	ui64 temppower; //used to handle fractional stack growth for tiny stacks
-
-	bool refusedJoining;
-
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	std::string getHoverText(PlayerColor player) const override;
-	std::string getHoverText(const CGHeroInstance * hero) const override;
-	void initObj(CRandomGenerator & rand) override;
-	void newTurn(CRandomGenerator & rand) const override;
-	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	//stack formation depends on position,
-	bool containsUpgradedStack() const;
-	int getNumberOfStacks(const CGHeroInstance *hero) const;
-
-	struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
-	{
-		si32 basicType;
-		ui8 upgrade; //random seed used to determine number of stacks and is there's upgraded stack
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & basicType;
-			h & upgrade;
-		}
-	} formation;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CArmedInstance&>(*this);
-		h & identifier;
-		h & character;
-		h & message;
-		h & resources;
-		h & gainedArtifact;
-		h & neverFlees;
-		h & notGrowingTeam;
-		h & temppower;
-		h & refusedJoining;
-		h & formation;
-	}
-protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
-	void serializeJsonOptions(JsonSerializeFormat & handler) override;
-
-private:
-	void fight(const CGHeroInstance *h) const;
-	void flee( const CGHeroInstance * h ) const;
-	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
-	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
-
-	int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
-	void giveReward(const CGHeroInstance * h) const;
-
-};
-
 class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
 {
 public:

+ 1 - 0
lib/mapping/MapFormatH3M.cpp

@@ -32,6 +32,7 @@
 #include "../filesystem/Filesystem.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../mapObjects/CGCreature.h"
 #include "../mapObjects/MapObjects.h"
 #include "../mapObjects/ObjectTemplate.h"
 #include "../spells/CSpellHandler.h"

+ 1 - 0
lib/registerTypes/RegisterTypes.h

@@ -26,6 +26,7 @@
 #include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
 #include "../mapObjectConstructors/ShrineInstanceConstructor.h"
 #include "../mapObjects/MapObjects.h"
+#include "../mapObjects/CGCreature.h"
 #include "../mapObjects/CGTownBuilding.h"
 #include "../mapObjects/ObjectTemplate.h"
 #include "../battle/CObstacleInstance.h"

+ 1 - 0
lib/rmg/modificators/ConnectionsPlacer.cpp

@@ -15,6 +15,7 @@
 #include "../../TerrainHandler.h"
 #include "../../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../../mapObjects/CGCreature.h"
 #include "../../mapping/CMapEditManager.h"
 #include "../RmgObject.h"
 #include "ObjectManager.h"

+ 1 - 0
lib/rmg/modificators/ObjectManager.cpp

@@ -23,6 +23,7 @@
 #include "../../CCreatureHandler.h"
 #include "../../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../../mapObjects/CGCreature.h"
 #include "../../mapping/CMap.h"
 #include "../../mapping/CMapEditManager.h"
 #include "../Functions.h"

+ 1 - 0
mapeditor/inspector/inspector.h

@@ -15,6 +15,7 @@
 #include <QStyledItemDelegate>
 #include "../lib/int3.h"
 #include "../lib/GameConstants.h"
+#include "../lib/mapObjects/CGCreature.h"
 #include "../lib/mapObjects/MapObjects.h"
 #include "../lib/ResourceSet.h"
 

+ 1 - 1
mapeditor/mapsettings.cpp

@@ -19,7 +19,7 @@
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CModHandler.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
-#include "../lib/mapObjects/MiscObjects.h"
+#include "../lib/mapObjects/CGCreature.h"
 #include "../lib/mapping/CMapService.h"
 #include "../lib/StringConstants.h"
 #include "inspector/townbulidingswidget.h" //to convert BuildingID to string