mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-05 00:49:09 +02:00
Merge branch 'master' into 'develop'
This commit is contained in:
7
.github/workflows/github.yml
vendored
7
.github/workflows/github.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
before_install: linux_qt5.sh
|
before_install: linux_qt5.sh
|
||||||
preset: linux-gcc-test
|
preset: linux-gcc-test
|
||||||
- platform: linux
|
- platform: linux
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
test: 0
|
test: 0
|
||||||
before_install: linux_qt5.sh
|
before_install: linux_qt5.sh
|
||||||
preset: linux-gcc-debug
|
preset: linux-gcc-debug
|
||||||
@ -246,6 +246,9 @@ jobs:
|
|||||||
if [[ ${{matrix.preset}} == linux-gcc-test ]]
|
if [[ ${{matrix.preset}} == linux-gcc-test ]]
|
||||||
then
|
then
|
||||||
cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 --preset ${{ matrix.preset }}
|
cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 --preset ${{ matrix.preset }}
|
||||||
|
elif [[ ${{matrix.preset}} == linux-gcc-debug ]]
|
||||||
|
then
|
||||||
|
cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 --preset ${{ matrix.preset }}
|
||||||
elif [[ (${{matrix.preset}} == android-conan-ninja-release) && (${{github.ref}} != 'refs/heads/master') ]]
|
elif [[ (${{matrix.preset}} == android-conan-ninja-release) && (${{github.ref}} != 'refs/heads/master') ]]
|
||||||
then
|
then
|
||||||
cmake -DENABLE_CCACHE:BOOL=ON -DANDROID_GRADLE_PROPERTIES="applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily;applicationVariant=daily" --preset ${{ matrix.preset }}
|
cmake -DENABLE_CCACHE:BOOL=ON -DANDROID_GRADLE_PROPERTIES="applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily;applicationVariant=daily" --preset ${{ matrix.preset }}
|
||||||
@ -355,7 +358,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-src:
|
deploy-src:
|
||||||
if: always() && github.ref == 'refs/heads/master'
|
if: always() && github.ref == 'refs/heads/master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -411,6 +411,7 @@ void AIGateway::heroCreated(const CGHeroInstance * h)
|
|||||||
{
|
{
|
||||||
LOG_TRACE(logAi);
|
LOG_TRACE(logAi);
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
|
nullkiller->invalidatePathfinderData(); // new hero needs to look around
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIGateway::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
|
void AIGateway::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
|
||||||
@ -929,7 +930,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
|||||||
|
|
||||||
const CArmedInstance * armies[] = {destinationArmy, source};
|
const CArmedInstance * armies[] = {destinationArmy, source};
|
||||||
|
|
||||||
auto bestArmy = nullkiller->armyManager->getBestArmy(destinationArmy, destinationArmy, source);
|
auto bestArmy = nullkiller->armyManager->getBestArmy(destinationArmy, destinationArmy, source, myCb->getTile(source->visitablePos())->getTerrainID());
|
||||||
|
|
||||||
for(auto army : armies)
|
for(auto army : armies)
|
||||||
{
|
{
|
||||||
@ -983,7 +984,7 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
|
|||||||
&& source->stacksCount() == 1
|
&& source->stacksCount() == 1
|
||||||
&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
|
&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
|
||||||
{
|
{
|
||||||
auto weakest = nullkiller->armyManager->getWeakestCreature(bestArmy);
|
auto weakest = nullkiller->armyManager->getBestUnitForScout(bestArmy, myCb->getTile(source->visitablePos())->getTerrainID());
|
||||||
|
|
||||||
if(weakest->creature == targetCreature)
|
if(weakest->creature == targetCreature)
|
||||||
{
|
{
|
||||||
|
@ -774,9 +774,9 @@ bool townHasFreeTavern(const CGTownInstance * town)
|
|||||||
return canMoveVisitingHeroToGarrison;
|
return canMoveVisitingHeroToGarrison;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCreatureSet * heroArmy)
|
uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCreatureSet * heroArmy, int fortLevel)
|
||||||
{
|
{
|
||||||
auto armyStrength = heroArmy->getArmyStrength();
|
auto armyStrength = heroArmy->getArmyStrength(fortLevel);
|
||||||
|
|
||||||
if(hero && hero->commander && hero->commander->alive)
|
if(hero && hero->commander && hero->commander->alive)
|
||||||
{
|
{
|
||||||
|
@ -217,7 +217,7 @@ int64_t getArtifactScoreForHero(const CGHeroInstance * hero, const CArtifactInst
|
|||||||
int64_t getPotentialArtifactScore(const CArtifact * art);
|
int64_t getPotentialArtifactScore(const CArtifact * art);
|
||||||
bool townHasFreeTavern(const CGTownInstance * town);
|
bool townHasFreeTavern(const CGTownInstance * town);
|
||||||
|
|
||||||
uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCreatureSet * heroArmy);
|
uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCreatureSet * heroArmy, int fortLevel = 0);
|
||||||
|
|
||||||
uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start);
|
uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start);
|
||||||
|
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../CCallback.h"
|
#include "../../../CCallback.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
#include "../../../lib/mapping/CMapDefines.h"
|
||||||
#include "../../../lib/IGameSettings.h"
|
#include "../../../lib/IGameSettings.h"
|
||||||
#include "../../../lib/GameConstants.h"
|
#include "../../../lib/GameConstants.h"
|
||||||
|
#include "../../../lib/TerrainHandler.h"
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
@ -76,7 +78,7 @@ std::vector<SlotInfo> ArmyManager::toSlotInfo(std::vector<creInfo> army) const
|
|||||||
|
|
||||||
uint64_t ArmyManager::howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const
|
uint64_t ArmyManager::howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const
|
||||||
{
|
{
|
||||||
return howManyReinforcementsCanGet(hero, hero, source);
|
return howManyReinforcementsCanGet(hero, hero, source, ai->cb->getTile(hero->visitablePos())->getTerrainID());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
|
std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
|
||||||
@ -111,17 +113,59 @@ std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, c
|
|||||||
return resultingArmy;
|
return resultingArmy;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<SlotInfo> & army) const
|
std::vector<SlotInfo>::iterator ArmyManager::getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const
|
||||||
{
|
{
|
||||||
auto weakest = boost::min_element(army, [](const SlotInfo & left, const SlotInfo & right) -> bool
|
uint64_t totalPower = 0;
|
||||||
|
|
||||||
|
for (const auto & unit : army)
|
||||||
|
totalPower += unit.power;
|
||||||
|
|
||||||
|
int baseMovementCost = cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||||
|
bool terrainHasPenalty = armyTerrain.hasValue() && armyTerrain.toEntity(VLC)->moveCost != baseMovementCost;
|
||||||
|
|
||||||
|
// arbitrary threshold - don't give scout more than specified part of total AI value of our army
|
||||||
|
uint64_t maxUnitValue = totalPower / 100;
|
||||||
|
|
||||||
|
const auto & movementPointsLimits = cb->getSettings().getVector(EGameSettings::HEROES_MOVEMENT_POINTS_LAND);
|
||||||
|
|
||||||
|
auto fastest = boost::min_element(army, [&](const SlotInfo & left, const SlotInfo & right) -> bool
|
||||||
{
|
{
|
||||||
if(left.creature->getLevel() != right.creature->getLevel())
|
uint64_t leftUnitPower = left.power / left.count;
|
||||||
return left.creature->getLevel() < right.creature->getLevel();
|
uint64_t rightUnitPower = left.power / left.count;
|
||||||
|
bool leftUnitIsWeak = leftUnitPower < maxUnitValue || left.creature->getLevel() < 4;
|
||||||
return left.creature->getMovementRange() > right.creature->getMovementRange();
|
bool rightUnitIsWeak = rightUnitPower < maxUnitValue || right.creature->getLevel() < 4;
|
||||||
|
|
||||||
|
if (leftUnitIsWeak != rightUnitIsWeak)
|
||||||
|
return leftUnitIsWeak;
|
||||||
|
|
||||||
|
if (terrainHasPenalty)
|
||||||
|
{
|
||||||
|
auto leftNativeTerrain = left.creature->getFactionID().toFaction()->nativeTerrain;
|
||||||
|
auto rightNativeTerrain = right.creature->getFactionID().toFaction()->nativeTerrain;
|
||||||
|
|
||||||
|
if (leftNativeTerrain != rightNativeTerrain)
|
||||||
|
{
|
||||||
|
if (leftNativeTerrain == armyTerrain)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (rightNativeTerrain == armyTerrain)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int leftEffectiveMovement = std::min<int>(movementPointsLimits.size() - 1, left.creature->getMovementRange());
|
||||||
|
int rightEffectiveMovement = std::min<int>(movementPointsLimits.size() - 1, right.creature->getMovementRange());
|
||||||
|
|
||||||
|
int leftMovementPointsLimit = movementPointsLimits[leftEffectiveMovement];
|
||||||
|
int rightMovementPointsLimit = movementPointsLimits[rightEffectiveMovement];
|
||||||
|
|
||||||
|
if (leftMovementPointsLimit != rightMovementPointsLimit)
|
||||||
|
return leftMovementPointsLimit > rightMovementPointsLimit;
|
||||||
|
|
||||||
|
return leftUnitPower < rightUnitPower;
|
||||||
});
|
});
|
||||||
|
|
||||||
return weakest;
|
return fastest;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemporaryArmy : public CArmedInstance
|
class TemporaryArmy : public CArmedInstance
|
||||||
@ -134,7 +178,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
|
std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const
|
||||||
{
|
{
|
||||||
auto sortedSlots = getSortedSlots(target, source);
|
auto sortedSlots = getSortedSlots(target, source);
|
||||||
|
|
||||||
@ -218,7 +262,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
|||||||
&& allowedFactions.size() == alignmentMap.size()
|
&& allowedFactions.size() == alignmentMap.size()
|
||||||
&& source->needsLastStack())
|
&& source->needsLastStack())
|
||||||
{
|
{
|
||||||
auto weakest = getWeakestCreature(resultingArmy);
|
auto weakest = getBestUnitForScout(resultingArmy, armyTerrain);
|
||||||
|
|
||||||
if(weakest->count == 1)
|
if(weakest->count == 1)
|
||||||
{
|
{
|
||||||
@ -398,14 +442,14 @@ std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(
|
|||||||
return creaturesInDwellings;
|
return creaturesInDwellings;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui64 ArmyManager::howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
|
ui64 ArmyManager::howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const
|
||||||
{
|
{
|
||||||
if(source->stacksCount() == 0)
|
if(source->stacksCount() == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bestArmy = getBestArmy(armyCarrier, target, source);
|
auto bestArmy = getBestArmy(armyCarrier, target, source, armyTerrain);
|
||||||
uint64_t newArmy = 0;
|
uint64_t newArmy = 0;
|
||||||
uint64_t oldArmy = target->getArmyStrength();
|
uint64_t oldArmy = target->getArmyStrength();
|
||||||
|
|
||||||
|
@ -53,10 +53,11 @@ public:
|
|||||||
virtual ui64 howManyReinforcementsCanGet(
|
virtual ui64 howManyReinforcementsCanGet(
|
||||||
const IBonusBearer * armyCarrier,
|
const IBonusBearer * armyCarrier,
|
||||||
const CCreatureSet * target,
|
const CCreatureSet * target,
|
||||||
const CCreatureSet * source) const = 0;
|
const CCreatureSet * source,
|
||||||
|
const TerrainId & armyTerrain) const = 0;
|
||||||
|
|
||||||
virtual std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
virtual std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const = 0;
|
||||||
virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
|
virtual std::vector<SlotInfo>::iterator getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const = 0;
|
||||||
virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
|
||||||
virtual std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
virtual std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
||||||
|
|
||||||
@ -97,9 +98,9 @@ public:
|
|||||||
uint8_t turn = 0) const override;
|
uint8_t turn = 0) const override;
|
||||||
|
|
||||||
ui64 howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const override;
|
ui64 howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const override;
|
||||||
ui64 howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const override;
|
ui64 howManyReinforcementsCanGet(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const override;
|
||||||
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const override;
|
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source, const TerrainId & armyTerrain) const override;
|
||||||
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
|
std::vector<SlotInfo>::iterator getBestUnitForScout(std::vector<SlotInfo> & army, const TerrainId & armyTerrain) const override;
|
||||||
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
|
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
|
||||||
std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const override;
|
std::vector<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const override;
|
||||||
|
|
||||||
|
@ -291,6 +291,7 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
|
|||||||
prerequisite.baseCreatureID = info.baseCreatureID;
|
prerequisite.baseCreatureID = info.baseCreatureID;
|
||||||
prerequisite.prerequisitesCount++;
|
prerequisite.prerequisitesCount++;
|
||||||
prerequisite.armyCost = info.armyCost;
|
prerequisite.armyCost = info.armyCost;
|
||||||
|
prerequisite.armyStrength = info.armyStrength;
|
||||||
bool haveSameOrBetterFort = false;
|
bool haveSameOrBetterFort = false;
|
||||||
if (prerequisite.id == BuildingID::FORT && highestFort >= CGTownInstance::EFortLevel::FORT)
|
if (prerequisite.id == BuildingID::FORT && highestFort >= CGTownInstance::EFortLevel::FORT)
|
||||||
haveSameOrBetterFort = true;
|
haveSameOrBetterFort = true;
|
||||||
|
@ -459,6 +459,8 @@ void ObjectClusterizer::clusterizeObject(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float priority = 0;
|
||||||
|
|
||||||
if(path.nodes.size() > 1)
|
if(path.nodes.size() > 1)
|
||||||
{
|
{
|
||||||
auto blocker = getBlocker(path);
|
auto blocker = getBlocker(path);
|
||||||
@ -475,7 +477,10 @@ void ObjectClusterizer::clusterizeObject(
|
|||||||
|
|
||||||
heroesProcessed.insert(path.targetHero);
|
heroesProcessed.insert(path.targetHero);
|
||||||
|
|
||||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)), PriorityEvaluator::PriorityTier::HUNTER_GATHER);
|
for (int prio = PriorityEvaluator::PriorityTier::BUILDINGS; prio <= PriorityEvaluator::PriorityTier::MAX_PRIORITY_TIER; ++prio)
|
||||||
|
{
|
||||||
|
priority = std::max(priority, priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)), prio));
|
||||||
|
}
|
||||||
|
|
||||||
if(ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
|
if(ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
|
||||||
continue;
|
continue;
|
||||||
@ -498,7 +503,10 @@ void ObjectClusterizer::clusterizeObject(
|
|||||||
|
|
||||||
heroesProcessed.insert(path.targetHero);
|
heroesProcessed.insert(path.targetHero);
|
||||||
|
|
||||||
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)), PriorityEvaluator::PriorityTier::HUNTER_GATHER);
|
for (int prio = PriorityEvaluator::PriorityTier::BUILDINGS; prio <= PriorityEvaluator::PriorityTier::MAX_PRIORITY_TIER; ++prio)
|
||||||
|
{
|
||||||
|
priority = std::max(priority, priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)), prio));
|
||||||
|
}
|
||||||
|
|
||||||
if (ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
|
if (ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
|
||||||
continue;
|
continue;
|
||||||
|
@ -57,7 +57,8 @@ Goals::TGoalVec BuyArmyBehavior::decompose(const Nullkiller * ai) const
|
|||||||
auto reinforcement = ai->armyManager->howManyReinforcementsCanGet(
|
auto reinforcement = ai->armyManager->howManyReinforcementsCanGet(
|
||||||
targetHero,
|
targetHero,
|
||||||
targetHero,
|
targetHero,
|
||||||
&*townArmyAvailableToBuy);
|
&*townArmyAvailableToBuy,
|
||||||
|
TerrainId::NONE);
|
||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
||||||
|
@ -214,11 +214,15 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
std::vector<int> pathsToDefend;
|
std::vector<int> pathsToDefend;
|
||||||
std::map<const CGHeroInstance *, std::vector<int>> defferedPaths;
|
std::map<const CGHeroInstance *, std::vector<int>> defferedPaths;
|
||||||
|
AIPath* closestWay = nullptr;
|
||||||
|
|
||||||
for(int i = 0; i < paths.size(); i++)
|
for(int i = 0; i < paths.size(); i++)
|
||||||
{
|
{
|
||||||
auto & path = paths[i];
|
auto & path = paths[i];
|
||||||
|
|
||||||
|
if (!closestWay || path.movementCost() < closestWay->movementCost())
|
||||||
|
closestWay = &path;
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Hero %s can defend town with force %lld in %s turns, cost: %f, path: %s",
|
"Hero %s can defend town with force %lld in %s turns, cost: %f, path: %s",
|
||||||
@ -382,7 +386,14 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
town->getObjectName());
|
town->getObjectName());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sequence.push_back(sptr(ExecuteHeroChain(path, town)));
|
ExecuteHeroChain heroChain = ExecuteHeroChain(path, town);
|
||||||
|
|
||||||
|
if (closestWay)
|
||||||
|
{
|
||||||
|
heroChain.closestWayRatio = closestWay->movementCost() / heroChain.getPath().movementCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.push_back(sptr(heroChain));
|
||||||
composition.addNextSequence(sequence);
|
composition.addNextSequence(sequence);
|
||||||
|
|
||||||
auto firstBlockedAction = path.getFirstBlockedAction();
|
auto firstBlockedAction = path.getFirstBlockedAction();
|
||||||
|
@ -300,7 +300,8 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
|
|||||||
ai->armyManager->getBestArmy(
|
ai->armyManager->getBestArmy(
|
||||||
path.targetHero,
|
path.targetHero,
|
||||||
path.heroArmy,
|
path.heroArmy,
|
||||||
upgrader->getUpperArmy()));
|
upgrader->getUpperArmy(),
|
||||||
|
TerrainId::NONE));
|
||||||
|
|
||||||
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
|
|||||||
|
|
||||||
ai->dangerHitMap->updateHitMap();
|
ai->dangerHitMap->updateHitMap();
|
||||||
int treasureSourcesCount = 0;
|
int treasureSourcesCount = 0;
|
||||||
|
int bestClosestThreat = UINT8_MAX;
|
||||||
|
|
||||||
for(auto town : towns)
|
for(auto town : towns)
|
||||||
{
|
{
|
||||||
@ -118,6 +119,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
|
|||||||
bestScore = score;
|
bestScore = score;
|
||||||
bestHeroToHire = hero;
|
bestHeroToHire = hero;
|
||||||
bestTownToHireFrom = town;
|
bestTownToHireFrom = town;
|
||||||
|
bestClosestThreat = closestThreat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +130,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
|
|||||||
{
|
{
|
||||||
if (ai->cb->getHeroesInfo().size() == 0
|
if (ai->cb->getHeroesInfo().size() == 0
|
||||||
|| treasureSourcesCount > ai->cb->getHeroesInfo().size() * 5
|
|| treasureSourcesCount > ai->cb->getHeroesInfo().size() * 5
|
||||||
|| bestHeroToHire->getArmyCost() > GameConstants::HERO_GOLD_COST / 2.0
|
|| (bestHeroToHire->getArmyCost() > GameConstants::HERO_GOLD_COST / 2.0 && (bestClosestThreat < 1 || !ai->buildAnalyzer->isGoldPressureHigh()))
|
||||||
|| (ai->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->buildAnalyzer->isGoldPressureHigh() && haveCapitol)
|
|| (ai->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->buildAnalyzer->isGoldPressureHigh() && haveCapitol)
|
||||||
|| (ai->getFreeResources()[EGameResID::GOLD] > 30000 && !ai->buildAnalyzer->isGoldPressureHigh()))
|
|| (ai->getFreeResources()[EGameResID::GOLD] > 30000 && !ai->buildAnalyzer->isGoldPressureHigh()))
|
||||||
{
|
{
|
||||||
|
@ -149,7 +149,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
|
|||||||
{
|
{
|
||||||
if(!startupTown->visitingHero)
|
if(!startupTown->visitingHero)
|
||||||
{
|
{
|
||||||
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero) > 200)
|
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero, TerrainId::NONE) > 200)
|
||||||
{
|
{
|
||||||
auto paths = ai->pathfinder->getPathInfo(startupTown->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(startupTown->visitablePos());
|
||||||
|
|
||||||
|
@ -127,9 +127,9 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
|
|||||||
auto fortLevel = town->fortLevel();
|
auto fortLevel = town->fortLevel();
|
||||||
|
|
||||||
if (fortLevel == CGTownInstance::EFortLevel::CASTLE)
|
if (fortLevel == CGTownInstance::EFortLevel::CASTLE)
|
||||||
danger = std::max(danger * 2, danger + 10000);
|
danger += 10000;
|
||||||
else if(fortLevel == CGTownInstance::EFortLevel::CITADEL)
|
else if(fortLevel == CGTownInstance::EFortLevel::CITADEL)
|
||||||
danger = std::max(ui64(danger * 1.4), danger + 4000);
|
danger += 4000;
|
||||||
}
|
}
|
||||||
|
|
||||||
return danger;
|
return danger;
|
||||||
|
@ -446,7 +446,7 @@ void Nullkiller::makeTurn()
|
|||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
int prioOfTask = 0;
|
int prioOfTask = 0;
|
||||||
#endif
|
#endif
|
||||||
for (int prio = PriorityEvaluator::PriorityTier::INSTAKILL; prio <= PriorityEvaluator::PriorityTier::DEFEND; ++prio)
|
for (int prio = PriorityEvaluator::PriorityTier::INSTAKILL; prio <= PriorityEvaluator::PriorityTier::MAX_PRIORITY_TIER; ++prio)
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
prioOfTask = prio;
|
prioOfTask = prio;
|
||||||
@ -535,7 +535,10 @@ void Nullkiller::makeTurn()
|
|||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hasAnySuccess = true;
|
else
|
||||||
|
{
|
||||||
|
hasAnySuccess = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAnySuccess |= handleTrading();
|
hasAnySuccess |= handleTrading();
|
||||||
@ -721,7 +724,7 @@ bool Nullkiller::handleTrading()
|
|||||||
if (toGive && toGive <= available[mostExpendable]) //don't try to sell 0 resources
|
if (toGive && toGive <= available[mostExpendable]) //don't try to sell 0 resources
|
||||||
{
|
{
|
||||||
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(mostExpendable), GameResID(mostWanted), toGive);
|
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(mostExpendable), GameResID(mostWanted), toGive);
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->info("Traded %d of %s for %d of %s at %s", toGive, mostExpendable, toGet, mostWanted, obj->getObjectName());
|
logAi->info("Traded %d of %s for %d of %s at %s", toGive, mostExpendable, toGet, mostWanted, obj->getObjectName());
|
||||||
#endif
|
#endif
|
||||||
haveTraded = true;
|
haveTraded = true;
|
||||||
|
@ -66,7 +66,8 @@ EvaluationContext::EvaluationContext(const Nullkiller* ai)
|
|||||||
isArmyUpgrade(false),
|
isArmyUpgrade(false),
|
||||||
isHero(false),
|
isHero(false),
|
||||||
isEnemy(false),
|
isEnemy(false),
|
||||||
explorePriority(0)
|
explorePriority(0),
|
||||||
|
powerRatio(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,9 +610,6 @@ float RewardEvaluator::getConquestValue(const CGObjectInstance* target) const
|
|||||||
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance*>(target))
|
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance*>(target))
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
case Obj::KEYMASTER:
|
|
||||||
return 0.6f;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -889,7 +887,14 @@ public:
|
|||||||
|
|
||||||
Goals::StayAtTown& stayAtTown = dynamic_cast<Goals::StayAtTown&>(*task);
|
Goals::StayAtTown& stayAtTown = dynamic_cast<Goals::StayAtTown&>(*task);
|
||||||
|
|
||||||
evaluationContext.armyReward += evaluationContext.evaluator.getManaRecoveryArmyReward(stayAtTown.getHero());
|
if (stayAtTown.getHero() != nullptr && stayAtTown.getHero()->movementPointsRemaining() < 100)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stayAtTown.town->mageGuildLevel() > 0)
|
||||||
|
evaluationContext.armyReward += evaluationContext.evaluator.getManaRecoveryArmyReward(stayAtTown.getHero());
|
||||||
|
|
||||||
if (evaluationContext.armyReward == 0)
|
if (evaluationContext.armyReward == 0)
|
||||||
evaluationContext.isDefend = true;
|
evaluationContext.isDefend = true;
|
||||||
else
|
else
|
||||||
@ -1018,6 +1023,45 @@ public:
|
|||||||
if(heroRole == HeroRole::MAIN)
|
if(heroRole == HeroRole::MAIN)
|
||||||
evaluationContext.heroRole = heroRole;
|
evaluationContext.heroRole = heroRole;
|
||||||
|
|
||||||
|
if (hero)
|
||||||
|
{
|
||||||
|
// Assuming Slots() returns a collection of slots with slot.second->getCreatureID() and slot.second->getPower()
|
||||||
|
float heroPower = 0;
|
||||||
|
float totalPower = 0;
|
||||||
|
|
||||||
|
// Map to store the aggregated power of creatures by CreatureID
|
||||||
|
std::map<int, float> totalPowerByCreatureID;
|
||||||
|
|
||||||
|
// Calculate hero power and total power by CreatureID
|
||||||
|
for (auto slot : hero->Slots())
|
||||||
|
{
|
||||||
|
int creatureID = slot.second->getCreatureID();
|
||||||
|
float slotPower = slot.second->getPower();
|
||||||
|
|
||||||
|
// Add the power of this slot to the heroPower
|
||||||
|
heroPower += slotPower;
|
||||||
|
|
||||||
|
// Accumulate the total power for the specific CreatureID
|
||||||
|
if (totalPowerByCreatureID.find(creatureID) == totalPowerByCreatureID.end())
|
||||||
|
{
|
||||||
|
// First time encountering this CreatureID, retrieve total creatures' power
|
||||||
|
totalPowerByCreatureID[creatureID] = ai->armyManager->getTotalCreaturesAvailable(creatureID).power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total power based on unique CreatureIDs
|
||||||
|
for (const auto& entry : totalPowerByCreatureID)
|
||||||
|
{
|
||||||
|
totalPower += entry.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the power ratio if total power is greater than zero
|
||||||
|
if (totalPower > 0)
|
||||||
|
{
|
||||||
|
evaluationContext.powerRatio = heroPower / totalPower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (target)
|
if (target)
|
||||||
{
|
{
|
||||||
evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
|
evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
|
||||||
@ -1030,6 +1074,8 @@ public:
|
|||||||
evaluationContext.isHero = true;
|
evaluationContext.isHero = true;
|
||||||
if (target->getOwner().isValidPlayer() && ai->cb->getPlayerRelations(ai->playerID, target->getOwner()) == PlayerRelations::ENEMIES)
|
if (target->getOwner().isValidPlayer() && ai->cb->getPlayerRelations(ai->playerID, target->getOwner()) == PlayerRelations::ENEMIES)
|
||||||
evaluationContext.isEnemy = true;
|
evaluationContext.isEnemy = true;
|
||||||
|
if (target->ID == Obj::TOWN)
|
||||||
|
evaluationContext.defenseValue = dynamic_cast<const CGTownInstance*>(target)->fortLevel();
|
||||||
evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
|
evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
|
||||||
if(evaluationContext.danger > 0)
|
if(evaluationContext.danger > 0)
|
||||||
evaluationContext.skillReward += (float)evaluationContext.danger / (float)hero->getArmyStrength();
|
evaluationContext.skillReward += (float)evaluationContext.danger / (float)hero->getArmyStrength();
|
||||||
@ -1169,6 +1215,19 @@ public:
|
|||||||
evaluationContext.goldCost += cost;
|
evaluationContext.goldCost += cost;
|
||||||
evaluationContext.closestWayRatio = 1;
|
evaluationContext.closestWayRatio = 1;
|
||||||
evaluationContext.buildingCost += bi.buildCostWithPrerequisites;
|
evaluationContext.buildingCost += bi.buildCostWithPrerequisites;
|
||||||
|
|
||||||
|
bool alreadyOwn = false;
|
||||||
|
int highestMageGuildPossible = BuildingID::MAGES_GUILD_3;
|
||||||
|
for (auto town : evaluationContext.evaluator.ai->cb->getTownsInfo())
|
||||||
|
{
|
||||||
|
if (town->hasBuilt(bi.id))
|
||||||
|
alreadyOwn = true;
|
||||||
|
if (evaluationContext.evaluator.ai->cb->canBuildStructure(town, BuildingID::MAGES_GUILD_5) != EBuildingState::FORBIDDEN)
|
||||||
|
highestMageGuildPossible = BuildingID::MAGES_GUILD_5;
|
||||||
|
else if (evaluationContext.evaluator.ai->cb->canBuildStructure(town, BuildingID::MAGES_GUILD_4) != EBuildingState::FORBIDDEN)
|
||||||
|
highestMageGuildPossible = BuildingID::MAGES_GUILD_4;
|
||||||
|
}
|
||||||
|
|
||||||
if (bi.id == BuildingID::MARKETPLACE || bi.dailyIncome[EGameResID::WOOD] > 0)
|
if (bi.id == BuildingID::MARKETPLACE || bi.dailyIncome[EGameResID::WOOD] > 0)
|
||||||
evaluationContext.isTradeBuilding = true;
|
evaluationContext.isTradeBuilding = true;
|
||||||
|
|
||||||
@ -1183,14 +1242,19 @@ public:
|
|||||||
if(bi.baseCreatureID == bi.creatureID)
|
if(bi.baseCreatureID == bi.creatureID)
|
||||||
{
|
{
|
||||||
evaluationContext.addNonCriticalStrategicalValue((0.5f + 0.1f * bi.creatureLevel) / (float)bi.prerequisitesCount);
|
evaluationContext.addNonCriticalStrategicalValue((0.5f + 0.1f * bi.creatureLevel) / (float)bi.prerequisitesCount);
|
||||||
evaluationContext.armyReward += bi.armyStrength;
|
evaluationContext.armyReward += bi.armyStrength * 1.5;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto potentialUpgradeValue = evaluationContext.evaluator.getUpgradeArmyReward(buildThis.town, bi);
|
auto potentialUpgradeValue = evaluationContext.evaluator.getUpgradeArmyReward(buildThis.town, bi);
|
||||||
|
|
||||||
evaluationContext.addNonCriticalStrategicalValue(potentialUpgradeValue / 10000.0f / (float)bi.prerequisitesCount);
|
evaluationContext.addNonCriticalStrategicalValue(potentialUpgradeValue / 10000.0f / (float)bi.prerequisitesCount);
|
||||||
evaluationContext.armyReward += potentialUpgradeValue / (float)bi.prerequisitesCount;
|
if(bi.id.IsDwelling())
|
||||||
|
evaluationContext.armyReward += bi.armyStrength - evaluationContext.evaluator.ai->armyManager->evaluateStackPower(bi.baseCreatureID.toCreature(), bi.creatureGrows);
|
||||||
|
else //This is for prerequisite-buildings
|
||||||
|
evaluationContext.armyReward += evaluationContext.evaluator.ai->armyManager->evaluateStackPower(bi.baseCreatureID.toCreature(), bi.creatureGrows);
|
||||||
|
if(alreadyOwn)
|
||||||
|
evaluationContext.armyReward /= bi.buildCostWithPrerequisites.marketValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(bi.id == BuildingID::CITADEL || bi.id == BuildingID::CASTLE)
|
else if(bi.id == BuildingID::CITADEL || bi.id == BuildingID::CASTLE)
|
||||||
@ -1201,9 +1265,14 @@ public:
|
|||||||
else if(bi.id >= BuildingID::MAGES_GUILD_1 && bi.id <= BuildingID::MAGES_GUILD_5)
|
else if(bi.id >= BuildingID::MAGES_GUILD_1 && bi.id <= BuildingID::MAGES_GUILD_5)
|
||||||
{
|
{
|
||||||
evaluationContext.skillReward += 2 * (bi.id - BuildingID::MAGES_GUILD_1);
|
evaluationContext.skillReward += 2 * (bi.id - BuildingID::MAGES_GUILD_1);
|
||||||
for (auto hero : evaluationContext.evaluator.ai->cb->getHeroesInfo())
|
if (!alreadyOwn && evaluationContext.evaluator.ai->cb->canBuildStructure(buildThis.town, highestMageGuildPossible) != EBuildingState::FORBIDDEN)
|
||||||
{
|
{
|
||||||
evaluationContext.armyInvolvement += hero->getArmyCost();
|
for (auto hero : evaluationContext.evaluator.ai->cb->getHeroesInfo())
|
||||||
|
{
|
||||||
|
if(hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + hero->getPrimSkillLevel(PrimarySkill::KNOWLEDGE) > hero->getPrimSkillLevel(PrimarySkill::ATTACK) + hero->getPrimSkillLevel(PrimarySkill::DEFENSE)
|
||||||
|
&& hero->manaLimit() > 30)
|
||||||
|
evaluationContext.armyReward += hero->getArmyCost();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int sameTownBonus = 0;
|
int sameTownBonus = 0;
|
||||||
@ -1333,18 +1402,35 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
float score = 0;
|
float score = 0;
|
||||||
const bool amIInDanger = ai->cb->getTownsInfo().empty() || (evaluationContext.isDefend && evaluationContext.threatTurns == 0);
|
bool currentPositionThreatened = false;
|
||||||
const float maxWillingToLose = amIInDanger ? 1 : ai->settings->getMaxArmyLossTarget();
|
if (task->hero)
|
||||||
|
{
|
||||||
|
auto currentTileThreat = ai->dangerHitMap->getTileThreat(task->hero->visitablePos());
|
||||||
|
if (currentTileThreat.fastestDanger.turn < 1 && currentTileThreat.fastestDanger.danger > task->hero->getTotalStrength())
|
||||||
|
currentPositionThreatened = true;
|
||||||
|
}
|
||||||
|
if (priorityTier == PriorityTier::FAR_HUNTER_GATHER && currentPositionThreatened == false)
|
||||||
|
{
|
||||||
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
|
logAi->trace("Skip FAR_HUNTER_GATHER because hero is not threatened.");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const bool amIInDanger = ai->cb->getTownsInfo().empty();
|
||||||
|
const float maxWillingToLose = amIInDanger ? 1 : ai->settings->getMaxArmyLossTarget() * evaluationContext.powerRatio > 0 ? ai->settings->getMaxArmyLossTarget() * evaluationContext.powerRatio : 1.0;
|
||||||
|
float dangerThreshold = 1;
|
||||||
|
dangerThreshold *= evaluationContext.powerRatio > 0 ? evaluationContext.powerRatio : 1.0;
|
||||||
|
|
||||||
bool arriveNextWeek = false;
|
bool arriveNextWeek = false;
|
||||||
if (ai->cb->getDate(Date::DAY_OF_WEEK) + evaluationContext.turn > 7 && priorityTier < PriorityTier::FAR_KILL)
|
if (ai->cb->getDate(Date::DAY_OF_WEEK) + evaluationContext.turn > 7 && priorityTier < PriorityTier::FAR_KILL)
|
||||||
arriveNextWeek = true;
|
arriveNextWeek = true;
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("BEFORE: priorityTier %d, Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, army-involvement: %f, gold: %f, cost: %d, army gain: %f, army growth: %f skill: %f danger: %d, threatTurns: %d, threat: %d, role: %s, strategical value: %f, conquest value: %f cwr: %f, fear: %f, explorePriority: %d isDefend: %d isEnemy: %d arriveNextWeek: %d",
|
logAi->trace("BEFORE: priorityTier %d, Evaluated %s, loss: %f, maxWillingToLose: %f, turn: %d, turns main: %f, scout: %f, army-involvement: %f, gold: %f, cost: %d, army gain: %f, army growth: %f skill: %f danger: %d, threatTurns: %d, threat: %d, role: %s, strategical value: %f, conquest value: %f cwr: %f, fear: %f, dangerThreshold: %f explorePriority: %d isDefend: %d isEnemy: %d arriveNextWeek: %d powerRatio: %f",
|
||||||
priorityTier,
|
priorityTier,
|
||||||
task->toString(),
|
task->toString(),
|
||||||
evaluationContext.armyLossPersentage,
|
evaluationContext.armyLossPersentage,
|
||||||
|
maxWillingToLose,
|
||||||
(int)evaluationContext.turn,
|
(int)evaluationContext.turn,
|
||||||
evaluationContext.movementCostByRole[HeroRole::MAIN],
|
evaluationContext.movementCostByRole[HeroRole::MAIN],
|
||||||
evaluationContext.movementCostByRole[HeroRole::SCOUT],
|
evaluationContext.movementCostByRole[HeroRole::SCOUT],
|
||||||
@ -1362,23 +1448,27 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
evaluationContext.conquestValue,
|
evaluationContext.conquestValue,
|
||||||
evaluationContext.closestWayRatio,
|
evaluationContext.closestWayRatio,
|
||||||
evaluationContext.enemyHeroDangerRatio,
|
evaluationContext.enemyHeroDangerRatio,
|
||||||
|
dangerThreshold,
|
||||||
evaluationContext.explorePriority,
|
evaluationContext.explorePriority,
|
||||||
evaluationContext.isDefend,
|
evaluationContext.isDefend,
|
||||||
evaluationContext.isEnemy,
|
evaluationContext.isEnemy,
|
||||||
arriveNextWeek);
|
arriveNextWeek,
|
||||||
|
evaluationContext.powerRatio);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (priorityTier)
|
switch (priorityTier)
|
||||||
{
|
{
|
||||||
case PriorityTier::INSTAKILL: //Take towns / kill heroes in immediate reach
|
case PriorityTier::INSTAKILL: //Take towns / kill heroes in immediate reach
|
||||||
{
|
{
|
||||||
if (evaluationContext.turn > 0)
|
if (evaluationContext.turn > 0 || evaluationContext.isExchange)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.movementCost >= 1)
|
if (evaluationContext.movementCost >= 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (evaluationContext.defenseValue < 2 && evaluationContext.enemyHeroDangerRatio > dangerThreshold)
|
||||||
|
return 0;
|
||||||
if(evaluationContext.conquestValue > 0)
|
if(evaluationContext.conquestValue > 0)
|
||||||
score = evaluationContext.armyInvolvement;
|
score = evaluationContext.armyInvolvement;
|
||||||
if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > 1 && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
|
if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > dangerThreshold && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
|
||||||
return 0;
|
return 0;
|
||||||
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1388,23 +1478,47 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
}
|
}
|
||||||
case PriorityTier::INSTADEFEND: //Defend immediately threatened towns
|
case PriorityTier::INSTADEFEND: //Defend immediately threatened towns
|
||||||
{
|
{
|
||||||
if (evaluationContext.isDefend && evaluationContext.threatTurns == 0 && evaluationContext.turn == 0)
|
//No point defending if we don't have defensive-structures
|
||||||
score = evaluationContext.armyInvolvement;
|
if (evaluationContext.defenseValue < 2)
|
||||||
if (evaluationContext.isEnemy && maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
||||||
|
return 0;
|
||||||
|
if (evaluationContext.closestWayRatio < 1.0)
|
||||||
|
return 0;
|
||||||
|
if (evaluationContext.isEnemy && evaluationContext.turn > 0)
|
||||||
|
return 0;
|
||||||
|
if (evaluationContext.isDefend && evaluationContext.threatTurns <= evaluationContext.turn)
|
||||||
|
{
|
||||||
|
const float OPTIMAL_PERCENTAGE = 0.75f; // We want army to be 75% of the threat
|
||||||
|
float optimalStrength = evaluationContext.threat * OPTIMAL_PERCENTAGE;
|
||||||
|
|
||||||
|
// Calculate how far the army is from optimal strength
|
||||||
|
float deviation = std::abs(evaluationContext.armyInvolvement - optimalStrength);
|
||||||
|
|
||||||
|
// Convert deviation to a percentage of the threat to normalize it
|
||||||
|
float deviationPercentage = deviation / evaluationContext.threat;
|
||||||
|
|
||||||
|
// Calculate score: 1.0 is perfect, decreasing as deviation increases
|
||||||
|
score = 1.0f / (1.0f + deviationPercentage);
|
||||||
|
|
||||||
|
// Apply turn penalty to still prefer earlier moves when scores are close
|
||||||
|
score = score / (evaluationContext.turn + 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PriorityTier::KILL: //Take towns / kill heroes that are further away
|
case PriorityTier::KILL: //Take towns / kill heroes that are further away
|
||||||
//FALL_THROUGH
|
//FALL_THROUGH
|
||||||
case PriorityTier::FAR_KILL:
|
case PriorityTier::FAR_KILL:
|
||||||
{
|
{
|
||||||
|
if (evaluationContext.defenseValue < 2 && evaluationContext.enemyHeroDangerRatio > dangerThreshold)
|
||||||
|
return 0;
|
||||||
if (evaluationContext.turn > 0 && evaluationContext.isHero)
|
if (evaluationContext.turn > 0 && evaluationContext.isHero)
|
||||||
return 0;
|
return 0;
|
||||||
if (arriveNextWeek && evaluationContext.isEnemy)
|
if (arriveNextWeek && evaluationContext.isEnemy)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.conquestValue > 0)
|
if (evaluationContext.conquestValue > 0)
|
||||||
score = evaluationContext.armyInvolvement;
|
score = evaluationContext.armyInvolvement;
|
||||||
if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > 1 && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
|
if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > dangerThreshold && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
|
||||||
return 0;
|
return 0;
|
||||||
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1413,24 +1527,9 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
score /= evaluationContext.movementCost;
|
score /= evaluationContext.movementCost;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PriorityTier::UPGRADE:
|
|
||||||
{
|
|
||||||
if (!evaluationContext.isArmyUpgrade)
|
|
||||||
return 0;
|
|
||||||
if (evaluationContext.enemyHeroDangerRatio > 1)
|
|
||||||
return 0;
|
|
||||||
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
|
||||||
return 0;
|
|
||||||
if (vstd::isAlmostZero(evaluationContext.armyLossPersentage) && evaluationContext.closestWayRatio < 1.0)
|
|
||||||
return 0;
|
|
||||||
score = 1000;
|
|
||||||
if (evaluationContext.movementCost > 0)
|
|
||||||
score /= evaluationContext.movementCost;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PriorityTier::HIGH_PRIO_EXPLORE:
|
case PriorityTier::HIGH_PRIO_EXPLORE:
|
||||||
{
|
{
|
||||||
if (evaluationContext.enemyHeroDangerRatio > 1)
|
if (evaluationContext.enemyHeroDangerRatio > dangerThreshold)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.explorePriority != 1)
|
if (evaluationContext.explorePriority != 1)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1447,17 +1546,15 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
//FALL_THROUGH
|
//FALL_THROUGH
|
||||||
case PriorityTier::FAR_HUNTER_GATHER:
|
case PriorityTier::FAR_HUNTER_GATHER:
|
||||||
{
|
{
|
||||||
if (evaluationContext.enemyHeroDangerRatio > 1 && !evaluationContext.isDefend)
|
if (evaluationContext.enemyHeroDangerRatio > dangerThreshold && !evaluationContext.isDefend && priorityTier != PriorityTier::FAR_HUNTER_GATHER)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.buildingCost.marketValue() > 0)
|
if (evaluationContext.buildingCost.marketValue() > 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.isDefend && (evaluationContext.enemyHeroDangerRatio < 1 || evaluationContext.threatTurns > 0 || evaluationContext.turn > 0))
|
if (priorityTier != PriorityTier::FAR_HUNTER_GATHER && evaluationContext.isDefend && (evaluationContext.enemyHeroDangerRatio > dangerThreshold || evaluationContext.threatTurns > 0 || evaluationContext.turn > 0))
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.explorePriority == 3)
|
if (evaluationContext.explorePriority == 3)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.isArmyUpgrade)
|
if (priorityTier != PriorityTier::FAR_HUNTER_GATHER && ((evaluationContext.enemyHeroDangerRatio > 0 && arriveNextWeek) || evaluationContext.enemyHeroDangerRatio > dangerThreshold))
|
||||||
return 0;
|
|
||||||
if ((evaluationContext.enemyHeroDangerRatio > 0 && arriveNextWeek) || evaluationContext.enemyHeroDangerRatio > 1)
|
|
||||||
return 0;
|
return 0;
|
||||||
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
if (maxWillingToLose - evaluationContext.armyLossPersentage < 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1475,12 +1572,14 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
score = 1000;
|
score = 1000;
|
||||||
if (evaluationContext.movementCost > 0)
|
if (evaluationContext.movementCost > 0)
|
||||||
score /= evaluationContext.movementCost;
|
score /= evaluationContext.movementCost;
|
||||||
|
if(priorityTier == PriorityTier::FAR_HUNTER_GATHER && evaluationContext.enemyHeroDangerRatio > 0)
|
||||||
|
score /= evaluationContext.enemyHeroDangerRatio;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PriorityTier::LOW_PRIO_EXPLORE:
|
case PriorityTier::LOW_PRIO_EXPLORE:
|
||||||
{
|
{
|
||||||
if (evaluationContext.enemyHeroDangerRatio > 1)
|
if (evaluationContext.enemyHeroDangerRatio > dangerThreshold)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.explorePriority != 3)
|
if (evaluationContext.explorePriority != 3)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1495,7 +1594,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
}
|
}
|
||||||
case PriorityTier::DEFEND: //Defend whatever if nothing else is to do
|
case PriorityTier::DEFEND: //Defend whatever if nothing else is to do
|
||||||
{
|
{
|
||||||
if (evaluationContext.enemyHeroDangerRatio > 1 && evaluationContext.isExchange)
|
if (evaluationContext.enemyHeroDangerRatio > dangerThreshold)
|
||||||
return 0;
|
return 0;
|
||||||
if (evaluationContext.isDefend || evaluationContext.isArmyUpgrade)
|
if (evaluationContext.isDefend || evaluationContext.isArmyUpgrade)
|
||||||
score = evaluationContext.armyInvolvement;
|
score = evaluationContext.armyInvolvement;
|
||||||
@ -1536,9 +1635,15 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
|
|||||||
TResources needed = evaluationContext.buildingCost - resourcesAvailable;
|
TResources needed = evaluationContext.buildingCost - resourcesAvailable;
|
||||||
needed.positive();
|
needed.positive();
|
||||||
int turnsTo = needed.maxPurchasableCount(income);
|
int turnsTo = needed.maxPurchasableCount(income);
|
||||||
|
bool haveEverythingButGold = true;
|
||||||
|
for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
|
||||||
|
{
|
||||||
|
if (i != GameResID::GOLD && resourcesAvailable[i] < evaluationContext.buildingCost[i])
|
||||||
|
haveEverythingButGold = false;
|
||||||
|
}
|
||||||
if (turnsTo == INT_MAX)
|
if (turnsTo == INT_MAX)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
if (!haveEverythingButGold)
|
||||||
score /= turnsTo;
|
score /= turnsTo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ struct DLL_EXPORT EvaluationContext
|
|||||||
bool isHero;
|
bool isHero;
|
||||||
bool isEnemy;
|
bool isEnemy;
|
||||||
int explorePriority;
|
int explorePriority;
|
||||||
|
float powerRatio;
|
||||||
|
|
||||||
EvaluationContext(const Nullkiller * ai);
|
EvaluationContext(const Nullkiller * ai);
|
||||||
|
|
||||||
@ -114,13 +115,13 @@ public:
|
|||||||
INSTAKILL,
|
INSTAKILL,
|
||||||
INSTADEFEND,
|
INSTADEFEND,
|
||||||
KILL,
|
KILL,
|
||||||
UPGRADE,
|
|
||||||
HIGH_PRIO_EXPLORE,
|
HIGH_PRIO_EXPLORE,
|
||||||
HUNTER_GATHER,
|
HUNTER_GATHER,
|
||||||
LOW_PRIO_EXPLORE,
|
LOW_PRIO_EXPLORE,
|
||||||
FAR_KILL,
|
FAR_KILL,
|
||||||
|
DEFEND,
|
||||||
FAR_HUNTER_GATHER,
|
FAR_HUNTER_GATHER,
|
||||||
DEFEND
|
MAX_PRIORITY_TIER = FAR_HUNTER_GATHER
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -583,42 +583,28 @@ public:
|
|||||||
|
|
||||||
bool AINodeStorage::calculateHeroChain()
|
bool AINodeStorage::calculateHeroChain()
|
||||||
{
|
{
|
||||||
std::random_device randomDevice;
|
|
||||||
std::mt19937 randomEngine(randomDevice());
|
|
||||||
|
|
||||||
heroChainPass = EHeroChainPass::CHAIN;
|
heroChainPass = EHeroChainPass::CHAIN;
|
||||||
heroChain.clear();
|
heroChain.clear();
|
||||||
|
|
||||||
std::vector<int3> data(committedTiles.begin(), committedTiles.end());
|
std::vector<int3> data(committedTiles.begin(), committedTiles.end());
|
||||||
|
|
||||||
if(data.size() > 100)
|
int maxConcurrency = tbb::this_task_arena::max_concurrency();
|
||||||
|
std::vector<std::vector<CGPathNode *>> results(maxConcurrency);
|
||||||
|
|
||||||
|
logAi->trace("Caculating hero chain for %d items", data.size());
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, data.size()), [&](const tbb::blocked_range<size_t>& r)
|
||||||
{
|
{
|
||||||
boost::mutex resultMutex;
|
|
||||||
|
|
||||||
std::shuffle(data.begin(), data.end(), randomEngine);
|
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, data.size()), [&](const tbb::blocked_range<size_t>& r)
|
|
||||||
{
|
|
||||||
//auto r = blocked_range<size_t>(0, data.size());
|
|
||||||
HeroChainCalculationTask task(*this, data, chainMask, heroChainTurn);
|
|
||||||
|
|
||||||
task.execute(r);
|
|
||||||
|
|
||||||
{
|
|
||||||
boost::lock_guard<boost::mutex> resultLock(resultMutex);
|
|
||||||
|
|
||||||
task.flushResult(heroChain);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto r = tbb::blocked_range<size_t>(0, data.size());
|
|
||||||
HeroChainCalculationTask task(*this, data, chainMask, heroChainTurn);
|
HeroChainCalculationTask task(*this, data, chainMask, heroChainTurn);
|
||||||
|
|
||||||
|
int ourThread = tbb::this_task_arena::current_thread_index();
|
||||||
task.execute(r);
|
task.execute(r);
|
||||||
task.flushResult(heroChain);
|
task.flushResult(results.at(ourThread));
|
||||||
}
|
});
|
||||||
|
|
||||||
|
// FIXME: potentially non-deterministic behavior due to parallel_for
|
||||||
|
for (const auto & result : results)
|
||||||
|
vstd::concatenate(heroChain, result);
|
||||||
|
|
||||||
committedTiles.clear();
|
committedTiles.clear();
|
||||||
|
|
||||||
@ -1464,9 +1450,20 @@ void AINodeStorage::calculateChainInfo(std::vector<AIPath> & paths, const int3 &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fortLevel = 0;
|
||||||
|
auto visitableObjects = cb->getVisitableObjs(pos);
|
||||||
|
for (auto obj : visitableObjects)
|
||||||
|
{
|
||||||
|
if (objWithID<Obj::TOWN>(obj))
|
||||||
|
{
|
||||||
|
auto town = dynamic_cast<const CGTownInstance*>(obj);
|
||||||
|
fortLevel = town->fortLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path.targetObjectArmyLoss = evaluateArmyLoss(
|
path.targetObjectArmyLoss = evaluateArmyLoss(
|
||||||
path.targetHero,
|
path.targetHero,
|
||||||
getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy),
|
getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy, fortLevel),
|
||||||
path.targetObjectDanger);
|
path.targetObjectDanger);
|
||||||
|
|
||||||
path.chainMask = node.actor->chainMask;
|
path.chainMask = node.actor->chainMask;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../CCallback.h"
|
#include "../../../CCallback.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
|
#include "../../../lib/mapping/CMapDefines.h"
|
||||||
#include "../../../lib/pathfinder/TurnInfo.h"
|
#include "../../../lib/pathfinder/TurnInfo.h"
|
||||||
#include "Actions/BuyArmyAction.h"
|
#include "Actions/BuyArmyAction.h"
|
||||||
|
|
||||||
@ -394,7 +395,7 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
|
|||||||
HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
||||||
{
|
{
|
||||||
auto * target = new HeroExchangeArmy();
|
auto * target = new HeroExchangeArmy();
|
||||||
auto bestArmy = ai->armyManager->getBestArmy(actor->hero, army1, army2);
|
auto bestArmy = ai->armyManager->getBestArmy(actor->hero, army1, army2, ai->cb->getTile(actor->hero->visitablePos())->getTerrainID());
|
||||||
|
|
||||||
for(auto & slotInfo : bestArmy)
|
for(auto & slotInfo : bestArmy)
|
||||||
{
|
{
|
||||||
|
@ -109,6 +109,8 @@ include(CMakeDependentOption)
|
|||||||
cmake_dependent_option(ENABLE_INNOEXTRACT "Enable innoextract for GOG file extraction in launcher" ON "ENABLE_LAUNCHER" OFF)
|
cmake_dependent_option(ENABLE_INNOEXTRACT "Enable innoextract for GOG file extraction in launcher" ON "ENABLE_LAUNCHER" OFF)
|
||||||
cmake_dependent_option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON "NOT ENABLE_GOLDMASTER" OFF)
|
cmake_dependent_option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON "NOT ENABLE_GOLDMASTER" OFF)
|
||||||
|
|
||||||
|
option(VCMI_PORTMASTER "PortMaster build" OFF)
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# Miscellaneous options #
|
# Miscellaneous options #
|
||||||
############################################
|
############################################
|
||||||
|
@ -319,6 +319,25 @@
|
|||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"ANDROID_GRADLE_PROPERTIES": "applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily;applicationVariant=daily"
|
"ANDROID_GRADLE_PROPERTIES": "applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily;applicationVariant=daily"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "portmaster-release",
|
||||||
|
"displayName": "PortMaster",
|
||||||
|
"description": "VCMI PortMaster",
|
||||||
|
"inherits": "default-release",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release",
|
||||||
|
"CMAKE_INSTALL_PREFIX": ".",
|
||||||
|
"ENABLE_DEBUG_CONSOLE": "OFF",
|
||||||
|
"ENABLE_EDITOR": "OFF",
|
||||||
|
"ENABLE_GITVERSION": "OFF",
|
||||||
|
"ENABLE_LAUNCHER": "OFF",
|
||||||
|
"ENABLE_SERVER": "OFF",
|
||||||
|
"ENABLE_TRANSLATIONS": "OFF",
|
||||||
|
"FORCE_BUNDLED_FL": "ON",
|
||||||
|
"ENABLE_GOLDMASTER": "ON",
|
||||||
|
"VCMI_PORTMASTER": "ON"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildPresets": [
|
"buildPresets": [
|
||||||
@ -447,6 +466,12 @@
|
|||||||
"name": "android-daily-release",
|
"name": "android-daily-release",
|
||||||
"configurePreset": "android-daily-release",
|
"configurePreset": "android-daily-release",
|
||||||
"inherits": "android-conan-ninja-release"
|
"inherits": "android-conan-ninja-release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "portmaster-release",
|
||||||
|
"configurePreset": "portmaster-release",
|
||||||
|
"inherits": "default-release",
|
||||||
|
"configuration": "Release"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"testPresets": [
|
"testPresets": [
|
||||||
|
75
ChangeLog.md
75
ChangeLog.md
@ -1,5 +1,80 @@
|
|||||||
# VCMI Project Changelog
|
# VCMI Project Changelog
|
||||||
|
|
||||||
|
## 1.6.5 -> 1.6.6
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
* Game no longer requires local network connection for single player games
|
||||||
|
* Reduced size of obstacle-filled junction zones in Coldshadow Fantasy template
|
||||||
|
* Upscaling filter xbrz x2 is now enabled by default on mobile systems
|
||||||
|
* Fixes failure to import Chronicles on Windows with non-ascii characters in username
|
||||||
|
* Added support for importing Chronicles using old All-in-One installer from gog.com
|
||||||
|
* It is now possible to enable portrait mode on mobile systems.
|
||||||
|
* Fixed grey bar at top of screen when returning to app while in game on Android
|
||||||
|
|
||||||
|
### Stability
|
||||||
|
|
||||||
|
* Fixed possible crash on opening unit description with unavailable upgrades
|
||||||
|
* Fixed crash on winning game after last player loses the game due to not controlling a town for 7 days
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
* Pressing Q during hero exchange will now swap both army and artifacts and will no longer trigger a quest log
|
||||||
|
* Spellbook search is no longer enabled by default, allowing standard h3 shortcuts to work. Search can now be activated by pressing Tab
|
||||||
|
* Ctrl/Shift + click on arrow buttons below creature slots during hero exchange now works in the familiar way from hd mod
|
||||||
|
* On mobile systems, clicking on a blocked tile of a visitable object on the adventure map will now build a path to it
|
||||||
|
* It is now possible to activate the adventure map overlay on the mobile system using the two-finger tap gesture
|
||||||
|
* Fixed incorrect pinch event calculation that caused problems when zooming with touchscreen gestures
|
||||||
|
* Game now displays both total cost in movement points and estimated time to arrive in turns when hovering over an accessible location
|
||||||
|
* Artifact sort buttons in the Hero Backpack window now have correct text describing the sort order
|
||||||
|
* Fixed non-standard color handling for shadows under selection highlight in creature animations from mods such as HotA's Iron Golem
|
||||||
|
* Effects such as Bloodlust, Clone, and Petrify will now display correctly when xbrz is in use
|
||||||
|
* Fixed broken Chronicles campaign screen available with new main menu themes mod
|
||||||
|
* Fixed empty bonus shown in unit info window when unit is in Necropolis with Cover of Darkness built
|
||||||
|
* Right-clicking on the difficulty button will now display the difficulty description popup
|
||||||
|
* Fixed regression causing two minus signs in Fountain of Fortune description
|
||||||
|
* Added option to upgrade all creatures in the radial menu when in town
|
||||||
|
* Added option to display remaining unit health in the form of a health bar
|
||||||
|
* Fixed regression that caused unavailable tiles to be displayed on the left and right sides of the battlefield when hovering with the mouse
|
||||||
|
* Fixed regression that caused all spells to be displayed as having a duration of 16 rounds
|
||||||
|
* Scrolling in the lobby window now only happens when hovering over the appropriate item, instead of scrolling all scrollable widgets at once
|
||||||
|
* Fixed regression that caused black pixels on some hero portraits in mods that use 8-bit palette images
|
||||||
|
* Fixed memory leak when upscaling images with xbrz filter
|
||||||
|
* Fixed creature windows text align and buttons background
|
||||||
|
|
||||||
|
### Mechanics
|
||||||
|
|
||||||
|
* It is no longer possible to attack heroes standing on a visitable object from blocked tiles or from water when the attacker uses Fly
|
||||||
|
* Fixed regression from 1.6 that caused multiple taverns in towns of the same faction to not be counted towards the level of information available for the thieves' guild
|
||||||
|
* Fixed regression that caused Cove towns placed on map to be replaced with Castles on HotA maps
|
||||||
|
* The amount of gold a player can receive from a bonfire is now always equal to the amount of rare resources received multiplied by 100
|
||||||
|
* Disabled default victory conditions on all Elixir of Life campaign maps that require an artifact to be found, in line with H3
|
||||||
|
|
||||||
|
### Nullkiller AI
|
||||||
|
|
||||||
|
* Improved scoring of town buildings by the AI
|
||||||
|
* AI will now prefer to give faster units to its scout heroes to optimize their movement points in future turns
|
||||||
|
* Fixed AI not constructing prerequisites for town buildings in some cases, like not building Stables when attempting to build Training Grounds
|
||||||
|
* AI will now avoid recruiting heroes if AI is low on gold or if the town is threatened by an enemy hero
|
||||||
|
* AI will no longer attempt to use more than one hero to defend a town
|
||||||
|
* AI will now devalue non-flying units when attacking towns with fortifications to prevent suicides against castles
|
||||||
|
* Increased the priority of building unupgraded dwellings, as they provide units that can be hired immediately, rather than next week like citadels and castles
|
||||||
|
* When multiple cities are threatened, the AI will now prefer to defend the one that takes the least number of turns to reach
|
||||||
|
* Fixed AI attempting to restore mana points in town without a mage guild built
|
||||||
|
* Reduced AI prioritization of army merging to the same level as general gathering
|
||||||
|
* AI will now prioritize army merging before attacking enemies
|
||||||
|
* Increased AI defense prioritization
|
||||||
|
* AI will no longer leave the defense of a threatened town in order to bring the army to another hero
|
||||||
|
* AI will no longer send heroes to die outside of towns that already have a garrisoning hero inside, if there's a stronger enemy hero lurking around the town
|
||||||
|
* AI will no longer focus excessively on reaching Keymaster tents
|
||||||
|
* AI will no longer rush towns that don't have a citadel or better if there is a strong enemy hero in the area
|
||||||
|
* AI will no longer try to maximize defenses by using the strongest defender possible, but will instead try to use the most appropriate defender
|
||||||
|
* Heroes that are currently threatened will be braver and not worry about attacking things that are also threatened if nothing safe is in range
|
||||||
|
|
||||||
|
### Launcher
|
||||||
|
|
||||||
|
* Added context menu for mod lists that allows disabling, enabling, installing, uninstalling, updating, opening installed mod location, and opening mod repository
|
||||||
|
|
||||||
## 1.6.4 -> 1.6.5
|
## 1.6.4 -> 1.6.5
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
BIN
Mods/vcmi/Content/Sprites/radialMenu/upgradeCreatures.png
Normal file
BIN
Mods/vcmi/Content/Sprites/radialMenu/upgradeCreatures.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 567 B |
Binary file not shown.
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@ -23,8 +23,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "没有酒馆可供查看。",
|
"vcmi.adventureMap.noTownWithTavern" : "没有酒馆可供查看。",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "无此魔法的信息。",
|
"vcmi.adventureMap.spellUnknownProblem" : "无此魔法的信息。",
|
||||||
"vcmi.adventureMap.playerAttacked" : "玩家遭受攻击: %s",
|
"vcmi.adventureMap.playerAttacked" : "玩家遭受攻击: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "移动点数 - 花费: %TURNS 轮 + %POINTS 点移动力, 剩余移动力: %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "移动点数 - 花费: %POINTS 点移动力, 剩余移动力: %REMAINING",
|
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(移动点数: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(移动点数: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "抱歉,重放对手行动功能目前暂未实现!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "抱歉,重放对手行动功能目前暂未实现!",
|
||||||
|
|
||||||
@ -422,11 +420,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "开启宝物背包界面",
|
"vcmi.heroWindow.openBackpack.hover" : "开启宝物背包界面",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "用更大的界面显示所有获得的宝物",
|
"vcmi.heroWindow.openBackpack.help" : "用更大的界面显示所有获得的宝物",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "按价格排序",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "按价格排序",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "将行囊里的宝物按价格排序。",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{按价格排序}\n\n将行囊里的宝物按价格排序。",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "按装备槽排序",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "按装备槽排序",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "将行囊里的宝物按装备槽排序。",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{按装备槽排序}\n\n将行囊里的宝物按装备槽排序。",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "按类型排序",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "按类型排序",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "将行囊里的宝物按装备槽排序:低级宝物、中级宝物、高级宝物、圣物。",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{按类型排序}\n\n将行囊里的宝物按装备槽排序:低级宝物、中级宝物、高级宝物、圣物。",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "你已拥有融合%s所需的全部组件,想现在进行融合吗?{所有组件在融合后将被消耗。}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "你已拥有融合%s所需的全部组件,想现在进行融合吗?{所有组件在融合后将被消耗。}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "邀请英雄",
|
"vcmi.tavernWindow.inviteHero" : "邀请英雄",
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Nejsou dostupná žádná města s putykou!",
|
"vcmi.adventureMap.noTownWithTavern" : "Nejsou dostupná žádná města s putykou!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Neznámý problém s tímto kouzlem! Další informace nejsou k dispozici.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Neznámý problém s tímto kouzlem! Další informace nejsou k dispozici.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Hráč byl napaden: %s",
|
"vcmi.adventureMap.playerAttacked" : "Hráč byl napaden: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Body pohybu - Cena: %TURNS tahů + %POINTS bodů, zbylé body: %REMAINING",
|
"vcmi.adventureMap.moveCostDetails" : "Přesun sem tě bude stát {%TOTAL} bodů (za {%TURNS} tahů a {%POINTS} bodů). Po přesunu ti zbyde {%REMAINING} bodů.",
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Body pohybu - Cena: %POINTS bodů, zbylé body: %REMAINING",
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Přesun sem tě bude stát {%POINTS} bodů. Po přesunu ti zbyde {%REMAINING} bodů.",
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Body pohybu: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Body pohybu: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Omlouváme se, přehrání tahu soupeře ještě není implementováno!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Omlouváme se, přehrání tahu soupeře ještě není implementováno!",
|
||||||
|
|
||||||
"vcmi.bonusSource.artifact" : "Artefakt",
|
"vcmi.bonusSource.artifact" : "Artefakt",
|
||||||
@ -68,6 +68,7 @@
|
|||||||
"vcmi.radialWheel.heroGetArtifacts" : "Získat artefakty od jiného hrdiny",
|
"vcmi.radialWheel.heroGetArtifacts" : "Získat artefakty od jiného hrdiny",
|
||||||
"vcmi.radialWheel.heroSwapArtifacts" : "Vyměnit artefakty s jiným hrdinou",
|
"vcmi.radialWheel.heroSwapArtifacts" : "Vyměnit artefakty s jiným hrdinou",
|
||||||
"vcmi.radialWheel.heroDismiss" : "Propustit hrdinu",
|
"vcmi.radialWheel.heroDismiss" : "Propustit hrdinu",
|
||||||
|
"vcmi.radialWheel.upgradeCreatures" : "Vylepšit všechny jednotky",
|
||||||
|
|
||||||
"vcmi.radialWheel.moveTop" : "Přesunout nahoru",
|
"vcmi.radialWheel.moveTop" : "Přesunout nahoru",
|
||||||
"vcmi.radialWheel.moveUp" : "Posunout výše",
|
"vcmi.radialWheel.moveUp" : "Posunout výše",
|
||||||
@ -86,9 +87,9 @@
|
|||||||
|
|
||||||
"vcmi.spellBook.search" : "Hledat",
|
"vcmi.spellBook.search" : "Hledat",
|
||||||
|
|
||||||
"vcmi.spellResearch.canNotAfford" : "Nemáte dostatek prostředků k nahrazení {%SPELL1} za {%SPELL2}. Stále však můžete toto kouzlo zrušit a pokračovat ve výzkumu dalších kouzel.",
|
"vcmi.spellResearch.canNotAfford" : "Nemáš dostatek prostředků na výměnu kouzla {%SPELL1} za {%SPELL2}. Můžeš ho však odstranit a pokračovat ve výzkumu.",
|
||||||
"vcmi.spellResearch.comeAgain" : "Výzkum už byl dnes proveden. Vraťte se zítra.",
|
"vcmi.spellResearch.comeAgain" : "Výzkum už byl dnes proveden. Vrať se zítra.",
|
||||||
"vcmi.spellResearch.pay" : "Chcete nahradit {%SPELL1} za {%SPELL2}? Nebo zrušit toto kouzlo a pokračovat ve výzkumu dalších kouzel?",
|
"vcmi.spellResearch.pay" : "Chceš nahradit {%SPELL1} za {%SPELL2}? Nebo zrušit toto kouzlo a pokračovat ve výzkumu dalších kouzel?",
|
||||||
"vcmi.spellResearch.research" : "Prozkoumat toto kouzlo",
|
"vcmi.spellResearch.research" : "Prozkoumat toto kouzlo",
|
||||||
"vcmi.spellResearch.skip" : "Přeskočit toto kouzlo",
|
"vcmi.spellResearch.skip" : "Přeskočit toto kouzlo",
|
||||||
"vcmi.spellResearch.abort" : "Přerušit",
|
"vcmi.spellResearch.abort" : "Přerušit",
|
||||||
@ -168,10 +169,10 @@
|
|||||||
"vcmi.lobby.login.as" : "Přihlásit se jako %s",
|
"vcmi.lobby.login.as" : "Přihlásit se jako %s",
|
||||||
"vcmi.lobby.login.spectator" : "Divák",
|
"vcmi.lobby.login.spectator" : "Divák",
|
||||||
"vcmi.lobby.header.rooms" : "Herní místnosti - %d",
|
"vcmi.lobby.header.rooms" : "Herní místnosti - %d",
|
||||||
"vcmi.lobby.header.channels" : "Kanály konverzace",
|
"vcmi.lobby.header.channels" : "Kanály chatu",
|
||||||
"vcmi.lobby.header.chat.global" : "Globální konverzace hry - %s", // %s -> language name
|
"vcmi.lobby.header.chat.global" : "Globální chat hry - %s", // %s -> language name
|
||||||
"vcmi.lobby.header.chat.match" : "Konverzace předchozí hry %s", // %s -> game start date & time
|
"vcmi.lobby.header.chat.match" : "Chat předchozí hry %s", // %s -> game start date & time
|
||||||
"vcmi.lobby.header.chat.player" : "Soukromá konverzace s %s", // %s -> nickname of another player
|
"vcmi.lobby.header.chat.player" : "Soukromý chat s %s", // %s -> nickname of another player
|
||||||
"vcmi.lobby.header.history" : "Vaše předchozí hry",
|
"vcmi.lobby.header.history" : "Vaše předchozí hry",
|
||||||
"vcmi.lobby.header.players" : "Online hráči - %d",
|
"vcmi.lobby.header.players" : "Online hráči - %d",
|
||||||
"vcmi.lobby.match.solo" : "Hra jednoho hráče",
|
"vcmi.lobby.match.solo" : "Hra jednoho hráče",
|
||||||
@ -185,7 +186,7 @@
|
|||||||
"vcmi.lobby.room.description.load" : "Pro start hry načtěte uloženou hru.",
|
"vcmi.lobby.room.description.load" : "Pro start hry načtěte uloženou hru.",
|
||||||
"vcmi.lobby.room.description.limit" : "Až %d hráčů se může připojit do vaší místnosti (včetně vás).",
|
"vcmi.lobby.room.description.limit" : "Až %d hráčů se může připojit do vaší místnosti (včetně vás).",
|
||||||
"vcmi.lobby.invite.header" : "Pozvat hráče",
|
"vcmi.lobby.invite.header" : "Pozvat hráče",
|
||||||
"vcmi.lobby.invite.notification" : "Pozval vás hráč do jejich soukromé místnosti. Nyní se do ní můžete připojit.",
|
"vcmi.lobby.invite.notification" : "Hráč vás pozval do své soukromé místnosti. Nyní se k ní můžete připojit.",
|
||||||
"vcmi.lobby.preview.title" : "Připojit se do herní místnosti",
|
"vcmi.lobby.preview.title" : "Připojit se do herní místnosti",
|
||||||
"vcmi.lobby.preview.subtitle" : "Hra na %s, pořádána %s", //TL Note: 1) name of map or RMG template 2) nickname of game host
|
"vcmi.lobby.preview.subtitle" : "Hra na %s, pořádána %s", //TL Note: 1) name of map or RMG template 2) nickname of game host
|
||||||
"vcmi.lobby.preview.version" : "Verze hry:",
|
"vcmi.lobby.preview.version" : "Verze hry:",
|
||||||
@ -216,9 +217,9 @@
|
|||||||
"vcmi.lobby.pvp.coin.hover" : "Mince",
|
"vcmi.lobby.pvp.coin.hover" : "Mince",
|
||||||
"vcmi.lobby.pvp.coin.help" : "Hodí mincí",
|
"vcmi.lobby.pvp.coin.help" : "Hodí mincí",
|
||||||
"vcmi.lobby.pvp.randomTown.hover" : "Náhodné město",
|
"vcmi.lobby.pvp.randomTown.hover" : "Náhodné město",
|
||||||
"vcmi.lobby.pvp.randomTown.help" : "Napsat náhodné město do konvezace",
|
"vcmi.lobby.pvp.randomTown.help" : "Napsat náhodné město do chatu",
|
||||||
"vcmi.lobby.pvp.randomTownVs.hover" : "Náhodné město vs.",
|
"vcmi.lobby.pvp.randomTownVs.hover" : "Náhodné město vs.",
|
||||||
"vcmi.lobby.pvp.randomTownVs.help" : "Napsat 2 náhodná města do konvezace",
|
"vcmi.lobby.pvp.randomTownVs.help" : "Napsat 2 náhodná města do chatu",
|
||||||
"vcmi.lobby.pvp.versus" : "vs.",
|
"vcmi.lobby.pvp.versus" : "vs.",
|
||||||
|
|
||||||
"vcmi.client.errors.invalidMap" : "{Neplatná mapa nebo kampaň}\n\nChyba při startu hry! Vybraná mapa nebo kampaň může být neplatná nebo poškozená. Důvod:\n%s",
|
"vcmi.client.errors.invalidMap" : "{Neplatná mapa nebo kampaň}\n\nChyba při startu hry! Vybraná mapa nebo kampaň může být neplatná nebo poškozená. Důvod:\n%s",
|
||||||
@ -363,6 +364,8 @@
|
|||||||
"vcmi.battleOptions.endWithAutocombat.help" : "{Přeskočit bitvu}\n\nAutomatický boj okamžitě dohraje bitvu do konce.",
|
"vcmi.battleOptions.endWithAutocombat.help" : "{Přeskočit bitvu}\n\nAutomatický boj okamžitě dohraje bitvu do konce.",
|
||||||
"vcmi.battleOptions.showQuickSpell.hover" : "Zobrazit rychlý panel kouzel",
|
"vcmi.battleOptions.showQuickSpell.hover" : "Zobrazit rychlý panel kouzel",
|
||||||
"vcmi.battleOptions.showQuickSpell.help" : "{Zobrazit rychlý panel kouzel}\n\nZobrazí panel pro rychlý výběr kouzel.",
|
"vcmi.battleOptions.showQuickSpell.help" : "{Zobrazit rychlý panel kouzel}\n\nZobrazí panel pro rychlý výběr kouzel.",
|
||||||
|
"vcmi.battleOptions.showHealthBar.hover": "Zobrazit ukazatel zdraví",
|
||||||
|
"vcmi.battleOptions.showHealthBar.help": "{Zobrazit ukazatel zdraví}\n\nZobrazí ukazatel, který znázorňuje, kolik zdraví zbývá, než jednotka zemře.",
|
||||||
|
|
||||||
"vcmi.adventureMap.revisitObject.hover" : "Znovu navštívit objekt",
|
"vcmi.adventureMap.revisitObject.hover" : "Znovu navštívit objekt",
|
||||||
"vcmi.adventureMap.revisitObject.help" : "{Znovu navštívit objekt}\n\nPokud hrdina právě stojí na objektu na mapě, může toto místo znovu navštívit.",
|
"vcmi.adventureMap.revisitObject.help" : "{Znovu navštívit objekt}\n\nPokud hrdina právě stojí na objektu na mapě, může toto místo znovu navštívit.",
|
||||||
@ -415,6 +418,9 @@
|
|||||||
"vcmi.townStructure.bank.borrow" : "Vstupujete do banky. Bankéř vás spatří a říká: \"Máme pro vás speciální nabídku. Můžete si vzít půjčku 2500 zlata na 5 dní. Každý den budete muset splácet 500 zlata.\"",
|
"vcmi.townStructure.bank.borrow" : "Vstupujete do banky. Bankéř vás spatří a říká: \"Máme pro vás speciální nabídku. Můžete si vzít půjčku 2500 zlata na 5 dní. Každý den budete muset splácet 500 zlata.\"",
|
||||||
"vcmi.townStructure.bank.payBack" : "Vstupujete do banky. Bankéř vás spatří a říká: \"Již jste si vzali půjčku. Nejprve ji splaťte, než si vezmete další.\"",
|
"vcmi.townStructure.bank.payBack" : "Vstupujete do banky. Bankéř vás spatří a říká: \"Již jste si vzali půjčku. Nejprve ji splaťte, než si vezmete další.\"",
|
||||||
|
|
||||||
|
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Nemáte dostatek surovin na vylepšení všech jednotek. Chcete vylepšit následující jednotky?",
|
||||||
|
"vcmi.townWindow.upgradeAll.notUpgradable" : "Nemáte dostatek surovin na vylepšení žádné z jednotek.",
|
||||||
|
|
||||||
"vcmi.logicalExpressions.anyOf" : "Nějaké z následujících:",
|
"vcmi.logicalExpressions.anyOf" : "Nějaké z následujících:",
|
||||||
"vcmi.logicalExpressions.allOf" : "Všechny následující:",
|
"vcmi.logicalExpressions.allOf" : "Všechny následující:",
|
||||||
"vcmi.logicalExpressions.noneOf" : "Žádné z následujících:",
|
"vcmi.logicalExpressions.noneOf" : "Žádné z následujících:",
|
||||||
@ -424,11 +430,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Otevřít okno s artefakty",
|
"vcmi.heroWindow.openBackpack.hover" : "Otevřít okno s artefakty",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Otevře okno, které umožňuje snadnější správu artefaktů v batohu.",
|
"vcmi.heroWindow.openBackpack.help" : "Otevře okno, které umožňuje snadnější správu artefaktů v batohu.",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Seřadit podle ceny",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Seřadit podle ceny",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Seřadí artefakty v batohu podle ceny.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Seřadit podle ceny}\n\nSeřadí artefakty v batohu podle ceny.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Seřadit podle slotu",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Seřadit podle slotu",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Seřadí artefakty v batohu podle přiřazeného slotu.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Seřadit podle slotu}\n\nSeřadí artefakty v batohu podle přiřazeného slotu.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Seřadit podle třídy",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Seřadit podle třídy",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Seřadí artefakty v batohu podle třídy artefaktu. Poklad, Menší, Větší, Relikvie.",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Seřadit podle třídy}\n\nSeřadí artefakty v batohu podle třídy artefaktu. Poklad, Menší, Větší, Relikvie.",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Máte všechny potřebné části k vytvoření %s. Chcete provést sloučení? {Při sloučení budou použity všechny části.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Máte všechny potřebné části k vytvoření %s. Chcete provést sloučení? {Při sloučení budou použity všechny části.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Pozvat hrdinu",
|
"vcmi.tavernWindow.inviteHero" : "Pozvat hrdinu",
|
||||||
@ -807,4 +813,4 @@
|
|||||||
"spell.core.strongholdMoatTrigger.name" : "Dřevěné bodce",
|
"spell.core.strongholdMoatTrigger.name" : "Dřevěné bodce",
|
||||||
"spell.core.summonDemons.name" : "Přivolání démonů",
|
"spell.core.summonDemons.name" : "Přivolání démonů",
|
||||||
"spell.core.towerMoat.name" : "Pozemní mina"
|
"spell.core.towerMoat.name" : "Pozemní mina"
|
||||||
}
|
}
|
@ -23,16 +23,16 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "There are no available towns with taverns!",
|
"vcmi.adventureMap.noTownWithTavern" : "There are no available towns with taverns!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "There is an unknown problem with this spell! No more information is available.",
|
"vcmi.adventureMap.spellUnknownProblem" : "There is an unknown problem with this spell! No more information is available.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Player has been attacked: %s",
|
"vcmi.adventureMap.playerAttacked" : "Player has been attacked: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
|
"vcmi.adventureMap.moveCostDetails" : "Moving here will cost {%TOTAL} points in total ({%TURNS} turns and {%POINTS} points). {%REMAINING} points will remain after moving.",
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Moving here will cost {%POINTS} points. {%REMAINING} points will remain after moving.",
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Movement points: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Movement points: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sorry, replay opponent turn is not implemented yet!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sorry, replay opponent turn is not implemented yet!",
|
||||||
|
|
||||||
"vcmi.bonusSource.artifact" : "Artifact",
|
"vcmi.bonusSource.artifact" : "Artifact",
|
||||||
"vcmi.bonusSource.creature" : "Ability",
|
"vcmi.bonusSource.creature" : "Ability",
|
||||||
"vcmi.bonusSource.spell" : "Spell",
|
"vcmi.bonusSource.spell" : "Spell",
|
||||||
"vcmi.bonusSource.hero" : "Hero",
|
"vcmi.bonusSource.hero" : "Hero",
|
||||||
"vcmi.bonusSource.commander" : "Commander",
|
"vcmi.bonusSource.commander" : "Command.",
|
||||||
"vcmi.bonusSource.other" : "Other",
|
"vcmi.bonusSource.other" : "Other",
|
||||||
|
|
||||||
"vcmi.capitalColors.0" : "Red",
|
"vcmi.capitalColors.0" : "Red",
|
||||||
@ -68,6 +68,7 @@
|
|||||||
"vcmi.radialWheel.heroGetArtifacts" : "Get artifacts from other hero",
|
"vcmi.radialWheel.heroGetArtifacts" : "Get artifacts from other hero",
|
||||||
"vcmi.radialWheel.heroSwapArtifacts" : "Swap artifacts with other hero",
|
"vcmi.radialWheel.heroSwapArtifacts" : "Swap artifacts with other hero",
|
||||||
"vcmi.radialWheel.heroDismiss" : "Dismiss hero",
|
"vcmi.radialWheel.heroDismiss" : "Dismiss hero",
|
||||||
|
"vcmi.radialWheel.upgradeCreatures" : "Upgrade all creatures",
|
||||||
|
|
||||||
"vcmi.radialWheel.moveTop" : "Move to top",
|
"vcmi.radialWheel.moveTop" : "Move to top",
|
||||||
"vcmi.radialWheel.moveUp" : "Move up",
|
"vcmi.radialWheel.moveUp" : "Move up",
|
||||||
@ -363,6 +364,8 @@
|
|||||||
"vcmi.battleOptions.endWithAutocombat.help": "{Ends battle}\n\nAuto-Combat plays battle to end instant",
|
"vcmi.battleOptions.endWithAutocombat.help": "{Ends battle}\n\nAuto-Combat plays battle to end instant",
|
||||||
"vcmi.battleOptions.showQuickSpell.hover": "Show Quickspell panel",
|
"vcmi.battleOptions.showQuickSpell.hover": "Show Quickspell panel",
|
||||||
"vcmi.battleOptions.showQuickSpell.help": "{Show Quickspell panel}\n\nShow panel for quick selecting spells",
|
"vcmi.battleOptions.showQuickSpell.help": "{Show Quickspell panel}\n\nShow panel for quick selecting spells",
|
||||||
|
"vcmi.battleOptions.showHealthBar.hover": "Show health bar",
|
||||||
|
"vcmi.battleOptions.showHealthBar.help": "{Show health bar}\n\nShow health bar indicating remaining health before one unit dies.",
|
||||||
|
|
||||||
"vcmi.adventureMap.revisitObject.hover" : "Revisit Object",
|
"vcmi.adventureMap.revisitObject.hover" : "Revisit Object",
|
||||||
"vcmi.adventureMap.revisitObject.help" : "{Revisit Object}\n\nIf a hero currently stands on a Map Object, he can revisit the location.",
|
"vcmi.adventureMap.revisitObject.help" : "{Revisit Object}\n\nIf a hero currently stands on a Map Object, he can revisit the location.",
|
||||||
@ -415,6 +418,9 @@
|
|||||||
"vcmi.townStructure.bank.borrow" : "You enter the bank. A banker sees you and says: \"We have made a special offer for you. You can take a loan of 2500 gold from us for 5 days. You will have to repay 500 gold every day.\"",
|
"vcmi.townStructure.bank.borrow" : "You enter the bank. A banker sees you and says: \"We have made a special offer for you. You can take a loan of 2500 gold from us for 5 days. You will have to repay 500 gold every day.\"",
|
||||||
"vcmi.townStructure.bank.payBack" : "You enter the bank. A banker sees you and says: \"You have already got your loan. Pay it back before taking a new one.\"",
|
"vcmi.townStructure.bank.payBack" : "You enter the bank. A banker sees you and says: \"You have already got your loan. Pay it back before taking a new one.\"",
|
||||||
|
|
||||||
|
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Not enough resources to upgrade all creatures. Do you want to upgrade following creatures?",
|
||||||
|
"vcmi.townWindow.upgradeAll.notUpgradable" : "Not enough resources to upgrade any creature.",
|
||||||
|
|
||||||
"vcmi.logicalExpressions.anyOf" : "Any of the following:",
|
"vcmi.logicalExpressions.anyOf" : "Any of the following:",
|
||||||
"vcmi.logicalExpressions.allOf" : "All of the following:",
|
"vcmi.logicalExpressions.allOf" : "All of the following:",
|
||||||
"vcmi.logicalExpressions.noneOf" : "None of the following:",
|
"vcmi.logicalExpressions.noneOf" : "None of the following:",
|
||||||
@ -423,12 +429,12 @@
|
|||||||
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero.",
|
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero.",
|
||||||
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
|
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.",
|
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sort by cost",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "By value",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Sort artifacts in backpack by cost.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Sort by cost}\n\nSort artifacts in backpack by cost.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sort by slot",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "By slot",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sort artifacts in backpack by equipped slot.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Sort by slot}\n\nSort artifacts in backpack by equipped slot.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sort by class",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "By class",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Sort artifacts in backpack by artifact class. Treasure, Minor, Major, Relic",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Sort by class}\n\nSort artifacts in backpack by artifact class. Treasure, Minor, Major, Relic",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "You possess all of the components needed for the fusion of the %s. Do you wish to perform the fusion? {All components will be consumed upon fusion.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "You possess all of the components needed for the fusion of the %s. Do you wish to perform the fusion? {All components will be consumed upon fusion.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Invite hero",
|
"vcmi.tavernWindow.inviteHero" : "Invite hero",
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Il n'y a pas de villes disponibles avec des tavernes !",
|
"vcmi.adventureMap.noTownWithTavern" : "Il n'y a pas de villes disponibles avec des tavernes !",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Il y a un problème inconnu avec ce sort ! Pas plus d'informations sont disponibles.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Il y a un problème inconnu avec ce sort ! Pas plus d'informations sont disponibles.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Le joueur a été attaqué : %s",
|
"vcmi.adventureMap.playerAttacked" : "Le joueur a été attaqué : %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Points de mouvement - Coût : %TURNS tours + %POINTS points, Points restants : %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Points de mouvement - Coût : %POINTS points, Points restants : %REMAINING",
|
|
||||||
|
|
||||||
"vcmi.capitalColors.0" : "Rouge",
|
"vcmi.capitalColors.0" : "Rouge",
|
||||||
"vcmi.capitalColors.1" : "Bleu",
|
"vcmi.capitalColors.1" : "Bleu",
|
||||||
|
@ -23,16 +23,16 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!",
|
"vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
|
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Bewegungspunkte - Kosten: %TURNS Runden + %POINTS Punkte, Verbleibende Punkte: %REMAINING",
|
"vcmi.adventureMap.moveCostDetails" : "Eine Bewegung hierher kostet insgesamt {%TOTAL} Punkte ({%TURNS} Runden und {%POINTS} Punkte). Nach der Bewegung bleiben {%REMAINING} Punkte übrig.",
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Bewegungspunkte - Kosten: %POINTS Punkte, Verbleibende Punkte: %REMAINING",
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Eine Bewegung hierher kostet {%POINTS} Punkte. Nach der Bewegung bleiben {%REMAINING} Punkte übrig.",
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Bewegungspunkte: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Bewegungspunkte: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Das Wiederholen des gegnerischen Zuges ist aktuell noch nicht implementiert!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Das Wiederholen des gegnerischen Zuges ist aktuell noch nicht implementiert!",
|
||||||
|
|
||||||
"vcmi.bonusSource.artifact" : "Artefakt",
|
"vcmi.bonusSource.artifact" : "Artefakt",
|
||||||
"vcmi.bonusSource.creature" : "Fähigkeit",
|
"vcmi.bonusSource.creature" : "Fähigkeit",
|
||||||
"vcmi.bonusSource.spell" : "Zauber",
|
"vcmi.bonusSource.spell" : "Zauber",
|
||||||
"vcmi.bonusSource.hero" : "Held",
|
"vcmi.bonusSource.hero" : "Held",
|
||||||
"vcmi.bonusSource.commander" : "Commander",
|
"vcmi.bonusSource.commander" : "Command.",
|
||||||
"vcmi.bonusSource.other" : "Anderes",
|
"vcmi.bonusSource.other" : "Anderes",
|
||||||
|
|
||||||
"vcmi.capitalColors.0" : "Rot",
|
"vcmi.capitalColors.0" : "Rot",
|
||||||
@ -68,6 +68,7 @@
|
|||||||
"vcmi.radialWheel.heroGetArtifacts" : "Artefakte von anderen Helden erhalten",
|
"vcmi.radialWheel.heroGetArtifacts" : "Artefakte von anderen Helden erhalten",
|
||||||
"vcmi.radialWheel.heroSwapArtifacts" : "Tausche Artefakte mit anderen Helden",
|
"vcmi.radialWheel.heroSwapArtifacts" : "Tausche Artefakte mit anderen Helden",
|
||||||
"vcmi.radialWheel.heroDismiss" : "Held entlassen",
|
"vcmi.radialWheel.heroDismiss" : "Held entlassen",
|
||||||
|
"vcmi.radialWheel.upgradeCreatures" : "Alle Kreaturen aufrüsten",
|
||||||
|
|
||||||
"vcmi.radialWheel.moveTop" : "Ganz nach oben bewegen",
|
"vcmi.radialWheel.moveTop" : "Ganz nach oben bewegen",
|
||||||
"vcmi.radialWheel.moveUp" : "Nach oben bewegen",
|
"vcmi.radialWheel.moveUp" : "Nach oben bewegen",
|
||||||
@ -363,6 +364,8 @@
|
|||||||
"vcmi.battleOptions.endWithAutocombat.help": "{Kampf beenden}\n\nAutokampf spielt den Kampf sofort zu Ende",
|
"vcmi.battleOptions.endWithAutocombat.help": "{Kampf beenden}\n\nAutokampf spielt den Kampf sofort zu Ende",
|
||||||
"vcmi.battleOptions.showQuickSpell.hover": "Schnellzauber-Panel anzeigen",
|
"vcmi.battleOptions.showQuickSpell.hover": "Schnellzauber-Panel anzeigen",
|
||||||
"vcmi.battleOptions.showQuickSpell.help": "{Schnellzauber-Panel anzeigen}\n\nZeigt ein Panel, auf dem schnell Zauber ausgewählt werden können",
|
"vcmi.battleOptions.showQuickSpell.help": "{Schnellzauber-Panel anzeigen}\n\nZeigt ein Panel, auf dem schnell Zauber ausgewählt werden können",
|
||||||
|
"vcmi.battleOptions.showHealthBar.hover": "Gesundheits-Balken anzeigen",
|
||||||
|
"vcmi.battleOptions.showHealthBar.help": "{Gesundheits-Balken anzeigen}\n\nAnzeige eines Gesundheitsbalkens, der die verbleibende Gesundheit anzeigt, bevor eine Einheit stirbt.",
|
||||||
|
|
||||||
"vcmi.adventureMap.revisitObject.hover" : "Objekt erneut besuchen",
|
"vcmi.adventureMap.revisitObject.hover" : "Objekt erneut besuchen",
|
||||||
"vcmi.adventureMap.revisitObject.help" : "{Objekt erneut besuchen}\n\nSteht ein Held gerade auf einem Kartenobjekt, kann er den Ort erneut aufsuchen.",
|
"vcmi.adventureMap.revisitObject.help" : "{Objekt erneut besuchen}\n\nSteht ein Held gerade auf einem Kartenobjekt, kann er den Ort erneut aufsuchen.",
|
||||||
@ -415,6 +418,9 @@
|
|||||||
"vcmi.townStructure.bank.borrow" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Wir haben ein spezielles Angebot für Euch gemacht. Ihr könnt bei uns einen Kredit von 2500 Gold für 5 Tage aufnehmen. Ihr werdet jeden Tag 500 Gold zurückzahlen müssen.\"",
|
"vcmi.townStructure.bank.borrow" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Wir haben ein spezielles Angebot für Euch gemacht. Ihr könnt bei uns einen Kredit von 2500 Gold für 5 Tage aufnehmen. Ihr werdet jeden Tag 500 Gold zurückzahlen müssen.\"",
|
||||||
"vcmi.townStructure.bank.payBack" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Ihr habt Euren Kredit bereits erhalten. Zahlt Ihn ihn zurück, bevor Ihr einen neuen aufnehmt.\"",
|
"vcmi.townStructure.bank.payBack" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Ihr habt Euren Kredit bereits erhalten. Zahlt Ihn ihn zurück, bevor Ihr einen neuen aufnehmt.\"",
|
||||||
|
|
||||||
|
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Nicht genügend Ressourcen um alle Kreaturen aufzurüsten. Folgende Kreaturen aufrüsten?",
|
||||||
|
"vcmi.townWindow.upgradeAll.notUpgradable" : "Nicht genügend Ressourcen um mindestens eine Kreatur aufzurüsten.",
|
||||||
|
|
||||||
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
|
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
|
||||||
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
|
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
|
||||||
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
|
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
|
||||||
@ -424,11 +430,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Artefakt-Rucksack-Fenster öffnen",
|
"vcmi.heroWindow.openBackpack.hover" : "Artefakt-Rucksack-Fenster öffnen",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Öffnet ein Fenster, das die Verwaltung des Artefakt-Rucksacks erleichtert",
|
"vcmi.heroWindow.openBackpack.help" : "Öffnet ein Fenster, das die Verwaltung des Artefakt-Rucksacks erleichtert",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Nach Kosten sortieren",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Nach Kosten sortieren",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Artefakte im Rucksack nach Kosten sortieren.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Nach Kosten sortieren}\n\nArtefakte im Rucksack nach Kosten sortieren.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Nach Slot sortieren",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Nach Slot sortieren",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Artefakte im Rucksack nach Ausrüstungsslot sortieren.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Nach Slot sortieren}\n\nArtefakte im Rucksack nach Ausrüstungsslot sortieren.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Nach Klasse sortieren",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Nach Klasse sortieren",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Artefakte im Rucksack nach Artefaktklasse sortieren. Schatz, Klein, Groß, Relikt",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Nach Klasse sortieren}\n\nArtefakte im Rucksack nach Artefaktklasse sortieren. Schatz, Klein, Groß, Relikt",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Ihr verfügt über alle Komponenten, die für die Fusion der %s benötigt werden. Möchtet Ihr die Verschmelzung durchführen? {Alle Komponenten werden bei der Fusion verbraucht.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Ihr verfügt über alle Komponenten, die für die Fusion der %s benötigt werden. Möchtet Ihr die Verschmelzung durchführen? {Alle Komponenten werden bei der Fusion verbraucht.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Helden einladen",
|
"vcmi.tavernWindow.inviteHero" : "Helden einladen",
|
||||||
@ -608,7 +614,7 @@
|
|||||||
"core.seerhut.quest.reachDate.visit.5" : "Geschlossen bis %s.",
|
"core.seerhut.quest.reachDate.visit.5" : "Geschlossen bis %s.",
|
||||||
|
|
||||||
"mapObject.core.hillFort.object.description" : "Aufwertungen von Kreaturen. Die Stufen 1 - 4 sind billiger als in der zugehörigen Stadt.",
|
"mapObject.core.hillFort.object.description" : "Aufwertungen von Kreaturen. Die Stufen 1 - 4 sind billiger als in der zugehörigen Stadt.",
|
||||||
|
|
||||||
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
|
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
|
||||||
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
|
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
|
||||||
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
|
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
|
||||||
@ -763,16 +769,6 @@
|
|||||||
"core.bonus.MECHANICAL.description": "Immunität gegen viele Effekte, reparierbar",
|
"core.bonus.MECHANICAL.description": "Immunität gegen viele Effekte, reparierbar",
|
||||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "Prisma-Atem",
|
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "Prisma-Atem",
|
||||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "Prisma-Atem-Angriff (drei Richtungen)",
|
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "Prisma-Atem-Angriff (drei Richtungen)",
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.name": "Zauber-Immunität",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description": "Immunität gegen alle Zauber-Schulen",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.air": "Luft-Immunität",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.fire": "Feuer-Immunität",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.water": "Wasser-Immunität",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.earth": "Erde-Immunität",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.air": "Immunität gegen Zauber der Luft-Schule",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.fire": "Immunität gegen Zauber der Feuer-Schule",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water": "Immunität gegen Zauber der Wasser-Schule",
|
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth": "Immunität gegen Zauber der Erde-Schule",
|
|
||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand",
|
||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name.air": "Luft-Zauberwiderstand",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.air": "Luft-Zauberwiderstand",
|
||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name.fire": "Feuer-Zauberwiderstand",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.fire": "Feuer-Zauberwiderstand",
|
||||||
@ -783,6 +779,38 @@
|
|||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description.fire": "Schaden von Feuer-Zaubern um ${val}% reduziert.",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.fire": "Schaden von Feuer-Zaubern um ${val}% reduziert.",
|
||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description.water": "Schaden von Wasser-Zaubern um ${val}% reduziert.",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.water": "Schaden von Wasser-Zaubern um ${val}% reduziert.",
|
||||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description.earth": "Schaden von Erde-Zaubern um ${val}% reduziert.",
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.earth": "Schaden von Erde-Zaubern um ${val}% reduziert.",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name": "Zauber-Immunität",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.air": "Luft-Immunität",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.fire": "Feuer-Immunität",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.water": "Wasser-Immunität",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.earth": "Erde-Immunität",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description": "Immunität gegen alle Zauber-Schulen",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.air": "Immunität gegen Zauber der Luft-Schule",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.fire": "Immunität gegen Zauber der Feuer-Schule",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water": "Immunität gegen Zauber der Wasser-Schule",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth": "Immunität gegen Zauber der Erde-Schule",
|
||||||
"core.bonus.OPENING_BATTLE_SPELL.name": "Startet mit Zauber",
|
"core.bonus.OPENING_BATTLE_SPELL.name": "Startet mit Zauber",
|
||||||
"core.bonus.OPENING_BATTLE_SPELL.description": "Wirkt ${subtype.spell} beim Start des Kampfes"
|
"core.bonus.OPENING_BATTLE_SPELL.description": "Wirkt ${subtype.spell} beim Start des Kampfes",
|
||||||
|
|
||||||
|
"spell.core.castleMoat.name" : "Graben",
|
||||||
|
"spell.core.castleMoatTrigger.name" : "Graben",
|
||||||
|
"spell.core.catapultShot.name" : "Katapultschuss",
|
||||||
|
"spell.core.cyclopsShot.name" : "Belagerungsschuss",
|
||||||
|
"spell.core.dungeonMoat.name" : "Siedeöl",
|
||||||
|
"spell.core.dungeonMoatTrigger.name" : "Siedeöl",
|
||||||
|
"spell.core.fireWallTrigger.name" : "Feuerwand",
|
||||||
|
"spell.core.firstAid.name" : "Erste Hilfe",
|
||||||
|
"spell.core.fortressMoat.name" : "Siedender Teer",
|
||||||
|
"spell.core.fortressMoatTrigger.name" : "Siedender Teer",
|
||||||
|
"spell.core.infernoMoat.name" : "Lava",
|
||||||
|
"spell.core.infernoMoatTrigger.name" : "Lava",
|
||||||
|
"spell.core.landMineTrigger.name" : "Landmine",
|
||||||
|
"spell.core.necropolisMoat.name" : "Knochenplatz",
|
||||||
|
"spell.core.necropolisMoatTrigger.name" : "Knochenplatz",
|
||||||
|
"spell.core.rampartMoat.name" : "Brombeeren",
|
||||||
|
"spell.core.rampartMoatTrigger.name" : "Brombeeren",
|
||||||
|
"spell.core.strongholdMoat.name" : "Holzspieße",
|
||||||
|
"spell.core.strongholdMoatTrigger.name" : "Holzspieße",
|
||||||
|
"spell.core.summonDemons.name" : "Dämonen beschwören",
|
||||||
|
"spell.core.towerMoat.name" : "Landmine"
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Nincsenek elérhető kocsmával rendelkező városok!",
|
"vcmi.adventureMap.noTownWithTavern" : "Nincsenek elérhető kocsmával rendelkező városok!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Ismeretlen probléma van ezzel a varázslattal! További információ nem érhető el.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Ismeretlen probléma van ezzel a varázslattal! További információ nem érhető el.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "A játékost megtámadták: %s",
|
"vcmi.adventureMap.playerAttacked" : "A játékost megtámadták: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Mozgáspontok - Költség: %TURNS kör + %POINTS pont, Hátralévő pontok: %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Mozgáspontok - Költség: %POINTS pont, Hátralévő pontok: %REMAINING",
|
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Mozgáspontok: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Mozgáspontok: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sajnáljuk, az ellenfél körének visszajátszása még nincs megvalósítva!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sajnáljuk, az ellenfél körének visszajátszása még nincs megvalósítva!",
|
||||||
|
|
||||||
@ -424,11 +422,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Műtárgy hátizsák ablak megnyitása",
|
"vcmi.heroWindow.openBackpack.hover" : "Műtárgy hátizsák ablak megnyitása",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Az ablak megnyitása, amely megkönnyíti a műtárgy hátizsák kezelését.",
|
"vcmi.heroWindow.openBackpack.help" : "Az ablak megnyitása, amely megkönnyíti a műtárgy hátizsák kezelését.",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Rendezés ár szerint",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Rendezés ár szerint",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "A műtárgyak ár szerinti rendezése a hátizsákban.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Rendezés ár szerint}\n\nA műtárgyak ár szerinti rendezése a hátizsákban.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Rendezés nyílás szerint",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Rendezés nyílás szerint",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "A műtárgyak nyílás szerinti rendezése a hátizsákban.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Rendezés nyílás szerint}\n\nA műtárgyak nyílás szerinti rendezése a hátizsákban.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Rendezés osztály szerint",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Rendezés osztály szerint",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "A műtárgyak osztály szerinti rendezése a hátizsákban. Kincs, Kisebb, Nagyobb, Relikvia",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Rendezés osztály szerint}\n\nA műtárgyak osztály szerinti rendezése a hátizsákban. Kincs, Kisebb, Nagyobb, Relikvia",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Ön birtokában van az összes szükséges komponensnek a(z) %s összeolvasztásához. Szeretné elvégezni az összeolvasztást? {Minden komponens elfogy az összeolvasztás során.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Ön birtokában van az összes szükséges komponensnek a(z) %s összeolvasztásához. Szeretné elvégezni az összeolvasztást? {Minden komponens elfogy az összeolvasztás során.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Hős meghívása",
|
"vcmi.tavernWindow.inviteHero" : "Hős meghívása",
|
||||||
|
809
Mods/vcmi/Content/config/italian.json
Normal file
809
Mods/vcmi/Content/config/italian.json
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
{
|
||||||
|
"vcmi.adventureMap.monsterThreat.title" : "\n\nMinaccia: ",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.0" : "Facile",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.1" : "Molto Debole",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.2" : "Debole",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.3" : "Un po' più debole",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.4" : "Uguale",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.5" : "Un po' più forte",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.6" : "Forte",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.7" : "Molto Forte",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.8" : "Difficile",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.9" : "Schiacciante",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.10" : "Mortale",
|
||||||
|
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossibile",
|
||||||
|
"vcmi.adventureMap.monsterLevel" : "\n\nUnità di livello %LEVEL %TOWN %ATTACK_TYPE",
|
||||||
|
"vcmi.adventureMap.monsterMeleeType" : "corpo a corpo",
|
||||||
|
"vcmi.adventureMap.monsterRangedType" : "a distanza",
|
||||||
|
"vcmi.adventureMap.search.hover" : "Cerca oggetto sulla mappa",
|
||||||
|
"vcmi.adventureMap.search.help" : "Seleziona un oggetto da cercare sulla mappa.",
|
||||||
|
|
||||||
|
"vcmi.adventureMap.confirmRestartGame" : "Sei sicuro di voler riavviare il gioco?",
|
||||||
|
"vcmi.adventureMap.noTownWithMarket" : "Non ci sono mercati disponibili!",
|
||||||
|
"vcmi.adventureMap.noTownWithTavern" : "Non ci sono città disponibili con taverne!",
|
||||||
|
"vcmi.adventureMap.spellUnknownProblem" : "C'è un problema sconosciuto con questo incantesimo! Nessuna informazione aggiuntiva disponibile.",
|
||||||
|
"vcmi.adventureMap.playerAttacked" : "Il giocatore è stato attaccato: %s",
|
||||||
|
"vcmi.adventureMap.moveCostDetails" : "Punti movimento - Costo: %TURNS turni + %POINTS punti, Punti rimanenti: %REMAINING",
|
||||||
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Punti movimento - Costo: %POINTS punti, Punti rimanenti: %REMAINING",
|
||||||
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Punti movimento: %REMAINING / %POINTS)",
|
||||||
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Spiacente, la riproduzione del turno avversario non è ancora implementata!",
|
||||||
|
|
||||||
|
"vcmi.bonusSource.artifact" : "Artefatto",
|
||||||
|
"vcmi.bonusSource.creature" : "Abilità",
|
||||||
|
"vcmi.bonusSource.spell" : "Incantesimo",
|
||||||
|
"vcmi.bonusSource.hero" : "Eroe",
|
||||||
|
"vcmi.bonusSource.commander" : "Comandante",
|
||||||
|
"vcmi.bonusSource.other" : "Altro",
|
||||||
|
|
||||||
|
"vcmi.capitalColors.0" : "Rosso",
|
||||||
|
"vcmi.capitalColors.1" : "Blu",
|
||||||
|
"vcmi.capitalColors.2" : "Marrone",
|
||||||
|
"vcmi.capitalColors.3" : "Verde",
|
||||||
|
"vcmi.capitalColors.4" : "Arancione",
|
||||||
|
"vcmi.capitalColors.5" : "Viola",
|
||||||
|
"vcmi.capitalColors.6" : "Turchese",
|
||||||
|
"vcmi.capitalColors.7" : "Rosa",
|
||||||
|
|
||||||
|
"vcmi.heroOverview.startingArmy" : "Unità Iniziali",
|
||||||
|
"vcmi.heroOverview.warMachine" : "Macchine da Guerra",
|
||||||
|
"vcmi.heroOverview.secondarySkills" : "Abilità Secondarie",
|
||||||
|
"vcmi.heroOverview.spells" : "Incantesimi",
|
||||||
|
|
||||||
|
"vcmi.quickExchange.moveUnit" : "Sposta Unità",
|
||||||
|
"vcmi.quickExchange.moveAllUnits" : "Sposta Tutte le Unità",
|
||||||
|
"vcmi.quickExchange.swapAllUnits" : "Scambia Eserciti",
|
||||||
|
"vcmi.quickExchange.moveAllArtifacts" : "Sposta Tutti gli Artefatti",
|
||||||
|
"vcmi.quickExchange.swapAllArtifacts" : "Scambia Artefatti",
|
||||||
|
|
||||||
|
"vcmi.radialWheel.mergeSameUnit" : "Unisci creature dello stesso tipo",
|
||||||
|
"vcmi.radialWheel.fillSingleUnit" : "Riempi con creature singole",
|
||||||
|
"vcmi.radialWheel.splitSingleUnit" : "Dividi una singola creatura",
|
||||||
|
"vcmi.radialWheel.splitUnitEqually" : "Dividi equamente le creature",
|
||||||
|
"vcmi.radialWheel.moveUnit" : "Sposta creature in un altro esercito",
|
||||||
|
"vcmi.radialWheel.splitUnit" : "Dividi creatura in un altro slot",
|
||||||
|
|
||||||
|
"vcmi.radialWheel.heroGetArmy" : "Prendi l'esercito da un altro eroe",
|
||||||
|
"vcmi.radialWheel.heroSwapArmy" : "Scambia l'esercito con un altro eroe",
|
||||||
|
"vcmi.radialWheel.heroExchange" : "Apri scambio eroe",
|
||||||
|
"vcmi.radialWheel.heroGetArtifacts" : "Prendi artefatti da un altro eroe",
|
||||||
|
"vcmi.radialWheel.heroSwapArtifacts" : "Scambia artefatti con un altro eroe",
|
||||||
|
"vcmi.radialWheel.heroDismiss" : "Congeda l'eroe",
|
||||||
|
|
||||||
|
"vcmi.radialWheel.moveTop" : "Sposta in alto",
|
||||||
|
"vcmi.radialWheel.moveUp" : "Sposta su",
|
||||||
|
"vcmi.radialWheel.moveDown" : "Sposta giù",
|
||||||
|
"vcmi.radialWheel.moveBottom" : "Sposta in basso",
|
||||||
|
|
||||||
|
"vcmi.randomMap.description" : "Mappa creata dal Generatore di Mappe Casuali.\nIl modello era %s, dimensione %dx%d, livelli %d, giocatori %d, computer %d, acqua %s, mostri %s, mappa VCMI",
|
||||||
|
"vcmi.randomMap.description.isHuman" : ", %s è un umano",
|
||||||
|
"vcmi.randomMap.description.townChoice" : ", la scelta della città di %s è %s",
|
||||||
|
"vcmi.randomMap.description.water.none" : "nessuna",
|
||||||
|
"vcmi.randomMap.description.water.normal" : "normale",
|
||||||
|
"vcmi.randomMap.description.water.islands" : "isole",
|
||||||
|
"vcmi.randomMap.description.monster.weak" : "debole",
|
||||||
|
"vcmi.randomMap.description.monster.normal" : "normale",
|
||||||
|
"vcmi.randomMap.description.monster.strong" : "forte",
|
||||||
|
|
||||||
|
"vcmi.spellBook.search" : "cerca...",
|
||||||
|
|
||||||
|
"vcmi.spellResearch.canNotAfford" : "Non puoi permetterti di sostituire {%SPELL1} con {%SPELL2}. Ma puoi comunque scartare questo incantesimo e continuare la ricerca.",
|
||||||
|
"vcmi.spellResearch.comeAgain" : "La ricerca è già stata fatta oggi. Torna domani.",
|
||||||
|
"vcmi.spellResearch.pay" : "Vuoi sostituire {%SPELL1} con {%SPELL2}? Oppure scartare questo incantesimo e continuare la ricerca?",
|
||||||
|
"vcmi.spellResearch.research" : "Ricerca questo Incantesimo",
|
||||||
|
"vcmi.spellResearch.skip" : "Salta questo Incantesimo",
|
||||||
|
"vcmi.spellResearch.abort" : "Annulla",
|
||||||
|
"vcmi.spellResearch.noMoreSpells" : "Non ci sono più incantesimi disponibili per la ricerca.",
|
||||||
|
|
||||||
|
"vcmi.mainMenu.serverConnecting" : "Connessione in corso...",
|
||||||
|
"vcmi.mainMenu.serverAddressEnter" : "Inserisci indirizzo:",
|
||||||
|
"vcmi.mainMenu.serverConnectionFailed" : "Connessione fallita",
|
||||||
|
"vcmi.mainMenu.serverClosing" : "Chiusura in corso...",
|
||||||
|
"vcmi.mainMenu.hostTCP" : "Ospita una partita TCP/IP",
|
||||||
|
"vcmi.mainMenu.joinTCP" : "Unisciti a una partita TCP/IP",
|
||||||
|
|
||||||
|
"vcmi.lobby.filepath" : "Percorso file",
|
||||||
|
"vcmi.lobby.creationDate" : "Data di creazione",
|
||||||
|
"vcmi.lobby.scenarioName" : "Nome dello scenario",
|
||||||
|
"vcmi.lobby.mapPreview" : "Anteprima mappa",
|
||||||
|
"vcmi.lobby.noPreview" : "nessuna anteprima",
|
||||||
|
"vcmi.lobby.noUnderground" : "nessun sotterraneo",
|
||||||
|
"vcmi.lobby.sortDate" : "Ordina le mappe per data di modifica",
|
||||||
|
"vcmi.lobby.backToLobby" : "Ritorna alla lobby",
|
||||||
|
"vcmi.lobby.author" : "Autore",
|
||||||
|
"vcmi.lobby.handicap" : "Handicap",
|
||||||
|
"vcmi.lobby.handicap.resource" : "Dà ai giocatori risorse appropriate per iniziare oltre a quelle normali. I valori negativi sono consentiti, ma limitati a 0 (il giocatore non inizia mai con risorse negative).",
|
||||||
|
"vcmi.lobby.handicap.income" : "Modifica le entrate del giocatore in percentuale. Arrotondato per eccesso.",
|
||||||
|
"vcmi.lobby.handicap.growth" : "Modifica la crescita delle creature nelle città possedute dal giocatore. Arrotondato per eccesso.",
|
||||||
|
"vcmi.lobby.deleteUnsupportedSave" : "{Salvataggi non supportati trovati}\n\nVCMI ha trovato %d salvataggi non più supportati, probabilmente a causa di differenze tra le versioni di VCMI.\n\nVuoi eliminarli?",
|
||||||
|
"vcmi.lobby.deleteSaveGameTitle" : "Seleziona un salvataggio da eliminare",
|
||||||
|
"vcmi.lobby.deleteMapTitle" : "Seleziona uno scenario da eliminare",
|
||||||
|
"vcmi.lobby.deleteFile" : "Vuoi eliminare il seguente file?",
|
||||||
|
"vcmi.lobby.deleteFolder" : "Vuoi eliminare la seguente cartella?",
|
||||||
|
"vcmi.lobby.deleteMode" : "Passa alla modalità elimina e torna indietro",
|
||||||
|
|
||||||
|
"vcmi.broadcast.failedLoadGame" : "Impossibile caricare la partita",
|
||||||
|
"vcmi.broadcast.command" : "Usa '!help' per elencare i comandi disponibili",
|
||||||
|
"vcmi.broadcast.simturn.end" : "I turni simultanei sono terminati",
|
||||||
|
"vcmi.broadcast.simturn.endBetween" : "I turni simultanei tra i giocatori %s e %s sono terminati",
|
||||||
|
"vcmi.broadcast.serverProblem" : "Il server ha riscontrato un problema",
|
||||||
|
"vcmi.broadcast.gameTerminated" : "la partita è stata terminata",
|
||||||
|
"vcmi.broadcast.gameSavedAs" : "partita salvata come",
|
||||||
|
"vcmi.broadcast.noCheater" : "Nessun baro registrato!",
|
||||||
|
"vcmi.broadcast.playerCheater" : "Il giocatore %s ha usato i trucchi!",
|
||||||
|
"vcmi.broadcast.statisticFile" : "I file delle statistiche possono essere trovati nella directory %s",
|
||||||
|
"vcmi.broadcast.help.commands" : "Comandi disponibili per l'host:",
|
||||||
|
"vcmi.broadcast.help.exit" : "'!exit' - termina immediatamente la partita in corso",
|
||||||
|
"vcmi.broadcast.help.kick" : "'!kick <player>' - espelle il giocatore specificato dalla partita",
|
||||||
|
"vcmi.broadcast.help.save" : "'!save <filename>' - salva la partita con il nome specificato",
|
||||||
|
"vcmi.broadcast.help.statistic" : "'!statistic' - salva le statistiche della partita come file CSV",
|
||||||
|
"vcmi.broadcast.help.commandsAll" : "Comandi disponibili per tutti i giocatori:",
|
||||||
|
"vcmi.broadcast.help.help" : "'!help' - mostra questo aiuto",
|
||||||
|
"vcmi.broadcast.help.cheaters" : "'!cheaters' - elenca i giocatori che hanno usato trucchi durante la partita",
|
||||||
|
"vcmi.broadcast.help.vote" : "'!vote' - consente di modificare alcune impostazioni di gioco se tutti i giocatori sono d'accordo",
|
||||||
|
"vcmi.broadcast.vote.allow" : "'!vote simturns allow X' - consente turni simultanei per un numero specificato di giorni, o fino al contatto",
|
||||||
|
"vcmi.broadcast.vote.force" : "'!vote simturns force X' - forza turni simultanei per un numero specificato di giorni, bloccando i contatti tra giocatori",
|
||||||
|
"vcmi.broadcast.vote.abort" : "'!vote simturns abort' - interrompe i turni simultanei alla fine di questo turno",
|
||||||
|
"vcmi.broadcast.vote.timer" : "'!vote timer prolong X' - prolunga il timer di base per tutti i giocatori del numero specificato di secondi",
|
||||||
|
"vcmi.broadcast.vote.noActive" : "Nessuna votazione attiva!",
|
||||||
|
"vcmi.broadcast.vote.yes" : "sì",
|
||||||
|
"vcmi.broadcast.vote.no" : "no",
|
||||||
|
"vcmi.broadcast.vote.notRecognized" : "Comando di votazione non riconosciuto!",
|
||||||
|
"vcmi.broadcast.vote.success.untilContacts" : "Votazione riuscita. I turni simultanei dureranno ancora %s giorni, o fino al contatto",
|
||||||
|
"vcmi.broadcast.vote.success.contactsBlocked" : "Votazione riuscita. I turni simultanei dureranno ancora %s giorni. I contatti sono bloccati",
|
||||||
|
"vcmi.broadcast.vote.success.nextDay" : "Votazione riuscita. I turni simultanei termineranno il giorno successivo",
|
||||||
|
"vcmi.broadcast.vote.success.timer" : "Votazione riuscita. Il timer per tutti i giocatori è stato prolungato di %s secondi",
|
||||||
|
"vcmi.broadcast.vote.aborted" : "Un giocatore ha votato contro la modifica. Votazione annullata",
|
||||||
|
"vcmi.broadcast.vote.start.untilContacts" : "Votazione avviata per consentire turni simultanei per altri %s giorni",
|
||||||
|
"vcmi.broadcast.vote.start.contactsBlocked" : "Votazione avviata per forzare turni simultanei per altri %s giorni",
|
||||||
|
"vcmi.broadcast.vote.start.nextDay" : "Votazione avviata per terminare i turni simultanei dal giorno successivo",
|
||||||
|
"vcmi.broadcast.vote.start.timer" : "Votazione avviata per prolungare il timer per tutti i giocatori di %s secondi",
|
||||||
|
"vcmi.broadcast.vote.hint" : "Digita '!vote yes' per accettare la modifica o '!vote no' per rifiutarla",
|
||||||
|
|
||||||
|
"vcmi.lobby.login.title" : "VCMI Lobby Online",
|
||||||
|
"vcmi.lobby.login.username" : "Nome utente:",
|
||||||
|
"vcmi.lobby.login.connecting" : "Connessione in corso...",
|
||||||
|
"vcmi.lobby.login.error" : "Errore di connessione: %s",
|
||||||
|
"vcmi.lobby.login.create" : "Nuovo Account",
|
||||||
|
"vcmi.lobby.login.login" : "Accedi",
|
||||||
|
"vcmi.lobby.login.as" : "Accedi come %s",
|
||||||
|
"vcmi.lobby.login.spectator" : "Spettatore",
|
||||||
|
"vcmi.lobby.header.rooms" : "Stanze di gioco - %d",
|
||||||
|
"vcmi.lobby.header.channels" : "Canali chat",
|
||||||
|
"vcmi.lobby.header.chat.global" : "Chat globale - %s", // %s -> language name
|
||||||
|
"vcmi.lobby.header.chat.match" : "Chat dalla partita precedente su %s", // %s -> game start date & time
|
||||||
|
"vcmi.lobby.header.chat.player" : "Chat privata con %s", // %s -> nickname of another player
|
||||||
|
"vcmi.lobby.header.history" : "Le tue partite precedenti",
|
||||||
|
"vcmi.lobby.header.players" : "Giocatori online - %d",
|
||||||
|
"vcmi.lobby.match.solo" : "Partita in singolo",
|
||||||
|
"vcmi.lobby.match.duel" : "Partita con %s", // %s -> nickname of another player
|
||||||
|
"vcmi.lobby.match.multi" : "%d giocatori",
|
||||||
|
"vcmi.lobby.room.create" : "Crea nuova stanza",
|
||||||
|
"vcmi.lobby.room.players.limit" : "Limite giocatori",
|
||||||
|
"vcmi.lobby.room.description.public" : "Qualsiasi giocatore può entrare in una stanza pubblica.",
|
||||||
|
"vcmi.lobby.room.description.private" : "Solo i giocatori invitati possono entrare in una stanza privata.",
|
||||||
|
"vcmi.lobby.room.description.new" : "Per avviare la partita, seleziona uno scenario o imposta una mappa casuale.",
|
||||||
|
"vcmi.lobby.room.description.load" : "Per avviare la partita, usa uno dei tuoi salvataggi.",
|
||||||
|
"vcmi.lobby.room.description.limit" : "Fino a %d giocatori possono entrare nella tua stanza, incluso te.",
|
||||||
|
"vcmi.lobby.invite.header" : "Invita giocatori",
|
||||||
|
"vcmi.lobby.invite.notification" : "Un giocatore ti ha invitato nella sua stanza di gioco. Ora puoi unirti alla sua stanza privata.",
|
||||||
|
"vcmi.lobby.preview.title" : "Unisciti alla stanza di gioco",
|
||||||
|
"vcmi.lobby.preview.subtitle" : "Partita su %s, ospitata da %s", //TL Note: 1) name of map or RMG template 2) nickname of game host
|
||||||
|
"vcmi.lobby.preview.version" : "Versione di gioco:",
|
||||||
|
"vcmi.lobby.preview.players" : "Giocatori:",
|
||||||
|
"vcmi.lobby.preview.mods" : "Mod utilizzate:",
|
||||||
|
"vcmi.lobby.preview.allowed" : "Vuoi unirti alla stanza di gioco?",
|
||||||
|
"vcmi.lobby.preview.error.header" : "Impossibile unirsi a questa stanza.",
|
||||||
|
"vcmi.lobby.preview.error.playing" : "Devi prima uscire dalla tua partita attuale.",
|
||||||
|
"vcmi.lobby.preview.error.full" : "La stanza è già piena.",
|
||||||
|
"vcmi.lobby.preview.error.busy" : "La stanza non accetta più nuovi giocatori.",
|
||||||
|
"vcmi.lobby.preview.error.invite" : "Non sei stato invitato a questa stanza.",
|
||||||
|
"vcmi.lobby.preview.error.mods" : "Stai usando un set di mod diverso.",
|
||||||
|
"vcmi.lobby.preview.error.version" : "Stai usando una versione diversa di VCMI.",
|
||||||
|
"vcmi.lobby.room.new" : "Nuova partita",
|
||||||
|
"vcmi.lobby.room.load" : "Carica partita",
|
||||||
|
"vcmi.lobby.room.type" : "Tipo di stanza",
|
||||||
|
"vcmi.lobby.room.mode" : "Modalità di gioco",
|
||||||
|
"vcmi.lobby.room.state.public" : "Pubblica",
|
||||||
|
"vcmi.lobby.room.state.private" : "Privata",
|
||||||
|
"vcmi.lobby.room.state.busy" : "In gioco",
|
||||||
|
"vcmi.lobby.room.state.invited" : "Invitato",
|
||||||
|
"vcmi.lobby.mod.state.compatible" : "Compatibile",
|
||||||
|
"vcmi.lobby.mod.state.disabled" : "Deve essere attivato",
|
||||||
|
"vcmi.lobby.mod.state.version" : "Versione incompatibile",
|
||||||
|
"vcmi.lobby.mod.state.excessive" : "Deve essere disattivato",
|
||||||
|
"vcmi.lobby.mod.state.missing" : "Non installato",
|
||||||
|
"vcmi.lobby.pvp.coin.hover" : "Lancia la moneta",
|
||||||
|
"vcmi.lobby.pvp.coin.help" : "Lancia una moneta",
|
||||||
|
"vcmi.lobby.pvp.randomTown.hover" : "Città casuale",
|
||||||
|
"vcmi.lobby.pvp.randomTown.help" : "Scrivi una città casuale in chat",
|
||||||
|
"vcmi.lobby.pvp.randomTownVs.hover" : "Città casuale vs.",
|
||||||
|
"vcmi.lobby.pvp.randomTownVs.help" : "Scrivi due città casuali in chat",
|
||||||
|
"vcmi.lobby.pvp.versus" : "vs.",
|
||||||
|
|
||||||
|
"vcmi.client.errors.invalidMap" : "{Mappa o campagna non valida}\n\nImpossibile avviare la partita! La mappa o la campagna selezionata potrebbe essere non valida o corrotta. Motivo:\n%s",
|
||||||
|
"vcmi.client.errors.missingCampaigns" : "{File dati mancanti}\n\nI file dati delle campagne non sono stati trovati! Potresti avere file dati incompleti o corrotti di Heroes 3. Reinstalla i dati del gioco.",
|
||||||
|
"vcmi.client.errors.modLoadingFailure" : "{Errore di caricamento delle mod}\n\nSono stati rilevati problemi critici durante il caricamento delle mod! Il gioco potrebbe non funzionare correttamente o bloccarsi! Aggiorna o disattiva le seguenti mod:\n\n",
|
||||||
|
"vcmi.server.errors.disconnected" : "{Errore di rete}\n\nLa connessione al server di gioco è stata persa!",
|
||||||
|
"vcmi.server.errors.playerLeft" : "{Giocatore disconnesso}\n\nIl giocatore %s si è disconnesso dalla partita!", //%s -> player color
|
||||||
|
"vcmi.server.errors.existingProcess" : "Un altro processo del server VCMI è in esecuzione. Terminarlo prima di avviare una nuova partita.",
|
||||||
|
"vcmi.server.errors.modsToEnable" : "{Le seguenti mod sono richieste}",
|
||||||
|
"vcmi.server.errors.modsToDisable" : "{Le seguenti mod devono essere disattivate}",
|
||||||
|
"vcmi.server.errors.unknownEntity" : "Impossibile caricare il salvataggio! Entità sconosciuta '%s' trovata nel salvataggio! Il salvataggio potrebbe non essere compatibile con la versione attualmente installata delle mod!",
|
||||||
|
"vcmi.server.errors.wrongIdentified" : "Sei stato identificato come giocatore %s mentre ci si aspettava %s",
|
||||||
|
"vcmi.server.errors.notAllowed" : "Non ti è permesso eseguire questa azione!",
|
||||||
|
|
||||||
|
"vcmi.dimensionDoor.seaToLandError" : "Non è possibile teletrasportarsi dal mare alla terraferma o viceversa con la Porta Dimensionale.",
|
||||||
|
|
||||||
|
"vcmi.settingsMainWindow.generalTab.hover" : "Generale",
|
||||||
|
"vcmi.settingsMainWindow.generalTab.help" : "Passa alla scheda Opzioni generali, che contiene le impostazioni relative al comportamento generale del client di gioco.",
|
||||||
|
"vcmi.settingsMainWindow.battleTab.hover" : "Battaglia",
|
||||||
|
"vcmi.settingsMainWindow.battleTab.help" : "Passa alla scheda Opzioni di battaglia, che consente di configurare il comportamento del gioco durante le battaglie.",
|
||||||
|
"vcmi.settingsMainWindow.adventureTab.hover" : "Mappa Avventura",
|
||||||
|
"vcmi.settingsMainWindow.adventureTab.help" : "Passa alla scheda Opzioni Mappa Avventura (la mappa dell'avventura è la sezione del gioco in cui i giocatori controllano i movimenti degli eroi).",
|
||||||
|
|
||||||
|
"vcmi.systemOptions.videoGroup" : "Impostazioni video",
|
||||||
|
"vcmi.systemOptions.audioGroup" : "Impostazioni audio",
|
||||||
|
"vcmi.systemOptions.otherGroup" : "Altre impostazioni", // unused right now
|
||||||
|
"vcmi.systemOptions.townsGroup" : "Schermata della città",
|
||||||
|
|
||||||
|
"vcmi.statisticWindow.statistics" : "Statistiche",
|
||||||
|
"vcmi.statisticWindow.tsvCopy" : "Dati negli appunti",
|
||||||
|
"vcmi.statisticWindow.selectView" : "Seleziona vista",
|
||||||
|
"vcmi.statisticWindow.value" : "Valore",
|
||||||
|
"vcmi.statisticWindow.title.overview" : "Panoramica",
|
||||||
|
"vcmi.statisticWindow.title.resources" : "Risorse",
|
||||||
|
"vcmi.statisticWindow.title.income" : "Entrate",
|
||||||
|
"vcmi.statisticWindow.title.numberOfHeroes" : "Numero di eroi",
|
||||||
|
"vcmi.statisticWindow.title.numberOfTowns" : "Numero di città",
|
||||||
|
"vcmi.statisticWindow.title.numberOfArtifacts" : "Numero di artefatti",
|
||||||
|
"vcmi.statisticWindow.title.numberOfDwellings" : "Numero di dimore",
|
||||||
|
"vcmi.statisticWindow.title.numberOfMines" : "Numero di miniere",
|
||||||
|
"vcmi.statisticWindow.title.armyStrength" : "Forza dell'esercito",
|
||||||
|
"vcmi.statisticWindow.title.experience" : "Esperienza",
|
||||||
|
"vcmi.statisticWindow.title.resourcesSpentArmy" : "Costi dell'esercito",
|
||||||
|
"vcmi.statisticWindow.title.resourcesSpentBuildings" : "Costi degli edifici",
|
||||||
|
"vcmi.statisticWindow.title.mapExplored" : "Percentuale di mappa esplorata",
|
||||||
|
"vcmi.statisticWindow.param.playerName" : "Nome giocatore",
|
||||||
|
"vcmi.statisticWindow.param.daysSurvived" : "Giorni sopravvissuti",
|
||||||
|
"vcmi.statisticWindow.param.maxHeroLevel" : "Livello massimo dell'eroe",
|
||||||
|
"vcmi.statisticWindow.param.battleWinRatioHero" : "Tasso di vittoria (vs. eroe)",
|
||||||
|
"vcmi.statisticWindow.param.battleWinRatioNeutral" : "Tasso di vittoria (vs. neutrali)",
|
||||||
|
"vcmi.statisticWindow.param.battlesHero" : "Battaglie (vs. eroe)",
|
||||||
|
"vcmi.statisticWindow.param.battlesNeutral" : "Battaglie (vs. neutrali)",
|
||||||
|
"vcmi.statisticWindow.param.maxArmyStrength" : "Massima forza dell'esercito totale",
|
||||||
|
"vcmi.statisticWindow.param.tradeVolume" : "Volume di scambio",
|
||||||
|
"vcmi.statisticWindow.param.obeliskVisited" : "Obelisco visitato",
|
||||||
|
"vcmi.statisticWindow.icon.townCaptured" : "Città conquistate",
|
||||||
|
"vcmi.statisticWindow.icon.strongestHeroDefeated" : "Eroe più forte dell'avversario sconfitto",
|
||||||
|
"vcmi.statisticWindow.icon.grailFound" : "Graal trovato",
|
||||||
|
"vcmi.statisticWindow.icon.defeated" : "Sconfitto",
|
||||||
|
|
||||||
|
"vcmi.systemOptions.fullscreenBorderless.hover" : "Schermo intero (senza bordi)",
|
||||||
|
"vcmi.systemOptions.fullscreenBorderless.help" : "{Schermo intero senza bordi}\n\nSe selezionato, VCMI verrà eseguito in modalità schermo intero senza bordi. In questa modalità, il gioco utilizzerà sempre la stessa risoluzione del desktop, ignorando la risoluzione selezionata.",
|
||||||
|
"vcmi.systemOptions.fullscreenExclusive.hover" : "Schermo intero (esclusivo)",
|
||||||
|
"vcmi.systemOptions.fullscreenExclusive.help" : "{Schermo intero}\n\nSe selezionato, VCMI verrà eseguito in modalità schermo intero esclusiva. In questa modalità, il gioco cambierà la risoluzione del monitor con quella selezionata.",
|
||||||
|
"vcmi.systemOptions.resolutionButton.hover" : "Risoluzione: %wx%h",
|
||||||
|
"vcmi.systemOptions.resolutionButton.help" : "{Seleziona risoluzione}\n\nModifica la risoluzione dello schermo di gioco.",
|
||||||
|
"vcmi.systemOptions.resolutionMenu.hover" : "Seleziona risoluzione",
|
||||||
|
"vcmi.systemOptions.resolutionMenu.help" : "Modifica la risoluzione dello schermo di gioco.",
|
||||||
|
"vcmi.systemOptions.scalingButton.hover" : "Scala interfaccia: %p%",
|
||||||
|
"vcmi.systemOptions.scalingButton.help" : "{Scala interfaccia}\n\nModifica la scala dell'interfaccia di gioco.",
|
||||||
|
"vcmi.systemOptions.scalingMenu.hover" : "Seleziona scala interfaccia",
|
||||||
|
"vcmi.systemOptions.scalingMenu.help" : "Modifica la scala dell'interfaccia di gioco.",
|
||||||
|
"vcmi.systemOptions.longTouchButton.hover" : "Intervallo di tocco lungo: %d ms",
|
||||||
|
"vcmi.systemOptions.longTouchButton.help" : "{Intervallo di tocco lungo}\n\nQuando si utilizza il touchscreen, le finestre popup appariranno dopo aver toccato lo schermo per la durata specificata in millisecondi.",
|
||||||
|
"vcmi.systemOptions.longTouchMenu.hover" : "Seleziona intervallo di tocco lungo",
|
||||||
|
"vcmi.systemOptions.longTouchMenu.help" : "Modifica la durata dell'intervallo di tocco lungo.",
|
||||||
|
"vcmi.systemOptions.longTouchMenu.entry" : "%d millisecondi",
|
||||||
|
"vcmi.systemOptions.framerateButton.hover" : "Mostra FPS",
|
||||||
|
"vcmi.systemOptions.framerateButton.help" : "{Mostra FPS}\n\nAttiva o disattiva la visibilità del contatore dei fotogrammi al secondo nell'angolo della finestra di gioco.",
|
||||||
|
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Feedback aptico",
|
||||||
|
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Feedback aptico}\n\nAttiva o disattiva il feedback aptico sugli input tattili.",
|
||||||
|
"vcmi.systemOptions.enableUiEnhancementsButton.hover" : "Miglioramenti interfaccia",
|
||||||
|
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Miglioramenti interfaccia}\n\nAttiva vari miglioramenti della qualità della vita nell'interfaccia. Ad esempio, un pulsante per lo zaino, ecc. Disabilitalo per un'esperienza più classica.",
|
||||||
|
"vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Libro degli incantesimi grande",
|
||||||
|
"vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Libro degli incantesimi grande}\n\nAbilita un libro degli incantesimi più grande che mostra più incantesimi per pagina. L'animazione del cambio pagina non funziona con questa impostazione attivata.",
|
||||||
|
"vcmi.systemOptions.audioMuteFocus.hover" : "Silenzioso quando inattivo",
|
||||||
|
"vcmi.systemOptions.audioMuteFocus.help" : "{Silenzioso quando inattivo}\n\nDisattiva l'audio quando la finestra del gioco non è attiva. Fanno eccezione i messaggi di gioco e il suono del nuovo turno.",
|
||||||
|
|
||||||
|
"vcmi.adventureOptions.infoBarPick.hover" : "Mostra messaggi nel pannello informazioni",
|
||||||
|
"vcmi.adventureOptions.infoBarPick.help" : "{Mostra messaggi nel pannello informazioni}\n\nQuando possibile, i messaggi del gioco provenienti dagli oggetti sulla mappa saranno mostrati nel pannello informazioni, invece che comparire in una finestra separata.",
|
||||||
|
"vcmi.adventureOptions.numericQuantities.hover" : "Quantità di creature numeriche",
|
||||||
|
"vcmi.adventureOptions.numericQuantities.help" : "{Quantità di creature numeriche}\n\nMostra la quantità approssimativa di creature nemiche nel formato numerico A-B.",
|
||||||
|
"vcmi.adventureOptions.forceMovementInfo.hover" : "Mostra sempre il costo del movimento",
|
||||||
|
"vcmi.adventureOptions.forceMovementInfo.help" : "{Mostra sempre il costo del movimento}\n\nMostra sempre i dati dei punti movimento nella barra di stato (invece di visualizzarli solo quando si tiene premuto il tasto ALT).",
|
||||||
|
"vcmi.adventureOptions.showGrid.hover" : "Mostra griglia",
|
||||||
|
"vcmi.adventureOptions.showGrid.help" : "{Mostra griglia}\n\nMostra la griglia di sovrapposizione, evidenziando i confini tra le caselle della mappa avventura.",
|
||||||
|
"vcmi.adventureOptions.borderScroll.hover" : "Scorrimento ai bordi",
|
||||||
|
"vcmi.adventureOptions.borderScroll.help" : "{Scorrimento ai bordi}\n\nScorri la mappa avventura quando il cursore è vicino al bordo della finestra. Può essere disattivato tenendo premuto il tasto CTRL.",
|
||||||
|
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Gestione creature nel pannello informazioni",
|
||||||
|
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Gestione creature nel pannello informazioni}\n\nConsente di riordinare le creature nel pannello informazioni invece di alternarle tra i componenti predefiniti.",
|
||||||
|
"vcmi.adventureOptions.leftButtonDrag.hover" : "Trascinamento con clic sinistro",
|
||||||
|
"vcmi.adventureOptions.leftButtonDrag.help" : "{Trascinamento con clic sinistro}\n\nQuando attivato, spostando il mouse con il tasto sinistro premuto si trascina la visuale della mappa avventura.",
|
||||||
|
"vcmi.adventureOptions.rightButtonDrag.hover" : "Trascinamento con clic destro",
|
||||||
|
"vcmi.adventureOptions.rightButtonDrag.help" : "{Trascinamento con clic destro}\n\nQuando attivato, spostando il mouse con il tasto destro premuto si trascina la visuale della mappa avventura.",
|
||||||
|
"vcmi.adventureOptions.smoothDragging.hover" : "Trascinamento della mappa fluido",
|
||||||
|
"vcmi.adventureOptions.smoothDragging.help" : "{Trascinamento della mappa fluido}\n\nQuando attivato, il trascinamento della mappa ha un effetto di scorrimento fluido moderno.",
|
||||||
|
"vcmi.adventureOptions.skipAdventureMapAnimations.hover" : "Salta effetti di dissolvenza",
|
||||||
|
"vcmi.adventureOptions.skipAdventureMapAnimations.help" : "{Salta effetti di dissolvenza}\n\nQuando attivato, salta la dissolvenza degli oggetti e altri effetti simili (raccolta risorse, imbarco su navi, ecc.). Rende l'interfaccia più reattiva in alcuni casi, a scapito dell'estetica. Utile soprattutto nei giochi PvP. Per una velocità di movimento massima, la dissolvenza è sempre saltata indipendentemente da questa impostazione.",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed1.help": "Imposta la velocità di scorrimento della mappa su molto lenta.",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed5.help": "Imposta la velocità di scorrimento della mappa su molto veloce.",
|
||||||
|
"vcmi.adventureOptions.mapScrollSpeed6.help": "Imposta la velocità di scorrimento della mappa su istantanea.",
|
||||||
|
"vcmi.adventureOptions.hideBackground.hover" : "Nascondi Sfondo",
|
||||||
|
"vcmi.adventureOptions.hideBackground.help" : "{Nascondi Sfondo}\n\nNasconde la mappa dell'avventura nello sfondo e mostra una texture al suo posto.",
|
||||||
|
|
||||||
|
"vcmi.battleOptions.queueSizeLabel.hover": "Mostra coda dell'ordine di turno",
|
||||||
|
"vcmi.battleOptions.queueSizeNoneButton.hover": "SPENTO",
|
||||||
|
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
|
||||||
|
"vcmi.battleOptions.queueSizeSmallButton.hover": "PICCOLO",
|
||||||
|
"vcmi.battleOptions.queueSizeBigButton.hover": "GRANDE",
|
||||||
|
"vcmi.battleOptions.queueSizeNoneButton.help": "Non visualizzare la coda dell'ordine di turno.",
|
||||||
|
"vcmi.battleOptions.queueSizeAutoButton.help": "Regola automaticamente la dimensione della coda dell'ordine di turno in base alla risoluzione del gioco (PICCOLO viene utilizzato se l'altezza della risoluzione è inferiore a 700 pixel, GRANDE viene usato altrimenti).",
|
||||||
|
"vcmi.battleOptions.queueSizeSmallButton.help": "Imposta la dimensione della coda dell'ordine di turno su PICCOLO.",
|
||||||
|
"vcmi.battleOptions.queueSizeBigButton.help": "Imposta la dimensione della coda dell'ordine di turno su GRANDE (non supportato se l'altezza della risoluzione del gioco è inferiore a 700 pixel).",
|
||||||
|
"vcmi.battleOptions.animationsSpeed1.hover": "",
|
||||||
|
"vcmi.battleOptions.animationsSpeed5.hover": "",
|
||||||
|
"vcmi.battleOptions.animationsSpeed6.hover": "",
|
||||||
|
"vcmi.battleOptions.animationsSpeed1.help": "Imposta la velocità dell'animazione su molto lenta.",
|
||||||
|
"vcmi.battleOptions.animationsSpeed5.help": "Imposta la velocità dell'animazione su molto veloce.",
|
||||||
|
"vcmi.battleOptions.animationsSpeed6.help": "Imposta la velocità dell'animazione su istantanea.",
|
||||||
|
"vcmi.battleOptions.movementHighlightOnHover.hover": "Evidenzia il movimento al passaggio del mouse",
|
||||||
|
"vcmi.battleOptions.movementHighlightOnHover.help": "{Evidenzia il movimento al passaggio del mouse}\n\nEvidenzia il raggio di movimento dell'unità quando ci passi sopra con il cursore.",
|
||||||
|
"vcmi.battleOptions.rangeLimitHighlightOnHover.hover": "Mostra limiti di gittata per tiratori",
|
||||||
|
"vcmi.battleOptions.rangeLimitHighlightOnHover.help": "{Mostra limiti di gittata per tiratori}\n\nMostra i limiti di gittata del tiratore quando ci passi sopra con il cursore.",
|
||||||
|
"vcmi.battleOptions.showStickyHeroInfoWindows.hover": "Mostra finestre statistiche degli eroi",
|
||||||
|
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Mostra finestre statistiche degli eroi}\n\nAttiva in modo permanente le finestre statistiche degli eroi che mostrano le statistiche primarie e i punti incantesimo.",
|
||||||
|
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Salta musica introduttiva",
|
||||||
|
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Salta musica introduttiva}\n\nPermette di compiere azioni durante la musica introduttiva che viene riprodotta all'inizio di ogni battaglia.",
|
||||||
|
"vcmi.battleOptions.endWithAutocombat.hover": "Termina la battaglia",
|
||||||
|
"vcmi.battleOptions.endWithAutocombat.help": "{Termina la battaglia}\n\nL'Auto-Combat gioca la battaglia fino alla fine immediatamente.",
|
||||||
|
"vcmi.battleOptions.showQuickSpell.hover": "Mostra pannello incantesimi rapidi",
|
||||||
|
"vcmi.battleOptions.showQuickSpell.help": "{Mostra pannello incantesimi rapidi}\n\nMostra il pannello per la selezione rapida degli incantesimi.",
|
||||||
|
|
||||||
|
"vcmi.adventureMap.revisitObject.hover" : "Rivisita oggetto",
|
||||||
|
"vcmi.adventureMap.revisitObject.help" : "{Rivisita oggetto}\n\nSe un eroe si trova su un oggetto della mappa, può rivisitare la posizione.",
|
||||||
|
|
||||||
|
"vcmi.battleWindow.pressKeyToSkipIntro" : "Premi un tasto qualsiasi per iniziare immediatamente la battaglia",
|
||||||
|
"vcmi.battleWindow.damageEstimation.melee" : "Attacca %CREATURE (%DAMAGE).",
|
||||||
|
"vcmi.battleWindow.damageEstimation.meleeKills" : "Attacca %CREATURE (%DAMAGE, %KILLS).",
|
||||||
|
"vcmi.battleWindow.damageEstimation.ranged" : "Spara a %CREATURE (%SHOTS, %DAMAGE).",
|
||||||
|
"vcmi.battleWindow.damageEstimation.rangedKills" : "Spara a %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
|
||||||
|
"vcmi.battleWindow.damageEstimation.shots" : "%d colpi rimasti",
|
||||||
|
"vcmi.battleWindow.damageEstimation.shots.1" : "%d colpo rimasto",
|
||||||
|
"vcmi.battleWindow.damageEstimation.damage" : "%d danni",
|
||||||
|
"vcmi.battleWindow.damageEstimation.damage.1" : "%d danni",
|
||||||
|
"vcmi.battleWindow.damageEstimation.kills" : "%d periranno",
|
||||||
|
"vcmi.battleWindow.damageEstimation.kills.1" : "%d perirà",
|
||||||
|
|
||||||
|
"vcmi.battleWindow.damageRetaliation.will" : "Contrattaccherà",
|
||||||
|
"vcmi.battleWindow.damageRetaliation.may" : "Potrebbe contrattaccare",
|
||||||
|
"vcmi.battleWindow.damageRetaliation.never" : "Non contrattaccherà.",
|
||||||
|
"vcmi.battleWindow.damageRetaliation.damage" : "(%DAMAGE).",
|
||||||
|
"vcmi.battleWindow.damageRetaliation.damageKills" : "(%DAMAGE, %KILLS).",
|
||||||
|
|
||||||
|
"vcmi.battleWindow.killed" : "Ucciso",
|
||||||
|
"vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s sono stati uccisi da colpi precisi!",
|
||||||
|
"vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s è stato ucciso con un colpo preciso!",
|
||||||
|
"vcmi.battleWindow.accurateShot.resultDescription.2" : "%d %s sono stati uccisi da colpi precisi!",
|
||||||
|
"vcmi.battleWindow.endWithAutocombat" : "Sei sicuro di voler terminare la battaglia con il combattimento automatico?",
|
||||||
|
|
||||||
|
"vcmi.battleResultsWindow.applyResultsLabel" : "Accettare il risultato della battaglia?",
|
||||||
|
|
||||||
|
"vcmi.tutorialWindow.title" : "Introduzione touchscreen",
|
||||||
|
"vcmi.tutorialWindow.decription.RightClick" : "Tocca e tieni premuto l'elemento su cui desideri fare clic destro. Tocca un'area libera per chiudere.",
|
||||||
|
"vcmi.tutorialWindow.decription.MapPanning" : "Tocca e trascina con un dito per spostare la mappa.",
|
||||||
|
"vcmi.tutorialWindow.decription.MapZooming" : "Pizzica con due dita per modificare lo zoom della mappa.",
|
||||||
|
"vcmi.tutorialWindow.decription.RadialWheel" : "Scorrendo si apre la ruota radiale per varie azioni, come la gestione delle creature/eroi e l'ordinamento delle città.",
|
||||||
|
"vcmi.tutorialWindow.decription.BattleDirection" : "Per attaccare da una direzione specifica, scorri nella direzione da cui deve essere effettuato l'attacco.",
|
||||||
|
"vcmi.tutorialWindow.decription.BattleDirectionAbort" : "Il gesto di attacco direzionale può essere annullato se il dito è sufficientemente lontano.",
|
||||||
|
"vcmi.tutorialWindow.decription.AbortSpell" : "Tocca e tieni premuto per annullare un incantesimo.",
|
||||||
|
|
||||||
|
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Mostra creature disponibili",
|
||||||
|
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Mostra creature disponibili}\n\nMostra il numero di creature disponibili per l'acquisto invece della loro crescita nel riepilogo della città (angolo in basso a sinistra della schermata della città).",
|
||||||
|
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Mostra crescita settimanale delle creature",
|
||||||
|
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Mostra crescita settimanale delle creature}\n\nMostra la crescita settimanale delle creature invece della quantità disponibile nel riepilogo della città (angolo in basso a sinistra della schermata della città).",
|
||||||
|
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Info compatta delle creature",
|
||||||
|
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Info compatta delle creature}\n\nMostra informazioni più piccole per le creature della città nel riepilogo della città (angolo in basso a sinistra della schermata della città).",
|
||||||
|
|
||||||
|
"vcmi.townHall.missingBase" : "L'edificio base %s deve essere costruito prima",
|
||||||
|
"vcmi.townHall.noCreaturesToRecruit" : "Non ci sono creature da reclutare!",
|
||||||
|
|
||||||
|
"vcmi.townStructure.bank.borrow" : "Entri in banca. Un banchiere ti vede e dice: \"Abbiamo preparato un'offerta speciale per te. Puoi prendere un prestito di 2500 oro per 5 giorni. Dovrai restituire 500 oro ogni giorno.\"",
|
||||||
|
"vcmi.townStructure.bank.payBack" : "Entri in banca. Un banchiere ti vede e dice: \"Hai già ottenuto un prestito. Rimborsalo prima di prenderne un altro.\"",
|
||||||
|
|
||||||
|
"vcmi.logicalExpressions.anyOf" : "Qualsiasi dei seguenti:",
|
||||||
|
"vcmi.logicalExpressions.allOf" : "Tutti i seguenti:",
|
||||||
|
"vcmi.logicalExpressions.noneOf" : "Nessuno dei seguenti:",
|
||||||
|
|
||||||
|
"vcmi.heroWindow.openCommander.hover" : "Apri finestra informazioni comandante",
|
||||||
|
"vcmi.heroWindow.openCommander.help" : "Mostra i dettagli sul comandante di questo eroe.",
|
||||||
|
"vcmi.heroWindow.openBackpack.hover" : "Apri finestra zaino artefatti",
|
||||||
|
"vcmi.heroWindow.openBackpack.help" : "Apre una finestra che consente una gestione più semplice dello zaino artefatti.",
|
||||||
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Ordina per costo",
|
||||||
|
"vcmi.heroWindow.sortBackpackByCost.help" : "Ordina gli artefatti nello zaino in base al costo.",
|
||||||
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Ordina per slot",
|
||||||
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "Ordina gli artefatti nello zaino in base allo slot equipaggiato.",
|
||||||
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Ordina per classe",
|
||||||
|
"vcmi.heroWindow.sortBackpackByClass.help" : "Ordina gli artefatti nello zaino in base alla classe dell'artefatto. Tesoro, Minore, Maggiore, Reliquia",
|
||||||
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Possiedi tutti i componenti necessari per la fusione del %s. Vuoi eseguire la fusione? {Tutti i componenti saranno consumati durante la fusione.}",
|
||||||
|
|
||||||
|
"vcmi.tavernWindow.inviteHero" : "Invita eroe",
|
||||||
|
|
||||||
|
"vcmi.commanderWindow.artifactMessage" : "Vuoi restituire questo artefatto all'eroe?",
|
||||||
|
|
||||||
|
"vcmi.creatureWindow.showBonuses.hover" : "Passa alla vista bonus",
|
||||||
|
"vcmi.creatureWindow.showBonuses.help" : "Mostra tutti i bonus attivi del comandante.",
|
||||||
|
"vcmi.creatureWindow.showSkills.hover" : "Passa alla vista abilità",
|
||||||
|
"vcmi.creatureWindow.showSkills.help" : "Mostra tutte le abilità apprese del comandante.",
|
||||||
|
"vcmi.creatureWindow.returnArtifact.hover" : "Restituisci artefatto",
|
||||||
|
"vcmi.creatureWindow.returnArtifact.help" : "Fai clic su questo pulsante per restituire l'artefatto allo zaino dell'eroe.",
|
||||||
|
|
||||||
|
"vcmi.questLog.hideComplete.hover" : "Nascondi missioni completate",
|
||||||
|
"vcmi.questLog.hideComplete.help" : "Nasconde tutte le missioni completate.",
|
||||||
|
|
||||||
|
"vcmi.randomMapTab.widgets.randomTemplate" : "(Casuale)",
|
||||||
|
"vcmi.randomMapTab.widgets.templateLabel" : "Modello",
|
||||||
|
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Imposta...",
|
||||||
|
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Allineamenti squadra",
|
||||||
|
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Tipi di strade",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.turnOptions.hover" : "Opzioni turno",
|
||||||
|
"vcmi.optionsTab.turnOptions.help" : "Seleziona opzioni timer turno e turni simultanei",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.chessFieldBase.hover" : "Timer base",
|
||||||
|
"vcmi.optionsTab.chessFieldTurn.hover" : "Timer turno",
|
||||||
|
"vcmi.optionsTab.chessFieldBattle.hover" : "Timer battaglia",
|
||||||
|
"vcmi.optionsTab.chessFieldUnit.hover" : "Timer unità",
|
||||||
|
"vcmi.optionsTab.chessFieldBase.help" : "Utilizzato quando {Timer turno} raggiunge 0. Impostato una sola volta all'inizio del gioco. Al raggiungimento dello zero, il turno corrente termina. Qualsiasi combattimento in corso si concluderà con una sconfitta.",
|
||||||
|
"vcmi.optionsTab.chessFieldTurnAccumulate.help" : "Utilizzato fuori dal combattimento o quando {Timer battaglia} scade. Resettato ogni turno. Il tempo residuo viene aggiunto a {Timer base} alla fine del turno.",
|
||||||
|
"vcmi.optionsTab.chessFieldTurnDiscard.help" : "Utilizzato fuori dal combattimento o quando {Timer battaglia} scade. Resettato ogni turno. Il tempo non speso viene perso.",
|
||||||
|
"vcmi.optionsTab.chessFieldBattle.help" : "Utilizzato nelle battaglie con IA o nei combattimenti PvP quando {Timer unità} scade. Resettato all'inizio di ogni combattimento.",
|
||||||
|
"vcmi.optionsTab.chessFieldUnitAccumulate.help" : "Utilizzato quando si seleziona l'azione di un'unità nei combattimenti PvP. Il tempo residuo viene aggiunto a {Timer battaglia} alla fine del turno dell'unità.",
|
||||||
|
"vcmi.optionsTab.chessFieldUnitDiscard.help" : "Utilizzato quando si seleziona l'azione di un'unità nei combattimenti PvP. Resettato all'inizio del turno di ogni unità. Il tempo non speso viene perso.",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.accumulate" : "Accumula",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.simturnsTitle" : "Turni simultanei",
|
||||||
|
"vcmi.optionsTab.simturnsMin.hover" : "Almeno per",
|
||||||
|
"vcmi.optionsTab.simturnsMax.hover" : "Al massimo per",
|
||||||
|
"vcmi.optionsTab.simturnsAI.hover" : "(Sperimentale) Turni simultanei IA",
|
||||||
|
"vcmi.optionsTab.simturnsMin.help" : "Gioca simultaneamente per il numero di giorni specificato. I contatti tra i giocatori durante questo periodo sono bloccati.",
|
||||||
|
"vcmi.optionsTab.simturnsMax.help" : "Gioca simultaneamente per il numero di giorni specificato o fino al contatto con un altro giocatore.",
|
||||||
|
"vcmi.optionsTab.simturnsAI.help" : "{Turni simultanei IA}\nOpzione sperimentale. Consente ai giocatori IA di agire contemporaneamente ai giocatori umani quando i turni simultanei sono abilitati.",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.turnTime.select" : "Seleziona preset timer turno",
|
||||||
|
"vcmi.optionsTab.turnTime.unlimited" : "Tempo di turno illimitato",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.1" : "Timer classico: 1 minuto",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.2" : "Timer classico: 2 minuti",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.5" : "Timer classico: 5 minuti",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.10" : "Timer classico: 10 minuti",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.20" : "Timer classico: 20 minuti",
|
||||||
|
"vcmi.optionsTab.turnTime.classic.30" : "Timer classico: 30 minuti",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.20" : "Scacchi: 20:00 + 10:00 + 02:00 + 00:00",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.16" : "Scacchi: 16:00 + 08:00 + 01:30 + 00:00",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.8" : "Scacchi: 08:00 + 04:00 + 01:00 + 00:00",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.4" : "Scacchi: 04:00 + 02:00 + 00:30 + 00:00",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.2" : "Scacchi: 02:00 + 01:00 + 00:15 + 00:00",
|
||||||
|
"vcmi.optionsTab.turnTime.chess.1" : "Scacchi: 01:00 + 01:00 + 00:00 + 00:00",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.simturns.select" : "Seleziona preset turni simultanei",
|
||||||
|
"vcmi.optionsTab.simturns.none" : "Nessun turno simultaneo",
|
||||||
|
"vcmi.optionsTab.simturns.tillContactMax" : "Turni simultanei: fino al contatto",
|
||||||
|
"vcmi.optionsTab.simturns.tillContact1" : "Turni simultanei: 1 settimana, interruzione al contatto",
|
||||||
|
"vcmi.optionsTab.simturns.tillContact2" : "Turni simultanei: 2 settimane, interruzione al contatto",
|
||||||
|
"vcmi.optionsTab.simturns.tillContact4" : "Turni simultanei: 1 mese, interruzione al contatto",
|
||||||
|
"vcmi.optionsTab.simturns.blocked1" : "Turni simultanei: 1 settimana, contatti bloccati",
|
||||||
|
"vcmi.optionsTab.simturns.blocked2" : "Turni simultanei: 2 settimane, contatti bloccati",
|
||||||
|
"vcmi.optionsTab.simturns.blocked4" : "Turni simultanei: 1 mese, contatti bloccati",
|
||||||
|
|
||||||
|
// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language
|
||||||
|
// Using this information, VCMI will automatically select correct plural form for every possible amount
|
||||||
|
"vcmi.optionsTab.simturns.days.0" : " %d giorni",
|
||||||
|
"vcmi.optionsTab.simturns.days.1" : " %d giorno",
|
||||||
|
"vcmi.optionsTab.simturns.days.2" : " %d giorni",
|
||||||
|
"vcmi.optionsTab.simturns.weeks.0" : " %d settimane",
|
||||||
|
"vcmi.optionsTab.simturns.weeks.1" : " %d settimana",
|
||||||
|
"vcmi.optionsTab.simturns.weeks.2" : " %d settimane",
|
||||||
|
"vcmi.optionsTab.simturns.months.0" : " %d mesi",
|
||||||
|
"vcmi.optionsTab.simturns.months.1" : " %d mese",
|
||||||
|
"vcmi.optionsTab.simturns.months.2" : " %d mesi",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.extraOptions.hover" : "Opzioni extra",
|
||||||
|
"vcmi.optionsTab.extraOptions.help" : "Impostazioni aggiuntive per il gioco",
|
||||||
|
|
||||||
|
"vcmi.optionsTab.cheatAllowed.hover" : "Permetti trucchi",
|
||||||
|
"vcmi.optionsTab.unlimitedReplay.hover" : "Replay battaglia illimitato",
|
||||||
|
"vcmi.optionsTab.cheatAllowed.help" : "{Permetti trucchi}\nPermette l'inserimento di trucchi durante il gioco.",
|
||||||
|
"vcmi.optionsTab.unlimitedReplay.help" : "{Replay battaglia illimitato}\nNessun limite di riproduzione delle battaglie.",
|
||||||
|
|
||||||
|
// Custom victory conditions for H3 campaigns and HotA maps
|
||||||
|
"vcmi.map.victoryCondition.daysPassed.toOthers" : "Il nemico è riuscito a sopravvivere fino a questo giorno. La vittoria è sua!",
|
||||||
|
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Congratulazioni! Sei riuscito a sopravvivere. La vittoria è tua!",
|
||||||
|
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "Il nemico ha sconfitto tutti i mostri che infestavano questa terra e reclama la vittoria!",
|
||||||
|
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Congratulazioni! Hai sconfitto tutti i mostri che infestavano questa terra e puoi reclamare la vittoria!",
|
||||||
|
"vcmi.map.victoryCondition.collectArtifacts.message" : "Acquisisci tre artefatti",
|
||||||
|
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Congratulazioni! Hai sconfitto tutti i tuoi nemici e hai l'Alleanza Angelica! La vittoria è tua!",
|
||||||
|
"vcmi.map.victoryCondition.angelicAlliance.message" : "Sconfiggi tutti i nemici e crea l'Alleanza Angelica",
|
||||||
|
"vcmi.map.victoryCondition.angelicAlliancePartLost.toSelf" : "Ahimè, hai perso parte dell'Alleanza Angelica. Tutto è perduto.",
|
||||||
|
|
||||||
|
// few strings from WoG used by vcmi
|
||||||
|
"vcmi.stackExperience.description" : "» Dettagli esperienza truppa «\n\nTipo di creatura ................... : %s\nGrado esperienza ................. : %s (%i)\nPunti esperienza ............... : %i\nPunti esperienza al livello successivo .. : %i\nEsperienza massima per battaglia ... : %i%% (%i)\nNumero di creature nello stack .... : %i\nNumero massimo di nuove reclute\n senza perdere il grado attuale .... : %i\nMoltiplicatore esperienza ........... : %.2f\nMoltiplicatore miglioramento .............. : %.2f\nEsperienza dopo il livello 10 ........ : %i\nNumero massimo di nuove reclute per mantenere il\n grado 10 se a esperienza massima : %i",
|
||||||
|
"vcmi.stackExperience.rank.0" : "Base",
|
||||||
|
"vcmi.stackExperience.rank.1" : "Principiante",
|
||||||
|
"vcmi.stackExperience.rank.2" : "Addestrato",
|
||||||
|
"vcmi.stackExperience.rank.3" : "Abile",
|
||||||
|
"vcmi.stackExperience.rank.4" : "Esperto",
|
||||||
|
"vcmi.stackExperience.rank.5" : "Veterano",
|
||||||
|
"vcmi.stackExperience.rank.6" : "Maestro",
|
||||||
|
"vcmi.stackExperience.rank.7" : "Esperto",
|
||||||
|
"vcmi.stackExperience.rank.8" : "Gran Maestro",
|
||||||
|
"vcmi.stackExperience.rank.9" : "Campione",
|
||||||
|
"vcmi.stackExperience.rank.10" : "Asso",
|
||||||
|
|
||||||
|
// Strings for HotA Seer Hut / Quest Guards
|
||||||
|
"core.seerhut.quest.heroClass.complete.0" : "Ah, sei %s. Ecco un regalo per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.complete.1" : "Ah, sei %s. Ecco un regalo per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.complete.2" : "Ah, sei %s. Ecco un regalo per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.complete.3" : "Le guardie notano che sei %s e ti offrono di passare. Accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.complete.4" : "Le guardie notano che sei %s e ti offrono di passare. Accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.complete.5" : "Le guardie notano che sei %s e ti offrono di passare. Accetti?",
|
||||||
|
"core.seerhut.quest.heroClass.description.0" : "Invia %s a %s",
|
||||||
|
"core.seerhut.quest.heroClass.description.1" : "Invia %s a %s",
|
||||||
|
"core.seerhut.quest.heroClass.description.2" : "Invia %s a %s",
|
||||||
|
"core.seerhut.quest.heroClass.description.3" : "Invia %s ad aprire il cancello",
|
||||||
|
"core.seerhut.quest.heroClass.description.4" : "Invia %s ad aprire il cancello",
|
||||||
|
"core.seerhut.quest.heroClass.description.5" : "Invia %s ad aprire il cancello",
|
||||||
|
"core.seerhut.quest.heroClass.hover.0" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.hover.1" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.hover.2" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.hover.3" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.hover.4" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.hover.5" : "(cerca un eroe della classe %s)",
|
||||||
|
"core.seerhut.quest.heroClass.receive.0" : "Ho un regalo per %s.",
|
||||||
|
"core.seerhut.quest.heroClass.receive.1" : "Ho un regalo per %s.",
|
||||||
|
"core.seerhut.quest.heroClass.receive.2" : "Ho un regalo per %s.",
|
||||||
|
"core.seerhut.quest.heroClass.receive.3" : "Le guardie qui dicono che lasceranno passare solo %s.",
|
||||||
|
"core.seerhut.quest.heroClass.receive.4" : "Le guardie qui dicono che lasceranno passare solo %s.",
|
||||||
|
"core.seerhut.quest.heroClass.receive.5" : "Le guardie qui dicono che lasceranno passare solo %s.",
|
||||||
|
"core.seerhut.quest.heroClass.visit.0" : "Non sei %s. Non ho niente per te. Vattene!",
|
||||||
|
"core.seerhut.quest.heroClass.visit.1" : "Non sei %s. Non ho niente per te. Vattene!",
|
||||||
|
"core.seerhut.quest.heroClass.visit.2" : "Non sei %s. Non ho niente per te. Vattene!",
|
||||||
|
"core.seerhut.quest.heroClass.visit.3" : "Le guardie qui lasceranno passare solo %s.",
|
||||||
|
"core.seerhut.quest.heroClass.visit.4" : "Le guardie qui lasceranno passare solo %s.",
|
||||||
|
"core.seerhut.quest.heroClass.visit.5" : "Le guardie qui lasceranno passare solo %s.",
|
||||||
|
|
||||||
|
"core.seerhut.quest.reachDate.complete.0" : "Ora sono libero. Ecco cosa ho per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.reachDate.complete.1" : "Ora sono libero. Ecco cosa ho per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.reachDate.complete.2" : "Ora sono libero. Ecco cosa ho per te. Lo accetti?",
|
||||||
|
"core.seerhut.quest.reachDate.complete.3" : "Ora puoi passare. Vuoi attraversare?",
|
||||||
|
"core.seerhut.quest.reachDate.complete.4" : "Ora puoi passare. Vuoi attraversare?",
|
||||||
|
"core.seerhut.quest.reachDate.complete.5" : "Ora puoi passare. Vuoi attraversare?",
|
||||||
|
"core.seerhut.quest.reachDate.description.0" : "Aspetta fino a %s per %s",
|
||||||
|
"core.seerhut.quest.reachDate.description.1" : "Aspetta fino a %s per %s",
|
||||||
|
"core.seerhut.quest.reachDate.description.2" : "Aspetta fino a %s per %s",
|
||||||
|
"core.seerhut.quest.reachDate.description.3" : "Aspetta fino a %s per aprire il cancello",
|
||||||
|
"core.seerhut.quest.reachDate.description.4" : "Aspetta fino a %s per aprire il cancello",
|
||||||
|
"core.seerhut.quest.reachDate.description.5" : "Aspetta fino a %s per aprire il cancello",
|
||||||
|
"core.seerhut.quest.reachDate.hover.0" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.hover.1" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.hover.2" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.hover.3" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.hover.4" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.hover.5" : "(Non tornare prima di %s)",
|
||||||
|
"core.seerhut.quest.reachDate.receive.0" : "Sono occupato. Torna dopo il %s",
|
||||||
|
"core.seerhut.quest.reachDate.receive.1" : "Sono occupato. Torna dopo il %s",
|
||||||
|
"core.seerhut.quest.reachDate.receive.2" : "Sono occupato. Torna dopo il %s",
|
||||||
|
"core.seerhut.quest.reachDate.receive.3" : "Chiuso fino al %s.",
|
||||||
|
"core.seerhut.quest.reachDate.receive.4" : "Chiuso fino al %s.",
|
||||||
|
"core.seerhut.quest.reachDate.receive.5" : "Chiuso fino al %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.0" : "Sono occupato. Torna dopo il %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.1" : "Sono occupato. Torna dopo il %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.2" : "Sono occupato. Torna dopo il %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.3" : "Chiuso fino al %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.4" : "Chiuso fino al %s.",
|
||||||
|
"core.seerhut.quest.reachDate.visit.5" : "Chiuso fino al %s.",
|
||||||
|
|
||||||
|
"mapObject.core.hillFort.object.description" : "Aggiorna le creature. I livelli 1 - 4 sono meno costosi rispetto alla città associata.",
|
||||||
|
|
||||||
|
"core.bonus.ADDITIONAL_ATTACK.name": "Doppio colpo",
|
||||||
|
"core.bonus.ADDITIONAL_ATTACK.description": "Attacca due volte",
|
||||||
|
"core.bonus.ADDITIONAL_RETALIATION.name": "Ritorsioni aggiuntive",
|
||||||
|
"core.bonus.ADDITIONAL_RETALIATION.description": "Può contrattaccare ${val} volte in più",
|
||||||
|
"core.bonus.AIR_IMMUNITY.name": "Immunità all'aria",
|
||||||
|
"core.bonus.AIR_IMMUNITY.description": "Immune a tutti gli incantesimi della scuola di magia dell'Aria",
|
||||||
|
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Attacco a 360°",
|
||||||
|
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Attacca tutti i nemici adiacenti",
|
||||||
|
"core.bonus.BLOCKS_RETALIATION.name": "Nessuna ritorsione",
|
||||||
|
"core.bonus.BLOCKS_RETALIATION.description": "Il nemico non può contrattaccare",
|
||||||
|
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Nessuna ritorsione a distanza",
|
||||||
|
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Il nemico non può contrattaccare con un attacco a distanza",
|
||||||
|
"core.bonus.CATAPULT.name": "Catapulta",
|
||||||
|
"core.bonus.CATAPULT.description": "Attacca le mura d'assedio",
|
||||||
|
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Riduce il costo del lancio (${val})",
|
||||||
|
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Riduce il costo del lancio degli incantesimi dell'eroe di ${val}",
|
||||||
|
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Resistenza magica (${val}%)",
|
||||||
|
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Aumenta il costo del lancio degli incantesimi nemici di ${val}",
|
||||||
|
"core.bonus.CHARGE_IMMUNITY.name": "Immunità alla carica",
|
||||||
|
"core.bonus.CHARGE_IMMUNITY.description": "Immune alla carica di Cavalieri e Campioni",
|
||||||
|
"core.bonus.DARKNESS.name": "Oscurità",
|
||||||
|
"core.bonus.DARKNESS.description": "Crea un velo d'oscurità con raggio ${val}",
|
||||||
|
"core.bonus.DEATH_STARE.name": "Sguardo della morte (${val}%)",
|
||||||
|
"core.bonus.DEATH_STARE.description": "Ha una probabilità del ${val}% di uccidere un'unità singola",
|
||||||
|
"core.bonus.DEFENSIVE_STANCE.name": "Bonus di difesa",
|
||||||
|
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Difesa quando è in posizione difensiva",
|
||||||
|
"core.bonus.DESTRUCTION.name": "Distruzione",
|
||||||
|
"core.bonus.DESTRUCTION.description": "Ha una probabilità del ${val}% di uccidere unità extra dopo l'attacco",
|
||||||
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Colpo della morte",
|
||||||
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Ha una probabilità del ${val}% di infliggere il doppio dei danni base quando attacca",
|
||||||
|
"core.bonus.DRAGON_NATURE.name": "Drago",
|
||||||
|
"core.bonus.DRAGON_NATURE.description": "Creatura con Natura del Drago",
|
||||||
|
"core.bonus.EARTH_IMMUNITY.name": "Immunità alla terra",
|
||||||
|
"core.bonus.EARTH_IMMUNITY.description": "Immune a tutti gli incantesimi della scuola di magia della Terra",
|
||||||
|
"core.bonus.ENCHANTER.name": "Incantatore",
|
||||||
|
"core.bonus.ENCHANTER.description": "Può lanciare l'incantesimo ${subtype.spell} ogni turno",
|
||||||
|
"core.bonus.ENCHANTED.name": "Incantato",
|
||||||
|
"core.bonus.ENCHANTED.description": "Sotto effetto permanente di ${subtype.spell}",
|
||||||
|
"core.bonus.ENEMY_ATTACK_REDUCTION.name": "Ignora attacco (${val}%)",
|
||||||
|
"core.bonus.ENEMY_ATTACK_REDUCTION.description": "Quando viene attaccata, ignora il ${val}% dell'attacco dell'avversario",
|
||||||
|
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignora difesa (${val}%)",
|
||||||
|
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Quando attacca, ignora il ${val}% della difesa dell'avversario",
|
||||||
|
"core.bonus.FIRE_IMMUNITY.name": "Immunità al fuoco",
|
||||||
|
"core.bonus.FIRE_IMMUNITY.description": "Immune a tutti gli incantesimi della scuola di magia del Fuoco",
|
||||||
|
"core.bonus.FIRE_SHIELD.name": "Scudo di fuoco (${val}%)",
|
||||||
|
"core.bonus.FIRE_SHIELD.description": "Riflette una parte dei danni da mischia",
|
||||||
|
"core.bonus.FIRST_STRIKE.name": "Primo colpo",
|
||||||
|
"core.bonus.FIRST_STRIKE.description": "Questa creatura contrattacca prima di essere attaccata",
|
||||||
|
"core.bonus.FEAR.name": "Paura",
|
||||||
|
"core.bonus.FEAR.description": "Provoca paura su una pila nemica",
|
||||||
|
"core.bonus.FEARLESS.name": "Impavido",
|
||||||
|
"core.bonus.FEARLESS.description": "Immune all'abilità Paura",
|
||||||
|
"core.bonus.FEROCITY.name": "Ferocia",
|
||||||
|
"core.bonus.FEROCITY.description": "Attacca ${val} volte aggiuntive se uccide qualcuno",
|
||||||
|
"core.bonus.FLYING.name": "Volare",
|
||||||
|
"core.bonus.FLYING.description": "Si muove volando (ignora gli ostacoli)",
|
||||||
|
"core.bonus.FREE_SHOOTING.name": "Colpo ravvicinato",
|
||||||
|
"core.bonus.FREE_SHOOTING.description": "Può usare attacchi a distanza anche in mischia",
|
||||||
|
"core.bonus.GARGOYLE.name": "Gargoyle",
|
||||||
|
"core.bonus.GARGOYLE.description": "Non può essere rianimato o curato",
|
||||||
|
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Riduzione danno (${val}%)",
|
||||||
|
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Riduce il danno fisico da attacchi a distanza o corpo a corpo",
|
||||||
|
"core.bonus.HATE.name": "Odia ${subtype.creature}",
|
||||||
|
"core.bonus.HATE.description": "Infligge ${val}% di danni in più a ${subtype.creature}",
|
||||||
|
"core.bonus.HEALER.name": "Guaritore",
|
||||||
|
"core.bonus.HEALER.description": "Cura le unità alleate",
|
||||||
|
"core.bonus.HP_REGENERATION.name": "Rigenerazione",
|
||||||
|
"core.bonus.HP_REGENERATION.description": "Cura ${val} punti ferita ogni turno",
|
||||||
|
"core.bonus.JOUSTING.name": "Carica del Campione",
|
||||||
|
"core.bonus.JOUSTING.description": "+${val}% danno per ogni esagono percorso",
|
||||||
|
"core.bonus.KING.name": "Re",
|
||||||
|
"core.bonus.KING.description": "Vulnerabile a SLAUGHTER di livello ${val} o superiore",
|
||||||
|
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Immunità agli incantesimi 1-${val}",
|
||||||
|
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immunità agli incantesimi di livello 1-${val}",
|
||||||
|
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Portata limitata",
|
||||||
|
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Impossibile attaccare unità oltre ${val} esagoni",
|
||||||
|
"core.bonus.LIFE_DRAIN.name": "Assorbimento vitale (${val}%)",
|
||||||
|
"core.bonus.LIFE_DRAIN.description": "Drena ${val}% del danno inflitto",
|
||||||
|
"core.bonus.MANA_CHANNELING.name": "Canale Magico ${val}%",
|
||||||
|
"core.bonus.MANA_CHANNELING.description": "Fornisce al tuo eroe ${val}% del mana speso dal nemico",
|
||||||
|
"core.bonus.MANA_DRAIN.name": "Drenaggio di mana",
|
||||||
|
"core.bonus.MANA_DRAIN.description": "Drena ${val} mana ogni turno",
|
||||||
|
"core.bonus.MAGIC_MIRROR.name": "Specchio Magico (${val}%)",
|
||||||
|
"core.bonus.MAGIC_MIRROR.description": "Ha una probabilità del ${val}% di reindirizzare un incantesimo offensivo su un'unità nemica",
|
||||||
|
"core.bonus.MAGIC_RESISTANCE.name": "Resistenza Magica (${val}%)",
|
||||||
|
"core.bonus.MAGIC_RESISTANCE.description": "Ha una probabilità del ${val}% di resistere a un incantesimo nemico",
|
||||||
|
"core.bonus.MIND_IMMUNITY.name": "Immunità agli incantesimi mentali",
|
||||||
|
"core.bonus.MIND_IMMUNITY.description": "Immune agli incantesimi di tipo mentale",
|
||||||
|
"core.bonus.NO_DISTANCE_PENALTY.name": "Nessuna penalità a distanza",
|
||||||
|
"core.bonus.NO_DISTANCE_PENALTY.description": "Infligge il massimo danno a qualsiasi distanza",
|
||||||
|
"core.bonus.NO_MELEE_PENALTY.name": "Nessuna penalità in mischia",
|
||||||
|
"core.bonus.NO_MELEE_PENALTY.description": "L'unità non subisce penalità in mischia",
|
||||||
|
"core.bonus.NO_MORALE.name": "Morale neutrale",
|
||||||
|
"core.bonus.NO_MORALE.description": "L'unità è immune agli effetti del morale",
|
||||||
|
"core.bonus.NO_WALL_PENALTY.name": "Nessuna penalità per le mura",
|
||||||
|
"core.bonus.NO_WALL_PENALTY.description": "Danno pieno durante l'assedio",
|
||||||
|
"core.bonus.NON_LIVING.name": "Non vivente",
|
||||||
|
"core.bonus.NON_LIVING.description": "Immunità a molti effetti",
|
||||||
|
"core.bonus.RANDOM_SPELLCASTER.name": "Random spellcaster",
|
||||||
|
"core.bonus.RANDOM_SPELLCASTER.description": "Può lanciare un incantesimo casuale",
|
||||||
|
"core.bonus.RANGED_RETALIATION.name": "Ritorsione a distanza",
|
||||||
|
"core.bonus.RANGED_RETALIATION.description": "Può effettuare un contrattacco a distanza",
|
||||||
|
"core.bonus.RECEPTIVE.name": "Ricettivo",
|
||||||
|
"core.bonus.RECEPTIVE.description": "Nessuna immunità agli incantesimi amichevoli",
|
||||||
|
"core.bonus.REBIRTH.name": "Rinascita (${val}%)",
|
||||||
|
"core.bonus.REBIRTH.description": "${val}% della pila risorgerà dopo la morte",
|
||||||
|
"core.bonus.RETURN_AFTER_STRIKE.name": "Attacco e Ritorno",
|
||||||
|
"core.bonus.RETURN_AFTER_STRIKE.description": "Ritorna dopo un attacco in mischia",
|
||||||
|
"core.bonus.REVENGE.name": "Vendetta",
|
||||||
|
"core.bonus.REVENGE.description": "Infligge danni extra in base alla salute persa dell'attaccante in battaglia",
|
||||||
|
"core.bonus.SHOOTER.name": "A distanza",
|
||||||
|
"core.bonus.SHOOTER.description": "L'unità può attaccare a distanza",
|
||||||
|
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Tiro a raggio totale",
|
||||||
|
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Gli attacchi a distanza di questa unità colpiscono tutti i bersagli in una piccola area",
|
||||||
|
"core.bonus.SOUL_STEAL.name": "Furto d'anima",
|
||||||
|
"core.bonus.SOUL_STEAL.description": "Ottiene ${val} nuove creature per ogni nemico ucciso",
|
||||||
|
"core.bonus.SPELLCASTER.name": "Incantatore",
|
||||||
|
"core.bonus.SPELLCASTER.description": "Può lanciare ${subtype.spell}",
|
||||||
|
"core.bonus.SPELL_AFTER_ATTACK.name": "Lancia Dopo l'Attacco",
|
||||||
|
"core.bonus.SPELL_AFTER_ATTACK.description": "Ha una probabilità del ${val}% di lanciare ${subtype.spell} dopo l'attacco",
|
||||||
|
"core.bonus.SPELL_BEFORE_ATTACK.name": "Lancia Prima dell'Attacco",
|
||||||
|
"core.bonus.SPELL_BEFORE_ATTACK.description": "Ha una probabilità del ${val}% di lanciare ${subtype.spell} prima dell'attacco",
|
||||||
|
"core.bonus.SPELL_IMMUNITY.name": "Immunità agli incantesimi",
|
||||||
|
"core.bonus.SPELL_IMMUNITY.description": "Immune a ${subtype.spell}",
|
||||||
|
"core.bonus.SPELL_LIKE_ATTACK.name": "Attacco simile a un incantesimo",
|
||||||
|
"core.bonus.SPELL_LIKE_ATTACK.description": "Attacca con ${subtype.spell}",
|
||||||
|
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura di Resistenza",
|
||||||
|
"core.bonus.SPELL_RESISTANCE_AURA.description": "Gli stack vicini ottengono ${val}% di resistenza magica",
|
||||||
|
"core.bonus.SUMMON_GUARDIANS.name": "Evoca guardiani",
|
||||||
|
"core.bonus.SUMMON_GUARDIANS.description": "All'inizio della battaglia evoca ${subtype.creature} (${val}%)",
|
||||||
|
"core.bonus.SYNERGY_TARGET.name": "Sinergizzabile",
|
||||||
|
"core.bonus.SYNERGY_TARGET.description": "Questa creatura è vulnerabile all'effetto sinergico",
|
||||||
|
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Soffio",
|
||||||
|
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Attacco a soffio (raggio di 2 esagoni)",
|
||||||
|
"core.bonus.THREE_HEADED_ATTACK.name": "Attacco a tre teste",
|
||||||
|
"core.bonus.THREE_HEADED_ATTACK.description": "Attacca tre unità adiacenti",
|
||||||
|
"core.bonus.TRANSMUTATION.name": "Trasmutazione",
|
||||||
|
"core.bonus.TRANSMUTATION.description": "${val}% di possibilità di trasformare l'unità attaccata in un altro tipo",
|
||||||
|
"core.bonus.UNDEAD.name": "Non Morto",
|
||||||
|
"core.bonus.UNDEAD.description": "L'unità è Non Morta",
|
||||||
|
"core.bonus.UNLIMITED_RETALIATIONS.name": "Ritorsioni illimitate",
|
||||||
|
"core.bonus.UNLIMITED_RETALIATIONS.description": "Può contrattaccare un numero illimitato di attacchi",
|
||||||
|
"core.bonus.WATER_IMMUNITY.name": "Immunità all'acqua",
|
||||||
|
"core.bonus.WATER_IMMUNITY.description": "Immune a tutti gli incantesimi della scuola di magia dell'Acqua",
|
||||||
|
"core.bonus.WIDE_BREATH.name": "Soffio ampio",
|
||||||
|
"core.bonus.WIDE_BREATH.description": "Attacco a soffio ampio (più esagoni)",
|
||||||
|
"core.bonus.DISINTEGRATE.name": "Disintegrazione",
|
||||||
|
"core.bonus.DISINTEGRATE.description": "Nessun cadavere rimane dopo la morte",
|
||||||
|
"core.bonus.INVINCIBLE.name": "Invincibile",
|
||||||
|
"core.bonus.INVINCIBLE.description": "Non può essere influenzato da nulla",
|
||||||
|
"core.bonus.MECHANICAL.name": "Meccanico",
|
||||||
|
"core.bonus.MECHANICAL.description": "Immunità a molti effetti, riparabile",
|
||||||
|
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "Soffio Prisma",
|
||||||
|
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "Attacco Soffio Prisma (tre direzioni)",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Resistenza agli incantesimi",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.air": "Resistenza agli incantesimi dell'Aria",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.fire": "Resistenza agli incantesimi di fuoco",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.water": "Resistenza agli incantesimi dell'Acqua",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.name.earth": "Resistenza agli incantesimi della Terra",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Danno da tutti gli incantesimi ridotto del ${val}%.",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.air": "Danno da tutti gli incantesimi dell'Aria ridotto del ${val}%.",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.fire": "Danno da tutti gli incantesimi del Fuoco ridotto del ${val}%.",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.water": "Danno da tutti gli incantesimi dell'Acqua ridotto del ${val}%.",
|
||||||
|
"core.bonus.SPELL_DAMAGE_REDUCTION.description.earth": "Danno da tutti gli incantesimi della Terra ridotto del ${val}%.",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name": "Immunità agli incantesimi",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.air": "Immunità all'aria",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.fire": "Immunità al fuoco",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.water": "Immunità all'acqua",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.name.earth": "Immunità alla terra",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description": "Questa unità è immune a tutti gli incantesimi",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.air": "Questa unità è immune a tutti gli incantesimi della scuola dell'Aria",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.fire": "Questa unità è immune a tutti gli incantesimi della scuola del Fuoco",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water": "Questa unità è immune a tutti gli incantesimi della scuola dell'Acqua",
|
||||||
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth": "Questa unità è immune a tutti gli incantesimi della scuola della Terra",
|
||||||
|
"core.bonus.OPENING_BATTLE_SPELL.name": "Inizia con incantesimo",
|
||||||
|
"core.bonus.OPENING_BATTLE_SPELL.description": "Lancia ${subtype.spell} all'inizio della battaglia",
|
||||||
|
|
||||||
|
"spell.core.castleMoat.name" : "Fossato",
|
||||||
|
"spell.core.castleMoatTrigger.name" : "Fossato",
|
||||||
|
"spell.core.catapultShot.name" : "Colpo di Catapulta",
|
||||||
|
"spell.core.cyclopsShot.name" : "Colpo d'assedio",
|
||||||
|
"spell.core.dungeonMoat.name" : "Olio Bollente",
|
||||||
|
"spell.core.dungeonMoatTrigger.name" : "Olio Bollente",
|
||||||
|
"spell.core.fireWallTrigger.name" : "Muro di Fuoco",
|
||||||
|
"spell.core.firstAid.name" : "Pronto Soccorso",
|
||||||
|
"spell.core.fortressMoat.name" : "Catrame Bollente",
|
||||||
|
"spell.core.fortressMoatTrigger.name" : "Catrame Bollente",
|
||||||
|
"spell.core.infernoMoat.name" : "Lava",
|
||||||
|
"spell.core.infernoMoatTrigger.name" : "Lava",
|
||||||
|
"spell.core.landMineTrigger.name" : "Mina Terrestre",
|
||||||
|
"spell.core.necropolisMoat.name" : "Cimitero d'ossa",
|
||||||
|
"spell.core.necropolisMoatTrigger.name" : "Cimitero di ossa",
|
||||||
|
"spell.core.rampartMoat.name" : "Cimitero di ossa",
|
||||||
|
"spell.core.rampartMoatTrigger.name" : "Rovi",
|
||||||
|
"spell.core.strongholdMoat.name" : "Rovi",
|
||||||
|
"spell.core.strongholdMoatTrigger.name" : "Spuntoni di legno",
|
||||||
|
"spell.core.summonDemons.name" : "Spuntoni di legno",
|
||||||
|
"spell.core.towerMoat.name" : "Mina terrestre"
|
||||||
|
}
|
@ -23,8 +23,8 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Brak dostępnego miasta z karczmą!",
|
"vcmi.adventureMap.noTownWithTavern" : "Brak dostępnego miasta z karczmą!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Nieznany problem z zaklęciem, brak dodatkowych informacji.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Nieznany problem z zaklęciem, brak dodatkowych informacji.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Gracz został zaatakowany: %s",
|
"vcmi.adventureMap.playerAttacked" : "Gracz został zaatakowany: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Punkty ruchu - Koszt: %TURNS tury + %POINTS punktów, Pozostanie: %REMAINING punktów",
|
"vcmi.adventureMap.moveCostDetails" : "Ruch tutaj będzie kosztował łącznie {%TOTAL} punktów ({%TURNS} tur i {%POINTS} punktów). {%REMAINING} punktów zostanie po ruchu.",
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Punkty ruchu - Koszt: %POINTS punktów, Pozostanie: %REMAINING punktów",
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Ruch tutaj będzie kosztował {%POINTS} punktów. {%REMAINING} punktów zostanie po ruchu.",
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Punkty ruchu: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Punkty ruchu: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Wybacz, powtórka ruchu wroga nie została jeszcze zaimplementowana!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Wybacz, powtórka ruchu wroga nie została jeszcze zaimplementowana!",
|
||||||
|
|
||||||
@ -106,6 +106,7 @@
|
|||||||
"vcmi.radialWheel.heroGetArtifacts" : "Weź artefakty z innego bohatera",
|
"vcmi.radialWheel.heroGetArtifacts" : "Weź artefakty z innego bohatera",
|
||||||
"vcmi.radialWheel.heroSwapArtifacts" : "Zamień artefakty z innym bohaterem",
|
"vcmi.radialWheel.heroSwapArtifacts" : "Zamień artefakty z innym bohaterem",
|
||||||
"vcmi.radialWheel.heroDismiss" : "Dymisja bohatera",
|
"vcmi.radialWheel.heroDismiss" : "Dymisja bohatera",
|
||||||
|
"vcmi.radialWheel.upgradeCreatures" : "Ulepsz wszystkie stworzenia",
|
||||||
|
|
||||||
"vcmi.radialWheel.moveTop" : "Przenieś na początek",
|
"vcmi.radialWheel.moveTop" : "Przenieś na początek",
|
||||||
"vcmi.radialWheel.moveUp" : "Przenieś w górę",
|
"vcmi.radialWheel.moveUp" : "Przenieś w górę",
|
||||||
@ -212,12 +213,12 @@
|
|||||||
"vcmi.lobby.pvp.randomTownVs.hover" : "Wylosuj 2 miasta",
|
"vcmi.lobby.pvp.randomTownVs.hover" : "Wylosuj 2 miasta",
|
||||||
"vcmi.lobby.pvp.randomTownVs.help" : "Wyświetli nazwę 2 wylosowanych miast na czacie, które nie zostały zablokowane na liście",
|
"vcmi.lobby.pvp.randomTownVs.help" : "Wyświetli nazwę 2 wylosowanych miast na czacie, które nie zostały zablokowane na liście",
|
||||||
"vcmi.lobby.pvp.versus" : "vs.",
|
"vcmi.lobby.pvp.versus" : "vs.",
|
||||||
"vcmi.lobby.deleteFile" : "Czy chcesz usunąć ten plik ?",
|
"vcmi.lobby.deleteFile" : "Czy chcesz usunąć ten plik?",
|
||||||
"vcmi.lobby.deleteFolder" : "Czy chcesz usunąć ten folder ?",
|
"vcmi.lobby.deleteFolder" : "Czy chcesz usunąć ten folder?",
|
||||||
"vcmi.lobby.deleteMapTitle" : "Wskaż tytuł, który chcesz usunąć",
|
"vcmi.lobby.deleteMapTitle" : "Wskaż tytuł, który chcesz usunąć",
|
||||||
"vcmi.lobby.deleteMode" : "Przełącza tryb na usuwanie i spowrotem",
|
"vcmi.lobby.deleteMode" : "Przełącza tryb na usuwanie i spowrotem",
|
||||||
"vcmi.lobby.deleteSaveGameTitle" : "Wskaż zapis gry do usunięcia",
|
"vcmi.lobby.deleteSaveGameTitle" : "Wskaż zapis gry do usunięcia",
|
||||||
"vcmi.lobby.deleteUnsupportedSave" : "{Znaleziono niekompatybilne zapisy gry}\n\nVCMI wykrył %d zapisów gry, które nie są już wspierane. Prawdopodobnie ze względu na różne wersje gry.\n\nCzy chcesz je usunąć ?",
|
"vcmi.lobby.deleteUnsupportedSave" : "{Znaleziono niekompatybilne zapisy gry}\n\nVCMI wykrył %d zapisów gry, które nie są już wspierane. Prawdopodobnie ze względu na różne wersje gry.\n\nCzy chcesz je usunąć?",
|
||||||
|
|
||||||
"vcmi.client.errors.invalidMap" : "{Błędna mapa lub kampania}\n\nNie udało się stworzyć gry! Wybrana mapa lub kampania jest niepoprawna lub uszkodzona. Powód:\n%s",
|
"vcmi.client.errors.invalidMap" : "{Błędna mapa lub kampania}\n\nNie udało się stworzyć gry! Wybrana mapa lub kampania jest niepoprawna lub uszkodzona. Powód:\n%s",
|
||||||
"vcmi.client.errors.missingCampaigns" : "{Brakujące pliki gry}\n\nPliki kampanii nie zostały znalezione! Możliwe że używasz niekompletnych lub uszkodzonych plików Heroes 3. Spróbuj ponownej instalacji plików gry.",
|
"vcmi.client.errors.missingCampaigns" : "{Brakujące pliki gry}\n\nPliki kampanii nie zostały znalezione! Możliwe że używasz niekompletnych lub uszkodzonych plików Heroes 3. Spróbuj ponownej instalacji plików gry.",
|
||||||
@ -422,11 +423,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Otwórz okno sakwy",
|
"vcmi.heroWindow.openBackpack.hover" : "Otwórz okno sakwy",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Otwiera okno pozwalające łatwiej zarządzać artefaktami w sakwie",
|
"vcmi.heroWindow.openBackpack.help" : "Otwiera okno pozwalające łatwiej zarządzać artefaktami w sakwie",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sortuj wg. wartości",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sortuj wg. wartości",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Sortuj artefakty w sakwie według wartości",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Sortuj wg. wartości}\n\nSortuj artefakty w sakwie według wartości",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sortuj wg. miejsc",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sortuj wg. miejsc",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sortuj artefakty w sakwie według umiejscowienia na ciele",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Sortuj wg. miejsc}\n\nSortuj artefakty w sakwie według umiejscowienia na ciele",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sortuj wg. jakości",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sortuj wg. jakości",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Sortuj artefakty w sakwie według jakości: Skarb, Pomniejszy, Potężny, Relikt",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Sortuj wg. jakości}\n\nSortuj artefakty w sakwie według jakości: Skarb, Pomniejszy, Potężny, Relikt",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Posiadasz wszystkie niezbędne komponenty do stworzenia %s. Czy chcesz wykonać fuzję? {Wszystkie komponenty zostaną użyte}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Posiadasz wszystkie niezbędne komponenty do stworzenia %s. Czy chcesz wykonać fuzję? {Wszystkie komponenty zostaną użyte}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Zaproś bohatera",
|
"vcmi.tavernWindow.inviteHero" : "Zaproś bohatera",
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Não há cidades disponíveis com tavernas!",
|
"vcmi.adventureMap.noTownWithTavern" : "Não há cidades disponíveis com tavernas!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Há um problema desconhecido com este feitiço! Não há mais informações disponíveis.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Há um problema desconhecido com este feitiço! Não há mais informações disponíveis.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "O jogador foi atacado: %s",
|
"vcmi.adventureMap.playerAttacked" : "O jogador foi atacado: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Pontos de movimento - Custo: %TURNS turnos + %POINTS pontos, Pontos restantes: %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Pontos de movimento - Custo: %POINTS pontos, Pontos restantes: %REMAINING",
|
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Pontos de movimento: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Pontos de movimento: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Desculpe, a repetição do turno do oponente ainda não está implementada!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Desculpe, a repetição do turno do oponente ainda não está implementada!",
|
||||||
|
|
||||||
@ -424,11 +422,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Abrir janela da mochila de artefatos",
|
"vcmi.heroWindow.openBackpack.hover" : "Abrir janela da mochila de artefatos",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Abre a janela que facilita o gerenciamento da mochila de artefatos.",
|
"vcmi.heroWindow.openBackpack.help" : "Abre a janela que facilita o gerenciamento da mochila de artefatos.",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Ordenar por custo",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Ordenar por custo",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Ordena artefatos na mochila por custo.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Ordenar por custo}\n\nOrdena artefatos na mochila por custo.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Ordenar por espaço",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Ordenar por espaço",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Ordena artefatos na mochila por espaço equipado.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Ordenar por espaço}\n\nOrdena artefatos na mochila por espaço equipado.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Ordenar por classe",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Ordenar por classe",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Ordena artefatos na mochila por classe de artefato. Tesouro, Menor, Maior, Relíquia.",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Ordenar por classe}\n\nOrdena artefatos na mochila por classe de artefato. Tesouro, Menor, Maior, Relíquia.",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Você possui todos os componentes necessários para a fusão de %s. Deseja realizar a fusão? {Todos os componentes serão consumidos após a fusão.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Você possui todos os componentes necessários para a fusão de %s. Deseja realizar a fusão? {Todos os componentes serão consumidos após a fusão.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Convidar herói",
|
"vcmi.tavernWindow.inviteHero" : "Convidar herói",
|
||||||
|
@ -159,7 +159,7 @@
|
|||||||
},
|
},
|
||||||
"17":
|
"17":
|
||||||
{
|
{
|
||||||
"type" : "junction", "size" : 30,
|
"type" : "junction", "size" : 15,
|
||||||
"terrainTypeLikeZone" : 9,
|
"terrainTypeLikeZone" : 9,
|
||||||
"allowedTowns" : ["neutral"],
|
"allowedTowns" : ["neutral"],
|
||||||
"monsters" : "strong",
|
"monsters" : "strong",
|
||||||
@ -172,7 +172,7 @@
|
|||||||
},
|
},
|
||||||
"18":
|
"18":
|
||||||
{
|
{
|
||||||
"type" : "junction", "size" : 30,
|
"type" : "junction", "size" : 15,
|
||||||
"terrainTypeLikeZone" : 9,
|
"terrainTypeLikeZone" : 9,
|
||||||
"allowedTowns" : ["neutral"],
|
"allowedTowns" : ["neutral"],
|
||||||
"monsters" : "strong",
|
"monsters" : "strong",
|
||||||
@ -181,7 +181,7 @@
|
|||||||
},
|
},
|
||||||
"19":
|
"19":
|
||||||
{
|
{
|
||||||
"type" : "junction", "size" : 30,
|
"type" : "junction", "size" : 15,
|
||||||
"terrainTypeLikeZone" : 9,
|
"terrainTypeLikeZone" : 9,
|
||||||
"allowedTowns" : ["neutral"],
|
"allowedTowns" : ["neutral"],
|
||||||
"monsters" : "strong",
|
"monsters" : "strong",
|
||||||
@ -190,7 +190,7 @@
|
|||||||
},
|
},
|
||||||
"20":
|
"20":
|
||||||
{
|
{
|
||||||
"type" : "junction", "size" : 30,
|
"type" : "junction", "size" : 15,
|
||||||
"terrainTypeLikeZone" : 9,
|
"terrainTypeLikeZone" : 9,
|
||||||
"allowedTowns" : ["neutral"],
|
"allowedTowns" : ["neutral"],
|
||||||
"monsters" : "strong",
|
"monsters" : "strong",
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Нет союзных городов с тавернами!",
|
"vcmi.adventureMap.noTownWithTavern" : "Нет союзных городов с тавернами!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Неизвестная проблема с заклинанием, дополнительная информация недоступна.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Неизвестная проблема с заклинанием, дополнительная информация недоступна.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Игрок атакован: %s",
|
"vcmi.adventureMap.playerAttacked" : "Игрок атакован: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Очки движения - Стоимость: %TURNS ходов + %POINTS очков, Останется: %REMAINING очков",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Очки движения - Стоимость: %POINTS очков, Останется: %REMAINING очков",
|
|
||||||
|
|
||||||
"vcmi.capitalColors.0" : "Красный",
|
"vcmi.capitalColors.0" : "Красный",
|
||||||
"vcmi.capitalColors.1" : "Синий",
|
"vcmi.capitalColors.1" : "Синий",
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "¡No hay pueblo disponible con taberna!",
|
"vcmi.adventureMap.noTownWithTavern" : "¡No hay pueblo disponible con taberna!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Problema desconocido con este hechizo, no hay más información disponible.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Problema desconocido con este hechizo, no hay más información disponible.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "El jugador ha sido atacado: %s",
|
"vcmi.adventureMap.playerAttacked" : "El jugador ha sido atacado: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Puntos de movimiento - Coste: %TURNS turnos + %POINTS puntos, Puntos restantes: %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Puntos de movimiento - Coste: %POINTS puntos, Puntos restantes: %REMAINING",
|
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Disculpe, la repetición del turno del oponente aún no está implementada.",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Disculpe, la repetición del turno del oponente aún no está implementada.",
|
||||||
|
|
||||||
"vcmi.capitalColors.0" : "Rojo",
|
"vcmi.capitalColors.0" : "Rojo",
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern" : "Немає доступного міста з таверною!",
|
"vcmi.adventureMap.noTownWithTavern" : "Немає доступного міста з таверною!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem" : "Невідома проблема з цим заклинанням, більше інформації немає.",
|
"vcmi.adventureMap.spellUnknownProblem" : "Невідома проблема з цим заклинанням, більше інформації немає.",
|
||||||
"vcmi.adventureMap.playerAttacked" : "Гравця атаковано: %s",
|
"vcmi.adventureMap.playerAttacked" : "Гравця атаковано: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails" : "Очки руху - Вартість: %TURNS ходів + %POINTS очок. Залишок очок: %REMAINING",
|
"vcmi.adventureMap.moveCostDetails" : "Переміщення сюди коштуватиме {%TOTAL} очок загалом ({%TURNS} ходів і {%POINTS} очок). Після переміщення залишиться {%REMAINING} очок.",
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Очки руху - Вартість: %POINTS очок, Залишок очок: %REMAINING",
|
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Переміщення сюди коштуватиме {%TOTAL} очок. Після переміщення залишиться {%REMAINING} очок.",
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Очки руху: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo" : "(Очки руху: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Вибачте, функція повтору ходу суперника ще не реалізована!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Вибачте, функція повтору ходу суперника ще не реалізована!",
|
||||||
|
|
||||||
@ -68,6 +68,7 @@
|
|||||||
"vcmi.radialWheel.heroGetArtifacts" : "Отримати артефакти іншого героя",
|
"vcmi.radialWheel.heroGetArtifacts" : "Отримати артефакти іншого героя",
|
||||||
"vcmi.radialWheel.heroSwapArtifacts" : "Обміняти артефакти героїв",
|
"vcmi.radialWheel.heroSwapArtifacts" : "Обміняти артефакти героїв",
|
||||||
"vcmi.radialWheel.heroDismiss" : "Звільнити цього героя",
|
"vcmi.radialWheel.heroDismiss" : "Звільнити цього героя",
|
||||||
|
"vcmi.radialWheel.upgradeCreatures" : "Покращити усіх істот",
|
||||||
|
|
||||||
"vcmi.radialWheel.moveTop" : "Перемістити на початок",
|
"vcmi.radialWheel.moveTop" : "Перемістити на початок",
|
||||||
"vcmi.radialWheel.moveUp" : "Перемістити вгору",
|
"vcmi.radialWheel.moveUp" : "Перемістити вгору",
|
||||||
@ -363,6 +364,8 @@
|
|||||||
"vcmi.battleOptions.endWithAutocombat.help": "{Завершує бій}\n\nАвто-бій миттєво завершує бій",
|
"vcmi.battleOptions.endWithAutocombat.help": "{Завершує бій}\n\nАвто-бій миттєво завершує бій",
|
||||||
"vcmi.battleOptions.showQuickSpell.hover": "Панель швидкого чарування",
|
"vcmi.battleOptions.showQuickSpell.hover": "Панель швидкого чарування",
|
||||||
"vcmi.battleOptions.showQuickSpell.help": "{Панель швидкого чарування}\n\nПоказати панель для швидкого вибору заклять.",
|
"vcmi.battleOptions.showQuickSpell.help": "{Панель швидкого чарування}\n\nПоказати панель для швидкого вибору заклять.",
|
||||||
|
"vcmi.battleOptions.showHealthBar.hover": "Показувати шкалу здоров'я",
|
||||||
|
"vcmi.battleOptions.showHealthBar.help": "{Показувати шкалу здоров'я}\n\nПоказувати шкалу здоров'я, яка вказує на решту здоров'я до смерті однієї істоти.",
|
||||||
|
|
||||||
"vcmi.adventureMap.revisitObject.hover" : "Відвідати Об'єкт",
|
"vcmi.adventureMap.revisitObject.hover" : "Відвідати Об'єкт",
|
||||||
"vcmi.adventureMap.revisitObject.help" : "{Відвідати Об'єкт}\n\nЯкщо герой в даний момент стоїть на об'єкті мапи, він може знову відвідати цю локацію.",
|
"vcmi.adventureMap.revisitObject.help" : "{Відвідати Об'єкт}\n\nЯкщо герой в даний момент стоїть на об'єкті мапи, він може знову відвідати цю локацію.",
|
||||||
@ -415,6 +418,9 @@
|
|||||||
"vcmi.townStructure.bank.borrow" : "Ви заходите в банк. Вас бачить банкір і каже: 'Ми зробили для вас спеціальну пропозицію. Ви можете взяти у нас позику в розмірі 2500 золотих на 5 днів. Але щодня ви повинні будете повертати по 500 золотих'.",
|
"vcmi.townStructure.bank.borrow" : "Ви заходите в банк. Вас бачить банкір і каже: 'Ми зробили для вас спеціальну пропозицію. Ви можете взяти у нас позику в розмірі 2500 золотих на 5 днів. Але щодня ви повинні будете повертати по 500 золотих'.",
|
||||||
"vcmi.townStructure.bank.payBack" : "Ви заходите в банк. Банкір бачить вас і каже: 'Ви вже отримали позику. Погасіть її, перш ніж брати нову позику'.",
|
"vcmi.townStructure.bank.payBack" : "Ви заходите в банк. Банкір бачить вас і каже: 'Ви вже отримали позику. Погасіть її, перш ніж брати нову позику'.",
|
||||||
|
|
||||||
|
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Недостатньо коштів, щоб покращити всіх істот. Чи бажаєте ви покращити наступних істот?",
|
||||||
|
"vcmi.townWindow.upgradeAll.notUpgradable" : "Недостатньо коштів, щоб покращити будь-яку істоту.",
|
||||||
|
|
||||||
"vcmi.logicalExpressions.anyOf" : "Будь-що з перерахованого:",
|
"vcmi.logicalExpressions.anyOf" : "Будь-що з перерахованого:",
|
||||||
"vcmi.logicalExpressions.allOf" : "Все з перерахованого:",
|
"vcmi.logicalExpressions.allOf" : "Все з перерахованого:",
|
||||||
"vcmi.logicalExpressions.noneOf" : "Нічого з перерахованого:",
|
"vcmi.logicalExpressions.noneOf" : "Нічого з перерахованого:",
|
||||||
@ -423,12 +429,12 @@
|
|||||||
"vcmi.heroWindow.openCommander.help" : "Показує інформацію про командира героя",
|
"vcmi.heroWindow.openCommander.help" : "Показує інформацію про командира героя",
|
||||||
"vcmi.heroWindow.openBackpack.hover" : "Відкрити вікно рюкзака з артефактами",
|
"vcmi.heroWindow.openBackpack.hover" : "Відкрити вікно рюкзака з артефактами",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Відкриває вікно, що дозволяє легше керувати рюкзаком артефактів",
|
"vcmi.heroWindow.openBackpack.help" : "Відкриває вікно, що дозволяє легше керувати рюкзаком артефактів",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Сортувати за вартістю",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "За вартістю",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Сортувати артефакти в рюкзаку за вартістю.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Сортування за вартістю}\n\nСортувати артефакти в рюкзаку за вартістю.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Сортувати за типом",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "За слотом",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Сортувати артефакти в рюкзаку за слотом, в який цей артефакт може бути екіпірований",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Сортування за слотом}\n\nСортувати артефакти в рюкзаку за слотом, в який цей артефакт може бути екіпірований",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Сортування за рідкістю",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "За рідкістю",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Сортувати артефакти в рюкзаку за класом рідкісності артефакту. Скарб, Малий, Великий, Реліквія",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Сортування за рідкістю}\n\nСортувати артефакти в рюкзаку за класом рідкісності артефакту. Скарб, Малий, Великий, Реліквія",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Ви володієте всіма компонентами, необхідними для злиття %s. Ви бажаєте виконати злиття? {Всі компоненти буде спожито під час злиття.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Ви володієте всіма компонентами, необхідними для злиття %s. Ви бажаєте виконати злиття? {Всі компоненти буде спожито під час злиття.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Запросити героя",
|
"vcmi.tavernWindow.inviteHero" : "Запросити героя",
|
||||||
@ -464,7 +470,7 @@
|
|||||||
"vcmi.optionsTab.chessFieldBattle.help" : "Використовується у боях з ШІ чи у боях з гравцями якщо {таймер загону} вичерпується. Встановлюється на початку кожного бою.",
|
"vcmi.optionsTab.chessFieldBattle.help" : "Використовується у боях з ШІ чи у боях з гравцями якщо {таймер загону} вичерпується. Встановлюється на початку кожного бою.",
|
||||||
"vcmi.optionsTab.chessFieldUnitAccumulate.help" : "Використовується при обираннія дії загону у боях з гравцями. Встановлюється на початку дії. Залишок додається до {таймеру битви}",
|
"vcmi.optionsTab.chessFieldUnitAccumulate.help" : "Використовується при обираннія дії загону у боях з гравцями. Встановлюється на початку дії. Залишок додається до {таймеру битви}",
|
||||||
"vcmi.optionsTab.chessFieldUnitDiscard.help" : "Використовується при обираннія дії загону у боях з гравцями. Встановлюється на початку дії. Залишок часу буде втрачено.",
|
"vcmi.optionsTab.chessFieldUnitDiscard.help" : "Використовується при обираннія дії загону у боях з гравцями. Встановлюється на початку дії. Залишок часу буде втрачено.",
|
||||||
|
|
||||||
"vcmi.optionsTab.accumulate" : "Накопичувати",
|
"vcmi.optionsTab.accumulate" : "Накопичувати",
|
||||||
|
|
||||||
"vcmi.optionsTab.simturnsTitle" : "Одночасні ходи",
|
"vcmi.optionsTab.simturnsTitle" : "Одночасні ходи",
|
||||||
@ -740,7 +746,7 @@
|
|||||||
"core.bonus.TRANSMUTATION.name" : "Трансмутація",
|
"core.bonus.TRANSMUTATION.name" : "Трансмутація",
|
||||||
"core.bonus.TRANSMUTATION.description" : "${val}% шанс перетворити атакованого юніта в інший тип",
|
"core.bonus.TRANSMUTATION.description" : "${val}% шанс перетворити атакованого юніта в інший тип",
|
||||||
"core.bonus.UNDEAD.name" : "Нежить",
|
"core.bonus.UNDEAD.name" : "Нежить",
|
||||||
"core.bonus.UNDEAD.description" : "Істота є нежить",
|
"core.bonus.UNDEAD.description" : "Істота є нежиттю",
|
||||||
"core.bonus.UNLIMITED_RETALIATIONS.name" : "Необмежена кількість ударів у відповідь",
|
"core.bonus.UNLIMITED_RETALIATIONS.name" : "Необмежена кількість ударів у відповідь",
|
||||||
"core.bonus.UNLIMITED_RETALIATIONS.description" : "Відбиває будь-яку кількість атак",
|
"core.bonus.UNLIMITED_RETALIATIONS.description" : "Відбиває будь-яку кількість атак",
|
||||||
"core.bonus.WATER_IMMUNITY.name" : "Імунітет до води",
|
"core.bonus.WATER_IMMUNITY.name" : "Імунітет до води",
|
||||||
@ -782,5 +788,27 @@
|
|||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water": "На цей загін не діють жодні закляття школи Води",
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.water": "На цей загін не діють жодні закляття школи Води",
|
||||||
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth": "На цей загін не діють жодні закляття школи Землі",
|
"core.bonus.SPELL_SCHOOL_IMMUNITY.description.earth": "На цей загін не діють жодні закляття школи Землі",
|
||||||
"core.bonus.REVENGE.description" : "Завдає додаткової шкоди залежно від втраченого здоров'я в бою",
|
"core.bonus.REVENGE.description" : "Завдає додаткової шкоди залежно від втраченого здоров'я в бою",
|
||||||
"core.bonus.REVENGE.name" : "Помста"
|
"core.bonus.REVENGE.name" : "Помста",
|
||||||
|
|
||||||
|
"spell.core.castleMoat.name" : "Рів",
|
||||||
|
"spell.core.castleMoatTrigger.name" : "Рів",
|
||||||
|
"spell.core.catapultShot.name" : "Постріл з катапульти",
|
||||||
|
"spell.core.cyclopsShot.name" : "Постріл по стінам",
|
||||||
|
"spell.core.dungeonMoat.name" : "Кипляча нафта",
|
||||||
|
"spell.core.dungeonMoatTrigger.name" : "Кипляча нафта",
|
||||||
|
"spell.core.fireWallTrigger.name" : "Вогняна стіна",
|
||||||
|
"spell.core.firstAid.name" : "Перша допомога",
|
||||||
|
"spell.core.fortressMoat.name" : "Киплячий дьоготь",
|
||||||
|
"spell.core.fortressMoatTrigger.name" : "Киплячий дьоготь",
|
||||||
|
"spell.core.infernoMoat.name" : "Лава",
|
||||||
|
"spell.core.infernoMoatTrigger.name" : "Лава",
|
||||||
|
"spell.core.landMineTrigger.name" : "Наземна міна",
|
||||||
|
"spell.core.necropolisMoat.name" : "Могильник",
|
||||||
|
"spell.core.necropolisMoatTrigger.name" : "Могильник",
|
||||||
|
"spell.core.rampartMoat.name" : "Чагарник",
|
||||||
|
"spell.core.rampartMoatTrigger.name" : "Чагарник",
|
||||||
|
"spell.core.strongholdMoat.name" : "Дерев'яні піки",
|
||||||
|
"spell.core.strongholdMoatTrigger.name" : "Дерев'яні піки",
|
||||||
|
"spell.core.summonDemons.name" : "Виклик демонів",
|
||||||
|
"spell.core.towerMoat.name" : "Наземна міна"
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
"vcmi.adventureMap.noTownWithTavern": "Thành không có sẵn quán rượu!",
|
"vcmi.adventureMap.noTownWithTavern": "Thành không có sẵn quán rượu!",
|
||||||
"vcmi.adventureMap.spellUnknownProblem": "Phép này có lỗi! Không có thông tin nào khác.",
|
"vcmi.adventureMap.spellUnknownProblem": "Phép này có lỗi! Không có thông tin nào khác.",
|
||||||
"vcmi.adventureMap.playerAttacked": "Người chơi bị tấn công: %s",
|
"vcmi.adventureMap.playerAttacked": "Người chơi bị tấn công: %s",
|
||||||
"vcmi.adventureMap.moveCostDetails": "Điểm di chuyển - Cần: %TURNS lượt + %POINTS điểm, Còn lại: %REMAINING",
|
|
||||||
"vcmi.adventureMap.moveCostDetailsNoTurns": "Điểm di chuyển - Cần: %POINTS điểm, Còn lại: %REMAINING",
|
|
||||||
"vcmi.adventureMap.movementPointsHeroInfo": "(Điểm di chuyển: %REMAINING / %POINTS)",
|
"vcmi.adventureMap.movementPointsHeroInfo": "(Điểm di chuyển: %REMAINING / %POINTS)",
|
||||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented": "Xin lỗi, lượt chơi lại của đối thủ vẫn chưa được triển khai!",
|
"vcmi.adventureMap.replayOpponentTurnNotImplemented": "Xin lỗi, lượt chơi lại của đối thủ vẫn chưa được triển khai!",
|
||||||
|
|
||||||
@ -423,11 +421,11 @@
|
|||||||
"vcmi.heroWindow.openBackpack.hover" : "Mở cửa sổ ba lô báu vật",
|
"vcmi.heroWindow.openBackpack.hover" : "Mở cửa sổ ba lô báu vật",
|
||||||
"vcmi.heroWindow.openBackpack.help" : "Mở cửa sổ để quản lý ba lô báu vật dễ dàng hơn.",
|
"vcmi.heroWindow.openBackpack.help" : "Mở cửa sổ để quản lý ba lô báu vật dễ dàng hơn.",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sắp xếp theo giá",
|
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sắp xếp theo giá",
|
||||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Sắp xếp các báu vật trong ba lô theo giá.",
|
"vcmi.heroWindow.sortBackpackByCost.help" : "{Sắp xếp theo giá}\n\nSắp xếp các báu vật trong ba lô theo giá.",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sắp xếp theo vị trí",
|
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sắp xếp theo vị trí",
|
||||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sắp xếp báu vật trong ba lô theo ô được trang bị.",
|
"vcmi.heroWindow.sortBackpackBySlot.help" : "{Sắp xếp theo vị trí}\n\nSắp xếp báu vật trong ba lô theo ô được trang bị.",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sắp xếp theo loại",
|
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sắp xếp theo loại",
|
||||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Sắp xếp các báu vật trong ba lô theo loại: Chính, Phụ, Cổ đại và Quý hiếm.",
|
"vcmi.heroWindow.sortBackpackByClass.help" : "{Sắp xếp theo loại}\n\nSắp xếp các báu vật trong ba lô theo loại: Chính, Phụ, Cổ đại và Quý hiếm.",
|
||||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Bạn đã sở hữu tất cả các món đồ cần thiết để hợp nhất %s. Bạn có muốn hợp nhất không? {Tất cả các món đồ sẽ được sử dụng khi hợp nhất.}",
|
"vcmi.heroWindow.fusingArtifact.fusing" : "Bạn đã sở hữu tất cả các món đồ cần thiết để hợp nhất %s. Bạn có muốn hợp nhất không? {Tất cả các món đồ sẽ được sử dụng khi hợp nhất.}",
|
||||||
|
|
||||||
"vcmi.tavernWindow.inviteHero" : "Mới thêm tướng",
|
"vcmi.tavernWindow.inviteHero" : "Mới thêm tướng",
|
||||||
|
@ -52,6 +52,17 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"italian" : {
|
||||||
|
"name" : "VCMI - File di base",
|
||||||
|
"description" : "File di base necessari per il corretto funzionamento di VCMI",
|
||||||
|
"author" : "Team VCMI",
|
||||||
|
|
||||||
|
"skipValidation" : true,
|
||||||
|
"translations" : [
|
||||||
|
"config/italian.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
"polish" : {
|
"polish" : {
|
||||||
"name" : "Podstawowe pliki VCMI",
|
"name" : "Podstawowe pliki VCMI",
|
||||||
"description" : "Dodatkowe pliki wymagane do prawidłowego działania VCMI",
|
"description" : "Dodatkowe pliki wymagane do prawidłowego działania VCMI",
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="sensorLandscape" />
|
android:screenOrientation="fullSensor" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".ServerService"
|
android:name=".ServerService"
|
||||||
|
@ -107,6 +107,8 @@ public class VcmiSDLActivity extends SDLActivity
|
|||||||
mLayout = layout;
|
mLayout = layout;
|
||||||
|
|
||||||
setContentView(outerLayout);
|
setContentView(outerLayout);
|
||||||
|
|
||||||
|
VcmiSDLActivity.this.setWindowStyle(true); // set fullscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -493,5 +493,9 @@ if (ffmpeg_INCLUDE_DIRS)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(VCMI_PORTMASTER)
|
||||||
|
target_compile_definitions(vcmiclientcommon PRIVATE VCMI_PORTMASTER)
|
||||||
|
endif()
|
||||||
|
|
||||||
vcmi_set_output_dir(vcmiclientcommon "")
|
vcmi_set_output_dir(vcmiclientcommon "")
|
||||||
enable_pch(vcmiclientcommon)
|
enable_pch(vcmiclientcommon)
|
||||||
|
@ -491,6 +491,7 @@ void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, in
|
|||||||
cuw->updateSecondarySkills();
|
cuw->updateSecondarySkills();
|
||||||
|
|
||||||
localState->verifyPath(hero);
|
localState->verifyPath(hero);
|
||||||
|
adventureInt->onHeroChanged(hero);// secondary skill can change primary skill / mana limit
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
|
void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
|
||||||
@ -505,6 +506,7 @@ void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
|
|||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
if (makingTurn && hero->tempOwner == playerID)
|
if (makingTurn && hero->tempOwner == playerID)
|
||||||
adventureInt->onHeroChanged(hero);
|
adventureInt->onHeroChanged(hero);
|
||||||
|
invalidatePaths();
|
||||||
}
|
}
|
||||||
void CPlayerInterface::receivedResource()
|
void CPlayerInterface::receivedResource()
|
||||||
{
|
{
|
||||||
|
@ -186,9 +186,9 @@ void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
|
|||||||
si->difficulty = lastDifficulty.Integer();
|
si->difficulty = lastDifficulty.Integer();
|
||||||
|
|
||||||
logNetwork->trace("\tStarting local server");
|
logNetwork->trace("\tStarting local server");
|
||||||
uint16_t srvport = serverRunner->start(getLocalPort(), connectToLobby, si);
|
serverRunner->start(loadMode == ELoadMode::MULTI, connectToLobby, si);
|
||||||
logNetwork->trace("\tConnecting to local server");
|
logNetwork->trace("\tConnecting to local server");
|
||||||
connectToServer(getLocalHostname(), srvport);
|
connectToServer(getLocalHostname(), getLocalPort());
|
||||||
logNetwork->trace("\tWaiting for connection");
|
logNetwork->trace("\tWaiting for connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,9 +206,13 @@ void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
|
|||||||
|
|
||||||
Settings remotePort = settings.write["server"]["remotePort"];
|
Settings remotePort = settings.write["server"]["remotePort"];
|
||||||
remotePort->Integer() = port;
|
remotePort->Integer() = port;
|
||||||
}
|
|
||||||
|
|
||||||
networkHandler->connectToRemote(*this, addr, port);
|
networkHandler->connectToRemote(*this, addr, port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serverRunner->connect(*networkHandler, *this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::onConnectionFailed(const std::string & errorMessage)
|
void CServerHandler::onConnectionFailed(const std::string & errorMessage)
|
||||||
@ -245,7 +249,7 @@ void CServerHandler::onTimer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(isServerLocal());
|
assert(isServerLocal());
|
||||||
networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort());
|
serverRunner->connect(*networkHandler, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)
|
void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "../lib/VCMIDirs.h"
|
#include "../lib/VCMIDirs.h"
|
||||||
#include "../lib/CThreadHelper.h"
|
#include "../lib/CThreadHelper.h"
|
||||||
|
#include "../lib/network/NetworkInterface.h"
|
||||||
|
#include "../lib/CConfigHandler.h"
|
||||||
#include "../server/CVCMIServer.h"
|
#include "../server/CVCMIServer.h"
|
||||||
|
|
||||||
#ifdef ENABLE_SERVER_PROCESS
|
#ifdef ENABLE_SERVER_PROCESS
|
||||||
@ -33,10 +35,11 @@
|
|||||||
ServerThreadRunner::ServerThreadRunner() = default;
|
ServerThreadRunner::ServerThreadRunner() = default;
|
||||||
ServerThreadRunner::~ServerThreadRunner() = default;
|
ServerThreadRunner::~ServerThreadRunner() = default;
|
||||||
|
|
||||||
uint16_t ServerThreadRunner::start(uint16_t cfgport, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
|
void ServerThreadRunner::start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
|
||||||
{
|
{
|
||||||
// cfgport may be 0 -- the real port is returned after calling prepare()
|
// cfgport may be 0 -- the real port is returned after calling prepare()
|
||||||
server = std::make_unique<CVCMIServer>(cfgport, true);
|
uint16_t port = settings["server"]["localPort"].Integer();
|
||||||
|
server = std::make_unique<CVCMIServer>(port, true);
|
||||||
|
|
||||||
if (startingInfo)
|
if (startingInfo)
|
||||||
{
|
{
|
||||||
@ -45,18 +48,16 @@ uint16_t ServerThreadRunner::start(uint16_t cfgport, bool connectToLobby, std::s
|
|||||||
|
|
||||||
std::promise<uint16_t> promise;
|
std::promise<uint16_t> promise;
|
||||||
|
|
||||||
threadRunLocalServer = boost::thread([this, connectToLobby, &promise]{
|
threadRunLocalServer = boost::thread([this, connectToLobby, listenForConnections, &promise]{
|
||||||
setThreadName("runServer");
|
setThreadName("runServer");
|
||||||
uint16_t port = server->prepare(connectToLobby);
|
uint16_t port = server->prepare(connectToLobby, listenForConnections);
|
||||||
promise.set_value(port);
|
promise.set_value(port);
|
||||||
server->run();
|
server->run();
|
||||||
});
|
});
|
||||||
|
|
||||||
logNetwork->trace("Waiting for server port...");
|
logNetwork->trace("Waiting for server port...");
|
||||||
auto srvport = promise.get_future().get();
|
serverPort = promise.get_future().get();
|
||||||
logNetwork->debug("Server port: %d", srvport);
|
logNetwork->debug("Server port: %d", serverPort);
|
||||||
|
|
||||||
return srvport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerThreadRunner::shutdown()
|
void ServerThreadRunner::shutdown()
|
||||||
@ -74,6 +75,11 @@ int ServerThreadRunner::exitCode()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerThreadRunner::connect(INetworkHandler & network, INetworkClientListener & listener)
|
||||||
|
{
|
||||||
|
network.createInternalConnection(listener, server->getNetworkServer());
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SERVER_PROCESS
|
#ifdef ENABLE_SERVER_PROCESS
|
||||||
|
|
||||||
ServerProcessRunner::ServerProcessRunner() = default;
|
ServerProcessRunner::ServerProcessRunner() = default;
|
||||||
@ -94,8 +100,9 @@ int ServerProcessRunner::exitCode()
|
|||||||
return child->exit_code();
|
return child->exit_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ServerProcessRunner::start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
|
void ServerProcessRunner::start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
|
||||||
{
|
{
|
||||||
|
uint16_t port = settings["server"]["localPort"].Integer();
|
||||||
boost::filesystem::path serverPath = VCMIDirs::get().serverPath();
|
boost::filesystem::path serverPath = VCMIDirs::get().serverPath();
|
||||||
boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "server_log.txt";
|
boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "server_log.txt";
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
@ -109,8 +116,14 @@ uint16_t ServerProcessRunner::start(uint16_t port, bool connectToLobby, std::sha
|
|||||||
|
|
||||||
if (ec)
|
if (ec)
|
||||||
throw std::runtime_error("Failed to start server! Reason: " + ec.message());
|
throw std::runtime_error("Failed to start server! Reason: " + ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
return port;
|
void ServerProcessRunner::connect(INetworkHandler & network, INetworkClientListener & listener)
|
||||||
|
{
|
||||||
|
std::string host = settings["server"]["localHostname"].String();
|
||||||
|
uint16_t port = settings["server"]["localPort"].Integer();
|
||||||
|
|
||||||
|
network.connectToRemote(listener, host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
struct StartInfo;
|
struct StartInfo;
|
||||||
|
class INetworkHandler;
|
||||||
|
class INetworkClientListener;
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|
||||||
@ -20,25 +22,31 @@ class CVCMIServer;
|
|||||||
class IServerRunner
|
class IServerRunner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) = 0;
|
virtual void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) = 0;
|
||||||
virtual void shutdown() = 0;
|
virtual void shutdown() = 0;
|
||||||
virtual void wait() = 0;
|
virtual void wait() = 0;
|
||||||
virtual int exitCode() = 0;
|
virtual int exitCode() = 0;
|
||||||
|
|
||||||
|
virtual void connect(INetworkHandler & network, INetworkClientListener & listener) = 0;
|
||||||
|
|
||||||
virtual ~IServerRunner() = default;
|
virtual ~IServerRunner() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Class that runs server instance as a thread of client process
|
/// Class that runs server instance as a thread of client process
|
||||||
class ServerThreadRunner : public IServerRunner, boost::noncopyable
|
class ServerThreadRunner final : public IServerRunner, boost::noncopyable
|
||||||
{
|
{
|
||||||
std::unique_ptr<CVCMIServer> server;
|
std::unique_ptr<CVCMIServer> server;
|
||||||
boost::thread threadRunLocalServer;
|
boost::thread threadRunLocalServer;
|
||||||
|
uint16_t serverPort = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
|
void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
|
||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
void wait() override;
|
void wait() override;
|
||||||
int exitCode() override;
|
int exitCode() override;
|
||||||
|
|
||||||
|
void connect(INetworkHandler & network, INetworkClientListener & listener) override;
|
||||||
|
|
||||||
ServerThreadRunner();
|
ServerThreadRunner();
|
||||||
~ServerThreadRunner();
|
~ServerThreadRunner();
|
||||||
};
|
};
|
||||||
@ -64,16 +72,18 @@ class child;
|
|||||||
|
|
||||||
/// Class that runs server instance as a child process
|
/// Class that runs server instance as a child process
|
||||||
/// Available only on desktop systems where process management is allowed
|
/// Available only on desktop systems where process management is allowed
|
||||||
class ServerProcessRunner : public IServerRunner, boost::noncopyable
|
class ServerProcessRunner final : public IServerRunner, boost::noncopyable
|
||||||
{
|
{
|
||||||
std::unique_ptr<boost::process::child> child;
|
std::unique_ptr<boost::process::child> child;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
|
void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
|
||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
void wait() override;
|
void wait() override;
|
||||||
int exitCode() override;
|
int exitCode() override;
|
||||||
|
|
||||||
|
void connect(INetworkHandler & network, INetworkClientListener & listener) override;
|
||||||
|
|
||||||
ServerProcessRunner();
|
ServerProcessRunner();
|
||||||
~ServerProcessRunner();
|
~ServerProcessRunner();
|
||||||
};
|
};
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||||
#include "../../lib/mapping/CMapDefines.h"
|
#include "../../lib/mapping/CMapDefines.h"
|
||||||
#include "../../lib/pathfinder/CGPathNode.h"
|
#include "../../lib/pathfinder/CGPathNode.h"
|
||||||
|
#include "../../lib/pathfinder/TurnInfo.h"
|
||||||
#include "../../lib/spells/ISpellMechanics.h"
|
#include "../../lib/spells/ISpellMechanics.h"
|
||||||
#include "../../lib/spells/Problem.h"
|
#include "../../lib/spells/Problem.h"
|
||||||
|
|
||||||
@ -527,7 +528,6 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
|
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
|
||||||
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner) != PlayerRelations::ENEMIES;
|
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner) != PlayerRelations::ENEMIES;
|
||||||
|
|
||||||
bool isHero = false;
|
|
||||||
if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
|
if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
|
||||||
{
|
{
|
||||||
if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
|
if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
|
||||||
@ -537,9 +537,10 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
}
|
}
|
||||||
else if(const CGHeroInstance * currentHero = LOCPLINT->localState->getCurrentHero()) //hero is selected
|
else if(const CGHeroInstance * currentHero = LOCPLINT->localState->getCurrentHero()) //hero is selected
|
||||||
{
|
{
|
||||||
isHero = true;
|
|
||||||
|
|
||||||
const CGPathNode *pn = LOCPLINT->getPathsInfo(currentHero)->getPathInfo(targetPosition);
|
const CGPathNode *pn = LOCPLINT->getPathsInfo(currentHero)->getPathInfo(targetPosition);
|
||||||
|
|
||||||
|
const auto shipyard = dynamic_cast<const IShipyard *>(topBlocking);
|
||||||
|
|
||||||
if(currentHero == topBlocking) //clicked selected hero
|
if(currentHero == topBlocking) //clicked selected hero
|
||||||
{
|
{
|
||||||
LOCPLINT->openHeroWindow(currentHero);
|
LOCPLINT->openHeroWindow(currentHero);
|
||||||
@ -550,10 +551,19 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if(shipyard != nullptr && pn->turns == 255 && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner) != PlayerRelations::ENEMIES)
|
||||||
|
{
|
||||||
|
LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
|
||||||
|
}
|
||||||
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
|
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
|
||||||
{
|
{
|
||||||
|
int3 destinationTile = targetPosition;
|
||||||
|
|
||||||
|
if(topBlocking && topBlocking->isVisitable() && !topBlocking->visitableAt(destinationTile) && settings["gameTweaks"]["simpleObjectSelection"].Bool())
|
||||||
|
destinationTile = topBlocking->visitablePos();
|
||||||
|
|
||||||
if(LOCPLINT->localState->hasPath(currentHero) &&
|
if(LOCPLINT->localState->hasPath(currentHero) &&
|
||||||
LOCPLINT->localState->getPath(currentHero).endPos() == targetPosition &&
|
LOCPLINT->localState->getPath(currentHero).endPos() == destinationTile &&
|
||||||
!GH.isKeyboardShiftDown())//we'll be moving
|
!GH.isKeyboardShiftDown())//we'll be moving
|
||||||
{
|
{
|
||||||
assert(!CGI->mh->hasOngoingAnimations());
|
assert(!CGI->mh->hasOngoingAnimations());
|
||||||
@ -570,7 +580,7 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
}
|
}
|
||||||
else //remove old path and find a new one if we clicked on accessible tile
|
else //remove old path and find a new one if we clicked on accessible tile
|
||||||
{
|
{
|
||||||
LOCPLINT->localState->setPath(currentHero, targetPosition);
|
LOCPLINT->localState->setPath(currentHero, destinationTile);
|
||||||
onHeroChanged(currentHero);
|
onHeroChanged(currentHero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,12 +590,6 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Nothing is selected...");
|
throw std::runtime_error("Nothing is selected...");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto shipyard = ourInaccessibleShipyard(topBlocking);
|
|
||||||
if(isHero && shipyard != nullptr)
|
|
||||||
{
|
|
||||||
LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
||||||
@ -686,6 +690,28 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
|||||||
showMoveDetailsInStatusbar(*hero, *pathNode);
|
showMoveDetailsInStatusbar(*hero, *pathNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (objAtTile && pathNode->action == EPathNodeAction::UNKNOWN)
|
||||||
|
{
|
||||||
|
if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
|
||||||
|
{
|
||||||
|
CCS->curh->set(Cursor::Map::TOWN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
||||||
|
{
|
||||||
|
CCS->curh->set(Cursor::Map::HERO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (objAtTile->ID == Obj::SHIPYARD && objRelations != PlayerRelations::ENEMIES)
|
||||||
|
{
|
||||||
|
CCS->curh->set(Cursor::Map::T1_SAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(objAtTile->isVisitable() && !objAtTile->visitableAt(targetPosition) && settings["gameTweaks"]["simpleObjectSelection"].Bool())
|
||||||
|
pathNode = LOCPLINT->getPathsInfo(hero)->getPathInfo(objAtTile->visitablePos());
|
||||||
|
}
|
||||||
|
|
||||||
int turns = pathNode->turns;
|
int turns = pathNode->turns;
|
||||||
vstd::amin(turns, 3);
|
vstd::amin(turns, 3);
|
||||||
switch(pathNode->action)
|
switch(pathNode->action)
|
||||||
@ -737,38 +763,36 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(objAtTile && objRelations != PlayerRelations::ENEMIES)
|
|
||||||
{
|
|
||||||
if(objAtTile->ID == Obj::TOWN)
|
|
||||||
CCS->curh->set(Cursor::Map::TOWN);
|
|
||||||
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
|
||||||
CCS->curh->set(Cursor::Map::HERO);
|
|
||||||
else
|
|
||||||
CCS->curh->set(Cursor::Map::POINTER);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CCS->curh->set(Cursor::Map::POINTER);
|
CCS->curh->set(Cursor::Map::POINTER);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ourInaccessibleShipyard(objAtTile))
|
|
||||||
{
|
|
||||||
CCS->curh->set(Cursor::Map::T1_SAIL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
|
void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
|
||||||
{
|
{
|
||||||
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.movementPointsLimit(pathNode.layer == EPathfindingLayer::LAND) : hero.movementPointsRemaining();
|
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.movementPointsLimit(pathNode.layer == EPathfindingLayer::LAND) : hero.movementPointsRemaining();
|
||||||
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
|
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
|
||||||
const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
|
const int remainingPointsAfterMove = pathNode.moveRemains;
|
||||||
|
|
||||||
|
int totalMovementCost = 0;
|
||||||
|
for (int i = 0; i <= pathNode.turns; ++i)
|
||||||
|
{
|
||||||
|
auto turnInfo = hero.getTurnInfo(i);
|
||||||
|
if (pathNode.layer == EPathfindingLayer::SAIL)
|
||||||
|
totalMovementCost += turnInfo->getMovePointsLimitWater();
|
||||||
|
else
|
||||||
|
totalMovementCost += turnInfo->getMovePointsLimitLand();
|
||||||
|
}
|
||||||
|
|
||||||
|
totalMovementCost -= pathNode.moveRemains;
|
||||||
|
|
||||||
std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
|
std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
|
||||||
|
|
||||||
boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
|
boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
|
||||||
boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
|
boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
|
||||||
boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
|
boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
|
||||||
|
boost::replace_first(result, "%TOTAL", std::to_string(totalMovementCost));
|
||||||
|
|
||||||
GH.statusbar()->write(result);
|
GH.statusbar()->write(result);
|
||||||
}
|
}
|
||||||
@ -844,18 +868,6 @@ Rect AdventureMapInterface::terrainAreaPixels() const
|
|||||||
return widget->getMapView()->pos;
|
return widget->getMapView()->pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IShipyard * AdventureMapInterface::ourInaccessibleShipyard(const CGObjectInstance *obj) const
|
|
||||||
{
|
|
||||||
const auto *ret = dynamic_cast<const IShipyard *>(obj);
|
|
||||||
|
|
||||||
if(!ret ||
|
|
||||||
obj->tempOwner != currentPlayerID ||
|
|
||||||
(CCS->curh->get<Cursor::Map>() != Cursor::Map::T1_SAIL && CCS->curh->get<Cursor::Map>() != Cursor::Map::POINTER))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdventureMapInterface::hotkeyExitWorldView()
|
void AdventureMapInterface::hotkeyExitWorldView()
|
||||||
{
|
{
|
||||||
setState(EAdventureState::MAKING_TURN);
|
setState(EAdventureState::MAKING_TURN);
|
||||||
|
@ -75,9 +75,6 @@ private:
|
|||||||
/// updates active state of game window whenever game state changes
|
/// updates active state of game window whenever game state changes
|
||||||
void adjustActiveness();
|
void adjustActiveness();
|
||||||
|
|
||||||
/// checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
|
||||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
|
|
||||||
|
|
||||||
/// check and if necessary reacts on scrolling by moving cursor to screen edge
|
/// check and if necessary reacts on scrolling by moving cursor to screen edge
|
||||||
void handleMapScrollingUpdate(uint32_t msPassed);
|
void handleMapScrollingUpdate(uint32_t msPassed);
|
||||||
|
|
||||||
|
@ -594,16 +594,19 @@ void ColorTransformAnimation::tick(uint32_t msPassed)
|
|||||||
if (index == timePoints.size())
|
if (index == timePoints.size())
|
||||||
{
|
{
|
||||||
//end of animation. Apply ColorShifter using final values and die
|
//end of animation. Apply ColorShifter using final values and die
|
||||||
const auto & shifter = steps[index - 1];
|
const auto & lastColor = effectColors[index - 1];
|
||||||
owner.stacksController->setStackColorFilter(shifter, stack, spell, false);
|
const auto & lastAlpha = transparency[index - 1];
|
||||||
|
owner.stacksController->setStackColorFilter(lastColor, lastAlpha, stack, spell, false);
|
||||||
delete this;
|
delete this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(index != 0);
|
assert(index != 0);
|
||||||
|
|
||||||
const auto & prevShifter = steps[index - 1];
|
const auto & prevColor = effectColors[index - 1];
|
||||||
const auto & nextShifter = steps[index];
|
const auto & nextColor = effectColors[index];
|
||||||
|
const auto & prevAlpha = transparency[index - 1];
|
||||||
|
const auto & nextAlpha = transparency[index];
|
||||||
|
|
||||||
float prevPoint = timePoints[index-1];
|
float prevPoint = timePoints[index-1];
|
||||||
float nextPoint = timePoints[index];
|
float nextPoint = timePoints[index];
|
||||||
@ -611,9 +614,10 @@ void ColorTransformAnimation::tick(uint32_t msPassed)
|
|||||||
float stepDuration = (nextPoint - prevPoint);
|
float stepDuration = (nextPoint - prevPoint);
|
||||||
float factor = localProgress / stepDuration;
|
float factor = localProgress / stepDuration;
|
||||||
|
|
||||||
auto shifter = ColorFilter::genInterpolated(prevShifter, nextShifter, factor);
|
const auto & currColor = vstd::lerp(prevColor, nextColor, factor);
|
||||||
|
const auto & currAlpha = vstd::lerp(prevAlpha, nextAlpha, factor);
|
||||||
|
|
||||||
owner.stacksController->setStackColorFilter(shifter, stack, spell, true);
|
owner.stacksController->setStackColorFilter(currColor, currAlpha, stack, spell, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell):
|
ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell):
|
||||||
@ -622,10 +626,11 @@ ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const
|
|||||||
totalProgress(0.f)
|
totalProgress(0.f)
|
||||||
{
|
{
|
||||||
auto effect = owner.effectsController->getMuxerEffect(colorFilterName);
|
auto effect = owner.effectsController->getMuxerEffect(colorFilterName);
|
||||||
steps = effect.filters;
|
effectColors = effect.effectColors;
|
||||||
|
transparency = effect.transparency;
|
||||||
timePoints = effect.timePoints;
|
timePoints = effect.timePoints;
|
||||||
|
|
||||||
assert(!steps.empty() && steps.size() == timePoints.size());
|
assert(!effectColors.empty() && effectColors.size() == timePoints.size());
|
||||||
|
|
||||||
logAnim->debug("Created ColorTransformAnimation for %s", stack->getName());
|
logAnim->debug("Created ColorTransformAnimation for %s", stack->getName());
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "../../lib/battle/BattleHexArray.h"
|
#include "../../lib/battle/BattleHexArray.h"
|
||||||
#include "../../lib/filesystem/ResourcePath.h"
|
#include "../../lib/filesystem/ResourcePath.h"
|
||||||
|
#include "../../lib/Color.h"
|
||||||
#include "BattleConstants.h"
|
#include "BattleConstants.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
@ -113,8 +114,10 @@ public:
|
|||||||
|
|
||||||
class ColorTransformAnimation : public BattleStackAnimation
|
class ColorTransformAnimation : public BattleStackAnimation
|
||||||
{
|
{
|
||||||
std::vector<ColorFilter> steps;
|
std::vector<ColorRGBA> effectColors;
|
||||||
|
std::vector<float> transparency;
|
||||||
std::vector<float> timePoints;
|
std::vector<float> timePoints;
|
||||||
|
|
||||||
const CSpell * spell;
|
const CSpell * spell;
|
||||||
|
|
||||||
float totalProgress;
|
float totalProgress;
|
||||||
|
@ -143,7 +143,8 @@ void BattleEffectsController::loadColorMuxers()
|
|||||||
for (const JsonNode & entry : muxer.second.Vector() )
|
for (const JsonNode & entry : muxer.second.Vector() )
|
||||||
{
|
{
|
||||||
effect.timePoints.push_back(entry["time"].Float());
|
effect.timePoints.push_back(entry["time"].Float());
|
||||||
effect.filters.push_back(ColorFilter::genFromJson(entry));
|
effect.effectColors.push_back(ColorRGBA(255*entry["color"][0].Float(), 255*entry["color"][1].Float(), 255*entry["color"][2].Float(), 255*entry["color"][3].Float()));
|
||||||
|
effect.transparency.push_back(entry["alpha"].Float() * 255);
|
||||||
}
|
}
|
||||||
colorMuxerEffects[identifier] = effect;
|
colorMuxerEffects[identifier] = effect;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "../../lib/battle/BattleHex.h"
|
#include "../../lib/battle/BattleHex.h"
|
||||||
#include "../../lib/Point.h"
|
#include "../../lib/Point.h"
|
||||||
|
#include "../../lib/Color.h"
|
||||||
#include "../../lib/filesystem/ResourcePath.h"
|
#include "../../lib/filesystem/ResourcePath.h"
|
||||||
#include "BattleConstants.h"
|
#include "BattleConstants.h"
|
||||||
|
|
||||||
@ -21,13 +22,19 @@ struct BattleTriggerEffect;
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|
||||||
struct ColorMuxerEffect;
|
|
||||||
class CAnimation;
|
class CAnimation;
|
||||||
class Canvas;
|
class Canvas;
|
||||||
class BattleInterface;
|
class BattleInterface;
|
||||||
class BattleRenderer;
|
class BattleRenderer;
|
||||||
class EffectAnimation;
|
class EffectAnimation;
|
||||||
|
|
||||||
|
struct ColorMuxerEffect
|
||||||
|
{
|
||||||
|
std::vector<ColorRGBA> effectColors;
|
||||||
|
std::vector<float> transparency;
|
||||||
|
std::vector<float> timePoints;
|
||||||
|
};
|
||||||
|
|
||||||
/// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,...
|
/// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,...
|
||||||
struct BattleEffect
|
struct BattleEffect
|
||||||
{
|
{
|
||||||
|
@ -533,7 +533,7 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
|||||||
BattleHexArray hoveredMoveHexes = getHighlightedHexesForMovementTarget();
|
BattleHexArray hoveredMoveHexes = getHighlightedHexesForMovementTarget();
|
||||||
|
|
||||||
BattleHex hoveredHex = getHoveredHex();
|
BattleHex hoveredHex = getHoveredHex();
|
||||||
BattleHexArray hoveredMouseHex = hoveredHex.isValid() ? BattleHexArray({ hoveredHex }) : BattleHexArray();
|
BattleHexArray hoveredMouseHex = hoveredHex.isAvailable() ? BattleHexArray({ hoveredHex }) : BattleHexArray();
|
||||||
|
|
||||||
const CStack * hoveredStack = getHoveredStack();
|
const CStack * hoveredStack = getHoveredStack();
|
||||||
if(!hoveredStack && hoveredHex == BattleHex::INVALID)
|
if(!hoveredStack && hoveredHex == BattleHex::INVALID)
|
||||||
|
@ -636,7 +636,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
|
|||||||
auto attack = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getAttack(stack->isShooter())) + "(" + std::to_string(stack->getAttack(stack->isShooter())) + ")";
|
auto attack = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getAttack(stack->isShooter())) + "(" + std::to_string(stack->getAttack(stack->isShooter())) + ")";
|
||||||
auto defense = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getDefense(stack->isShooter())) + "(" + std::to_string(stack->getDefense(stack->isShooter())) + ")";
|
auto defense = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getDefense(stack->isShooter())) + "(" + std::to_string(stack->getDefense(stack->isShooter())) + ")";
|
||||||
auto damage = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getMinDamage(stack->isShooter())) + "-" + std::to_string(stack->getMaxDamage(stack->isShooter()));
|
auto damage = std::to_string(CGI->creatures()->getByIndex(stack->creatureIndex())->getMinDamage(stack->isShooter())) + "-" + std::to_string(stack->getMaxDamage(stack->isShooter()));
|
||||||
auto health = CGI->creatures()->getByIndex(stack->creatureIndex())->getMaxHealth();
|
auto health = stack->getMaxHealth();
|
||||||
auto morale = stack->moraleVal();
|
auto morale = stack->moraleVal();
|
||||||
auto luck = stack->luckVal();
|
auto luck = stack->luckVal();
|
||||||
|
|
||||||
@ -691,7 +691,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
|
|||||||
if (spellBonuses->empty())
|
if (spellBonuses->empty())
|
||||||
throw std::runtime_error("Failed to find effects for spell " + effect.toSpell()->getJsonKey());
|
throw std::runtime_error("Failed to find effects for spell " + effect.toSpell()->getJsonKey());
|
||||||
|
|
||||||
int duration = spellBonuses->front()->duration;
|
int duration = spellBonuses->front()->turnsRemain;
|
||||||
|
|
||||||
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
|
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
|
||||||
if(settings["general"]["enableUiEnhancements"].Bool())
|
if(settings["general"]["enableUiEnhancements"].Bool())
|
||||||
@ -890,7 +890,7 @@ BattleResultResources BattleResultWindow::getResources(const BattleResult & br)
|
|||||||
if (ourHero)
|
if (ourHero)
|
||||||
{
|
{
|
||||||
resources.resultText.appendTextID("core.genrltxt.305");
|
resources.resultText.appendTextID("core.genrltxt.305");
|
||||||
resources.resultText.replaceTextID(ourHero->getNameTranslated());
|
resources.resultText.replaceTextID(ourHero->getNameTextID());
|
||||||
resources.resultText.replaceNumber(br.exp[weAreAttacker ? BattleSide::ATTACKER : BattleSide::DEFENDER]);
|
resources.resultText.replaceNumber(br.exp[weAreAttacker ? BattleSide::ATTACKER : BattleSide::DEFENDER]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "../../lib/battle/BattleAction.h"
|
#include "../../lib/battle/BattleAction.h"
|
||||||
#include "../../lib/battle/BattleHex.h"
|
#include "../../lib/battle/BattleHex.h"
|
||||||
#include "../../lib/texts/TextOperations.h"
|
#include "../../lib/texts/TextOperations.h"
|
||||||
|
#include "../../lib/CConfigHandler.h"
|
||||||
#include "../../lib/CRandomGenerator.h"
|
#include "../../lib/CRandomGenerator.h"
|
||||||
#include "../../lib/CStack.h"
|
#include "../../lib/CStack.h"
|
||||||
|
|
||||||
@ -204,8 +205,7 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
|
|||||||
if (!instant)
|
if (!instant)
|
||||||
{
|
{
|
||||||
// immediately make stack transparent, giving correct shifter time to start
|
// immediately make stack transparent, giving correct shifter time to start
|
||||||
auto shifterFade = ColorFilter::genAlphaShifter(0);
|
setStackColorFilter(Colors::TRANSPARENCY, 0, stack, nullptr, true);
|
||||||
setStackColorFilter(shifterFade, stack, nullptr, true);
|
|
||||||
|
|
||||||
owner.addToAnimationStage(EAnimationEvents::HIT, [=]()
|
owner.addToAnimationStage(EAnimationEvents::HIT, [=]()
|
||||||
{
|
{
|
||||||
@ -318,20 +318,33 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack *
|
|||||||
|
|
||||||
Point textPosition = Point(amountBG->dimensions().x/2 + boxPosition.x, boxPosition.y + amountBG->dimensions().y/2);
|
Point textPosition = Point(amountBG->dimensions().x/2 + boxPosition.x, boxPosition.y + amountBG->dimensions().y/2);
|
||||||
|
|
||||||
|
if(settings["battle"]["showHealthBar"].Bool())
|
||||||
|
{
|
||||||
|
float health = stack->getMaxHealth();
|
||||||
|
float healthRemaining = std::max(stack->getAvailableHealth() - (stack->getCount() - 1) * health, .0f);
|
||||||
|
Rect r(boxPosition.x, boxPosition.y - 3, amountBG->width(), 4);
|
||||||
|
canvas.drawColor(r, Colors::RED);
|
||||||
|
canvas.drawColor(Rect(r.x, r.y, (r.w / health) * healthRemaining, r.h), Colors::GREEN);
|
||||||
|
canvas.drawBorder(r, Colors::YELLOW);
|
||||||
|
}
|
||||||
canvas.draw(amountBG, boxPosition);
|
canvas.draw(amountBG, boxPosition);
|
||||||
canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4));
|
canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
|
void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
|
||||||
{
|
{
|
||||||
ColorFilter fullFilter = ColorFilter::genEmptyShifter();
|
ColorRGBA effectColor = Colors::TRANSPARENCY;
|
||||||
|
uint8_t transparency = 255;
|
||||||
for(const auto & filter : stackFilterEffects)
|
for(const auto & filter : stackFilterEffects)
|
||||||
{
|
{
|
||||||
if (filter.target == stack)
|
if (filter.target == stack)
|
||||||
fullFilter = ColorFilter::genCombined(fullFilter, filter.effect);
|
{
|
||||||
|
effectColor = filter.effectColor;
|
||||||
|
transparency = static_cast<int>(filter.transparency) * transparency / 255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
|
stackAnimation[stack->unitId()]->nextFrame(canvas, effectColor, transparency, facingRight(stack)); // do actual blit
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStacksController::tick(uint32_t msPassed)
|
void BattleStacksController::tick(uint32_t msPassed)
|
||||||
@ -769,18 +782,19 @@ Point BattleStacksController::getStackPositionAtHex(const BattleHex & hexNum, co
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStacksController::setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell * source, bool persistent)
|
void BattleStacksController::setStackColorFilter(const ColorRGBA & effectColor, uint8_t transparency, const CStack * target, const CSpell * source, bool persistent)
|
||||||
{
|
{
|
||||||
for (auto & filter : stackFilterEffects)
|
for (auto & filter : stackFilterEffects)
|
||||||
{
|
{
|
||||||
if (filter.target == target && filter.source == source)
|
if (filter.target == target && filter.source == source)
|
||||||
{
|
{
|
||||||
filter.effect = effect;
|
filter.effectColor = effectColor;
|
||||||
|
filter.transparency = transparency;
|
||||||
filter.persistent = persistent;
|
filter.persistent = persistent;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stackFilterEffects.push_back({ effect, target, source, persistent });
|
stackFilterEffects.push_back({ target, source, effectColor, transparency, persistent });
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStacksController::removeExpiredColorFilters()
|
void BattleStacksController::removeExpiredColorFilters()
|
||||||
@ -791,7 +805,7 @@ void BattleStacksController::removeExpiredColorFilters()
|
|||||||
{
|
{
|
||||||
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(filter.source->id)), Selector::all))
|
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(filter.source->id)), Selector::all))
|
||||||
return true;
|
return true;
|
||||||
if (filter.effect == ColorFilter::genEmptyShifter())
|
if (filter.effectColor == Colors::TRANSPARENCY && filter.transparency == 255)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../render/ColorFilter.h"
|
#include "../../lib/Color.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -37,9 +37,10 @@ class IImage;
|
|||||||
|
|
||||||
struct BattleStackFilterEffect
|
struct BattleStackFilterEffect
|
||||||
{
|
{
|
||||||
ColorFilter effect;
|
|
||||||
const CStack * target;
|
const CStack * target;
|
||||||
const CSpell * source;
|
const CSpell * source;
|
||||||
|
ColorRGBA effectColor;
|
||||||
|
uint8_t transparency;
|
||||||
bool persistent;
|
bool persistent;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ public:
|
|||||||
/// Adds new color filter effect targeting stack
|
/// Adds new color filter effect targeting stack
|
||||||
/// Effect will last as long as stack is affected by specified spell (unless effect is persistent)
|
/// Effect will last as long as stack is affected by specified spell (unless effect is persistent)
|
||||||
/// If effect from same (target, source) already exists, it will be updated
|
/// If effect from same (target, source) already exists, it will be updated
|
||||||
void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent);
|
void setStackColorFilter(const ColorRGBA & effect, uint8_t transparency, const CStack * target, const CSpell *source, bool persistent);
|
||||||
void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims
|
void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims
|
||||||
|
|
||||||
const CStack* getActiveStack() const;
|
const CStack* getActiveStack() const;
|
||||||
|
@ -24,11 +24,6 @@ static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 };
|
|||||||
static const ColorRGBA creatureGoldBorder = { 255, 255, 0, 255 };
|
static const ColorRGBA creatureGoldBorder = { 255, 255, 0, 255 };
|
||||||
static const ColorRGBA creatureNoBorder = { 0, 0, 0, 0 };
|
static const ColorRGBA creatureNoBorder = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
static ColorRGBA genShadow(ui8 alpha)
|
|
||||||
{
|
|
||||||
return ColorRGBA(0, 0, 0, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorRGBA AnimationControls::getBlueBorder()
|
ColorRGBA AnimationControls::getBlueBorder()
|
||||||
{
|
{
|
||||||
return creatureBlueBorder;
|
return creatureBlueBorder;
|
||||||
@ -192,7 +187,6 @@ void CreatureAnimation::setType(ECreatureAnimType type)
|
|||||||
CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedController controller)
|
CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedController controller)
|
||||||
: name(name_),
|
: name(name_),
|
||||||
speed(0.1f),
|
speed(0.1f),
|
||||||
shadowAlpha(128),
|
|
||||||
currentFrame(0),
|
currentFrame(0),
|
||||||
animationEnd(-1),
|
animationEnd(-1),
|
||||||
elapsedTime(0),
|
elapsedTime(0),
|
||||||
@ -200,8 +194,15 @@ CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedControll
|
|||||||
speedController(controller),
|
speedController(controller),
|
||||||
once(false)
|
once(false)
|
||||||
{
|
{
|
||||||
forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
|
|
||||||
reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
|
forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_SELECTION);
|
||||||
|
reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_SELECTION);
|
||||||
|
|
||||||
|
if (forward->size(size_t(ECreatureAnimType::DEATH)) == 0)
|
||||||
|
throw std::runtime_error("Animation '" + name_.getOriginalName() + "' has empty death animation!");
|
||||||
|
|
||||||
|
if (forward->size(size_t(ECreatureAnimType::HOLDING)) == 0)
|
||||||
|
throw std::runtime_error("Animation '" + name_.getOriginalName() + "' has empty holding animation!");
|
||||||
|
|
||||||
// if necessary, add one frame into vcmi-only group DEAD
|
// if necessary, add one frame into vcmi-only group DEAD
|
||||||
if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
|
if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
|
||||||
@ -324,11 +325,8 @@ static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base)
|
|||||||
return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256));
|
return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight)
|
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorRGBA & effectColor, uint8_t transparency, bool facingRight)
|
||||||
{
|
{
|
||||||
ColorRGBA shadowTest = shifter.shiftColor(genShadow(128));
|
|
||||||
shadowAlpha = shadowTest.a;
|
|
||||||
|
|
||||||
size_t frame = static_cast<size_t>(floor(currentFrame));
|
size_t frame = static_cast<size_t>(floor(currentFrame));
|
||||||
|
|
||||||
std::shared_ptr<IImage> image;
|
std::shared_ptr<IImage> image;
|
||||||
@ -345,7 +343,8 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
|
|||||||
else
|
else
|
||||||
image->setOverlayColor(Colors::TRANSPARENCY);
|
image->setOverlayColor(Colors::TRANSPARENCY);
|
||||||
|
|
||||||
image->adjustPalette(shifter, 0);
|
image->setEffectColor(effectColor);
|
||||||
|
image->setAlpha(transparency);
|
||||||
|
|
||||||
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
|
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,6 @@ private:
|
|||||||
///type of animation being displayed
|
///type of animation being displayed
|
||||||
ECreatureAnimType type;
|
ECreatureAnimType type;
|
||||||
|
|
||||||
/// current value of shadow transparency
|
|
||||||
uint8_t shadowAlpha;
|
|
||||||
|
|
||||||
/// border color, disabled if alpha = 0
|
/// border color, disabled if alpha = 0
|
||||||
ColorRGBA border;
|
ColorRGBA border;
|
||||||
|
|
||||||
@ -127,7 +124,7 @@ public:
|
|||||||
/// returns currently rendered type of animation
|
/// returns currently rendered type of animation
|
||||||
ECreatureAnimType getType() const;
|
ECreatureAnimType getType() const;
|
||||||
|
|
||||||
void nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight);
|
void nextFrame(Canvas & canvas, const ColorRGBA & effectColor, uint8_t transparency, bool facingRight);
|
||||||
|
|
||||||
/// should be called every frame, return true when animation was reset to beginning
|
/// should be called every frame, return true when animation was reset to beginning
|
||||||
bool incrementFrame(float timePassed);
|
bool incrementFrame(float timePassed);
|
||||||
|
@ -234,6 +234,14 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
|
|||||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||||
GH.onScreenResize(false);
|
GH.onScreenResize(false);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
#ifdef VCMI_ANDROID
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||||
|
GH.onScreenResize(true);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||||
@ -389,6 +397,13 @@ bool InputHandler::hasTouchInputDevice() const
|
|||||||
return fingerHandler->hasTouchInputDevice();
|
return fingerHandler->hasTouchInputDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int InputHandler::getNumTouchFingers() const
|
||||||
|
{
|
||||||
|
if(currentInputMode != InputMode::TOUCH)
|
||||||
|
return 0;
|
||||||
|
return fingerHandler->getNumTouchFingers();
|
||||||
|
}
|
||||||
|
|
||||||
void InputHandler::dispatchMainThread(const std::function<void()> & functor)
|
void InputHandler::dispatchMainThread(const std::function<void()> & functor)
|
||||||
{
|
{
|
||||||
auto heapFunctor = new std::function<void()>(functor);
|
auto heapFunctor = new std::function<void()>(functor);
|
||||||
|
@ -90,6 +90,9 @@ public:
|
|||||||
/// returns true if system has active touchscreen
|
/// returns true if system has active touchscreen
|
||||||
bool hasTouchInputDevice() const;
|
bool hasTouchInputDevice() const;
|
||||||
|
|
||||||
|
/// returns number of fingers on touchscreen
|
||||||
|
int getNumTouchFingers() const;
|
||||||
|
|
||||||
/// Calls provided functor in main thread on next execution frame
|
/// Calls provided functor in main thread on next execution frame
|
||||||
void dispatchMainThread(const std::function<void()> & functor);
|
void dispatchMainThread(const std::function<void()> & functor);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "../gui/EventDispatcher.h"
|
#include "../gui/EventDispatcher.h"
|
||||||
#include "../gui/MouseButton.h"
|
#include "../gui/MouseButton.h"
|
||||||
#include "../gui/WindowHandler.h"
|
#include "../gui/WindowHandler.h"
|
||||||
|
#include "../render/IScreenHandler.h"
|
||||||
#include "../CServerHandler.h"
|
#include "../CServerHandler.h"
|
||||||
#include "../globalLobby/GlobalLobbyClient.h"
|
#include "../globalLobby/GlobalLobbyClient.h"
|
||||||
|
|
||||||
@ -34,7 +35,7 @@
|
|||||||
#include <SDL_timer.h>
|
#include <SDL_timer.h>
|
||||||
|
|
||||||
InputSourceTouch::InputSourceTouch()
|
InputSourceTouch::InputSourceTouch()
|
||||||
: lastTapTimeTicks(0), lastLeftClickTimeTicks(0)
|
: lastTapTimeTicks(0), lastLeftClickTimeTicks(0), numTouchFingers(0)
|
||||||
{
|
{
|
||||||
params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
||||||
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||||
@ -65,6 +66,7 @@ void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfin
|
|||||||
case TouchState::RELATIVE_MODE:
|
case TouchState::RELATIVE_MODE:
|
||||||
{
|
{
|
||||||
Point screenSize = GH.screenDimensions();
|
Point screenSize = GH.screenDimensions();
|
||||||
|
int scalingFactor = GH.screenHandler().getScalingFactor();
|
||||||
|
|
||||||
Point moveDistance {
|
Point moveDistance {
|
||||||
static_cast<int>(screenSize.x * params.relativeModeSpeedFactor * tfinger.dx),
|
static_cast<int>(screenSize.x * params.relativeModeSpeedFactor * tfinger.dx),
|
||||||
@ -73,7 +75,7 @@ void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfin
|
|||||||
|
|
||||||
GH.input().moveCursorPosition(moveDistance);
|
GH.input().moveCursorPosition(moveDistance);
|
||||||
if (CCS && CCS->curh)
|
if (CCS && CCS->curh)
|
||||||
CCS->curh->cursorMove(GH.getCursorPosition().x, GH.getCursorPosition().y);
|
CCS->curh->cursorMove(GH.getCursorPosition().x * scalingFactor, GH.getCursorPosition().y * scalingFactor);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -114,6 +116,8 @@ void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfin
|
|||||||
|
|
||||||
void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
|
void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
|
||||||
{
|
{
|
||||||
|
numTouchFingers = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||||
|
|
||||||
// FIXME: better place to update potentially changed settings?
|
// FIXME: better place to update potentially changed settings?
|
||||||
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
|
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
|
||||||
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
|
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
|
||||||
@ -172,6 +176,8 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
|
|||||||
|
|
||||||
void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
||||||
{
|
{
|
||||||
|
numTouchFingers = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case TouchState::RELATIVE_MODE:
|
case TouchState::RELATIVE_MODE:
|
||||||
@ -280,6 +286,11 @@ bool InputSourceTouch::hasTouchInputDevice() const
|
|||||||
return SDL_GetNumTouchDevices() > 0;
|
return SDL_GetNumTouchDevices() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int InputSourceTouch::getNumTouchFingers() const
|
||||||
|
{
|
||||||
|
return numTouchFingers;
|
||||||
|
}
|
||||||
|
|
||||||
void InputSourceTouch::emitPanningEvent(const SDL_TouchFingerEvent & tfinger)
|
void InputSourceTouch::emitPanningEvent(const SDL_TouchFingerEvent & tfinger)
|
||||||
{
|
{
|
||||||
Point distance = convertTouchToMouse(-tfinger.dx, -tfinger.dy);
|
Point distance = convertTouchToMouse(-tfinger.dx, -tfinger.dy);
|
||||||
@ -324,8 +335,8 @@ void InputSourceTouch::emitPinchEvent(const SDL_TouchFingerEvent & tfinger)
|
|||||||
float newX = thisX - otherX;
|
float newX = thisX - otherX;
|
||||||
float newY = thisY - otherY;
|
float newY = thisY - otherY;
|
||||||
|
|
||||||
double distanceOld = std::sqrt(oldX * oldX + oldY + oldY);
|
double distanceOld = std::sqrt(oldX * oldX + oldY * oldY);
|
||||||
double distanceNew = std::sqrt(newX * newX + newY + newY);
|
double distanceNew = std::sqrt(newX * newX + newY * newY);
|
||||||
|
|
||||||
if (distanceOld > params.pinchSensitivityThreshold)
|
if (distanceOld > params.pinchSensitivityThreshold)
|
||||||
GH.events().dispatchGesturePinch(lastTapPosition, distanceNew / distanceOld);
|
GH.events().dispatchGesturePinch(lastTapPosition, distanceNew / distanceOld);
|
||||||
|
@ -108,6 +108,7 @@ class InputSourceTouch
|
|||||||
|
|
||||||
uint32_t lastLeftClickTimeTicks;
|
uint32_t lastLeftClickTimeTicks;
|
||||||
Point lastLeftClickPosition;
|
Point lastLeftClickPosition;
|
||||||
|
int numTouchFingers;
|
||||||
|
|
||||||
Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
|
Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
|
||||||
Point convertTouchToMouse(float x, float y);
|
Point convertTouchToMouse(float x, float y);
|
||||||
@ -127,4 +128,6 @@ public:
|
|||||||
void handleUpdate();
|
void handleUpdate();
|
||||||
|
|
||||||
bool hasTouchInputDevice() const;
|
bool hasTouchInputDevice() const;
|
||||||
|
|
||||||
|
int getNumTouchFingers() const;
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "../widgets/Images.h"
|
#include "../widgets/Images.h"
|
||||||
#include "../widgets/MiscWidgets.h"
|
#include "../widgets/MiscWidgets.h"
|
||||||
#include "../widgets/ObjectLists.h"
|
#include "../widgets/ObjectLists.h"
|
||||||
|
#include "../widgets/Slider.h"
|
||||||
#include "../widgets/TextControls.h"
|
#include "../widgets/TextControls.h"
|
||||||
|
|
||||||
#include "../../lib/CConfigHandler.h"
|
#include "../../lib/CConfigHandler.h"
|
||||||
@ -126,6 +127,15 @@ std::shared_ptr<CIntObject> GlobalLobbyWidget::buildItemList(const JsonNode & co
|
|||||||
|
|
||||||
auto result = std::make_shared<CListBox>(callback, position, itemOffset, visibleAmount, totalAmount, initialPos, sliderMode, Rect(sliderPosition, sliderSize));
|
auto result = std::make_shared<CListBox>(callback, position, itemOffset, visibleAmount, totalAmount, initialPos, sliderMode, Rect(sliderPosition, sliderSize));
|
||||||
|
|
||||||
|
if (result->getSlider())
|
||||||
|
{
|
||||||
|
Point scrollBoundsDimensions(sliderPosition.x + result->getSlider()->pos.w, result->getSlider()->pos.h);
|
||||||
|
Point scrollBoundsOffset = -sliderPosition;
|
||||||
|
|
||||||
|
result->getSlider()->setScrollBounds(Rect(scrollBoundsOffset, scrollBoundsDimensions));
|
||||||
|
result->getSlider()->setPanningStep(itemOffset.length());
|
||||||
|
}
|
||||||
|
|
||||||
result->setRedrawParent(true);
|
result->setRedrawParent(true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
||||||
{
|
{
|
||||||
#if defined(VCMI_MOBILE)
|
#if defined(VCMI_MOBILE) || defined(VCMI_PORTMASTER)
|
||||||
if (settings["general"]["userRelativePointer"].Bool())
|
if (settings["general"]["userRelativePointer"].Bool())
|
||||||
return std::make_unique<CursorSoftware>();
|
return std::make_unique<CursorSoftware>();
|
||||||
#endif
|
#endif
|
||||||
|
@ -295,6 +295,7 @@ enum class EShortcut
|
|||||||
// Spellbook screen
|
// Spellbook screen
|
||||||
SPELLBOOK_TAB_ADVENTURE,
|
SPELLBOOK_TAB_ADVENTURE,
|
||||||
SPELLBOOK_TAB_COMBAT,
|
SPELLBOOK_TAB_COMBAT,
|
||||||
|
SPELLBOOK_SEARCH_FOCUS,
|
||||||
|
|
||||||
LIST_HERO_UP,
|
LIST_HERO_UP,
|
||||||
LIST_HERO_DOWN,
|
LIST_HERO_DOWN,
|
||||||
|
@ -277,6 +277,7 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
|||||||
{"heroCostumeLoad9", EShortcut::HERO_COSTUME_LOAD_9 },
|
{"heroCostumeLoad9", EShortcut::HERO_COSTUME_LOAD_9 },
|
||||||
{"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE },
|
{"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE },
|
||||||
{"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT },
|
{"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT },
|
||||||
|
{"spellbookSearchFocus", EShortcut::SPELLBOOK_SEARCH_FOCUS },
|
||||||
{"listHeroUp", EShortcut::LIST_HERO_UP },
|
{"listHeroUp", EShortcut::LIST_HERO_UP },
|
||||||
{"listHeroDown", EShortcut::LIST_HERO_DOWN },
|
{"listHeroDown", EShortcut::LIST_HERO_DOWN },
|
||||||
{"listHeroTop", EShortcut::LIST_HERO_TOP },
|
{"listHeroTop", EShortcut::LIST_HERO_TOP },
|
||||||
|
@ -114,6 +114,7 @@ CBonusSelection::CBonusSelection()
|
|||||||
for(size_t b = 0; b < difficultyIcons.size(); ++b)
|
for(size_t b = 0; b < difficultyIcons.size(); ++b)
|
||||||
{
|
{
|
||||||
difficultyIcons[b] = std::make_shared<CAnimImage>(AnimationPath::builtinTODO("GSPBUT" + std::to_string(b + 3) + ".DEF"), 0, 0, 709, settings["general"]["enableUiEnhancements"].Bool() ? 480 : 455);
|
difficultyIcons[b] = std::make_shared<CAnimImage>(AnimationPath::builtinTODO("GSPBUT" + std::to_string(b + 3) + ".DEF"), 0, 0, 709, settings["general"]["enableUiEnhancements"].Bool() ? 480 : 455);
|
||||||
|
difficultyIconAreas[b] = std::make_shared<LRClickableArea>(difficultyIcons[b]->pos - pos.topLeft(), nullptr, [b]() { CRClickPopup::createAndPush(CGI->generaltexth->zelp[24 + b].second); });
|
||||||
}
|
}
|
||||||
|
|
||||||
if(getCampaign()->playerSelectedDifficulty())
|
if(getCampaign()->playerSelectedDifficulty())
|
||||||
@ -377,9 +378,16 @@ void CBonusSelection::updateAfterStateChange()
|
|||||||
for(size_t i = 0; i < difficultyIcons.size(); i++)
|
for(size_t i = 0; i < difficultyIcons.size(); i++)
|
||||||
{
|
{
|
||||||
if(i == CSH->si->difficulty)
|
if(i == CSH->si->difficulty)
|
||||||
|
{
|
||||||
difficultyIcons[i]->enable();
|
difficultyIcons[i]->enable();
|
||||||
|
difficultyIconAreas[i]->enable();
|
||||||
|
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
difficultyIcons[i]->disable();
|
difficultyIcons[i]->disable();
|
||||||
|
difficultyIconAreas[i]->disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
flagbox->recreate();
|
flagbox->recreate();
|
||||||
createBonusesIcons();
|
createBonusesIcons();
|
||||||
|
@ -31,6 +31,7 @@ class ISelectionScreenInfo;
|
|||||||
class ExtraOptionsTab;
|
class ExtraOptionsTab;
|
||||||
class VideoWidgetOnce;
|
class VideoWidgetOnce;
|
||||||
class CBonusSelection;
|
class CBonusSelection;
|
||||||
|
class LRClickableArea;
|
||||||
|
|
||||||
|
|
||||||
/// Campaign screen where you can choose one out of three starting bonuses
|
/// Campaign screen where you can choose one out of three starting bonuses
|
||||||
@ -93,6 +94,7 @@ public:
|
|||||||
std::shared_ptr<CToggleGroup> groupBonuses;
|
std::shared_ptr<CToggleGroup> groupBonuses;
|
||||||
std::shared_ptr<CLabel> labelDifficulty;
|
std::shared_ptr<CLabel> labelDifficulty;
|
||||||
std::array<std::shared_ptr<CAnimImage>, 5> difficultyIcons;
|
std::array<std::shared_ptr<CAnimImage>, 5> difficultyIcons;
|
||||||
|
std::array<std::shared_ptr<LRClickableArea>, 5> difficultyIconAreas;
|
||||||
std::shared_ptr<CButton> buttonDifficultyLeft;
|
std::shared_ptr<CButton> buttonDifficultyLeft;
|
||||||
std::shared_ptr<CButton> buttonDifficultyRight;
|
std::shared_ptr<CButton> buttonDifficultyRight;
|
||||||
std::shared_ptr<CAnimImage> iconsMapSizes;
|
std::shared_ptr<CAnimImage> iconsMapSizes;
|
||||||
|
@ -457,7 +457,7 @@ void SelectionTab::showPopupWindow(const Point & cursorPosition)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GH.windows().createAndPushWindow<CMapOverview>(
|
GH.windows().createAndPushWindow<CMapOverview>(
|
||||||
curItems[py]->getNameTranslated(),
|
curItems[py]->name,
|
||||||
curItems[py]->fullFileURI,
|
curItems[py]->fullFileURI,
|
||||||
creationDateTime,
|
creationDateTime,
|
||||||
author,
|
author,
|
||||||
|
@ -45,8 +45,6 @@ CHighScoreScreen::CHighScoreScreen(HighScorePage highscorepage, int highlighted)
|
|||||||
OBJECT_CONSTRUCTION;
|
OBJECT_CONSTRUCTION;
|
||||||
pos = center(Rect(0, 0, 800, 600));
|
pos = center(Rect(0, 0, 800, 600));
|
||||||
|
|
||||||
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
|
|
||||||
|
|
||||||
addHighScores();
|
addHighScores();
|
||||||
addButtons();
|
addButtons();
|
||||||
}
|
}
|
||||||
@ -174,6 +172,12 @@ void CHighScoreScreen::buttonExitClick()
|
|||||||
CMM->playMusic();
|
CMM->playMusic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHighScoreScreen::showAll(Canvas & to)
|
||||||
|
{
|
||||||
|
to.fillTexture(GH.renderHandler().loadImage(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE));
|
||||||
|
CWindowObject::showAll(to);
|
||||||
|
}
|
||||||
|
|
||||||
CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc, const StatisticDataSet & statistic)
|
CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc, const StatisticDataSet & statistic)
|
||||||
: CWindowObject(BORDERED), won(won), calc(calc), stat(statistic)
|
: CWindowObject(BORDERED), won(won), calc(calc), stat(statistic)
|
||||||
{
|
{
|
||||||
@ -182,7 +186,6 @@ CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc
|
|||||||
OBJECT_CONSTRUCTION;
|
OBJECT_CONSTRUCTION;
|
||||||
pos = center(Rect(0, 0, 800, 600));
|
pos = center(Rect(0, 0, 800, 600));
|
||||||
|
|
||||||
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
|
|
||||||
background = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), Colors::BLACK);
|
background = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), Colors::BLACK);
|
||||||
|
|
||||||
if(won)
|
if(won)
|
||||||
@ -272,6 +275,12 @@ void CHighScoreInputScreen::show(Canvas & to)
|
|||||||
CWindowObject::showAll(to);
|
CWindowObject::showAll(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHighScoreInputScreen::showAll(Canvas & to)
|
||||||
|
{
|
||||||
|
to.fillTexture(GH.renderHandler().loadImage(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE));
|
||||||
|
CWindowObject::showAll(to);
|
||||||
|
}
|
||||||
|
|
||||||
void CHighScoreInputScreen::clickPressed(const Point & cursorPosition)
|
void CHighScoreInputScreen::clickPressed(const Point & cursorPosition)
|
||||||
{
|
{
|
||||||
if(statisticButton && statisticButton->pos.isInside(cursorPosition))
|
if(statisticButton && statisticButton->pos.isInside(cursorPosition))
|
||||||
|
@ -39,11 +39,11 @@ private:
|
|||||||
void buttonExitClick();
|
void buttonExitClick();
|
||||||
|
|
||||||
void showPopupWindow(const Point & cursorPosition) override;
|
void showPopupWindow(const Point & cursorPosition) override;
|
||||||
|
void showAll(Canvas & to) override;
|
||||||
|
|
||||||
HighScorePage highscorepage;
|
HighScorePage highscorepage;
|
||||||
|
|
||||||
std::shared_ptr<CPicture> background;
|
std::shared_ptr<CPicture> background;
|
||||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
|
||||||
std::vector<std::shared_ptr<CButton>> buttons;
|
std::vector<std::shared_ptr<CButton>> buttons;
|
||||||
std::vector<std::shared_ptr<CLabel>> texts;
|
std::vector<std::shared_ptr<CLabel>> texts;
|
||||||
std::vector<std::shared_ptr<CAnimImage>> images;
|
std::vector<std::shared_ptr<CAnimImage>> images;
|
||||||
@ -77,7 +77,6 @@ class CHighScoreInputScreen : public CWindowObject, public IVideoHolder
|
|||||||
std::shared_ptr<CHighScoreInput> input;
|
std::shared_ptr<CHighScoreInput> input;
|
||||||
std::shared_ptr<TransparentFilledRectangle> background;
|
std::shared_ptr<TransparentFilledRectangle> background;
|
||||||
std::shared_ptr<VideoWidgetBase> videoPlayer;
|
std::shared_ptr<VideoWidgetBase> videoPlayer;
|
||||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
|
||||||
|
|
||||||
std::shared_ptr<CButton> statisticButton;
|
std::shared_ptr<CButton> statisticButton;
|
||||||
|
|
||||||
@ -95,4 +94,5 @@ public:
|
|||||||
void clickPressed(const Point & cursorPosition) override;
|
void clickPressed(const Point & cursorPosition) override;
|
||||||
void keyPressed(EShortcut key) override;
|
void keyPressed(EShortcut key) override;
|
||||||
void show(Canvas & to) override;
|
void show(Canvas & to) override;
|
||||||
|
void showAll(Canvas & to) override;
|
||||||
};
|
};
|
||||||
|
@ -407,7 +407,7 @@ std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const AnimationPath
|
|||||||
if(it != animations.end())
|
if(it != animations.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
auto ret = GH.renderHandler().loadAnimation(filename, enableOverlay ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::WITH_SHADOW);
|
auto ret = GH.renderHandler().loadAnimation(filename, enableOverlay ? EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR: EImageBlitMode::WITH_SHADOW);
|
||||||
animations[filename] = ret;
|
animations[filename] = ret;
|
||||||
|
|
||||||
if(generateMovementGroups)
|
if(generateMovementGroups)
|
||||||
|
@ -278,6 +278,9 @@ std::string MapRendererAdventureContext::overlayText(const int3 & coordinates) c
|
|||||||
if (!tile.visitable())
|
if (!tile.visitable())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
if ( tile.visitableObjects.back()->ID == Obj::EVENT)
|
||||||
|
return {};
|
||||||
|
|
||||||
return tile.visitableObjects.back()->getObjectName();
|
return tile.visitableObjects.back()->getObjectName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ void MapViewController::updateState()
|
|||||||
adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool();
|
adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool();
|
||||||
adventureContext->settingShowBlocked = settings["session"]["showBlocked"].Bool();
|
adventureContext->settingShowBlocked = settings["session"]["showBlocked"].Bool();
|
||||||
adventureContext->settingSpellRange = settings["session"]["showSpellRange"].Bool();
|
adventureContext->settingSpellRange = settings["session"]["showSpellRange"].Bool();
|
||||||
adventureContext->settingTextOverlay = GH.isKeyboardAltDown();
|
adventureContext->settingTextOverlay = GH.isKeyboardAltDown() || GH.input().getNumTouchFingers() == 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,6 @@
|
|||||||
#include "../../lib/TerrainHandler.h"
|
#include "../../lib/TerrainHandler.h"
|
||||||
#include "../../lib/filesystem/Filesystem.h"
|
#include "../../lib/filesystem/Filesystem.h"
|
||||||
|
|
||||||
#include <SDL_mixer.h>
|
|
||||||
|
|
||||||
void CMusicHandler::onVolumeChange(const JsonNode & volumeNode)
|
void CMusicHandler::onVolumeChange(const JsonNode & volumeNode)
|
||||||
{
|
{
|
||||||
setVolume(volumeNode.Integer());
|
setVolume(volumeNode.Integer());
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
|
|
||||||
#include "../lib/CConfigHandler.h"
|
#include "../lib/CConfigHandler.h"
|
||||||
|
|
||||||
struct _Mix_Music;
|
#include <SDL_mixer.h>
|
||||||
using Mix_Music = struct _Mix_Music;
|
|
||||||
|
|
||||||
class CMusicHandler;
|
class CMusicHandler;
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ AssetGenerator::CanvasPtr AssetGenerator::createCampaignBackground()
|
|||||||
auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE);
|
auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE);
|
||||||
|
|
||||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||||
auto image = GH.renderHandler().createImage(Point(200, 116), CanvasScalingPolicy::IGNORE);
|
auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||||
Canvas canvas = image->getCanvas();
|
Canvas canvas = image->getCanvas();
|
||||||
|
|
||||||
canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
|
canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
|
||||||
@ -247,11 +247,11 @@ AssetGenerator::CanvasPtr AssetGenerator::createCampaignBackground()
|
|||||||
|
|
||||||
AssetGenerator::CanvasPtr AssetGenerator::createChroniclesCampaignImages(int chronicle)
|
AssetGenerator::CanvasPtr AssetGenerator::createChroniclesCampaignImages(int chronicle)
|
||||||
{
|
{
|
||||||
auto imgPathBg = ImagePath::builtin("data/chronicles_" + std::to_string(chronicle) + "/GamSelBk");
|
auto imgPathBg = ImagePath::builtin("chronicles_" + std::to_string(chronicle) + "/GamSelBk");
|
||||||
auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE);
|
auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE);
|
||||||
|
|
||||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||||
auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
auto image = GH.renderHandler().createImage(Point(200, 116), CanvasScalingPolicy::IGNORE);
|
||||||
Canvas canvas = image->getCanvas();
|
Canvas canvas = image->getCanvas();
|
||||||
|
|
||||||
std::array sourceRect = {
|
std::array sourceRect = {
|
||||||
|
@ -25,6 +25,11 @@ CanvasImage::CanvasImage(const Point & size, CanvasScalingPolicy scalingPolicy)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CanvasImage::~CanvasImage()
|
||||||
|
{
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasImage::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const
|
void CanvasImage::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const
|
||||||
{
|
{
|
||||||
if(src)
|
if(src)
|
||||||
|
@ -16,6 +16,7 @@ class CanvasImage : public IImage
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CanvasImage(const Point & size, CanvasScalingPolicy scalingPolicy);
|
CanvasImage(const Point & size, CanvasScalingPolicy scalingPolicy);
|
||||||
|
~CanvasImage();
|
||||||
|
|
||||||
Canvas getCanvas();
|
Canvas getCanvas();
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ public:
|
|||||||
void setAlpha(uint8_t value) override{};
|
void setAlpha(uint8_t value) override{};
|
||||||
void playerColored(const PlayerColor & player) override{};
|
void playerColored(const PlayerColor & player) override{};
|
||||||
void setOverlayColor(const ColorRGBA & color) override{};
|
void setOverlayColor(const ColorRGBA & color) override{};
|
||||||
|
void setEffectColor(const ColorRGBA & color) override{};
|
||||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override{};
|
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override{};
|
||||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override{};
|
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override{};
|
||||||
|
|
||||||
|
@ -129,40 +129,3 @@ ColorFilter ColorFilter::genCombined(const ColorFilter & left, const ColorFilter
|
|||||||
float a = left.a * right.a;
|
float a = left.a * right.a;
|
||||||
return genMuxerShifter(r,g,b,a);
|
return genMuxerShifter(r,g,b,a);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorFilter ColorFilter::genFromJson(const JsonNode & entry)
|
|
||||||
{
|
|
||||||
ChannelMuxer r{ 1.f, 0.f, 0.f, 0.f };
|
|
||||||
ChannelMuxer g{ 0.f, 1.f, 0.f, 0.f };
|
|
||||||
ChannelMuxer b{ 0.f, 0.f, 1.f, 0.f };
|
|
||||||
float a{ 1.0};
|
|
||||||
|
|
||||||
if (!entry["red"].isNull())
|
|
||||||
{
|
|
||||||
r.r = entry["red"].Vector()[0].Float();
|
|
||||||
r.g = entry["red"].Vector()[1].Float();
|
|
||||||
r.b = entry["red"].Vector()[2].Float();
|
|
||||||
r.a = entry["red"].Vector()[3].Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry["green"].isNull())
|
|
||||||
{
|
|
||||||
g.r = entry["green"].Vector()[0].Float();
|
|
||||||
g.g = entry["green"].Vector()[1].Float();
|
|
||||||
g.b = entry["green"].Vector()[2].Float();
|
|
||||||
g.a = entry["green"].Vector()[3].Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry["blue"].isNull())
|
|
||||||
{
|
|
||||||
b.r = entry["blue"].Vector()[0].Float();
|
|
||||||
b.g = entry["blue"].Vector()[1].Float();
|
|
||||||
b.b = entry["blue"].Vector()[2].Float();
|
|
||||||
b.a = entry["blue"].Vector()[3].Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry["alpha"].isNull())
|
|
||||||
a = entry["alpha"].Float();
|
|
||||||
|
|
||||||
return genMuxerShifter(r,g,b,a);
|
|
||||||
}
|
|
||||||
|
@ -54,13 +54,4 @@ public:
|
|||||||
|
|
||||||
/// Scales down strength of a shifter to a specified factor
|
/// Scales down strength of a shifter to a specified factor
|
||||||
static ColorFilter genInterpolated(const ColorFilter & left, const ColorFilter & right, float power);
|
static ColorFilter genInterpolated(const ColorFilter & left, const ColorFilter & right, float power);
|
||||||
|
|
||||||
/// Generates object using supplied Json config
|
|
||||||
static ColorFilter genFromJson(const JsonNode & entry);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ColorMuxerEffect
|
|
||||||
{
|
|
||||||
std::vector<ColorFilter> filters;
|
|
||||||
std::vector<float> timePoints;
|
|
||||||
};
|
};
|
||||||
|
@ -47,19 +47,24 @@ enum class EImageBlitMode : uint8_t
|
|||||||
WITH_SHADOW,
|
WITH_SHADOW,
|
||||||
|
|
||||||
/// RGBA, may consist from 3 separate parts: base, shadow, and overlay
|
/// RGBA, may consist from 3 separate parts: base, shadow, and overlay
|
||||||
WITH_SHADOW_AND_OVERLAY,
|
WITH_SHADOW_AND_SELECTION,
|
||||||
|
WITH_SHADOW_AND_FLAG_COLOR,
|
||||||
|
|
||||||
/// RGBA, contains only body, with shadow and overlay disabled
|
/// RGBA, contains only body, with shadow and overlay disabled
|
||||||
ONLY_BODY,
|
GRAYSCALE_BODY_HIDE_SELECTION,
|
||||||
|
ONLY_BODY_HIDE_SELECTION,
|
||||||
|
ONLY_BODY_HIDE_FLAG_COLOR,
|
||||||
|
|
||||||
/// RGBA, contains only body, with shadow disabled and overlay treated as part of body
|
/// RGBA, contains only body, with shadow disabled and overlay treated as part of body
|
||||||
ONLY_BODY_IGNORE_OVERLAY,
|
ONLY_BODY_IGNORE_OVERLAY,
|
||||||
|
|
||||||
/// RGBA, contains only shadow
|
/// RGBA, contains only shadow
|
||||||
ONLY_SHADOW,
|
ONLY_SHADOW_HIDE_SELECTION,
|
||||||
|
ONLY_SHADOW_HIDE_FLAG_COLOR,
|
||||||
|
|
||||||
/// RGBA, contains only overlay
|
/// RGBA, contains only overlay
|
||||||
ONLY_OVERLAY,
|
ONLY_SELECTION,
|
||||||
|
ONLY_FLAG_COLOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EScalingAlgorithm : int8_t
|
enum class EScalingAlgorithm : int8_t
|
||||||
@ -100,8 +105,8 @@ public:
|
|||||||
|
|
||||||
virtual void setAlpha(uint8_t value) = 0;
|
virtual void setAlpha(uint8_t value) = 0;
|
||||||
|
|
||||||
//only indexed bitmaps with 7 special colors
|
|
||||||
virtual void setOverlayColor(const ColorRGBA & color) = 0;
|
virtual void setOverlayColor(const ColorRGBA & color) = 0;
|
||||||
|
virtual void setEffectColor(const ColorRGBA & color) = 0;
|
||||||
|
|
||||||
virtual ~IImage() = default;
|
virtual ~IImage() = default;
|
||||||
};
|
};
|
||||||
|
@ -74,9 +74,12 @@ bool FontChain::bitmapFontsPrioritized(const std::string & bitmapFontName) const
|
|||||||
return true; // else - use original bitmap fonts
|
return true; // else - use original bitmap fonts
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontChain::addTrueTypeFont(const JsonNode & trueTypeConfig)
|
void FontChain::addTrueTypeFont(const JsonNode & trueTypeConfig, bool begin)
|
||||||
{
|
{
|
||||||
chain.insert(chain.begin(), std::make_unique<CTrueTypeFont>(trueTypeConfig));
|
if(begin)
|
||||||
|
chain.insert(chain.begin(), std::make_unique<CTrueTypeFont>(trueTypeConfig));
|
||||||
|
else
|
||||||
|
chain.push_back(std::make_unique<CTrueTypeFont>(trueTypeConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontChain::addBitmapFont(const std::string & bitmapFilename)
|
void FontChain::addBitmapFont(const std::string & bitmapFilename)
|
||||||
|
@ -33,7 +33,7 @@ class FontChain final : public IFont
|
|||||||
public:
|
public:
|
||||||
FontChain() = default;
|
FontChain() = default;
|
||||||
|
|
||||||
void addTrueTypeFont(const JsonNode & trueTypeConfig);
|
void addTrueTypeFont(const JsonNode & trueTypeConfig, bool begin);
|
||||||
void addBitmapFont(const std::string & bitmapFilename);
|
void addBitmapFont(const std::string & bitmapFilename);
|
||||||
|
|
||||||
size_t getLineHeightScaled() const override;
|
size_t getLineHeightScaled() const override;
|
||||||
|
@ -230,6 +230,7 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const Ima
|
|||||||
if (generated)
|
if (generated)
|
||||||
return generated;
|
return generated;
|
||||||
|
|
||||||
|
logGlobal->error("Failed to load image %s", locator.image->getOriginalName());
|
||||||
return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"));
|
return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,9 +293,9 @@ std::shared_ptr<SDLImageShared> RenderHandler::loadScaledImage(const ImageLocato
|
|||||||
|
|
||||||
std::string imagePathString = pathToLoad.getName();
|
std::string imagePathString = pathToLoad.getName();
|
||||||
|
|
||||||
if(locator.layer == EImageBlitMode::ONLY_OVERLAY)
|
if(locator.layer == EImageBlitMode::ONLY_FLAG_COLOR || locator.layer == EImageBlitMode::ONLY_SELECTION)
|
||||||
imagePathString += "-OVERLAY";
|
imagePathString += "-OVERLAY";
|
||||||
if(locator.layer == EImageBlitMode::ONLY_SHADOW)
|
if(locator.layer == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION || locator.layer == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR)
|
||||||
imagePathString += "-SHADOW";
|
imagePathString += "-SHADOW";
|
||||||
if(locator.playerColored.isValidPlayer())
|
if(locator.playerColored.isValidPlayer())
|
||||||
imagePathString += "-" + boost::to_upper_copy(GameConstants::PLAYER_COLOR_NAMES[locator.playerColored.getNum()]);
|
imagePathString += "-" + boost::to_upper_copy(GameConstants::PLAYER_COLOR_NAMES[locator.playerColored.getNum()]);
|
||||||
@ -347,7 +348,10 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int
|
|||||||
if (!locator.empty())
|
if (!locator.empty())
|
||||||
return loadImage(locator);
|
return loadImage(locator);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
logGlobal->error("Failed to load non-existing image");
|
||||||
return loadImage(ImageLocator(ImagePath::builtin("DEFAULT"), mode));
|
return loadImage(ImageLocator(ImagePath::builtin("DEFAULT"), mode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode)
|
std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode)
|
||||||
@ -375,7 +379,7 @@ void RenderHandler::addImageListEntries(const EntityService * service)
|
|||||||
if (imageName.empty())
|
if (imageName.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName), 1, EImageBlitMode::SIMPLE);
|
auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName), 1, EImageBlitMode::COLORKEY);
|
||||||
|
|
||||||
JsonNode entry;
|
JsonNode entry;
|
||||||
entry["file"].String() = imageName;
|
entry["file"].String() = imageName;
|
||||||
@ -413,8 +417,8 @@ static void detectOverlappingBuildings(RenderHandler * renderHandler, const Fact
|
|||||||
if (left->pos.z != right->pos.z)
|
if (left->pos.z != right->pos.z)
|
||||||
continue; // buildings already have different z-index and have well-defined overlap logic
|
continue; // buildings already have different z-index and have well-defined overlap logic
|
||||||
|
|
||||||
auto leftImage = renderHandler->loadImage(left->defName, 0, 0, EImageBlitMode::SIMPLE);
|
auto leftImage = renderHandler->loadImage(left->defName, 0, 0, EImageBlitMode::COLORKEY);
|
||||||
auto rightImage = renderHandler->loadImage(right->defName, 0, 0, EImageBlitMode::SIMPLE);
|
auto rightImage = renderHandler->loadImage(right->defName, 0, 0, EImageBlitMode::COLORKEY);
|
||||||
|
|
||||||
Rect leftRect( left->pos.x, left->pos.y, leftImage->width(), leftImage->height());
|
Rect leftRect( left->pos.x, left->pos.y, leftImage->width(), leftImage->height());
|
||||||
Rect rightRect( right->pos.x, right->pos.y, rightImage->width(), rightImage->height());
|
Rect rightRect( right->pos.x, right->pos.y, rightImage->width(), rightImage->height());
|
||||||
@ -490,7 +494,7 @@ std::shared_ptr<const IFont> RenderHandler::loadFont(EFonts font)
|
|||||||
|
|
||||||
bitmapPath = bmpConf[index].String();
|
bitmapPath = bmpConf[index].String();
|
||||||
if (!ttfConf[bitmapPath].isNull())
|
if (!ttfConf[bitmapPath].isNull())
|
||||||
loadedFont->addTrueTypeFont(ttfConf[bitmapPath]);
|
loadedFont->addTrueTypeFont(ttfConf[bitmapPath], !config["lowPriority"].Bool());
|
||||||
}
|
}
|
||||||
loadedFont->addBitmapFont(bitmapPath);
|
loadedFont->addBitmapFont(bitmapPath);
|
||||||
|
|
||||||
|
@ -97,47 +97,67 @@ void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette
|
|||||||
{
|
{
|
||||||
switch(blitMode)
|
switch(blitMode)
|
||||||
{
|
{
|
||||||
case EImageBlitMode::ONLY_SHADOW:
|
case EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR:
|
||||||
case EImageBlitMode::ONLY_OVERLAY:
|
case EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION:
|
||||||
|
case EImageBlitMode::ONLY_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::ONLY_SELECTION:
|
||||||
adjustPalette(originalPalette, blitMode, ColorFilter::genAlphaShifter(0), 0);
|
adjustPalette(originalPalette, blitMode, ColorFilter::genAlphaShifter(0), 0);
|
||||||
break;
|
break;
|
||||||
|
case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION:
|
||||||
|
adjustPalette(originalPalette, blitMode, ColorFilter::genMuxerShifter( { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, 1), 0);
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(blitMode)
|
switch(blitMode)
|
||||||
{
|
{
|
||||||
case EImageBlitMode::SIMPLE:
|
case EImageBlitMode::SIMPLE:
|
||||||
case EImageBlitMode::WITH_SHADOW:
|
case EImageBlitMode::WITH_SHADOW:
|
||||||
case EImageBlitMode::ONLY_SHADOW:
|
case EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR:
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION:
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
setShadowTransparency(originalPalette, 1.0);
|
setShadowTransparency(originalPalette, 1.0);
|
||||||
break;
|
break;
|
||||||
case EImageBlitMode::ONLY_BODY:
|
case EImageBlitMode::ONLY_BODY_HIDE_SELECTION:
|
||||||
|
case EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR:
|
||||||
case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
|
case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
|
||||||
case EImageBlitMode::ONLY_OVERLAY:
|
case EImageBlitMode::ONLY_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::ONLY_SELECTION:
|
||||||
|
case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION:
|
||||||
setShadowTransparency(originalPalette, 0.0);
|
setShadowTransparency(originalPalette, 0.0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(blitMode)
|
switch(blitMode)
|
||||||
{
|
{
|
||||||
case EImageBlitMode::ONLY_OVERLAY:
|
case EImageBlitMode::ONLY_FLAG_COLOR:
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
setOverlayColor(originalPalette, Colors::WHITE_TRUE);
|
setOverlayColor(originalPalette, Colors::WHITE_TRUE, false);
|
||||||
break;
|
break;
|
||||||
case EImageBlitMode::ONLY_SHADOW:
|
case EImageBlitMode::ONLY_SELECTION:
|
||||||
case EImageBlitMode::ONLY_BODY:
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
setOverlayColor(originalPalette, Colors::TRANSPARENCY);
|
setOverlayColor(originalPalette, Colors::WHITE_TRUE, true);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR:
|
||||||
|
setOverlayColor(originalPalette, Colors::TRANSPARENCY, false);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION:
|
||||||
|
case EImageBlitMode::ONLY_BODY_HIDE_SELECTION:
|
||||||
|
case EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION:
|
||||||
|
setOverlayColor(originalPalette, Colors::TRANSPARENCY, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScalableImageParameters::setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color)
|
void ScalableImageParameters::setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color, bool includeShadow)
|
||||||
{
|
{
|
||||||
palette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color));
|
palette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color));
|
||||||
|
|
||||||
for (int i : {6,7})
|
if (includeShadow)
|
||||||
{
|
{
|
||||||
if (colorsSimilar(originalPalette->colors[i], sourcePalette[i]))
|
for (int i : {6,7})
|
||||||
palette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color));
|
palette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,7 +203,7 @@ void ScalableImageParameters::setShadowTransparency(const SDL_Palette * original
|
|||||||
void ScalableImageParameters::adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask)
|
void ScalableImageParameters::adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask)
|
||||||
{
|
{
|
||||||
// If shadow is enabled, following colors must be skipped unconditionally
|
// If shadow is enabled, following colors must be skipped unconditionally
|
||||||
if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_OVERLAY)
|
if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_SELECTION || blitMode == EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR)
|
||||||
colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4);
|
colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4);
|
||||||
|
|
||||||
// Note: here we skip first colors in the palette that are predefined in H3 images
|
// Note: here we skip first colors in the palette that are predefined in H3 images
|
||||||
@ -259,11 +279,15 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
|||||||
bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading();
|
bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading();
|
||||||
bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading();
|
bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading();
|
||||||
bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading();
|
bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading();
|
||||||
|
bool grayscaleLoading = scaled.at(scalingFactor).bodyGrayscale.at(0) && scaled.at(scalingFactor).bodyGrayscale.at(0)->isLoading();
|
||||||
bool playerLoading = parameters.player != PlayerColor::CANNOT_DETERMINE && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())->isLoading();
|
bool playerLoading = parameters.player != PlayerColor::CANNOT_DETERMINE && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())->isLoading();
|
||||||
|
|
||||||
if (shadowLoading || bodyLoading || overlayLoading || playerLoading)
|
if (shadowLoading || bodyLoading || overlayLoading || playerLoading || grayscaleLoading)
|
||||||
{
|
{
|
||||||
getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer);
|
getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer);
|
||||||
|
|
||||||
|
if (parameters.effectColorMultiplier.a != ColorRGBA::ALPHA_TRANSPARENT)
|
||||||
|
getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.effectColorMultiplier, parameters.alphaValue, locator.layer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +302,9 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
|||||||
{
|
{
|
||||||
if (scaled.at(scalingFactor).body.at(0))
|
if (scaled.at(scalingFactor).body.at(0))
|
||||||
flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue);
|
flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue);
|
||||||
|
|
||||||
|
if (scaled.at(scalingFactor).bodyGrayscale.at(0) && parameters.effectColorMultiplier.a != ColorRGBA::ALPHA_TRANSPARENT)
|
||||||
|
flipAndDraw(scaled.at(scalingFactor).bodyGrayscale, parameters.effectColorMultiplier, parameters.alphaValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scaled.at(scalingFactor).overlay.at(0))
|
if (scaled.at(scalingFactor).overlay.at(0))
|
||||||
@ -353,7 +380,25 @@ void ScalableImageInstance::setOverlayColor(const ColorRGBA & color)
|
|||||||
parameters.ovelayColorMultiplier = color;
|
parameters.ovelayColorMultiplier = color;
|
||||||
|
|
||||||
if (parameters.palette)
|
if (parameters.palette)
|
||||||
parameters.setOverlayColor(image->getPalette(), color);
|
parameters.setOverlayColor(image->getPalette(), color, blitMode == EImageBlitMode::WITH_SHADOW_AND_SELECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScalableImageInstance::setEffectColor(const ColorRGBA & color)
|
||||||
|
{
|
||||||
|
parameters.effectColorMultiplier = color;
|
||||||
|
|
||||||
|
if (parameters.palette)
|
||||||
|
{
|
||||||
|
const auto grayscaleFilter = ColorFilter::genMuxerShifter( { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, { 0.299, 0.587, 0.114, 0}, 1);
|
||||||
|
const auto effectStrengthFilter = ColorFilter::genRangeShifter( 0, 0, 0, color.r / 255.f, color.g / 255.f, color.b / 255.f);
|
||||||
|
const auto effectFilter = ColorFilter::genCombined(grayscaleFilter, effectStrengthFilter);
|
||||||
|
const auto effectiveFilter = ColorFilter::genInterpolated(ColorFilter::genEmptyShifter(), effectFilter, color.a / 255.f);
|
||||||
|
|
||||||
|
parameters.adjustPalette(image->getPalette(), blitMode, effectiveFilter, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color.a != ColorRGBA::ALPHA_TRANSPARENT)
|
||||||
|
image->prepareEffectImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScalableImageInstance::playerColored(const PlayerColor & player)
|
void ScalableImageInstance::playerColored(const PlayerColor & player)
|
||||||
@ -410,11 +455,19 @@ std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EIm
|
|||||||
if (loadedImage)
|
if (loadedImage)
|
||||||
return loadedImage;
|
return loadedImage;
|
||||||
|
|
||||||
|
// optional images for 1x resolution - only try load them, don't attempt to generate
|
||||||
|
bool optionalImage =
|
||||||
|
mode == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR ||
|
||||||
|
mode == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION ||
|
||||||
|
mode == EImageBlitMode::ONLY_FLAG_COLOR ||
|
||||||
|
mode == EImageBlitMode::ONLY_SELECTION ||
|
||||||
|
mode == EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION ||
|
||||||
|
color != PlayerColor::CANNOT_DETERMINE;
|
||||||
|
|
||||||
if (scalingFactor == 1)
|
if (scalingFactor == 1)
|
||||||
{
|
{
|
||||||
// optional images for 1x resolution - only try load them, don't attempt to generate
|
|
||||||
// this block should never be called for 'body' layer - that image is loaded unconditionally before construction
|
// this block should never be called for 'body' layer - that image is loaded unconditionally before construction
|
||||||
assert(mode == EImageBlitMode::ONLY_SHADOW || mode == EImageBlitMode::ONLY_OVERLAY || color != PlayerColor::CANNOT_DETERMINE);
|
assert(optionalImage);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +480,7 @@ std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EIm
|
|||||||
{
|
{
|
||||||
if (scaling == 1)
|
if (scaling == 1)
|
||||||
{
|
{
|
||||||
if (mode == EImageBlitMode::ONLY_SHADOW || mode == EImageBlitMode::ONLY_OVERLAY || color != PlayerColor::CANNOT_DETERMINE)
|
if (optionalImage)
|
||||||
{
|
{
|
||||||
ScalableImageParameters parameters(getPalette(), mode);
|
ScalableImageParameters parameters(getPalette(), mode);
|
||||||
return loadedImage->scaleInteger(scalingFactor, parameters.palette, mode);
|
return loadedImage->scaleInteger(scalingFactor, parameters.palette, mode);
|
||||||
@ -464,9 +517,13 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col
|
|||||||
scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]);
|
scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
case EImageBlitMode::ONLY_BODY:
|
case EImageBlitMode::ONLY_BODY_HIDE_SELECTION:
|
||||||
scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]);
|
scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_HIDE_SELECTION, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR:
|
||||||
|
scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EImageBlitMode::WITH_SHADOW:
|
case EImageBlitMode::WITH_SHADOW:
|
||||||
@ -486,9 +543,13 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col
|
|||||||
scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(locator.layer, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]);
|
scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(locator.layer, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
case EImageBlitMode::ONLY_BODY:
|
case EImageBlitMode::ONLY_BODY_HIDE_SELECTION:
|
||||||
scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]);
|
scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_HIDE_SELECTION, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR:
|
||||||
|
scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_HIDE_FLAG_COLOR, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EImageBlitMode::WITH_SHADOW:
|
case EImageBlitMode::WITH_SHADOW:
|
||||||
@ -503,9 +564,13 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col
|
|||||||
switch(locator.layer)
|
switch(locator.layer)
|
||||||
{
|
{
|
||||||
case EImageBlitMode::WITH_SHADOW:
|
case EImageBlitMode::WITH_SHADOW:
|
||||||
case EImageBlitMode::ONLY_SHADOW:
|
case EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION:
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].shadow[0]);
|
scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].shadow[0]);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR:
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
|
scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].shadow[0]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -516,9 +581,13 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col
|
|||||||
{
|
{
|
||||||
switch(locator.layer)
|
switch(locator.layer)
|
||||||
{
|
{
|
||||||
case EImageBlitMode::ONLY_OVERLAY:
|
case EImageBlitMode::ONLY_FLAG_COLOR:
|
||||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
case EImageBlitMode::WITH_SHADOW_AND_FLAG_COLOR:
|
||||||
scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].overlay[0]);
|
scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_FLAG_COLOR, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].overlay[0]);
|
||||||
|
break;
|
||||||
|
case EImageBlitMode::ONLY_SELECTION:
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
|
scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SELECTION, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].overlay[0]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -530,3 +599,20 @@ void ScalableImageShared::preparePlayerColoredImage(PlayerColor color)
|
|||||||
{
|
{
|
||||||
loadScaledImages(GH.screenHandler().getScalingFactor(), color);
|
loadScaledImages(GH.screenHandler().getScalingFactor(), color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScalableImageShared::prepareEffectImage()
|
||||||
|
{
|
||||||
|
int scalingFactor = GH.screenHandler().getScalingFactor();
|
||||||
|
|
||||||
|
if (scaled[scalingFactor].bodyGrayscale[0] == nullptr)
|
||||||
|
{
|
||||||
|
switch(locator.layer)
|
||||||
|
{
|
||||||
|
case EImageBlitMode::WITH_SHADOW_AND_SELECTION:
|
||||||
|
scaled[scalingFactor].bodyGrayscale[0] = loadOrGenerateImage(EImageBlitMode::GRAYSCALE_BODY_HIDE_SELECTION, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].bodyGrayscale[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ struct ScalableImageParameters : boost::noncopyable
|
|||||||
|
|
||||||
ColorRGBA colorMultiplier = Colors::WHITE_TRUE;
|
ColorRGBA colorMultiplier = Colors::WHITE_TRUE;
|
||||||
ColorRGBA ovelayColorMultiplier = Colors::WHITE_TRUE;
|
ColorRGBA ovelayColorMultiplier = Colors::WHITE_TRUE;
|
||||||
|
ColorRGBA effectColorMultiplier = Colors::TRANSPARENCY;
|
||||||
|
|
||||||
PlayerColor player = PlayerColor::CANNOT_DETERMINE;
|
PlayerColor player = PlayerColor::CANNOT_DETERMINE;
|
||||||
uint8_t alphaValue = 255;
|
uint8_t alphaValue = 255;
|
||||||
@ -39,7 +40,7 @@ struct ScalableImageParameters : boost::noncopyable
|
|||||||
void setShadowTransparency(const SDL_Palette * originalPalette, float factor);
|
void setShadowTransparency(const SDL_Palette * originalPalette, float factor);
|
||||||
void shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove);
|
void shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove);
|
||||||
void playerColored(PlayerColor player);
|
void playerColored(PlayerColor player);
|
||||||
void setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color);
|
void setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color, bool includeShadow);
|
||||||
void preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode);
|
void preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode);
|
||||||
void adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask);
|
void adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask);
|
||||||
};
|
};
|
||||||
@ -64,6 +65,9 @@ class ScalableImageShared final : public std::enable_shared_from_this<ScalableIm
|
|||||||
/// Upscaled overlay (player color, selection highlight) of our image, may be null
|
/// Upscaled overlay (player color, selection highlight) of our image, may be null
|
||||||
FlippedImages overlay;
|
FlippedImages overlay;
|
||||||
|
|
||||||
|
/// Upscaled grayscale version of body, for special effects in combat (e.g clone / petrify / berserk)
|
||||||
|
FlippedImages bodyGrayscale;
|
||||||
|
|
||||||
/// player-colored images of this particular scale, mostly for UI. These are never flipped in h3
|
/// player-colored images of this particular scale, mostly for UI. These are never flipped in h3
|
||||||
PlayerColoredImages playerColored;
|
PlayerColoredImages playerColored;
|
||||||
};
|
};
|
||||||
@ -91,6 +95,7 @@ public:
|
|||||||
|
|
||||||
std::shared_ptr<ScalableImageInstance> createImageReference();
|
std::shared_ptr<ScalableImageInstance> createImageReference();
|
||||||
|
|
||||||
|
void prepareEffectImage();
|
||||||
void preparePlayerColoredImage(PlayerColor color);
|
void preparePlayerColoredImage(PlayerColor color);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,6 +120,7 @@ public:
|
|||||||
void setAlpha(uint8_t value) override;
|
void setAlpha(uint8_t value) override;
|
||||||
void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override;
|
void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override;
|
||||||
void setOverlayColor(const ColorRGBA & color) override;
|
void setOverlayColor(const ColorRGBA & color) override;
|
||||||
|
void setEffectColor(const ColorRGBA & color) override;
|
||||||
void playerColored(const PlayerColor & player) override;
|
void playerColored(const PlayerColor & player) override;
|
||||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||||
|
@ -208,6 +208,10 @@ ScreenHandler::ScreenHandler()
|
|||||||
// NOTE: requires SDL 2.24.
|
// NOTE: requires SDL 2.24.
|
||||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitor");
|
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitor");
|
||||||
#endif
|
#endif
|
||||||
|
if(settings["video"]["allowPortrait"].Bool())
|
||||||
|
SDL_SetHint(SDL_HINT_ORIENTATIONS, "Portrait PortraitUpsideDown LandscapeLeft LandscapeRight");
|
||||||
|
else
|
||||||
|
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
|
||||||
|
|
||||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER))
|
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER))
|
||||||
{
|
{
|
||||||
@ -354,34 +358,23 @@ EUpscalingFilter ScreenHandler::loadUpscalingFilter() const
|
|||||||
return filter;
|
return filter;
|
||||||
|
|
||||||
// else - autoselect
|
// else - autoselect
|
||||||
#ifdef VCMI_MOBILE
|
|
||||||
// to help with performance - only if player explicitly enabled xbrz
|
|
||||||
return EUpscalingFilter::NONE;
|
|
||||||
#else
|
|
||||||
Point outputResolution = getRenderResolution();
|
Point outputResolution = getRenderResolution();
|
||||||
Point logicalResolution = getPreferredLogicalResolution();
|
Point logicalResolution = getPreferredLogicalResolution();
|
||||||
|
|
||||||
float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||||
float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||||
float scaling = std::min(scaleX, scaleY);
|
float scaling = std::min(scaleX, scaleY);
|
||||||
|
int systemMemoryMb = SDL_GetSystemRAM();
|
||||||
|
|
||||||
if (scaling <= 1.001f)
|
if (scaling <= 1.001f)
|
||||||
return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz
|
return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz
|
||||||
else
|
|
||||||
return EUpscalingFilter::XBRZ_2;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
if (systemMemoryMb < 2048)
|
||||||
// Old version, most optimal, but rather performance-heavy
|
return EUpscalingFilter::NONE; // xbrz2 may use ~1.0 - 1.5 Gb of RAM and has notable CPU cost - avoid on low-spec hardware
|
||||||
if (scaling <= 1.001f)
|
|
||||||
return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz
|
|
||||||
if (scaling <= 2.001f)
|
|
||||||
return EUpscalingFilter::XBRZ_2; // resolutions below 1200p (including 1080p / FullHD)
|
|
||||||
if (scaling <= 3.001f)
|
|
||||||
return EUpscalingFilter::XBRZ_3; // resolutions below 2400p (including 1440p and 2160p / 4K)
|
|
||||||
|
|
||||||
return EUpscalingFilter::XBRZ_4; // Only for massive displays, e.g. 8K
|
// Only using xbrz2 for autoselection.
|
||||||
#endif
|
// Higher options may have high system requirements and should be only selected explicitly by player
|
||||||
|
return EUpscalingFilter::XBRZ_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenHandler::selectUpscalingFilter()
|
void ScreenHandler::selectUpscalingFilter()
|
||||||
@ -478,7 +471,7 @@ SDL_Window * ScreenHandler::createWindow()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
return createWindowImpl(Point(), SDL_WINDOW_FULLSCREEN, false);
|
return createWindowImpl(Point(), SDL_WINDOW_RESIZABLE, false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +110,22 @@ void CExchangeController::moveStack(bool leftToRight, SlotID sourceSlot)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CExchangeController::moveSingleStackCreature(bool leftToRight, SlotID sourceSlot, bool forceEmptySlotTarget)
|
||||||
|
{
|
||||||
|
const auto source = leftToRight ? left : right;
|
||||||
|
const auto target = leftToRight ? right : left;
|
||||||
|
auto creature = source->getCreature(sourceSlot);
|
||||||
|
|
||||||
|
if(creature == nullptr || source->stacksCount() == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SlotID targetSlot = forceEmptySlotTarget ? target->getFreeSlot() : target->getSlotFor(creature);
|
||||||
|
if(targetSlot.validSlot())
|
||||||
|
{
|
||||||
|
LOCPLINT->cb->splitStack(source, target, sourceSlot, targetSlot, target->getStackCount(targetSlot) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CExchangeController::swapArtifacts(bool equipped, bool baclpack)
|
void CExchangeController::swapArtifacts(bool equipped, bool baclpack)
|
||||||
{
|
{
|
||||||
LOCPLINT->cb->bulkMoveArtifacts(left->id, right->id, true, equipped, baclpack);
|
LOCPLINT->cb->bulkMoveArtifacts(left->id, right->id, true, equipped, baclpack);
|
||||||
|
@ -20,6 +20,7 @@ public:
|
|||||||
void swapArmy();
|
void swapArmy();
|
||||||
void moveArmy(bool leftToRight, std::optional<SlotID> heldSlot);
|
void moveArmy(bool leftToRight, std::optional<SlotID> heldSlot);
|
||||||
void moveStack(bool leftToRight, SlotID sourceSlot);
|
void moveStack(bool leftToRight, SlotID sourceSlot);
|
||||||
|
void moveSingleStackCreature(bool leftToRight, SlotID sourceSlot, bool forceEmptySlotTarget);
|
||||||
void swapArtifacts(bool equipped, bool baclpack);
|
void swapArtifacts(bool equipped, bool baclpack);
|
||||||
void moveArtifacts(bool leftToRight, bool equipped, bool baclpack);
|
void moveArtifacts(bool leftToRight, bool equipped, bool baclpack);
|
||||||
|
|
||||||
|
@ -32,6 +32,16 @@
|
|||||||
#include "../../lib/texts/CGeneralTextHandler.h" //for Unicode related stuff
|
#include "../../lib/texts/CGeneralTextHandler.h" //for Unicode related stuff
|
||||||
#include "../../lib/CRandomGenerator.h"
|
#include "../../lib/CRandomGenerator.h"
|
||||||
|
|
||||||
|
static EImageBlitMode getModeForFlags( uint8_t flags)
|
||||||
|
{
|
||||||
|
if (flags & CCreatureAnim::CREATURE_MODE)
|
||||||
|
return EImageBlitMode::WITH_SHADOW_AND_SELECTION;
|
||||||
|
if (flags & CCreatureAnim::MAP_OBJECT_MODE)
|
||||||
|
return EImageBlitMode::WITH_SHADOW;
|
||||||
|
|
||||||
|
return EImageBlitMode::COLORKEY;
|
||||||
|
}
|
||||||
|
|
||||||
CPicture::CPicture(std::shared_ptr<IImage> image, const Point & position)
|
CPicture::CPicture(std::shared_ptr<IImage> image, const Point & position)
|
||||||
: bg(image)
|
: bg(image)
|
||||||
, needRefresh(false)
|
, needRefresh(false)
|
||||||
@ -195,12 +205,12 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i
|
|||||||
{
|
{
|
||||||
pos.x += x;
|
pos.x += x;
|
||||||
pos.y += y;
|
pos.y += y;
|
||||||
anim = GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY);
|
anim = GH.renderHandler().loadAnimation(name, getModeForFlags(Flags));
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags):
|
CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags):
|
||||||
anim(GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
|
anim(GH.renderHandler().loadAnimation(name, getModeForFlags(Flags))),
|
||||||
frame(Frame),
|
frame(Frame),
|
||||||
group(Group),
|
group(Group),
|
||||||
flags(Flags),
|
flags(Flags),
|
||||||
@ -318,7 +328,7 @@ bool CAnimImage::isPlayerColored() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
|
CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
|
||||||
anim(GH.renderHandler().loadAnimation(name, (Flags & CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
|
anim(GH.renderHandler().loadAnimation(name, getModeForFlags(Flags))),
|
||||||
group(Group),
|
group(Group),
|
||||||
frame(0),
|
frame(0),
|
||||||
first(0),
|
first(0),
|
||||||
|
@ -151,7 +151,8 @@ public:
|
|||||||
BASE=1, //base frame will be blitted before current one
|
BASE=1, //base frame will be blitted before current one
|
||||||
HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
|
HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
|
||||||
VERTICAL_FLIP=4, //TODO: will be displayed rotated
|
VERTICAL_FLIP=4, //TODO: will be displayed rotated
|
||||||
CREATURE_MODE=8, // use alpha channel for images with palette. Required for creatures in battle and map objects
|
CREATURE_MODE=8, // use alpha channel for images with palette. Required for creatures in battle
|
||||||
|
MAP_OBJECT_MODE=16, // use alpha channel for images with palette. Required for map objects
|
||||||
PLAY_ONCE=32 //play animation only once and stop at last frame
|
PLAY_ONCE=32 //play animation only once and stop at last frame
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
|
@ -550,6 +550,15 @@ CreatureTooltip::CreatureTooltip(Point pos, const CGCreature * creature)
|
|||||||
tooltipTextbox = std::make_shared<CTextBox>(textContent, Rect(15, 95, 230, 150), 0, FONT_SMALL, ETextAlignment::TOPCENTER, Colors::WHITE);
|
tooltipTextbox = std::make_shared<CTextBox>(textContent, Rect(15, 95, 230, 150), 0, FONT_SMALL, ETextAlignment::TOPCENTER, Colors::WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreatureTooltip::show(Canvas & to)
|
||||||
|
{
|
||||||
|
// fixes scrolling of textbox (#5076)
|
||||||
|
setRedrawParent(true);
|
||||||
|
redraw();
|
||||||
|
|
||||||
|
CIntObject::show(to);
|
||||||
|
}
|
||||||
|
|
||||||
void MoraleLuckBox::set(const AFactionMember * node)
|
void MoraleLuckBox::set(const AFactionMember * node)
|
||||||
{
|
{
|
||||||
OBJECT_CONSTRUCTION;
|
OBJECT_CONSTRUCTION;
|
||||||
@ -615,9 +624,9 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
|||||||
imageName = morale ? "IMRL42" : "ILCK42";
|
imageName = morale ? "IMRL42" : "ILCK42";
|
||||||
|
|
||||||
image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), *component.value + 3);
|
image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), *component.value + 3);
|
||||||
image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2));//center icon
|
image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2)); //center icon
|
||||||
if(settings["general"]["enableUiEnhancements"].Bool())
|
if(settings["general"]["enableUiEnhancements"].Bool())
|
||||||
label = std::make_shared<CLabel>(small ? 30 : 42, small ? 20 : 38, EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(modifierList->totalValue()));
|
label = std::make_shared<CLabel>((image->pos.topLeft() - pos.topLeft()).x + (small ? 28 : 40), (image->pos.topLeft() - pos.topLeft()).y + (small ? 20 : 38), EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(modifierList->totalValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small)
|
MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small)
|
||||||
|
@ -166,6 +166,7 @@ class CreatureTooltip : public CIntObject
|
|||||||
std::shared_ptr<CAnimImage> creatureImage;
|
std::shared_ptr<CAnimImage> creatureImage;
|
||||||
std::shared_ptr<CTextBox> tooltipTextbox;
|
std::shared_ptr<CTextBox> tooltipTextbox;
|
||||||
|
|
||||||
|
void show(Canvas & to) override;
|
||||||
public:
|
public:
|
||||||
CreatureTooltip(Point pos, const CGCreature * creature);
|
CreatureTooltip(Point pos, const CGCreature * creature);
|
||||||
};
|
};
|
||||||
|
@ -168,6 +168,11 @@ std::shared_ptr<CIntObject> CListBox::getItem(size_t which)
|
|||||||
return std::shared_ptr<CIntObject>();
|
return std::shared_ptr<CIntObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CSlider> CListBox::getSlider()
|
||||||
|
{
|
||||||
|
return slider;
|
||||||
|
}
|
||||||
|
|
||||||
size_t CListBox::getIndexOf(std::shared_ptr<CIntObject> item)
|
size_t CListBox::getIndexOf(std::shared_ptr<CIntObject> item)
|
||||||
{
|
{
|
||||||
size_t i=first;
|
size_t i=first;
|
||||||
|
@ -91,6 +91,8 @@ public:
|
|||||||
//return item with index which or null if not present
|
//return item with index which or null if not present
|
||||||
std::shared_ptr<CIntObject> getItem(size_t which);
|
std::shared_ptr<CIntObject> getItem(size_t which);
|
||||||
|
|
||||||
|
std::shared_ptr<CSlider> getSlider();
|
||||||
|
|
||||||
//return currently active items
|
//return currently active items
|
||||||
const std::list<std::shared_ptr<CIntObject>> & getItems();
|
const std::list<std::shared_ptr<CIntObject>> & getItems();
|
||||||
|
|
||||||
|
@ -101,6 +101,9 @@ std::shared_ptr<RadialMenuItem> RadialMenu::findNearestItem(const Point & cursor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bestDistance > 2 * requiredDistanceFromCenter)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
assert(bestItem);
|
assert(bestItem);
|
||||||
return bestItem;
|
return bestItem;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "../CGameInfo.h"
|
#include "../CGameInfo.h"
|
||||||
#include "../CPlayerInterface.h"
|
#include "../CPlayerInterface.h"
|
||||||
#include "../PlayerLocalState.h"
|
#include "../PlayerLocalState.h"
|
||||||
|
#include "../eventsSDL/InputHandler.h"
|
||||||
#include "../gui/CGuiHandler.h"
|
#include "../gui/CGuiHandler.h"
|
||||||
#include "../gui/Shortcut.h"
|
#include "../gui/Shortcut.h"
|
||||||
#include "../gui/WindowHandler.h"
|
#include "../gui/WindowHandler.h"
|
||||||
@ -51,6 +52,7 @@
|
|||||||
#include "../../lib/IGameSettings.h"
|
#include "../../lib/IGameSettings.h"
|
||||||
#include "../../lib/spells/CSpellHandler.h"
|
#include "../../lib/spells/CSpellHandler.h"
|
||||||
#include "../../lib/GameConstants.h"
|
#include "../../lib/GameConstants.h"
|
||||||
|
#include "../../lib/gameState/UpgradeInfo.h"
|
||||||
#include "../../lib/StartInfo.h"
|
#include "../../lib/StartInfo.h"
|
||||||
#include "../../lib/campaign/CampaignState.h"
|
#include "../../lib/campaign/CampaignState.h"
|
||||||
#include "../../lib/entities/building/CBuilding.h"
|
#include "../../lib/entities/building/CBuilding.h"
|
||||||
@ -175,7 +177,7 @@ void CBuildingRect::show(Canvas & to)
|
|||||||
{
|
{
|
||||||
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT;
|
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT;
|
||||||
|
|
||||||
bool showTextOverlay = GH.isKeyboardAltDown();
|
bool showTextOverlay = GH.isKeyboardAltDown() || GH.input().getNumTouchFingers() == 2;
|
||||||
|
|
||||||
if(stateTimeCounter < BUILDING_APPEAR_TIMEPOINT)
|
if(stateTimeCounter < BUILDING_APPEAR_TIMEPOINT)
|
||||||
{
|
{
|
||||||
@ -337,17 +339,92 @@ CHeroGSlot::CHeroGSlot(int x, int y, int updown, const CGHeroInstance * h, HeroS
|
|||||||
|
|
||||||
CHeroGSlot::~CHeroGSlot() = default;
|
CHeroGSlot::~CHeroGSlot() = default;
|
||||||
|
|
||||||
|
auto CHeroGSlot::getUpgradableSlots(const CArmedInstance *obj)
|
||||||
|
{
|
||||||
|
struct result { bool isCreatureUpgradePossible; bool canAffordAny; bool canAffordAll; TResources totalCosts; std::vector<std::pair<SlotID, UpgradeInfo>> upgradeInfos; };
|
||||||
|
|
||||||
|
auto slots = std::map<SlotID, const CStackInstance*>(obj->Slots().begin(), obj->Slots().end());
|
||||||
|
std::vector<std::pair<SlotID, UpgradeInfo>> upgradeInfos;
|
||||||
|
for(auto & slot : slots)
|
||||||
|
{
|
||||||
|
auto upgradeInfo = std::make_pair(slot.first, UpgradeInfo(slot.second->getCreatureID()));
|
||||||
|
LOCPLINT->cb->fillUpgradeInfo(slot.second->armyObj, slot.first, upgradeInfo.second);
|
||||||
|
bool canUpgrade = obj->tempOwner == LOCPLINT->playerID && upgradeInfo.second.canUpgrade();
|
||||||
|
if(canUpgrade)
|
||||||
|
upgradeInfos.push_back(upgradeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(upgradeInfos.begin(), upgradeInfos.end(), [&](const std::pair<SlotID, UpgradeInfo> & lhs, const std::pair<SlotID, UpgradeInfo> & rhs) {
|
||||||
|
return lhs.second.oldID.toCreature()->getLevel() > rhs.second.oldID.toCreature()->getLevel();
|
||||||
|
});
|
||||||
|
bool creaturesToUpgrade = static_cast<bool>(upgradeInfos.size());
|
||||||
|
|
||||||
|
TResources costs = TResources();
|
||||||
|
std::vector<SlotID> slotInfosToDelete;
|
||||||
|
for(auto & upgradeInfo : upgradeInfos)
|
||||||
|
{
|
||||||
|
TResources upgradeCosts = upgradeInfo.second.getUpgradeCosts() * slots[upgradeInfo.first]->getCount();
|
||||||
|
if(LOCPLINT->cb->getResourceAmount().canAfford(costs + upgradeCosts))
|
||||||
|
costs += upgradeCosts;
|
||||||
|
else
|
||||||
|
slotInfosToDelete.push_back(upgradeInfo.first);
|
||||||
|
}
|
||||||
|
upgradeInfos.erase(std::remove_if(upgradeInfos.begin(), upgradeInfos.end(), [&slotInfosToDelete](const auto& item) {
|
||||||
|
return std::count(slotInfosToDelete.begin(), slotInfosToDelete.end(), item.first);
|
||||||
|
}), upgradeInfos.end());
|
||||||
|
|
||||||
|
return result { creaturesToUpgrade, static_cast<bool>(upgradeInfos.size()), !slotInfosToDelete.size(), costs, upgradeInfos };
|
||||||
|
}
|
||||||
|
|
||||||
void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
|
void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
|
||||||
{
|
{
|
||||||
if(!on)
|
if(!on)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!hero)
|
const CArmedInstance *obj = hero;
|
||||||
|
if(upg == 0 && !obj)
|
||||||
|
obj = owner->town->getUpperArmy();
|
||||||
|
if(!obj)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto upgradableSlots = getUpgradableSlots(obj);
|
||||||
|
auto upgradeAll = [upgradableSlots, obj](){
|
||||||
|
if(!upgradableSlots.canAffordAny)
|
||||||
|
{
|
||||||
|
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.townWindow.upgradeAll.notUpgradable"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<CComponent>> resComps;
|
||||||
|
for(TResources::nziterator i(upgradableSlots.totalCosts); i.valid(); i++)
|
||||||
|
resComps.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i->resType, i->resVal));
|
||||||
|
resComps.back()->newLine = true;
|
||||||
|
for(auto & upgradeInfo : upgradableSlots.upgradeInfos)
|
||||||
|
resComps.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, upgradeInfo.second.getUpgrade(), obj->Slots().at(upgradeInfo.first)->count));
|
||||||
|
|
||||||
|
std::string textID = upgradableSlots.canAffordAll ? "core.genrltxt.207" : "vcmi.townWindow.upgradeAll.notAllUpgradable";
|
||||||
|
|
||||||
|
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate(textID), [upgradableSlots, obj](){
|
||||||
|
for(auto & upgradeInfo : upgradableSlots.upgradeInfos)
|
||||||
|
LOCPLINT->cb->upgradeCreature(obj, upgradeInfo.first, upgradeInfo.second.getUpgrade());
|
||||||
|
}, nullptr, resComps);
|
||||||
|
};
|
||||||
|
|
||||||
if (!settings["input"]["radialWheelGarrisonSwipe"].Bool())
|
if (!settings["input"]["radialWheelGarrisonSwipe"].Bool())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(!hero)
|
||||||
|
{
|
||||||
|
if(upgradableSlots.isCreatureUpgradePossible)
|
||||||
|
{
|
||||||
|
std::vector<RadialMenuConfig> menuElements = {
|
||||||
|
{ RadialMenuConfig::ITEM_WW, true, "upgradeCreatures", "vcmi.radialWheel.upgradeCreatures", [upgradeAll](){ upgradeAll(); } },
|
||||||
|
};
|
||||||
|
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<CHeroGSlot> other = upg ? owner->garrisonedHero : owner->visitingHero;
|
std::shared_ptr<CHeroGSlot> other = upg ? owner->garrisonedHero : owner->visitingHero;
|
||||||
|
|
||||||
bool twoHeroes = hero && other->hero;
|
bool twoHeroes = hero && other->hero;
|
||||||
@ -360,14 +437,15 @@ void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & f
|
|||||||
{ RadialMenuConfig::ITEM_NE, twoHeroes, "stackSplitDialog", "vcmi.radialWheel.heroSwapArmy", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).swapArmy();} },
|
{ RadialMenuConfig::ITEM_NE, twoHeroes, "stackSplitDialog", "vcmi.radialWheel.heroSwapArmy", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).swapArmy();} },
|
||||||
{ RadialMenuConfig::ITEM_EE, twoHeroes, "tradeHeroes", "vcmi.radialWheel.heroExchange", [heroId, heroOtherId](){LOCPLINT->showHeroExchange(heroId, heroOtherId);} },
|
{ RadialMenuConfig::ITEM_EE, twoHeroes, "tradeHeroes", "vcmi.radialWheel.heroExchange", [heroId, heroOtherId](){LOCPLINT->showHeroExchange(heroId, heroOtherId);} },
|
||||||
{ RadialMenuConfig::ITEM_SW, twoHeroes, "moveArtifacts", "vcmi.radialWheel.heroGetArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).moveArtifacts(false, true, true);} },
|
{ RadialMenuConfig::ITEM_SW, twoHeroes, "moveArtifacts", "vcmi.radialWheel.heroGetArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).moveArtifacts(false, true, true);} },
|
||||||
{ RadialMenuConfig::ITEM_SE, twoHeroes, "swapArtifacts", "vcmi.radialWheel.heroSwapArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).swapArtifacts(true, true);} },
|
{ RadialMenuConfig::ITEM_SE, twoHeroes, "swapArtifacts", "vcmi.radialWheel.heroSwapArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).swapArtifacts(true, true);} }
|
||||||
{ RadialMenuConfig::ITEM_WW, true, "dismissHero", "vcmi.radialWheel.heroDismiss", [this]()
|
|
||||||
{
|
|
||||||
CFunctionList<void()> ony = [=](){ };
|
|
||||||
ony += [=](){ LOCPLINT->cb->dismissHero(hero); };
|
|
||||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], ony, nullptr);
|
|
||||||
} },
|
|
||||||
};
|
};
|
||||||
|
RadialMenuConfig upgradeSlot = { RadialMenuConfig::ITEM_WW, true, "upgradeCreatures", "vcmi.radialWheel.upgradeCreatures", [upgradeAll](){ upgradeAll(); } };
|
||||||
|
RadialMenuConfig dismissSlot = { RadialMenuConfig::ITEM_WW, true, "dismissHero", "vcmi.radialWheel.heroDismiss", [this](){ LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], [=](){ LOCPLINT->cb->dismissHero(hero); }, nullptr); } };
|
||||||
|
|
||||||
|
if(upgradableSlots.isCreatureUpgradePossible)
|
||||||
|
menuElements.push_back(upgradeSlot);
|
||||||
|
else
|
||||||
|
menuElements.push_back(dismissSlot);
|
||||||
|
|
||||||
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
|
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
|
||||||
}
|
}
|
||||||
@ -692,7 +770,7 @@ void CCastleBuildings::show(Canvas & to)
|
|||||||
{
|
{
|
||||||
CIntObject::show(to);
|
CIntObject::show(to);
|
||||||
|
|
||||||
bool showTextOverlay = GH.isKeyboardAltDown();
|
bool showTextOverlay = GH.isKeyboardAltDown() || GH.input().getNumTouchFingers() == 2;
|
||||||
if(showTextOverlay)
|
if(showTextOverlay)
|
||||||
drawOverlays(to, buildings);
|
drawOverlays(to, buildings);
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,8 @@ class CHeroGSlot : public CIntObject
|
|||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
int upg; //0 - up garrison, 1 - down garrison
|
int upg; //0 - up garrison, 1 - down garrison
|
||||||
|
|
||||||
|
auto getUpgradableSlots(const CArmedInstance *obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CHeroGSlot(int x, int y, int updown, const CGHeroInstance *h, HeroSlots * Owner);
|
CHeroGSlot(int x, int y, int updown, const CGHeroInstance *h, HeroSlots * Owner);
|
||||||
~CHeroGSlot();
|
~CHeroGSlot();
|
||||||
|
@ -238,7 +238,7 @@ CStackWindow::ActiveSpellsSection::ActiveSpellsSection(CStackWindow * owner, int
|
|||||||
if (spellBonuses->empty())
|
if (spellBonuses->empty())
|
||||||
throw std::runtime_error("Failed to find effects for spell " + effect.toSpell()->getJsonKey());
|
throw std::runtime_error("Failed to find effects for spell " + effect.toSpell()->getJsonKey());
|
||||||
|
|
||||||
int duration = spellBonuses->front()->duration;
|
int duration = spellBonuses->front()->turnsRemain;
|
||||||
boost::replace_first(spellText, "%d", std::to_string(duration));
|
boost::replace_first(spellText, "%d", std::to_string(duration));
|
||||||
|
|
||||||
spellIcons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
|
spellIcons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
|
||||||
@ -671,7 +671,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
|||||||
expArea->text = parent->generateStackExpDescription();
|
expArea->text = parent->generateStackExpDescription();
|
||||||
}
|
}
|
||||||
expLabel = std::make_shared<CLabel>(
|
expLabel = std::make_shared<CLabel>(
|
||||||
pos.x + 21, pos.y + 52, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
|
pos.x + 21, pos.y + 55, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
|
||||||
TextOperations::formatMetric(stack->experience, 6));
|
TextOperations::formatMetric(stack->experience, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,9 +776,13 @@ CStackWindow::CStackWindow(const CStackInstance * stack, std::function<void()> d
|
|||||||
info->creature = stack->getCreature();
|
info->creature = stack->getCreature();
|
||||||
info->creatureCount = stack->count;
|
info->creatureCount = stack->count;
|
||||||
|
|
||||||
info->upgradeInfo = std::make_optional(UnitView::StackUpgradeInfo(upgradeInfo));
|
if(upgradeInfo.canUpgrade())
|
||||||
|
{
|
||||||
|
info->upgradeInfo = std::make_optional(UnitView::StackUpgradeInfo(upgradeInfo));
|
||||||
|
info->upgradeInfo->callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
info->dismissInfo = std::make_optional(UnitView::StackDismissInfo());
|
info->dismissInfo = std::make_optional(UnitView::StackDismissInfo());
|
||||||
info->upgradeInfo->callback = callback;
|
|
||||||
info->dismissInfo->callback = dismiss;
|
info->dismissInfo->callback = dismiss;
|
||||||
info->owner = dynamic_cast<const CGHeroInstance *> (stack->armyObj);
|
info->owner = dynamic_cast<const CGHeroInstance *> (stack->armyObj);
|
||||||
init();
|
init();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user