Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/develop' into biome_system

Tomasz Zieliński 1 anno fa
parent
commit
d9a598ad9c

+ 3 - 0
AI/VCAI/VCAI.cpp

@@ -2031,6 +2031,9 @@ void VCAI::tryRealize(Goals::Explore & g)
 
 void VCAI::tryRealize(Goals::RecruitHero & g)
 {
+	if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST)
+		throw cannotFulfillGoalException("Not enough gold to recruit hero!");
+
 	if(const CGTownInstance * t = findTownWithTavern())
 	{
 		recruitHero(t, true);

+ 5 - 0
client/CPlayerInterface.cpp

@@ -194,6 +194,11 @@ void CPlayerInterface::playerEndsTurn(PlayerColor player)
 				GH.windows().popWindows(1);
 		}
 
+		if(castleInt)
+			castleInt->close();
+
+		castleInt = nullptr;
+
 		// remove all pending dialogs that do not expect query answer
 		vstd::erase_if(dialogs, [](const std::shared_ptr<CInfoWindow> & window){
 			return window->ID == QueryID::NONE;

+ 1 - 1
client/globalLobby/GlobalLobbyClient.cpp

@@ -141,7 +141,7 @@ void GlobalLobbyClient::receiveChatHistory(const JsonNode & json)
 
 		chatHistory[channelKey].push_back(message);
 
-		if(lobbyWindowPtr)
+		if(lobbyWindowPtr && lobbyWindowPtr->isChannelOpen(channelType, channelName))
 			lobbyWindowPtr->onGameChatMessage(message.displayName, message.messageText, message.timeFormatted, channelType, channelName);
 	}
 }

+ 2 - 1
client/globalLobby/GlobalLobbyLoginWindow.cpp

@@ -38,7 +38,7 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
 
 	MetaString loginAs;
 	loginAs.appendTextID("vcmi.lobby.login.as");
-	loginAs.replaceTextID(CSH->getGlobalLobby().getAccountDisplayName());
+	loginAs.replaceRawString(CSH->getGlobalLobby().getAccountDisplayName());
 
 	filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
 	labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title"));
@@ -65,6 +65,7 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
 	{
 		buttonLogin->block(true);
 		toggleMode->setSelected(0);
+		onLoginModeChanged(0); // call it manually to disable widgets - toggleMode will not emit this call if this is currenly selected option
 	}
 	else
 		toggleMode->setSelected(1);

+ 1 - 0
client/globalLobby/GlobalLobbyWindow.cpp

@@ -64,6 +64,7 @@ void GlobalLobbyWindow::doOpenChannel(const std::string & channelType, const std
 	widget->getGameChatHeader()->setText(text.toString());
 
 	// Update currently selected item in UI
+	// WARNING: this invalidates function parameters since some of them are members of objects that will be destroyed by reset
 	widget->getAccountList()->reset();
 	widget->getChannelList()->reset();
 	widget->getMatchList()->reset();

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -119,7 +119,7 @@ CBonusSelection::CBonusSelection()
 	if (!getCampaign()->getMusic().empty())
 		CCS->musich->playMusic( getCampaign()->getMusic(), true, false);
 
-	if(settings["general"]["enableUiEnhancements"].Bool())
+	if(CSH->getState() != EClientState::GAMEPLAY && settings["general"]["enableUiEnhancements"].Bool())
 	{
 		tabExtraOptions = std::make_shared<ExtraOptionsTab>();
 		tabExtraOptions->recActions = UPDATE | SHOWALL | LCLICK | RCLICK_POPUP;

+ 1 - 1
config/heroes/conflux.json

@@ -176,7 +176,7 @@
 			"bonuses" : {
 				"damage" : {
 					"type" : "CREATURE_DAMAGE",
-					"subtype" : "creatureDamageMin",
+					"subtype" : "creatureDamageBoth",
 					"val" : 5
 				},
 				"attack" : {

+ 4 - 1
docs/modders/Readme.md

@@ -31,7 +31,10 @@ Minimalistic version of this file:
 ``` javascript
 {
     "name" : "My test mod",
-    "description" : "My test mod that add a lot of useless stuff into the game"
+    "description" : "My test mod that add a lot of useless stuff into the game",
+    "version" : "1.00",
+	"modType" : "Graphical",	
+	"contact" : "http://www.contact.example.com"
 }
 ```
 

+ 1 - 1
lib/battle/CBattleInfoCallback.cpp

@@ -742,7 +742,7 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
 {
 	RETURN_IF_NOT_BATTLE({});
 	auto reachability = battleGetDistances(attacker, attacker->getPosition());
-	int getMovementRange = reachability[attackerPosition];
+	int getMovementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0;
 	return battleEstimateDamage(attacker, defender, getMovementRange, retaliationDmg);
 }
 

+ 1 - 1
lib/campaign/CampaignState.cpp

@@ -369,7 +369,7 @@ JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero) const
 CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node, CMap * map) const
 {
 	JsonDeserializer handler(nullptr, const_cast<JsonNode&>(node));
-	auto * hero = new CGHeroInstance(map->cb);
+	auto * hero = new CGHeroInstance(map ? map->cb : nullptr);
 	hero->ID = Obj::HERO;
 	hero->serializeJsonOptions(handler);
 	if (map)

+ 0 - 1
lib/gameState/CGameState.cpp

@@ -642,7 +642,6 @@ void CGameState::initHeroes()
 			boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
 
 			map->objects.emplace_back(boat);
-			map->addBlockVisTiles(boat);
 
 			hero->attachToBoat(boat);
 		}

+ 37 - 1
lib/mapping/CMap.cpp

@@ -572,16 +572,18 @@ void CMap::removeObject(CGObjectInstance * obj)
 	instanceNames.erase(obj->instanceName);
 
 	//update indeces
+
 	auto iter = std::next(objects.begin(), obj->id.getNum());
 	iter = objects.erase(iter);
 	for(int i = obj->id.getNum(); iter != objects.end(); ++i, ++iter)
 	{
 		(*iter)->id = ObjectInstanceID(i);
 	}
-	
+
 	obj->afterRemoveFromMap(this);
 
 	//TOOD: Clean artifact instances (mostly worn by hero?) and quests related to this object
+	//This causes crash with undo/redo in editor
 }
 
 bool CMap::isWaterMap() const
@@ -704,4 +706,38 @@ void CMap::resolveQuestIdentifiers()
 	questIdentifierToId.clear();
 }
 
+void CMap::reindexObjects()
+{
+	// Only reindex at editor / RMG operations
+
+	std::sort(objects.begin(), objects.end(), [](const CGObjectInstance * lhs, const CGObjectInstance * rhs)
+	{
+		// Obstacles first, then visitable, at the end - removable
+
+		if (!lhs->isVisitable() && rhs->isVisitable())
+			return true;
+		if (lhs->isVisitable() && !rhs->isVisitable())
+			return false;
+
+		// Special case for Windomill - draw on top of other objects
+		if (lhs->ID != Obj::WINDMILL && rhs->ID == Obj::WINDMILL)
+			return true;
+		if (lhs->ID == Obj::WINDMILL && rhs->ID != Obj::WINDMILL)
+			return false;
+
+		if (!lhs->isRemovable() && rhs->isRemovable())
+			return true;
+		if (lhs->isRemovable() && !rhs->isRemovable())
+			return false;
+
+		return lhs->pos.y < rhs->pos.y;
+	});
+
+	// instanceNames don't change
+	for (size_t i = 0; i < objects.size(); ++i)
+	{
+		objects[i]->id = ObjectInstanceID(i);
+	}
+}
+
 VCMI_LIB_NAMESPACE_END

+ 2 - 0
lib/mapping/CMap.h

@@ -128,6 +128,8 @@ public:
 	void resetStaticData();
 	void resolveQuestIdentifiers();
 
+	void reindexObjects();
+
 	ui32 checksum;
 	std::vector<Rumor> rumors;
 	std::vector<DisposedHero> disposedHeroes;

+ 2 - 0
lib/mapping/CMapOperation.cpp

@@ -47,6 +47,8 @@ CComposedOperation::CComposedOperation(CMap* map) : CMapOperation(map)
 
 void CComposedOperation::execute()
 {
+	// FIXME: Only reindex objects at the end of composite operation
+
 	for(auto & operation : operations)
 	{
 		operation->execute();

+ 1 - 0
lib/rmg/CMapGenerator.cpp

@@ -419,6 +419,7 @@ void CMapGenerator::fillZones()
 	auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
 
 	map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths()->getTiles(), rand);
+	map->getMap(this).reindexObjects();
 
 	logGlobal->info("Zones filled successfully");
 

+ 1 - 1
lobby/LobbyDatabase.cpp

@@ -288,7 +288,7 @@ void LobbyDatabase::prepareStatements()
 	isAccountNameExistsStatement = database->prepare(R"(
 		SELECT COUNT(displayName)
 		FROM accounts
-		WHERE displayName = ?
+		WHERE displayName = ? COLLATE NOCASE
 	)");
 }