فهرست منبع

Merge pull request #1789 from IvanSavenko/beta_fixes

Fixes for 1.2 release
Ivan Savenko 2 سال پیش
والد
کامیت
8a3e1b0230

+ 11 - 11
Mods/vcmi/config/vcmi/chinese.json

@@ -118,17 +118,17 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» 经 验 获 得 明 细 «\n\n生物类型 ................... : %s\n经验等级 ................. : %s (%i)\n经验点数 ............... : %i\n下一个等级所需经验 .. : %i\n每次战斗最大获得经验 ... : %i%% (%i)\n获得经验的生物数量 .... : %i\n最大招募数量\n不会丢失经验升级 .... : %i\n经验倍数 ........... : %.2f\n升级倍数 .............. : %.2f\n10级后经验值 ........ : %i\n最大招募数量下\n 升级到10级所需经验数量: %i",
-	"vcmi.stackExperience.rank.1" : "新兵 1级",
-	"vcmi.stackExperience.rank.2" : "列兵 2级",
-	"vcmi.stackExperience.rank.3" : "下士 3级",
-	"vcmi.stackExperience.rank.4" : "中士 4级",
-	"vcmi.stackExperience.rank.5" : "上士 5级",
-	"vcmi.stackExperience.rank.6" : "少尉 6级",
-	"vcmi.stackExperience.rank.7" : "中尉 7级",
-	"vcmi.stackExperience.rank.8" : "上尉 8级",
-	"vcmi.stackExperience.rank.9" : "少校 9级",
-	"vcmi.stackExperience.rank.10" : "中校 10级",
-	"vcmi.stackExperience.rank.11" : "上校 11级",
+	"vcmi.stackExperience.rank.0" : "新兵 1级",
+	"vcmi.stackExperience.rank.1" : "列兵 2级",
+	"vcmi.stackExperience.rank.2" : "下士 3级",
+	"vcmi.stackExperience.rank.3" : "中士 4级",
+	"vcmi.stackExperience.rank.4" : "上士 5级",
+	"vcmi.stackExperience.rank.5" : "少尉 6级",
+	"vcmi.stackExperience.rank.6" : "中尉 7级",
+	"vcmi.stackExperience.rank.7" : "上尉 8级",
+	"vcmi.stackExperience.rank.8" : "少校 9级",
+	"vcmi.stackExperience.rank.9" : "中校 10级",
+	"vcmi.stackExperience.rank.10" : "上校 11级",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "双击",
 	"core.bonus.ADDITIONAL_ATTACK.description": "可以攻击两次",

+ 11 - 11
Mods/vcmi/config/vcmi/english.json

@@ -142,17 +142,17 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» S t a c k   E x p e r i e n c e   D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
-	"vcmi.stackExperience.rank.1" : "Basic",
-	"vcmi.stackExperience.rank.2" : "Novice",
-	"vcmi.stackExperience.rank.3" : "Trained",
-	"vcmi.stackExperience.rank.4" : "Skilled",
-	"vcmi.stackExperience.rank.5" : "Proven",
-	"vcmi.stackExperience.rank.6" : "Veteran",
-	"vcmi.stackExperience.rank.7" : "Adept",
-	"vcmi.stackExperience.rank.8" : "Expert",
-	"vcmi.stackExperience.rank.9" : "Elite",
-	"vcmi.stackExperience.rank.10" : "Master",
-	"vcmi.stackExperience.rank.11" : "Ace",
+	"vcmi.stackExperience.rank.0" : "Basic",
+	"vcmi.stackExperience.rank.1" : "Novice",
+	"vcmi.stackExperience.rank.2" : "Trained",
+	"vcmi.stackExperience.rank.3" : "Skilled",
+	"vcmi.stackExperience.rank.4" : "Proven",
+	"vcmi.stackExperience.rank.5" : "Veteran",
+	"vcmi.stackExperience.rank.6" : "Adept",
+	"vcmi.stackExperience.rank.7" : "Expert",
+	"vcmi.stackExperience.rank.8" : "Elite",
+	"vcmi.stackExperience.rank.9" : "Master",
+	"vcmi.stackExperience.rank.10" : "Ace",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "Double Strike",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice",

+ 11 - 11
Mods/vcmi/config/vcmi/german.json

@@ -143,17 +143,17 @@
 	
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» D e t a i l s   z u r   S t a p e l e r f a h r u n g «\n\nKreatur-Typ ................... : %s\nErfahrungsrang ................. : %s (%i)\nErfahrungspunkte ............... : %i\nErfahrungspunkte für den nächsten Rang .. : %i\nMaximale Erfahrung pro Kampf ... : %i%% (%i)\nAnzahl der Kreaturen im Stapel .... : %i\nMaximale Anzahl neuer Rekruten\n ohne Verlust von aktuellem Rang .... : %i\nErfahrungs-Multiplikator ........... : %.2f\nUpgrade-Multiplikator .............. : %.2f\nErfahrung nach Rang 10 ........ : %i\nMaximale Anzahl der neuen Rekruten, die bei\n Rang 10 bei maximaler Erfahrung übrig sind : %i",
-	"vcmi.stackExperience.rank.1" : "Grundlagen",
-	"vcmi.stackExperience.rank.2" : "Neuling",
-	"vcmi.stackExperience.rank.3" : "Ausgebildet",
-	"vcmi.stackExperience.rank.4" : "Kompetent",
-	"vcmi.stackExperience.rank.5" : "Bewährt",
-	"vcmi.stackExperience.rank.6" : "Veteran",
-	"vcmi.stackExperience.rank.7" : "Gekonnt",
-	"vcmi.stackExperience.rank.8" : "Experte",
-	"vcmi.stackExperience.rank.9" : "Elite",
-	"vcmi.stackExperience.rank.10" : "Meister",
-	"vcmi.stackExperience.rank.11" : "Ass",
+	"vcmi.stackExperience.rank.0" : "Grundlagen",
+	"vcmi.stackExperience.rank.1" : "Neuling",
+	"vcmi.stackExperience.rank.2" : "Ausgebildet",
+	"vcmi.stackExperience.rank.3" : "Kompetent",
+	"vcmi.stackExperience.rank.4" : "Bewährt",
+	"vcmi.stackExperience.rank.5" : "Veteran",
+	"vcmi.stackExperience.rank.6" : "Gekonnt",
+	"vcmi.stackExperience.rank.7" : "Experte",
+	"vcmi.stackExperience.rank.8" : "Elite",
+	"vcmi.stackExperience.rank.9" : "Meister",
+	"vcmi.stackExperience.rank.10" : "Ass",
 	
 	"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",

+ 11 - 11
Mods/vcmi/config/vcmi/russian.json

@@ -145,17 +145,17 @@
 
 	// few strings from WoG used by vcmi
 	"vcmi.stackExperience.description" : "» О п ы т   с у щ е с т в «\n\nТип существа ................... : %s\nРанг опыта ................. : %s (%i)\nОчки опыта ............... : %i\nДо следующего .. : %i\nМаксимум за битву ... : %i%% (%i)\nЧисло в отряде .... : %i\nМаксимум новичков\n без потери ранга .... : %i\nМножитель опыта ........... : %.2f\nМножитель улучшения .......... : %.2f\nОпыт после 10 ранга ........ : %i\nМаксимум новичков для сохранения\n ранга 10 при максимальном опыте : %i",
-	"vcmi.stackExperience.rank.1" : "Рекрут",
-	"vcmi.stackExperience.rank.2" : "Новичок",
-	"vcmi.stackExperience.rank.3" : "Тренирован",
-	"vcmi.stackExperience.rank.4" : "Знающий",
-	"vcmi.stackExperience.rank.5" : "Подтвержденный",
-	"vcmi.stackExperience.rank.6" : "Ветеран",
-	"vcmi.stackExperience.rank.7" : "Адепт",
-	"vcmi.stackExperience.rank.8" : "Эксперт",
-	"vcmi.stackExperience.rank.9" : "Элита",
-	"vcmi.stackExperience.rank.10" : "Мастер",
-	"vcmi.stackExperience.rank.11" : "Ас",
+	"vcmi.stackExperience.rank.0" : "Рекрут",
+	"vcmi.stackExperience.rank.1" : "Новичок",
+	"vcmi.stackExperience.rank.2" : "Тренирован",
+	"vcmi.stackExperience.rank.3" : "Знающий",
+	"vcmi.stackExperience.rank.4" : "Подтвержденный",
+	"vcmi.stackExperience.rank.5" : "Ветеран",
+	"vcmi.stackExperience.rank.6" : "Адепт",
+	"vcmi.stackExperience.rank.7" : "Эксперт",
+	"vcmi.stackExperience.rank.8" : "Элита",
+	"vcmi.stackExperience.rank.9" : "Мастер",
+	"vcmi.stackExperience.rank.10" : "Ас",
 
 	"core.bonus.ADDITIONAL_ATTACK.name": "Двойной удар",
 	"core.bonus.ADDITIONAL_ATTACK.description": "Бьет дважды",

+ 11 - 11
Mods/vcmi/config/vcmi/spanish.json

@@ -131,17 +131,17 @@
 	
 	// few strings from WoG used by vcmi
     "vcmi.stackExperience.description" : "» D e t a l l e s  d e  E x p e r i e n c i a  d e l  G r u p o «\n\nTipo de Criatura ................ : %s\nRango de Experiencia ............ : %s (%i)\nPuntos de Experiencia ............ : %i\nPuntos de Experiencia para el\nSiguiente Rango ............... : %i\nExperiencia Máxima por Batalla .. : %i%% (%i)\nNúmero de Criaturas en el grupo .. : %i\nMáximo de Nuevos Reclutas sin\nPerder el Rango Actual ......... : %i\nMultiplicador de Experiencia .... : %.2f\nMultiplicador de Actualización .. : %.2f\nExperiencia después del Rango 10 : %i\nMáximo de Nuevos Reclutas para\nMantener el Rango 10 si\nEstá en la Experiencia Máxima : %i",
-    "vcmi.stackExperience.rank.1" : "Básico",
-    "vcmi.stackExperience.rank.2" : "Novato",
-    "vcmi.stackExperience.rank.3" : "Entrenado",
-    "vcmi.stackExperience.rank.4" : "Hábil",
-    "vcmi.stackExperience.rank.5" : "Probado",
-    "vcmi.stackExperience.rank.6" : "Veterano",
-    "vcmi.stackExperience.rank.7" : "Experto",
-    "vcmi.stackExperience.rank.8" : "Experto Superior",
-    "vcmi.stackExperience.rank.9" : "Élite",
-    "vcmi.stackExperience.rank.10" : "Maestro",
-    "vcmi.stackExperience.rank.11" : "As",
+    "vcmi.stackExperience.rank.0" : "Básico",
+    "vcmi.stackExperience.rank.1" : "Novato",
+    "vcmi.stackExperience.rank.2" : "Entrenado",
+    "vcmi.stackExperience.rank.3" : "Hábil",
+    "vcmi.stackExperience.rank.4" : "Probado",
+    "vcmi.stackExperience.rank.5" : "Veterano",
+    "vcmi.stackExperience.rank.6" : "Experto",
+    "vcmi.stackExperience.rank.7" : "Experto Superior",
+    "vcmi.stackExperience.rank.8" : "Élite",
+    "vcmi.stackExperience.rank.9" : "Maestro",
+    "vcmi.stackExperience.rank.10" : "As",
 
     "core.bonus.ADDITIONAL_ATTACK.name": "Doble Ataque",
     "core.bonus.ADDITIONAL_ATTACK.description": "Ataca dos veces",

+ 11 - 11
Mods/vcmi/config/vcmi/ukrainian.json

@@ -295,15 +295,15 @@
 	"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Не може стріляти по цілях на відстані більше ${val} гексів",
 	
 	"vcmi.stackExperience.description" : "» S t a c k   E x p e r i e n c e   D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
-	"vcmi.stackExperience.rank.1" :  "Початковий",
-	"vcmi.stackExperience.rank.2" :  "Новачок",
-	"vcmi.stackExperience.rank.3" :  "Підготовлений",
-	"vcmi.stackExperience.rank.4" :  "Досвідчений",
-	"vcmi.stackExperience.rank.5" :  "Випробуваний",
-	"vcmi.stackExperience.rank.6" :  "Ветеран",
-	"vcmi.stackExperience.rank.7" :  "Адепт",
-	"vcmi.stackExperience.rank.8" :  "Експерт",
-	"vcmi.stackExperience.rank.9" :  "Еліта",
-	"vcmi.stackExperience.rank.10" : "Майстер",
-	"vcmi.stackExperience.rank.11" : "Профі",
+	"vcmi.stackExperience.rank.0" :  "Початковий",
+	"vcmi.stackExperience.rank.1" :  "Новачок",
+	"vcmi.stackExperience.rank.2" :  "Підготовлений",
+	"vcmi.stackExperience.rank.3" :  "Досвідчений",
+	"vcmi.stackExperience.rank.4" :  "Випробуваний",
+	"vcmi.stackExperience.rank.5" :  "Ветеран",
+	"vcmi.stackExperience.rank.6" :  "Адепт",
+	"vcmi.stackExperience.rank.7" :  "Експерт",
+	"vcmi.stackExperience.rank.8" :  "Еліта",
+	"vcmi.stackExperience.rank.9" : "Майстер",
+	"vcmi.stackExperience.rank.10" : "Профі",
 }

+ 40 - 13
client/CMusicHandler.cpp

@@ -142,11 +142,13 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound, bool cache)
 
 int CSoundHandler::ambientDistToVolume(int distance) const
 {
-	if(distance >= ambientConfig["distances"].Vector().size())
+	const auto & distancesVector = ambientConfig["distances"].Vector();
+
+	if(distance >= distancesVector.size())
 		return 0;
 
-	int volume = static_cast<int>(ambientConfig["distances"].Vector()[distance].Integer());
-	return volume * (int)ambientConfig["volume"].Integer() * getVolume() / 10000;
+	int volume = static_cast<int>(distancesVector[distance].Integer());
+	return volume * (int)ambientConfig["volume"].Integer() / 100;
 }
 
 void CSoundHandler::ambientStopSound(std::string soundId)
@@ -211,7 +213,20 @@ void CSoundHandler::setVolume(ui32 percent)
 	CAudioBase::setVolume(percent);
 
 	if (initialized)
+	{
 		setChannelVolume(-1, volume);
+
+		for (auto const & channel : channelVolumes)
+			updateChannelVolume(channel.first);
+	}
+}
+
+void CSoundHandler::updateChannelVolume(int channel)
+{
+	if (channelVolumes.count(channel))
+		setChannelVolume(channel, getVolume() * channelVolumes[channel] / 100);
+	else
+		setChannelVolume(channel, getVolume());
 }
 
 // Sets the sound volume, from 0 (mute) to 100
@@ -258,29 +273,40 @@ void CSoundHandler::ambientUpdateChannels(std::map<std::string, int> soundsArg)
 	std::vector<std::string> stoppedSounds;
 	for(auto & pair : ambientChannels)
 	{
-		if(!vstd::contains(soundsArg, pair.first))
+		const std::string & soundId = pair.first;
+		const int channel = pair.second;
+
+		if(!vstd::contains(soundsArg, soundId))
 		{
-			ambientStopSound(pair.first);
-			stoppedSounds.push_back(pair.first);
+			ambientStopSound(soundId);
+			stoppedSounds.push_back(soundId);
 		}
 		else
 		{
-			int volume = ambientDistToVolume(soundsArg[pair.first]);
-			CCS->soundh->setChannelVolume(pair.second, volume);
+			int volume = ambientDistToVolume(soundsArg[soundId]);
+			channelVolumes[channel] = volume;
+			updateChannelVolume(channel);
 		}
 	}
 	for(auto soundId : stoppedSounds)
+	{
+		channelVolumes.erase(ambientChannels[soundId]);
 		ambientChannels.erase(soundId);
+	}
 
 	for(auto & pair : soundsArg)
 	{
-		if(!vstd::contains(ambientChannels, pair.first))
+		const std::string & soundId = pair.first;
+		const int distance = pair.second;
+
+		if(!vstd::contains(ambientChannels, soundId))
 		{
-			int channel = CCS->soundh->playSound(pair.first, -1);
-			int volume = ambientDistToVolume(pair.second);
+			int channel = playSound(soundId, -1);
+			int volume = ambientDistToVolume(distance);
+			channelVolumes[channel] = volume;
 
-			CCS->soundh->setChannelVolume(channel, volume);
-			CCS->soundh->ambientChannels.insert(std::make_pair(pair.first, channel));
+			updateChannelVolume(channel);
+			ambientChannels[soundId] = channel;
 		}
 	}
 }
@@ -293,6 +319,7 @@ void CSoundHandler::ambientStopAllChannels()
 	{
 		ambientStopSound(ch.first);
 	}
+	channelVolumes.clear();
 	ambientChannels.clear();
 }
 

+ 3 - 0
client/CMusicHandler.h

@@ -51,9 +51,12 @@ private:
 
 	int ambientDistToVolume(int distance) const;
 	void ambientStopSound(std::string soundId);
+	void updateChannelVolume(int channel);
 
 	const JsonNode ambientConfig;
+
 	std::map<std::string, int> ambientChannels;
+	std::map<int, int> channelVolumes;
 
 public:
 	CSoundHandler();

+ 5 - 0
client/battle/BattleActionsController.cpp

@@ -119,7 +119,10 @@ BattleActionsController::BattleActionsController(BattleInterface & owner):
 void BattleActionsController::endCastingSpell()
 {
 	if(heroSpellToCast)
+	{
 		heroSpellToCast.reset();
+		owner.windowObject->blockUI(false);
+	}
 
 	if(owner.stacksController->getActiveStack())
 		possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); //restore actions after they were cleared
@@ -287,6 +290,8 @@ void BattleActionsController::castThisSpell(SpellID spellID)
 		possibleActions.push_back (spellSelMode); //only this one action can be performed at the moment
 		GH.fakeMouseMove();//update cursor
 	}
+
+	owner.windowObject->blockUI(true);
 }
 
 const CSpell * BattleActionsController::getHeroSpellToCast( ) const

+ 9 - 1
client/battle/BattleAnimationClasses.cpp

@@ -756,7 +756,15 @@ void ShootingAnimation::createProjectile(const Point & from, const Point & dest)
 uint32_t ShootingAnimation::getAttackClimaxFrame() const
 {
 	const CCreature *shooterInfo = getCreature();
-	return shooterInfo->animation.attackClimaxFrame;
+
+	uint32_t maxFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
+	uint32_t climaxFrame = shooterInfo->animation.attackClimaxFrame;
+	uint32_t selectedFrame = vstd::clamp(shooterInfo->animation.attackClimaxFrame, 1, maxFrames);
+
+	if (climaxFrame != selectedFrame)
+		logGlobal->warn("Shooter %s has ranged attack climax frame set to %d, but only %d available!", shooterInfo->getNamePluralTranslated(), climaxFrame, maxFrames);
+
+	return selectedFrame - 1; // H3 counts frames from 1
 }
 
 ECreatureAnimType ShootingAnimation::getUpwardsGroup() const

+ 2 - 5
client/battle/BattleWindow.cpp

@@ -541,11 +541,8 @@ void BattleWindow::blockUI(bool on)
 		w->block(on || owner.tacticsMode);
 	if(auto w = widget<CButton>("alternativeAction"))
 		w->block(on || owner.tacticsMode);
-
-	// block only if during enemy turn and auto-fight is off
-	// otherwise - crash on accessing non-exisiting active stack
-	if(auto w = widget<CButton>("options"))
-		w->block(!owner.curInt->isAutoFightOn && !owner.stacksController->getActiveStack());
+	if(auto w = widget<CButton>("autofight"))
+		w->block(owner.actionsController->spellcastingModeActive());
 
 	auto btactEnd = widget<CButton>("tacticEnd");
 	auto btactNext = widget<CButton>("tacticNext");

+ 1 - 0
client/windows/CCastleInterface.cpp

@@ -727,6 +727,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 				switch(subID)
 				{
 				case BuildingSubID::NONE:
+						enterBuilding(building);
 						break;
 
 				case BuildingSubID::MYSTIC_POND:

+ 2 - 2
config/heroClasses.json

@@ -104,7 +104,7 @@
 		"index": 10,
 		"faction" : "dungeon",
 		"defaultTavern" : 5,
-		"affinity" : "might",
+		"affinity" : "magic",
 		"commander" : "medusaQueen",
 		"mapObject" : { "templates" : { "default" : { "animation" : "AH11_.def", "editorAnimation": "AH11_E.def" } } },
 		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }
@@ -114,7 +114,7 @@
 		"index": 11,
 		"faction" : "dungeon",
 		"defaultTavern" : 5,
-		"affinity" : "magic",
+		"affinity" : "might",
 		"commander" : "medusaQueen",
 		"mapObject" : { "templates" : { "default" : { "animation" : "AH10_.def", "editorAnimation": "AH10_E.def" } } },
 		"animation":  { "battle" : { "male" : "CH010.DEF",  "female" : "CH11.DEF" } }

+ 1 - 1
config/objects/generic.json

@@ -215,7 +215,7 @@
 		"handler" : "whirlpool",
 		"base" : {
 			"sounds" : {
-				"ambient" : ["LOOPWHIRL"],
+				"ambient" : ["LOOPWHIR"],
 				"visit" : ["DANGER"]
 			}
 		},

+ 5 - 1
config/schemas/obstacle.json

@@ -32,7 +32,11 @@
 		},
 		"animation": {
 			"type": "string",
-			"description": "Image resource"
+			"description": "Image resource",
+			"anyOf" : [
+				{ "format" : "defFile" },
+				{ "format" : "imageFile" }
+			]
 		},
 		"unknown": {
 			"type": "number",

+ 3 - 0
server/CQuery.cpp

@@ -380,6 +380,9 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
 	if(auto upgrade = dynamic_ptr_cast<UpgradeCreature>(pack))
 		return !vstd::contains(ourIds, upgrade->id);
 
+	if(auto formation = dynamic_ptr_cast<SetFormation>(pack))
+		return !vstd::contains(ourIds, formation->hid);
+
 	return CDialogQuery::blocksPack(pack);
 }