瀏覽代碼

Merge branch 'develop' into SpellsRefactoring8

AlexVinS 9 年之前
父節點
當前提交
ea2e336f54

+ 3 - 1
AI/VCAI/VCAI.cpp

@@ -697,7 +697,9 @@ void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *do
 	//you can't request action from action-response thread
 	requestActionASAP([=]()
 	{
-		pickBestCreatures (down, up);
+		if(removableUnits)
+			pickBestCreatures(down, up);
+
 		answerQuery(queryID, 0);
 	});
 }

+ 3 - 1
client/CMT.cpp

@@ -248,7 +248,8 @@ int main(int argc, char** argv)
         ("loadserverip",po::value<std::string>(),"IP for loaded game server")
 		("loadserverport",po::value<std::string>(),"port for loaded game server")
 		("testingport",po::value<std::string>(),"port for testing, override specified in config file")
-		("testingfileprefix",po::value<std::string>(),"prefix for auto save files");
+		("testingfileprefix",po::value<std::string>(),"prefix for auto save files")
+		("testingsavefrequency",po::value<int>(),"how often auto save should be created");
 
 	if(argc > 1)
 	{
@@ -313,6 +314,7 @@ int main(int argc, char** argv)
 		testingSettings["enabled"].Bool() = true;
 		testingSettings["port"].String() = vm["testingport"].as<std::string>();
 		testingSettings["prefix"].String() = vm["testingfileprefix"].as<std::string>();
+		testingSettings["savefrequency"].Float() = vm.count("testingsavefrequency") ? vm["testingsavefrequency"].as<int>() : 1;
 	}
 
 	// Initialize logging based on settings

+ 1 - 1
client/CPlayerInterface.cpp

@@ -178,7 +178,7 @@ void CPlayerInterface::yourTurn()
 			}
 			firstCall = 0;
 		}
-		else
+		else if(settings["testing"].isNull() || cb->getDate() % static_cast<int>(settings["testing"]["savefrequency"].Float()) == 0)
 		{
 			LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + boost::lexical_cast<std::string>(autosaveCount++ + 1));
 			autosaveCount %= 5;

+ 14 - 8
client/CPreGame.cpp

@@ -699,7 +699,10 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		if(isHost())
 		{
 			assert(playerNames.size() == 1  &&  vstd::contains(playerNames, 1)); //TODO hot-seat/network combo
-			serv = sh->connectToServer();
+			if(CServerHandler::DO_NOT_START_SERVER)
+				serv = CServerHandler::justConnectToServer(Address, Port);
+			else
+				serv = sh->connectToServer();
 			*serv << (ui8) 4;
 			myNameID = 1;
 		}
@@ -3129,14 +3132,17 @@ 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));
+	if(CServerHandler::DO_NOT_START_SERVER)
+		GH.pushInt(new CSimpleJoinScreen(CMenuScreen::MULTI_NETWORK_HOST));
+	else
+		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);
+	GH.pushInt(new CSimpleJoinScreen(CMenuScreen::MULTI_NETWORK_GUEST));
 }
 
 CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
@@ -4273,7 +4279,7 @@ void CPrologEpilogVideo::clickLeft( tribool down, bool previousState )
 	exitCb();
 }
 
-CSimpleJoinScreen::CSimpleJoinScreen()
+CSimpleJoinScreen::CSimpleJoinScreen(CMenuScreen::EMultiMode mode)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	bg = new CPicture("MUDIALOG.bmp"); // address background
@@ -4289,7 +4295,7 @@ CSimpleJoinScreen::CSimpleJoinScreen()
     port->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);
     port->filters += 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);
+	ok     = new CButton(Point( 26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::enterSelectionScreen, this, mode), 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));
 
@@ -4298,15 +4304,15 @@ CSimpleJoinScreen::CSimpleJoinScreen()
 	address->giveFocus();
 }
 
-void CSimpleJoinScreen::enterSelectionScreen()
+void CSimpleJoinScreen::enterSelectionScreen(CMenuScreen::EMultiMode mode)
 {
 	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));
-}
 
+	GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, mode, nullptr, textAddress, textPort));
+}
 void CSimpleJoinScreen::onChange(const std::string & newText)
 {
 	ok->block(address->text.empty() || port->text.empty());

+ 2 - 2
client/CPreGame.h

@@ -642,10 +642,10 @@ class CSimpleJoinScreen : public CIntObject
 	CTextInput * address;
 	CTextInput * port;
 
-	void enterSelectionScreen();
+	void enterSelectionScreen(CMenuScreen::EMultiMode mode);
 	void onChange(const std::string & newText);
 public:
-	CSimpleJoinScreen();
+	CSimpleJoinScreen(CMenuScreen::EMultiMode mode);
 };
 
 extern ISelectionScreenInfo *SEL;

+ 1 - 1
client/Client.cpp

@@ -285,7 +285,7 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
 
 		if(clientSaveName.empty())
 			throw std::runtime_error("Cannot open client part of " + fname);
-		if(controlServerSaveName.empty())
+		if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName))
 			throw std::runtime_error("Cannot open server part of " + fname);
 
 		{

+ 6 - 0
client/windows/CHeroWindow.cpp

@@ -344,6 +344,12 @@ void CHeroWindow::commanderWindow()
 
 }
 
+void CHeroWindow::updateGarrisons()
+{
+	CWindowWithGarrison::updateGarrisons();
+	morale->set(&heroWArt);
+}
+
 void CHeroWindow::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);

+ 1 - 0
client/windows/CHeroWindow.h

@@ -91,6 +91,7 @@ public:
 	void questlog(); //show quest log in hero window
 	void commanderWindow();
 	void switchHero(); //changes displayed hero
+	virtual void updateGarrisons() override;  //updates the morale widget and calls the parent
 
 	//friends
 	friend void CArtPlace::clickLeft(tribool down, bool previousState);

+ 1 - 1
config/creatures/neutral.json

@@ -151,7 +151,7 @@
 			"mirror" :
 			{
 				"type" : "MAGIC_MIRROR",
-				"val" : 30
+				"val" : 20
 			},
 			"casts" :
 			{

+ 4 - 2
lib/CBattleCallback.cpp

@@ -432,8 +432,10 @@ ui8 CBattleInfoEssentials::battleGetSiegeLevel() const
 bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const
 {
 	RETURN_IF_NOT_BATTLE(false);
-	//conditions like for fleeing + enemy must have a hero
-	return battleCanFlee(player) && battleHasHero(!playerToSide(player));
+	ui8 mySide = playerToSide(player);
+	bool iAmSiegeDefender = ( mySide == BattleSide::DEFENDER  &&  battleGetSiegeLevel() );
+	//conditions like for fleeing (except escape tunnel presence) + enemy must have a hero
+	return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(!mySide);
 }
 
 bool CBattleInfoEssentials::battleHasHero(ui8 side) const

+ 2 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -255,7 +255,8 @@ void CGHeroInstance::setType(si32 ID, si32 subID)
 	assert(ID == Obj::HERO); // just in case
 	type = VLC->heroh->heroes[subID];
 	portrait = type->imageIndex;
-	CGObjectInstance::setType(ID, type->heroClass->id);
+	CGObjectInstance::setType(ID, type->heroClass->id); // to find object handler we must use heroClass->id
+	this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details
 	randomizeArmy(type->heroClass->faction);
 }
 

+ 28 - 13
lib/mapObjects/CGPandoraBox.cpp

@@ -65,35 +65,49 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
 			break;
 		}
 	}
+	
+	std::vector<std::pair<SecondarySkill, ui8>> unpossessedAbilities; //ability + ability level
+	int abilitiesRequiringSlot = 0;
 
-	if(gainedExp || changesPrimSkill || abilities.size())
+	//filter out unnecessary secondary skills
+	for (int i = 0; i < abilities.size(); i++)
+	{
+		int curLev = h->getSecSkillLevel(abilities[i]);
+		bool abilityCanUseSlot = !curLev && ((h->secSkills.size() + abilitiesRequiringSlot) < GameConstants::SKILL_PER_HERO); //limit new abilities to number of slots
+
+		if (abilityCanUseSlot)
+			abilitiesRequiringSlot++;
+
+		if ((curLev && curLev < abilityLevels[i]) || abilityCanUseSlot)
+		{
+			unpossessedAbilities.push_back({ abilities[i], abilityLevels[i] });
+		}
+	}
+
+	if(gainedExp || changesPrimSkill || unpossessedAbilities.size())
 	{
 		TExpType expVal = h->calculateXp(gainedExp);
 		//getText(iw,afterBattle,175,h); //wtf?
 		iw.text.addTxt(MetaString::ADVOB_TXT, 175); //%s learns something
 		iw.text.addReplacement(h->name);
-
+		
 		if(expVal)
 			iw.components.push_back(Component(Component::EXPERIENCE,0,expVal,0));
+			
 		for(int i=0; i<primskills.size(); i++)
 			if(primskills[i])
 				iw.components.push_back(Component(Component::PRIM_SKILL,i,primskills[i],0));
 
-		for(int i=0; i<abilities.size(); i++)
-			iw.components.push_back(Component(Component::SEC_SKILL,abilities[i],abilityLevels[i],0));
-
+		for(auto abilityData : unpossessedAbilities)
+			iw.components.push_back(Component(Component::SEC_SKILL, abilityData.first, abilityData.second, 0));
+			
 		cb->showInfoDialog(&iw);
 
 		//give sec skills
-		for(int i=0; i<abilities.size(); i++)
-		{
-			int curLev = h->getSecSkillLevel(abilities[i]);
+		for (auto abilityData : unpossessedAbilities)
+			cb->changeSecSkill(h, abilityData.first, abilityData.second, true);
 
-			if( (curLev  &&  curLev < abilityLevels[i])	|| (h->canLearnSkill() ))
-			{
-				cb->changeSecSkill(h,abilities[i],abilityLevels[i],true);
-			}
-		}
+		assert(h->secSkills.size() <= GameConstants::SKILL_PER_HERO);
 
 		//give prim skills
 		for(int i=0; i<primskills.size(); i++)
@@ -106,6 +120,7 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
 		if(expVal)
 			cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false);
 	}
+	//else { } //TODO:Create information that box was empty for now, and deliver to CGPandoraBox::giveContentsAfterExp or refactor
 
 	if(!cb->isVisitCoveredByAnotherQuery(this, h))
 		giveContentsAfterExp(h);

+ 4 - 4
lib/mapObjects/CGTownInstance.cpp

@@ -618,16 +618,16 @@ void CGTownInstance::initObj(CRandomGenerator & rand)
 
 	switch (subID)
 	{ //add new visitable objects
-		case 0:
+		case ETownType::CASTLE:
 			bonusingBuildings.push_back (new COPWBonus(BuildingID::STABLES, this));
 			break;
-		case 5:
+		case ETownType::DUNGEON:
 			bonusingBuildings.push_back (new COPWBonus(BuildingID::MANA_VORTEX, this));
 			//fallthrough
-		case 2: case 3: case 6:
+		case ETownType::TOWER: case ETownType::INFERNO: case ETownType::STRONGHOLD:
 			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this));
 			break;
-		case 7:
+		case ETownType::FORTRESS:
 			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_1, this));
 			break;
 	}

+ 3 - 3
lib/mapObjects/MiscObjects.cpp

@@ -867,10 +867,10 @@ void CGResource::initObj(CRandomGenerator & rand)
 	{
 		switch(subID)
 		{
-		case 6:
-			amount = rand.nextInt(500, 1000);
+		case Res::GOLD:
+			amount = rand.nextInt(5, 10) * 100;
 			break;
-		case 0: case 2:
+		case Res::WOOD: case Res::ORE:
 			amount = rand.nextInt(6, 10);
 			break;
 		default:

+ 102 - 56
server/CGameHandler.cpp

@@ -1356,7 +1356,7 @@ static bool evntCmp(const CMapEvent &a, const CMapEvent &b)
 
 void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false)
 {// bool forced = true - if creature should be replaced, if false - only if no creature was set
-	const PlayerState *p = gs->getPlayer(town->tempOwner);
+	const PlayerState * p = getPlayer(town->tempOwner);
 	if(!p)
 	{
 		logGlobal->warn("There is no player owner of town %s at %s", town->name, town->pos());
@@ -1626,7 +1626,7 @@ void CGameHandler::newTurn()
 				fw.mode = 1;
 				fw.player = player;
 				// find all hidden tiles
-				const auto & fow = gs->getPlayerTeam(player)->fogOfWarMap;
+				const auto & fow = getPlayerTeam(player)->fogOfWarMap;
 				for (size_t i=0; i<fow.size(); i++)
 					for (size_t j=0; j<fow.at(i).size(); j++)
 						for (size_t k=0; k<fow.at(i).at(j).size(); k++)
@@ -1638,7 +1638,7 @@ void CGameHandler::newTurn()
 		}
 		if (t->hasBonusOfType (Bonus::DARKNESS))
 		{
-			for (auto & player : gameState()->players)
+			for(auto & player : gs->players)
 			{
 				if (getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
 					getPlayerRelations(player.first, t->tempOwner) == PlayerRelations::ENEMIES)
@@ -1863,7 +1863,7 @@ void CGameHandler::setupBattle( int3 tile, const CArmedInstance *armies[2], cons
 {
 	battleResult.set(nullptr);
 
-	const auto t = gs->getTile(tile);
+	const auto t = getTile(tile);
 	ETerrainType terrain = t->terType;
 	if(gs->map->isCoastalTile(tile)) //coastal tile is always ground
 		terrain = ETerrainType::SAND;
@@ -1972,7 +1972,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
 		return false;
 	}
 
-	const TerrainTile t = *gs->getTile(hmpos);
+	const TerrainTile t = *getTile(hmpos);
 	const int3 guardPos = gs->guardingCreaturePosition(hmpos);
 
 	const bool embarking = !h->boat && !t.visitableObjects.empty() && t.visitableObjects.back()->ID == Obj::BOAT;
@@ -2181,7 +2181,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, PlayerColor owner)
 
 		if (oldOwner < PlayerColor::PLAYER_LIMIT) //old owner is real player
 		{
-			if (gs->getPlayer(oldOwner)->towns.empty())//previous player lost last last town
+			if(getPlayer(oldOwner)->towns.empty()) //previous player lost last last town
 			{
 				InfoWindow iw;
 				iw.player = oldOwner;
@@ -2192,11 +2192,11 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, PlayerColor owner)
 		}
 	}
 
-	const PlayerState * p = gs->getPlayer(owner);
+	const PlayerState * p = getPlayer(owner);
 
 	if((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4 ) && p && p->dwellings.size()==1)//first dwelling captured
 	{
-		for(const CGTownInstance *t : gs->getPlayer(owner)->towns)
+		for(const CGTownInstance * t : getPlayer(owner)->towns)
 		{
 			if (t->hasBuilt(BuildingID::PORTAL_OF_SUMMON, ETownType::DUNGEON))
 				setPortalDwelling(t);//set initial creatures for all portals of summoning
@@ -2226,7 +2226,7 @@ void CGameHandler::giveResource(PlayerColor player, Res::ERes which, int val) //
 	SetResource sr;
 	sr.player = player;
 	sr.resid = which;
-	sr.val = gs->players.find(player)->second.resources.at(which) + val;
+	sr.val = getPlayer(player)->resources.at(which) + val;
 	sendAndApply(&sr);
 }
 
@@ -2505,7 +2505,7 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)
 {
 	auto h1 = getHero(hero1), h2 = getHero(hero2);
 
-	if( gameState()->getPlayerRelations(h1->getOwner(), h2->getOwner()))
+	if(getPlayerRelations(h1->getOwner(), h2->getOwner()))
 	{
 		auto exchange = std::make_shared<CGarrisonDialogQuery>(h1, h2);
 		ExchangeDialog hex;
@@ -2610,8 +2610,8 @@ void CGameHandler::close()
 
 bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player )
 {
-	const CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->getObjInstance(id1)),
-		*s2 = static_cast<CArmedInstance*>(gs->getObjInstance(id2));
+	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(getObjInstance(id1)),
+		* s2 = static_cast<const CArmedInstance *>(getObjInstance(id2));
 	const CCreatureSet &S1 = *s1, &S2 = *s2;
 	StackLocation sl1(s1, p1), sl2(s2, p2);
 	if(!sl1.slot.validSlot()  ||  !sl2.slot.validSlot())
@@ -2626,6 +2626,21 @@ bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui
 		return false;
 	}
 
+	// We can always put stacks into locked garrison, but not take them out of it
+	auto notRemovable = [&](const CArmedInstance * army)
+	{
+		if(id1 != id2) // Stack arrangement inside locked garrison is allowed
+		{
+			auto g = dynamic_cast<const CGGarrison *>(army);
+			if(g && !g->removableUnits)
+			{
+				complain("Stacks in this garrison are not removable!\n");
+				return true;
+			}
+		}
+		return false;
+	};
+
 	if(what==1) //swap
 	{
 		if ( ((s1->tempOwner != player && s1->tempOwner != PlayerColor::UNFLAGGABLE) && s1->getStackCount(p1))
@@ -2641,6 +2656,16 @@ bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui
 			return false;
 		}
 
+		if(!s1->slotEmpty(p1) && !s2->slotEmpty(p2))
+		{
+			if(notRemovable(sl1.army) || notRemovable(sl2.army))
+				return false;
+		}
+		if(s1->slotEmpty(p1) && notRemovable(sl2.army))
+			return false;
+		else if(s2->slotEmpty(p2) && notRemovable(sl1.army))
+			return false;
+
 		swapStacks(sl1, sl2);
 	}
 	else if(what==2)//merge
@@ -2649,6 +2674,14 @@ bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui
 		|| (((s1->tempOwner != player && s1->tempOwner != PlayerColor::UNFLAGGABLE) && s2->getStackCount(p2)) && complain("Can't take troops from another player!")))
 			return false;
 
+		if(s1->slotEmpty(p1) || s2->slotEmpty(p2))
+		{
+			complain("Cannot merge empty stack!");
+			return false;
+		}
+		else if(notRemovable(sl1.army))
+			return false;
+
 		moveStack(sl1, sl2);
 	}
 	else if(what==3) //split
@@ -2681,6 +2714,17 @@ bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui
 				return false;
 			}
 
+			if(notRemovable(sl1.army))
+			{
+				if(s1->getStackCount(p1) > countLeftOnSrc)
+					return false;
+			}
+			else if(notRemovable(sl2.army))
+			{
+				if(s2->getStackCount(p1) < countLeftOnSrc)
+					return false;
+			}
+
 			moveStack(sl1, sl2, countToMove);
 			//S2.slots[p2]->count = val;
 			//S1.slots[p1]->count = total - val;
@@ -2693,6 +2737,8 @@ bool CGameHandler::arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui
 				return false;
 			}
 
+			if(notRemovable(sl1.army))
+				return false;
 
 			moveStack(sl1, sl2, val);
 		}
@@ -2727,7 +2773,7 @@ PlayerColor CGameHandler::getPlayerAt( CConnection *c ) const
 
 bool CGameHandler::disbandCreature( ObjectInstanceID id, SlotID pos )
 {
-	CArmedInstance *s1 = static_cast<CArmedInstance*>(gs->getObjInstance(id));
+	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(getObjInstance(id));
 	if(!vstd::contains(s1->stacks,pos))
 	{
 		complain("Illegal call to disbandCreature - no such stack in army!");
@@ -2760,7 +2806,7 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID requestedID,
 		switch (requestedBuilding->mode)
 		{
 		case CBuilding::BUILD_NORMAL :
-			if (gs->canBuildStructure(t, requestedID) != EBuildingState::ALLOWED)
+			if(canBuildStructure(t, requestedID) != EBuildingState::ALLOWED)
 				COMPLAIN_RET("Cannot build that building!");
 			break;
 
@@ -2877,7 +2923,7 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID requestedID,
 	{
 		SetResources sr;
 		sr.player = t->tempOwner;
-		sr.res = gs->getPlayer(t->tempOwner)->resources - requestedBuilding->resources;
+		sr.res = getPlayer(t->tempOwner)->resources - requestedBuilding->resources;
 		sendAndApply(&sr);
 	}
 
@@ -2935,7 +2981,7 @@ void CGameHandler::sendMessageToAll( const std::string &message )
 
 bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dstid, CreatureID crid, ui32 cram, si32 fromLvl )
 {
-	const CGDwelling *dw = static_cast<const CGDwelling*>(gs->getObj(objid));
+	const CGDwelling * dw = static_cast<const CGDwelling *>(getObj(objid));
 	const CArmedInstance *dst = nullptr;
 	const CCreature *c = VLC->creh->creatures.at(crid);
 	bool warMachine = c->hasBonusOfType(Bonus::SIEGE_WEAPON);
@@ -2970,7 +3016,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 	SlotID slot = dst->getSlotFor(crid);
 
 	if( (!found && complain("Cannot recruit: no such creatures!"))
-		|| (cram  >  VLC->creh->creatures.at(crid)->maxAmount(gs->getPlayer(dst->tempOwner)->resources) && complain("Cannot recruit: lack of resources!"))
+		|| (cram  >  VLC->creh->creatures.at(crid)->maxAmount(getPlayer(dst->tempOwner)->resources) && complain("Cannot recruit: lack of resources!"))
 		|| (cram<=0  &&  complain("Cannot recruit: cram <= 0!"))
 		|| (!slot.validSlot()  && !warMachine && complain("Cannot recruit: no available slot!")))
 	{
@@ -2980,7 +3026,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 	//recruit
 	SetResources sr;
 	sr.player = dst->tempOwner;
-	sr.res = gs->getPlayer(dst->tempOwner)->resources - (c->cost * cram);
+	sr.res = getPlayer(dst->tempOwner)->resources - (c->cost * cram);
 
 	SetAvailableCreatures sac;
 	sac.tid = objid;
@@ -3021,12 +3067,13 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 
 bool CGameHandler::upgradeCreature( ObjectInstanceID objid, SlotID pos, CreatureID upgID )
 {
-	CArmedInstance *obj = static_cast<CArmedInstance*>(gs->getObjInstance(objid));
+	const CArmedInstance * obj = static_cast<const CArmedInstance *>(getObjInstance(objid));
 	if (!obj->hasStackAtSlot(pos))
 	{
 		COMPLAIN_RET("Cannot upgrade, no stack at slot " + boost::to_string(pos));
 	}
-	UpgradeInfo ui = gs->getUpgradeInfo(obj->getStack(pos));
+	UpgradeInfo ui;
+	getUpgradeInfo(obj, pos, ui);
 	PlayerColor player = obj->tempOwner;
 	const PlayerState *p = getPlayer(player);
 	int crQuantity = obj->stacks.at(pos)->count;
@@ -3100,7 +3147,7 @@ void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst
 
 bool CGameHandler::garrisonSwap( ObjectInstanceID tid )
 {
-	CGTownInstance *town = gs->getTown(tid);
+	const CGTownInstance * town = getTown(tid);
 	if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army
 	{
 
@@ -3218,8 +3265,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
  */
 bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
 {
-
-	CGHeroInstance *hero = gs->getHero(heroID);
+	const CGHeroInstance * hero = getHero(heroID);
 	const CArtifactInstance *destArtifact = hero->getArt(artifactSlot);
 
 	if(!destArtifact)
@@ -3253,8 +3299,8 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
 
 bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid )
 {
-	CGHeroInstance *hero = gs->getHero(hid);
-	CGTownInstance *town = hero->visitedTown;
+	const CGHeroInstance * hero = getHero(hid);
+	const CGTownInstance * town = hero->visitedTown;
 	if(aid==ArtifactID::SPELLBOOK)
 	{
 		if((!town->hasBuilt(BuildingID::MAGES_GUILD_1) && complain("Cannot buy a spellbook, no mage guild in the town!"))
@@ -3274,7 +3320,7 @@ bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid )
 		int price = VLC->arth->artifacts[aid]->price;
 
 		if(( hero->getArt(ArtifactPosition(9+aid)) && complain("Hero already has this machine!"))
-		 || (gs->getPlayer(hero->getOwner())->resources.at(Res::GOLD) < price && complain("Not enough gold!")))
+		 || (getPlayer(hero->getOwner())->resources.at(Res::GOLD) < price && complain("Not enough gold!")))
 		{
 			return false;
 		}
@@ -3399,8 +3445,8 @@ bool CGameHandler::buySecSkill( const IMarket *m, const CGHeroInstance *h, Secon
 
 bool CGameHandler::tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2)
 {
-	int r1 = gs->getPlayer(player)->resources.at(id1),
-		r2 = gs->getPlayer(player)->resources.at(id2);
+	int r1 = getPlayer(player)->resources.at(id1),
+		r2 = getPlayer(player)->resources.at(id2);
 
 	vstd::amin(val, r1); //can't trade more resources than have
 
@@ -3490,15 +3536,15 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
 
 bool CGameHandler::sendResources(ui32 val, PlayerColor player, Res::ERes r1, PlayerColor r2)
 {
-	const PlayerState *p2 = gs->getPlayer(r2, false);
+	const PlayerState *p2 = getPlayer(r2, false);
 	if(!p2  ||  p2->status != EPlayerStatus::INGAME)
 	{
 		complain("Dest player must be in game!");
 		return false;
 	}
 
-	si32 curRes1 = gs->getPlayer(player)->resources.at(r1),
-	     curRes2 = gs->getPlayer(r2)->resources.at(r1);
+	si32 curRes1 = getPlayer(player)->resources.at(r1),
+		 curRes2 = getPlayer(r2)->resources.at(r1);
 	val = std::min(si32(val),curRes1);
 
 	SetResource sr;
@@ -3533,8 +3579,8 @@ bool CGameHandler::setFormation(ObjectInstanceID hid, ui8 formation)
 
 bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor player)
 {
-	const PlayerState *p = gs->getPlayer(player);
-	const CGTownInstance *t = gs->getTown(obj->id);
+	const PlayerState * p = getPlayer(player);
+	const CGTownInstance * t = getTown(obj->id);
 
 	//common preconditions
 //	if( (p->resources.at(Res::GOLD)<GOLD_NEEDED  && complain("Not enough gold for buying hero!"))
@@ -4217,7 +4263,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 		SetMana sm;
 		GiveBonus giveBonus(GiveBonus::HERO);
 
-		CGHeroInstance *h = gs->getHero(currObj);
+		const CGHeroInstance * h = getHero(currObj);
 		if(!h && complain("Cannot realize cheat, no hero selected!")) return;
 
 		sm.hid = h->id;
@@ -4243,13 +4289,13 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if (message == "vcmiarmenelos") //build all buildings in selected town
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
-		CGTownInstance *town;
+		const CGHeroInstance * hero = getHero(currObj);
+		const CGTownInstance * town;
 
 		if (hero)
 			town = hero->visitedTown;
 		else
-			town = gs->getTown(currObj);
+			town = getTown(currObj);
 
 		if (town)
 		{
@@ -4266,7 +4312,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if(message == "vcmiainur") //gives 5 archangels into each slot
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
+		const CGHeroInstance * hero = getHero(currObj);
 		const CCreature *archangel = VLC->creh->creatures.at(13);
 		if(!hero) return;
 
@@ -4276,7 +4322,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if(message == "vcmiangband") //gives 10 black knight into each slot
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
+		const CGHeroInstance * hero = getHero(currObj);
 		const CCreature *blackKnight = VLC->creh->creatures.at(66);
 		if(!hero) return;
 
@@ -4286,7 +4332,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if(message == "vcmiglaurung") //gives 5000 crystal dragons into each slot
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
+		const CGHeroInstance * hero = getHero(currObj);
 		const CCreature *crystalDragon = VLC->creh->creatures.at(133);
 		if(!hero) return;
 
@@ -4296,7 +4342,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if(message == "vcminoldor") //all war machines
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
+		const CGHeroInstance * hero = getHero(currObj);
 		if(!hero) return;
 
 		if(!hero->getArt(ArtifactPosition::MACH1))
@@ -4308,7 +4354,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	}
 	else if (message == "vcmiforgeofnoldorking") //hero gets all artifacts except war machines, spell scrolls and spell book
 	{
-		CGHeroInstance *hero = gs->getHero(currObj);
+		const CGHeroInstance *hero = gs->getHero(currObj);
 		if(!hero) return;
 		for (int g = 7; g < VLC->arth->artifacts.size(); ++g) //including artifacts from mods
 			giveHeroNewArtifact(hero, VLC->arth->artifacts[g], ArtifactPosition::PRE_FIRST);
@@ -4331,23 +4377,23 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
 	{
 		SetResources sr;
 		sr.player = player;
-		sr.res = gs->getPlayer(player)->resources;
+		sr.res = getPlayer(player)->resources;
 		for(int i=0;i<Res::GOLD;i++)
 			sr.res[i] += 100;
 		sr.res[Res::GOLD] += 100000; //100k
 		sendAndApply(&sr);
 	}
-	else if(message == "vcmieagles") //reveal FoW
+	else if(message == "vcmieagles" || message == "vcmiungoliant") //reveal or conceal FoW
 	{
 		FoWChange fc;
-		fc.mode = 1;
+		fc.mode = (message == "vcmieagles" ? 1 : 0);
 		fc.player = player;
 		auto  hlp_tab = new int3[gs->map->width * gs->map->height * (gs->map->twoLevel ? 2 : 1)];
 		int lastUnc = 0;
 		for(int i=0;i<gs->map->width;i++)
 			for(int j=0;j<gs->map->height;j++)
 				for(int k = 0; k < (gs->map->twoLevel ? 2 : 1); k++)
-					if(!gs->getPlayerTeam(fc.player)->fogOfWarMap.at(i).at(j).at(k))
+					if(!gs->getPlayerTeam(fc.player)->fogOfWarMap.at(i).at(j).at(k) || message == "vcmiungoliant")
 						hlp_tab[lastUnc++] = int3(i,j,k);
 		fc.tiles.insert(hlp_tab, hlp_tab + lastUnc);
 		delete [] hlp_tab;
@@ -4669,7 +4715,7 @@ void CGameHandler::handleTimeEvents()
 		{
 			auto color = PlayerColor(player);
 
-			PlayerState *pinfo = gs->getPlayer(color, false); //do not output error if player does not exist
+			const PlayerState * pinfo = getPlayer(color, false); //do not output error if player does not exist
 
 			if( pinfo  //player exists
 				&& (ev.players & 1<<player) //event is enabled to this player
@@ -4733,7 +4779,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
 	{
 		PlayerColor player = town->tempOwner;
 		CCastleEvent ev = town->events.front();
-		PlayerState *pinfo = gs->getPlayer(player, false);
+		const PlayerState * pinfo = getPlayer(player, false);
 
 		if( pinfo  //player exists
 			&& (ev.players & 1<<player.getNum()) //event is enabled to this player
@@ -4944,7 +4990,7 @@ bool CGameHandler::buildBoat( ObjectInstanceID objid )
 	const PlayerColor playerID = obj->o->tempOwner;
 	TResources boatCost;
 	obj->getBoatCost(boatCost);
-	TResources aviable = gs->getPlayer(playerID)->resources;
+	TResources aviable = getPlayer(playerID)->resources;
 
 	if (!aviable.canAfford(boatCost))
 	{
@@ -4989,7 +5035,7 @@ void CGameHandler::checkVictoryLossConditions(const std::set<PlayerColor> & play
 {
 	for(auto playerColor : playerColors)
 	{
-		if(gs->getPlayer(playerColor, false))
+		if(getPlayer(playerColor, false))
 			checkVictoryLossConditionsForPlayer(playerColor);
 	}
 }
@@ -5006,7 +5052,7 @@ void CGameHandler::checkVictoryLossConditionsForAll()
 
 void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 {
-	const PlayerState *p = gs->getPlayer(player);
+	const PlayerState * p = getPlayer(player);
 	if(p->status != EPlayerStatus::INGAME) return;
 
 	auto victoryLossCheckResult = gs->checkForVictoryAndLoss(player);
@@ -5027,10 +5073,10 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			//one player won -> all enemies lost
 			for (auto i = gs->players.cbegin(); i!=gs->players.cend(); i++)
 			{
-				if(i->first != player && gs->getPlayer(i->first)->status == EPlayerStatus::INGAME)
+				if(i->first != player && getPlayer(i->first)->status == EPlayerStatus::INGAME)
 				{
 					peg.player = i->first;
-					peg.victoryLossCheckResult = gameState()->getPlayerRelations(player, i->first) == PlayerRelations::ALLIES ?
+					peg.victoryLossCheckResult = getPlayerRelations(player, i->first) == PlayerRelations::ALLIES ?
 								victoryLossCheckResult : victoryLossCheckResult.invert(); // ally of winner
 
 					InfoWindow iw;
@@ -5119,7 +5165,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			//notify all players
 			for (auto pc : playerColors)
 			{
-				if (gs->getPlayer(pc)->status == EPlayerStatus::INGAME)
+				if(getPlayer(pc)->status == EPlayerStatus::INGAME)
 				{
 					InfoWindow iw;
 					getVictoryLossMessage(player, victoryLossCheckResult.invert(), iw);
@@ -5130,7 +5176,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			checkVictoryLossConditions(playerColors);
 		}
 
-		auto playerInfo = gs->getPlayer(gs->currentPlayer, false);
+		auto playerInfo = getPlayer(gs->currentPlayer, false);
 		// If we are called before the actual game start, there might be no current player
 		if (playerInfo && playerInfo->status != EPlayerStatus::INGAME)
 		{
@@ -6018,7 +6064,7 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 	if (hide)
 	{
 		std::unordered_set<int3, ShashInt3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
-		auto p = gs->getPlayer(player);
+		auto p = getPlayer(player);
 		for (auto h : p->heroes)
 		{
 			getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), h->tempOwner, -1);