Browse Source

Merge pull request #3372 from IvanSavenko/bugfixing

[1.4.2] Bugfixing
Ivan Savenko 1 year ago
parent
commit
08a4b67cd7

+ 2 - 0
client/PlayerLocalState.cpp

@@ -239,6 +239,8 @@ void PlayerLocalState::swapWanderingHero(int pos1, int pos2)
 {
 	assert(wanderingHeroes[pos1] && wanderingHeroes[pos2]);
 	std::swap(wanderingHeroes[pos1], wanderingHeroes[pos2]);
+
+	adventureInt->onHeroOrderChanged();
 }
 
 const std::vector<const CGTownInstance *> & PlayerLocalState::getOwnedTowns()

+ 5 - 0
client/adventureMap/AdventureMapInterface.cpp

@@ -331,6 +331,11 @@ void AdventureMapInterface::onTownOrderChanged()
 	widget->getTownList()->updateWidget();
 }
 
+void AdventureMapInterface::onHeroOrderChanged()
+{
+	widget->getHeroList()->updateWidget();
+}
+
 void AdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
 {
 	if (positions)

+ 3 - 0
client/adventureMap/AdventureMapInterface.h

@@ -149,6 +149,9 @@ public:
 	/// Called when town order changes
 	void onTownOrderChanged();
 
+	/// Called when hero order changes
+	void onHeroOrderChanged();
+
 	/// Called when map audio should be paused, e.g. on combat or town screen access
 	void onAudioPaused();
 

+ 2 - 4
client/lobby/CBonusSelection.cpp

@@ -273,17 +273,15 @@ void CBonusSelection::createBonusesIcons()
 		}
 
 		case CampaignBonusType::HERO:
-
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
-			desc.replaceTextID(TextIdentifier("core", "genrltxt", "capColors", bonDescs[i].info1).get());
 			if(bonDescs[i].info2 == 0xFFFF)
 			{
-				desc.replaceLocalString(EMetaText::GENERAL_TXT, 101);
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 720); // Start with random hero
 				picNumber = -1;
 				picName = "CBONN1A3.BMP";
 			}
 			else
 			{
+				desc.appendLocalString(EMetaText::GENERAL_TXT, 715); // Start with %s
 				desc.replaceTextID(CGI->heroh->objects[bonDescs[i].info2]->getNameTextID());
 			}
 			break;

+ 6 - 3
client/lobby/CSelectionBase.cpp

@@ -135,7 +135,7 @@ InfoCard::InfoCard()
 	Rect descriptionRect(26, 149, 320, 115);
 	mapDescription = std::make_shared<CTextBox>("", descriptionRect, 1);
 	playerListBg = std::make_shared<CPicture>(ImagePath::builtin("CHATPLUG.bmp"), 16, 276);
-	chat = std::make_shared<CChatBox>(Rect(26, 132, 340, 132));
+	chat = std::make_shared<CChatBox>(Rect(18, 126, 335, 143));
 
 	if(SEL->screenType == ESelectionScreen::campaignList)
 	{
@@ -332,9 +332,12 @@ CChatBox::CChatBox(const Rect & rect)
 	setRedrawParent(true);
 
 	const int height = static_cast<int>(graphics->fonts[FONT_SMALL]->getLineHeight());
-	inputBox = std::make_shared<CTextInput>(Rect(0, rect.h - height, rect.w, height), EFonts::FONT_SMALL, 0);
+	Rect textInputArea(1, rect.h - height, rect.w - 1, height);
+	Rect chatHistoryArea(3, 1, rect.w - 3, rect.h - height - 1);
+	inputBackground = std::make_shared<TransparentFilledRectangle>(textInputArea, ColorRGBA(0,0,0,192));
+	inputBox = std::make_shared<CTextInput>(textInputArea, EFonts::FONT_SMALL, 0);
 	inputBox->removeUsedEvents(KEYBOARD);
-	chatHistory = std::make_shared<CTextBox>("", Rect(0, 0, rect.w, rect.h - height), 1);
+	chatHistory = std::make_shared<CTextBox>("", chatHistoryArea, 1);
 
 	chatHistory->label->color = Colors::GREEN;
 }

+ 2 - 0
client/lobby/CSelectionBase.h

@@ -33,6 +33,7 @@ class CChatBox;
 class CLabel;
 class CFlagBox;
 class CLabelGroup;
+class TransparentFilledRectangle;
 
 class ISelectionScreenInfo
 {
@@ -122,6 +123,7 @@ class CChatBox : public CIntObject
 public:
 	std::shared_ptr<CTextBox> chatHistory;
 	std::shared_ptr<CTextInput> inputBox;
+	std::shared_ptr<TransparentFilledRectangle> inputBackground;
 
 	CChatBox(const Rect & rect);
 

+ 3 - 3
client/widgets/TextControls.cpp

@@ -375,7 +375,7 @@ void CTextBox::setText(const std::string & text)
 	else if(slider)
 	{
 		// decrease width again if slider still used
-		label->pos.w = pos.w - 32;
+		label->pos.w = pos.w - 16;
 		assert(label->pos.w > 0);
 		label->setText(text);
 		slider->setAmount(label->textSize.y);
@@ -383,12 +383,12 @@ void CTextBox::setText(const std::string & text)
 	else if(label->textSize.y > label->pos.h)
 	{
 		// create slider and update widget
-		label->pos.w = pos.w - 32;
+		label->pos.w = pos.w - 16;
 		assert(label->pos.w > 0);
 		label->setText(text);
 
 		OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
-		slider = std::make_shared<CSlider>(Point(pos.w - 32, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1),
+		slider = std::make_shared<CSlider>(Point(pos.w - 16, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1),
 			label->pos.h, label->textSize.y, 0, Orientation::VERTICAL, CSlider::EStyle(sliderStyle));
 		slider->setScrollStep((int)graphics->fonts[label->font]->getLineHeight());
 		slider->setPanningStep(1);

+ 35 - 25
lib/JsonNode.cpp

@@ -310,63 +310,73 @@ JsonMap & JsonNode::Struct()
 const bool boolDefault = false;
 bool JsonNode::Bool() const
 {
-	if (getType() == JsonType::DATA_NULL)
-		return boolDefault;
-	assert(getType() == JsonType::DATA_BOOL);
-	return std::get<bool>(data);
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_BOOL);
+
+	if (getType() == JsonType::DATA_BOOL)
+		return std::get<bool>(data);
+
+	return boolDefault;
 }
 
 const double floatDefault = 0;
 double JsonNode::Float() const
 {
-	if(getType() == JsonType::DATA_NULL)
-		return floatDefault;
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
+
+	if(getType() == JsonType::DATA_FLOAT)
+		return std::get<double>(data);
 
 	if(getType() == JsonType::DATA_INTEGER)
 		return static_cast<double>(std::get<si64>(data));
 
-	assert(getType() == JsonType::DATA_FLOAT);
-	return std::get<double>(data);
+	return floatDefault;
 }
 
-const si64 integetDefault = 0;
+const si64 integerDefault = 0;
 si64 JsonNode::Integer() const
 {
-	if(getType() == JsonType::DATA_NULL)
-		return integetDefault;
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_INTEGER || getType() == JsonType::DATA_FLOAT);
+
+	if(getType() == JsonType::DATA_INTEGER)
+		return std::get<si64>(data);
 
 	if(getType() == JsonType::DATA_FLOAT)
 		return static_cast<si64>(std::get<double>(data));
 
-	assert(getType() == JsonType::DATA_INTEGER);
-	return std::get<si64>(data);
+	return integerDefault;
 }
 
 const std::string stringDefault = std::string();
 const std::string & JsonNode::String() const
 {
-	if (getType() == JsonType::DATA_NULL)
-		return stringDefault;
-	assert(getType() == JsonType::DATA_STRING);
-	return std::get<std::string>(data);
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRING);
+
+	if (getType() == JsonType::DATA_STRING)
+		return std::get<std::string>(data);
+
+	return stringDefault;
 }
 
 const JsonVector vectorDefault = JsonVector();
 const JsonVector & JsonNode::Vector() const
 {
-	if (getType() == JsonType::DATA_NULL)
-		return vectorDefault;
-	assert(getType() == JsonType::DATA_VECTOR);
-	return std::get<JsonVector>(data);
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_VECTOR);
+
+	if (getType() == JsonType::DATA_VECTOR)
+		return std::get<JsonVector>(data);
+
+	return vectorDefault;
 }
 
 const JsonMap mapDefault = JsonMap();
 const JsonMap & JsonNode::Struct() const
 {
-	if (getType() == JsonType::DATA_NULL)
-		return mapDefault;
-	assert(getType() == JsonType::DATA_STRUCT);
-	return std::get<JsonMap>(data);
+	assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRUCT);
+
+	if (getType() == JsonType::DATA_STRUCT)
+		return std::get<JsonMap>(data);
+
+	return mapDefault;
 }
 
 JsonNode & JsonNode::operator[](const std::string & child)

+ 2 - 2
server/CGameHandler.cpp

@@ -2225,12 +2225,12 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
 
 bool CGameHandler::hasPlayerAt(PlayerColor player, std::shared_ptr<CConnection> c) const
 {
-	return connections.at(player).count(c);
+	return connections.count(player) && connections.at(player).count(c);
 }
 
 bool CGameHandler::hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const
 {
-	return connections.at(left) == connections.at(right);
+	return connections.count(left) && connections.count(right) && connections.at(left) == connections.at(right);
 }
 
 bool CGameHandler::disbandCreature(ObjectInstanceID id, SlotID pos)

+ 37 - 5
server/processors/TurnOrderProcessor.cpp

@@ -9,6 +9,7 @@
  */
 #include "StdInc.h"
 #include "TurnOrderProcessor.h"
+#include "PlayerMessageProcessor.h"
 
 #include "../queries/QueriesProcessor.h"
 #include "../queries/MapQueries.h"
@@ -35,9 +36,9 @@ int TurnOrderProcessor::simturnsTurnsMinLimit() const
 	return gameHandler->getStartInfo()->simturnsInfo.requiredTurns;
 }
 
-void TurnOrderProcessor::updateContactStatus()
+std::vector<TurnOrderProcessor::PlayerPair> TurnOrderProcessor::computeContactStatus() const
 {
-	blockedContacts.clear();
+	std::vector<PlayerPair> result;
 
 	assert(actedPlayers.empty());
 	assert(actingPlayers.empty());
@@ -50,9 +51,40 @@ void TurnOrderProcessor::updateContactStatus()
 				continue;
 
 			if (computeCanActSimultaneously(left, right))
-				blockedContacts.push_back({left, right});
+				result.push_back({left, right});
 		}
 	}
+	return result;
+}
+
+void TurnOrderProcessor::updateAndNotifyContactStatus()
+{
+	auto newBlockedContacts = computeContactStatus();
+
+	if (newBlockedContacts.empty())
+	{
+		// Simturns between all players have ended - send single global notification
+		if (!blockedContacts.empty())
+			gameHandler->playerMessages->broadcastSystemMessage("Simultaneous turns have ended");
+	}
+	else
+	{
+		// Simturns between some players have ended - notify each pair
+		for (auto const & contact : blockedContacts)
+		{
+			if (vstd::contains(newBlockedContacts, contact))
+				continue;
+
+			MetaString message;
+			message.appendRawString("Simultaneous turns between players %s and %s have ended"); // FIXME: we should send MetaString itself and localize it on client side
+			message.replaceName(contact.a);
+			message.replaceName(contact.b);
+
+			gameHandler->playerMessages->broadcastSystemMessage(message.toString());
+		}
+	}
+
+	blockedContacts = newBlockedContacts;
 }
 
 bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) const
@@ -204,7 +236,7 @@ void TurnOrderProcessor::doStartNewDay()
 	std::swap(actedPlayers, awaitingPlayers);
 
 	gameHandler->onNewTurn();
-	updateContactStatus();
+	updateAndNotifyContactStatus();
 	tryStartTurnsForPlayers();
 }
 
@@ -301,7 +333,7 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
 void TurnOrderProcessor::onGameStarted()
 {
 	if (actingPlayers.empty())
-		updateContactStatus();
+		blockedContacts = computeContactStatus();
 
 	// this may be game load - send notification to players that they can act
 	auto actingPlayersCopy = actingPlayers;

+ 3 - 1
server/processors/TurnOrderProcessor.h

@@ -62,7 +62,9 @@ class TurnOrderProcessor : boost::noncopyable
 	/// Starts turn for all players that can start turn
 	void tryStartTurnsForPlayers();
 
-	void updateContactStatus();
+	void updateAndNotifyContactStatus();
+
+	std::vector<PlayerPair> computeContactStatus() const;
 
 	void doStartNewDay();
 	void doStartPlayerTurn(PlayerColor which);