瀏覽代碼

Merge branch 'develop' into any-hex-shooting

Dydzio 1 年之前
父節點
當前提交
230893f423

+ 1 - 0
Mods/vcmi/config/vcmi/english.json

@@ -21,6 +21,7 @@
 	"vcmi.adventureMap.playerAttacked"                   : "Player has been attacked: %s",
 	"vcmi.adventureMap.moveCostDetails"                  : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
 	"vcmi.adventureMap.moveCostDetailsNoTurns"           : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
+	"vcmi.adventureMap.movementPointsHeroInfo" 			 : "(Movement points: %REMAINING / %POINTS)",
 	"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sorry, replay opponent turn is not implemented yet!",
 
 	"vcmi.capitalColors.0" : "Red",

+ 1 - 0
Mods/vcmi/config/vcmi/polish.json

@@ -20,6 +20,7 @@
 	"vcmi.adventureMap.playerAttacked"         : "Gracz został zaatakowany: %s",
 	"vcmi.adventureMap.moveCostDetails"        : "Punkty ruchu - Koszt: %TURNS tury + %POINTS punktów, Pozostanie: %REMAINING punktów",
 	"vcmi.adventureMap.moveCostDetailsNoTurns" : "Punkty ruchu - Koszt: %POINTS punktów, Pozostanie: %REMAINING punktów",
+	"vcmi.adventureMap.movementPointsHeroInfo" : "(Punkty ruchu: %REMAINING / %POINTS)",
 	"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Wybacz, powtórka ruchu wroga nie została jeszcze zaimplementowana!",
 
 	"vcmi.capitalColors.0" : "Czerwony",

+ 4 - 10
client/CPlayerInterface.cpp

@@ -171,10 +171,11 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
 void CPlayerInterface::closeAllDialogs()
 {
 	// remove all active dialogs that do not expect query answer
-	for (;;)
+	while(true)
 	{
 		auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
 		auto infoWindow = GH.windows().topWindow<CInfoWindow>();
+		auto topWindow = GH.windows().topWindow<WindowBase>();
 
 		if(adventureWindow != nullptr)
 			break;
@@ -182,16 +183,8 @@ void CPlayerInterface::closeAllDialogs()
 		if(infoWindow && infoWindow->ID != QueryID::NONE)
 			break;
 
-		if (infoWindow)
-			infoWindow->close();
-		else
-			GH.windows().popWindows(1);
+		topWindow->close();
 	}
-
-	if(castleInt)
-		castleInt->close();
-
-	castleInt = nullptr;
 }
 
 void CPlayerInterface::playerEndsTurn(PlayerColor player)
@@ -1460,6 +1453,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
 			cmp.push_back(std::make_shared<CComponent>(ComponentType::FLAG, playerID));
 			makingTurn = true; //workaround for stiff showInfoDialog implementation
 			showInfoDialog(msg, cmp);
+			waitWhileDialog();
 			makingTurn = false;
 		}
 	}

+ 2 - 1
client/adventureMap/AdventureMapInterface.cpp

@@ -557,7 +557,8 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
 		else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
 		{
 			if(LOCPLINT->localState->hasPath(currentHero) &&
-			   LOCPLINT->localState->getPath(currentHero).endPos() == targetPosition)//we'll be moving
+			   LOCPLINT->localState->getPath(currentHero).endPos() == targetPosition &&
+			   !GH.isKeyboardShiftDown())//we'll be moving
 			{
 				assert(!CGI->mh->hasOngoingAnimations());
 				if(!CGI->mh->hasOngoingAnimations() && LOCPLINT->localState->getPath(currentHero).nextNode().turns == 0)

+ 1 - 1
client/adventureMap/CList.cpp

@@ -266,7 +266,7 @@ void CHeroList::CHeroItem::showTooltip()
 
 std::string CHeroList::CHeroItem::getHoverText()
 {
-	return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->getClassNameTranslated());
+	return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->getClassNameTranslated()) + hero->getMovementPointsTextIfOwner(hero->getOwner());
 }
 
 void CHeroList::CHeroItem::gesture(bool on, const Point & initialPosition, const Point & finalPosition)

+ 1 - 1
client/gui/CIntObject.cpp

@@ -343,6 +343,6 @@ WindowBase::WindowBase(int used_, Point pos_)
 void WindowBase::close()
 {
 	if(!GH.windows().isTopWindow(this))
-		logGlobal->error("Only top interface must be closed");
+		throw std::runtime_error("Only top interface can be closed");
 	GH.windows().popWindows(1);
 }

+ 0 - 1
client/gui/CIntObject.h

@@ -148,7 +148,6 @@ class WindowBase : public CIntObject
 {
 public:
 	WindowBase(int used_ = 0, Point pos_ = Point());
-protected:
 	virtual void close();
 };
 

+ 1 - 1
config/schemas/faction.json

@@ -96,7 +96,7 @@
 				"creatures" : {
 					"type" : "array",
 					"minItems" : 7,
-					"maxItems" : 7,
+					"maxItems" : 8,
 					"description" : "List of creatures available for recruitment on each level",
 					"items" : {
 						"type" : "array",

+ 151 - 0
config/schemas/gameSettings.json

@@ -0,0 +1,151 @@
+{
+	"type" : "object",
+	"$schema" : "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI game settings format",
+	"description" : "Format used to define game settings in VCMI",
+	"additionalProperties" : false,
+	"properties" : {
+		"textData" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"heroClass"   : { "type" : "number" },
+				"artifact"    : { "type" : "number" },
+				"creature"    : { "type" : "number" },
+				"faction"     : { "type" : "number" },
+				"hero"        : { "type" : "number" },
+				"spell"       : { "type" : "number" },
+				"object"      : { "type" : "number" },
+				"terrain"     : { "type" : "number" },
+				"river"       : { "type" : "number" },
+				"road"        : { "type" : "number" }
+			}
+		},
+		"mapFormat" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"restorationOfErathia" : { "type" : "object" },
+				"armageddonsBlade" :     { "type" : "object" },
+				"shadowOfDeath" :        { "type" : "object" },
+				"chronicles" :           { "type" : "object" },
+				"jsonVCMI" :             { "type" : "object" },
+				"hornOfTheAbyss" :       { "type" : "object" },
+				"inTheWakeOfGods" :      { "type" : "object" }
+			}
+		},
+		"heroes" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"perPlayerOnMapCap"  :        { "type" : "number" },
+				"perPlayerTotalCap"  :        { "type" : "number" },
+				"retreatOnWinWithoutTroops" : { "type" : "boolean" },
+				"startingStackChances" :      { "type" : "array" },
+				"backpackSize" :              { "type" : "number" },
+				"tavernInvite" :              { "type" : "boolean" },
+				"minimalPrimarySkills" :      { "type" : "array" }
+			}
+		},
+		"towns" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"buildingsPerTurnCap"  :    { "type" : "number" },
+				"startingDwellingChances" : { "type" : "array" }
+			}
+		},
+		"combat": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"goodMoraleDice" :              { "type" : "array" },
+				"badMoraleDice" :               { "type" : "array" },
+				"goodLuckDice" :                { "type" : "array" },
+				"badLuckDice" :                 { "type" : "array" },
+				"backpackSize" :                { "type" : "number" },
+				"attackPointDamageFactor" :     { "type" : "number" },
+				"attackPointDamageFactorCap" :  { "type" : "number" },
+				"defensePointDamageFactor" :    { "type" : "number" },
+				"defensePointDamageFactorCap" : { "type" : "number" },
+				"oneHexTriggersObstacles" :     { "type" : "boolean" },
+				"layouts" :                     { "type" : "object" }
+			}
+		},
+		"creatures": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"weeklyGrowthPercent" :     { "type" : "number" },
+				"weeklyGrowthCap" :         { "type" : "number" },
+				"dailyStackExperience" :    { "type" : "number" },
+				"allowRandomSpecialWeeks" : { "type" : "boolean" },
+				"allowAllForDoubleMonth" :  { "type" : "boolean" }
+			}
+		},
+		"dwellings": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"accumulateWhenNeutral" :  { "type" : "boolean" },
+				"accumulateWhenOwned" :  { "type" : "boolean" },
+				"mergeOnRecruit" :  { "type" : "boolean" }
+			}
+		},
+		"markets": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"blackMarketRestockPeriod" : { "type" : "number" }
+			}
+		},
+		"banks": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"showGuardsComposition" : { "type" : "boolean" }
+			}
+		},
+		"modules": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"stackExperience" : { "type" : "boolean" },
+				"stackArtifact" : { "type" : "boolean" },
+				"commanders" : { "type" : "boolean" }
+			}
+		},
+		"pathfinder": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"ignoreGuards" :            { "type" : "boolean" },
+				"useBoat" :                 { "type" : "boolean" },
+				"useMonolithTwoWay" :       { "type" : "boolean" },
+				"useMonolithOneWayUnique" : { "type" : "boolean" },
+				"useMonolithOneWayRandom" : { "type" : "boolean" },
+				"useWhirlpool" :            { "type" : "boolean" },
+				"originalFlyRules" :        { "type" : "boolean" }
+			}
+		},
+		"spells": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"dimensionDoorOnlyToUncoveredTiles" : { "type" : "boolean" },
+				"dimensionDoorExposesTerrainType" :   { "type" : "boolean" },
+				"dimensionDoorFailureSpendsPoints" :  { "type" : "boolean" },
+				"dimensionDoorTriggersGuards" :       { "type" : "boolean" },
+				"dimensionDoorTournamentRulesLimit" : { "type" : "boolean" }
+			}
+		},
+		"bonuses": {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"global" : { "type" : "object" },
+				"perHero" : { "type" : "object" }
+			}
+		},
+	}
+}

+ 1 - 6
config/schemas/mod.json

@@ -124,12 +124,7 @@
 		"settings" : {
 			"type" : "object",
 			"description" : "List of changed game settings by mod",
-			"additionalProperties" : {
-				"type" : "object",
-				"properties" : { 
-					"type" : "object"
-				}
-			}
+			"$ref" : "gameSettings.json"
 		},
 		"filesystem" : {
 			"type" : "object",

+ 1 - 3
config/schemas/template.json

@@ -134,9 +134,7 @@
 		"settings" : {
 			"description" : "List of changed game settings by template",
 			"type" : "object",
-			"additionalProperties" : {
-				"type" : "object"
-			}
+			"$ref" : "gameSettings.json"
 		},
 		"name" : {
 			"description" : "Optional name - useful to have several template variations with same name",

+ 4 - 1
docs/Readme.md

@@ -52,12 +52,15 @@ Please see corresponding installation guide articles for details for your platfo
 - [Cheat codes](players/Cheat_Codes.md)
 - [Privacy Policy](players/Privacy_Policy.md)
 
+## Documentation and guidelines for translators
+
+- [Translations](translators/Translations.md)
+
 ## Documentation and guidelines for game modders
 
 - [Modding Guidelines](modders/Readme.md)
 - [Mod File Format](modders/Mod_File_Format.md)
 - [Bonus Format](modders/Bonus_Format.md)
-- [Translations](modders/Translations.md)
 - [Map Editor](modders/Map_Editor.md)
 - [Campaign Format](modders/Campaign_Format.md)
 - [Configurable Widgets](modders/Configurable_Widgets.md)

+ 1 - 0
docs/modders/Translations.md → docs/translators/Translations.md

@@ -33,6 +33,7 @@ The page will be automatically updated once a week.
 VCMI allows translating game data into languages other than English. In order to translate Heroes III in your language easiest approach is to:
 
 - Copy existing translation, such as English translation from here: https://github.com/vcmi-mods/h3-for-vcmi-englisation (delete sound and video folders)
+- Copy text-free images from here: https://github.com/vcmi-mods/empty-translation
 - Rename mod to indicate your language, preferred form is "(language)-translation"
 - Update mod.json to match your mod
 - Translate all texts strings from `game.json`, `campaigns.json` and `maps.json`

+ 1 - 1
launcher/firstLaunch/firstlaunch_moc.cpp

@@ -323,7 +323,7 @@ void FirstLaunchView::extractGogData()
 		QFile tmpFile(file);
 		if(!tmpFile.open(QIODevice::ReadOnly))
 		{
-			QMessageBox::critical(this, tr("File cannot opened"), tmpFile.errorString());
+			QMessageBox::critical(this, tr("File cannot be opened"), tmpFile.errorString());
 			return QString{};
 		}
 		QByteArray magicFile = tmpFile.read(magic.length());

+ 1 - 1
launcher/translation/chinese.ts

@@ -1248,7 +1248,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>打开文件失败</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/czech.ts

@@ -1241,7 +1241,7 @@ Offline instalátor obsahuje dvě části, .exe a .bin. Ujistěte se, že stahuj
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation type="unfinished"></translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/english.ts

@@ -1220,7 +1220,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation type="unfinished"></translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/french.ts

@@ -1246,7 +1246,7 @@ Heroes® of Might and Magic® III HD n&quot;est actuellement pas pris en charge
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>Le fichier ne peut pas être ouvert</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/german.ts

@@ -1241,7 +1241,7 @@ Der Offline-Installer besteht aus zwei Teilen, .exe und .bin. Stellen Sie sicher
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>Datei kann nicht geöffnet werden</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/polish.ts

@@ -1241,7 +1241,7 @@ Instalator offline składa się z dwóch części, .exe i .bin. Upewnij się, ż
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>Nieudane otwarcie pliku</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/portuguese.ts

@@ -1241,7 +1241,7 @@ O instalador offline consiste em duas partes, .exe e .bin. Certifique-se de baix
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>O arquivo não pode ser aberto</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/russian.ts

@@ -1226,7 +1226,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation type="unfinished"></translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/spanish.ts

@@ -1239,7 +1239,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation type="unfinished"></translation>
     </message>
     <message>

文件差異過大導致無法顯示
+ 188 - 188
launcher/translation/swedish.ts


+ 1 - 1
launcher/translation/ukrainian.ts

@@ -1241,7 +1241,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation>Не вдається відкрити файл</translation>
     </message>
     <message>

+ 1 - 1
launcher/translation/vietnamese.ts

@@ -1232,7 +1232,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
     </message>
     <message>
         <location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
-        <source>File cannot opened</source>
+        <source>File cannot be opened</source>
         <translation type="unfinished"></translation>
     </message>
     <message>

+ 2 - 0
lib/GameSettings.cpp

@@ -105,6 +105,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 
 void GameSettings::loadBase(const JsonNode & input)
 {
+	JsonUtils::validate(input, "vcmi:gameSettings", input.getModScope());
+
 	for(const auto & option : settingProperties)
 	{
 		const JsonNode & optionValue = input[option.group][option.key];

+ 5 - 0
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -203,6 +203,11 @@ TObjectTypeHandler CObjectClassesHandler::loadSubObjectFromJson(const std::strin
 		assert(handlerConstructors.count(handler) != 0);
 	}
 
+	// Compatibility with 1.5 mods for 1.6. To be removed in 1.7
+	// Detect banks that use old format and load them using old bank hander
+	if (baseObject->id == Obj::CREATURE_BANK && entry.Struct().count("levels") && !entry.Struct().count("rewards"))
+		handler = "bank";
+
 	auto createdObject = handlerConstructors.at(handler)();
 
 	createdObject->modScope = scope;

+ 19 - 0
lib/mapObjects/CGHeroInstance.cpp

@@ -567,6 +567,25 @@ std::string CGHeroInstance::getObjectName() const
 		return VLC->objtypeh->getObjectName(ID, 0);
 }
 
+std::string CGHeroInstance::getHoverText(PlayerColor player) const
+{
+	std::string hoverText = CArmedInstance::getHoverText(player) + getMovementPointsTextIfOwner(player);
+	return hoverText;
+}
+
+std::string CGHeroInstance::getMovementPointsTextIfOwner(PlayerColor player) const
+{
+	std::string output = "";
+	if(player == getOwner())
+	{
+		output += " " + VLC->generaltexth->translate("vcmi.adventureMap.movementPointsHeroInfo");
+		boost::replace_first(output, "%POINTS", std::to_string(movementPointsLimit(!boat)));
+		boost::replace_first(output, "%REMAINING", std::to_string(movementPointsRemaining()));
+	}
+
+	return output;
+}
+
 ui8 CGHeroInstance::maxlevelsToMagicSchool() const
 {
 	return type->heroClass->isMagicHero() ? 3 : 4;

+ 2 - 0
lib/mapObjects/CGHeroInstance.h

@@ -301,6 +301,8 @@ public:
 	void pickRandomObject(vstd::RNG & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	std::string getObjectName() const override;
+	std::string getHoverText(PlayerColor player) const override;
+	std::string getMovementPointsTextIfOwner(PlayerColor player) const;
 
 	void afterAddToMap(CMap * map) override;
 	void afterRemoveFromMap(CMap * map) override;

+ 1 - 0
lib/rewardable/Limiter.cpp

@@ -244,6 +244,7 @@ void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler)
 	handler.serializeIdArray("colors", players);
 	handler.serializeInt("manaPoints", manaPoints);
 	handler.serializeIdArray("artifacts", artifacts);
+	handler.serializeIdArray("spells", spells);
 	handler.enterArray("creatures").serializeStruct(creatures);
 	handler.enterArray("primary").serializeArray(primary);
 	{

+ 17 - 17
mapeditor/inspector/questwidget.cpp

@@ -48,48 +48,48 @@ QuestWidget::QuestWidget(MapController & _controller, CQuest & _sh, QWidget *par
 	}
 	
 	//fill artifacts
-	for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
+	for(const auto & artifactPtr : VLC->arth->objects)
 	{
-		auto * item = new QListWidgetItem(QString::fromStdString(VLC->artifacts()->getByIndex(i)->getNameTranslated()));
-		item->setData(Qt::UserRole, QVariant::fromValue(i));
+		auto artifactIndex = artifactPtr->getIndex();
+		auto * item = new QListWidgetItem(QString::fromStdString(artifactPtr->getNameTranslated()));
 		item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 		item->setCheckState(Qt::Unchecked);
-		if(controller.map()->allowedArtifact.count(i) == 0)
+		if(controller.map()->allowedArtifact.count(artifactIndex) == 0)
 			item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
 		ui->lArtifacts->addItem(item);
 	}
 	
 	//fill spells
-	for(int i = 0; i < controller.map()->allowedSpells.size(); ++i)
+	for(const auto & spellPtr : VLC->spellh->objects)
 	{
-		auto * item = new QListWidgetItem(QString::fromStdString(VLC->spells()->getByIndex(i)->getNameTranslated()));
-		item->setData(Qt::UserRole, QVariant::fromValue(i));
+		auto spellIndex = spellPtr->getIndex();
+		auto * item = new QListWidgetItem(QString::fromStdString(spellPtr->getNameTranslated()));
 		item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 		item->setCheckState(Qt::Unchecked);
-		if(controller.map()->allowedSpells.count(i) == 0)
+		if(controller.map()->allowedSpells.count(spellIndex) == 0)
 			item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
 		ui->lSpells->addItem(item);
 	}
 	
 	//fill skills
-	ui->lSkills->setRowCount(controller.map()->allowedAbilities.size());
-	for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
+	ui->lSkills->setRowCount(VLC->skillh->objects.size());
+	for(const auto & skillPtr : VLC->skillh->objects)
 	{
-		auto * item = new QTableWidgetItem(QString::fromStdString(VLC->skills()->getByIndex(i)->getNameTranslated()));
-		item->setData(Qt::UserRole, QVariant::fromValue(i));
+		auto skillIndex = skillPtr->getIndex();
+		auto * item = new QTableWidgetItem(QString::fromStdString(skillPtr->getNameTranslated()));
 		
 		auto * widget = new QComboBox;
-		for(auto & s : NSecondarySkill::levels)
+		for(const auto & s : NSecondarySkill::levels)
 			widget->addItem(QString::fromStdString(s));
 		
-		if(controller.map()->allowedAbilities.count(i) == 0)
+		if(controller.map()->allowedAbilities.count(skillIndex) == 0)
 		{
 			item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
 			widget->setEnabled(false);
 		}
 			
-		ui->lSkills->setItem(i, 0, item);
-		ui->lSkills->setCellWidget(i, 1, widget);
+		ui->lSkills->setItem(skillIndex, 0, item);
+		ui->lSkills->setCellWidget(skillIndex, 1, widget);
 	}
 	
 	//fill creatures
@@ -156,7 +156,7 @@ void QuestWidget::obtainData()
 	for(auto i : quest.mission.artifacts)
 		ui->lArtifacts->item(VLC->artifacts()->getById(i)->getIndex())->setCheckState(Qt::Checked);
 	for(auto i : quest.mission.spells)
-		ui->lArtifacts->item(VLC->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
+		ui->lSpells->item(VLC->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
 	for(auto & i : quest.mission.secondary)
 	{
 		int index = VLC->skills()->getById(i.first)->getIndex();

部分文件因文件數量過多而無法顯示