| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264 | #include "StdInc.h"#include "CPreGame.h"#include "../lib/filesystem/Filesystem.h"#include "../lib/filesystem/CFileInfo.h"#include "../lib/filesystem/CCompressedStream.h"#include "../lib/CStopWatch.h"#include "gui/SDL_Extensions.h"#include "CGameInfo.h"#include "gui/CCursorHandler.h"#include "CDefHandler.h"#include "../lib/CGeneralTextHandler.h"#include "../lib/CTownHandler.h"#include "../lib/CHeroHandler.h"#include "../lib/mapping/CCampaignHandler.h"#include "../lib/CCreatureHandler.h"#include "../lib/JsonNode.h"#include "CMusicHandler.h"#include "CVideoHandler.h"#include "Graphics.h"#include "../lib/Connection.h"#include "../lib/VCMIDirs.h"#include "../lib/mapping/CMap.h"#include "windows/GUIClasses.h"#include "CPlayerInterface.h"#include "../CCallback.h"#include "CMessage.h"#include "../lib/CSpellHandler.h" /*for campaign bonuses*/#include "../lib/CArtHandler.h" /*for campaign bonuses*/#include "../lib/CBuildingHandler.h" /*for campaign bonuses*/#include "CBitmapHandler.h"#include "Client.h"#include "../lib/NetPacks.h"#include "../lib/registerTypes//RegisterTypes.h"#include "../lib/CThreadHelper.h"#include "../lib/CConfigHandler.h"#include "../lib/GameConstants.h"#include "gui/CGuiHandler.h"#include "gui/CAnimation.h"#include "widgets/CComponent.h"#include "widgets/Buttons.h"#include "widgets/MiscWidgets.h"#include "widgets/ObjectLists.h"#include "widgets/TextControls.h"#include "windows/InfoWindows.h"#include "../lib/mapping/CMapService.h"#include "../lib/mapping/CMap.h"#include "../lib/CRandomGenerator.h"/* * CPreGame.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */namespace fs = boost::filesystem;void startGame(StartInfo * options, CConnection *serv = nullptr);void endGame();CGPreGame * CGP = nullptr;ISelectionScreenInfo *SEL;static PlayerColor playerColor; //if more than one player - applies to the first/** * Stores the current name of the savegame. * * TODO better solution for auto-selection when saving already saved games. * -> CSelectionScreen should be divided into CLoadGameScreen, CSaveGameScreen,... * The name of the savegame can then be stored non-statically in CGameState and * passed separately to CSaveGameScreen. */static std::string saveGameName;struct EvilHlpStruct{	CConnection *serv;	StartInfo *sInfo;	void reset(bool strong = true)	{		if(strong)		{			vstd::clear_pointer(serv);			vstd::clear_pointer(sInfo);		}		else		{			serv = nullptr;			sInfo = nullptr;		}	}} startingInfo;static void do_quit(){	SDL_Event event;	event.quit.type = SDL_QUIT;	SDL_PushEvent(&event);}static CMapInfo *mapInfoFromGame(){	auto   ret = new CMapInfo();	ret->mapHeader = std::unique_ptr<CMapHeader>(new CMapHeader(*LOCPLINT->cb->getMapHeader()));	return ret;}static void setPlayersFromGame(){	playerColor = LOCPLINT->playerID;}static void swapPlayers(PlayerSettings &a, PlayerSettings &b){	std::swap(a.playerID, b.playerID);	std::swap(a.name, b.name);	if(a.playerID == 1)		playerColor = a.color;	else if(b.playerID == 1)		playerColor = b.color;}void setPlayer(PlayerSettings &pset, ui8 player, const std::map<ui8, std::string> &playerNames){	if(vstd::contains(playerNames, player))		pset.name = playerNames.find(player)->second;	else		pset.name = CGI->generaltexth->allTexts[468];//Computer	pset.playerID = player;	if(player == playerNames.begin()->first)		playerColor = pset.color;}void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader, const std::map<ui8, std::string> &playerNames){	sInfo.playerInfos.clear();	if(!mapHeader)	{		return;	}	sInfo.mapname = filename;	playerColor = PlayerColor::NEUTRAL;	auto namesIt = playerNames.cbegin();	for (int i = 0; i < mapHeader->players.size(); i++)	{		const PlayerInfo &pinfo = mapHeader->players[i];		//neither computer nor human can play - no player		if (!(pinfo.canHumanPlay || pinfo.canComputerPlay))			continue;		PlayerSettings &pset = sInfo.playerInfos[PlayerColor(i)];		pset.color = PlayerColor(i);		if(pinfo.canHumanPlay && namesIt != playerNames.cend())		{			setPlayer(pset, namesIt++->first, playerNames);		}		else		{			setPlayer(pset, 0, playerNames);			if(!pinfo.canHumanPlay)			{				pset.compOnly = true;			}		}		pset.castle = pinfo.defaultCastle();		pset.hero = pinfo.defaultHero();		if(pset.hero != PlayerSettings::RANDOM && pinfo.hasCustomMainHero())		{			pset.hero = pinfo.mainCustomHeroId;			pset.heroName = pinfo.mainCustomHeroName;			pset.heroPortrait = pinfo.mainCustomHeroPortrait;		}		pset.handicap = PlayerSettings::NO_HANDICAP;	}}template <typename T> class CApplyOnPG;class CBaseForPGApply{public:	virtual void applyOnPG(CSelectionScreen *selScr, void *pack) const =0;	virtual ~CBaseForPGApply(){};	template<typename U> static CBaseForPGApply *getApplier(const U * t=nullptr)	{		return new CApplyOnPG<U>;	}};template <typename T> class CApplyOnPG : public CBaseForPGApply{public:	void applyOnPG(CSelectionScreen *selScr, void *pack) const	{		T *ptr = static_cast<T*>(pack);		ptr->apply(selScr);	}};template <> class CApplyOnPG<CPack> : public CBaseForPGApply{public:	void applyOnPG(CSelectionScreen *selScr, void *pack) const	{			logGlobal->errorStream() << "Cannot apply on PG plain CPack!";			assert(0);	}};static CApplier<CBaseForPGApply> *applier = nullptr;static CPicture* createPicture(const JsonNode& config){	return new CPicture(config["name"].String(), config["x"].Float(), config["y"].Float());}CMenuScreen::CMenuScreen(const JsonNode& configNode):	config(configNode){	OBJ_CONSTRUCTION_CAPTURING_ALL;	background = new CPicture(config["background"].String());	if (config["scalable"].Bool())	{		if (background->bg->format->palette)			background->convertToScreenBPP();		background->scaleTo(Point(screen->w, screen->h));	}	pos = background->center();	for(const JsonNode& node : config["items"].Vector())		menuNameToEntry.push_back(node["name"].String());	for(const JsonNode& node : config["images"].Vector())		images.push_back(createPicture(node));	//Hardcoded entry	menuNameToEntry.push_back("credits");    tabs = new CTabbedInt(std::bind(&CMenuScreen::createTab, this, _1), CTabbedInt::DestroyFunc());	tabs->type |= REDRAW_PARENT;}CIntObject * CMenuScreen::createTab(size_t index){	if (config["items"].Vector().size() == index)		return new CreditsScreen();	return new CMenuEntry(this, config["items"].Vector()[index]);}void CMenuScreen::showAll(SDL_Surface * to){	CIntObject::showAll(to);	if (pos.h != to->h || pos.w != to->w)		CMessage::drawBorder(PlayerColor(1), to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);}void CMenuScreen::show(SDL_Surface * to){	if (!config["video"].isNull())		CCS->videoh->update(config["video"]["x"].Float() + pos.x, config["video"]["y"].Float() + pos.y, to, true, false);	CIntObject::show(to);}void CMenuScreen::activate(){	CCS->musich->playMusic("Music/MainMenu", true);	if (!config["video"].isNull())		CCS->videoh->open(config["video"]["name"].String());	CIntObject::activate();}void CMenuScreen::deactivate(){	if (!config["video"].isNull())		CCS->videoh->close();	CIntObject::deactivate();}void CMenuScreen::switchToTab(size_t index){	tabs->setActive(index);}//funciton for std::string -> std::function conversion for main menustatic std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::string> menuType, const std::string &string){	static const std::vector<std::string> commandType  = boost::assign::list_of		("to")("campaigns")("start")("load")("exit")("highscores");	static const std::vector<std::string> gameType = boost::assign::list_of		("single")("multi")("campaign")("tutorial");	std::list<std::string> commands;	boost::split(commands, string, boost::is_any_of("\t "));	if (!commands.empty())	{		size_t index = std::find(commandType.begin(), commandType.end(), commands.front()) - commandType.begin();		commands.pop_front();		if (index > 3 || !commands.empty())		{			switch (index)			{				break; case 0://to - switch to another tab, if such tab exists				{					size_t index2 = std::find(menuType.begin(), menuType.end(), commands.front()) - menuType.begin();					if ( index2 != menuType.size())                        return std::bind(&CMenuScreen::switchToTab, menu, index2);				}				break; case 1://open campaign selection window				{                    return std::bind(&CGPreGame::openCampaignScreen, CGP, commands.front());				}				break; case 2://start				{					switch (std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())					{                        case 0: return std::bind(&CGPreGame::openSel, CGP, CMenuScreen::newGame, CMenuScreen::SINGLE_PLAYER);						case 1: return &pushIntT<CMultiMode>;                        case 2: return std::bind(&CGPreGame::openSel, CGP, CMenuScreen::campaignList, CMenuScreen::SINGLE_PLAYER);						//TODO: start tutorial                        case 3: return std::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));					}				}				break; case 3://load				{					switch (std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())					{                        case 0: return std::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::SINGLE_PLAYER);                        case 1: return std::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::MULTI_HOT_SEAT);						//TODO: load campaign                        case 2: return std::bind(CInfoWindow::showInfoDialog, "This function is not implemented yet. Campaign saves can be loaded from \"Single Player\" menu", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));						//TODO: load tutorial                        case 3: return std::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));					}				}				break; case 4://exit				{                    return std::bind(CInfoWindow::showYesNoDialog, std::ref(CGI->generaltexth->allTexts[69]), (const std::vector<CComponent*>*)nullptr, do_quit, 0, false, PlayerColor(1));				}				break; case 5://highscores				{					//TODO: high scores                    return std::bind(CInfoWindow::showInfoDialog, "Sorry, high scores menu is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));				}			}		}	}    logGlobal->errorStream()<<"Failed to parse command: "<<string;	return std::function<void()>();}CButton* CMenuEntry::createButton(CMenuScreen* parent, const JsonNode& button){	std::function<void()> command = genCommand(parent, parent->menuNameToEntry, button["command"].String());	std::pair<std::string, std::string> help;	if (!button["help"].isNull() && button["help"].Float() > 0)		help = CGI->generaltexth->zelp[button["help"].Float()];	int posx = button["x"].Float();	if (posx < 0)		posx = pos.w + posx;	int posy = button["y"].Float();	if (posy < 0)		posy = pos.h + posy;	return new CButton(Point(posx, posy), button["name"].String(), help, command, button["hotkey"].Float());}CMenuEntry::CMenuEntry(CMenuScreen* parent, const JsonNode &config){	OBJ_CONSTRUCTION_CAPTURING_ALL;	type |= REDRAW_PARENT;	pos = parent->pos;	for(const JsonNode& node : config["images"].Vector())		images.push_back(createPicture(node));	for(const JsonNode& node : config["buttons"].Vector())	{		buttons.push_back(createButton(parent, node));		buttons.back()->hoverable = true;		buttons.back()->type |= REDRAW_PARENT;	}}CreditsScreen::CreditsScreen():    positionCounter(0){	addUsedEvents(LCLICK | RCLICK);	type |= REDRAW_PARENT;	OBJ_CONSTRUCTION_CAPTURING_ALL;	pos.w = CGP->menu->pos.w;	pos.h = CGP->menu->pos.h;	auto textFile = CResourceHandler::get()->load(ResourceID("DATA/CREDITS.TXT"))->readAll();	std::string text((char*)textFile.first.get(), textFile.second);	size_t firstQuote = text.find('\"')+1;	text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote );	credits = new CMultiLineLabel(Rect(pos.w - 350, 0, 350, 600), FONT_CREDITS, CENTER, Colors::WHITE, text);	credits->scrollTextTo(-600); // move all text below the screen}void CreditsScreen::show(SDL_Surface * to){	CIntObject::show(to);	positionCounter++;	if (positionCounter % 2 == 0)		credits->scrollTextBy(1);	//end of credits, close this screen	if (credits->textSize.y + 600 < positionCounter / 2)		clickRight(false, false);}void CreditsScreen::clickLeft(tribool down, bool previousState){	clickRight(down, previousState);}void CreditsScreen::clickRight(tribool down, bool previousState){	CTabbedInt* menu = dynamic_cast<CTabbedInt*>(parent);	assert(menu);	menu->setActive(0);}CGPreGameConfig & CGPreGameConfig::get(){	static CGPreGameConfig config;	return config;}const JsonNode & CGPreGameConfig::getConfig() const{	return config;}const JsonNode & CGPreGameConfig::getCampaigns() const{	return campaignSets;}CGPreGameConfig::CGPreGameConfig() :	campaignSets(JsonNode(ResourceID("config/campaignSets.json"))),	config(JsonNode(ResourceID("config/mainmenu.json"))){}CGPreGame::CGPreGame(){	pos.w = screen->w;	pos.h = screen->h;	GH.defActionsDef = 63;	CGP = this;	menu = new CMenuScreen(CGPreGameConfig::get().getConfig()["window"]);	loadGraphics();}CGPreGame::~CGPreGame(){	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);	disposeGraphics();	if(CGP == this)		CGP = nullptr;	if(GH.curInt == this)		GH.curInt = nullptr;}void CGPreGame::openSel(CMenuScreen::EState screenType, CMenuScreen::EMultiMode multi /*= CMenuScreen::SINGLE_PLAYER*/){	GH.pushInt(new CSelectionScreen(screenType, multi));}void CGPreGame::loadGraphics(){	OBJ_CONSTRUCTION_CAPTURING_ALL;	new CFilledTexture("DIBOXBCK", pos);	victory = CDefHandler::giveDef("SCNRVICT.DEF");	loss = CDefHandler::giveDef("SCNRLOSS.DEF");}void CGPreGame::disposeGraphics(){	delete victory;	delete loss;}void CGPreGame::update(){	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);	if(CGP != this) //don't update if you are not a main interface		return;	if (GH.listInt.empty())	{		GH.pushInt(this);		GH.pushInt(menu);		menu->switchToTab(0);	}	if(SEL)		SEL->update();	// Handles mouse and key input	GH.updateTime();	GH.handleEvents();	// check for null othervice crash on finishing a campaign	// /FIXME: find out why GH.listInt is empty to begin with	if (GH.topInt() != nullptr)		GH.topInt()->show(screen);	if (settings["general"]["showfps"].Bool())		GH.drawFPSCounter();}void CGPreGame::runLocked(std::function<void(IUpdateable * )> cb){	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);	cb(this);	}void CGPreGame::openCampaignScreen(std::string name){	if (vstd::contains(CGPreGameConfig::get().getCampaigns().Struct(), name))	{		GH.pushInt(new CCampaignScreen(CGPreGameConfig::get().getCampaigns()[name]));		return;	}	logGlobal->errorStream()<<"Unknown campaign set: "<<name;}CGPreGame *CGPreGame::create(){	if(!CGP)		CGP = new CGPreGame();	return CGP;}void CGPreGame::removeFromGui(){	//remove everything but main menu and background	GH.popInts(GH.listInt.size() - 2);	GH.popInt(GH.topInt()); //remove main menu	GH.popInt(GH.topInt()); //remove background}CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map<ui8, std::string> * Names /*= nullptr*/, const std::string & Address /*=""*/, const std::string & Port /*= ""*/)	: ISelectionScreenInfo(Names), serverHandlingThread(nullptr), mx(new boost::recursive_mutex),	  serv(nullptr), ongoingClosing(false), myNameID(255){	CGPreGame::create(); //we depend on its graphics	screenType = Type;	multiPlayer = MultiPlayer;	OBJ_CONSTRUCTION_CAPTURING_ALL;	bool network = (MultiPlayer == CMenuScreen::MULTI_NETWORK_GUEST || MultiPlayer == CMenuScreen::MULTI_NETWORK_HOST);	CServerHandler *sh = nullptr;	if(multiPlayer == CMenuScreen::MULTI_NETWORK_HOST)	{		sh = new CServerHandler;		sh->startServer();	}	IShowActivatable::type = BLOCK_ADV_HOTKEYS;	pos.w = 762;	pos.h = 584;	if(Type == CMenuScreen::saveGame)	{		bordered = false;		center(pos);	}	else if(Type == CMenuScreen::campaignList)	{		bordered = false;		bg = new CPicture("CamCust.bmp", 0, 0);		pos = bg->center();	}	else	{		bordered = true;		//load random background		const JsonVector & bgNames = CGPreGameConfig::get().getConfig()["game-select"].Vector();		bg = new CPicture(RandomGeneratorUtil::nextItem(bgNames, CRandomGenerator::getDefault())->String(), 0, 0);		pos = bg->center();	}	sInfo.difficulty = 1;	current = nullptr;	sInfo.mode = (Type == CMenuScreen::newGame ? StartInfo::NEW_GAME : StartInfo::LOAD_GAME);	sInfo.turnTime = 0;	curTab = nullptr;	card = new InfoCard(network); //right info card	if (screenType == CMenuScreen::campaignList)	{		opt = nullptr;	}	else	{		opt = new OptionsTab(); //scenario options tab		opt->recActions = DISPOSE;		randMapTab = new CRandomMapTab();        randMapTab->getMapInfoChanged() += std::bind(&CSelectionScreen::changeSelection, this, _1);		randMapTab->recActions = DISPOSE;	}    sel = new SelectionTab(screenType, std::bind(&CSelectionScreen::changeSelection, this, _1), multiPlayer); //scenario selection tab	sel->recActions = DISPOSE;	switch(screenType)	{	case CMenuScreen::newGame:		{			SDL_Color orange = {232, 184, 32, 0};			SDL_Color overlayColor = multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST ? orange : Colors::WHITE;			card->difficulty->addCallback(std::bind(&CSelectionScreen::difficultyChange, this, _1));			card->difficulty->setSelected(1);			CButton * select = new CButton(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, SDLK_s);			select->addCallback([&]()			{				toggleTab(sel);				changeSelection(sel->getSelectedMapInfo());			});			select->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL, overlayColor);			CButton *opts = new CButton(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CSelectionScreen::toggleTab, this, opt), SDLK_a);			opts->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL, overlayColor);			CButton * randomBtn = new CButton(Point(411, 105), "GSPBUTT.DEF", CGI->generaltexth->zelp[47], 0, SDLK_r);			randomBtn->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, overlayColor);			randomBtn->addCallback([&]()			{				toggleTab(randMapTab);				changeSelection(randMapTab->getMapInfo());			});			start  = new CButton(Point(411, 535), "SCNRBEG.DEF", CGI->generaltexth->zelp[103], std::bind(&CSelectionScreen::startScenario, this), SDLK_b);			if(network)			{				CButton *hideChat = new CButton(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&InfoCard::toggleChat, card), SDLK_h);				hideChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);				if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)				{					select->block(true);					opts->block(true);					randomBtn->block(true);					start->block(true);				}			}		}		break;	case CMenuScreen::loadGame:		sel->recActions = 255;		start  = new CButton(Point(411, 535), "SCNRLOD.DEF", CGI->generaltexth->zelp[103], std::bind(&CSelectionScreen::startScenario, this), SDLK_l);		break;	case CMenuScreen::saveGame:		sel->recActions = 255;		start  = new CButton(Point(411, 535), "SCNRSAV.DEF", CGI->generaltexth->zelp[103], std::bind(&CSelectionScreen::startScenario, this), SDLK_s);		break;	case CMenuScreen::campaignList:		sel->recActions = 255;		start  = new CButton(Point(411, 535), "SCNRLOD.DEF", CButton::tooltip(), std::bind(&CSelectionScreen::startCampaign, this), SDLK_b);		break;	}	start->assignedKeys.insert(SDLK_RETURN);	back = new CButton(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);	if(network)	{		if(multiPlayer == CMenuScreen::MULTI_NETWORK_HOST)		{			assert(playerNames.size() == 1  &&  vstd::contains(playerNames, 1)); //TODO hot-seat/network combo			serv = sh->connectToServer();			*serv << (ui8) 4;			myNameID = 1;		}		else		{			serv = CServerHandler::justConnectToServer(Address, Port);		}		serv->enterPregameConnectionMode();		*serv << playerNames.begin()->second;		if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)		{			const CMapInfo *map;			*serv >> myNameID >> map;			serv->connectionID = myNameID;			changeSelection(map);		}		else //host		{			if(current)			{				SelectMap sm(*current);				*serv << &sm;				UpdateStartOptions uso(sInfo);				*serv << &uso;			}		}		applier = new CApplier<CBaseForPGApply>;		registerTypesPregamePacks(*applier);		serverHandlingThread = new boost::thread(&CSelectionScreen::handleConnection, this);	}	delete sh;}CSelectionScreen::~CSelectionScreen(){	ongoingClosing = true;	if(serv)	{		assert(serverHandlingThread);		QuitMenuWithoutStarting qmws;		*serv << &qmws;// 		while(!serverHandlingThread->timed_join(boost::posix_time::milliseconds(50)))// 			processPacks();		serverHandlingThread->join();		delete serverHandlingThread;	}	playerColor = PlayerColor::CANNOT_DETERMINE;	playerNames.clear();	assert(!serv);	vstd::clear_pointer(applier);	delete mx;}void CSelectionScreen::toggleTab(CIntObject *tab){	if(multiPlayer == CMenuScreen::MULTI_NETWORK_HOST && serv)	{		PregameGuiAction pga;		if(tab == curTab)			pga.action = PregameGuiAction::NO_TAB;		else if(tab == opt)			pga.action = PregameGuiAction::OPEN_OPTIONS;		else if(tab == sel)			pga.action = PregameGuiAction::OPEN_SCENARIO_LIST;		else if(tab == randMapTab)			pga.action = PregameGuiAction::OPEN_RANDOM_MAP_OPTIONS;		*serv << &pga;	}	if(curTab && curTab->active)	{		curTab->deactivate();		curTab->recActions = DISPOSE;	}	if(curTab != tab)	{		tab->recActions = 255;		tab->activate();		curTab = tab;	}	else	{		curTab = nullptr;	};	GH.totalRedraw();}void CSelectionScreen::changeSelection(const CMapInfo * to){	if(current == to) return;	if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)	{		vstd::clear_pointer(current);	}	current = to;	if(to && (screenType == CMenuScreen::loadGame ||			  screenType == CMenuScreen::saveGame))	   SEL->sInfo.difficulty = to->scenarioOpts->difficulty;	if(screenType != CMenuScreen::campaignList)	{		updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader.get() : nullptr);		if(screenType == CMenuScreen::newGame)		{			if(to && to->isRandomMap)			{				sInfo.mapGenOptions = std::shared_ptr<CMapGenOptions>(new CMapGenOptions(randMapTab->getMapGenOptions()));			}			else			{				sInfo.mapGenOptions.reset();			}		}	}	card->changeSelection(to);	if(screenType != CMenuScreen::campaignList)	{		opt->recreate();	}	if(multiPlayer == CMenuScreen::MULTI_NETWORK_HOST && serv)	{		SelectMap sm(*to);		*serv << &sm;		UpdateStartOptions uso(sInfo);		*serv << &uso;	}}void CSelectionScreen::startCampaign(){	if (SEL->current)	{		GH.pushInt(new CBonusSelection(SEL->current->fileURI));	}}void CSelectionScreen::startScenario(){	if(screenType == CMenuScreen::newGame)	{		//there must be at least one human player before game can be started		std::map<PlayerColor, PlayerSettings>::const_iterator i;		for(i = SEL->sInfo.playerInfos.cbegin(); i != SEL->sInfo.playerInfos.cend(); i++)			if(i->second.playerID != PlayerSettings::PLAYER_AI)				break;		if(i == SEL->sInfo.playerInfos.cend())		{			GH.pushInt(CInfoWindow::create(CGI->generaltexth->allTexts[530])); //You must position yourself prior to starting the game.			return;		}	}	if(multiPlayer == CMenuScreen::MULTI_NETWORK_HOST)	{		start->block(true);		StartWithCurrentSettings swcs;		*serv << &swcs;		ongoingClosing = true;		return;	}	if(screenType != CMenuScreen::saveGame)	{		if(!current)			return;		if(sInfo.mapGenOptions)		{			//copy settings from interface to actual options. TODO: refactor, it used to have no effect at all -.-			sInfo.mapGenOptions = std::shared_ptr<CMapGenOptions>(new CMapGenOptions(randMapTab->getMapGenOptions()));			// Update player settings for RMG			for(const auto & psetPair : sInfo.playerInfos)			{				const auto & pset = psetPair.second;				sInfo.mapGenOptions->setStartingTownForPlayer(pset.color, pset.castle);				if(pset.playerID != PlayerSettings::PLAYER_AI)				{					sInfo.mapGenOptions->setPlayerTypeForStandardPlayer(pset.color, EPlayerType::HUMAN);				}			}			if(!sInfo.mapGenOptions->checkOptions())			{				GH.pushInt(CInfoWindow::create(CGI->generaltexth->allTexts[751]));				return;			}		}		saveGameName.clear();		if(screenType == CMenuScreen::loadGame)		{			saveGameName = sInfo.mapname;		}		auto   si = new StartInfo(sInfo);		CGP->removeFromGui();        CGP->showLoadingScreen(std::bind(&startGame, si, (CConnection *)nullptr));	}	else	{		if(!(sel && sel->txt && sel->txt->text.size()))			return;		saveGameName = "Saves/" + sel->txt->text;		CFunctionList<void()> overWrite;        overWrite += std::bind(&CCallback::save, LOCPLINT->cb.get(), saveGameName);        overWrite += std::bind(&CGuiHandler::popIntTotally, &GH, this);		if(CResourceHandler::get("local")->existsResource(ResourceID(saveGameName, EResType::CLIENT_SAVEGAME)))		{			std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?			boost::algorithm::replace_first(hlp, "%s", sel->txt->text);			LOCPLINT->showYesNoDialog(hlp, overWrite, 0, false);		}		else			overWrite();	}}void CSelectionScreen::difficultyChange( int to ){	assert(screenType == CMenuScreen::newGame);	sInfo.difficulty = to;	propagateOptions();	redraw();}void CSelectionScreen::handleConnection(){	setThreadName("CSelectionScreen::handleConnection");	try	{		assert(serv);		while(serv)		{			CPackForSelectionScreen *pack = nullptr;			*serv >> pack;            logNetwork->traceStream() << "Received a pack of type " << typeid(*pack).name();			assert(pack);			if(QuitMenuWithoutStarting *endingPack = dynamic_cast<QuitMenuWithoutStarting *>(pack))			{				endingPack->apply(this);			}			else if(StartWithCurrentSettings *endingPack = dynamic_cast<StartWithCurrentSettings *>(pack))			{				endingPack->apply(this);			}			else			{				boost::unique_lock<boost::recursive_mutex> lll(*mx);				upcomingPacks.push_back(pack);			}		}	} HANDLE_EXCEPTION	catch(int i)	{		if(i != 666)			throw;	}}void CSelectionScreen::setSInfo(const StartInfo &si){	std::map<PlayerColor, PlayerSettings>::const_iterator i;	for(i = si.playerInfos.cbegin(); i != si.playerInfos.cend(); i++)	{		if(i->second.playerID == myNameID)		{			playerColor = i->first;			break;		}	}	if(i == si.playerInfos.cend()) //not found		playerColor = PlayerColor::CANNOT_DETERMINE;	sInfo = si;	if(current)		opt->recreate(); //will force to recreate using current sInfo	card->difficulty->setSelected(si.difficulty);	GH.totalRedraw();}void CSelectionScreen::processPacks(){	boost::unique_lock<boost::recursive_mutex> lll(*mx);	while(!upcomingPacks.empty())	{		CPackForSelectionScreen *pack = upcomingPacks.front();		upcomingPacks.pop_front();		CBaseForPGApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier		apply->applyOnPG(this, pack);		delete pack;	}}void CSelectionScreen::update(){	if(serverHandlingThread)		processPacks();}void CSelectionScreen::propagateOptions(){	if(isHost() && serv)	{		UpdateStartOptions ups(sInfo);		*serv << &ups;	}}void CSelectionScreen::postRequest(ui8 what, ui8 dir){	if(!isGuest() || !serv)		return;	RequestOptionsChange roc(what, dir, myNameID);	*serv << &roc;}void CSelectionScreen::postChatMessage(const std::string &txt){	assert(serv);	ChatMessage cm;	cm.message = txt;	cm.playerName = sInfo.getPlayersSettings(myNameID)->name;	*serv << &cm;}void CSelectionScreen::propagateNames(){	PlayersNames pn;	pn.playerNames = playerNames;	*serv << &pn;}void CSelectionScreen::showAll(SDL_Surface *to){	CIntObject::showAll(to);	if (bordered && (pos.h != to->h || pos.w != to->w))		CMessage::drawBorder(PlayerColor(1), to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);}// A new size filter (Small, Medium, ...) has been selected. Populate// selMaps with the relevant data.void SelectionTab::filter( int size, bool selectFirst ){	curItems.clear();	if(tabType == CMenuScreen::campaignList)	{		for (auto & elem : allItems)			curItems.push_back(&elem);	}	else	{		for (auto & elem : allItems)			if( elem.mapHeader && elem.mapHeader->version  &&  (!size || elem.mapHeader->width == size))				curItems.push_back(&elem);	}	if(curItems.size())	{		slider->block(false);		slider->setAmount(curItems.size());		sort();		if(selectFirst)		{			slider->moveTo(0);			onSelect(curItems[0]);		}		selectAbs(0);	}	else	{		slider->block(true);		onSelect(nullptr);	}}std::unordered_set<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType){	boost::to_upper(dirURI);	std::unordered_set<ResourceID> ret = CResourceHandler::get()->getFilteredFiles([&](const ResourceID & ident)	{		return ident.getType() == resType			&& boost::algorithm::starts_with(ident.getName(), dirURI);	});	return ret;}void SelectionTab::parseMaps(const std::unordered_set<ResourceID> &files){	allItems.clear();	for(auto & file : files)	{		try		{			CMapInfo mapInfo;			mapInfo.mapInit(file.getName());			// ignore unsupported map versions (e.g. WoG maps without WoG			if (mapInfo.mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float())				allItems.push_back(std::move(mapInfo));		}		catch(std::exception & e)		{            logGlobal->errorStream() << "Map " << file.getName() << " is invalid. Message: " << e.what();		}	}}void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool multi){	for(auto & file : files)	{		try		{			CLoadFile lf(*CResourceHandler::get()->getResourceName(file), minSupportedVersion);			lf.checkMagicBytes(SAVEGAME_MAGIC);// 			ui8 sign[8];// 			lf >> sign;// 			if(std::memcmp(sign,"VCMISVG",7))// 			{// 				throw std::runtime_error("not a correct savefile!");// 			}			// Create the map info object			CMapInfo mapInfo;			mapInfo.mapHeader = make_unique<CMapHeader>();			mapInfo.scenarioOpts = new StartInfo;			lf >> *(mapInfo.mapHeader.get()) >> mapInfo.scenarioOpts;			mapInfo.fileURI = file.getName();			mapInfo.countPlayers();			std::time_t time = CFileInfo(*CResourceHandler::get()->getResourceName(file)).getDate();			mapInfo.date = std::asctime(std::localtime(&time));			// If multi mode then only multi games, otherwise single			if((mapInfo.actualHumanPlayers > 1) != multi)			{				mapInfo.mapHeader.reset();			}			allItems.push_back(std::move(mapInfo));		}		catch(const std::exception & e)		{            logGlobal->errorStream() << "Error: Failed to process " << file.getName() <<": " << e.what();		}	}}void SelectionTab::parseCampaigns(const std::unordered_set<ResourceID> &files ){	allItems.reserve(files.size());	for (auto & file : files)	{		CMapInfo info;		//allItems[i].date = std::asctime(std::localtime(&files[i].date));		info.fileURI = file.getName();		info.campaignInit();		allItems.push_back(std::move(info));	}}SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CMapInfo *)> &OnSelect, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/)	:bg(nullptr), onSelect(OnSelect){	OBJ_CONSTRUCTION;	selectionPos = 0;	addUsedEvents(LCLICK | WHEEL | KEYBOARD | DOUBLECLICK);	slider = nullptr;	txt = nullptr;	tabType = Type;	if (Type != CMenuScreen::campaignList)	{		bg = new CPicture("SCSELBCK.bmp", 0, 6);		pos = bg->pos;	}	else	{		bg = nullptr; //use background from parent		pos.w = parent->pos.w;		pos.h = parent->pos.h;		pos.x += 3; pos.y += 6;	}	if(MultiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)	{		positions = 18;	}	else	{		switch(tabType)		{		case CMenuScreen::newGame:			parseMaps(getFiles("Maps/", EResType::MAP));			positions = 18;			break;		case CMenuScreen::loadGame:		case CMenuScreen::saveGame:			parseGames(getFiles("Saves/", EResType::CLIENT_SAVEGAME), MultiPlayer);			if(tabType == CMenuScreen::loadGame)			{				positions = 18;			}			else			{				positions = 16;			}			if(tabType == CMenuScreen::saveGame)			{				txt = new CTextInput(Rect(32, 539, 350, 20), Point(-32, -25), "GSSTRIP.bmp", 0);				txt->filters.add(CTextInput::filenameFilter);			}			break;		case CMenuScreen::campaignList:			parseCampaigns(getFiles("Maps/", EResType::CAMPAIGN));			positions = 18;			break;		default:			assert(0);			break;		}	}	generalSortingBy = (tabType == CMenuScreen::loadGame || tabType == CMenuScreen::saveGame) ? _fileName : _name;	if (tabType != CMenuScreen::campaignList)	{		//size filter buttons		{			int sizes[] = {36, 72, 108, 144, 0};			const char * names[] = {"SCSMBUT.DEF", "SCMDBUT.DEF", "SCLGBUT.DEF", "SCXLBUT.DEF", "SCALBUT.DEF"};			for(int i = 0; i < 5; i++)				new CButton(Point(158 + 47*i, 46), names[i], CGI->generaltexth->zelp[54+i], std::bind(&SelectionTab::filter, this, sizes[i], true));		}		//sort buttons buttons		{			int xpos[] = {23, 55, 88, 121, 306, 339};			const char * names[] = {"SCBUTT1.DEF", "SCBUTT2.DEF", "SCBUTCP.DEF", "SCBUTT3.DEF", "SCBUTT4.DEF", "SCBUTT5.DEF"};			for(int i = 0; i < 6; i++)			{				ESortBy criteria = (ESortBy)i;				if(criteria == _name)					criteria = generalSortingBy;				new CButton(Point(xpos[i], 86), names[i], CGI->generaltexth->zelp[107+i], std::bind(&SelectionTab::sortBy, this, criteria));			}		}	}	else	{		//sort by buttons		new CButton(Point(23, 86), "CamCusM.DEF", CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _numOfMaps)); //by num of maps		new CButton(Point(55, 86), "CamCusL.DEF", CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _name)); //by name	}	slider = new CSlider(Point(372, 86), tabType != CMenuScreen::saveGame ? 480 : 430, std::bind(&SelectionTab::sliderMove, this, _1), positions, curItems.size(), 0, false, CSlider::BLUE);	slider->addUsedEvents(WHEEL);	format =  CDefHandler::giveDef("SCSELC.DEF");	sortingBy = _format;	ascending = true;	filter(0);	//select(0);	switch(tabType)	{	case CMenuScreen::newGame:		selectFName("Maps/Arrogance");		break;	case CMenuScreen::loadGame:	case CMenuScreen::campaignList:		select(0);		break;	case CMenuScreen::saveGame:;		if(saveGameName.empty())		{			txt->setText("NEWGAME");		}		else		{			selectFName(saveGameName);		}	}}SelectionTab::~SelectionTab(){	delete format;}void SelectionTab::sortBy( int criteria ){	if(criteria == sortingBy)	{		ascending = !ascending;	}	else	{		sortingBy = (ESortBy)criteria;		ascending = true;	}	sort();	selectAbs(0);}void SelectionTab::sort(){	if(sortingBy != generalSortingBy)		std::stable_sort(curItems.begin(), curItems.end(), mapSorter(generalSortingBy));	std::stable_sort(curItems.begin(), curItems.end(), mapSorter(sortingBy));	if(!ascending)		std::reverse(curItems.begin(), curItems.end());	redraw();}void SelectionTab::select( int position ){	if(!curItems.size()) return;	// New selection. py is the index in curItems.	int py = position + slider->getValue();	vstd::amax(py, 0);	vstd::amin(py, curItems.size()-1);	selectionPos = py;	if(position < 0)		slider->moveBy(position);	else if(position >= positions)		slider->moveBy(position - positions + 1);	if(txt)	{		std::string filename = *CResourceHandler::get("local")->getResourceName(								   ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));		txt->setText(CFileInfo(filename).getBaseName());	}	onSelect(curItems[py]);}void SelectionTab::selectAbs( int position ){	select(position - slider->getValue());}int SelectionTab::getPosition( int x, int y ){	return -1;}void SelectionTab::sliderMove( int slidPos ){	if(!slider) return; //ignore spurious call when slider is being created	redraw();}// Display the tab with the scenario names//// elemIdx is the index of the maps or saved game to display on line 0// slider->capacity contains the number of available screen lines// slider->positionsAmnt is the number of elements after filteringvoid SelectionTab::printMaps(SDL_Surface *to){	int elemIdx = slider->getValue();	// Display all elements if there's enough space	//if(slider->amount < slider->capacity)	//	elemIdx = 0;	SDL_Color itemColor;	for (int line = 0; line < positions  &&  elemIdx < curItems.size(); elemIdx++, line++)	{		CMapInfo *currentItem = curItems[elemIdx];		if (elemIdx == selectionPos)			itemColor=Colors::YELLOW;		else			itemColor=Colors::WHITE;		if(tabType != CMenuScreen::campaignList)		{			//amount of players			std::ostringstream ostr(std::ostringstream::out);			ostr << currentItem->playerAmnt << "/" << currentItem->humanPlayers;			printAtLoc(ostr.str(), 29, 120 + line * 25, FONT_SMALL, itemColor, to);			//map size			std::string temp2 = "C";			switch (currentItem->mapHeader->width)			{			case 36:				temp2="S";				break;			case 72:				temp2="M";				break;			case 108:				temp2="L";				break;			case 144:				temp2="XL";				break;			}			printAtMiddleLoc(temp2, 70, 128 + line * 25, FONT_SMALL, itemColor, to);			int temp=-1;			switch (currentItem->mapHeader->version)			{			case EMapFormat::ROE:				temp=0;				break;			case EMapFormat::AB:				temp=1;				break;			case EMapFormat::SOD:				temp=2;				break;			case EMapFormat::WOG:				temp=3;				break;			default:				// Unknown version. Be safe and ignore that map                logGlobal->warnStream() << "Warning: " << currentItem->fileURI << " has wrong version!";				continue;			}			blitAtLoc(format->ourImages[temp].bitmap, 88, 117 + line * 25, to);			//victory conditions			blitAtLoc(CGP->victory->ourImages[currentItem->mapHeader->victoryIconIndex].bitmap, 306, 117 + line * 25, to);			//loss conditions			blitAtLoc(CGP->loss->ourImages[currentItem->mapHeader->defeatIconIndex].bitmap, 339, 117 + line * 25, to);		}		else //if campaign		{			//number of maps in campaign			std::ostringstream ostr(std::ostringstream::out);			ostr << CGI->generaltexth->campaignRegionNames[ currentItem->campaignHeader->mapVersion ].size();			printAtLoc(ostr.str(), 29, 120 + line * 25, FONT_SMALL, itemColor, to);		}		std::string name;		if(tabType == CMenuScreen::newGame)		{			if (!currentItem->mapHeader->name.length())				currentItem->mapHeader->name = "Unnamed";			name = currentItem->mapHeader->name;		}		else if(tabType == CMenuScreen::campaignList)		{			name = currentItem->campaignHeader->name;		}		else		{			name = CFileInfo(*CResourceHandler::get("local")->getResourceName(								 ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();		}		//print name		printAtMiddleLoc(name, 213, 128 + line * 25, FONT_SMALL, itemColor, to);	}}void SelectionTab::showAll(SDL_Surface * to){	CIntObject::showAll(to);	printMaps(to);	std::string title;	switch(tabType) {	case CMenuScreen::newGame:		title = CGI->generaltexth->arraytxt[229];		break;	case CMenuScreen::loadGame:		title = CGI->generaltexth->arraytxt[230];		break;	case CMenuScreen::saveGame:		title = CGI->generaltexth->arraytxt[231];		break;	case CMenuScreen::campaignList:		title = CGI->generaltexth->allTexts[726];		break;	}	printAtMiddleLoc(title, 205, 28, FONT_MEDIUM, Colors::YELLOW, to); //Select a Scenario to Play	if(tabType != CMenuScreen::campaignList)	{		printAtMiddleLoc(CGI->generaltexth->allTexts[510], 87, 62, FONT_SMALL, Colors::YELLOW, to); //Map sizes	}}void SelectionTab::clickLeft( tribool down, bool previousState ){	if(down)	{		int line = getLine();		if(line != -1)			select(line);	}}void SelectionTab::keyPressed( const SDL_KeyboardEvent & key ){	if(key.state != SDL_PRESSED) return;	int moveBy = 0;	switch(key.keysym.sym)	{	case SDLK_UP:		moveBy = -1;		break;	case SDLK_DOWN:		moveBy = +1;		break;	case SDLK_PAGEUP:		moveBy = -positions+1;		break;	case SDLK_PAGEDOWN:		moveBy = +positions-1;		break;	case SDLK_HOME:		select(-slider->getValue());		return;	case SDLK_END:		select(curItems.size() - slider->getValue());		return;	default:		return;	}	select(selectionPos - slider->getValue() + moveBy);}void SelectionTab::onDoubleClick(){	if(getLine() != -1) //double clicked scenarios list	{		//act as if start button was pressed		(static_cast<CSelectionScreen*>(parent))->start->clickLeft(false, true);	}}int SelectionTab::getLine(){	int line = -1;	Point clickPos(GH.current->button.x, GH.current->button.y);	clickPos = clickPos - pos.topLeft();	if (clickPos.y > 115  &&  clickPos.y < 564  &&  clickPos.x > 22  &&  clickPos.x < 371)	{		line = (clickPos.y-115) / 25; //which line	}	return line;}void SelectionTab::selectFName( std::string fname ){	boost::to_upper(fname);	for(int i = curItems.size() - 1; i >= 0; i--)	{		if(curItems[i]->fileURI == fname)		{			slider->moveTo(i);			selectAbs(i);			return;		}	}	selectAbs(0);}const CMapInfo * SelectionTab::getSelectedMapInfo() const{	return curItems.empty() ? nullptr : curItems[selectionPos];}CRandomMapTab::CRandomMapTab(){	OBJ_CONSTRUCTION;	bg = new CPicture("RANMAPBK", 0, 6);	// Map Size	mapSizeBtnGroup = new CToggleGroup(0);	mapSizeBtnGroup->pos.y += 81;	mapSizeBtnGroup->pos.x += 158;	const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX");	addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198);	mapSizeBtnGroup->setSelected(1);	mapSizeBtnGroup->addCallback([&](int btnId)	{		const std::vector<int> mapSizeVal = boost::assign::list_of(CMapHeader::MAP_SIZE_SMALL)(CMapHeader::MAP_SIZE_MIDDLE)				(CMapHeader::MAP_SIZE_LARGE)(CMapHeader::MAP_SIZE_XLARGE);		mapGenOptions.setWidth(mapSizeVal[btnId]);		mapGenOptions.setHeight(mapSizeVal[btnId]);		updateMapInfo();	});	// Two levels	twoLevelsBtn = new CToggleButton(Point(346, 81), "RANUNDR", CGI->generaltexth->zelp[202]);	//twoLevelsBtn->select(true); for now, deactivated	twoLevelsBtn->addCallback([&](bool on) { mapGenOptions.setHasTwoLevels(on); updateMapInfo(); });	// Create number defs list	std::vector<std::string> numberDefs;	for(int i = 0; i <= 8; ++i)	{		numberDefs.push_back("RANNUM" + boost::lexical_cast<std::string>(i));	}	const int NUMBERS_WIDTH = 32;	const int BTNS_GROUP_LEFT_MARGIN = 67;	// Amount of players	playersCntGroup = new CToggleGroup(0);	playersCntGroup->pos.y += 153;	playersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);	playersCntGroup->addCallback([&](int btnId)	{		mapGenOptions.setPlayerCount(btnId);		deactivateButtonsFrom(teamsCntGroup, btnId);		deactivateButtonsFrom(compOnlyPlayersCntGroup, 8 - btnId + 1);		validatePlayersCnt(btnId);		updateMapInfo();	});	// Amount of teams	teamsCntGroup = new CToggleGroup(0);	teamsCntGroup->pos.y += 219;	teamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);	teamsCntGroup->addCallback([&](int btnId)	{		mapGenOptions.setTeamCount(btnId);		updateMapInfo();	});	// Computer only players	compOnlyPlayersCntGroup = new CToggleGroup(0);	compOnlyPlayersCntGroup->pos.y += 285;	compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);	compOnlyPlayersCntGroup->setSelected(0);	compOnlyPlayersCntGroup->addCallback([&](int btnId)	{		mapGenOptions.setCompOnlyPlayerCount(btnId);		deactivateButtonsFrom(compOnlyTeamsCntGroup, btnId);		validateCompOnlyPlayersCnt(btnId);		updateMapInfo();	});	// Computer only teams	compOnlyTeamsCntGroup = new CToggleGroup(0);	compOnlyTeamsCntGroup->pos.y += 351;	compOnlyTeamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);	deactivateButtonsFrom(compOnlyTeamsCntGroup, 0);	compOnlyTeamsCntGroup->addCallback([&](int btnId)	{		mapGenOptions.setCompOnlyTeamCount(btnId);		updateMapInfo();	});	const int WIDE_BTN_WIDTH = 85;	// Water content	waterContentGroup = new CToggleGroup(0);	waterContentGroup->pos.y += 419;	waterContentGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD");	addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);	waterContentGroup->addCallback([&](int btnId)	{		mapGenOptions.setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId));	});	// Monster strength	monsterStrengthGroup = new CToggleGroup(0);	monsterStrengthGroup->pos.y += 485;	monsterStrengthGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;	const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG");	addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);	monsterStrengthGroup->addCallback([&](int btnId)	{		if (btnId < 0)			mapGenOptions.setMonsterStrength(EMonsterStrength::RANDOM);		else			mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId + EMonsterStrength::GLOBAL_WEAK)); //value 2 to 4	});	// Show random maps btn	showRandMaps = new CButton(Point(54, 535), "RANSHOW", CGI->generaltexth->zelp[252]);	// Initialize map info object	updateMapInfo();}void CRandomMapTab::addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const{	addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex);	// Buttons are relative to button group, TODO better solution?	SObjectConstruction obj__i(group);	const std::string RANDOM_DEF = "RANRAND";	group->addToggle(CMapGenOptions::RANDOM_SIZE, new CToggleButton(Point(256, 0), RANDOM_DEF, CGI->generaltexth->zelp[helpRandIndex]));	group->setSelected(CMapGenOptions::RANDOM_SIZE);}void CRandomMapTab::addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const{	// Buttons are relative to button group, TODO better solution?	SObjectConstruction obj__i(group);	int cnt = nEnd - nStart + 1;	for(int i = 0; i < cnt; ++i)	{		auto button = new CToggleButton(Point(i * btnWidth, 0), defs[i + nStart], CGI->generaltexth->zelp[helpStartIndex + i]);		// For blocked state we should use pressed image actually		button->setImageOrder(0, 1, 1, 3);		group->addToggle(i + nStart, button);	}}void CRandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId){	logGlobal->infoStream() << "Blocking buttons from " << startId;	for(auto toggle : group->buttons)	{		if (auto button = dynamic_cast<CToggleButton*>(toggle.second))		{			if(startId == CMapGenOptions::RANDOM_SIZE || toggle.first < startId)			{				button->block(false);			}			else			{				button->block(true);			}		}	}}void CRandomMapTab::validatePlayersCnt(int playersCnt){	if(playersCnt == CMapGenOptions::RANDOM_SIZE)	{		return;	}	if(mapGenOptions.getTeamCount() >= playersCnt)	{		mapGenOptions.setTeamCount(playersCnt - 1);		teamsCntGroup->setSelected(mapGenOptions.getTeamCount());	}	if(mapGenOptions.getCompOnlyPlayerCount() > 8 - playersCnt)	{		mapGenOptions.setCompOnlyPlayerCount(8 - playersCnt);		compOnlyPlayersCntGroup->setSelected(mapGenOptions.getCompOnlyPlayerCount());	}	validateCompOnlyPlayersCnt(mapGenOptions.getCompOnlyPlayerCount());}void CRandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt){	if(compOnlyPlayersCnt == CMapGenOptions::RANDOM_SIZE)	{		return;	}	if(mapGenOptions.getCompOnlyTeamCount() >= compOnlyPlayersCnt)	{		mapGenOptions.setCompOnlyTeamCount(compOnlyPlayersCnt - 1);		compOnlyTeamsCntGroup->setSelected(mapGenOptions.getCompOnlyTeamCount());	}}void CRandomMapTab::showAll(SDL_Surface * to){	CIntObject::showAll(to);	// Headline	printAtMiddleLoc(CGI->generaltexth->allTexts[738], 222, 36, FONT_BIG, Colors::YELLOW, to);	printAtMiddleLoc(CGI->generaltexth->allTexts[739], 222, 56, FONT_SMALL, Colors::WHITE, to);	// Map size	printAtMiddleLoc(CGI->generaltexth->allTexts[752], 104, 97, FONT_SMALL, Colors::WHITE, to);	// Players cnt	printAtLoc(CGI->generaltexth->allTexts[753], 68, 133, FONT_SMALL, Colors::WHITE, to);	// Teams cnt	printAtLoc(CGI->generaltexth->allTexts[754], 68, 199, FONT_SMALL, Colors::WHITE, to);	// Computer only players cnt	printAtLoc(CGI->generaltexth->allTexts[755], 68, 265, FONT_SMALL, Colors::WHITE, to);	// Computer only teams cnt	printAtLoc(CGI->generaltexth->allTexts[756], 68, 331, FONT_SMALL, Colors::WHITE, to);	// Water content	printAtLoc(CGI->generaltexth->allTexts[757], 68, 398, FONT_SMALL, Colors::WHITE, to);	// Monster strength	printAtLoc(CGI->generaltexth->allTexts[758], 68, 465, FONT_SMALL, Colors::WHITE, to);}void CRandomMapTab::updateMapInfo(){	// Generate header info	mapInfo = make_unique<CMapInfo>();	mapInfo->isRandomMap = true;	mapInfo->mapHeader = make_unique<CMapHeader>();	mapInfo->mapHeader->version = EMapFormat::SOD;	mapInfo->mapHeader->name = CGI->generaltexth->allTexts[740];	mapInfo->mapHeader->description = CGI->generaltexth->allTexts[741];	mapInfo->mapHeader->difficulty = 1; // Normal	mapInfo->mapHeader->height = mapGenOptions.getHeight();	mapInfo->mapHeader->width = mapGenOptions.getWidth();	mapInfo->mapHeader->twoLevel = mapGenOptions.getHasTwoLevels();	// Generate player information	mapInfo->mapHeader->players.clear();	int playersToGen = (mapGenOptions.getPlayerCount() == CMapGenOptions::RANDOM_SIZE		|| mapGenOptions.getCompOnlyPlayerCount() == CMapGenOptions::RANDOM_SIZE)			? 8 : mapGenOptions.getPlayerCount() + mapGenOptions.getCompOnlyPlayerCount();	mapInfo->mapHeader->howManyTeams = playersToGen;	for(int i = 0; i < playersToGen; ++i)	{		PlayerInfo player;		player.isFactionRandom = true;		player.canComputerPlay = true;		if(i >= mapGenOptions.getPlayerCount() && mapGenOptions.getPlayerCount() != CMapGenOptions::RANDOM_SIZE)		{			player.canHumanPlay = false;		}		else		{			player.canHumanPlay = true;		}		player.team = TeamID(i);		player.hasMainTown = true;		player.generateHeroAtMainTown = true;		mapInfo->mapHeader->players.push_back(player);	}	mapInfoChanged(mapInfo.get());}CFunctionList<void(const CMapInfo *)> & CRandomMapTab::getMapInfoChanged(){	return mapInfoChanged;}const CMapInfo * CRandomMapTab::getMapInfo() const{	return mapInfo.get();}const CMapGenOptions & CRandomMapTab::getMapGenOptions() const{	return mapGenOptions;}CChatBox::CChatBox(const Rect &rect){	OBJ_CONSTRUCTION;	pos += rect;	addUsedEvents(KEYBOARD | TEXTINPUT);	captureAllKeys = true;	const int height = graphics->fonts[FONT_SMALL]->getLineHeight();	inputBox = new CTextInput(Rect(0, rect.h - height, rect.w, height));	inputBox->removeUsedEvents(KEYBOARD);	chatHistory = new CTextBox("", Rect(0, 0, rect.w, rect.h - height), 1);	chatHistory->label->color = Colors::GREEN;}void CChatBox::keyPressed(const SDL_KeyboardEvent & key){	if(key.keysym.sym == SDLK_RETURN  &&  key.state == SDL_PRESSED  &&  inputBox->text.size())	{		SEL->postChatMessage(inputBox->text);		inputBox->setText("");	}	else		inputBox->keyPressed(key);}void CChatBox::addNewMessage(const std::string &text){	chatHistory->setText(chatHistory->label->text + text + "\n");	if(chatHistory->slider)		chatHistory->slider->moveToMax();}InfoCard::InfoCard( bool Network )  : bg(nullptr), network(Network), chatOn(false), chat(nullptr), playerListBg(nullptr),	difficulty(nullptr), sizes(nullptr), sFlags(nullptr){	OBJ_CONSTRUCTION_CAPTURING_ALL;	CIntObject::type |= REDRAW_PARENT;	pos.x += 393;	pos.y += 6;	addUsedEvents(RCLICK);	mapDescription = nullptr;	Rect descriptionRect(26, 149, 320, 115);	mapDescription = new CTextBox("", descriptionRect, 1);	if(SEL->screenType == CMenuScreen::campaignList)	{		CSelectionScreen *ss = static_cast<CSelectionScreen*>(parent);		mapDescription->addChild(new CPicture(*ss->bg, descriptionRect + Point(-393, 0)), true); //move subpicture bg to our description control (by default it's our (Infocard) child)	}	else	{		bg = new CPicture("GSELPOP1.bmp", 0, 0);		parent->addChild(bg);		auto it = vstd::find(parent->children, this); //our position among parent children		parent->children.insert(it, bg); //put BG before us		parent->children.pop_back();		pos.w = bg->pos.w;		pos.h = bg->pos.h;		sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");		sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");		difficulty = new CToggleGroup(0);		{			static const char *difButns[] = {"GSPBUT3.DEF", "GSPBUT4.DEF", "GSPBUT5.DEF", "GSPBUT6.DEF", "GSPBUT7.DEF"};			for(int i = 0; i < 5; i++)			{				auto button = new CToggleButton(Point(110 + i*32, 450), difButns[i], CGI->generaltexth->zelp[24+i]);				difficulty->addToggle(i, button);				if(SEL->screenType != CMenuScreen::newGame)					button->block(true);			}		}		if(network)		{			playerListBg = new CPicture("CHATPLUG.bmp", 16, 276);			chat = new CChatBox(descriptionRect);			chat->chatHistory->addChild(new CPicture(*bg, chat->chatHistory->pos - pos), true); //move subpicture bg to our description control (by default it's our (Infocard) child)			chatOn = true;			mapDescription->disable();		}	}}InfoCard::~InfoCard(){	delete sizes;	delete sFlags;}void InfoCard::showAll(SDL_Surface * to){	CIntObject::showAll(to);	//blit texts	if(SEL->screenType != CMenuScreen::campaignList)	{		printAtLoc(CGI->generaltexth->allTexts[390] + ":", 24, 400, FONT_SMALL, Colors::WHITE, to); //Allies		printAtLoc(CGI->generaltexth->allTexts[391] + ":", 190, 400, FONT_SMALL, Colors::WHITE, to); //Enemies		printAtLoc(CGI->generaltexth->allTexts[494], 33, 430, FONT_SMALL, Colors::YELLOW, to);//"Map Diff:"		printAtLoc(CGI->generaltexth->allTexts[492] + ":", 133,430, FONT_SMALL, Colors::YELLOW, to); //player difficulty		printAtLoc(CGI->generaltexth->allTexts[218] + ":", 290,430, FONT_SMALL, Colors::YELLOW, to); //"Rating:"		printAtLoc(CGI->generaltexth->allTexts[495], 26, 22, FONT_SMALL, Colors::YELLOW, to); //Scenario Name:		if(!chatOn)		{			printAtLoc(CGI->generaltexth->allTexts[496], 26, 132, FONT_SMALL, Colors::YELLOW, to); //Scenario Description:			printAtLoc(CGI->generaltexth->allTexts[497], 26, 283, FONT_SMALL, Colors::YELLOW, to); //Victory Condition:			printAtLoc(CGI->generaltexth->allTexts[498], 26, 339, FONT_SMALL, Colors::YELLOW, to); //Loss Condition:		}		else //players list		{			std::map<ui8, std::string> playerNames = SEL->playerNames;			int playerSoFar = 0;			for (auto i = SEL->sInfo.playerInfos.cbegin(); i != SEL->sInfo.playerInfos.cend(); i++)			{				if(i->second.playerID != PlayerSettings::PLAYER_AI)				{					printAtLoc(i->second.name, 24, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->getLineHeight(), FONT_SMALL, Colors::WHITE, to);					playerNames.erase(i->second.playerID);				}			}			playerSoFar = 0;			for (auto i = playerNames.cbegin(); i != playerNames.cend(); i++)			{				printAtLoc(i->second, 193, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->getLineHeight(), FONT_SMALL, Colors::WHITE, to);			}		}	}	if(SEL->current)	{		if(SEL->screenType != CMenuScreen::campaignList)		{			int temp = -1;			if(!chatOn)			{				CDefHandler * loss    = CGP ? CGP->loss    : CDefHandler::giveDef("SCNRLOSS.DEF");				CDefHandler * victory = CGP ? CGP->victory : CDefHandler::giveDef("SCNRVICT.DEF");				CMapHeader * header = SEL->current->mapHeader.get();				//victory conditions				printAtLoc(header->victoryMessage, 60, 307, FONT_SMALL, Colors::WHITE, to);				blitAtLoc(victory->ourImages[header->victoryIconIndex].bitmap, 24, 302, to); //victory cond descr				//loss conditoins				printAtLoc(header->defeatMessage, 60, 366, FONT_SMALL, Colors::WHITE, to);				blitAtLoc(loss->ourImages[header->defeatIconIndex].bitmap, 24, 359, to); //loss cond				if (!CGP)				{					delete loss;					delete victory;				}			}			//difficulty			assert(SEL->current->mapHeader->difficulty <= 4);			std::string &diff = CGI->generaltexth->arraytxt[142 + SEL->current->mapHeader->difficulty];			printAtMiddleLoc(diff, 62, 472, FONT_SMALL, Colors::WHITE, to);			//selecting size icon			switch (SEL->current->mapHeader->width)			{			case 36:				temp=0;				break;			case 72:				temp=1;				break;			case 108:				temp=2;				break;			case 144:				temp=3;				break;			default:				temp=4;				break;			}			blitAtLoc(sizes->ourImages[temp].bitmap, 318, 22, to);			if(SEL->screenType == CMenuScreen::loadGame)				printToLoc((static_cast<const CMapInfo*>(SEL->current))->date,308,34, FONT_SMALL, Colors::WHITE, to);			//print flags			int fx = 34  + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[390]);			int ex = 200 + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[391]);			TeamID myT;			if(playerColor < PlayerColor::PLAYER_LIMIT)				myT = SEL->current->mapHeader->players[playerColor.getNum()].team;			else				myT = TeamID::NO_TEAM;			for (auto i = SEL->sInfo.playerInfos.cbegin(); i != SEL->sInfo.playerInfos.cend(); i++)			{				int *myx = ((i->first == playerColor  ||  SEL->current->mapHeader->players[i->first.getNum()].team == myT) ? &fx : &ex);				blitAtLoc(sFlags->ourImages[i->first.getNum()].bitmap, *myx, 399, to);				*myx += sFlags->ourImages[i->first.getNum()].bitmap->w;			}			std::string tob;			switch (SEL->sInfo.difficulty)			{			case 0:				tob="80%";				break;			case 1:				tob="100%";				break;			case 2:				tob="130%";				break;			case 3:				tob="160%";				break;			case 4:				tob="200%";				break;			}			printAtMiddleLoc(tob, 311, 472, FONT_SMALL, Colors::WHITE, to);		}		//blit description		std::string name;		if (SEL->screenType == CMenuScreen::campaignList)		{			name = SEL->current->campaignHeader->name;		}		else		{			name = SEL->current->mapHeader->name;		}		//name		if (name.length())			printAtLoc(name, 26, 39, FONT_BIG, Colors::YELLOW, to);		else			printAtLoc("Unnamed", 26, 39, FONT_BIG, Colors::YELLOW, to);	}}void InfoCard::changeSelection( const CMapInfo *to ){	if(to && mapDescription)	{		if (SEL->screenType == CMenuScreen::campaignList)			mapDescription->setText(to->campaignHeader->description);		else			mapDescription->setText(to->mapHeader->description);		if(SEL->screenType != CMenuScreen::newGame && SEL->screenType != CMenuScreen::campaignList) {			//difficulty->block(true);			difficulty->setSelected(SEL->sInfo.difficulty);		}	}	redraw();}void InfoCard::clickRight( tribool down, bool previousState ){	static const Rect flagArea(19, 397, 335, 23);	if(down && SEL->current && isItInLoc(flagArea, GH.current->motion.x, GH.current->motion.y))		showTeamsPopup();}void InfoCard::showTeamsPopup(){	SDL_Surface *bmp = CMessage::drawDialogBox(256, 90 + 50 * SEL->current->mapHeader->howManyTeams);	graphics->fonts[FONT_MEDIUM]->renderTextCenter(bmp, CGI->generaltexth->allTexts[657], Colors::YELLOW, Point(128, 30));	for(int i = 0; i < SEL->current->mapHeader->howManyTeams; i++)	{		std::vector<ui8> flags;		std::string hlp = CGI->generaltexth->allTexts[656]; //Team %d		hlp.replace(hlp.find("%d"), 2, boost::lexical_cast<std::string>(i+1));		graphics->fonts[FONT_SMALL]->renderTextCenter(bmp, hlp, Colors::WHITE, Point(128, 65 + 50 * i));		for(int j = 0; j < PlayerColor::PLAYER_LIMIT_I; j++)			if((SEL->current->mapHeader->players[j].canHumanPlay || SEL->current->mapHeader->players[j].canComputerPlay)				&& SEL->current->mapHeader->players[j].team == TeamID(i))				flags.push_back(j);		int curx = 128 - 9*flags.size();		for(auto & flag : flags)		{			blitAt(sFlags->ourImages[flag].bitmap, curx, 75 + 50*i, bmp);			curx += 18;		}	}	GH.pushInt(new CInfoPopup(bmp, true));}void InfoCard::toggleChat(){	setChat(!chatOn);}void InfoCard::setChat(bool activateChat){	if(chatOn == activateChat)		return;	assert(active);	if(activateChat)	{		mapDescription->disable();		chat->enable();		playerListBg->enable();	}	else	{		mapDescription->enable();		chat->disable();		playerListBg->disable();	}	chatOn = activateChat;	GH.totalRedraw();}OptionsTab::OptionsTab():	turnDuration(nullptr){	OBJ_CONSTRUCTION;	bg = new CPicture("ADVOPTBK", 0, 6);	pos = bg->pos;	if(SEL->screenType == CMenuScreen::newGame)		turnDuration = new CSlider(Point(55, 551), 194, std::bind(&OptionsTab::setTurnLength, this, _1), 1, 11, 11, true, CSlider::BLUE);}OptionsTab::~OptionsTab(){}void OptionsTab::showAll(SDL_Surface * to){	CIntObject::showAll(to);	printAtMiddleLoc(CGI->generaltexth->allTexts[515], 222, 30, FONT_BIG, Colors::YELLOW, to);	printAtMiddleWBLoc(CGI->generaltexth->allTexts[516], 222, 68, FONT_SMALL, 300, Colors::WHITE, to); //Select starting options, handicap, and name for each player in the game.	printAtMiddleWBLoc(CGI->generaltexth->allTexts[517], 107, 110, FONT_SMALL, 100, Colors::YELLOW, to); //Player Name Handicap Type	printAtMiddleWBLoc(CGI->generaltexth->allTexts[518], 197, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Town	printAtMiddleWBLoc(CGI->generaltexth->allTexts[519], 273, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Hero	printAtMiddleWBLoc(CGI->generaltexth->allTexts[520], 349, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Bonus	printAtMiddleLoc(CGI->generaltexth->allTexts[521], 222, 538, FONT_SMALL, Colors::YELLOW, to); // Player Turn Duration	if (turnDuration)		printAtMiddleLoc(CGI->generaltexth->turnDurations[turnDuration->getValue()], 319,559, FONT_SMALL, Colors::WHITE, to);//Turn duration value}void OptionsTab::nextCastle( PlayerColor player, int dir ){	if(SEL->isGuest())	{		SEL->postRequest(RequestOptionsChange::TOWN, dir);		return;	}	PlayerSettings &s = SEL->sInfo.playerInfos[player];	si16 &cur = s.castle;	auto & allowed = SEL->current->mapHeader->players[s.color.getNum()].allowedFactions;	if (cur == PlayerSettings::NONE) //no change		return;	if (cur == PlayerSettings::RANDOM) //first/last available	{		if (dir > 0)			cur = *allowed.begin(); //id of first town		else			cur = *allowed.rbegin(); //id of last town	}	else // next/previous available	{		if ( (cur == *allowed.begin() && dir < 0 )		  || (cur == *allowed.rbegin() && dir > 0) )			cur = -1;		else		{			assert(dir >= -1 && dir <= 1); //othervice std::advance may go out of range			auto iter = allowed.find(cur);			std::advance(iter, dir);			cur = *iter;		}	}	if(s.hero >= 0 && !SEL->current->mapHeader->players[s.color.getNum()].hasCustomMainHero()) // remove hero unless it set to fixed one in map editor		s.hero =  PlayerSettings::RANDOM;	if(cur < 0  &&  s.bonus == PlayerSettings::RESOURCE)		s.bonus = PlayerSettings::RANDOM;	entries[player]->selectButtons();	SEL->propagateOptions();	entries[player]->update();	redraw();}void OptionsTab::nextHero( PlayerColor player, int dir ){	if(SEL->isGuest())	{		SEL->postRequest(RequestOptionsChange::HERO, dir);		return;	}	PlayerSettings &s = SEL->sInfo.playerInfos[player];	int old = s.hero;	if (s.castle < 0  ||  s.playerID == PlayerSettings::PLAYER_AI  ||  s.hero == PlayerSettings::NONE)		return;	if (s.hero == PlayerSettings::RANDOM) // first/last available	{		int max = CGI->heroh->heroes.size(),			min = 0;		s.hero = nextAllowedHero(player, min,max,0,dir);	}	else	{		if(dir > 0)			s.hero = nextAllowedHero(player, s.hero, CGI->heroh->heroes.size(), 1, dir);		else			s.hero = nextAllowedHero(player,  0, s.hero, 1, dir);	}	if(old != s.hero)	{		usedHeroes.erase(old);		usedHeroes.insert(s.hero);		entries[player]->update();		redraw();	}	SEL->propagateOptions();}int OptionsTab::nextAllowedHero( PlayerColor player, int min, int max, int incl, int dir ){	if(dir>0)	{		for(int i=min+incl; i<=max-incl; i++)			if(canUseThisHero(player, i))				return i;	}	else	{		for(int i=max-incl; i>=min+incl; i--)			if(canUseThisHero(player, i))				return i;	}	return -1;}bool OptionsTab::canUseThisHero( PlayerColor player, int ID ){	return CGI->heroh->heroes.size() > ID	        && SEL->sInfo.playerInfos[player].castle == CGI->heroh->heroes[ID]->heroClass->faction	        && !vstd::contains(usedHeroes, ID)	        && SEL->current->mapHeader->allowedHeroes[ID];}void OptionsTab::nextBonus( PlayerColor player, int dir ){	if(SEL->isGuest())	{		SEL->postRequest(RequestOptionsChange::BONUS, dir);		return;	}	PlayerSettings &s = SEL->sInfo.playerInfos[player];	PlayerSettings::Ebonus &ret = s.bonus = static_cast<PlayerSettings::Ebonus>(static_cast<int>(s.bonus) + dir);	if (s.hero==PlayerSettings::NONE &&		!SEL->current->mapHeader->players[s.color.getNum()].heroesNames.size() &&		ret==PlayerSettings::ARTIFACT) //no hero - can't be artifact	{		if (dir<0)			ret=PlayerSettings::RANDOM;		else ret=PlayerSettings::GOLD;	}	if(ret > PlayerSettings::RESOURCE)		ret = PlayerSettings::RANDOM;	if(ret < PlayerSettings::RANDOM)		ret = PlayerSettings::RESOURCE;	if (s.castle==PlayerSettings::RANDOM && ret==PlayerSettings::RESOURCE) //random castle - can't be resource	{		if (dir<0)			ret=PlayerSettings::GOLD;		else ret=PlayerSettings::RANDOM;	}	SEL->propagateOptions();	entries[player]->update();	redraw();}void OptionsTab::recreate(){	for(auto & elem : entries)	{		delete elem.second;	}	entries.clear();	usedHeroes.clear();	OBJ_CONSTRUCTION_CAPTURING_ALL;	for(auto it = SEL->sInfo.playerInfos.begin(); it != SEL->sInfo.playerInfos.end(); ++it)	{		entries.insert(std::make_pair(it->first, new PlayerOptionsEntry(this, it->second)));		const std::vector<SHeroName> &heroes = SEL->current->mapHeader->players[it->first.getNum()].heroesNames;		for(auto & heroe : heroes)			usedHeroes.insert(heroe.heroId);	}}void OptionsTab::setTurnLength( int npos ){	static const int times[] = {1, 2, 4, 6, 8, 10, 15, 20, 25, 30, 0};	vstd::amin(npos, ARRAY_COUNT(times) - 1);	SEL->sInfo.turnTime = times[npos];	redraw();}void OptionsTab::flagPressed( PlayerColor color ){	PlayerSettings &clicked =  SEL->sInfo.playerInfos[color];	PlayerSettings *old = nullptr;	if(SEL->playerNames.size() == 1) //single player -> just swap	{		if(color == playerColor) //that color is already selected, no action needed			return;		old = &SEL->sInfo.playerInfos[playerColor];		swapPlayers(*old, clicked);	}	else	{		//identify clicked player		int clickedNameID = clicked.playerID; //human is a number of player, zero means AI		if(clickedNameID > 0  &&  playerToRestore.id == clickedNameID) //player to restore is about to being replaced -> put him back to the old place		{			PlayerSettings &restPos = SEL->sInfo.playerInfos[playerToRestore.color];			SEL->setPlayer(restPos, playerToRestore.id);			playerToRestore.reset();		}		int newPlayer; //which player will take clicked position		//who will be put here?		if(!clickedNameID) //AI player clicked -> if possible replace computer with unallocated player		{			newPlayer = SEL->getIdOfFirstUnallocatedPlayer();			if(!newPlayer) //no "free" player -> get just first one				newPlayer = SEL->playerNames.begin()->first;		}		else //human clicked -> take next		{			auto i = SEL->playerNames.find(clickedNameID); //clicked one			i++; //player AFTER clicked one			if(i != SEL->playerNames.end())				newPlayer = i->first;			else				newPlayer = 0; //AI if we scrolled through all players		}		SEL->setPlayer(clicked, newPlayer); //put player		//if that player was somewhere else, we need to replace him with computer		if(newPlayer) //not AI		{			for(auto i = SEL->sInfo.playerInfos.begin(); i != SEL->sInfo.playerInfos.end(); i++)			{				int curNameID = i->second.playerID;				if(i->first != color  &&  curNameID == newPlayer)				{					assert(i->second.playerID);					playerToRestore.color = i->first;					playerToRestore.id = newPlayer;					SEL->setPlayer(i->second, 0); //set computer					old = &i->second;					break;				}			}		}	}	entries[clicked.color]->selectButtons();	if(old)	{		entries[old->color]->selectButtons();		if(old->hero >= 0)			usedHeroes.erase(old->hero);		old->hero = entries[old->color]->pi.defaultHero();	}	SEL->propagateOptions();	GH.totalRedraw();}OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSettings &S) : pi(SEL->current->mapHeader->players[S.color.getNum()]), s(S){	OBJ_CONSTRUCTION;	defActions |= SHARE_POS;	int serial = 0;	for(int g=0; g < s.color.getNum(); ++g)	{		PlayerInfo &itred = SEL->current->mapHeader->players[g];		if( itred.canComputerPlay || itred.canHumanPlay)			serial++;	}	pos.x += 54;	pos.y += 122 + serial*50;	static const char *flags[] = {"AOFLGBR.DEF", "AOFLGBB.DEF", "AOFLGBY.DEF", "AOFLGBG.DEF",		"AOFLGBO.DEF", "AOFLGBP.DEF", "AOFLGBT.DEF", "AOFLGBS.DEF"};	static const char *bgs[] = {"ADOPRPNL.bmp", "ADOPBPNL.bmp", "ADOPYPNL.bmp", "ADOPGPNL.bmp",		"ADOPOPNL.bmp", "ADOPPPNL.bmp", "ADOPTPNL.bmp", "ADOPSPNL.bmp"};	bg = new CPicture(BitmapHandler::loadBitmap(bgs[s.color.getNum()]), 0, 0, true);	if(SEL->screenType == CMenuScreen::newGame)	{		btns[0] = new CButton(Point(107, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[132], std::bind(&OptionsTab::nextCastle, owner, s.color, -1));		btns[1] = new CButton(Point(168, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[133], std::bind(&OptionsTab::nextCastle, owner, s.color, +1));		btns[2] = new CButton(Point(183, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[148], std::bind(&OptionsTab::nextHero, owner, s.color, -1));		btns[3] = new CButton(Point(244, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[149], std::bind(&OptionsTab::nextHero, owner, s.color, +1));		btns[4] = new CButton(Point(259, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[164], std::bind(&OptionsTab::nextBonus, owner, s.color, -1));		btns[5] = new CButton(Point(320, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[165], std::bind(&OptionsTab::nextBonus, owner, s.color, +1));	}	else		for(auto & elem : btns)			elem = nullptr;	selectButtons();	assert(SEL->current && SEL->current->mapHeader);	const PlayerInfo &p = SEL->current->mapHeader->players[s.color.getNum()];	assert(p.canComputerPlay || p.canHumanPlay); //someone must be able to control this player	if(p.canHumanPlay && p.canComputerPlay)		whoCanPlay = HUMAN_OR_CPU;	else if(p.canComputerPlay)		whoCanPlay = CPU;	else		whoCanPlay = HUMAN;	if(SEL->screenType != CMenuScreen::scenarioInfo		&&  SEL->current->mapHeader->players[s.color.getNum()].canHumanPlay		&&  SEL->multiPlayer != CMenuScreen::MULTI_NETWORK_GUEST)	{		flag = new CButton(Point(-43, 2), flags[s.color.getNum()], CGI->generaltexth->zelp[180], std::bind(&OptionsTab::flagPressed, owner, s.color));		flag->hoverable = true;	}	else		flag = nullptr;	town = new SelectedBox(Point(119, 2), s, TOWN);	hero = new SelectedBox(Point(195, 2), s, HERO);	bonus = new SelectedBox(Point(271, 2), s, BONUS);}void OptionsTab::PlayerOptionsEntry::showAll(SDL_Surface * to){	CIntObject::showAll(to);	printAtMiddleLoc(s.name, 55, 10, FONT_SMALL, Colors::WHITE, to);	printAtMiddleWBLoc(CGI->generaltexth->arraytxt[206+whoCanPlay], 28, 39, FONT_TINY, 50, Colors::WHITE, to);}void OptionsTab::PlayerOptionsEntry::update(){	town->update();	hero->update();	bonus->update();}void OptionsTab::PlayerOptionsEntry::selectButtons(){	if(!btns[0])		return;	if( (pi.defaultCastle() != -1) //fixed tow		|| (SEL->isGuest() && s.color != playerColor)) //or not our player	{		btns[0]->disable();		btns[1]->disable();	}	else	{		btns[0]->enable();		btns[1]->enable();	}	if( (pi.defaultHero() != -1  ||  !s.playerID  ||  s.castle < 0) //fixed hero		|| (SEL->isGuest() && s.color != playerColor))//or not our player	{		btns[2]->disable();		btns[3]->disable();	}	else	{		btns[2]->enable();		btns[3]->enable();	}	if(SEL->isGuest() && s.color != playerColor)//or not our player	{		btns[4]->disable();		btns[5]->disable();	}	else	{		btns[4]->enable();		btns[5]->enable();	}}size_t OptionsTab::CPlayerSettingsHelper::getImageIndex(){	enum EBonusSelection //frames of bonuses file	{		WOOD_ORE = 0,   CRYSTAL = 1,    GEM  = 2,		MERCURY  = 3,   SULFUR  = 5,    GOLD = 8,		ARTIFACT = 9,   RANDOM  = 10,		WOOD = 0,       ORE     = 0,    MITHRIL = 10, // resources unavailable in bonuses file		TOWN_RANDOM = 38,  TOWN_NONE = 39, // Special frames in ITPA		HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall	};	switch(type)	{	case TOWN:		switch (settings.castle)		{		case PlayerSettings::NONE:   return TOWN_NONE;		case PlayerSettings::RANDOM: return TOWN_RANDOM;		default: return CGI->townh->factions[settings.castle]->town->clientInfo.icons[true][false] + 2;		}	case HERO:		switch (settings.hero)		{		case PlayerSettings::NONE:   return HERO_NONE;		case PlayerSettings::RANDOM: return HERO_RANDOM;		default:			{				if(settings.heroPortrait >= 0)					return settings.heroPortrait;				return CGI->heroh->heroes[settings.hero]->imageIndex;			}		}	case BONUS:		{			switch(settings.bonus)			{			case PlayerSettings::RANDOM:   return RANDOM;			case PlayerSettings::ARTIFACT: return ARTIFACT;			case PlayerSettings::GOLD:     return GOLD;			case PlayerSettings::RESOURCE:				{					switch(CGI->townh->factions[settings.castle]->town->primaryRes)					{					case Res::WOOD_AND_ORE : return WOOD_ORE;					case Res::WOOD    : return WOOD;					case Res::MERCURY : return MERCURY;					case Res::ORE     : return ORE;					case Res::SULFUR  : return SULFUR;					case Res::CRYSTAL : return CRYSTAL;					case Res::GEMS    : return GEM;					case Res::GOLD    : return GOLD;					case Res::MITHRIL : return MITHRIL;					}				}			}		}	}	return 0;}std::string OptionsTab::CPlayerSettingsHelper::getImageName(){	switch(type)	{	case OptionsTab::TOWN:  return "ITPA";	case OptionsTab::HERO:  return "PortraitsSmall";	case OptionsTab::BONUS: return "SCNRSTAR";	}	return "";}std::string OptionsTab::CPlayerSettingsHelper::getTitle(){	switch(type)	{	case OptionsTab::TOWN: return (settings.castle < 0) ? CGI->generaltexth->allTexts[103] : CGI->generaltexth->allTexts[80];	case OptionsTab::HERO: return (settings.hero   < 0) ? CGI->generaltexth->allTexts[101] : CGI->generaltexth->allTexts[77];	case OptionsTab::BONUS:		{			switch(settings.bonus)			{			case PlayerSettings::RANDOM:   return CGI->generaltexth->allTexts[86]; //{Random Bonus}			case PlayerSettings::ARTIFACT: return CGI->generaltexth->allTexts[83]; //{Artifact Bonus}			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[84]; //{Gold Bonus}			case PlayerSettings::RESOURCE: return CGI->generaltexth->allTexts[85]; //{Resource Bonus}			}		}	}	return "";}std::string OptionsTab::CPlayerSettingsHelper::getName(){	switch(type)	{	case TOWN:		{			switch (settings.castle)			{			case PlayerSettings::NONE   : return CGI->generaltexth->allTexts[523];			case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];			default : return CGI->townh->factions[settings.castle]->name;			}		}	case HERO:		{			switch (settings.hero)			{			case PlayerSettings::NONE   : return CGI->generaltexth->allTexts[523];			case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];			default :				{					if (!settings.heroName.empty())						return settings.heroName;					return CGI->heroh->heroes[settings.hero]->name;				}			}		}	case BONUS:		{			switch (settings.bonus)			{				case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];				default: return CGI->generaltexth->arraytxt[214 + settings.bonus];			}		}	}	return "";}std::string OptionsTab::CPlayerSettingsHelper::getSubtitle(){	switch(type)	{	case TOWN: return getName();	case HERO:		{			if (settings.hero >= 0)				return getName() + " - " + CGI->heroh->heroes[settings.hero]->heroClass->name;			return getName();		}	case BONUS:		{			switch(settings.bonus)			{			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[87]; //500-1000			case PlayerSettings::RESOURCE:				{					switch(CGI->townh->factions[settings.castle]->town->primaryRes)					{					case Res::MERCURY: return CGI->generaltexth->allTexts[694];					case Res::SULFUR:  return CGI->generaltexth->allTexts[695];					case Res::CRYSTAL: return CGI->generaltexth->allTexts[692];					case Res::GEMS:    return CGI->generaltexth->allTexts[693];					case Res::WOOD_AND_ORE: return CGI->generaltexth->allTexts[89]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool					}				}			}		}	}	return "";}std::string OptionsTab::CPlayerSettingsHelper::getDescription(){	switch(type)	{	case TOWN: return CGI->generaltexth->allTexts[104];	case HERO: return CGI->generaltexth->allTexts[102];	case BONUS:		{			switch(settings.bonus)			{			case PlayerSettings::RANDOM:   return CGI->generaltexth->allTexts[94]; //Gold, wood and ore, or an artifact is randomly chosen as your starting bonus			case PlayerSettings::ARTIFACT: return CGI->generaltexth->allTexts[90]; //An artifact is randomly chosen and equipped to your starting hero			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool			case PlayerSettings::RESOURCE:				{					switch(CGI->townh->factions[settings.castle]->town->primaryRes)					{					case Res::MERCURY: return CGI->generaltexth->allTexts[690];					case Res::SULFUR:  return CGI->generaltexth->allTexts[691];					case Res::CRYSTAL: return CGI->generaltexth->allTexts[688];					case Res::GEMS:    return CGI->generaltexth->allTexts[689];					case Res::WOOD_AND_ORE: return CGI->generaltexth->allTexts[93]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool					}				}			}		}	}	return "";}OptionsTab::CPregameTooltipBox::CPregameTooltipBox(CPlayerSettingsHelper & helper):	CWindowObject(BORDERED | RCLICK_POPUP),	CPlayerSettingsHelper(helper){	OBJ_CONSTRUCTION_CAPTURING_ALL;	int value = PlayerSettings::NONE;	switch(CPlayerSettingsHelper::type)	{	break; case TOWN:			value = settings.castle;	break; case HERO:			value = settings.hero;	break; case BONUS:			value = settings.bonus;	}	if (value == PlayerSettings::RANDOM)		genBonusWindow();	else if (CPlayerSettingsHelper::type == BONUS)		genBonusWindow();	else if (CPlayerSettingsHelper::type == HERO)		genHeroWindow();	else if (CPlayerSettingsHelper::type == TOWN)		genTownWindow();	center();}void OptionsTab::CPregameTooltipBox::genHeader(){	new CFilledTexture("DIBOXBCK", pos);	updateShadow();	new CLabel(pos.w / 2 + 8, 21, FONT_MEDIUM, CENTER, Colors::YELLOW, getTitle());	new CLabel(pos.w / 2, 88, FONT_SMALL, CENTER, Colors::WHITE, getSubtitle());	new CAnimImage(getImageName(), getImageIndex(), 0, pos.w / 2 - 24, 45);}void OptionsTab::CPregameTooltipBox::genTownWindow(){	pos = Rect(0, 0, 228, 290);	genHeader();	new CLabel(pos.w / 2 + 8, 122, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);	std::vector<CComponent *> components;	const CTown * town = CGI->townh->factions[settings.castle]->town;	for (auto & elem : town->creatures)	{		if (!elem.empty())			components.push_back(new CComponent(CComponent::creature, elem.front(), 0, CComponent::tiny));	}	new CComponentBox(components, Rect(10, 140, pos.w - 20, 140));}void OptionsTab::CPregameTooltipBox::genHeroWindow(){	pos = Rect(0, 0, 292, 226);	genHeader();	// specialty	new CAnimImage("UN44", CGI->heroh->heroes[settings.hero]->imageIndex, 0, pos.w / 2 - 22, 134);	new CLabel(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER,  Colors::YELLOW, CGI->generaltexth->allTexts[78]);	new CLabel(pos.w / 2,     188, FONT_SMALL,  CENTER, Colors::WHITE, CGI->heroh->heroes[settings.hero]->specName);}void OptionsTab::CPregameTooltipBox::genBonusWindow(){	pos = Rect(0, 0, 228, 162);	genHeader();	new CTextBox(getDescription(), Rect(10, 100, pos.w - 20, 70), 0, FONT_SMALL, CENTER, Colors::WHITE );}OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & settings, SelType type)	:CIntObject(RCLICK, position),	CPlayerSettingsHelper(settings, type){	OBJ_CONSTRUCTION_CAPTURING_ALL;	image = new CAnimImage(getImageName(), getImageIndex());	subtitle = new CLabel(23, 39, FONT_TINY, CENTER, Colors::WHITE, getName());	pos = image->pos;}void OptionsTab::SelectedBox::update(){	image->setFrame(getImageIndex());	subtitle->setText(getName());}void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState ){	if (down)	{		// cases when we do not need to display a message		if (settings.castle == -2 && CPlayerSettingsHelper::type == TOWN )			return;		if (settings.hero == -2 && !SEL->current->mapHeader->players[settings.color.getNum()].hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)			return;		GH.pushInt(new CPregameTooltipBox(*this));	}}CScenarioInfo::CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *startInfo){	OBJ_CONSTRUCTION_CAPTURING_ALL;	for(auto it = startInfo->playerInfos.cbegin(); it != startInfo->playerInfos.cend(); ++it)	{		if(it->second.playerID)		{			playerColor = it->first;		}	}	pos.w = 762;	pos.h = 584;	center(pos);	assert(LOCPLINT);	sInfo = *LOCPLINT->cb->getStartInfo();	assert(!SEL->current);	current = mapInfoFromGame();	setPlayersFromGame();	screenType = CMenuScreen::scenarioInfo;	card = new InfoCard();	opt = new OptionsTab();	opt->recreate();	card->changeSelection(current);	card->difficulty->setSelected(startInfo->difficulty);	back = new CButton(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);}CScenarioInfo::~CScenarioInfo(){	delete current;}bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb){	const CMapHeader * a = aaa->mapHeader.get(),			* b = bbb->mapHeader.get();	if(a && b) //if we are sorting scenarios	{		switch (sortBy)		{		case _format: //by map format (RoE, WoG, etc)			return (a->version<b->version);			break;		case _loscon: //by loss conditions			return (a->defeatMessage < b->defeatMessage);			break;		case _playerAm: //by player amount			int playerAmntB,humenPlayersB,playerAmntA,humenPlayersA;			playerAmntB=humenPlayersB=playerAmntA=humenPlayersA=0;			for (int i=0;i<8;i++)			{				if (a->players[i].canHumanPlay) {playerAmntA++;humenPlayersA++;}				else if (a->players[i].canComputerPlay) {playerAmntA++;}				if (b->players[i].canHumanPlay) {playerAmntB++;humenPlayersB++;}				else if (b->players[i].canComputerPlay) {playerAmntB++;}			}			if (playerAmntB!=playerAmntA)				return (playerAmntA<playerAmntB);			else				return (humenPlayersA<humenPlayersB);			break;		case _size: //by size of map			return (a->width<b->width);			break;		case _viccon: //by victory conditions			return (a->victoryMessage < b->victoryMessage);			break;		case _name: //by name			return boost::ilexicographical_compare(a->name, b->name);		case _fileName: //by filename			return boost::ilexicographical_compare(aaa->fileURI, bbb->fileURI);		default:			return boost::ilexicographical_compare(a->name, b->name);		}	}	else //if we are sorting campaigns	{		switch(sortBy)		{			case _numOfMaps: //by number of maps in campaign				return CGI->generaltexth->campaignRegionNames[ aaa->campaignHeader->mapVersion ].size() <					CGI->generaltexth->campaignRegionNames[ bbb->campaignHeader->mapVersion ].size();				break;			case _name: //by name				return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);			default:				return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);		}	}}CMultiMode::CMultiMode(){	OBJ_CONSTRUCTION_CAPTURING_ALL;	bg = new CPicture("MUPOPUP.bmp");	bg->convertToScreenBPP(); //so we could draw without problems	blitAt(CPicture("MUMAP.bmp"), 16, 77, *bg); //blit img	pos = bg->center(); //center, window has size of bg graphic	bar = new CGStatusBar(new CPicture(Rect(7, 465, 440, 18), 0));//226, 472	txt = new CTextInput(Rect(19, 436, 334, 16), *bg);	txt->setText(settings["general"]["playerName"].String()); //Player	btns[0] = new CButton(Point(373, 78),        "MUBHOT.DEF",  CGI->generaltexth->zelp[266], std::bind(&CMultiMode::openHotseat, this));	btns[1] = new CButton(Point(373, 78 + 57*1), "MUBHOST.DEF", CButton::tooltip("Host TCP/IP game", ""), std::bind(&CMultiMode::hostTCP, this));	btns[2] = new CButton(Point(373, 78 + 57*2), "MUBJOIN.DEF", CButton::tooltip("Join TCP/IP game", ""), std::bind(&CMultiMode::joinTCP, this));	btns[6] = new CButton(Point(373, 424),       "MUBCANC.DEF", CGI->generaltexth->zelp[288], [&] { GH.popIntTotally(this);}, SDLK_ESCAPE);}void CMultiMode::openHotseat(){	GH.pushInt(new CHotSeatPlayers(txt->text));}void CMultiMode::hostTCP(){	Settings name = settings.write["general"]["playerName"];	name->String() = txt->text;	GH.popIntTotally(this);	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_NETWORK_HOST));}void CMultiMode::joinTCP(){	Settings name = settings.write["general"]["playerName"];	name->String() = txt->text;	GH.pushInt(new CSimpleJoinScreen);}CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer){	OBJ_CONSTRUCTION_CAPTURING_ALL;	bg = new CPicture("MUHOTSEA.bmp");	pos = bg->center(); //center, window has size of bg graphic	std::string text = CGI->generaltexth->allTexts[446];	boost::replace_all(text, "\t","\n");	Rect boxRect(25, 20, 315, 50);	title = new CTextBox(text, boxRect, 0, FONT_BIG, CENTER, Colors::WHITE);//HOTSEAT	Please enter names	for(int i = 0; i < ARRAY_COUNT(txt); i++)	{		txt[i] = new CTextInput(Rect(60, 85 + i*30, 280, 16), *bg);        txt[i]->cb += std::bind(&CHotSeatPlayers::onChange, this, _1);	}	ok = new CButton(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CHotSeatPlayers::enterSelectionScreen, this), SDLK_RETURN);	cancel = new CButton(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);	bar = new CGStatusBar(new CPicture(Rect(7, 381, 348, 18), 0));//226, 472	txt[0]->setText(firstPlayer, true);	txt[0]->giveFocus();}void CHotSeatPlayers::onChange(std::string newText){	size_t namesCount = 0;	for(auto & elem : txt)		if(!elem->text.empty())			namesCount++;	ok->block(namesCount < 2);}void CHotSeatPlayers::enterSelectionScreen(){	std::map<ui8, std::string> names;	for(int i = 0, j = 1; i < ARRAY_COUNT(txt); i++)		if(txt[i]->text.length())			names[j++] = txt[i]->text;	Settings name = settings.write["general"]["playerName"];	name->String() = names.begin()->second;	GH.popInts(2); //pop MP mode window and this	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_HOT_SEAT, &names));}void CBonusSelection::init(){	highlightedRegion = nullptr;	ourHeader = nullptr;	diffLb = nullptr;	diffRb = nullptr;	bonuses = nullptr;	selectedMap = 0;	// Initialize start info	startInfo.mapname = ourCampaign->camp->header.filename;	startInfo.mode = StartInfo::CAMPAIGN;	startInfo.campState = ourCampaign;	startInfo.turnTime = 0;	OBJ_CONSTRUCTION_CAPTURING_ALL;	static const std::string bgNames [] = {"E1_BG.BMP", "G2_BG.BMP", "E2_BG.BMP", "G1_BG.BMP", "G3_BG.BMP", "N1_BG.BMP",		"S1_BG.BMP", "BR_BG.BMP", "IS_BG.BMP", "KR_BG.BMP", "NI_BG.BMP", "TA_BG.BMP", "AR_BG.BMP", "HS_BG.BMP",		"BB_BG.BMP", "NB_BG.BMP", "EL_BG.BMP", "RN_BG.BMP", "UA_BG.BMP", "SP_BG.BMP"};	loadPositionsOfGraphics();	background = BitmapHandler::loadBitmap(bgNames[ourCampaign->camp->header.mapVersion]);	pos.h = background->h;	pos.w = background->w;	center();	SDL_Surface * panel = BitmapHandler::loadBitmap("CAMPBRF.BMP");	blitAt(panel, 456, 6, background);	startB   = new CButton(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), SDLK_RETURN);	restartB = new CButton(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), SDLK_RETURN);	backB    = new CButton(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), SDLK_ESCAPE);	//campaign name	if (ourCampaign->camp->header.name.length())		graphics->fonts[FONT_BIG]->renderTextLeft(background, ourCampaign->camp->header.name, Colors::YELLOW, Point(481, 28));	else		graphics->fonts[FONT_BIG]->renderTextLeft(background, CGI->generaltexth->allTexts[508], Colors::YELLOW, Point(481, 28));	//map size icon	sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");	//campaign description	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[38], Colors::YELLOW, Point(481, 63));	campaignDescription = new CTextBox(ourCampaign->camp->header.description, Rect(480, 86, 286, 117), 1);	//campaignDescription->showAll(background);	//map description	mapDescription = new CTextBox("", Rect(480, 280, 286, 117), 1);	//bonus choosing	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, CGI->generaltexth->allTexts[71], Colors::WHITE, Point(511, 432));	bonuses = new CToggleGroup(bind(&CBonusSelection::selectBonus, this, _1));	//set left part of window	bool isCurrentMapConquerable = ourCampaign->currentMap && ourCampaign->camp->conquerable(*ourCampaign->currentMap);	for(int g = 0; g < ourCampaign->camp->scenarios.size(); ++g)	{		if(ourCampaign->camp->conquerable(g))		{			regions.push_back(new CRegion(this, true, true, g));			regions[regions.size()-1]->rclickText = ourCampaign->camp->scenarios[g].regionText;			if(highlightedRegion == nullptr)			{				if(!isCurrentMapConquerable || (isCurrentMapConquerable && g == *ourCampaign->currentMap))				{					highlightedRegion = regions.back();					selectMap(g, true);				}			}		}		else if (ourCampaign->camp->scenarios[g].conquered) //display as striped		{			regions.push_back(new CRegion(this, false, false, g));			regions[regions.size()-1]->rclickText = ourCampaign->camp->scenarios[g].regionText;		}	}	//allies / enemies	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[390] + ":", Colors::WHITE, Point(486, 407));	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[391] + ":", Colors::WHITE, Point(619, 407));	SDL_FreeSurface(panel);	//difficulty	std::vector<std::string> difficulty;	boost::split(difficulty, CGI->generaltexth->allTexts[492], boost::is_any_of(" "));	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, difficulty.back(), Colors::WHITE, Point(689, 432));	//difficulty pics	for (int b=0; b<ARRAY_COUNT(diffPics); ++b)	{		CDefEssential * cde = CDefHandler::giveDefEss("GSPBUT" + boost::lexical_cast<std::string>(b+3) + ".DEF");		SDL_Surface * surfToDuplicate = cde->ourImages[0].bitmap;		diffPics[b] = SDL_ConvertSurface(surfToDuplicate, surfToDuplicate->format,			surfToDuplicate->flags);		delete cde;	}	//difficulty selection buttons	if (ourCampaign->camp->header.difficultyChoosenByPlayer)	{		diffLb = new CButton(Point(694, 508), "SCNRBLF.DEF", CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this));		diffRb = new CButton(Point(738, 508), "SCNRBRT.DEF", CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this));	}	//load miniflags	sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");}CBonusSelection::CBonusSelection(shared_ptr<CCampaignState> _ourCampaign) : ourCampaign(_ourCampaign){	init();}CBonusSelection::CBonusSelection(const std::string & campaignFName){	ourCampaign = make_shared<CCampaignState>(CCampaignHandler::getCampaign(campaignFName));	init();}CBonusSelection::~CBonusSelection(){	SDL_FreeSurface(background);	delete sizes;	delete ourHeader;	delete sFlags;	for (auto & elem : diffPics)	{		SDL_FreeSurface(elem);	}}void CBonusSelection::goBack(){	GH.popIntTotally(this);}void CBonusSelection::showAll(SDL_Surface * to){	blitAt(background, pos.x, pos.y, to);	CIntObject::showAll(to);	show(to);	if (pos.h != to->h || pos.w != to->w)		CMessage::drawBorder(PlayerColor(1), to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);}void CBonusSelection::loadPositionsOfGraphics(){	const JsonNode config(ResourceID("config/campaign_regions.json"));	int idx = 0;	for(const JsonNode &campaign : config["campaign_regions"].Vector())	{		SCampPositions sc;		sc.campPrefix = campaign["prefix"].String();		sc.colorSuffixLength = campaign["color_suffix_length"].Float();		for(const JsonNode &desc :  campaign["desc"].Vector())		{			SCampPositions::SRegionDesc rd;			rd.infix = desc["infix"].String();			rd.xpos = desc["x"].Float();			rd.ypos = desc["y"].Float();			sc.regions.push_back(rd);		}		campDescriptions.push_back(sc);		idx++;	}	assert(idx == CGI->generaltexth->campaignMapNames.size());}void CBonusSelection::selectMap(int mapNr, bool initialSelect){	if(initialSelect || selectedMap != mapNr)	{		// initialize restart / start button		if(!ourCampaign->currentMap || *ourCampaign->currentMap != mapNr)		{			// draw start button			restartB->disable();			startB->enable();			if (!ourCampaign->mapsConquered.empty())				backB->block(true);			else				backB->block(false);		}		else		{			// draw restart button			startB->disable();			restartB->enable();			backB->block(false);		}		startInfo.difficulty = ourCampaign->camp->scenarios[mapNr].difficulty;		selectedMap = mapNr;		selectedBonus = boost::none;		std::string scenarioName = ourCampaign->camp->header.filename.substr(0, ourCampaign->camp->header.filename.find('.'));		boost::to_lower(scenarioName);		scenarioName += ':' + boost::lexical_cast<std::string>(selectedMap);		//get header		delete ourHeader;		std::string & headerStr = ourCampaign->camp->mapPieces.find(mapNr)->second;		auto buffer = reinterpret_cast<const ui8 *>(headerStr.data());		ourHeader = CMapService::loadMapHeader(buffer, headerStr.size(), scenarioName).release();		std::map<ui8, std::string> names;		names[1] = settings["general"]["playerName"].String();		updateStartInfo(ourCampaign->camp->header.filename, startInfo, ourHeader, names);		mapDescription->setText(ourHeader->description);		updateBonusSelection();		GH.totalRedraw();	}}void CBonusSelection::show(SDL_Surface * to){	//blitAt(background, pos.x, pos.y, to);	//map name	std::string mapName = ourHeader->name;	if (mapName.length())		printAtLoc(mapName, 481, 219, FONT_BIG, Colors::YELLOW, to);	else		printAtLoc("Unnamed", 481, 219, FONT_BIG, Colors::YELLOW, to);	//map description	printAtLoc(CGI->generaltexth->allTexts[496], 481, 253, FONT_SMALL, Colors::YELLOW, to);	mapDescription->showAll(to); //showAll because CTextBox has no show()	//map size icon	int temp;	switch (ourHeader->width)	{	case 36:		temp=0;		break;	case 72:		temp=1;		break;	case 108:		temp=2;		break;	case 144:		temp=3;		break;	default:		temp=4;		break;	}	blitAtLoc(sizes->ourImages[temp].bitmap, 735, 26, to);	//flags	int fx = 496  + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[390]);	int ex = 629 + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[391]);	TeamID myT;	myT = ourHeader->players[playerColor.getNum()].team;	for (auto i = startInfo.playerInfos.cbegin(); i != startInfo.playerInfos.cend(); i++)	{		int *myx = ((i->first == playerColor  ||  ourHeader->players[i->first.getNum()].team == myT) ? &fx : &ex);		blitAtLoc(sFlags->ourImages[i->first.getNum()].bitmap, *myx, 405, to);		*myx += sFlags->ourImages[i->first.getNum()].bitmap->w;	}	//difficulty	blitAtLoc(diffPics[startInfo.difficulty], 709, 455, to);	CIntObject::show(to);}void CBonusSelection::updateBonusSelection(){	OBJ_CONSTRUCTION_CAPTURING_ALL;	//graphics:	//spell - SPELLBON.DEF	//monster - TWCRPORT.DEF	//building - BO*.BMP graphics	//artifact - ARTIFBON.DEF	//spell scroll - SPELLBON.DEF	//prim skill - PSKILBON.DEF	//sec skill - SSKILBON.DEF	//resource - BORES.DEF	//player - CREST58.DEF	//hero - PORTRAITSLARGE (HPL###.BMPs)	const CCampaignScenario &scenario = ourCampaign->camp->scenarios[selectedMap];	const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;	updateStartButtonState(-1);	delete bonuses;	bonuses = new CToggleGroup(bind(&CBonusSelection::selectBonus, this, _1));	static const char *bonusPics[] = {"SPELLBON.DEF", "TWCRPORT.DEF", "", "ARTIFBON.DEF", "SPELLBON.DEF",		"PSKILBON.DEF", "SSKILBON.DEF", "BORES.DEF", "PORTRAITSLARGE", "PORTRAITSLARGE"};	for(int i = 0; i < bonDescs.size(); i++)	{		std::string picName=bonusPics[bonDescs[i].type];		size_t picNumber=bonDescs[i].info2;		std::string desc;		switch(bonDescs[i].type)		{		case CScenarioTravel::STravelBonus::SPELL:			desc = CGI->generaltexth->allTexts[715];			boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);			break;		case CScenarioTravel::STravelBonus::MONSTER:			picNumber = bonDescs[i].info2 + 2;			desc = CGI->generaltexth->allTexts[717];			boost::algorithm::replace_first(desc, "%d", boost::lexical_cast<std::string>(bonDescs[i].info3));			boost::algorithm::replace_first(desc, "%s", CGI->creh->creatures[bonDescs[i].info2]->namePl);			break;		case CScenarioTravel::STravelBonus::BUILDING:			{				int faction = -1;				for(auto & elem : startInfo.playerInfos)				{					if (elem.second.playerID)					{						faction = elem.second.castle;						break;					}				}				assert(faction != -1);				BuildingID buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());				picName = graphics->ERMUtoPicture[faction][buildID];				picNumber = -1;				if (vstd::contains(CGI->townh->factions[faction]->town->buildings, buildID))					desc = CGI->townh->factions[faction]->town->buildings.find(buildID)->second->Name();			}			break;		case CScenarioTravel::STravelBonus::ARTIFACT:			desc = CGI->generaltexth->allTexts[715];			boost::algorithm::replace_first(desc, "%s", CGI->arth->artifacts[bonDescs[i].info2]->Name());			break;		case CScenarioTravel::STravelBonus::SPELL_SCROLL:			desc = CGI->generaltexth->allTexts[716];			boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);			break;		case CScenarioTravel::STravelBonus::PRIMARY_SKILL:			{				int leadingSkill = -1;				std::vector<std::pair<int, int> > toPrint; //primary skills to be listed <num, val>				const ui8* ptr = reinterpret_cast<const ui8*>(&bonDescs[i].info2);				for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)				{					if (leadingSkill == -1 || ptr[g] > ptr[leadingSkill])					{						leadingSkill = g;					}					if (ptr[g] != 0)					{						toPrint.push_back(std::make_pair(g, ptr[g]));					}				}				picNumber = leadingSkill;				desc = CGI->generaltexth->allTexts[715];				std::string substitute; //text to be printed instead of %s				for (int v=0; v<toPrint.size(); ++v)				{					substitute += boost::lexical_cast<std::string>(toPrint[v].second);					substitute += " " + CGI->generaltexth->primarySkillNames[toPrint[v].first];					if(v != toPrint.size() - 1)					{						substitute += ", ";					}				}				boost::algorithm::replace_first(desc, "%s", substitute);				break;			}		case CScenarioTravel::STravelBonus::SECONDARY_SKILL:			desc = CGI->generaltexth->allTexts[718];			boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level			boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->skillName[bonDescs[i].info2]); //skill name			picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;			break;		case CScenarioTravel::STravelBonus::RESOURCE:			{				int serialResID = 0;				switch(bonDescs[i].info1)				{				case 0: case 1: case 2: case 3: case 4: case 5: case 6:					serialResID = bonDescs[i].info1;					break;				case 0xFD: //wood + ore					serialResID = 7;					break;				case 0xFE: //rare resources					serialResID = 8;					break;				}				picNumber = serialResID;				desc = CGI->generaltexth->allTexts[717];				boost::algorithm::replace_first(desc, "%d", boost::lexical_cast<std::string>(bonDescs[i].info2));				std::string replacement;				if (serialResID <= 6)				{					replacement = CGI->generaltexth->restypes[serialResID];				}				else				{					replacement = CGI->generaltexth->allTexts[714 + serialResID];				}				boost::algorithm::replace_first(desc, "%s", replacement);			}			break;		case CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO:			{				auto superhero = ourCampaign->camp->scenarios[bonDescs[i].info2].strongestHero(PlayerColor(bonDescs[i].info1));				if (!superhero) logGlobal->warnStream() << "No superhero! How could it be transferred?";				picNumber = superhero ? superhero->portrait : 0;				desc = CGI->generaltexth->allTexts[719];				boost::algorithm::replace_first(desc, "%s", ourCampaign->camp->scenarios[bonDescs[i].info2].scenarioName); //scenario			}			break;		case CScenarioTravel::STravelBonus::HERO:			desc = CGI->generaltexth->allTexts[718];			boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->capColors[bonDescs[i].info1]); //hero's color			if (bonDescs[i].info2 == 0xFFFF)			{				boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->allTexts[101]); //hero's name				picNumber = -1;				picName = "CBONN1A3.BMP";			}			else			{				boost::algorithm::replace_first(desc, "%s", CGI->heroh->heroes[bonDescs[i].info2]->name); //hero's name			}			break;		}		CToggleButton *bonusButton = new CToggleButton(Point(475 + i*68, 455), "", CButton::tooltip(desc, desc));		if (picNumber != -1)			picName += ":" + boost::lexical_cast<std::string>(picNumber);		auto   anim = new CAnimation();		anim->setCustom(picName, 0);		bonusButton->setImage(anim);		const SDL_Color brightYellow = { 242, 226, 110, 0 };		bonusButton->borderColor = brightYellow;		bonuses->addToggle(i, bonusButton);	}	// set bonus if already chosen	if(vstd::contains(ourCampaign->chosenCampaignBonuses, selectedMap))	{		bonuses->setSelected(ourCampaign->chosenCampaignBonuses[selectedMap]);	}}void CBonusSelection::updateCampaignState(){	ourCampaign->currentMap = selectedMap;	if (selectedBonus)		ourCampaign->chosenCampaignBonuses[selectedMap] = *selectedBonus;}void CBonusSelection::startMap(){	auto si = new StartInfo(startInfo);	auto showPrologVideo = [=]()	{		auto exitCb = [=]()		{			logGlobal->infoStream() << "Starting scenario " << selectedMap;            CGP->showLoadingScreen(std::bind(&startGame, si, (CConnection *)nullptr));		};		const CCampaignScenario & scenario = ourCampaign->camp->scenarios[selectedMap];		if (scenario.prolog.hasPrologEpilog)		{			GH.pushInt(new CPrologEpilogVideo(scenario.prolog, exitCb));		}		else		{			exitCb();		}	};	if(LOCPLINT) // we're currently ingame, so ask for starting new map and end game	{		GH.popInt(this);		LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]		{			updateCampaignState();			endGame();			GH.curInt = CGPreGame::create();			showPrologVideo();		}, 0);	}	else	{		updateCampaignState();		showPrologVideo();	}}void CBonusSelection::restartMap(){	GH.popInt(this);	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]	{		updateCampaignState();		auto si = new StartInfo(startInfo);		SDL_Event event;		event.type = SDL_USEREVENT;		event.user.code = PREPARE_RESTART_CAMPAIGN;		event.user.data1 = si;		SDL_PushEvent(&event);	}, 0);}void CBonusSelection::selectBonus(int id){	// Total redraw is needed because the border around the bonus images	// have to be undrawn/drawn.	if (!selectedBonus || *selectedBonus != id)	{		selectedBonus = id;		GH.totalRedraw();		updateStartButtonState(id);	}	const CCampaignScenario &scenario = ourCampaign->camp->scenarios[selectedMap];	const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;	if (bonDescs[id].type == CScenarioTravel::STravelBonus::HERO)	{		std::map<ui8, std::string> names;		names[1] = settings["general"]["playerName"].String();		for(auto & elem : startInfo.playerInfos)		{			if(elem.first == PlayerColor(bonDescs[id].info1))				::setPlayer(elem.second, 1, names);			else				::setPlayer(elem.second, 0, names);		}	}}void CBonusSelection::increaseDifficulty(){	startInfo.difficulty = std::min(startInfo.difficulty + 1, 4);}void CBonusSelection::decreaseDifficulty(){	startInfo.difficulty = std::max(startInfo.difficulty - 1, 0);}void CBonusSelection::updateStartButtonState(int selected /*= -1*/){	if(selected == -1)	{		startB->block(ourCampaign->camp->scenarios[selectedMap].travelOptions.bonusesToChoose.size());	}	else if(startB->isBlocked())	{		startB->block(false);	}}CBonusSelection::CRegion::CRegion( CBonusSelection * _owner, bool _accessible, bool _selectable, int _myNumber ): owner(_owner), accessible(_accessible), selectable(_selectable), myNumber(_myNumber){	OBJ_CONSTRUCTION;	addUsedEvents(LCLICK | RCLICK);	static const std::string colors[2][8] = {		{"R", "B", "N", "G", "O", "V", "T", "P"},		{"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"}};	const SCampPositions & campDsc = owner->campDescriptions[owner->ourCampaign->camp->header.mapVersion];	const SCampPositions::SRegionDesc & desc = campDsc.regions[myNumber];	pos.x += desc.xpos;	pos.y += desc.ypos;	//loading of graphics	std::string prefix = campDsc.campPrefix + desc.infix + "_";	std::string suffix = colors[campDsc.colorSuffixLength - 1][owner->ourCampaign->camp->scenarios[myNumber].regionColor];	static const std::string infix [] = {"En", "Se", "Co"};	for (int g = 0; g < ARRAY_COUNT(infix); g++)	{		graphics[g] = BitmapHandler::loadBitmap(prefix + infix[g] + suffix + ".BMP");	}	pos.w = graphics[0]->w;	pos.h = graphics[0]->h;}CBonusSelection::CRegion::~CRegion(){	for (auto & elem : graphics)	{		SDL_FreeSurface(elem);	}}void CBonusSelection::CRegion::clickLeft( tribool down, bool previousState ){	//select if selectable & clicked inside our graphic	if ( indeterminate(down) )	{		return;	}	if( !down && selectable && !CSDL_Ext::isTransparent(graphics[0], GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) )	{		owner->selectMap(myNumber, false);		owner->highlightedRegion = this;		parent->showAll(screen);	}}void CBonusSelection::CRegion::clickRight( tribool down, bool previousState ){	//show r-click text	if( down && !CSDL_Ext::isTransparent(graphics[0], GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) &&		rclickText.size() )	{		CRClickPopup::createAndPush(rclickText);	}}void CBonusSelection::CRegion::show(SDL_Surface * to){	//const SCampPositions::SRegionDesc & desc = owner->campDescriptions[owner->ourCampaign->camp->header.mapVersion].regions[myNumber];	if (!accessible)	{		//show as striped		blitAtLoc(graphics[2], 0, 0, to);	}	else if (this == owner->highlightedRegion)	{		//show as selected		blitAtLoc(graphics[1], 0, 0, to);	}	else	{		//show as not selected selected		blitAtLoc(graphics[0], 0, 0, to);	}}CSavingScreen::CSavingScreen(bool hotseat) : CSelectionScreen(CMenuScreen::saveGame, hotseat ? CMenuScreen::MULTI_HOT_SEAT : CMenuScreen::SINGLE_PLAYER){	ourGame = mapInfoFromGame();	sInfo = *LOCPLINT->cb->getStartInfo();	setPlayersFromGame();}CSavingScreen::~CSavingScreen(){}ISelectionScreenInfo::ISelectionScreenInfo(const std::map<ui8, std::string> *Names /*= nullptr*/){	multiPlayer = CMenuScreen::SINGLE_PLAYER;	assert(!SEL);	SEL = this;	current = nullptr;	if(Names && !Names->empty()) //if have custom set of player names - use it		playerNames = *Names;	else		playerNames[1] = settings["general"]["playerName"].String(); //by default we have only one player and his name is "Player" (or whatever the last used name was)}ISelectionScreenInfo::~ISelectionScreenInfo(){	assert(SEL == this);	SEL = nullptr;}void ISelectionScreenInfo::updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader){	::updateStartInfo(filename, sInfo, mapHeader, playerNames);}void ISelectionScreenInfo::setPlayer(PlayerSettings &pset, ui8 player){	::setPlayer(pset, player, playerNames);}ui8 ISelectionScreenInfo::getIdOfFirstUnallocatedPlayer(){	for(auto i = playerNames.cbegin(); i != playerNames.cend(); i++)		if(!sInfo.getPlayersSettings(i->first))  //			return i->first;	return 0;}bool ISelectionScreenInfo::isGuest() const{	return multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST;}bool ISelectionScreenInfo::isHost() const{	return multiPlayer == CMenuScreen::MULTI_NETWORK_HOST;}void ChatMessage::apply(CSelectionScreen *selScreen){	selScreen->card->chat->addNewMessage(playerName + ": " + message);	GH.totalRedraw();}void QuitMenuWithoutStarting::apply(CSelectionScreen *selScreen){	if(!selScreen->ongoingClosing)	{		*selScreen->serv << this; //resend to confirm		GH.popIntTotally(selScreen); //will wait with deleting us before this thread ends	}	vstd::clear_pointer(selScreen->serv);}void PlayerJoined::apply(CSelectionScreen *selScreen){	//assert(SEL->playerNames.size() == connectionID); //temporary, TODO when player exits	SEL->playerNames[connectionID] = playerName;	//put new player in first slot with AI	for(auto & elem : SEL->sInfo.playerInfos)	{		if(!elem.second.playerID)		{			selScreen->setPlayer(elem.second, connectionID);			selScreen->opt->entries[elem.second.color]->selectButtons();			break;		}	}	selScreen->propagateNames();	selScreen->propagateOptions();	GH.totalRedraw();}void SelectMap::apply(CSelectionScreen *selScreen){	if(selScreen->multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)	{		free = false;		selScreen->changeSelection(mapInfo);	}}void UpdateStartOptions::apply(CSelectionScreen *selScreen){	if(!selScreen->isGuest())		return;	selScreen->setSInfo(*options);}void PregameGuiAction::apply(CSelectionScreen *selScreen){	if(!selScreen->isGuest())		return;	switch(action)	{	case NO_TAB:		selScreen->toggleTab(selScreen->curTab);		break;	case OPEN_OPTIONS:		selScreen->toggleTab(selScreen->opt);		break;	case OPEN_SCENARIO_LIST:		selScreen->toggleTab(selScreen->sel);		break;	case OPEN_RANDOM_MAP_OPTIONS:		selScreen->toggleTab(selScreen->randMapTab);		break;	}}void RequestOptionsChange::apply(CSelectionScreen *selScreen){	if(!selScreen->isHost())		return;	PlayerColor color = selScreen->sInfo.getPlayersSettings(playerID)->color;	switch(what)	{	case TOWN:		selScreen->opt->nextCastle(color, direction);		break;	case HERO:		selScreen->opt->nextHero(color, direction);		break;	case BONUS:		selScreen->opt->nextBonus(color, direction);		break;	}}void PlayerLeft::apply(CSelectionScreen *selScreen){	if(selScreen->isGuest())		return;	SEL->playerNames.erase(playerID);	if(PlayerSettings *s = selScreen->sInfo.getPlayersSettings(playerID)) //it's possible that player was unallocated	{		selScreen->setPlayer(*s, 0);		selScreen->opt->entries[s->color]->selectButtons();	}	selScreen->propagateNames();	selScreen->propagateOptions();	GH.totalRedraw();}void PlayersNames::apply(CSelectionScreen *selScreen){	if(selScreen->isGuest())		selScreen->playerNames = playerNames;}void StartWithCurrentSettings::apply(CSelectionScreen *selScreen){	startingInfo.reset();	startingInfo.serv = selScreen->serv;	startingInfo.sInfo = new StartInfo(selScreen->sInfo);	if(!selScreen->ongoingClosing)	{		*selScreen->serv << this; //resend to confirm	}	selScreen->serv = nullptr; //hide it so it won't be deleted	vstd::clear_pointer(selScreen->serverHandlingThread); //detach us	saveGameName.clear();    CGP->showLoadingScreen(std::bind(&startGame, startingInfo.sInfo, startingInfo.serv));	throw 666; //EVIL, EVIL, EVIL workaround to kill thread (does "goto catch" outside listening loop)}CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode &config ){	pos.x += config["x"].Float();	pos.y += config["y"].Float();	pos.w = 200;	pos.h = 116;	campFile = config["file"].String();	video = config["video"].String();	OBJ_CONSTRUCTION_CAPTURING_ALL;	status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;	CCampaignHeader header = CCampaignHandler::getHeader(campFile);	hoverText = header.name;	if (status != CCampaignScreen::DISABLED)	{		addUsedEvents(LCLICK | HOVER);		image = new CPicture(config["image"].String());		hoverLabel = new CLabel(pos.w / 2, pos.h + 20, FONT_MEDIUM, CENTER, Colors::YELLOW, "");		parent->addChild(hoverLabel);	}	if (status == CCampaignScreen::COMPLETED)		checkMark = new CPicture("CAMPCHK");}void CCampaignScreen::CCampaignButton::clickLeft(tribool down, bool previousState){	if (down)	{		// Close running video and open the selected campaign		CCS->videoh->close();		GH.pushInt( new CBonusSelection(campFile) );	}}void CCampaignScreen::CCampaignButton::hover(bool on){	if (on)		hoverLabel->setText(hoverText); // Shows the name of the campaign when you get into the bounds of the button	else		hoverLabel->setText(" ");}void CCampaignScreen::CCampaignButton::show(SDL_Surface * to){	if (status == CCampaignScreen::DISABLED)		return;	CIntObject::show(to);	// Play the campaign button video when the mouse cursor is placed over the button	if (hovered)	{		if (CCS->videoh->fname != video)			CCS->videoh->open(video);		CCS->videoh->update(pos.x, pos.y, to, true, false); // plays sequentially frame by frame, starts at the beginning when the video is over	}	else if (CCS->videoh->fname == video) // When you got out of the bounds of the button then close the video	{		CCS->videoh->close();		redraw();	}}CButton* CCampaignScreen::createExitButton(const JsonNode& button){	std::pair<std::string, std::string> help;	if (!button["help"].isNull() && button["help"].Float() > 0)		help = CGI->generaltexth->zelp[button["help"].Float()];	std::function<void()> close = std::bind(&CGuiHandler::popIntTotally, &GH, this);	return new CButton(Point(button["x"].Float(), button["y"].Float()), button["name"].String(), help, close, button["hotkey"].Float());}CCampaignScreen::CCampaignScreen(const JsonNode &config){	OBJ_CONSTRUCTION_CAPTURING_ALL;	for(const JsonNode& node : config["images"].Vector())		images.push_back(createPicture(node));	if (!images.empty())	{		images[0]->center();              // move background to center		moveTo(images[0]->pos.topLeft()); // move everything else to center		images[0]->moveTo(pos.topLeft()); // restore moved twice background		pos = images[0]->pos;             // fix height\width of this window	}	if (!config["exitbutton"].isNull())	{		back = createExitButton(config["exitbutton"]);		back->hoverable = true;	}	for(const JsonNode& node : config["items"].Vector())		campButtons.push_back(new CCampaignButton(node));}void CCampaignScreen::showAll(SDL_Surface *to){	CIntObject::showAll(to);	if (pos.h != to->h || pos.w != to->w)		CMessage::drawBorder(PlayerColor(1), to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);}void CGPreGame::showLoadingScreen(std::function<void()> loader){	if (GH.listInt.size() && GH.listInt.front() == CGP) //pregame active		CGP->removeFromGui();	GH.pushInt(new CLoadingScreen(loader));}std::string CLoadingScreen::getBackground(){	const auto & conf = CGPreGameConfig::get().getConfig()["loading"].Vector();	if(conf.empty())	{		return "loadbar";	}	else	{		return RandomGeneratorUtil::nextItem(conf, CRandomGenerator::getDefault())->String();	}}CLoadingScreen::CLoadingScreen(std::function<void ()> loader):    CWindowObject(BORDERED, getBackground()),    loadingThread(loader){	CCS->musich->stopMusic(5000);}CLoadingScreen::~CLoadingScreen(){	loadingThread.join();}void CLoadingScreen::showAll(SDL_Surface *to){	Rect rect(0,0,to->w, to->h);	SDL_FillRect(to, &rect, 0);	CWindowObject::showAll(to);}CPrologEpilogVideo::CPrologEpilogVideo( CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback ):    CWindowObject(BORDERED),    spe(_spe),    positionCounter(0),    voiceSoundHandle(-1),    exitCb(callback){	OBJ_CONSTRUCTION_CAPTURING_ALL;	addUsedEvents(LCLICK);	pos = center(Rect(0,0, 800, 600));	updateShadow();	CCS->videoh->open(CCampaignHandler::prologVideoName(spe.prologVideo));	CCS->musich->playMusic("Music/" + CCampaignHandler::prologMusicName(spe.prologMusic), true);	voiceSoundHandle = CCS->soundh->playSound(CCampaignHandler::prologVoiceName(spe.prologVideo));	text = new CMultiLineLabel(Rect(100, 500, 600, 100), EFonts::FONT_BIG, CENTER, Colors::METALLIC_GOLD, spe.prologText );	text->scrollTextTo(-100);}void CPrologEpilogVideo::show( SDL_Surface * to ){	CSDL_Ext::fillRectBlack(to, &pos);	//BUG: some videos are 800x600 in size while some are 800x400	//VCMI should center them in the middle of the screen. Possible but needs modification	//of video player API which I'd like to avoid until we'll get rid of Windows-specific player	CCS->videoh->update(pos.x, pos.y, to, true, false);	//move text every 5 calls/frames; seems to be good enough	++positionCounter;	if(positionCounter % 5 == 0)	{		text->scrollTextBy(1);	}	else		text->showAll(to);// blit text over video, if needed	if (text->textSize.y + 100 < positionCounter / 5)		clickLeft(false, false);}void CPrologEpilogVideo::clickLeft( tribool down, bool previousState ){	GH.popInt(this);	CCS->soundh->stopSound(voiceSoundHandle);	exitCb();}CSimpleJoinScreen::CSimpleJoinScreen(){	OBJ_CONSTRUCTION_CAPTURING_ALL;	bg = new CPicture("MUDIALOG.bmp"); // address background	pos = bg->center(); //center, window has size of bg graphic (x,y = 396,278 w=232 h=212)	Rect boxRect(20, 20, 205, 50);	title = new CTextBox("Enter address:", boxRect, 0, FONT_BIG, CENTER, Colors::WHITE);	address = new CTextInput(Rect(25, 68, 175, 16), *bg);    address->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);	port = new CTextInput(Rect(25, 115, 175, 16), *bg);    port->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);    port->filters.add(std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535));	ok     = new CButton(Point( 26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::enterSelectionScreen, this), SDLK_RETURN);	cancel = new CButton(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);	bar = new CGStatusBar(new CPicture(Rect(7, 186, 218, 18), 0));	port->setText(boost::lexical_cast<std::string>(settings["server"]["port"].Float()), true);	address->setText(settings["server"]["server"].String(), true);	address->giveFocus();}void CSimpleJoinScreen::enterSelectionScreen(){	std::string textAddress = address->text;	std::string textPort = port->text;	GH.popIntTotally(this);	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, CMenuScreen::MULTI_NETWORK_GUEST, nullptr, textAddress, textPort));}void CSimpleJoinScreen::onChange(const std::string & newText){	ok->block(address->text.empty() || port->text.empty());}
 |