|
@@ -58,19 +58,28 @@ void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroS
|
|
|
gameHandler->sendAndApply(&sah);
|
|
|
}
|
|
|
|
|
|
-void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot)
|
|
|
+void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveArmy)
|
|
|
{
|
|
|
SetAvailableHero sah;
|
|
|
sah.player = color;
|
|
|
sah.slotID = static_cast<int>(slot);
|
|
|
|
|
|
//first hero - native if possible, second hero -> any other class
|
|
|
- CGHeroInstance *h = gameHandler->gameState()->hpool->pickHeroFor(slot, color, gameHandler->getPlayerSettings(color)->castle, gameHandler->getRandomGenerator());
|
|
|
+ CGHeroInstance *h = pickHeroFor(needNativeHero, color, gameHandler->getPlayerSettings(color)->castle, gameHandler->getRandomGenerator(), nullptr);
|
|
|
|
|
|
if (h)
|
|
|
{
|
|
|
sah.hid = h->subID;
|
|
|
- h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
|
|
|
+
|
|
|
+ if (giveArmy)
|
|
|
+ {
|
|
|
+ h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sah.army.clear();
|
|
|
+ sah.army.setCreature(SlotID(0), h->type->initialArmy[0].creature, 1);
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -83,8 +92,8 @@ void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
|
|
|
{
|
|
|
clearHeroFromSlot(color, TavernHeroSlot::NATIVE);
|
|
|
clearHeroFromSlot(color, TavernHeroSlot::RANDOM);
|
|
|
- selectNewHeroForSlot(color, TavernHeroSlot::NATIVE);
|
|
|
- selectNewHeroForSlot(color, TavernHeroSlot::RANDOM);
|
|
|
+ selectNewHeroForSlot(color, TavernHeroSlot::NATIVE, true, true);
|
|
|
+ selectNewHeroForSlot(color, TavernHeroSlot::RANDOM, false, true);
|
|
|
}
|
|
|
|
|
|
bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & heroToRecruit, const PlayerColor & player)
|
|
@@ -101,34 +110,34 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
|
|
|
if (gameHandler->getHeroCount(player, true) >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && gameHandler->complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))
|
|
|
return false;
|
|
|
|
|
|
- if (town) //tavern in town
|
|
|
+ if(town) //tavern in town
|
|
|
{
|
|
|
- if (!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
|
|
|
+ if(!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
|
|
|
return false;
|
|
|
|
|
|
- if (town->visitingHero && gameHandler->complain("There is visiting hero - no place!"))
|
|
|
+ if(town->visitingHero && gameHandler->complain("There is visiting hero - no place!"))
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- if (obj->ID == Obj::TAVERN)
|
|
|
+ if(obj->ID == Obj::TAVERN)
|
|
|
{
|
|
|
- if (gameHandler->getTile(obj->visitablePos())->visitableObjects.back() != obj && gameHandler->complain("Tavern entry must be unoccupied!"))
|
|
|
+ if(gameHandler->getTile(obj->visitablePos())->visitableObjects.back() != obj && gameHandler->complain("Tavern entry must be unoccupied!"))
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
auto recruitableHeroes = gameHandler->gameState()->hpool->getHeroesFor(player);
|
|
|
|
|
|
- const CGHeroInstance *recruitedHero = nullptr;;
|
|
|
+ const CGHeroInstance * recruitedHero = nullptr;
|
|
|
|
|
|
for(const auto & hero : recruitableHeroes)
|
|
|
{
|
|
|
- if (hero->subID == heroToRecruit)
|
|
|
+ if(hero->subID == heroToRecruit)
|
|
|
recruitedHero = hero;
|
|
|
}
|
|
|
|
|
|
- if (!recruitedHero)
|
|
|
+ if(!recruitedHero)
|
|
|
{
|
|
|
- gameHandler->complain ("Hero is not available for hiring!");
|
|
|
+ gameHandler->complain("Hero is not available for hiring!");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -137,19 +146,21 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
|
|
|
hr.hid = recruitedHero->subID;
|
|
|
hr.player = player;
|
|
|
hr.tile = recruitedHero->convertFromVisitablePos(obj->visitablePos());
|
|
|
- if (gameHandler->getTile(hr.tile)->isWater())
|
|
|
+ if(gameHandler->getTile(hr.tile)->isWater())
|
|
|
{
|
|
|
//Create a new boat for hero
|
|
|
gameHandler->createObject(obj->visitablePos(), Obj::BOAT, recruitedHero->getBoatType().getNum());
|
|
|
|
|
|
hr.boatId = gameHandler->getTopObj(hr.tile)->id;
|
|
|
}
|
|
|
+
|
|
|
+ // apply netpack -> this will remove hired hero from tavern slot
|
|
|
gameHandler->sendAndApply(&hr);
|
|
|
|
|
|
- if (recruitableHeroes[0] == recruitedHero)
|
|
|
- selectNewHeroForSlot(player, TavernHeroSlot::NATIVE);
|
|
|
+ if(recruitableHeroes[0] == recruitedHero)
|
|
|
+ selectNewHeroForSlot(player, TavernHeroSlot::NATIVE, false, false);
|
|
|
else
|
|
|
- selectNewHeroForSlot(player, TavernHeroSlot::RANDOM);
|
|
|
+ selectNewHeroForSlot(player, TavernHeroSlot::RANDOM, false, false);
|
|
|
|
|
|
gameHandler->giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST);
|
|
|
|
|
@@ -160,3 +171,70 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative,
|
|
|
+ const PlayerColor & player,
|
|
|
+ const FactionID & factionID,
|
|
|
+ CRandomGenerator & rand,
|
|
|
+ const CHeroClass * bannedClass) const
|
|
|
+{
|
|
|
+ if(player >= PlayerColor::PLAYER_LIMIT)
|
|
|
+ {
|
|
|
+ logGlobal->error("Cannot pick hero for player %d. Wrong owner!", player.getStr());
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto & hpool = gameHandler->gameState()->hpool;
|
|
|
+
|
|
|
+ if(isNative)
|
|
|
+ {
|
|
|
+ std::vector<CGHeroInstance *> pool;
|
|
|
+
|
|
|
+ for(auto & elem : hpool->unusedHeroesFromPool())
|
|
|
+ {
|
|
|
+ //get all available heroes
|
|
|
+ bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player);
|
|
|
+ bool heroClassNative = elem.second->type->heroClass->faction == factionID;
|
|
|
+
|
|
|
+ if(heroAvailable && heroClassNative)
|
|
|
+ pool.push_back(elem.second);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!pool.empty())
|
|
|
+ return *RandomGeneratorUtil::nextItem(pool, rand);
|
|
|
+
|
|
|
+ logGlobal->error("Cannot pick native hero for %s. Picking any...", player.getStr());
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<CGHeroInstance *> pool;
|
|
|
+ int totalWeight = 0;
|
|
|
+
|
|
|
+ for(auto & elem : hpool->unusedHeroesFromPool())
|
|
|
+ {
|
|
|
+ bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player);
|
|
|
+ bool heroClassBanned = bannedClass && elem.second->type->heroClass == bannedClass;
|
|
|
+
|
|
|
+ if(heroAvailable && !heroClassBanned)
|
|
|
+ {
|
|
|
+ pool.push_back(elem.second);
|
|
|
+ totalWeight += elem.second->type->heroClass->selectionProbability[factionID]; //total weight
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(pool.empty() || totalWeight == 0)
|
|
|
+ {
|
|
|
+ logGlobal->error("There are no heroes available for player %s!", player.getStr());
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ int roll = rand.nextInt(totalWeight - 1);
|
|
|
+ for(auto & elem : pool)
|
|
|
+ {
|
|
|
+ roll -= elem->type->heroClass->selectionProbability[factionID];
|
|
|
+ if(roll < 0)
|
|
|
+ {
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return pool.back();
|
|
|
+}
|