diff --git a/global.h b/global.h index 3465955bb..4300ba25b 100644 --- a/global.h +++ b/global.h @@ -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) diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 17518626a..536157fa9 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -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) diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index a18c3e1da..830cdaed7 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -375,6 +375,7 @@ public: void initHero(); void initHero(int SUBID); + void initArmy(CCreatureSet *dst = NULL); void recreateArtBonuses(); void giveArtifact (ui32 aid); void initHeroDefInfo(); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 80d39f82b..347b2f311 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -778,7 +778,7 @@ bool CStack::doubleWide() const return type->doubleWide; } -CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map &available) const +CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map &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::iterator i=available.begin(); i!=available.end(); i++) { - if(pavailable.find(i->first)->second & 1<first)->second & 1<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 hids; + std::set hids; //hero ids to create pool + for(unsigned int i=0; iallowedHeroes.size(); i++) //add to hids all allowed heroes if(map->allowedHeroes[i]) hids.insert(i); + for (unsigned int i=0; iheroes.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; iobjects.size();i++) //heroes instances initialization + + for (unsigned int i=0; iobjects.size();i++) //prisons { if (map->objects[i]->ID == 62) hids.erase(map->objects[i]->subID); } + for(unsigned int i=0; ipredefinedHeroes.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; idisposedHeroes.size(); i++) { hpool.pavailable[map->disposedHeroes[i].ID] = map->disposedHeroes[i].players; } + /*************************FOG**OF**WAR******************************************/ for(std::map::iterator k=players.begin(); k!=players.end(); ++k) { @@ -3422,6 +3430,17 @@ int CGameState::lossCheck( ui8 player ) const return false; } +std::map CGameState::unusedHeroesFromPool() +{ + std::map pool = hpool.heroesPool; + for ( std::map::iterator i = players.begin() ; i != players.end();i++) + for(std::vector::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 hlp; diff --git a/lib/CGameState.h b/lib/CGameState.h index a672a0731..e2456d416 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -386,7 +386,7 @@ public: std::map heroesPool; //[subID] - heroes available to buy; NULL if not available std::map pavailable; // [subid] -> which players can recruit hero (binary flags) - CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map &available) const; + CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, std::map &available, const CHeroClass *bannedClass = NULL) const; template 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 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; itowns.size(); i++) -// if(map->towns[i]->tempOwner < PLAYER_LIMIT) -// getPlayer(map->towns[i]->tempOwner)->towns.push_back(map->towns[i]); -// for(int i=0; iheroes.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::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; -// } -// } -// } } } diff --git a/lib/NetPacks.h b/lib/NetPacks.h index a0615dcc7..d7e3933be 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -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 void serialize(Handler &h, const int version) { - h & player & hid1 & hid2 & flags; + h & player & hid & army; } }; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 51552806c..69e4f517a 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -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 { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 2015bdcdd..70d56dfcf 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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<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::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 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 pool = gs->hpool.heroesPool; - for ( std::map::iterator i=gs->players.begin() ; i!=gs->players.end();i++) - for(std::vector::iterator j = i->second.availableHeroes.begin(); j != i->second.availableHeroes.end(); j++) - if(*j) - pool.erase((**j).subID); + std::map 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);