1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Fixed some issues with hiring heroes: #25, #232, #333

This commit is contained in:
Michał W. Urbańczyk 2010-07-08 05:52:11 +00:00
parent 78789ef7fc
commit 94f7ee41e2
8 changed files with 166 additions and 116 deletions

View File

@ -102,6 +102,7 @@ const int MAX_BUILDING_PER_TURN = 1;
const int SPELL_LEVELS = 5;
const int CREEP_SIZE = 4000; // neutral stacks won't grow beyon this number
const int WEEKLY_GROWTH = 10; //percent
const int AVAILABLE_HEROES_PER_PLAYER = 2;
#define BFIELD_WIDTH (17)
#define BFIELD_HEIGHT (11)

View File

@ -791,46 +791,10 @@ void CGHeroInstance::initHero()
setFormation(false);
if (!stacksCount()) //standard army//initial army
{
int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
int pom = ran()%100;
int warMachinesGiven = 0;
if(pom < 9)
howManyStacks = 1;
else if(pom < 79)
howManyStacks = 2;
else
howManyStacks = 3;
for(int stackNo=0; stackNo<3; stackNo++)
{
int creID = (VLC->creh->nameToID[type->refTypeStack[stackNo]]);
int range = type->highStack[stackNo] - type->lowStack[stackNo];
int count = ran()%(range+1) + type->lowStack[stackNo];
if(creID>=145 && creID<=149) //war machine
{
warMachinesGiven++;
switch (creID)
{
case 145: //catapult
VLC->arth->equipArtifact(artifWorn, 16, 3, &bonuses);
break;
default:
VLC->arth->equipArtifact(
artifWorn,
9+CArtHandler::convertMachineID(creID,true),
CArtHandler::convertMachineID(creID,true),
&bonuses);
break;
}
}
else
addStack(stackNo-warMachinesGiven, CStackInstance(creID, count));
}
initArmy();
}
assert(validTypes());
hoverName = VLC->generaltexth->allTexts[15];
boost::algorithm::replace_first(hoverName,"%s",name);
boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name);
@ -840,6 +804,49 @@ void CGHeroInstance::initHero()
mana = manaLimit(); //after all bonuses are taken into account
}
void CGHeroInstance::initArmy(CCreatureSet *dst /*= NULL*/)
{
if(!dst)
dst = this;
int howManyStacks = 0; //how many stacks will hero receives <1 - 3>
int pom = ran()%100;
int warMachinesGiven = 0;
if(pom < 9)
howManyStacks = 1;
else if(pom < 79)
howManyStacks = 2;
else
howManyStacks = 3;
for(int stackNo=0; stackNo<3; stackNo++)
{
int creID = (VLC->creh->nameToID[type->refTypeStack[stackNo]]);
int range = type->highStack[stackNo] - type->lowStack[stackNo];
int count = ran()%(range+1) + type->lowStack[stackNo];
if(creID>=145 && creID<=149) //war machine
{
warMachinesGiven++;
switch (creID)
{
case 145: //catapult
VLC->arth->equipArtifact(artifWorn, 16, 3, &bonuses);
break;
default:
VLC->arth->equipArtifact(
artifWorn,
9+CArtHandler::convertMachineID(creID,true),
CArtHandler::convertMachineID(creID,true),
&bonuses);
break;
}
}
else
dst->addStack(stackNo-warMachinesGiven, CStackInstance(creID, count));
}
}
void CGHeroInstance::initHeroDefInfo()
{
if(!defInfo || defInfo->id != HEROI_TYPE)

View File

@ -375,6 +375,7 @@ public:
void initHero();
void initHero(int SUBID);
void initArmy(CCreatureSet *dst = NULL);
void recreateArtBonuses();
void giveArtifact (ui32 aid);
void initHeroDefInfo();

View File

@ -778,7 +778,7 @@ bool CStack::doubleWide() const
return type->doubleWide;
}
CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const
CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass /*= NULL*/) const
{
CGHeroInstance *ret = NULL;
@ -816,7 +816,8 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, co
for(std::map<ui32,CGHeroInstance *>::iterator i=available.begin(); i!=available.end(); i++)
{
if(pavailable.find(i->first)->second & 1<<player)
if(pavailable.find(i->first)->second & 1<<player
&& !bannedClass || i->second->type->heroClass != bannedClass)
{
pool.push_back(i->second);
sum += i->second->type->heroClass->selectionProbability[town->typeID]; //total weight
@ -1351,10 +1352,12 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
/*************************HEROES************************************************/
std::set<int> hids;
std::set<int> hids; //hero ids to create pool
for(unsigned int i=0; i<map->allowedHeroes.size(); i++) //add to hids all allowed heroes
if(map->allowedHeroes[i])
hids.insert(i);
for (unsigned int i=0; i<map->heroes.size();i++) //heroes instances initialization
{
if (map->heroes[i]->getOwner()<0)
@ -1367,11 +1370,13 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
players.find(vhi->getOwner())->second.heroes.push_back(vhi);
hids.erase(vhi->subID);
}
for (unsigned int i=0; i<map->objects.size();i++) //heroes instances initialization
for (unsigned int i=0; i<map->objects.size();i++) //prisons
{
if (map->objects[i]->ID == 62)
hids.erase(map->objects[i]->subID);
}
for(unsigned int i=0; i<map->predefinedHeroes.size(); i++)
{
if(!vstd::contains(hids,map->predefinedHeroes[i]->subID))
@ -1381,17 +1386,20 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
hpool.pavailable[map->predefinedHeroes[i]->subID] = 0xff;
hids.erase(map->predefinedHeroes[i]->subID);
}
BOOST_FOREACH(int hid, hids) //all not used allowed heroes go into the pool
BOOST_FOREACH(int hid, hids) //all not used allowed heroes go with default state into the pool
{
CGHeroInstance * vhi = new CGHeroInstance();
vhi->initHero(hid);
hpool.heroesPool[hid] = vhi;
hpool.pavailable[hid] = 0xff;
}
for(unsigned int i=0; i<map->disposedHeroes.size(); i++)
{
hpool.pavailable[map->disposedHeroes[i].ID] = map->disposedHeroes[i].players;
}
/*************************FOG**OF**WAR******************************************/
for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
{
@ -3422,6 +3430,17 @@ int CGameState::lossCheck( ui8 player ) const
return false;
}
std::map<ui32,CGHeroInstance *> CGameState::unusedHeroesFromPool()
{
std::map<ui32,CGHeroInstance *> pool = hpool.heroesPool;
for ( std::map<ui8, PlayerState>::iterator i = players.begin() ; i != players.end();i++)
for(std::vector<CGHeroInstance *>::iterator j = i->second.availableHeroes.begin(); j != i->second.availableHeroes.end(); j++)
if(*j)
pool.erase((**j).subID);
return pool;
}
const CStack * BattleInfo::getNextStack() const
{
std::vector<const CStack *> hlp;

View File

@ -386,7 +386,7 @@ public:
std::map<ui32,CGHeroInstance *> heroesPool; //[subID] - heroes available to buy; NULL if not available
std::map<ui32,ui8> pavailable; // [subid] -> which players can recruit hero (binary flags)
CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const;
CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available, const CHeroClass *bannedClass = NULL) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
@ -430,6 +430,7 @@ public:
ui8 checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 if no winner
bool checkForStandardLoss(ui8 player) const; //checks if given player lost the game
void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild
std::map<ui32,CGHeroInstance *> unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns
bool isVisible(int3 pos, int player);
bool isVisible(const CGObjectInstance *obj, int player);
@ -445,32 +446,6 @@ public:
if(!h.saving)
{
loadTownDInfos();
// //recreating towns/heroes vectors in players entries
// for(int i=0; i<map->towns.size(); i++)
// if(map->towns[i]->tempOwner < PLAYER_LIMIT)
// getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]);
// for(int i=0; i<map->heroes.size(); i++)
// if(map->heroes[i]->tempOwner < PLAYER_LIMIT)
// getPlayer(map->heroes[i]->tempOwner)->heroes.push_back(map->heroes[i]);
// //recreating available heroes
// for(std::map<ui8,PlayerState>::iterator i=players.begin(); i!=players.end(); i++)
// {
// for(size_t j=0; j < i->second.availableHeroes.size(); j++)
// {
// ui32 hlp = i->second.availableHeroes[j]->subID;
// delete i->second.availableHeroes[j];
// if(hlp != 0xffffffff)
// {
// assert(vstd::contains(hpool.heroesPool, hlp));
// i->second.availableHeroes[j] = hpool.heroesPool[hlp];
// }
// else
// {
// i->second.availableHeroes[j] = NULL;
// }
// }
// }
}
}

View File

@ -346,16 +346,26 @@ struct FoWChange : public CPackForClient //112
struct SetAvailableHeroes : public CPackForClient //113
{
SetAvailableHeroes(){type = 113;flags=0;};
SetAvailableHeroes()
{
type = 113;
for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
army[i] = NULL;
}
~SetAvailableHeroes()
{
for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
delete army[i];
}
void applyCl(CClient *cl);
DLL_EXPORT void applyGs(CGameState *gs);
ui8 player;
si32 hid1, hid2;
ui8 flags; //1 - reset army of hero1; 2 - reset army of hero 2
si32 hid[AVAILABLE_HEROES_PER_PLAYER]; //-1 if no hero
CCreatureSet *army[AVAILABLE_HEROES_PER_PLAYER];
template <typename Handler> void serialize(Handler &h, const int version)
{
h & player & hid1 & hid2 & flags;
h & player & hid & army;
}
};

View File

@ -190,22 +190,15 @@ DLL_EXPORT void FoWChange::applyGs( CGameState *gs )
}
DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
{
gs->getPlayer(player)->availableHeroes.clear();
PlayerState *p = gs->getPlayer(player);
p->availableHeroes.clear();
CGHeroInstance *h = (hid1>=0 ? gs->hpool.heroesPool[hid1] : NULL);
gs->getPlayer(player)->availableHeroes.push_back(h);
if(h && flags & 1)
for (int i = 0; i < AVAILABLE_HEROES_PER_PLAYER; i++)
{
h->clear();
h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
}
h = (hid2>=0 ? gs->hpool.heroesPool[hid2] : NULL);
gs->getPlayer(player)->availableHeroes.push_back(h);
if(flags & 2)
{
h->clear();
h->addStack(0, CStackInstance(VLC->creh->nameToID[h->type->refTypeStack[0]],1));
CGHeroInstance *h = (hid[i]>=0 ? gs->hpool.heroesPool[hid[i]] : NULL);
if(h && army[i])
h->setArmy(*army[i]);
p->availableHeroes.push_back(h);
}
}
@ -315,7 +308,10 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
h->visitedTown = NULL;
}
//TODO: add to the pool?
//return hero to the pool, so he may reappear in tavern
gs->hpool.heroesPool[h->subID] = h;
if(!vstd::contains(gs->hpool.pavailable, h->subID))
gs->hpool.pavailable[h->subID] = 0xff;
}
else if (obj->ID==CREI_TYPE && gs->map->version > CMapHeader::RoE) //only fixed monsters can be a part of quest
{

View File

@ -545,6 +545,7 @@ askInterfaceForMove:
void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
{
BattleResultsApplied resultsApplied;
resultsApplied.player1 = bEndArmy1->tempOwner;
resultsApplied.player2 = bEndArmy2->tempOwner;
@ -565,6 +566,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
sides[0] = gs->curB->side1;
sides[1] = gs->curB->side2;
ui8 loser = sides[!battleResult.data->winner];
//end battle, remove all info, free memory
giveExp(*battleResult.data);
sendAndApply(battleResult.data);
@ -598,6 +601,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
// Necromancy if applicable.
const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1;
const CGHeroInstance *loserHero = battleResult.data->winner != 0 ? hero1 : hero2;
if (winnerHero)
{
CStackInstance raisedStack = winnerHero->calculateNecromancy(*battleResult.data);
@ -630,6 +635,27 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
visitObjectAfterVictory = false;
winLoseHandle(1<<sides[0] | 1<<sides[1]); //handle victory/loss of engaged players
int result = battleResult.get()->result;
if(result == 1 || result == 2) //loser has escaped or surrendered
{
SetAvailableHeroes sah;
sah.player = loser;
sah.hid[0] = loserHero->subID;
if(result == 1) //retreat
{
sah.army[0] = new CCreatureSet();
sah.army[0]->addToSlot(0, VLC->creh->nameToID[loserHero->type->refTypeStack[0]],1);
}
if(const CGHeroInstance *another = getPlayerState(loser)->availableHeroes[1])
sah.hid[1] = another->subID;
else
sah.hid[1] = -1;
sendAndApply(&sah);
}
delete battleResult.data;
}
@ -916,29 +942,37 @@ void CGameHandler::newTurn()
for ( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
{
if(i->first == 255) continue;
else if(i->first > PLAYER_LIMIT) assert(0); //illegal player number!
if(i->first == 255)
continue;
else if(i->first >= PLAYER_LIMIT)
assert(0); //illegal player number!
std::pair<ui8,si32> playerGold(i->first,i->second.resources[6]);
hadGold.insert(playerGold);
if(gs->getDate(1)==7) //first day of week - new heroes in tavern
{
const CTown *nativeTownType = &VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle];
SetAvailableHeroes sah;
sah.player = i->first;
CGHeroInstance *h = gs->hpool.pickHeroFor(true,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle], pool);
if(h)
sah.hid1 = h->subID;
else
sah.hid1 = -1;
h = gs->hpool.pickHeroFor(false,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle], pool);
if(h)
sah.hid2 = h->subID;
else
sah.hid2 = -1;
//pick heroes and their armies
CHeroClass *banned = NULL;
for (int j = 0; j < AVAILABLE_HEROES_PER_PLAYER; j++)
{
if(CGHeroInstance *h = gs->hpool.pickHeroFor(j == 0, i->first, nativeTownType, pool, banned)) //first hero - native if possible, second hero -> any other class
{
sah.hid[j] = h->subID;
h->initArmy(sah.army[j] = new CCreatureSet());
banned = h->type->heroClass;
}
else
sah.hid[j] = -1;
}
sendAndApply(&sah);
}
if(i->first>=PLAYER_LIMIT) continue;
n.res[i->first] = i->second.resources;
// SetResources r;
@ -3247,14 +3281,16 @@ bool CGameHandler::setFormation( si32 hid, ui8 formation )
bool CGameHandler::hireHero( ui32 tid, ui8 hid )
{
CGTownInstance *t = gs->getTown(tid);
const CGTownInstance *t = gs->getTown(tid);
const PlayerState *p = gs->getPlayer(t->tempOwner);
if(!vstd::contains(t->builtBuildings,5) && complain("No tavern!")
|| gs->getPlayer(t->tempOwner)->resources[6]<2500 && complain("Not enough gold for buying hero!")
|| p->resources[6]<2500 && complain("Not enough gold for buying hero!")
|| t->visitingHero && complain("There is visiting hero - no place!")
|| getHeroCount(t->tempOwner,false) >= 8 && complain("Cannot hire hero, only 8 wandering heroes are allowed!")
)
return false;
CGHeroInstance *nh = gs->getPlayer(t->tempOwner)->availableHeroes[hid];
CGHeroInstance *nh = p->availableHeroes[hid];
assert(nh);
HeroRecruited hr;
@ -3265,25 +3301,30 @@ bool CGameHandler::hireHero( ui32 tid, ui8 hid )
sendAndApply(&hr);
std::map<ui32,CGHeroInstance *> pool = gs->hpool.heroesPool;
for ( std::map<ui8, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
for(std::vector<CGHeroInstance *>::iterator j = i->second.availableHeroes.begin(); j != i->second.availableHeroes.end(); j++)
if(*j)
pool.erase((**j).subID);
std::map<ui32,CGHeroInstance *> pool = gs->unusedHeroesFromPool();
const CGHeroInstance *theOtherHero = p->availableHeroes[!hid];
const CGHeroInstance *newHero = gs->hpool.pickHeroFor(false, t->tempOwner,t->town, pool, theOtherHero->type->heroClass);
SetAvailableHeroes sah;
CGHeroInstance *h1 = gs->hpool.pickHeroFor(false,t->tempOwner,t->town, pool),
*h2 = gs->getPlayer(t->tempOwner)->availableHeroes[!hid];
(hid ? sah.hid2 : sah.hid1) = h1 ? h1->subID : -1;
(hid ? sah.hid1 : sah.hid2) = h2 ? h2->subID : -1;
sah.player = t->tempOwner;
sah.flags = hid+1;
if(newHero)
{
sah.hid[hid] = newHero->subID;
sah.army[hid] = new CCreatureSet();
sah.army[hid]->addToSlot(0, VLC->creh->nameToID[newHero->type->refTypeStack[0]],1);
}
else
sah.hid[hid] = -1;
sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
sendAndApply(&sah);
SetResource sr;
sr.player = t->tempOwner;
sr.resid = 6;
sr.val = gs->getPlayer(t->tempOwner)->resources[6] - 2500;
sr.val = p->resources[6] - 2500;
sendAndApply(&sr);
vistiCastleObjects (t, nh);