diff --git a/CBattleInterface.cpp b/CBattleInterface.cpp index 35eef143b..5a7eb0c5c 100644 --- a/CBattleInterface.cpp +++ b/CBattleInterface.cpp @@ -1819,6 +1819,7 @@ void CBattleHex::clickRight(boost::logic::tribool down) pom->defenseBonus = h->primSkills[1]; pom->luck = h->getCurrentLuck(); pom->morale = h->getCurrentMorale(); + pom->shotsLeft = myst.shots; } pom->currentHealth = myst.firstHPleft; (new CCreInfoWindow(myst.creature->idNumber,0,myst.amount,pom,boost::function(),boost::function(),NULL)) diff --git a/CCallback.cpp b/CCallback.cpp index c7a40eb78..e5ec6eea9 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -523,7 +523,7 @@ bool CCallback::battleIsStackMine(int ID) } return false; } -bool CCallback::battleCanShoot(int ID, int dest) //TODO: check arrows amount +bool CCallback::battleCanShoot(int ID, int dest) { boost::shared_lock lock(*gs->mx); CStack *our=battleGetStackByID(ID), *dst=battleGetStackByPos(dest); @@ -532,6 +532,7 @@ bool CCallback::battleCanShoot(int ID, int dest) //TODO: check arrows amount && our->owner != dst->owner && dst->alive() && !gs->curB->isStackBlocked(ID) + && our->shots ) return true; return false; diff --git a/CGameState.cpp b/CGameState.cpp index 830dd87f8..bd9a75467 100644 --- a/CGameState.cpp +++ b/CGameState.cpp @@ -247,6 +247,62 @@ CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S) abilities = C->abilities; state.insert(ALIVE); } + +CGHeroInstance* CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, int notThatOne) +{ + if(player<0 || player>=PLAYER_LIMIT) + { + tlog1 << "Cannot pick hero for " << town->name << ". Wrong owner!\n"; + return NULL; + } + std::vector pool; + int sum=0, r; + if(native) + { + for(std::map::iterator i=heroesPool.begin(); i!=heroesPool.end(); i++) + { + if(pavailable[i->first] & 1<second->type->heroType/2 == town->typeID + && i->second->subID != notThatOne + ) + { + pool.push_back(i->second); + } + } + if(!pool.size()) + return pickHeroFor(false,player,town,notThatOne); + else + return pool[rand()%pool.size()]; + } + else + { + for(std::map::iterator i=heroesPool.begin(); i!=heroesPool.end(); i++) + { + if(pavailable[i->first] & 1<second->subID != notThatOne + ) + { + pool.push_back(i->second); + sum += i->second->type->heroClass->selectionProbability[town->typeID]; + } + } + if(!pool.size()) + { + tlog1 << "There are no heroes available for player " << player<<"!\n"; + return NULL; + } + r = rand()%sum; + for(int i=0; itype->heroClass->selectionProbability[town->typeID]; + if(r<0) + return pool[i]; + } + return pool[pool.size()-1]; + } +} + + void CGameState::applyNL(IPack * pack) { switch(pack->getType()) @@ -401,6 +457,16 @@ void CGameState::applyNL(IPack * pack) players[rh->player].availableHeroes.clear(); players[rh->player].availableHeroes.push_back(hpool.heroesPool[rh->hid1]); players[rh->player].availableHeroes.push_back(hpool.heroesPool[rh->hid2]); + if(rh->flags & 1) + { + hpool.heroesPool[rh->hid1]->army.slots.clear(); + hpool.heroesPool[rh->hid1]->army.slots[0] = std::pair(VLC->creh->nameToID[hpool.heroesPool[rh->hid1]->type->refTypeStack[0]],1); + } + if(rh->flags & 2) + { + hpool.heroesPool[rh->hid2]->army.slots.clear(); + hpool.heroesPool[rh->hid2]->army.slots[0] = std::pair(VLC->creh->nameToID[hpool.heroesPool[rh->hid2]->type->refTypeStack[0]],1); + } break; } case 500: @@ -622,8 +688,11 @@ void CGameState::applyNL(IPack * pack) case 3006: { BattleAttack *br = static_cast(pack); + CStack *attacker = curB->getStack(br->stackAttacking); if(br->counter()) - curB->getStack(br->stackAttacking)->counterAttacks--; + attacker->counterAttacks--; + if(br->shot()) + attacker->shots--; applyNL(&br->bsa); break; } diff --git a/CGameState.h b/CGameState.h index 85519c6a2..5515d1f57 100644 --- a/CGameState.h +++ b/CGameState.h @@ -13,6 +13,7 @@ #include "tchar_amigaos4.h" #endif +class CTown; class CScriptCallback; class CCallback; class CLuaCallback; @@ -91,6 +92,7 @@ public: ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle) ui16 position; //position on battlefield ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1) + si16 shots; //how many shots left std::set abilities; std::set state; @@ -108,6 +110,7 @@ public: h & id; creature = &VLC->creh->creatures[id]; abilities = creature->abilities; + shots = creature->shots; } template void serialize(Handler &h, const int version) { @@ -144,10 +147,12 @@ private: std::map villages, forts, capitols; //def-info for town graphics std::vector resVals; - struct HeroesPool + struct DLL_EXPORT HeroesPool { std::map heroesPool; //[subID] - heroes available to buy; NULL if not available std::map pavailable; // [subid] -> which players can recruit hero + + CGHeroInstance * pickHeroFor(bool native, int player, const CTown *town, int notThatOne=-1); } hpool; //we have here all heroes available on this map that are not hired boost::shared_mutex *mx; diff --git a/CHeroWindow.cpp b/CHeroWindow.cpp index 631074fd6..2504c2387 100644 --- a/CHeroWindow.cpp +++ b/CHeroWindow.cpp @@ -231,7 +231,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero) secSkillAreas[g]->hoverText = std::string(bufor); } - sprintf(bufor, CGI->generaltexth->allTexts[2].substr(1, CGI->generaltexth->allTexts[2].size()-2).c_str(), hero->level, CGI->heroh->reqExp(hero->level+1), hero->exp); + sprintf(bufor, CGI->generaltexth->allTexts[2].c_str(), hero->level, CGI->heroh->reqExp(hero->level+1), hero->exp); expArea->text = std::string(bufor); sprintf(bufor, CGI->generaltexth->allTexts[205].substr(1, CGI->generaltexth->allTexts[205].size()-2).c_str(), hero->name.c_str(), hero->mana, hero->getPrimSkillLevel(3)*10); diff --git a/CLua.cpp b/CLua.cpp index d9f3a9482..a93bf9f80 100644 --- a/CLua.cpp +++ b/CLua.cpp @@ -310,7 +310,7 @@ void CVisitableOPH::onNAHeroVisit(int objid, int heroID, bool alreadyVisited) case 102: { const CGHeroInstance *h = cb->getHero(heroID); - val = VLC->heroh->reqExp(h->level) + VLC->heroh->reqExp(h->level+val); + val = VLC->heroh->reqExp(h->level+val) - VLC->heroh->reqExp(h->level); if(!typeOfTree[objid]) { visitors[objid].insert(heroID); diff --git a/CMT.cpp b/CMT.cpp index c7ecf1b35..832505b16 100644 --- a/CMT.cpp +++ b/CMT.cpp @@ -277,7 +277,7 @@ void processCommand(const std::string &message, CClient *&client) boost::filesystem::create_directory("Extracted_txts"); tlog0<<"Command accepted. Opening .lod file...\t"; CLodHandler * txth = new CLodHandler; - txth->init(std::string(DATA_DIR "Data" PATHSEPARATOR "H3bitmap.lod"),"data"); + txth->init(std::string(DATA_DIR "Data" PATHSEPARATOR "H3bitmap.lod"),""); tlog0<<"done.\nScanning .lod file\n"; int curp=0; std::string pattern = ".TXT", pom; diff --git a/CPlayerInterface.cpp b/CPlayerInterface.cpp index 5b1869b56..45b33e6bd 100644 --- a/CPlayerInterface.cpp +++ b/CPlayerInterface.cpp @@ -2086,9 +2086,9 @@ void CPlayerInterface::battleStackAttacked(BattleStackAttacked * bsa) battleInt->displayEffect(bsa->effect, cb->battleGetStackByID(bsa->stackAttacked)->position, cb->battleGetStackByID(bsa->stackAttacked)->attackerOwned); } if(bsa->killed()) - battleInt->stackKilled(bsa->stackAttacked, bsa->damageAmount, bsa->killedAmount, -1, false); + battleInt->stackKilled(bsa->stackAttacked, bsa->damageAmount, bsa->killedAmount, LOCPLINT->curAction->stackNumber, LOCPLINT->curAction->actionType==7 ); else - battleInt->stackIsAttacked(bsa->stackAttacked, bsa->damageAmount, bsa->killedAmount, -1, false); + battleInt->stackIsAttacked(bsa->stackAttacked, bsa->damageAmount, bsa->killedAmount, LOCPLINT->curAction->stackNumber, LOCPLINT->curAction->actionType==7); } void CPlayerInterface::battleAttack(BattleAttack *ba) { @@ -3129,7 +3129,7 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState anim = new CCreaturePic(c); if(!type) anim->anim->setType(1); - char pom[25];int hlp=0; + char pom[75];int hlp=0; if(creatureCount) { @@ -3169,7 +3169,10 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, int creatureCount, StackState if(c->shots) { printAt(CGI->generaltexth->allTexts[198],155,86,GEOR13,zwykly,bitmap); - SDL_itoa(c->shots,pom,10); + if(State) + sprintf(pom,"%d(%d)",c->shots,State->shotsLeft); + else + SDL_itoa(c->shots,pom,10); printToWR(pom,276,99,GEOR13,zwykly,bitmap); } @@ -3930,8 +3933,10 @@ void CTavernWindow::show(SDL_Surface * to) HeroPortrait *sel = selected ? &h2 : &h1; char descr[300]; - int artifs = sel->h->artifWorn.size()+sel->h->artifacts.size() - 1; //artifacts amount; - 1 is for catapult - if(vstd::contains(sel->h->artifWorn,0)) artifs--; //spellbook doesn't count neither + int artifs = sel->h->artifWorn.size()+sel->h->artifacts.size(); + for(int i=13; i<=17; i++) //war machines and spellbook doesn't count + if(vstd::contains(sel->h->artifWorn,i)) + artifs--; sprintf_s(descr,300,CGI->generaltexth->allTexts[215].c_str(), sel->h->name.c_str(),sel->h->level,sel->h->type->heroClass->name.c_str(),artifs); printAtMiddleWB(descr,pos.x+146,pos.y+389,GEOR13,40,zwykly,screen); @@ -3984,4 +3989,4 @@ CTavernWindow::HeroPortrait::HeroPortrait(int &sel, int id, int x, int y, const void CTavernWindow::HeroPortrait::show(SDL_Surface * to) { blitAt(graphics->portraitLarge[h->subID],pos); -} \ No newline at end of file +} diff --git a/hch/CCreatureHandler.cpp b/hch/CCreatureHandler.cpp index 01096d7ee..dc8e02d4c 100644 --- a/hch/CCreatureHandler.cpp +++ b/hch/CCreatureHandler.cpp @@ -64,6 +64,7 @@ si32 CCreature::maxAmount(const std::vector &res) const //how many creatur void CCreatureHandler::loadCreatures() { notUsedMonsters += 122,124,126,128,145,146,147,148,149,160,161,162,163,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191; + tlog5 << "\t\tReading ZCRTRAIT.TXT" << std::endl; std::string buf = bitmaph->getTextFile("ZCRTRAIT.TXT"); int andame = buf.size(); int i=0; //buf iterator @@ -319,21 +320,11 @@ void CCreatureHandler::loadCreatures() if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string("")) { ncre.idNumber = creatures.size(); - ncre.isDefinite = true; creatures.push_back(ncre); } } - for(int bb=1; bb<8; ++bb) - { - CCreature ncre; - ncre.isDefinite = false; - ncre.indefLevel = bb; - ncre.indefUpgraded = false; - creatures.push_back(ncre); - ncre.indefUpgraded = true; - creatures.push_back(ncre); - } + tlog5 << "\t\tReading config/crerefnam.txt" << std::endl; //loading reference names std::ifstream ifs("config/crerefnam.txt"); int tempi; @@ -350,6 +341,8 @@ void CCreatureHandler::loadCreatures() ifs.clear(); for(int i=1;i<=10;i++) levelCreatures.insert(std::pair >(i,std::vector())); + + tlog5 << "\t\tReading config/monsters.txt" << std::endl; ifs.open("config/monsters.txt"); { while(!ifs.eof()) @@ -366,6 +359,7 @@ void CCreatureHandler::loadCreatures() ifs.close(); ifs.clear(); + tlog5 << "\t\tReading config/cr_factions.txt" << std::endl; ifs.open("config/cr_factions.txt"); while(!ifs.eof()) { @@ -376,6 +370,7 @@ void CCreatureHandler::loadCreatures() ifs.close(); ifs.clear(); + tlog5 << "\t\tReading config/cr_upgrade_list.txt" << std::endl; ifs.open("config/cr_upgrade_list.txt"); while(!ifs.eof()) { @@ -387,6 +382,7 @@ void CCreatureHandler::loadCreatures() ifs.clear(); //loading unit animation def names + tlog5 << "\t\tReading config/CREDEFS.TXT" << std::endl; std::ifstream inp("config/CREDEFS.TXT", std::ios::in | std::ios::binary); //this file is not in lod inp.seekg(0,std::ios::end); // na koniec int andame2 = inp.tellg(); // read length @@ -395,11 +391,7 @@ void CCreatureHandler::loadCreatures() inp.read((char*)bufor, andame2); // read map file to buffer inp.close(); buf = std::string(bufor); -#ifndef __GNUC__ - delete [andame2] bufor; -#else delete [] bufor; -#endif i = 0; //buf iterator hmcr = 0; @@ -409,8 +401,10 @@ void CCreatureHandler::loadCreatures() break; } i+=2; - for(int s=0; s //112 struct SetAvailableHeroes : public CPack //113 { - SetAvailableHeroes(){type = 113;}; + SetAvailableHeroes(){type = 113;flags=0;}; ui8 player; ui32 hid1, hid2; + ui8 flags; //1 - reset army of hero1; 2 - reset army of hero 2 template void serialize(Handler &h, const int version) { - h & player & hid1 & hid2; + h & player & hid1 & hid2 & flags; } }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index be5b923a6..4db98d2a7 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -987,6 +987,11 @@ upgend: fc.tiles.insert(int3(i,j,k)); sendAndApply(&fc); } + else if(message == "vcmiglorfindel") + { + CGHeroInstance *hero = gs->getHero(gs->players[*players.begin()].currentSelection); + changePrimSkill(hero->id,4,VLC->heroh->reqExp(hero->level+1) - VLC->heroh->reqExp(hero->level)); + } else cheated = false; if(cheated) @@ -1019,12 +1024,26 @@ upgend: ) break; CGHeroInstance *nh = gs->players[t->tempOwner].availableHeroes[hid]; + HeroRecruited hr; hr.tid = tid; hr.hid = nh->subID; hr.player = t->tempOwner; hr.tile = t->pos - int3(1,0,0); sendAndApply(&hr); + + SetAvailableHeroes sah; + (hid ? sah.hid2 : sah.hid1) = gs->hpool.pickHeroFor(false,t->tempOwner,t->town)->subID; + (hid ? sah.hid1 : sah.hid2) = gs->players[t->tempOwner].availableHeroes[!hid]->subID; + sah.player = t->tempOwner; + sah.flags = hid+1; + sendAndApply(&sah); + + SetResource sr; + sr.player = t->tempOwner; + sr.resid = 6; + sr.val = gs->players[t->tempOwner].resources[6] - 2500; + sendAndApply(&sr); break; } case 2001: @@ -1075,8 +1094,9 @@ upgend: CStack *curStack = gs->curB->getStack(ba.stackNumber), *stackAtEnd = gs->curB->getStackT(ba.additionalInfo); - if((curStack->position != ba.destinationTile) || //we wasn't able to reach destination tile - (BattleInfo::mutualPosition(ba.destinationTile,ba.additionalInfo)<0) ) //destination tile is not neighbouring with enemy stack + if( curStack->position != ba.destinationTile //we wasn't able to reach destination tile + || BattleInfo::mutualPosition(ba.destinationTile,ba.additionalInfo) < 0 //destination tile is not neighbouring with enemy stack + ) return; BattleAttack bat; @@ -1105,25 +1125,33 @@ upgend: } case 7: //shoot { - //TODO: check arrows count - //TODO: check if stack isn't blocked by enemy - - sendAndApply(&StartAction(ba)); //start shooting CStack *curStack = gs->curB->getStack(ba.stackNumber), *destStack= gs->curB->getStackT(ba.destinationTile); + if(!curStack //our stack exists + || !destStack //there is a stack at destination tile + || !curStack->shots //stack has shots + || gs->curB->isStackBlocked(curStack->ID) //we are not blocked + || !vstd::contains(curStack->abilities,SHOOTER) //our stack is shooting unit + ) + break; + + sendAndApply(&StartAction(ba)); //start shooting BattleAttack bat; prepareAttack(bat,curStack,destStack); bat.flags |= 1; + sendAndApply(&bat); - if(vstd::contains(curStack->abilities,TWICE_ATTACK) - && curStack->alive()) + if(vstd::contains(curStack->abilities,TWICE_ATTACK) //if unit shots twice let's make another shot + && curStack->alive() + && destStack->alive() + && curStack->shots + ) { prepareAttack(bat,curStack,destStack); sendAndApply(&bat); } - sendAndApply(&bat); sendDataToClients(ui16(3008)); //end shooting break; } @@ -1335,25 +1363,15 @@ void CGameHandler::newTurn() for ( std::map::iterator i=gs->players.begin() ; i!=gs->players.end();i++) { + if(i->first == 255) continue; if(gs->getDate(1)==7) //first day of week - new heroes in tavern { SetAvailableHeroes sah; sah.player = i->first; - int r; - if(!gs->hpool.heroesPool.size()) return; - for(int a=0;a<2;a++) - { - r = rand() % gs->hpool.heroesPool.size(); - std::map::iterator ch = gs->hpool.heroesPool.begin(); - while(r--) ch++; - if(a) sah.hid2 = ch->first; - else sah.hid1 = ch->first; - } + //TODO: - will fail when there are not enough available heroes + sah.hid1 = gs->hpool.pickHeroFor(true,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle])->subID; + sah.hid2 = gs->hpool.pickHeroFor(false,i->first,&VLC->townh->towns[gs->scenarioOps->getIthPlayersSettings(i->first).castle],sah.hid1)->subID; sendAndApply(&sah); - //TODO: guarantee that heroes are different - //TODO: first hero should be from initial player town - //TODO: use selectionProbability from CHeroClass - //int town = gs->scenarioOps->getIthPlayersSettings(i->first).castle; } if(i->first>=PLAYER_LIMIT) continue; SetResources r; @@ -1369,7 +1387,7 @@ void CGameHandler::newTurn() hth.mana = std::max(h->mana,std::min(h->mana+1+h->getSecSkillLevel(8), h->manaLimit())); //hero regains 1 mana point + mysticism lvel n.heroes.insert(hth); - switch(h->getSecSkillLevel(13)) //handle estates - give gols + switch(h->getSecSkillLevel(13)) //handle estates - give gold { case 1: //basic r.res[6] += 125;