diff --git a/client/AdventureMapClasses.cpp b/client/AdventureMapClasses.cpp index 6fd5f5022..efe13bfbc 100644 --- a/client/AdventureMapClasses.cpp +++ b/client/AdventureMapClasses.cpp @@ -247,7 +247,7 @@ void CHeroList::update(const CGHeroInstance * hero) for (auto iter = list->getItems().begin(); iter != list->getItems().end(); iter++) { auto item = dynamic_cast(*iter); - if (item && item->hero == hero) + if (item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero)) { item->update(); return; @@ -817,13 +817,13 @@ void CInfoBar::showSelection() auto hero = dynamic_cast(adventureInt->selection); if (hero) { - showHeroSelection(hero, false); + showHeroSelection(hero); return; } auto town = dynamic_cast(adventureInt->selection); if (town) { - showTownSelection(town, false); + showTownSelection(town); return; } } @@ -903,7 +903,7 @@ void CInfoBar::updateEnemyTurn(double progress) redraw(); } -void CInfoBar::showHeroSelection(const CGHeroInstance * hero, bool onlyUpdate) +void CInfoBar::showHeroSelection(const CGHeroInstance * hero) { if (!hero) return; @@ -914,7 +914,7 @@ void CInfoBar::showHeroSelection(const CGHeroInstance * hero, bool onlyUpdate) redraw(); } -void CInfoBar::showTownSelection(const CGTownInstance * town, bool onlyUpdate) +void CInfoBar::showTownSelection(const CGTownInstance * town) { if (!town) return; diff --git a/client/AdventureMapClasses.h b/client/AdventureMapClasses.h index aefab1d5e..5ad97ed5a 100644 --- a/client/AdventureMapClasses.h +++ b/client/AdventureMapClasses.h @@ -277,8 +277,6 @@ class CInfoBar : public CIntObject //removes all information about current state, deactivates timer (if any) void reset(EState newState); - //reset to default view - selected object - void showSelection(); void tick(); @@ -301,10 +299,12 @@ public: /// NOTE: currently DISABLED. Check comments in CInfoBar::CVisibleInfo::loadEnemyTurn() void updateEnemyTurn(double progress); + /// reset to default view - selected object + void showSelection(); + /// show hero\town information - /// if onlyUpdate set to true this call won't switch to town\hero but only update current view - void showHeroSelection(const CGHeroInstance * hero, bool onlyUpdate); - void showTownSelection(const CGTownInstance * town, bool onlyUpdate); + void showHeroSelection(const CGHeroInstance * hero); + void showTownSelection(const CGTownInstance * town); /// for 3 seconds shows amount of town halls and players status void showGameStatus(); diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index 03494790e..b46e97c73 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -981,7 +981,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/) assert(sel); LOCPLINT->cb->setSelection(sel); selection = sel; - if (LOCPLINT->battleInt == NULL) + if (LOCPLINT->battleInt == NULL && active & GENERAL) CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(sel->visitablePos())->tertype], -1); if(centerView) centerOn(sel); @@ -991,7 +991,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/) { auto town = dynamic_cast(sel); - infoBar.showTownSelection(town, false); + infoBar.showTownSelection(town); townList.select(town); heroList.select(nullptr); @@ -1002,7 +1002,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/) { auto hero = dynamic_cast(sel); - infoBar.showHeroSelection(hero, false); + infoBar.showHeroSelection(hero); heroList.select(hero); townList.select(nullptr); diff --git a/client/CCastleInterface.h b/client/CCastleInterface.h index 064895ec0..b19f4acbf 100644 --- a/client/CCastleInterface.h +++ b/client/CCastleInterface.h @@ -198,7 +198,6 @@ class CCastleInterface : public CWindowObject, public CWindowWithGarrison CGStatusBar * statusbar; CTownInfo *hall, *fort; - CTownList * townlist; CAdventureMapButton *exit; CAdventureMapButton *split; @@ -206,6 +205,8 @@ class CCastleInterface : public CWindowObject, public CWindowWithGarrison std::vector creainfo;//small icons of creatures (bottom-left corner); public: + CTownList * townlist; + //TODO: remove - currently used only in dialog messages CDefEssential* bicons; //150x70 buildings imgs @@ -214,6 +215,7 @@ public: HeroSlots *heroes; CCastleBuildings *builds; + //from - previously selected castle (if any) CCastleInterface(const CGTownInstance * Town, const CGTownInstance * from = nullptr); //c-tor ~CCastleInterface(); diff --git a/client/CMT.cpp b/client/CMT.cpp index c5cbb2712..cb0f87ff0 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -251,6 +251,17 @@ int main(int argc, char** argv) const JsonNode& video = settings["video"]; const JsonNode& res = video["screenRes"]; + //something is really wrong... + if (res["width"].Float() < 100 || res["height"].Float() < 100) + { + tlog0 << "Fatal error: failed to load settings!\n"; + tlog0 << "Possible reasons:\n"; + tlog0 << "\tCorrupted local configuration file at " << GVCMIDirs.UserPath << "/config/settings.json\n"; + tlog0 << "\tMissing or corrupted global configuration file at " << GameConstants::DATA_DIR << "/config/defaultSettings.json\n"; + tlog0 << "VCMI will now exit...\n"; + exit(EXIT_FAILURE); + } + setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool()); tlog0 <<"\tInitializing screen: "<getNextHeroIndex(vstd::find_pos(wanderingHeroes, hero)); + if (next >= 0) + newSelection = wanderingHeroes[next]; + + //or town + if (!newSelection || newSelection == hero) + { + if (towns.empty()) + newSelection = nullptr; + else + newSelection = towns.front(); + } + } + wanderingHeroes -= hero; if(vstd::contains(paths, hero)) paths.erase(hero); adventureInt->heroList.update(hero); + if (makingTurn) + adventureInt->select(newSelection, true); + else + adventureInt->selection = nullptr; } + void CPlayerInterface::heroCreated(const CGHeroInstance * hero) { EVENT_HANDLER_CALLED_BY_CLIENT; @@ -560,6 +584,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID, break; } adventureInt->townList.update(town); + castleInt->townlist->update(town); } void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) @@ -1076,8 +1101,8 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town ) else if(GH.listInt.size() && (town->ID == 17 || town->ID == 20 || town->ID == 106)) //external dwelling { CRecruitmentWindow *crw = dynamic_cast(GH.topInt()); - if(crw) - crw->initCres(); + if(crw && crw->dwelling == town) + crw->availableCreaturesChanged(); } } @@ -2159,10 +2184,7 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance *h) void CPlayerInterface::updateInfo(const CGObjectInstance * specific) { - if (specific->ID == GameConstants::TOWNI_TYPE) - adventureInt->infoBar.showTownSelection(dynamic_cast(specific), true); - else - adventureInt->infoBar.showHeroSelection(dynamic_cast(specific), true); + adventureInt->infoBar.showSelection(); } void CPlayerInterface::battleNewRoundFirst( int round ) diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 2443d5082..86ab8290f 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -1039,15 +1039,146 @@ CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool An anim = new CCreatureAnim(0, 0, cre->animDefName, Rect()); anim->clipRect(cre->doubleWide?170:150, 155, bg->pos.w, bg->pos.h); anim->startPreview(); + + pos.w = bg->pos.w; + pos.h = bg->pos.h; } -void CRecruitmentWindow::Max() +CRecruitmentWindow::CCreatureCard::CCreatureCard(CRecruitmentWindow *window, const CCreature *crea, int totalAmount): + CIntObject(LCLICK | RCLICK), + parent(window), + selected(false), + creature(crea), + amount(totalAmount) { - slider->moveToMax(); + OBJ_CONSTRUCTION_CAPTURING_ALL; + pic = new CCreaturePic(1,1, creature, true, true); + // 1 + 1 px for borders + pos.w = pic->pos.w + 2; + pos.h = pic->pos.h + 2; } -void CRecruitmentWindow::Buy() + +void CRecruitmentWindow::CCreatureCard::select(bool on) { - int crid = creatures[which].ID, + selected = on; + redraw(); +} + +void CRecruitmentWindow::CCreatureCard::clickLeft(tribool down, bool previousState) +{ + if (down) + parent->select(this); +} + +void CRecruitmentWindow::CCreatureCard::clickRight(tribool down, bool previousState) +{ + if (down) + GH.pushInt(createCreWindow(creature->idNumber, 0, 0)); +} + +void CRecruitmentWindow::CCreatureCard::showAll(SDL_Surface * to) +{ + CIntObject::showAll(to); + if (selected) + drawBorder(to, pos, int3(248, 0, 0)); + else + drawBorder(to, pos, int3(232, 212, 120)); +} + +CRecruitmentWindow::CCostBox::CCostBox(Rect position, std::string title) +{ + type |= REDRAW_PARENT; + pos = position + pos; + OBJ_CONSTRUCTION_CAPTURING_ALL; + new CLabel(pos.w/2, 10, FONT_SMALL, CENTER, Colors::Cornsilk, title); +} + +void CRecruitmentWindow::CCostBox::set(TResources res) +{ + //just update values + BOOST_FOREACH(auto & item, resources) + { + item.second.first->setTxt(boost::lexical_cast(res[item.first])); + } +} + +void CRecruitmentWindow::CCostBox::createItems(TResources res) +{ + OBJ_CONSTRUCTION_CAPTURING_ALL; + + BOOST_FOREACH(auto & curr, resources) + { + delete curr.second.first; + delete curr.second.second; + } + resources.clear(); + + TResources::nziterator iter(res); + while (iter.valid()) + { + CAnimImage * image = new CAnimImage("RESOURCE", iter->resType); + CLabel * text = new CLabel(15, 43, FONT_SMALL, CENTER, Colors::Cornsilk, "0"); + + resources.insert(std::make_pair(iter->resType, std::make_pair(text, image))); + iter++; + } + + if (!resources.empty()) + { + int curx = pos.w / 2 - (16 * resources.size()) - (8 * (resources.size() - 1)); + //reverse to display gold as first resource + BOOST_REVERSE_FOREACH(auto & res, resources) + { + res.second.first->moveBy(Point(curx, 22)); + res.second.second->moveBy(Point(curx, 22)); + curx += 48; + } + } + redraw(); +} + +void CRecruitmentWindow::select(CCreatureCard *card) +{ + if (card == selected) + return; + + if (selected) + selected->select(false); + + selected = card; + + if (selected) + selected->select(true); + + if (card) + { + si32 maxAmount = card->creature->maxAmount(LOCPLINT->cb->getResourceAmount()); + + vstd::amin(maxAmount, card->amount); + + slider->setAmount(maxAmount); + + if(slider->value) + slider->moveTo(0); + else // if slider already at 0 - emulate call to sliderMoved() + sliderMoved(0); + + costPerTroopValue->createItems(card->creature->cost); + totalCostValue->createItems(card->creature->cost); + + costPerTroopValue->set(card->creature->cost); + + //Recruit %s + title->setTxt(boost::str(boost::format(CGI->generaltexth->tcommands[21]) % card->creature->namePl)); + + maxButton->block(maxAmount == 0); + slider->block(maxAmount == 0); + } +} + +void CRecruitmentWindow::buy() +{ + int crid = selected->creature->idNumber, dstslot = dst-> getSlotFor(crid); if(dstslot < 0 && !vstd::contains(CGI->arth->bigArtifacts,CGI->arth->convertMachineID(crid, true))) //no available slot @@ -1067,175 +1198,122 @@ void CRecruitmentWindow::Buy() return; } - recruit(crid, slider->value); + onRecruit(crid, slider->value); if(level >= 0) close(); else slider->moveTo(0); } -void CRecruitmentWindow::Cancel() -{ - close(); -} -void CRecruitmentWindow::sliderMoved(int to) -{ - buy->block(!to); - redraw(); -} -void CRecruitmentWindow::clickLeft(tribool down, bool previousState) -{ - for(int i=0;imotion.x, GH.current->motion.y)) - { - which = i; - int newAmount = std::min(amounts[i],creatures[i].amount); - slider->setAmount(newAmount); - max->block(!newAmount); - - if(slider->value > newAmount) - slider->moveTo(newAmount); - else - slider->moveTo(slider->value); - redraw(); - break; - } - } -} -void CRecruitmentWindow::clickRight(tribool down, bool previousState) -{ - if(down) - { - int curx = 192 + 51 - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); - for(int i=0;imotion.x, GH.current->motion.y)) - { - CIntObject *popup = createCreWindow(creatures[i].ID, 0, 0); - GH.pushInt(popup); - break; - } - curx += TOTAL_CREATURE_WIDTH; - } - } -} void CRecruitmentWindow::showAll(SDL_Surface * to) { CWindowObject::showAll(to); + + // recruit\total values drawBorder(to, pos.x + 172, pos.y + 222, 67, 42, int3(239,215,123)); drawBorder(to, pos.x + 246, pos.y + 222, 67, 42, int3(239,215,123)); + + //cost boxes drawBorder(to, pos.x + 64, pos.y + 222, 99, 76, int3(239,215,123)); drawBorder(to, pos.x + 322, pos.y + 222, 99, 76, int3(239,215,123)); + + //buttons borders drawBorder(to, pos.x + 133, pos.y + 312, 66, 34, int3(173,142,66)); drawBorder(to, pos.x + 211, pos.y + 312, 66, 34, int3(173,142,66)); drawBorder(to, pos.x + 289, pos.y + 312, 66, 34, int3(173,142,66)); - - char pom[15]; - SDL_itoa(creatures[which].amount-slider->value,pom,10); //available - printAtMiddleLoc(pom,205,253,FONT_SMALL,Colors::Cornsilk,to); - SDL_itoa(slider->value,pom,10); //recruit - printAtMiddleLoc(pom,279,253,FONT_SMALL,Colors::Cornsilk,to); - printAtMiddleLoc(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID]->namePl,243,32,FONT_BIG,Colors::Jasmine,to); //eg "Recruit Dragon flies" - - int curx = 122-creatures[which].res.size()*24; - for(int i=creatures[which].res.size()-1; i>=0; i--)// decrement used to make gold displayed as first res - { - blitAtLoc(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx,243,to); - blitAtLoc(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx+258,243,to); - SDL_itoa(creatures[which].res[i].second,pom,10); - printAtMiddleLoc(pom,curx+15,287,FONT_SMALL,Colors::Cornsilk,to); - SDL_itoa(creatures[which].res[i].second * slider->value,pom,10); - printAtMiddleLoc(pom,curx+15+258,287,FONT_SMALL,Colors::Cornsilk,to); - curx+=32+16;//size of bitmap + distance between them - } - - for(int j=0;j &Recruit, int y_offset): CWindowObject(PLAYER_COLORED, "TPRCRT"), - recruit(Recruit), - dwelling(Dwelling), + onRecruit(Recruit), level(Level), - dst(Dst) + dst(Dst), + selected(nullptr), + dwelling(Dwelling) { - addUsedEvents(LCLICK | RCLICK); - OBJ_CONSTRUCTION_CAPTURING_ALL; + moveBy(Point(0, y_offset)); + + OBJ_CONSTRUCTION_CAPTURING_ALL; + new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - which = 0; - bar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - max = new CAdventureMapButton(CGI->generaltexth->zelp[553],boost::bind(&CRecruitmentWindow::Max,this),134,313,"IRCBTNS.DEF",SDLK_m); - buy = new CAdventureMapButton(CGI->generaltexth->zelp[554],boost::bind(&CRecruitmentWindow::Buy,this),212,313,"IBY6432.DEF",SDLK_RETURN); - cancel = new CAdventureMapButton(CGI->generaltexth->zelp[555],boost::bind(&CRecruitmentWindow::Cancel,this),290,313,"ICN6432.DEF",SDLK_ESCAPE); slider = new CSlider(176,279,135,0,0,0,0,true); slider->moved = boost::bind(&CRecruitmentWindow::sliderMoved,this, _1); - initCres(); + maxButton = new CAdventureMapButton(CGI->generaltexth->zelp[553],boost::bind(&CSlider::moveToMax,slider),134,313,"IRCBTNS.DEF",SDLK_m); + buyButton = new CAdventureMapButton(CGI->generaltexth->zelp[554],boost::bind(&CRecruitmentWindow::buy,this),212,313,"IBY6432.DEF",SDLK_RETURN); + cancelButton = new CAdventureMapButton(CGI->generaltexth->zelp[555],boost::bind(&CRecruitmentWindow::close,this),290,313,"ICN6432.DEF",SDLK_ESCAPE); + + title = new CLabel(243, 32, FONT_BIG, CENTER, Colors::Jasmine); + availableValue = new CLabel(205, 253, FONT_SMALL, CENTER, Colors::Cornsilk); + toRecruitValue = new CLabel(279, 253, FONT_SMALL, CENTER, Colors::Cornsilk); + + costPerTroopValue = new CCostBox(Rect(65, 222, 97, 74), CGI->generaltexth->allTexts[346]); + totalCostValue = new CCostBox(Rect(323, 222, 97, 74), CGI->generaltexth->allTexts[466]); - new CLabel(113, 232, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[346]); //cost per troop t new CLabel(205, 233, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[465]); //available t new CLabel(279, 233, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[16]); //recruit t - new CLabel(371, 232, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[466]); //total cost t - //border for creatures - int curx = 192 + 50 - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); - for(int i=0;icreh->creatures[creatures[i].ID]); - curx += TOTAL_CREATURE_WIDTH; - } - - if(!creatures[0].amount || !amounts[0]) - { - max->block(true); - slider->block(true); - } - buy->block(true); + availableCreaturesChanged(); } -void CRecruitmentWindow::initCres() +void CRecruitmentWindow::availableCreaturesChanged() { - creatures.clear(); - amounts.clear(); + OBJ_CONSTRUCTION_CAPTURING_ALL; + + //deselect card + select(nullptr); + + static const int SPACE_BETWEEN = 18; + static const int CREATURE_WIDTH = 102; + static const int TOTAL_CREATURE_WIDTH = SPACE_BETWEEN + CREATURE_WIDTH; + + //delete old cards + BOOST_FOREACH(auto & card, cards) + delete card; + cards.clear(); for(int i=0; icreatures.size(); i++) { + //find appropriate level if(level >= 0 && i != level) continue; - for(int j = dwelling->creatures[i].second.size() - 1; j >= 0 ; j--) - { - creatures.resize(creatures.size()+1); - creinfo &cur = creatures.back(); + int amount = dwelling->creatures[i].first; - cur.amount = dwelling->creatures[i].first; - cur.ID = dwelling->creatures[i].second[j]; - const CCreature * cre= CGI->creh->creatures[cur.ID]; - - for(int k=0; kcost.size(); k++) - if(cre->cost[k]) - cur.res.push_back(std::make_pair(k,cre->cost[k])); - amounts.push_back(cre->maxAmount(LOCPLINT->cb->getResourceAmount())); - } + //create new cards + BOOST_REVERSE_FOREACH(auto & creature, dwelling->creatures[i].second) + cards.push_back(new CCreatureCard(this, CGI->creh->creatures[creature], amount)); } - slider->setAmount(std::min(amounts[which],creatures[which].amount)); + assert(!cards.empty()); + + //now we know total amount of cards and can move them to correct position + int curx = 192 + 50 - (CREATURE_WIDTH*cards.size()/2) - (SPACE_BETWEEN*(cards.size()-1)/2); + BOOST_FOREACH(auto & card, cards) + { + card->moveBy(Point(curx, 64)); + curx += TOTAL_CREATURE_WIDTH; + } + + select(cards.front()); + + if(slider->value) + slider->moveTo(0); + else // if slider already at 0 - emulate call to sliderMoved() + sliderMoved(0); +} + +void CRecruitmentWindow::sliderMoved(int to) +{ + if (!selected) + return; + + buyButton->block(!to); + availableValue->setTxt(boost::lexical_cast(selected->amount - to)); + toRecruitValue->setTxt(boost::lexical_cast(to)); + + totalCostValue->set(selected->creature->cost * to); } CSplitWindow::CSplitWindow(const CCreature * creature, boost::function callback_, @@ -2202,6 +2280,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan case EMarketMode::RESOURCE_PLAYER: case EMarketMode::RESOURCE_ARTIFACT: new CLabel(154, 148, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[270]); + break; case EMarketMode::CREATURE_RESOURCE: //%s's Creatures @@ -3138,6 +3217,9 @@ void CSystemOptionsWindow::setGameRes(int index) while (index--) iter++; + //do not set resolution to illegal one (0x0) + assert(iter!=conf.guiOptions.end() && iter->first.first > 0 && iter->first.second > 0); + Settings gameRes = settings.write["video"]["screenRes"]; gameRes["width"].Float() = iter->first.first; gameRes["height"].Float() = iter->first.second; diff --git a/client/GUIClasses.h b/client/GUIClasses.h index f0a91b45e..76271755c 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -333,39 +333,62 @@ public: /// Recruitment window where you can recruit creatures class CRecruitmentWindow : public CWindowObject { -public: - static const int SPACE_BETWEEN = 18; - static const int CREATURE_WIDTH = 102; - static const int TOTAL_CREATURE_WIDTH = SPACE_BETWEEN + CREATURE_WIDTH; - - struct creinfo + class CCreatureCard : public CIntObject { - SDL_Rect pos; + CRecruitmentWindow * parent; CCreaturePic *pic; //creature's animation - int ID, amount; //creature ID and available amount - std::vector > res; //res_id - cost_per_unit - }; - std::vector amounts; //how many creatures we can afford - std::vector creatures; //recruitable creatures - boost::function recruit; //void (int ID, int amount) <-- call to recruit creatures - CSlider *slider; //for selecting amount - CAdventureMapButton *max, *buy, *cancel; - CGStatusBar *bar; - int which; //which creature is active + bool selected; + + void clickLeft(tribool down, bool previousState); + void clickRight(tribool down, bool previousState); + void showAll(SDL_Surface *to); + public: + const CCreature * creature; + si32 amount; + + void select(bool on); + + CCreatureCard(CRecruitmentWindow * window, const CCreature *crea, int totalAmount); + }; + + /// small class to display creature costs + class CCostBox : public CIntObject + { + std::map > resources; + public: + //res - resources to show + void set(TResources res); + //res - visible resources + CCostBox(Rect position, std::string title); + void createItems(TResources res); + }; + + boost::function onRecruit; //void (int ID, int amount) <-- call to recruit creatures - const CGDwelling *dwelling; int level; const CArmedInstance *dst; - void Max(); - void Buy(); - void Cancel(); + CCreatureCard * selected; + std::vector cards; + + CSlider *slider; //for selecting amount + CAdventureMapButton *maxButton, *buyButton, *cancelButton; + //labels for visible values + CLabel * title; + CLabel * availableValue; + CLabel * toRecruitValue; + CCostBox * costPerTroopValue; + CCostBox * totalCostValue; + + void select(CCreatureCard * card); + void buy(); void sliderMoved(int to); - void clickLeft(tribool down, bool previousState); - void clickRight(tribool down, bool previousState); - void showAll(SDL_Surface * to); - void initCres(); + + void showAll(SDL_Surface *to); +public: + const CGDwelling * const dwelling; CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function & Recruit, int y_offset = 0); //creatures - pairs //c-tor + void availableCreaturesChanged(); }; /// Split window where creatures can be splitted up into two single unit stacks diff --git a/client/UIFramework/CIntObjectClasses.cpp b/client/UIFramework/CIntObjectClasses.cpp index 149492801..9a23cac19 100644 --- a/client/UIFramework/CIntObjectClasses.cpp +++ b/client/UIFramework/CIntObjectClasses.cpp @@ -751,17 +751,22 @@ CSlider::CSlider(int x, int y, int totalw, boost::function Moved, int if(style == 0) { std::string name = horizontal?"IGPCRDIV.DEF":"OVBUTN2.DEF"; - CAnimation *animLeft = new CAnimation(name); + //NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...) + + //use source def to create custom animations. Format "name.def:123" will load this frame from def file + CAnimation *animLeft = new CAnimation(); + animLeft->setCustom(name + ":0", 0); + animLeft->setCustom(name + ":1", 1); left->setImage(animLeft); - left->setOffset(0); - CAnimation *animRight = new CAnimation(name); + CAnimation *animRight = new CAnimation(); + animRight->setCustom(name + ":2", 0); + animRight->setCustom(name + ":3", 1); right->setImage(animRight); - right->setOffset(2); - CAnimation *animSlider = new CAnimation(name); + CAnimation *animSlider = new CAnimation(); + animSlider->setCustom(name + ":4", 0); slider->setImage(animSlider); - slider->setOffset(4); } else { @@ -1512,7 +1517,7 @@ bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key) void CTextInput::filenameFilter(std::string & text, const std::string &) { - static const std::string forbiddenChars = "<>:\"/\\|?*"; //if we are entering a filename, some special characters won't be allowed + static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed size_t pos; while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos) text.erase(pos, 1); diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 1bb08d1a2..6e212e6e3 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -995,7 +995,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) } else if(ID == 6) //shipyard { - const TerrainTile *tile = getTile(t->bestLocation()); + const TerrainTile *tile = getTile(t->bestLocation(), false); if(!tile || tile->tertype != TerrainTile::water ) ret = EBuildingState::NO_WATER; //lack of water