diff --git a/ChangeLog b/ChangeLog index 63a1b8beb..436b9d3db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -38,12 +38,14 @@ TOWNS: * Support for new town structures: - Lighthouse - Colossus +- Freelancer's Guild - Guardian Spirit - Necromancy Amplifier - Soul Prison OBJECTS: New object supported: +- Freelancer's Guild - Trading Post - War Machine Factory diff --git a/client/CCastleInterface.cpp b/client/CCastleInterface.cpp index 1c45bc0a0..3b89ae323 100644 --- a/client/CCastleInterface.cpp +++ b/client/CCastleInterface.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace boost::assign; using namespace CSDL_Ext; @@ -557,6 +558,8 @@ void CCastleInterface::buildingClicked(int building) building = town->town->hordeLvl[1] + 30; } + const CBuilding *b = CGI->buildh->buildings[town->subID][building]; + if(building >= 30) { showRecruitmentWindow(building); @@ -635,7 +638,7 @@ void CCastleInterface::buildingClicked(int building) break; case 14: //marketplace { - CMarketplaceWindow *cmw = new CMarketplaceWindow(town); + CMarketplaceWindow *cmw = new CMarketplaceWindow(town, town->visitingHero); GH.pushInt(cmw); break; } @@ -648,13 +651,13 @@ void CCastleInterface::buildingClicked(int building) { switch(town->subID) { - /*Rampart*/ case 1://Mystic Pond + /*Rampart*/ case 1://Mystic Pond enterFountain(building); break; - /*Tower*/ case 2://Artifact Merchant - /*Dungeon*/ case 5://Artifact Merchant - /*Conflux*/ case 8://Artifact Merchant - tlog4<<"Artifact Merchant not handled\n"; + /*Tower*/ case 2://Artifact Merchant + /*Dungeon*/ case 5://Artifact Merchant + /*Conflux*/ case 8://Artifact Merchant + GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, RESOURCE_ARTIFACT)); break; default: defaultBuildingClicked(building); @@ -671,13 +674,16 @@ void CCastleInterface::buildingClicked(int building) { switch(town->subID) { - /*Rampart*/ case 1: //Fountain of Fortune + /*Rampart*/ case 1: //Fountain of Fortune enterFountain(building); break; - /*Stronghold*/ case 6: //Freelancer's Guild - tlog4<<"Freelancer's Guild not handled\n"; + /*Stronghold*/case 6: //Freelancer's Guild + if(town->visitingHero) + GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, CREATURE_RESOURCE)); + else + LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s. break; - /*Conflux*/ case 8: //Magic University + /*Conflux*/ case 8: //Magic University tlog4<<"Magic University not handled\n"; break; default: diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 6a5bb8fe4..581d13f9d 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -478,11 +478,15 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj) wwg->garr->recreateSlots(); wasGarrison = true; } - else + else if(CKingdomInterface *cki = dynamic_cast(*i)) {//a cheat for Kingdom Overview window (it has CWindowWithGarrison-childrens which are not present in ListInt) - CKingdomInterface *cki = dynamic_cast(*i);//need to create "Garrison Holder" class thingy - if (cki) - cki->updateAllGarrisons(); + //need to create "Garrison Holder" class thingy + cki->updateAllGarrisons(); + } + else if(CMarketplaceWindow *cmw = dynamic_cast(*i)) + { + if(obj == cmw->hero) + cmw->garrisonChanged(); } } @@ -1977,6 +1981,6 @@ void CPlayerInterface::stopMovement() void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) { - CMarketplaceWindow *cmw = new CMarketplaceWindow(market, market->availableModes().front()); + CMarketplaceWindow *cmw = new CMarketplaceWindow(market, visitor, market->availableModes().front()); GH.pushInt(cmw); } \ No newline at end of file diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 2ab884a9c..0d9639794 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -2581,8 +2581,9 @@ CCustomImgComponent::~CCustomImgComponent() SDL_FreeSurface(bmp); } -CMarketplaceWindow::CTradeableItem::CTradeableItem( int Type, int ID, bool Left) +CMarketplaceWindow::CTradeableItem::CTradeableItem( EType Type, int ID, bool Left, int Serial) { + serial = Serial; left = Left; type = Type; id = ID; @@ -2639,24 +2640,29 @@ SDL_Surface * CMarketplaceWindow::CTradeableItem::getSurface() return graphics->flags->ourImages[id].bitmap; case ARTIFACT: return graphics->artDefs->ourImages[id].bitmap; + case CREATURE: + return graphics->bigImgs[id]; default: return NULL; } } -static void initItems( std::vector &i, std::vector &p, int type, int amount, bool left, std::vector *ids/*=NULL*/ ) +static void initItems( std::vector &i, std::vector &p, CMarketplaceWindow::EType type, int amount, bool left, std::vector *ids/*=NULL*/ ) { if(ids) amin(amount, ids->size()); - i.resize(amount); for(int j=0;jsize()>j) ? (*ids)[j] : j, left); - i[j]->pos = p[j] + i[j]->pos; + int id = (ids && ids->size()>j) ? (*ids)[j] : j; + if(id < 0) + continue; + + i.push_back(new CMarketplaceWindow::CTradeableItem(type, id, left, j)); + i.back()->pos = p[j] + i.back()->pos; } } -CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode) - :market(Market) +CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode Mode) + :market(Market), hero(Hero), hLeft(NULL), hRight(NULL), readyToTrade(false) { OBJ_CONSTRUCTION_CAPTURING_ALL; type = BLOCK_ADV_HOTKEYS; @@ -2675,6 +2681,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode) ltype = RESOURCE; rtype = RESOURCE; break; + case RESOURCE_PLAYER: bgName = "TPMRKPTS.bmp"; ltype = RESOURCE; @@ -2685,13 +2692,40 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode) if(i != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(i) == PlayerState::INGAME) rIds->push_back(i); break; + + case CREATURE_RESOURCE: + bgName = "TPMRKCRS.bmp"; + ltype = CREATURE; + rtype = RESOURCE; + lIds = new std::vector; + for(int i = 0; i < 7; i++) + { + if(const CCreature *c = hero->getCreature(i)) + lIds->push_back(c->idNumber); + else + lIds->push_back(-1); + } + break; } bg = new CPicture(bgName); bg->colorizeAndConvert(LOCPLINT->playerID); - printAtMiddle(CGI->generaltexth->allTexts[158],300,27,FONT_BIG,tytulowy,*bg); //title - printAtMiddle(CGI->generaltexth->allTexts[270],154,148,FONT_SMALL,zwykly,*bg); //kingdom res. + if(market->o->ID == 99 || market->o->ID == 221) + { + printAtMiddle(CGI->generaltexth->allTexts[159],300,27,FONT_BIG,tytulowy,*bg); //title + } + else if(mode == CREATURE_RESOURCE) + { + if(market->o->ID == TOWNI_TYPE) + printAtMiddle(CGI->buildh->buildings[6][21]->Name(), 300, 27, FONT_BIG, tytulowy, *bg); //title + else + printAtMiddle(market->o->getHoverText(), 300, 27, FONT_BIG, tytulowy, *bg); //title + } + else + { + printAtMiddle(CGI->generaltexth->allTexts[158],300,27,FONT_BIG,tytulowy,*bg); //trading post + } std::vector lpos, rpos; getPositionsFor(lpos, false, ltype); @@ -2705,28 +2739,44 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, EMarketMode Mode) //slider and buttons must be created after bg slider = new CSlider(231,490,137,boost::bind(&CMarketplaceWindow::sliderMoved,this,_1),0,0); - hLeft = hRight = NULL; + ok = new AdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN); ok->assignedKeys.insert(SDLK_ESCAPE); deal = new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF"); max = new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF"); + //left side switch(Mode) { case RESOURCE_RESOURCE: - { - new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF"); - printAtMiddle(CGI->generaltexth->allTexts[168],445,147,FONT_SMALL,zwykly,*bg); //available for trade - } - break; case RESOURCE_PLAYER: - { - new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450,"TPMRKBU5.DEF"); - printAtMiddle(CGI->generaltexth->allTexts[169],445,55,FONT_SMALL,zwykly,*bg); //players - } + printAtMiddle(CGI->generaltexth->allTexts[270],154,148,FONT_SMALL,zwykly,*bg); //kingdom res. + break; + case CREATURE_RESOURCE: + printAtMiddle(boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name), 152, 102, FONT_SMALL, zwykly, *bg); //%s's Creatures break; } + //right side + switch(Mode) + { + case RESOURCE_RESOURCE: + case CREATURE_RESOURCE: + printAtMiddle(CGI->generaltexth->allTexts[168],445,148,FONT_SMALL,zwykly,*bg); //available for trade + break; + case RESOURCE_PLAYER: + printAtMiddle(CGI->generaltexth->allTexts[169],445,55,FONT_SMALL,zwykly,*bg); //players + break; + } + + if(printButtonFor(RESOURCE_PLAYER)) + new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF"); + if(printButtonFor(RESOURCE_RESOURCE)) + new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, RESOURCE_RESOURCE), 516, 450,"TPMRKBU5.DEF"); + if(printButtonFor(CREATURE_RESOURCE)) + new AdventureMapButton("","",boost::bind(&CMarketplaceWindow::setMode,this, CREATURE_RESOURCE), 516, 450,"TPMRKBU4.DEF"); + + max->block(true); deal->block(true); } @@ -2745,9 +2795,9 @@ CMarketplaceWindow::~CMarketplaceWindow() bg = NULL; } -void CMarketplaceWindow::show(SDL_Surface * to) +void CMarketplaceWindow::showAll(SDL_Surface * to) { - CIntObject::show(to); + CIntObject::showAll(to); if(hRight) @@ -2756,49 +2806,62 @@ void CMarketplaceWindow::show(SDL_Surface * to) CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148)); //left side - if(mode == RESOURCE_RESOURCE || mode == RESOURCE_PLAYER) + switch(ltype) { + case RESOURCE: for(int i=0;i(LOCPLINT->cb->getResourceAmount(i)), - left[i]->pos.x+36,left[i]->pos.y+57,FONT_SMALL,zwykly,to); + printAtMiddle(boost::lexical_cast(LOCPLINT->cb->getResourceAmount(i)), left[i]->pos.x+36,left[i]->pos.y+57,FONT_SMALL,zwykly,to); - - if(hLeft && hRight && (hLeft->id != hRight->id || mode != RESOURCE_RESOURCE)) + if(readyToTrade) { blitAt(hLeft->getSurface(),pos.x+141,pos.y+457,to); printAtMiddle(boost::lexical_cast( slider->value * r1 ),pos.x+156,pos.y+505,FONT_SMALL,zwykly,to); } + break; + + case CREATURE: + BOOST_FOREACH(CTradeableItem *t, left) + printAtMiddle(boost::lexical_cast(hero->getAmount(t->serial)), t->pos.x+29, t->pos.y+76, FONT_SMALL, zwykly, to); + + if(readyToTrade) + { + blitAt(hLeft->getSurface(),pos.x+128,pos.y+450,to); + printAtMiddle(boost::lexical_cast( slider->value * r1 ),pos.x+160,pos.y+527,FONT_SMALL,zwykly,to); + } + break; } - if(mode == RESOURCE_RESOURCE) + //right side + switch(rtype) { + case RESOURCE: if(hLeft) //print prices { for(int i=0; iid != hLeft->id) + if(right[i]->id != hLeft->id || mode != RESOURCE_RESOURCE) printAtMiddle(rSubs[i],right[i]->pos.x+36,right[i]->pos.y+57,FONT_SMALL,zwykly,to); else printAtMiddle(CGI->generaltexth->allTexts[164],right[i]->pos.x+36,right[i]->pos.y+57,FONT_SMALL,zwykly,to); } } - if(hLeft && hRight && (hLeft->id != hRight->id)) + if(readyToTrade) { blitAt(hRight->getSurface(),pos.x+429,pos.y+457,to); printAtMiddle(boost::lexical_cast( slider->value * r2 ),pos.x+443,pos.y+505,FONT_SMALL,zwykly,to); } - } - else if(mode == RESOURCE_PLAYER) - { + break; + + case PLAYER: BOOST_FOREACH(CTradeableItem *i, right) printAtMiddle(CGI->generaltexth->capColors[i->id], i->pos.x + 31, i->pos.y + 76, FONT_SMALL, zwykly, to); - - if(hLeft && hRight) + if(readyToTrade) { blitAt(hRight->getSurface(),pos.x+417,pos.y+451,to); printAtMiddle(CGI->generaltexth->capColors[hRight->id], pos.x+417 + 31, pos.y+451 + 76, FONT_SMALL, zwykly, to); } + break; } } @@ -2809,30 +2872,44 @@ void CMarketplaceWindow::setMax() void CMarketplaceWindow::makeDeal() { - LOCPLINT->cb->trade(market->o, mode, hLeft->id, hRight->id, slider->value*r1); + if(!slider->value) + return; + + int leftIdToSend = -1; + if(mode == CREATURE_RESOURCE) + leftIdToSend = hLeft->serial; + else + leftIdToSend = hLeft->id; + + LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero); slider->moveTo(0); hLeft = NULL; + hRight = NULL; selectionChanged(true); } void CMarketplaceWindow::sliderMoved( int to ) { + redraw(); } void CMarketplaceWindow::selectionChanged(bool side) { - if(hLeft && hRight && (hLeft->id!= hRight->id || mode != RESOURCE_RESOURCE)) + readyToTrade = (hLeft && hRight && (hLeft->id!= hRight->id || mode != RESOURCE_RESOURCE)); + + if(readyToTrade) { - if(mode == RESOURCE_RESOURCE) - { - market->getOffer(hLeft->id, hRight->id, r1, r2, mode); - slider->setAmount(LOCPLINT->cb->getResourceAmount(hLeft->id) / r1); - } - else if(mode == RESOURCE_PLAYER) - { - r1 = 1; - slider->setAmount(LOCPLINT->cb->getResourceAmount(hLeft->id)); - } + int newAmount = -1; + market->getOffer(hLeft->id, hRight->id, r1, r2, mode); + + if(ltype == RESOURCE) + newAmount = LOCPLINT->cb->getResourceAmount(hLeft->id); + else if(ltype == CREATURE) + newAmount = hero->getAmount(hLeft->serial) - (hero->Slots().size() == 1 && hero->needsLastStack()); + else + assert(0); + + slider->setAmount(newAmount / r1); slider->moveTo(0); max->block(false); deal->block(false); @@ -2851,7 +2928,7 @@ void CMarketplaceWindow::selectionChanged(bool side) int h1, h2; for(int i=0;igetOffer(hLeft->id, i, h1, h2, RESOURCE_RESOURCE); + market->getOffer(hLeft->id, i, h1, h2, mode); std::ostringstream oss; oss << h2; @@ -2860,35 +2937,84 @@ void CMarketplaceWindow::selectionChanged(bool side) rSubs[i] = oss.str(); } } + redraw(); } void CMarketplaceWindow::getPositionsFor(std::vector &poss, bool Right, EType type) const { - if(type == RESOURCE) - { - poss += genRect(66,74,39 ,180), genRect(66,74,122,180), genRect(66,74,204,180), - genRect(66,74,39,259), genRect(66,74,122,259), genRect(66,74,204,259), - genRect(66,74,122,338); - if(Right) - BOOST_FOREACH(Rect &r, poss) - r.x += 288; - } - else if(type == PLAYER) + int h, w, x, y, dx, dy; + int leftToRightOffset = 288; + + switch(type) { + case RESOURCE: + dx = 82; + dy = 79; + x = 39; + y = 180; + h = 66; + w = 74; + break; + case PLAYER: + dx = 83; + dy = 118; + h = 64; + w = 58; + x = 44; + y = 83; assert(Right); - poss += genRect(64, 58, 333, 84), genRect(64, 58, 333 + 83, 84), genRect(64, 58, 333 + 2 * 83, 84), - genRect(64, 58, 333, 84 + 118), genRect(64, 58, 333 + 83, 84 + 118), genRect(64, 58, 333 + 2 * 83, 84 + 118), - genRect(64, 58, 333 + 83, 84 + 2*118); + break; + case CREATURE://45,123 + x = 45; + y = 123; + w = 58; + h = 64; + dx = 83; + dy = 98; + assert(!Right); } + + + poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y), + genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy), + genRect(h, w, x + dx, y + 2*dy); + + if(Right) + BOOST_FOREACH(Rect &r, poss) + r.x += leftToRightOffset; } void CMarketplaceWindow::setMode(EMarketMode Mode) { - CMarketplaceWindow *nwindow = new CMarketplaceWindow(market, Mode); + CMarketplaceWindow *nwindow = new CMarketplaceWindow(market, hero, Mode); GH.popIntTotally(this); GH.pushInt(nwindow); } +bool CMarketplaceWindow::printButtonFor(EMarketMode M) const +{ + return market->allowsTrade(M) && M != mode && (hero || mode != CREATURE_RESOURCE); +} + +void CMarketplaceWindow::garrisonChanged() +{ + if(mode != CREATURE_RESOURCE) + return; + + std::set toRemove; + BOOST_FOREACH(CTradeableItem *t, left) + if(!hero->getAmount(t->serial)) + toRemove.insert(t); + + BOOST_FOREACH(CTradeableItem *t, toRemove) + { + if(active) + t->deactivate(); + left -= t; + delChild(t); + } +} + CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface * owner) { this->pos = pos; diff --git a/client/GUIClasses.h b/client/GUIClasses.h index 6e7226c92..37a80d924 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -518,26 +518,29 @@ public: class CMarketplaceWindow : public CIntObject { + bool printButtonFor(EMarketMode M) const; public: enum EType { - RESOURCE, PLAYER, ARTIFACT + RESOURCE, PLAYER, ARTIFACT, CREATURE }; class CTradeableItem : public CIntObject { public: - int type; //0 - res, 1 - artif big, 2 - artif small, 3 - player flag - int id; + EType type; + int id; + int serial; bool left; CFunctionList callback; void show(SDL_Surface * to); void clickLeft(tribool down, bool previousState); SDL_Surface *getSurface(); - CTradeableItem(int Type, int ID, bool Left); + CTradeableItem(EType Type, int ID, bool Left, int Serial); }; const IMarket *market; + const CGHeroInstance *hero; CPicture *bg; //background std::vector left, right; std::vector rSubs; //offer caption @@ -548,17 +551,20 @@ public: int r1, r2; //suggested amounts of traded resources AdventureMapButton *ok, *max, *deal; CSlider *slider; //for choosing amount to be exchanged + bool readyToTrade; - void show(SDL_Surface * to); + void showAll(SDL_Surface * to); void setMax(); void sliderMoved(int to); void makeDeal(); void selectionChanged(bool side); //true == left - CMarketplaceWindow(const IMarket *Market, EMarketMode Mode = RESOURCE_RESOURCE); //c-tor + CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = NULL, EMarketMode Mode = RESOURCE_RESOURCE); //c-tor ~CMarketplaceWindow(); //d-tor void setMode(EMarketMode Mode); //mode setter void getPositionsFor(std::vector &poss, bool Right, EType type) const; + + void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold) }; class CSystemOptionsWindow : public CIntObject diff --git a/client/Graphics.cpp b/client/Graphics.cpp index ece8798e2..d45d64980 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -61,7 +61,7 @@ SDL_Surface * Graphics::drawHeroInfoWin(const InfoAboutHero &curh) } else { - printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*i->second.count],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret); + printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*(i->second.count-1)],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,zwykly,ret); } } diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 99b9bdd2b..ffdce7b24 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -79,7 +79,7 @@ void IObjectInterface::preInit() void CPlayersVisited::setPropertyDer( ui8 what, ui32 val ) { if(what == 10) - players.insert(val); + players.insert((ui8)val); } bool CPlayersVisited::hasVisited( ui8 player ) const @@ -1162,6 +1162,7 @@ void CGHeroInstance::pushPrimSkill(int which, int val) void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root /*= NULL*/) const { +#define FOREACH_OWNER_TOWN(town) if(const PlayerState *p = cb->getPlayerState(tempOwner)) BOOST_FOREACH(const CGTownInstance *town, p->towns) CArmedInstance::getBonuses(out, selector, root); //TODO eliminate by moving secondary skills effects to bonus system @@ -1172,7 +1173,7 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::SECONDARY_SKILL, luckSkill, 9, VLC->generaltexth->arraytxt[73+luckSkill])); //guardian spirit - BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns) + FOREACH_OWNER_TOWN(t) if(t->subID ==1 && vstd::contains(t->builtBuildings,26)) //rampart with grail out.push_back(Bonus(Bonus::PERMANENT, Bonus::LUCK, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[1][26].first + " +2")); } @@ -1180,7 +1181,7 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const if(Selector::matchesType(selector, Bonus::SEA_MOVEMENT)) { //lighthouses - BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns) + FOREACH_OWNER_TOWN(t) if(t->subID == 0 && vstd::contains(t->builtBuildings,17)) //castle out.push_back(Bonus(Bonus::PERMANENT, Bonus::SEA_MOVEMENT, Bonus::TOWN_STRUCTURE, +500, 17, VLC->generaltexth->buildings[0][17].first + " +500")); } @@ -1192,14 +1193,14 @@ void CGHeroInstance::getBonuses(BonusList &out, const CSelector &selector, const out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::SECONDARY_SKILL, moraleSkill, 6, VLC->generaltexth->arraytxt[104+moraleSkill])); //colossus - BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns) + FOREACH_OWNER_TOWN(t) if(t->subID == 0 && vstd::contains(t->builtBuildings,26)) //castle out.push_back(Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::TOWN_STRUCTURE, +2, 26, VLC->generaltexth->buildings[0][26].first + " +2")); } if(Selector::matchesTypeSubtype(selector, Bonus::SECONDARY_SKILL_PREMY, 12)) //necromancy { - BOOST_FOREACH(const CGTownInstance *t, cb->getPlayerState(tempOwner)->towns) + FOREACH_OWNER_TOWN(t) { if(t->subID == 4) //necropolis { @@ -5932,7 +5933,30 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) val2 = 1; } } + break; + case CREATURE_RESOURCE: + { + const float effectivenessArray[] = {0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1}; + float effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)]; + float r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold + g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource + + if(r>g) //if given resource is more expensive than wanted + { + val2 = ceil(r / g); + val1 = 1; + } + else //if wanted resource is more expensive + { + val1 = (g / r) + 0.5f; + val2 = 1; + } + } + break; + case RESOURCE_PLAYER: + val1 = 1; + val2 = 1; break; default: assert(0); @@ -5949,19 +5973,29 @@ bool IMarket::allowsTrade(EMarketMode mode) const int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const { - if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE) - return -1; - else - return 1; + switch(mode) + { + case RESOURCE_RESOURCE: + case ARTIFACT_RESOURCE: + case CREATURE_RESOURCE: + return -1; + default: + return 1; + } } std::vector IMarket::availableItemsIds(EMarketMode mode) const { std::vector ret; - if(mode == RESOURCE_RESOURCE || ARTIFACT_RESOURCE || CREATURE_RESOURCE) + switch(mode) + { + case RESOURCE_RESOURCE: + case ARTIFACT_RESOURCE: + case CREATURE_RESOURCE: for (int i = 0; i < 7; i++) ret.push_back(i); - + break; + } return ret; } @@ -5971,8 +6005,10 @@ const IMarket * IMarket::castFrom(const CGObjectInstance *obj) { case TOWNI_TYPE: return static_cast(obj); + case 7: //Black Market case 99: //Trading Post case 221: //Trading Post (snow) + case 213: //Freelancer's Guild return static_cast(obj); default: tlog1 << "Cannot cast to IMarket object with ID " << obj->ID << std::endl; @@ -6034,7 +6070,10 @@ bool CGMarket::allowsTrade(EMarketMode mode) const default: return false; } + case CREATURE_RESOURCE: + return ID == 213; //Freelancer's Guild } + return false; } int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 3ea554fbb..08b10f06a 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -192,6 +192,7 @@ void CCreatureSet::setFormation(bool tight) void CCreatureSet::setStackCount(TSlot slot, TQuantity count) { assert(vstd::contains(slots, slot)); + assert(count > 0); slots[slot].count = count; } diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 707857235..31a312201 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -80,7 +80,7 @@ public: virtual bool needsLastStack() const; //true if last stack cannot be taken int getArmyStrength() const; //sum of AI values of creatures ui64 getPower (TSlot slot) const; //value of specific stack - std::string getRoughAmount (TSlot slot) const; //rought size of specific stack + std::string getRoughAmount (TSlot slot) const; //rough size of specific stack bool contains(const CStackInstance *stack) const; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index ac0a4147b..7d6996795 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1752,7 +1752,7 @@ void CGameState::getNeighbours( const TerrainTile &srct, int3 tile, std::vector< const TerrainTile &hlpt = map->getTile(hlp); //we cannot visit things from blocked tiles - if(srct.blocked && hlpt.visitable) + if(srct.blocked && hlpt.visitable && srct.blockingObjects.front()->ID != HEROI_TYPE) { continue; } @@ -2216,6 +2216,8 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int CGPathNode *cp = mq.front(); mq.pop(); + const int3 guardPosition = guardingCreaturePosition(cp->coord); + const bool guardedPosition = (guardPosition != int3(-1, -1, -1) && cp->coord != src); const TerrainTile &ct = map->getTile(cp->coord); int movement = cp->moveRemains, turn = cp->turns; if(!movement) @@ -2252,7 +2254,6 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int remains = moveAtNextTile - cost; } - const bool guardedPosition = guardingCreaturePosition(cp->coord) != int3(-1, -1, -1); const bool neighborIsGuard = guardingCreaturePosition(cp->coord) == dp.coord; if((dp.turns==0xff //we haven't been here before @@ -2267,10 +2268,10 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int dp.theNodeBefore = cp; const bool guardedNeighbor = guardingCreaturePosition(dp.coord) != int3(-1, -1, -1); - const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord; + //const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord; //can this be true? hero never passes from monster tile... if (dp.accessible == CGPathNode::ACCESSIBLE || dp.accessible == CGPathNode::FLYABLE - || (guardedNeighbor && !positionIsGuard)) // Can step into a hostile tile once. + || (guardedNeighbor && !guardedPosition)) // Can step into a hostile tile once. { mq.push(&dp); } @@ -3851,7 +3852,7 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed ) //hide info about hero stacks counts using descriptives names ids for(TSlots::const_iterator i = army.Slots().begin(); i != army.Slots().end(); ++i) { - army.setStackCount(i->first, i->second.getQuantityID()); + army.setStackCount(i->first, i->second.getQuantityID()+1); } } } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 13ff28215..6699fb4f8 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1064,7 +1064,7 @@ void CGameHandler::newTurn() } } } -void CGameHandler::run(bool resume) +void CGameHandler::run(bool resume, const StartInfo *si /*= NULL*/) { using namespace boost::posix_time; BOOST_FOREACH(CConnection *cc, conns) @@ -1072,7 +1072,7 @@ void CGameHandler::run(bool resume) ui8 quantity, pom; //ui32 seed; if(!resume) - (*cc) << gs->scenarioOps << gs->map->checksum << gs->seed; + (*cc) << si << gs->map->checksum << gs->seed; // gs->scenarioOps (*cc) >> quantity; //how many players will be handled at that client for(int i=0;iSlots(), slot)) + COMPLAIN_RET("Hero doesn't have any creature in that slot!"); + + const CStackInstance &s = hero->getStack(slot); + + if(s.count < count //can't sell more creatures than have + || hero->Slots().size() == 1 && hero->needsLastStack() && s.count == count) //can't sell last stack + { + COMPLAIN_RET("Not enough creatures in army!"); + } + + int b1, b2; //base quantities for trade + market->getOffer(s.type->idNumber, resourceID, b1, b2, CREATURE_RESOURCE); + int units = count / b1; //how many base quantities we trade + + if(count%b1) //all offered units of resource should be used, if not -> somewhere in calculations must be an error + { + //TODO: complain? + assert(0); + } + + + SetGarrisons sg; + sg.garrs[hero->id] = hero->getArmy(); + if(s.count > count) + sg.garrs[hero->id].setStackCount(slot, s.count - count); + else + sg.garrs[hero->id].eraseStack(slot); + sendAndApply(&sg); + + SetResource sr; + sr.player = hero->tempOwner; + sr.resid = resourceID; + sr.val = getResource(hero->tempOwner, resourceID) + b2 * units; + sendAndApply(&sr); + + return true; +} bool CGameHandler::sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2) { diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 7ce5ddc96..7c7bb0b82 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -167,6 +167,7 @@ public: bool setFormation( si32 hid, ui8 formation ); bool tradeResources(const IMarket *market, ui32 val, ui8 player, ui32 id1, ui32 id2); bool sendResources(ui32 val, ui8 player, ui32 r1, ui32 r2); + bool sellCreatures(ui32 count, const IMarket *market, const CGHeroInstance * hero, ui32 slot, ui32 resourceID); bool assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo); bool buyArtifact( ui32 hid, si32 aid ); bool swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot); @@ -203,7 +204,7 @@ public: void sendAndApply(SetResources * info); void sendAndApply(NewStructures * info); - void run(bool resume); + void run(bool resume, const StartInfo *si = NULL); void newTurn(); void handleAfterAttackCasting( const BattleAttack & bat ); friend class CVCMIServer; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index fa85ec8d6..cc6b5f834 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -92,6 +92,7 @@ void CVCMIServer::newGame(CConnection *c) *c << ui8(0); //OK! } + StartInfo startInfoCpy = *si; gh.init(si,rand()); c->setGS(gh.gs); @@ -118,7 +119,7 @@ void CVCMIServer::newGame(CConnection *c) gh.conns.insert(cc); } - gh.run(false); + gh.run(false, &startInfoCpy); } void CVCMIServer::start() { diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index ee595758c..f4229f3bd 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -141,6 +141,10 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh ) return gh->tradeResources(m, val, player, r1, r2); case RESOURCE_PLAYER: return gh->sendResources(val, player, r1, r2); + case CREATURE_RESOURCE: + if(!hero) + COMPLAIN_AND_RETURN("Only hero can sell creatures!"); + return gh->sellCreatures(val, m, hero, r1, r2); default: gh->complain("Unknown exchange mode!"); ERROR_AND_RETURN;