mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-19 00:17:56 +02:00
Implemented tavern slot selection using rules similar to H3
This commit is contained in:
@ -396,6 +396,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
|||||||
${MAIN_LIB_DIR}/gameState/InfoAboutArmy.h
|
${MAIN_LIB_DIR}/gameState/InfoAboutArmy.h
|
||||||
${MAIN_LIB_DIR}/gameState/SThievesGuildInfo.h
|
${MAIN_LIB_DIR}/gameState/SThievesGuildInfo.h
|
||||||
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.h
|
${MAIN_LIB_DIR}/gameState/TavernHeroesPool.h
|
||||||
|
${MAIN_LIB_DIR}/gameState/TavernSlot.h
|
||||||
${MAIN_LIB_DIR}/gameState/QuestInfo.h
|
${MAIN_LIB_DIR}/gameState/QuestInfo.h
|
||||||
|
|
||||||
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.h
|
${MAIN_LIB_DIR}/logging/CBasicLogConfigurator.h
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "battle/BattleAction.h"
|
#include "battle/BattleAction.h"
|
||||||
#include "battle/CObstacleInstance.h"
|
#include "battle/CObstacleInstance.h"
|
||||||
#include "gameState/EVictoryLossCheckResult.h"
|
#include "gameState/EVictoryLossCheckResult.h"
|
||||||
|
#include "gameState/TavernSlot.h"
|
||||||
#include "gameState/QuestInfo.h"
|
#include "gameState/QuestInfo.h"
|
||||||
#include "mapObjects/CGHeroInstance.h"
|
#include "mapObjects/CGHeroInstance.h"
|
||||||
#include "mapping/CMapDefines.h"
|
#include "mapping/CMapDefines.h"
|
||||||
@ -338,7 +339,8 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient
|
|||||||
}
|
}
|
||||||
void applyGs(CGameState * gs);
|
void applyGs(CGameState * gs);
|
||||||
|
|
||||||
uint8_t slotID;
|
TavernHeroSlot slotID;
|
||||||
|
TavernSlotRole roleID;
|
||||||
PlayerColor player;
|
PlayerColor player;
|
||||||
HeroTypeID hid; //-1 if no hero
|
HeroTypeID hid; //-1 if no hero
|
||||||
CSimpleArmy army;
|
CSimpleArmy army;
|
||||||
@ -348,6 +350,7 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient
|
|||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & slotID;
|
h & slotID;
|
||||||
|
h & roleID;
|
||||||
h & player;
|
h & player;
|
||||||
h & hid;
|
h & hid;
|
||||||
h & army;
|
h & army;
|
||||||
|
@ -942,7 +942,7 @@ void FoWChange::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
void SetAvailableHero::applyGs(CGameState *gs)
|
void SetAvailableHero::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
gs->hpool->setHeroForPlayer(player, TavernHeroSlot(slotID), hid, army);
|
gs->hpool->setHeroForPlayer(player, slotID, hid, army, roleID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GiveBonus::applyGs(CGameState *gs)
|
void GiveBonus::applyGs(CGameState *gs)
|
||||||
|
@ -23,17 +23,27 @@ TavernHeroesPool::~TavernHeroesPool()
|
|||||||
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const
|
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const
|
||||||
{
|
{
|
||||||
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
|
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
|
||||||
for(const auto & player : currentTavern)
|
for(const auto & slot : currentTavern)
|
||||||
for(auto availableHero : player.second)
|
pool.erase(HeroTypeID(slot.hero->subID));
|
||||||
if(availableHero.second)
|
|
||||||
pool.erase(HeroTypeID(availableHero.second->subID));
|
|
||||||
|
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TavernHeroesPool::setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army)
|
TavernSlotRole TavernHeroesPool::getSlotRole(HeroTypeID hero) const
|
||||||
{
|
{
|
||||||
currentTavern[player].erase(slot);
|
for (auto const & slot : currentTavern)
|
||||||
|
{
|
||||||
|
if (HeroTypeID(slot.hero->subID) == hero)
|
||||||
|
return slot.role;
|
||||||
|
}
|
||||||
|
return TavernSlotRole::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TavernHeroesPool::setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army, TavernSlotRole role)
|
||||||
|
{
|
||||||
|
vstd::erase_if(currentTavern, [&](const TavernSlot & entry){
|
||||||
|
return entry.player == player && entry.slot == slot;
|
||||||
|
});
|
||||||
|
|
||||||
if (hero == HeroTypeID::NONE)
|
if (hero == HeroTypeID::NONE)
|
||||||
return;
|
return;
|
||||||
@ -43,7 +53,21 @@ void TavernHeroesPool::setHeroForPlayer(PlayerColor player, TavernHeroSlot slot,
|
|||||||
if (h && army)
|
if (h && army)
|
||||||
h->setToArmy(army);
|
h->setToArmy(army);
|
||||||
|
|
||||||
currentTavern[player][slot] = h;
|
TavernSlot newSlot;
|
||||||
|
newSlot.hero = h;
|
||||||
|
newSlot.player = player;
|
||||||
|
newSlot.role = TavernSlotRole::SINGLE_UNIT; // TODO
|
||||||
|
newSlot.slot = slot;
|
||||||
|
|
||||||
|
currentTavern.push_back(newSlot);
|
||||||
|
|
||||||
|
boost::range::sort(currentTavern, [](const TavernSlot & left, const TavernSlot & right)
|
||||||
|
{
|
||||||
|
if (left.slot == right.slot)
|
||||||
|
return left.player < right.player;
|
||||||
|
else
|
||||||
|
return left.slot < right.slot;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const
|
bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const
|
||||||
@ -58,11 +82,11 @@ std::vector<const CGHeroInstance *> TavernHeroesPool::getHeroesFor(PlayerColor c
|
|||||||
{
|
{
|
||||||
std::vector<const CGHeroInstance *> result;
|
std::vector<const CGHeroInstance *> result;
|
||||||
|
|
||||||
if(!currentTavern.count(color))
|
for(const auto & slot : currentTavern)
|
||||||
return result;
|
{
|
||||||
|
if (slot.player == color)
|
||||||
for(const auto & hero : currentTavern.at(color))
|
result.push_back(slot.hero);
|
||||||
result.push_back(hero.second);
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../ConstTransitivePtr.h"
|
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
#include "TavernSlot.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -21,14 +21,24 @@ class CHeroClass;
|
|||||||
class CGameState;
|
class CGameState;
|
||||||
class CSimpleArmy;
|
class CSimpleArmy;
|
||||||
|
|
||||||
enum class TavernHeroSlot
|
|
||||||
{
|
|
||||||
NATIVE, // 1st / left slot in tavern, contains hero native to player's faction on new week
|
|
||||||
RANDOM // 2nd / right slot in tavern, contains hero of random class
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE TavernHeroesPool
|
class DLL_LINKAGE TavernHeroesPool
|
||||||
{
|
{
|
||||||
|
struct TavernSlot
|
||||||
|
{
|
||||||
|
CGHeroInstance * hero;
|
||||||
|
TavernHeroSlot slot;
|
||||||
|
TavernSlotRole role;
|
||||||
|
PlayerColor player;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & hero;
|
||||||
|
h & slot;
|
||||||
|
h & role;
|
||||||
|
h & player;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// list of all heroes in pool, including those currently present in taverns
|
/// list of all heroes in pool, including those currently present in taverns
|
||||||
std::map<HeroTypeID, CGHeroInstance* > heroesPool;
|
std::map<HeroTypeID, CGHeroInstance* > heroesPool;
|
||||||
|
|
||||||
@ -36,8 +46,8 @@ class DLL_LINKAGE TavernHeroesPool
|
|||||||
/// if hero is not present in list, he is available for everyone
|
/// if hero is not present in list, he is available for everyone
|
||||||
std::map<HeroTypeID, PlayerColor::Mask> pavailable;
|
std::map<HeroTypeID, PlayerColor::Mask> pavailable;
|
||||||
|
|
||||||
/// list of heroes currently available in a tavern of a specific player
|
/// list of heroes currently available in taverns
|
||||||
std::map<PlayerColor, std::map<TavernHeroSlot, CGHeroInstance*> > currentTavern;
|
std::vector<TavernSlot> currentTavern;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~TavernHeroesPool();
|
~TavernHeroesPool();
|
||||||
@ -51,6 +61,8 @@ public:
|
|||||||
/// Returns true if hero is available to a specific player
|
/// Returns true if hero is available to a specific player
|
||||||
bool isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const;
|
bool isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const;
|
||||||
|
|
||||||
|
TavernSlotRole getSlotRole(HeroTypeID hero) const;
|
||||||
|
|
||||||
CGHeroInstance * takeHero(HeroTypeID hero);
|
CGHeroInstance * takeHero(HeroTypeID hero);
|
||||||
|
|
||||||
/// reset mana and movement points for all heroes in pool
|
/// reset mana and movement points for all heroes in pool
|
||||||
@ -58,7 +70,7 @@ public:
|
|||||||
|
|
||||||
void addHeroToPool(CGHeroInstance * hero);
|
void addHeroToPool(CGHeroInstance * hero);
|
||||||
void setAvailability(HeroTypeID hero, PlayerColor::Mask mask);
|
void setAvailability(HeroTypeID hero, PlayerColor::Mask mask);
|
||||||
void setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army);
|
void setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army, TavernSlotRole role);
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
|
35
lib/gameState/TavernSlot.h
Normal file
35
lib/gameState/TavernSlot.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* TavernSlot.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
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
enum class TavernHeroSlot : int8_t
|
||||||
|
{
|
||||||
|
NONE = -1,
|
||||||
|
NATIVE, // 1st / left slot in tavern, contains hero native to player's faction on new week
|
||||||
|
RANDOM // 2nd / right slot in tavern, contains hero of random class
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TavernSlotRole : int8_t
|
||||||
|
{
|
||||||
|
NONE = -1,
|
||||||
|
|
||||||
|
SINGLE_UNIT, // hero was added after buying hero from this slot, and only has 1 creature in army
|
||||||
|
FULL_ARMY, // hero was added to tavern on new week and still has full army
|
||||||
|
|
||||||
|
RETREATED, // hero was owned by player before, but have retreated from battle and only has 1 creature in army
|
||||||
|
SURRENDERED, // hero was owned by player before, but have surrendered in battle and kept some troops
|
||||||
|
|
||||||
|
// SURRENDERED_DAY7, // helper value for heroes that surrendered after 7th day during enemy turn
|
||||||
|
// RETREATED_DAY7,
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -20,6 +20,7 @@
|
|||||||
#include "../lib/mapObjects/CGTownInstance.h"
|
#include "../lib/mapObjects/CGTownInstance.h"
|
||||||
#include "../lib/gameState/CGameState.h"
|
#include "../lib/gameState/CGameState.h"
|
||||||
#include "../lib/gameState/TavernHeroesPool.h"
|
#include "../lib/gameState/TavernHeroesPool.h"
|
||||||
|
#include "../lib/gameState/TavernSlot.h"
|
||||||
|
|
||||||
HeroPoolProcessor::HeroPoolProcessor()
|
HeroPoolProcessor::HeroPoolProcessor()
|
||||||
: gameHandler(nullptr)
|
: gameHandler(nullptr)
|
||||||
@ -31,10 +32,43 @@ HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID)
|
||||||
|
{
|
||||||
|
const auto & hpool = gameHandler->gameState()->hpool;
|
||||||
|
|
||||||
|
const auto & heroes = hpool->getHeroesFor(player);
|
||||||
|
|
||||||
|
// if tavern has empty slot - use it
|
||||||
|
if (heroes.size() == 0)
|
||||||
|
return TavernHeroSlot::NATIVE;
|
||||||
|
|
||||||
|
if (heroes.size() == 1)
|
||||||
|
return TavernHeroSlot::RANDOM;
|
||||||
|
|
||||||
|
// try to find "better" slot to overwrite
|
||||||
|
// we want to avoid overwriting retreated heroes when tavern still has slot with random hero
|
||||||
|
// as well as avoid overwriting surrendered heroes if we can overwrite retreated hero
|
||||||
|
auto roleLeft = hpool->getSlotRole(HeroTypeID(heroes[0]->subID));
|
||||||
|
auto roleRight = hpool->getSlotRole(HeroTypeID(heroes[1]->subID));
|
||||||
|
|
||||||
|
if (roleLeft > roleRight)
|
||||||
|
return TavernHeroSlot::RANDOM;
|
||||||
|
|
||||||
|
if (roleLeft < roleRight)
|
||||||
|
return TavernHeroSlot::NATIVE;
|
||||||
|
|
||||||
|
// both slots are equal in "value", so select randomly
|
||||||
|
if (getRandomGenerator(player).nextInt(100) > 50)
|
||||||
|
return TavernHeroSlot::RANDOM;
|
||||||
|
else
|
||||||
|
return TavernHeroSlot::NATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero)
|
void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero)
|
||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
sah.slotID = 0;
|
sah.slotID = selectSlotForRole(color, TavernSlotRole::SURRENDERED);
|
||||||
|
sah.roleID = TavernSlotRole::SURRENDERED;
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
sah.hid = hero->subID;
|
sah.hid = hero->subID;
|
||||||
sah.army.clear();
|
sah.army.clear();
|
||||||
@ -45,7 +79,8 @@ void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHer
|
|||||||
void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero)
|
void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero)
|
||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
sah.slotID = 0;
|
sah.slotID = selectSlotForRole(color, TavernSlotRole::RETREATED);
|
||||||
|
sah.roleID = TavernSlotRole::RETREATED;
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
sah.hid = hero->subID;
|
sah.hid = hero->subID;
|
||||||
|
|
||||||
@ -56,7 +91,8 @@ void HeroPoolProcessor::clearHeroFromSlot(const PlayerColor & color, TavernHeroS
|
|||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
sah.slotID = static_cast<int>(slot);
|
sah.roleID = TavernSlotRole::NONE;
|
||||||
|
sah.slotID = slot;
|
||||||
sah.hid = HeroTypeID::NONE;
|
sah.hid = HeroTypeID::NONE;
|
||||||
gameHandler->sendAndApply(&sah);
|
gameHandler->sendAndApply(&sah);
|
||||||
}
|
}
|
||||||
@ -65,7 +101,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
|
|||||||
{
|
{
|
||||||
SetAvailableHero sah;
|
SetAvailableHero sah;
|
||||||
sah.player = color;
|
sah.player = color;
|
||||||
sah.slotID = static_cast<int>(slot);
|
sah.slotID = slot;
|
||||||
|
|
||||||
//first hero - native if possible, second hero -> any other class
|
//first hero - native if possible, second hero -> any other class
|
||||||
CGHeroInstance *h = pickHeroFor(needNativeHero, color);
|
CGHeroInstance *h = pickHeroFor(needNativeHero, color);
|
||||||
@ -76,10 +112,12 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
|
|||||||
|
|
||||||
if (giveArmy)
|
if (giveArmy)
|
||||||
{
|
{
|
||||||
|
sah.roleID = TavernSlotRole::FULL_ARMY;
|
||||||
h->initArmy(getRandomGenerator(color), &sah.army);
|
h->initArmy(getRandomGenerator(color), &sah.army);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
sah.roleID = TavernSlotRole::SINGLE_UNIT;
|
||||||
sah.army.clear();
|
sah.army.clear();
|
||||||
sah.army.setCreature(SlotID(0), h->type->initialArmy[0].creature, 1);
|
sah.army.setCreature(SlotID(0), h->type->initialArmy[0].creature, 1);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
enum class TavernHeroSlot;
|
enum class TavernHeroSlot : int8_t;
|
||||||
|
enum class TavernSlotRole : int8_t;
|
||||||
class PlayerColor;
|
class PlayerColor;
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class HeroTypeID;
|
class HeroTypeID;
|
||||||
@ -41,6 +42,7 @@ class HeroPoolProcessor : boost::noncopyable
|
|||||||
|
|
||||||
CRandomGenerator & getRandomGenerator(const PlayerColor & player);
|
CRandomGenerator & getRandomGenerator(const PlayerColor & player);
|
||||||
|
|
||||||
|
TavernHeroSlot selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID);
|
||||||
public:
|
public:
|
||||||
CGameHandler * gameHandler;
|
CGameHandler * gameHandler;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user