Merge remote-tracking branch 'origin/develop' into parellel_rmg
# Conflicts: # lib/rmg/modificators/WaterProxy.cpp
@ -50,7 +50,22 @@ int64_t AttackPossibility::calculateDamageReduce(
|
||||
vstd::amin(damageDealt, defender->getAvailableHealth());
|
||||
|
||||
// FIXME: provide distance info for Jousting bonus
|
||||
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(defender, attacker, 0);
|
||||
auto attackerUnitForMeasurement = attacker;
|
||||
|
||||
if(attackerUnitForMeasurement->isTurret())
|
||||
{
|
||||
auto ourUnits = cb.battleGetUnitsIf([&](const battle::Unit * u) -> bool
|
||||
{
|
||||
return u->unitSide() == attacker->unitSide() && !u->isTurret();
|
||||
});
|
||||
|
||||
if(ourUnits.empty())
|
||||
attackerUnitForMeasurement = defender;
|
||||
else
|
||||
attackerUnitForMeasurement = ourUnits.front();
|
||||
}
|
||||
|
||||
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(defender, attackerUnitForMeasurement, 0);
|
||||
auto enemiesKilled = damageDealt / defender->getMaxHealth() + (damageDealt % defender->getMaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
|
||||
auto enemyDamage = averageDmg(enemyDamageBeforeAttack.damage);
|
||||
auto damagePerEnemy = enemyDamage / (double)defender->getCount();
|
||||
|
@ -386,9 +386,13 @@ int64_t BattleExchangeEvaluator::calculateExchange(
|
||||
|
||||
for(auto unit : exchangeUnits)
|
||||
{
|
||||
if(unit->isTurret())
|
||||
continue;
|
||||
|
||||
bool isOur = cb->battleMatchOwner(ap.attack.attacker, unit, true);
|
||||
auto & attackerQueue = isOur ? ourStacks : enemyStacks;
|
||||
|
||||
|
||||
if(!vstd::contains(attackerQueue, unit))
|
||||
{
|
||||
attackerQueue.push_back(unit);
|
||||
@ -593,6 +597,9 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
|
||||
|
||||
for(const battle::Unit * unit : turnQueue)
|
||||
{
|
||||
if(unit->isTurret())
|
||||
continue;
|
||||
|
||||
if(turnBattle.battleCanShoot(unit))
|
||||
{
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
|
@ -255,7 +255,7 @@ bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, Pla
|
||||
{
|
||||
auto quest = dynamic_cast<const CGKeys *>(obj);
|
||||
|
||||
if(quest->passableFor(playerColor))
|
||||
if(quest->wasMyColorVisited(playerColor))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
|
||||
if(blockerObject)
|
||||
{
|
||||
blockers.push_back(blockerObject);
|
||||
blockers.insert(blockers.begin(), blockerObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,27 @@ AISharedStorage::~AISharedStorage()
|
||||
}
|
||||
}
|
||||
|
||||
void AIPathNode::addSpecialAction(std::shared_ptr<const SpecialAction> action)
|
||||
{
|
||||
if(!specialAction)
|
||||
{
|
||||
specialAction = action;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto parts = specialAction->getParts();
|
||||
|
||||
if(parts.empty())
|
||||
{
|
||||
parts.push_back(specialAction);
|
||||
}
|
||||
|
||||
parts.push_back(action);
|
||||
|
||||
specialAction = std::make_shared<CompositeAction>(parts);
|
||||
}
|
||||
}
|
||||
|
||||
AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
|
||||
: sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes)
|
||||
{
|
||||
@ -765,7 +786,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
|
||||
if(exchangeNode->actor->actorAction)
|
||||
{
|
||||
exchangeNode->theNodeBefore = carrier;
|
||||
exchangeNode->specialAction = exchangeNode->actor->actorAction;
|
||||
exchangeNode->addSpecialAction(exchangeNode->actor->actorAction);
|
||||
}
|
||||
|
||||
exchangeNode->chainOther = other;
|
||||
@ -1045,7 +1066,7 @@ struct TowmPortalFinder
|
||||
movementCost);
|
||||
|
||||
node->theNodeBefore = bestNode;
|
||||
node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
|
||||
node->addSpecialAction(std::make_shared<AIPathfinding::TownPortalAction>(targetTown));
|
||||
}
|
||||
|
||||
return nodeOptional;
|
||||
|
@ -55,6 +55,8 @@ struct AIPathNode : public CGPathNode
|
||||
return accessible == CGPathNode::EAccessibility::NOT_SET
|
||||
|| accessible == CGPathNode::EAccessibility::BLOCKED;
|
||||
}
|
||||
|
||||
void addSpecialAction(std::shared_ptr<const SpecialAction> action);
|
||||
};
|
||||
|
||||
struct AIPathNodeInfo
|
||||
|
@ -27,4 +27,67 @@ void SpecialAction::execute(const CGHeroInstance * hero) const
|
||||
throw cannotFulfillGoalException("Can not execute " + toString());
|
||||
}
|
||||
|
||||
bool CompositeAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
for(auto part : parts)
|
||||
{
|
||||
if(!part->canAct(source)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Goals::TSubgoal CompositeAction::decompose(const CGHeroInstance * hero) const
|
||||
{
|
||||
for(auto part : parts)
|
||||
{
|
||||
auto goal = part->decompose(hero);
|
||||
|
||||
if(!goal->invalid()) return goal;
|
||||
}
|
||||
|
||||
return SpecialAction::decompose(hero);
|
||||
}
|
||||
|
||||
void CompositeAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
for(auto part : parts)
|
||||
{
|
||||
part->execute(hero);
|
||||
}
|
||||
}
|
||||
|
||||
void CompositeAction::applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstNode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
for(auto part : parts)
|
||||
{
|
||||
part->applyOnDestination(hero, destination, source, dstNode, srcNode);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CompositeAction::toString() const
|
||||
{
|
||||
std::string result = "";
|
||||
|
||||
for(auto part : parts)
|
||||
{
|
||||
result += ", " + part->toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const CGObjectInstance * CompositeAction::targetObject() const
|
||||
{
|
||||
if(parts.empty())
|
||||
return nullptr;
|
||||
|
||||
return parts.front()->targetObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
AIPathNode * dstNode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
}
|
||||
@ -44,6 +44,38 @@ public:
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
virtual const CGObjectInstance * targetObject() const { return nullptr; }
|
||||
|
||||
virtual std::vector<std::shared_ptr<const SpecialAction>> getParts() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class CompositeAction : public SpecialAction
|
||||
{
|
||||
private:
|
||||
std::vector<std::shared_ptr<const SpecialAction>> parts;
|
||||
|
||||
public:
|
||||
CompositeAction(std::vector<std::shared_ptr<const SpecialAction>> parts) : parts(parts) {}
|
||||
|
||||
bool canAct(const AIPathNode * source) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
std::string toString() const override;
|
||||
const CGObjectInstance * targetObject() const override;
|
||||
Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
||||
std::vector<std::shared_ptr<const SpecialAction>> getParts() const override
|
||||
{
|
||||
return parts;
|
||||
}
|
||||
|
||||
void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstNode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ namespace AIPathfinding
|
||||
|
||||
if(boatNode->action == CGPathNode::UNKNOWN)
|
||||
{
|
||||
boatNode->specialAction = virtualBoat;
|
||||
boatNode->addSpecialAction(virtualBoat);
|
||||
destination.blocked = false;
|
||||
destination.action = CGPathNode::ENodeAction::EMBARK;
|
||||
destination.node = boatNode;
|
||||
|
@ -157,7 +157,7 @@ namespace AIPathfinding
|
||||
|
||||
nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
|
||||
{
|
||||
node->specialAction.reset(new QuestAction(questAction));
|
||||
node->addSpecialAction(std::make_shared<QuestAction>(questAction));
|
||||
});
|
||||
}
|
||||
|
||||
@ -279,6 +279,11 @@ namespace AIPathfinding
|
||||
|
||||
if(loss < actualArmyValue)
|
||||
{
|
||||
if(destNode->specialAction)
|
||||
{
|
||||
battleNode->specialAction = destNode->specialAction;
|
||||
}
|
||||
|
||||
destination.node = battleNode;
|
||||
nodeStorage->commit(destination, source);
|
||||
|
||||
@ -288,7 +293,7 @@ namespace AIPathfinding
|
||||
|
||||
AIPreviousNodeRule(nodeStorage).process(source, destination, pathfinderConfig, pathfinderHelper);
|
||||
|
||||
battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
|
||||
battleNode->addSpecialAction(std::make_shared<BattleAction>(destination.coord));
|
||||
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace(
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 747 B |
@ -201,8 +201,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}%几率造成双倍基础伤害",
|
||||
"core.bonus.DRAGON_NATURE.name": "龙",
|
||||
"core.bonus.DRAGON_NATURE.description": "生物拥有龙的特性",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "魔法直伤免疫",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "免疫直接造成伤害的魔法",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "土系免疫",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "免疫所有土系魔法",
|
||||
"core.bonus.ENCHANTER.name": "强化师",
|
||||
|
@ -49,10 +49,13 @@
|
||||
"vcmi.systemOptions.fullscreenButton.hover" : "Fullscreen",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\nIf selected, VCMI will run in fullscreen mode, otherwise it will run in windowed mode",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "Resolution: %wx%h",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Select Resolution}\n\nChange in-game screen resolution. A game restart is required to apply the new resolution.",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Select Resolution}\n\nChange in-game screen resolution.",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "Select Resolution",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "Change in-game screen resolution.",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{Fullscreen}\n\nFailed to switch to fullscreen mode! The current resolution is not supported by the display!",
|
||||
"vcmi.systemOptions.scalingButton.hover" : "Interface Scaling: %p%",
|
||||
"vcmi.systemOptions.scalingButton.help" : "{Interface Scaling}\n\nChanges scaling of in-game interface",
|
||||
"vcmi.systemOptions.scalingMenu.hover" : "Select Interface Scaling",
|
||||
"vcmi.systemOptions.scalingMenu.help" : "Change in-game interface scaling.",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "Show FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
|
||||
|
||||
@ -201,8 +204,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Has a ${val}% chance of dealing double base damage when attacking",
|
||||
"core.bonus.DRAGON_NATURE.name": "Dragon",
|
||||
"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direct Damage Immunity",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immune to direct damage spells",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Earth immunity",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Immune to all spells from the school of Earth magic",
|
||||
"core.bonus.ENCHANTER.name": "Enchanter",
|
||||
|
@ -196,8 +196,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% Chance auf doppelten Schaden",
|
||||
"core.bonus.DRAGON_NATURE.name": "Drache",
|
||||
"core.bonus.DRAGON_NATURE.description": "Kreatur hat eine Drachennatur",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direkte Schadensimmunität",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immun gegen Direktschadenszauber",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Erdimmunität",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
|
||||
"core.bonus.ENCHANTER.name": "Verzauberer",
|
||||
|
@ -175,8 +175,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% szans na podwójne obrażenia",
|
||||
"core.bonus.DRAGON_NATURE.name": "Smok",
|
||||
"core.bonus.DRAGON_NATURE.description": "Stworzenie posiada smoczą naturę",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Odporność na bezpośrednie obrażenia",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Odporny na czary zadające bezpośrednie obrażenia",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Odporność na ziemię",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Odporny na wszystkie czary szkoły ziemi",
|
||||
"core.bonus.ENCHANTER.name": "Czarodziej",
|
||||
|
@ -199,8 +199,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Шанс ${val}% на двойной урон",
|
||||
"core.bonus.DRAGON_NATURE.name": "Дракон",
|
||||
"core.bonus.DRAGON_NATURE.description": "Это существо - дракон",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Иммунитет к магии прямого урона",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Заклинания прямого урона не могут быть применены",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Иммунитет к земле",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Земли",
|
||||
"core.bonus.ENCHANTER.name": "Заклинатель (массовое)",
|
||||
|
@ -1,296 +1,314 @@
|
||||
{
|
||||
"vcmi.adventureMap.monsterThreat.title" : "\n\n Amenaza: ",
|
||||
"vcmi.adventureMap.monsterThreat.levels.0" : "Sin esfuerzo",
|
||||
"vcmi.adventureMap.monsterThreat.levels.1" : "Muy débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.2" : "Débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.3" : "Un poco más débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.4" : "Igual",
|
||||
"vcmi.adventureMap.monsterThreat.levels.5" : "Un poco más fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.6" : "Fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.7" : "Muy fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.8" : "Desafiante",
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "Abrumador",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "Mortal",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "Imposible",
|
||||
"vcmi.adventureMap.monsterThreat.title" : "\n\n Amenaza: ",
|
||||
"vcmi.adventureMap.monsterThreat.levels.0" : "Sin esfuerzo",
|
||||
"vcmi.adventureMap.monsterThreat.levels.1" : "Muy débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.2" : "Débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.3" : "Un poco más débil",
|
||||
"vcmi.adventureMap.monsterThreat.levels.4" : "Igual",
|
||||
"vcmi.adventureMap.monsterThreat.levels.5" : "Un poco más fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.6" : "Fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.7" : "Muy fuerte",
|
||||
"vcmi.adventureMap.monsterThreat.levels.8" : "Desafiante",
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "Abrumador",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "Mortal",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "Imposible",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "¿Estás seguro de que quieres reiniciar el juego?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "¡No hay mercado disponible!",
|
||||
"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.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.confirmRestartGame" : "¿Estás seguro de que quieres reiniciar el juego?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "¡No hay mercado disponible!",
|
||||
"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.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.server.errors.existingProcess" : "Otro proceso de vcmiserver está en ejecución, por favor termínalo primero",
|
||||
"vcmi.server.errors.modsIncompatibility" : "Mods necesarios para cargar el juego:",
|
||||
"vcmi.server.confirmReconnect" : "¿Conectar a la última sesión?",
|
||||
"vcmi.capitalColors.0" : "Rojo",
|
||||
"vcmi.capitalColors.1" : "Azul",
|
||||
"vcmi.capitalColors.2" : "Marron",
|
||||
"vcmi.capitalColors.3" : "Verde",
|
||||
"vcmi.capitalColors.4" : "Naranja",
|
||||
"vcmi.capitalColors.5" : "Morado",
|
||||
"vcmi.capitalColors.6" : "Turquesa",
|
||||
"vcmi.capitalColors.7" : "Rosa",
|
||||
|
||||
"vcmi.settingsMainWindow.generalTab.hover" : "General",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "Cambiar a la pestaña de opciones generales, que contiene ajustes relacionados con el comportamiento general del juego",
|
||||
"vcmi.settingsMainWindow.battleTab.hover" : "Batalla",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "Cambiar a la pestaña de opciones de batalla, que permite configurar el comportamiento del juego durante las batallas",
|
||||
"vcmi.settingsMainWindow.adventureTab.hover" : "Mapa de Aventuras",
|
||||
"vcmi.settingsMainWindow.adventureTab.help" : "Cambiar a la pestaña de opciones de Mapa de Aventuras - el mapa de aventuras es parte del juego donde puedes mover a tus héroes",
|
||||
"vcmi.server.errors.existingProcess" : "Otro proceso de vcmiserver está en ejecución, por favor termínalo primero",
|
||||
"vcmi.server.errors.modsIncompatibility" : "Mods necesarios para cargar el juego:",
|
||||
"vcmi.server.confirmReconnect" : "¿Conectar a la última sesión?",
|
||||
|
||||
"vcmi.settingsMainWindow.generalTab.hover" : "General",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "Cambiar a la pestaña de opciones generales, que contiene ajustes relacionados con el comportamiento general del juego",
|
||||
"vcmi.settingsMainWindow.battleTab.hover" : "Batalla",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "Cambiar a la pestaña de opciones de batalla, que permite configurar el comportamiento del juego durante las batallas",
|
||||
"vcmi.settingsMainWindow.adventureTab.hover" : "Mapa de Aventuras",
|
||||
"vcmi.settingsMainWindow.adventureTab.help" : "Cambiar a la pestaña de opciones de Mapa de Aventuras - el mapa de aventuras es parte del juego donde puedes mover a tus héroes",
|
||||
|
||||
"vcmi.systemOptions.videoGroup" : "Configuración de vídeo",
|
||||
"vcmi.systemOptions.audioGroup" : "Configuración de audio",
|
||||
"vcmi.systemOptions.otherGroup" : "Otras configuraciones", // actualmente no utilizada
|
||||
"vcmi.systemOptions.townsGroup" : "Pantalla de la ciudad",
|
||||
"vcmi.systemOptions.videoGroup" : "Configuración de vídeo",
|
||||
"vcmi.systemOptions.audioGroup" : "Configuración de audio",
|
||||
"vcmi.systemOptions.otherGroup" : "Otras configuraciones", // actualmente no utilizada
|
||||
"vcmi.systemOptions.townsGroup" : "Pantalla de la ciudad",
|
||||
|
||||
"vcmi.systemOptions.fullscreenButton.hover" : "Pantalla completa",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{Pantalla completa}\n\n Si se selecciona, VCMI se ejecutará en modo de pantalla completa, de lo contrario se ejecutará en ventana",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "Resolución: %wx%h",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Seleccionar resolución}\n\n Cambia la resolución de la pantalla del juego. Se requiere reiniciar el juego para aplicar la nueva resolución.",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "Seleccionar resolución",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "Cambia la resolución de la pantalla del juego.",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{Pantalla completa}\n\n ¡Fallo al cambiar a modo de pantalla completa! ¡La resolución actual no es compatible con la pantalla!",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "Mostrar FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{Mostrar FPS}\n\n Muestra el contador de Frames Por Segundo en la esquina de la ventana del juego.",
|
||||
"vcmi.systemOptions.fullscreenButton.hover" : "Pantalla completa",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{Pantalla completa}\n\n Si se selecciona, VCMI se ejecutará en modo de pantalla completa, de lo contrario se ejecutará en ventana",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "Resolución: %wx%h",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Seleccionar resolución}\n\n Cambia la resolución de la pantalla del juego. Se requiere reiniciar el juego para aplicar la nueva resolución.",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "Seleccionar resolución",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "Cambia la resolución de la pantalla del juego.",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{Pantalla completa}\n\n ¡Fallo al cambiar a modo de pantalla completa! ¡La resolución actual no es compatible con la pantalla!",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "Mostrar FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{Mostrar FPS}\n\n Muestra el contador de Frames Por Segundo en la esquina de la ventana del juego.",
|
||||
|
||||
"vcmi.adventureOptions.numericQuantities.hover": "Cantidades numéricas de criaturas",
|
||||
"vcmi.adventureOptions.numericQuantities.help": "{Cantidades numéricas de criaturas}\n\n Muestra las cantidades aproximadas de criaturas enemigas en formato A-B numérico.",
|
||||
"vcmi.adventureOptions.forceMovementInfo.hover": "Mostrar siempre el coste de movimiento",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help": "{Mostrar siempre el coste de movimiento}\n\n Reemplaza la información predeterminada de la barra de estado con datos de puntos de movimiento sin necesidad de mantener presionado el botón ALT.",
|
||||
"vcmi.adventureOptions.showGrid.hover": "Mostrar cuadrícula",
|
||||
"vcmi.adventureOptions.showGrid.help": "{Mostrar cuadrícula}\n\n Muestra una superposición de cuadrícula que muestra las fronteras entre las casillas del mapa de aventuras.",
|
||||
"vcmi.adventureOptions.mapSwipe.hover": "Deslizamiento de mapa",
|
||||
"vcmi.adventureOptions.mapSwipe.help": "{Deslizamiento de mapa}\n\n Permite el movimiento del mapa mediante el gesto de deslizamiento con el dedo en sistemas con pantalla táctil. En este momento, también se puede acceder mediante el botón izquierdo del mouse.",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.help": "Establece la velocidad de desplazamiento del mapa como muy lenta",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "Establece la velocidad de desplazamiento del mapa como muy rápida",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.help": "Establece la velocidad de desplazamiento del mapa como instantánea.",
|
||||
"vcmi.adventureOptions.infoBarPick.hover" : "Mostrar mensajes en el panel de información",
|
||||
"vcmi.adventureOptions.infoBarPick.help" : "{Mostrar mensajes en el panel de información}\n\nSiempre que sea posible, los mensajes del juego sobre los objetos del mapa que se visiten se mostrarán en el panel de información, en lugar de aparecer en una ventana separada.",
|
||||
"vcmi.adventureOptions.numericQuantities.hover": "Cantidades numéricas de criaturas",
|
||||
"vcmi.adventureOptions.numericQuantities.help": "{Cantidades numéricas de criaturas}\n\n Muestra las cantidades aproximadas de criaturas enemigas en formato A-B numérico.",
|
||||
"vcmi.adventureOptions.forceMovementInfo.hover": "Mostrar siempre el coste de movimiento",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help": "{Mostrar siempre el coste de movimiento}\n\n Reemplaza la información predeterminada de la barra de estado con datos de puntos de movimiento sin necesidad de mantener presionado el botón ALT.",
|
||||
"vcmi.adventureOptions.showGrid.hover": "Mostrar cuadrícula",
|
||||
"vcmi.adventureOptions.showGrid.help": "{Mostrar cuadrícula}\n\n Muestra una superposición de cuadrícula que muestra las fronteras entre las casillas del mapa de aventuras.",
|
||||
"vcmi.adventureOptions.mapSwipe.hover": "Deslizamiento de mapa",
|
||||
"vcmi.adventureOptions.mapSwipe.help": "{Deslizamiento de mapa}\n\n Permite el movimiento del mapa mediante el gesto de deslizamiento con el dedo en sistemas con pantalla táctil. En este momento, también se puede acceder mediante el botón izquierdo del mouse.",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.help": "Establece la velocidad de desplazamiento del mapa como muy lenta",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "Establece la velocidad de desplazamiento del mapa como muy rápida",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.help": "Establece la velocidad de desplazamiento del mapa como instantánea.",
|
||||
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "Mostrar orden de turno de criaturas",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.hover": "APAGADO",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.hover": "PEQUEÑO",
|
||||
"vcmi.battleOptions.queueSizeBigButton.hover": "GRANDE",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.help": "Desactiva completamente la visibilidad del orden de turno de las criaturas en la batalla.",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.help": "Establece el tamaño del orden de turno según la resolución del juego (pequeño cuando se juega con una resolución de pantalla inferior a 700 píxeles de alto, grande en caso contrario).",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.help": "Establece el tamaño del orden de turno como pequeño.",
|
||||
"vcmi.battleOptions.queueSizeBigButton.help": "Establece el tamaño del orden de turno como grande (no compatible si la resolución del juego es inferior a 700 píxeles de alto).",
|
||||
"vcmi.battleOptions.animationsSpeed1.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed5.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed6.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed1.help": "Establece la velocidad de animación como muy lenta.",
|
||||
"vcmi.battleOptions.animationsSpeed5.help": "Establece la velocidad de animación como muy rápida.",
|
||||
"vcmi.battleOptions.animationsSpeed6.help": "Establece la velocidad de animación como instantánea.",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Omitir música de introducción",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Omitir música de introducción}\n\n Omitir la breve música que se reproduce al comienzo de cada batalla antes de que comience la acción. También se puede omitir presionando la tecla ESC.",
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "Mostrar orden de turno de criaturas",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.hover": "APAGADO",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.hover": "PEQUEÑO",
|
||||
"vcmi.battleOptions.queueSizeBigButton.hover": "GRANDE",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.help": "Desactiva completamente la visibilidad del orden de turno de las criaturas en la batalla.",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.help": "Establece el tamaño del orden de turno según la resolución del juego (pequeño cuando se juega con una resolución de pantalla inferior a 700 píxeles de alto, grande en caso contrario).",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.help": "Establece el tamaño del orden de turno como pequeño.",
|
||||
"vcmi.battleOptions.queueSizeBigButton.help": "Establece el tamaño del orden de turno como grande (no compatible si la resolución del juego es inferior a 700 píxeles de alto).",
|
||||
"vcmi.battleOptions.animationsSpeed1.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed5.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed6.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed1.help": "Establece la velocidad de animación como muy lenta.",
|
||||
"vcmi.battleOptions.animationsSpeed5.help": "Establece la velocidad de animación como muy rápida.",
|
||||
"vcmi.battleOptions.animationsSpeed6.help": "Establece la velocidad de animación como instantánea.",
|
||||
"vcmi.battleOptions.touchscreenMode.hover": "Modo pantalla táctil",
|
||||
"vcmi.battleOptions.touchscreenMode.help": "{Modo pantalla táctil}\n\nSi está habilitado, se requiere de un segundo clic para confirmar y ejecutar la acción. Adecuado para dispositivos con pantalla táctil.",
|
||||
"vcmi.battleOptions.movementHighlightOnHover.hover": "Resaltado de movimiento al pasar el ratón",
|
||||
"vcmi.battleOptions.movementHighlightOnHover.help": "{Resaltado de movimiento al pasar el ratón}\n\nResalta el rango de movimiento de la unidad cuando el cursor esta sobre esta.",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Omitir música de introducción",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Omitir música de introducción}\n\n Omitir la breve música que se reproduce al comienzo de cada batalla antes de que comience la acción. También se puede omitir presionando la tecla ESC.",
|
||||
"vcmi.battleWindow.pressKeyToSkipIntro" : "Presiona una tecla para comenzar la batalla inmediatamente.",
|
||||
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover": "Mostrar criaturas disponibles",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help": "{Mostrar criaturas disponibles}\n\n Muestra las criaturas disponibles para comprar en lugar de su crecimiento en el resumen de la ciudad (esquina inferior izquierda).",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover": "Mostrar crecimiento semanal de criaturas",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help": "{Mostrar crecimiento semanal de criaturas}\n\n Muestra el crecimiento semanal de las criaturas en lugar de la cantidad disponible en el resumen de la ciudad (esquina inferior izquierda).",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Información compacta de criaturas de la ciudad",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Información compacta de criaturas de la ciudad}\n\n Información más pequeña de las criaturas de la ciudad en el resumen de la ciudad.",
|
||||
"vcmi.battleWindow.damageEstimation.melee" : "Ataque %CREATURE (%DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.meleeKills" : "Ataque %CREATURE (%DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.ranged" : "Disparo %CREATURE (%SHOTS, %DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.rangedKills" : "Disparo %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.shots" : "%d disparos restantes",
|
||||
"vcmi.battleWindow.damageEstimation.shots.1" : "%d disparos restantes",
|
||||
"vcmi.battleWindow.damageEstimation.damage" : "%d daño",
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "%d daño",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d perecerán",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d perecerán",
|
||||
|
||||
"vcmi.townHall.missingBase": "El edificio base %s debe ser construido primero",
|
||||
"vcmi.townHall.noCreaturesToRecruit": "¡No hay criaturas para reclutar!",
|
||||
"vcmi.townHall.greetingManaVortex": "A medida que te acercas a %s, tu cuerpo se llena de nueva energía. Has duplicado tus puntos de magia normales.",
|
||||
"vcmi.townHall.greetingKnowledge": "Estudias los glifos en %s y obtienes una visión de cómo funcionan varias magias (+1 Conocimiento).",
|
||||
"vcmi.townHall.greetingSpellPower": "%s te enseña nuevas formas de enfocar tus poderes mágicos (+1 Poder).",
|
||||
"vcmi.townHall.greetingExperience": "Una visita a %s te enseña muchas habilidades nuevas (+1000 experiencia).",
|
||||
"vcmi.townHall.greetingAttack": "El tiempo pasado en %s te permite aprender habilidades de combate más efectivas (+1 habilidad de Ataque).",
|
||||
"vcmi.townHall.greetingDefence": "Pasando tiempo en %s, los guerreros experimentados allí te enseñan habilidades defensivas adicionales (+1 Defensa).",
|
||||
"vcmi.townHall.hasNotProduced": "%s no ha producido nada todavía.",
|
||||
"vcmi.townHall.hasProduced": "%s produjo %d %s esta semana.",
|
||||
"vcmi.townHall.greetingCustomBonus": "%s te da +%d %s%s",
|
||||
"vcmi.townHall.greetingCustomUntil": " hasta la próxima batalla.",
|
||||
"vcmi.townHall.greetingInTownMagicWell": "%s ha restaurado tus puntos de magia al máximo.",
|
||||
"vcmi.battleResultsWindow.applyResultsLabel" : "Aplicar resultado de la batalla",
|
||||
|
||||
"vcmi.logicalExpressions.anyOf" : "Cualquiera de los siguientes:",
|
||||
"vcmi.logicalExpressions.allOf" : "Todos los siguientes:",
|
||||
"vcmi.logicalExpressions.noneOf" : "Ninguno de los siguientes:",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover": "Mostrar criaturas disponibles",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help": "{Mostrar criaturas disponibles}\n\n Muestra las criaturas disponibles para comprar en lugar de su crecimiento en el resumen de la ciudad (esquina inferior izquierda).",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover": "Mostrar crecimiento semanal de criaturas",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help": "{Mostrar crecimiento semanal de criaturas}\n\n Muestra el crecimiento semanal de las criaturas en lugar de la cantidad disponible en el resumen de la ciudad (esquina inferior izquierda).",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Información compacta de criaturas de la ciudad",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Información compacta de criaturas de la ciudad}\n\n Información más pequeña de las criaturas de la ciudad en el resumen de la ciudad.",
|
||||
|
||||
"vcmi.heroWindow.openCommander.hover" : "Abrir ventana de comandante",
|
||||
"vcmi.heroWindow.openCommander.help" : "Muestra información sobre el comandante de este héroe",
|
||||
"vcmi.townHall.missingBase": "El edificio base %s debe ser construido primero",
|
||||
"vcmi.townHall.noCreaturesToRecruit": "¡No hay criaturas para reclutar!",
|
||||
"vcmi.townHall.greetingManaVortex": "A medida que te acercas a %s, tu cuerpo se llena de nueva energía. Has duplicado tus puntos de magia normales.",
|
||||
"vcmi.townHall.greetingKnowledge": "Estudias los glifos en %s y obtienes una visión de cómo funcionan varias magias (+1 Conocimiento).",
|
||||
"vcmi.townHall.greetingSpellPower": "%s te enseña nuevas formas de enfocar tus poderes mágicos (+1 Poder).",
|
||||
"vcmi.townHall.greetingExperience": "Una visita a %s te enseña muchas habilidades nuevas (+1000 experiencia).",
|
||||
"vcmi.townHall.greetingAttack": "El tiempo pasado en %s te permite aprender habilidades de combate más efectivas (+1 habilidad de Ataque).",
|
||||
"vcmi.townHall.greetingDefence": "Pasando tiempo en %s, los guerreros experimentados allí te enseñan habilidades defensivas adicionales (+1 Defensa).",
|
||||
"vcmi.townHall.hasNotProduced": "%s no ha producido nada todavía.",
|
||||
"vcmi.townHall.hasProduced": "%s produjo %d %s esta semana.",
|
||||
"vcmi.townHall.greetingCustomBonus": "%s te da +%d %s%s",
|
||||
"vcmi.townHall.greetingCustomUntil": " hasta la próxima batalla.",
|
||||
"vcmi.townHall.greetingInTownMagicWell": "%s ha restaurado tus puntos de magia al máximo.",
|
||||
|
||||
"vcmi.commanderWindow.artifactMessage" : "¿Quieres devolver este artefacto al héroe?",
|
||||
"vcmi.logicalExpressions.anyOf" : "Cualquiera de los siguientes:",
|
||||
"vcmi.logicalExpressions.allOf" : "Todos los siguientes:",
|
||||
"vcmi.logicalExpressions.noneOf" : "Ninguno de los siguientes:",
|
||||
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "Cambiar a vista de bonificaciones",
|
||||
"vcmi.creatureWindow.showBonuses.help" : "Muestra todas las bonificaciones activas del comandante",
|
||||
"vcmi.creatureWindow.showSkills.hover" : "Cambiar a vista de habilidades",
|
||||
"vcmi.creatureWindow.showSkills.help" : "Muestra todas las habilidades aprendidas del comandante",
|
||||
"vcmi.creatureWindow.returnArtifact.hover" : "Devolver artefacto",
|
||||
"vcmi.creatureWindow.returnArtifact.help" : "Usa este botón para devolver un artefacto del almacen al inventario del héroe",
|
||||
"vcmi.heroWindow.openCommander.hover" : "Abrir ventana de comandante",
|
||||
"vcmi.heroWindow.openCommander.help" : "Muestra información sobre el comandante de este héroe",
|
||||
|
||||
"vcmi.commanderWindow.artifactMessage" : "¿Quieres devolver este artefacto al héroe?",
|
||||
|
||||
"vcmi.questLog.hideComplete.hover" : "Ocultar misiones completas",
|
||||
"vcmi.questLog.hideComplete.help" : "Ocultar todas las misiones que ya han sido completadas",
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "Cambiar a vista de bonificaciones",
|
||||
"vcmi.creatureWindow.showBonuses.help" : "Muestra todas las bonificaciones activas del comandante",
|
||||
"vcmi.creatureWindow.showSkills.hover" : "Cambiar a vista de habilidades",
|
||||
"vcmi.creatureWindow.showSkills.help" : "Muestra todas las habilidades aprendidas del comandante",
|
||||
"vcmi.creatureWindow.returnArtifact.hover" : "Devolver artefacto",
|
||||
"vcmi.creatureWindow.returnArtifact.help" : "Usa este botón para devolver un artefacto del almacen al inventario del héroe",
|
||||
|
||||
"vcmi.randomMapTab.widgets.defaultTemplate" : "(predeterminado)",
|
||||
"vcmi.randomMapTab.widgets.templateLabel" : "Plantilla",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Configurar...",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Alineaciones de equipos",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Tipos de caminos",
|
||||
"vcmi.questLog.hideComplete.hover" : "Ocultar misiones completas",
|
||||
"vcmi.questLog.hideComplete.help" : "Ocultar todas las misiones que ya han sido completadas",
|
||||
|
||||
"vcmi.randomMapTab.widgets.defaultTemplate" : "(predeterminado)",
|
||||
"vcmi.randomMapTab.widgets.templateLabel" : "Plantilla",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Configurar...",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Alineaciones de equipos",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Tipos de caminos",
|
||||
|
||||
|
||||
// few strings from WoG used by vcmi
|
||||
"vcmi.stackExperience.description" : "» D e t a l l e s d e E x p e r i e n c i a d e l G r u p o «\n\nTipo de Criatura ................ : %s\nRango de Experiencia ............ : %s (%i)\nPuntos de Experiencia ............ : %i\nPuntos de Experiencia para el\nSiguiente Rango ............... : %i\nExperiencia Máxima por Batalla .. : %i%% (%i)\nNúmero de Criaturas en el grupo .. : %i\nMáximo de Nuevos Reclutas sin\nPerder el Rango Actual ......... : %i\nMultiplicador de Experiencia .... : %.2f\nMultiplicador de Actualización .. : %.2f\nExperiencia después del Rango 10 : %i\nMáximo de Nuevos Reclutas para\nMantener el Rango 10 si\nEstá en la Experiencia Máxima : %i",
|
||||
"vcmi.stackExperience.rank.0" : "Básico",
|
||||
"vcmi.stackExperience.rank.1" : "Novato",
|
||||
"vcmi.stackExperience.rank.2" : "Entrenado",
|
||||
"vcmi.stackExperience.rank.3" : "Hábil",
|
||||
"vcmi.stackExperience.rank.4" : "Probado",
|
||||
"vcmi.stackExperience.rank.5" : "Veterano",
|
||||
"vcmi.stackExperience.rank.6" : "Experto",
|
||||
"vcmi.stackExperience.rank.7" : "Experto Superior",
|
||||
"vcmi.stackExperience.rank.8" : "Élite",
|
||||
"vcmi.stackExperience.rank.9" : "Maestro",
|
||||
"vcmi.stackExperience.rank.10" : "As",
|
||||
"vcmi.stackExperience.description" : "» D e t a l l e s d e E x p e r i e n c i a d e l G r u p o «\n\nTipo de Criatura ................ : %s\nRango de Experiencia ............ : %s (%i)\nPuntos de Experiencia ............ : %i\nPuntos de Experiencia para el\nSiguiente Rango ............... : %i\nExperiencia Máxima por Batalla .. : %i%% (%i)\nNúmero de Criaturas en el grupo .. : %i\nMáximo de Nuevos Reclutas sin\nPerder el Rango Actual ......... : %i\nMultiplicador de Experiencia .... : %.2f\nMultiplicador de Actualización .. : %.2f\nExperiencia después del Rango 10 : %i\nMáximo de Nuevos Reclutas para\nMantener el Rango 10 si\nEstá en la Experiencia Máxima : %i",
|
||||
"vcmi.stackExperience.rank.0" : "Básico",
|
||||
"vcmi.stackExperience.rank.1" : "Novato",
|
||||
"vcmi.stackExperience.rank.2" : "Entrenado",
|
||||
"vcmi.stackExperience.rank.3" : "Hábil",
|
||||
"vcmi.stackExperience.rank.4" : "Probado",
|
||||
"vcmi.stackExperience.rank.5" : "Veterano",
|
||||
"vcmi.stackExperience.rank.6" : "Experto",
|
||||
"vcmi.stackExperience.rank.7" : "Experto Superior",
|
||||
"vcmi.stackExperience.rank.8" : "Élite",
|
||||
"vcmi.stackExperience.rank.9" : "Maestro",
|
||||
"vcmi.stackExperience.rank.10" : "As",
|
||||
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "Doble Ataque",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "Ataca dos veces",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name": "Contraataques adicionales",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.description": "Puede contraatacar ${val} veces adicionales",
|
||||
"core.bonus.AIR_IMMUNITY.name": "Inmunidad al Aire",
|
||||
"core.bonus.AIR_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de Aire",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Ataque en todas las direcciones",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Ataca a todos los enemigos adyacentes",
|
||||
"core.bonus.BLOCKS_RETALIATION.name": "Sin contraataque",
|
||||
"core.bonus.BLOCKS_RETALIATION.description": "El enemigo no puede contraatacar",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Sin contraataque a distancia",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "El enemigo no puede contraatacar disparando",
|
||||
"core.bonus.CATAPULT.name": "Catapulta",
|
||||
"core.bonus.CATAPULT.description": "Ataca a las paredes de asedio",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reducir coste del conjuro (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduce el coste del conjuro para el héroe",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Disminuir efecto mágico (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Aumenta el coste de los hechizos del enemigo",
|
||||
"core.bonus.CHARGE_IMMUNITY.name": "Inmunidad a la Carga",
|
||||
"core.bonus.CHARGE_IMMUNITY.description": "Inmune al ataque de carga del campeón",
|
||||
"core.bonus.DARKNESS.name": "Cobertura de Oscuridad",
|
||||
"core.bonus.DARKNESS.description": "Añade un radio de oscuridad de ${val}",
|
||||
"core.bonus.DEATH_STARE.name": "Mirada Mortal (${val}%)",
|
||||
"core.bonus.DEATH_STARE.description": "Tiene ${val}% de probabilidad de matar a una criatura",
|
||||
"core.bonus.DEFENSIVE_STANCE.name": "Bonificación de Defensa",
|
||||
"core.bonus.DEFENSIVE_STANCE.description": "+${val} de Defensa al defender",
|
||||
"core.bonus.DESTRUCTION.name": "Destrucción",
|
||||
"core.bonus.DESTRUCTION.description": "Tiene ${val}% de probabilidad de matar unidades extra después del ataque",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Golpe Mortal",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% de probabilidad de doble daño",
|
||||
"core.bonus.DRAGON_NATURE.name": "Dragón",
|
||||
"core.bonus.DRAGON_NATURE.description": "La criatura tiene la naturaleza de dragón",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Inmunidad al Daño Directo",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Inmune a hechizos de daño directo",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Inmunidad a la Tierra",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de tierra",
|
||||
"core.bonus.ENCHANTER.name": "Encantador",
|
||||
"core.bonus.ENCHANTER.description": "Puede lanzar ${subtype.spell} masivo cada turno",
|
||||
"core.bonus.ENCHANTED.name": "Encantado",
|
||||
"core.bonus.ENCHANTED.description": "Afectado por el hechizo permanente ${subtype.spell}",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignorar Defensa (${val}%)",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignora una parte de la defensa al atacar",
|
||||
"core.bonus.FIRE_IMMUNITY.name": "Inmunidad al Fuego",
|
||||
"core.bonus.FIRE_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de fuego",
|
||||
"core.bonus.FIRE_SHIELD.name": "Escudo de Fuego (${val}%)",
|
||||
"core.bonus.FIRE_SHIELD.description": "Refleja una parte del daño cuerpo a cuerpo",
|
||||
"core.bonus.FIRST_STRIKE.name": "Primer Ataque",
|
||||
"core.bonus.FIRST_STRIKE.description": "Esta criatura ataca primero en lugar de contraatacar",
|
||||
"core.bonus.FEAR.name": "Miedo",
|
||||
"core.bonus.FEAR.description": "Causa miedo a un grupo enemigo",
|
||||
"core.bonus.FEARLESS.name": "Inmune al miedo",
|
||||
"core.bonus.FEARLESS.description": "Inmune a la habilidad de miedo",
|
||||
"core.bonus.FLYING.name": "Volar",
|
||||
"core.bonus.FLYING.description": "Puede volar (ignora obstáculos)",
|
||||
"core.bonus.FREE_SHOOTING.name": "Disparo cercano",
|
||||
"core.bonus.FREE_SHOOTING.description": "Puede disparar en combate cercano",
|
||||
"core.bonus.FULL_HP_REGENERATION.name": "Regeneración completa",
|
||||
"core.bonus.FULL_HP_REGENERATION.description": "Puede regenerar hasta la salud completa",
|
||||
"core.bonus.GARGOYLE.name": "Gárgola",
|
||||
"core.bonus.GARGOYLE.description": "No puede ser resucitado o curado",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reducir daño (${val}%)",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduce el daño físico de ataques a distancia o cuerpo a cuerpo",
|
||||
"core.bonus.HATE.name": "Odio a ${subtype.creature}",
|
||||
"core.bonus.HATE.description": "Hace un ${val}% más de daño",
|
||||
"core.bonus.HEALER.name": "Curador",
|
||||
"core.bonus.HEALER.description": "Cura unidades aliadas",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneración",
|
||||
"core.bonus.HP_REGENERATION.description": "Cura ${val} puntos de vida cada ronda",
|
||||
"core.bonus.JOUSTING.name": "Carga de campeón",
|
||||
"core.bonus.JOUSTING.description": "+5% de daño por hexágono recorrido",
|
||||
"core.bonus.KING1.name": "Rey 1",
|
||||
"core.bonus.KING1.description": "Vulnerable al SLAYER básico",
|
||||
"core.bonus.KING2.name": "Rey 2",
|
||||
"core.bonus.KING2.description": "Vulnerable al SLAYER avanzado",
|
||||
"core.bonus.KING3.name": "Rey 3",
|
||||
"core.bonus.KING3.description":"Vulnerable al SLAYER experto",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Inmunidad a hechizos 1-${val}",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Inmune a hechizos de nivel 1-${val}",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Rango de disparo limitado",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "No puede disparar a objetivos más allá de ${val} hexágonos",
|
||||
"core.bonus.LIFE_DRAIN.name": "Drenaje de vida (${val}%)",
|
||||
"core.bonus.LIFE_DRAIN.description": "Drena ${val}% del daño causado",
|
||||
"core.bonus.MANA_CHANNELING.name": "Canalización mágica ${val}%",
|
||||
"core.bonus.MANA_CHANNELING.description": "Tu héroe recibe el maná gastado por el enemigo",
|
||||
"core.bonus.MANA_DRAIN.name": "Drenaje de maná",
|
||||
"core.bonus.MANA_DRAIN.description": "Drena ${val} de maná cada turno",
|
||||
"core.bonus.MAGIC_MIRROR.name": "Espejo mágico (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "Tiene una probabilidad del ${val}% de redirigir un hechizo ofensivo al enemigo",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "Resistencia mágica (${MR}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "Tiene una probabilidad del ${MR}% de resistir el hechizo del enemigo",
|
||||
"core.bonus.MIND_IMMUNITY.name": "Inmunidad a hechizos mentales",
|
||||
"core.bonus.MIND_IMMUNITY.description": "Inmune a hechizos de tipo mental",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "Sin penalización por distancia",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "Causa el daño completo desde cualquier distancia",
|
||||
"core.bonus.NO_MELEE_PENALTY.name": "Sin penalización de melé",
|
||||
"core.bonus.NO_MELEE_PENALTY.description": "La criatura no tiene penalización en el combate cuerpo a cuerpo",
|
||||
"core.bonus.NO_MORALE.name": "Moral neutral",
|
||||
"core.bonus.NO_MORALE.description": "La criatura es inmune a los efectos de la moral",
|
||||
"core.bonus.NO_WALL_PENALTY.name": "Sin penalización por muros",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "Daño completo durante el asedio",
|
||||
"core.bonus.NON_LIVING.name": "No vivo",
|
||||
"core.bonus.NON_LIVING.description": "Inmunidad a muchos efectos",
|
||||
"core.bonus.RANDOM_SPELLCASTER.name": "Lanzador de hechizos aleatorio",
|
||||
"core.bonus.RANDOM_SPELLCASTER.description": "Puede lanzar hechizos aleatorios",
|
||||
"core.bonus.RANGED_RETALIATION.name": "Contraataque a distancia",
|
||||
"core.bonus.RANGED_RETALIATION.description": "Puede realizar un contraataque a distancia",
|
||||
"core.bonus.RECEPTIVE.name": "Receptivo",
|
||||
"core.bonus.RECEPTIVE.description": "No tiene inmunidad a hechizos amistosos",
|
||||
"core.bonus.REBIRTH.name": "Renacimiento (${val}%)",
|
||||
"core.bonus.REBIRTH.description": "El ${val}% del grupo resucitará después de la muerte",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "Atacar y volver",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "Regresa después de un ataque cuerpo a cuerpo",
|
||||
"core.bonus.SHOOTER.name": "A distancia",
|
||||
"core.bonus.SHOOTER.description": "La criatura puede disparar",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Dispara en todas direcciones",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Los ataques a distancia de esta criatura impactan a todos los objetivos en un área pequeña",
|
||||
"core.bonus.SOUL_STEAL.name": "Roba almas",
|
||||
"core.bonus.SOUL_STEAL.description": "Gana ${val} nuevas criaturas por cada enemigo eliminado",
|
||||
"core.bonus.SPELLCASTER.name": "Lanzador de hechizos",
|
||||
"core.bonus.SPELLCASTER.description": "Puede lanzar ${subtype.spell}",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.name": "Lanzar después del ataque",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% de probabilidad de lanzar ${subtype.spell} después del ataque",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.name": "Lanzar antes del ataque",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% de probabilidad de lanzar ${subtype.spell} antes del ataque",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Resistencia a hechizos",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Reduce el daño de los hechizos en un ${val}%.",
|
||||
"core.bonus.SPELL_IMMUNITY.name": "Inmunidad a hechizos",
|
||||
"core.bonus.SPELL_IMMUNITY.description": "Inmune a ${subtype.spell}",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.name": "Ataque similar a hechizo",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.description": "Ataca con ${subtype.spell}",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura de resistencia",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.description": "Las unidades cercanas obtienen una resistencia mágica del ${val}%",
|
||||
"core.bonus.SUMMON_GUARDIANS.name": "Invocar guardianes",
|
||||
"core.bonus.SUMMON_GUARDIANS.description": "Al comienzo de la batalla invoca ${subtype.creature} (${val}%)",
|
||||
"core.bonus.SYNERGY_TARGET.name": "Sinergia",
|
||||
"core.bonus.SYNERGY_TARGET.description": "Esta criatura es vulnerable al efecto de sinergia",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Aliento",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Ataque de aliento (rango de 2 hexágonos)",
|
||||
"core.bonus.THREE_HEADED_ATTACK.name": "Ataque de tres cabezas",
|
||||
"core.bonus.THREE_HEADED_ATTACK.description": "Ataca a tres unidades adyacentes",
|
||||
"core.bonus.TRANSMUTATION.name": "Transmutación",
|
||||
"core.bonus.TRANSMUTATION.description": "${val}% de probabilidad de transformar la unidad atacada en otro tipo",
|
||||
"core.bonus.UNDEAD.name": "No muerto",
|
||||
"core.bonus.UNDEAD.description": "La criatura es un no muerto",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.name": "Retaliaciones ilimitadas",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.description": "Puede realizar un número ilimitado de ataques de represalia",
|
||||
"core.bonus.WATER_IMMUNITY.name": "Inmunidad al agua",
|
||||
"core.bonus.WATER_IMMUNITY.description": "Inmune a todos los hechizos de la escuela del agua",
|
||||
"core.bonus.WIDE_BREATH.name": "Aliento amplio",
|
||||
"core.bonus.WIDE_BREATH.description": "Ataque de aliento amplio (varios hexágonos)"
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "Doble Ataque",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "Ataca dos veces",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name": "Contraataques adicionales",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.description": "Puede contraatacar ${val} veces adicionales",
|
||||
"core.bonus.AIR_IMMUNITY.name": "Inmunidad al Aire",
|
||||
"core.bonus.AIR_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de Aire",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Ataque en todas las direcciones",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Ataca a todos los enemigos adyacentes",
|
||||
"core.bonus.BLOCKS_RETALIATION.name": "Sin contraataque",
|
||||
"core.bonus.BLOCKS_RETALIATION.description": "El enemigo no puede contraatacar",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Sin contraataque a distancia",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "El enemigo no puede contraatacar disparando",
|
||||
"core.bonus.CATAPULT.name": "Catapulta",
|
||||
"core.bonus.CATAPULT.description": "Ataca a las paredes de asedio",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reducir coste del conjuro (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduce el coste del conjuro para el héroe",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Disminuir efecto mágico (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Aumenta el coste de los hechizos del enemigo",
|
||||
"core.bonus.CHARGE_IMMUNITY.name": "Inmunidad a la Carga",
|
||||
"core.bonus.CHARGE_IMMUNITY.description": "Inmune al ataque de carga del campeón",
|
||||
"core.bonus.DARKNESS.name": "Cobertura de Oscuridad",
|
||||
"core.bonus.DARKNESS.description": "Añade un radio de oscuridad de ${val}",
|
||||
"core.bonus.DEATH_STARE.name": "Mirada Mortal (${val}%)",
|
||||
"core.bonus.DEATH_STARE.description": "Tiene ${val}% de probabilidad de matar a una criatura",
|
||||
"core.bonus.DEFENSIVE_STANCE.name": "Bonificación de Defensa",
|
||||
"core.bonus.DEFENSIVE_STANCE.description": "+${val} de Defensa al defender",
|
||||
"core.bonus.DESTRUCTION.name": "Destrucción",
|
||||
"core.bonus.DESTRUCTION.description": "Tiene ${val}% de probabilidad de matar unidades extra después del ataque",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Golpe Mortal",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% de probabilidad de doble daño",
|
||||
"core.bonus.DRAGON_NATURE.name": "Dragón",
|
||||
"core.bonus.DRAGON_NATURE.description": "La criatura tiene la naturaleza de dragón",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "Inmunidad a la Tierra",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de tierra",
|
||||
"core.bonus.ENCHANTER.name": "Encantador",
|
||||
"core.bonus.ENCHANTER.description": "Puede lanzar ${subtype.spell} masivo cada turno",
|
||||
"core.bonus.ENCHANTED.name": "Encantado",
|
||||
"core.bonus.ENCHANTED.description": "Afectado por el hechizo permanente ${subtype.spell}",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignorar Defensa (${val}%)",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignora una parte de la defensa al atacar",
|
||||
"core.bonus.FIRE_IMMUNITY.name": "Inmunidad al Fuego",
|
||||
"core.bonus.FIRE_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de fuego",
|
||||
"core.bonus.FIRE_SHIELD.name": "Escudo de Fuego (${val}%)",
|
||||
"core.bonus.FIRE_SHIELD.description": "Refleja una parte del daño cuerpo a cuerpo",
|
||||
"core.bonus.FIRST_STRIKE.name": "Primer Ataque",
|
||||
"core.bonus.FIRST_STRIKE.description": "Esta criatura ataca primero en lugar de contraatacar",
|
||||
"core.bonus.FEAR.name": "Miedo",
|
||||
"core.bonus.FEAR.description": "Causa miedo a un grupo enemigo",
|
||||
"core.bonus.FEARLESS.name": "Inmune al miedo",
|
||||
"core.bonus.FEARLESS.description": "Inmune a la habilidad de miedo",
|
||||
"core.bonus.FLYING.name": "Volar",
|
||||
"core.bonus.FLYING.description": "Puede volar (ignora obstáculos)",
|
||||
"core.bonus.FREE_SHOOTING.name": "Disparo cercano",
|
||||
"core.bonus.FREE_SHOOTING.description": "Puede disparar en combate cercano",
|
||||
"core.bonus.GARGOYLE.name": "Gárgola",
|
||||
"core.bonus.GARGOYLE.description": "No puede ser resucitado o curado",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reducir daño (${val}%)",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduce el daño físico de ataques a distancia o cuerpo a cuerpo",
|
||||
"core.bonus.HATE.name": "Odio a ${subtype.creature}",
|
||||
"core.bonus.HATE.description": "Hace un ${val}% más de daño",
|
||||
"core.bonus.HEALER.name": "Curador",
|
||||
"core.bonus.HEALER.description": "Cura unidades aliadas",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneración",
|
||||
"core.bonus.HP_REGENERATION.description": "Cura ${val} puntos de vida cada ronda",
|
||||
"core.bonus.JOUSTING.name": "Carga de campeón",
|
||||
"core.bonus.JOUSTING.description": "+5% de daño por hexágono recorrido",
|
||||
"core.bonus.KING.name": "Rey",
|
||||
"core.bonus.KING.description": "Vulnerable a nivel SLAYER ${val} o superior.",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Inmunidad a hechizos 1-${val}",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Inmune a hechizos de nivel 1-${val}",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Rango de disparo limitado",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "No puede disparar a objetivos más allá de ${val} hexágonos",
|
||||
"core.bonus.LIFE_DRAIN.name": "Drenaje de vida (${val}%)",
|
||||
"core.bonus.LIFE_DRAIN.description": "Drena ${val}% del daño causado",
|
||||
"core.bonus.MANA_CHANNELING.name": "Canalización mágica ${val}%",
|
||||
"core.bonus.MANA_CHANNELING.description": "Tu héroe recibe el maná gastado por el enemigo",
|
||||
"core.bonus.MANA_DRAIN.name": "Drenaje de maná",
|
||||
"core.bonus.MANA_DRAIN.description": "Drena ${val} de maná cada turno",
|
||||
"core.bonus.MAGIC_MIRROR.name": "Espejo mágico (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "Tiene una probabilidad del ${val}% de redirigir un hechizo ofensivo al enemigo",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "Resistencia mágica (${MR}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "Tiene una probabilidad del ${MR}% de resistir el hechizo del enemigo",
|
||||
"core.bonus.MIND_IMMUNITY.name": "Inmunidad a hechizos mentales",
|
||||
"core.bonus.MIND_IMMUNITY.description": "Inmune a hechizos de tipo mental",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "Sin penalización por distancia",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "Causa el daño completo desde cualquier distancia",
|
||||
"core.bonus.NO_MELEE_PENALTY.name": "Sin penalización de melé",
|
||||
"core.bonus.NO_MELEE_PENALTY.description": "La criatura no tiene penalización en el combate cuerpo a cuerpo",
|
||||
"core.bonus.NO_MORALE.name": "Moral neutral",
|
||||
"core.bonus.NO_MORALE.description": "La criatura es inmune a los efectos de la moral",
|
||||
"core.bonus.NO_WALL_PENALTY.name": "Sin penalización por muros",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "Daño completo durante el asedio",
|
||||
"core.bonus.NON_LIVING.name": "No vivo",
|
||||
"core.bonus.NON_LIVING.description": "Inmunidad a muchos efectos",
|
||||
"core.bonus.RANDOM_SPELLCASTER.name": "Lanzador de hechizos aleatorio",
|
||||
"core.bonus.RANDOM_SPELLCASTER.description": "Puede lanzar hechizos aleatorios",
|
||||
"core.bonus.RANGED_RETALIATION.name": "Contraataque a distancia",
|
||||
"core.bonus.RANGED_RETALIATION.description": "Puede realizar un contraataque a distancia",
|
||||
"core.bonus.RECEPTIVE.name": "Receptivo",
|
||||
"core.bonus.RECEPTIVE.description": "No tiene inmunidad a hechizos amistosos",
|
||||
"core.bonus.REBIRTH.name": "Renacimiento (${val}%)",
|
||||
"core.bonus.REBIRTH.description": "El ${val}% del grupo resucitará después de la muerte",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "Atacar y volver",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "Regresa después de un ataque cuerpo a cuerpo",
|
||||
"core.bonus.SHOOTER.name": "A distancia",
|
||||
"core.bonus.SHOOTER.description": "La criatura puede disparar",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Dispara en todas direcciones",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Los ataques a distancia de esta criatura impactan a todos los objetivos en un área pequeña",
|
||||
"core.bonus.SOUL_STEAL.name": "Roba almas",
|
||||
"core.bonus.SOUL_STEAL.description": "Gana ${val} nuevas criaturas por cada enemigo eliminado",
|
||||
"core.bonus.SPELLCASTER.name": "Lanzador de hechizos",
|
||||
"core.bonus.SPELLCASTER.description": "Puede lanzar ${subtype.spell}",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.name": "Lanzar después del ataque",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% de probabilidad de lanzar ${subtype.spell} después del ataque",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.name": "Lanzar antes del ataque",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% de probabilidad de lanzar ${subtype.spell} antes del ataque",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Resistencia a hechizos",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Reduce el daño de los hechizos en un ${val}%.",
|
||||
"core.bonus.SPELL_IMMUNITY.name": "Inmunidad a hechizos",
|
||||
"core.bonus.SPELL_IMMUNITY.description": "Inmune a ${subtype.spell}",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.name": "Ataque similar a hechizo",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.description": "Ataca con ${subtype.spell}",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura de resistencia",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.description": "Las unidades cercanas obtienen una resistencia mágica del ${val}%",
|
||||
"core.bonus.SUMMON_GUARDIANS.name": "Invocar guardianes",
|
||||
"core.bonus.SUMMON_GUARDIANS.description": "Al comienzo de la batalla invoca ${subtype.creature} (${val}%)",
|
||||
"core.bonus.SYNERGY_TARGET.name": "Sinergia",
|
||||
"core.bonus.SYNERGY_TARGET.description": "Esta criatura es vulnerable al efecto de sinergia",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Aliento",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Ataque de aliento (rango de 2 hexágonos)",
|
||||
"core.bonus.THREE_HEADED_ATTACK.name": "Ataque de tres cabezas",
|
||||
"core.bonus.THREE_HEADED_ATTACK.description": "Ataca a tres unidades adyacentes",
|
||||
"core.bonus.TRANSMUTATION.name": "Transmutación",
|
||||
"core.bonus.TRANSMUTATION.description": "${val}% de probabilidad de transformar la unidad atacada en otro tipo",
|
||||
"core.bonus.UNDEAD.name": "No muerto",
|
||||
"core.bonus.UNDEAD.description": "La criatura es un no muerto",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.name": "Retaliaciones ilimitadas",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.description": "Puede realizar un número ilimitado de ataques de represalia",
|
||||
"core.bonus.WATER_IMMUNITY.name": "Inmunidad al agua",
|
||||
"core.bonus.WATER_IMMUNITY.description": "Inmune a todos los hechizos de la escuela del agua",
|
||||
"core.bonus.WIDE_BREATH.name": "Aliento amplio",
|
||||
"core.bonus.WIDE_BREATH.description": "Ataque de aliento amplio (varios hexágonos)"
|
||||
}
|
||||
|
@ -175,8 +175,6 @@
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "${val}% шанс нанести подвійної шкоди",
|
||||
"core.bonus.DRAGON_NATURE.name" : "Дракон",
|
||||
"core.bonus.DRAGON_NATURE.description" : "Істота має драконячу природу",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name" : "Імунітет до прямої шкоди",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description" : "Імунітет до заклять, що завдають прямої шкоди",
|
||||
"core.bonus.EARTH_IMMUNITY.name" : "Імунітет Землі",
|
||||
"core.bonus.EARTH_IMMUNITY.description" : "Імунітет до всіх заклять школи Землі",
|
||||
"core.bonus.ENCHANTER.name" : "Чарівник",
|
||||
|
538
client/CMT.cpp
@ -7,61 +7,49 @@
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
// CMT.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
#include "StdInc.h"
|
||||
#include "CMT.h"
|
||||
|
||||
#include "CGameInfo.h"
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "Client.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "ClientCommandManager.h"
|
||||
#include "windows/CMessage.h"
|
||||
#include "renderSDL/SDL_Extensions.h"
|
||||
#include "render/IScreenHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/FileStream.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include <vstd/StringUtils.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_hints.h>
|
||||
#include <SDL_main.h>
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "../lib/CAndroidVMHelper.h"
|
||||
#include <SDL_system.h>
|
||||
#endif
|
||||
|
||||
#include "CMT.h"
|
||||
|
||||
#if __MINGW32__
|
||||
#undef main
|
||||
#endif
|
||||
@ -70,44 +58,21 @@ namespace po = boost::program_options;
|
||||
namespace po_style = boost::program_options::command_line_style;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
std::string NAME_AFFIX = "client";
|
||||
std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
CGuiHandler GH;
|
||||
|
||||
int preferredDriverIndex = -1;
|
||||
SDL_Window * mainWindow = nullptr;
|
||||
SDL_Renderer * mainRenderer = nullptr;
|
||||
SDL_Texture * screenTexture = nullptr;
|
||||
|
||||
extern boost::thread_specific_ptr<bool> inGuiThread;
|
||||
|
||||
SDL_Surface *screen = nullptr, //main screen surface
|
||||
*screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer)
|
||||
*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
std::queue<SDL_Event> SDLEventsQueue;
|
||||
boost::mutex eventsM;
|
||||
|
||||
static po::variables_map vm;
|
||||
|
||||
//static bool setResolution = false; //set by event handling thread after resolution is adjusted
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
void processCommand(const std::string &message);
|
||||
#endif
|
||||
static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayIndex, bool resetVideo=true);
|
||||
void playIntro();
|
||||
static void mainLoop();
|
||||
|
||||
static CBasicLogConfigurator *logConfig;
|
||||
|
||||
#ifndef VCMI_WINDOWS
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
void init()
|
||||
{
|
||||
CStopWatch tmh;
|
||||
@ -139,17 +104,6 @@ static void prog_help(const po::options_description &opts)
|
||||
std::cout << opts;
|
||||
}
|
||||
|
||||
static void SDLLogCallback(void* userdata,
|
||||
int category,
|
||||
SDL_LogPriority priority,
|
||||
const char* message)
|
||||
{
|
||||
//todo: convert SDL log priority to vcmi log priority
|
||||
//todo: make separate log domain for SDL
|
||||
|
||||
logGlobal->debug("SDL(category %d; priority %d) %s", category, priority, message);
|
||||
}
|
||||
|
||||
#if defined(VCMI_WINDOWS) && !defined(__GNUC__) && defined(VCMI_WITH_DEBUG_CONSOLE)
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
#elif defined(VCMI_MOBILE)
|
||||
@ -255,7 +209,7 @@ int main(int argc, char * argv[])
|
||||
const bfs::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
|
||||
logConfig = new CBasicLogConfigurator(logPath, console);
|
||||
logConfig->configureDefault();
|
||||
logGlobal->info(NAME);
|
||||
logGlobal->info("Starting client of '%s'", GameConstants::VCMI_VERSION);
|
||||
logGlobal->info("Creating console and configuring logger: %d ms", pomtime.getDiff());
|
||||
logGlobal->info("The log file will be saved to %s", logPath);
|
||||
|
||||
@ -338,73 +292,11 @@ int main(int argc, char * argv[])
|
||||
testFile("VIDEO/GOOD1A.SMK", "campaign movies");
|
||||
testFile("SOUNDS/G1A.WAV", "campaign music"); //technically not a music but voiced intro sounds
|
||||
|
||||
conf.init();
|
||||
logGlobal->info("Loading settings: %d ms", pomtime.getDiff());
|
||||
|
||||
srand ( (unsigned int)time(nullptr) );
|
||||
|
||||
|
||||
const JsonNode& video = settings["video"];
|
||||
const JsonNode& res = video["screenRes"];
|
||||
|
||||
//something is really wrong...
|
||||
if (res["width"].Float() < 100 || res["height"].Float() < 100)
|
||||
{
|
||||
logGlobal->error("Fatal error: failed to load settings!");
|
||||
logGlobal->error("Possible reasons:");
|
||||
logGlobal->error("\tCorrupted local configuration file at %s/settings.json", VCMIDirs::get().userConfigPath());
|
||||
logGlobal->error("\tMissing or corrupted global configuration file at %s/schemas/settings.json", VCMIDirs::get().userConfigPath());
|
||||
logGlobal->error("VCMI will now exit...");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE))
|
||||
{
|
||||
logGlobal->error("Something was wrong: %s", SDL_GetError());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
// manually setting egl pixel format, as a possible solution for sdl2<->android problem
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=2291
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
|
||||
#endif // VCMI_ANDROID
|
||||
|
||||
//(!)init here AFTER SDL_Init() while using SDL for FPS management
|
||||
GH.init();
|
||||
|
||||
SDL_LogSetOutputFunction(&SDLLogCallback, nullptr);
|
||||
|
||||
int driversCount = SDL_GetNumRenderDrivers();
|
||||
std::string preferredDriverName = video["driver"].String();
|
||||
|
||||
logGlobal->info("Found %d render drivers", driversCount);
|
||||
|
||||
for(int it = 0; it < driversCount; it++)
|
||||
{
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRenderDriverInfo(it,&info);
|
||||
|
||||
std::string driverName(info.name);
|
||||
|
||||
if(!preferredDriverName.empty() && driverName == preferredDriverName)
|
||||
{
|
||||
preferredDriverIndex = it;
|
||||
logGlobal->info("\t%s (active)", driverName);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t%s", driverName);
|
||||
}
|
||||
|
||||
setScreenRes((int)res["width"].Float(), (int)res["height"].Float(), (int)video["bitsPerPixel"].Float(), video["fullscreen"].Bool(), (int)video["displayIndex"].Float());
|
||||
logGlobal->info("\tInitializing screen: %d ms", pomtime.getDiff());
|
||||
}
|
||||
|
||||
CCS = new CClientState();
|
||||
CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.)
|
||||
CSH = new CServerHandler();
|
||||
@ -457,9 +349,7 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
|
||||
playIntro();
|
||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(mainRenderer);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
GH.screenHandler().clearScreen();
|
||||
}
|
||||
|
||||
|
||||
@ -490,7 +380,6 @@ int main(int argc, char * argv[])
|
||||
CCS->curh->show();
|
||||
}
|
||||
|
||||
|
||||
logGlobal->info("Initialization of VCMI (together): %d ms", total.getDiff());
|
||||
|
||||
session["autoSkip"].Bool() = vm.count("autoSkip");
|
||||
@ -580,350 +469,6 @@ void playIntro()
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(VCMI_MOBILE)
|
||||
static bool checkVideoMode(int monitorIndex, int w, int h)
|
||||
{
|
||||
//we only check that our desired window size fits on screen
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
if (0 != SDL_GetDesktopDisplayMode(monitorIndex, &mode))
|
||||
{
|
||||
logGlobal->error("SDL_GetDesktopDisplayMode failed");
|
||||
logGlobal->error(SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
logGlobal->info("Check display mode: requested %d x %d; available up to %d x %d ", w, h, mode.w, mode.h);
|
||||
|
||||
if (!mode.w || !mode.h || (w <= mode.w && h <= mode.h))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cleanupRenderer()
|
||||
{
|
||||
screenBuf = nullptr; //it`s a link - just nullify
|
||||
|
||||
if(nullptr != screen2)
|
||||
{
|
||||
SDL_FreeSurface(screen2);
|
||||
screen2 = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screen)
|
||||
{
|
||||
SDL_FreeSurface(screen);
|
||||
screen = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screenTexture)
|
||||
{
|
||||
SDL_DestroyTexture(screenTexture);
|
||||
screenTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIndex)
|
||||
{
|
||||
// VCMI will only work with 2 or 4 bytes per pixel
|
||||
vstd::amax(bpp, 16);
|
||||
vstd::amin(bpp, 32);
|
||||
if(bpp>16)
|
||||
bpp = 32;
|
||||
|
||||
if(displayIndex < 0)
|
||||
{
|
||||
if (mainWindow != nullptr)
|
||||
displayIndex = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
if (displayIndex < 0)
|
||||
displayIndex = 0;
|
||||
}
|
||||
|
||||
#if defined(VCMI_MOBILE)
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
#else
|
||||
if(!checkVideoMode(displayIndex, w, h))
|
||||
{
|
||||
logGlobal->error("Error: SDL says that %dx%d resolution is not available!", w, h);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bufOnScreen = (screenBuf == screen);
|
||||
bool realFullscreen = settings["video"]["realFullscreen"].Bool();
|
||||
|
||||
/* match best rendering resolution */
|
||||
int renderWidth = 0, renderHeight = 0;
|
||||
auto aspectRatio = (float)w / (float)h;
|
||||
auto minDiff = 10.f;
|
||||
for (const auto& pair : conf.guiOptions)
|
||||
{
|
||||
int pWidth, pHeight;
|
||||
std::tie(pWidth, pHeight) = pair.first;
|
||||
/* filter out resolution which is larger than window */
|
||||
if (pWidth > w || pHeight > h)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto ratio = (float)pWidth / (float)pHeight;
|
||||
auto diff = fabs(aspectRatio - ratio);
|
||||
/* select closest aspect ratio */
|
||||
if (diff < minDiff)
|
||||
{
|
||||
renderWidth = pWidth;
|
||||
renderHeight = pHeight;
|
||||
minDiff = diff;
|
||||
}
|
||||
/* select largest resolution meets prior conditions.
|
||||
* since there are resolutions like 1366x768(not exactly 16:9), a deviation of 0.005 is allowed. */
|
||||
else if (fabs(diff - minDiff) < 0.005f && pWidth > renderWidth)
|
||||
{
|
||||
renderWidth = pWidth;
|
||||
renderHeight = pHeight;
|
||||
}
|
||||
}
|
||||
if (renderWidth == 0)
|
||||
{
|
||||
// no matching resolution for upscaling - complain & fallback to default resolution.
|
||||
logGlobal->error("Failed to match rendering resolution for %dx%d!", w, h);
|
||||
Settings newRes = settings.write["video"]["screenRes"];
|
||||
std::tie(w, h) = conf.guiOptions.begin()->first;
|
||||
newRes["width"].Float() = w;
|
||||
newRes["height"].Float() = h;
|
||||
conf.SetResolution(w, h);
|
||||
logGlobal->error("Falling back to %dx%d", w, h);
|
||||
renderWidth = w;
|
||||
renderHeight = h;
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->info("Set logical rendering resolution to %dx%d", renderWidth, renderHeight);
|
||||
}
|
||||
|
||||
cleanupRenderer();
|
||||
|
||||
if(nullptr == mainWindow)
|
||||
{
|
||||
#if defined(VCMI_MOBILE)
|
||||
auto createWindow = [displayIndex](uint32_t extraFlags) -> bool {
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN | extraFlags);
|
||||
return mainWindow != nullptr;
|
||||
};
|
||||
|
||||
# ifdef VCMI_IOS
|
||||
SDL_SetHint(SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1");
|
||||
SDL_SetHint(SDL_HINT_RETURN_KEY_HIDES_IME, "1");
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if(!createWindow(windowFlags | SDL_WINDOW_METAL))
|
||||
{
|
||||
logGlobal->warn("Metal unavailable, using OpenGLES");
|
||||
createWindow(windowFlags);
|
||||
}
|
||||
# else
|
||||
createWindow(0);
|
||||
# endif // VCMI_IOS
|
||||
|
||||
// SDL on mobile doesn't do proper letterboxing, and will show an annoying flickering in the blank space in case you're not using the full screen estate
|
||||
// That's why we need to make sure our width and height we'll use below have the same aspect ratio as the screen itself to ensure we fill the full screen estate
|
||||
|
||||
SDL_Rect screenRect;
|
||||
|
||||
if(SDL_GetDisplayBounds(0, &screenRect) == 0)
|
||||
{
|
||||
const auto screenWidth = screenRect.w;
|
||||
const auto screenHeight = screenRect.h;
|
||||
|
||||
const auto aspect = static_cast<double>(screenWidth) / screenHeight;
|
||||
|
||||
logGlobal->info("Screen size and aspect ratio: %dx%d (%lf)", screenWidth, screenHeight, aspect);
|
||||
|
||||
if((double)w / aspect > (double)h)
|
||||
{
|
||||
h = (int)round((double)w / aspect);
|
||||
}
|
||||
else
|
||||
{
|
||||
w = (int)round((double)h * aspect);
|
||||
}
|
||||
|
||||
logGlobal->info("Changing logical screen size to %dx%d", w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Can't fix aspect ratio for screen");
|
||||
}
|
||||
#else
|
||||
if(fullscreen)
|
||||
{
|
||||
if(realFullscreen)
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), renderWidth, renderHeight, SDL_WINDOW_FULLSCREEN);
|
||||
else //in windowed full-screen mode use desktop resolution
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
}
|
||||
else
|
||||
{
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
|
||||
}
|
||||
#endif // defined(VCMI_MOBILE)
|
||||
|
||||
if(nullptr == mainWindow)
|
||||
{
|
||||
throw std::runtime_error("Unable to create window\n");
|
||||
}
|
||||
|
||||
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
|
||||
mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0);
|
||||
|
||||
if(nullptr == mainRenderer)
|
||||
{
|
||||
throw std::runtime_error("Unable to create renderer\n");
|
||||
}
|
||||
|
||||
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRendererInfo(mainRenderer, &info);
|
||||
logGlobal->info("Created renderer %s", info.name);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(VCMI_MOBILE)
|
||||
|
||||
if(fullscreen)
|
||||
{
|
||||
if(realFullscreen)
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN);
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetDesktopDisplayMode(displayIndex, &mode);
|
||||
mode.w = renderWidth;
|
||||
mode.h = renderHeight;
|
||||
|
||||
SDL_SetWindowDisplayMode(mainWindow, &mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex));
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, 0);
|
||||
SDL_SetWindowSize(mainWindow, w, h);
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!(fullscreen && realFullscreen))
|
||||
{
|
||||
SDL_RenderSetLogicalSize(mainRenderer, renderWidth, renderHeight);
|
||||
|
||||
//following line is bugged not only on android, do not re-enable without checking
|
||||
//#ifndef VCMI_ANDROID
|
||||
// // on android this stretches the game to fit the screen, not preserving aspect and apparently this also breaks coordinates scaling in mouse events
|
||||
// SDL_RenderSetViewport(mainRenderer, nullptr);
|
||||
//#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef VCMI_ENDIAN_BIG
|
||||
int bmask = 0xff000000;
|
||||
int gmask = 0x00ff0000;
|
||||
int rmask = 0x0000ff00;
|
||||
int amask = 0x000000ff;
|
||||
#else
|
||||
int bmask = 0x000000ff;
|
||||
int gmask = 0x0000ff00;
|
||||
int rmask = 0x00ff0000;
|
||||
int amask = 0xFF000000;
|
||||
#endif
|
||||
|
||||
screen = SDL_CreateRGBSurface(0,renderWidth,renderHeight,bpp,rmask,gmask,bmask,amask);
|
||||
if(nullptr == screen)
|
||||
{
|
||||
logGlobal->error("Unable to create surface %dx%d with %d bpp: %s", renderWidth, renderHeight, bpp, SDL_GetError());
|
||||
throw std::runtime_error("Unable to create surface");
|
||||
}
|
||||
//No blending for screen itself. Required for proper cursor rendering.
|
||||
SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
|
||||
|
||||
screenTexture = SDL_CreateTexture(mainRenderer,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
renderWidth, renderHeight);
|
||||
|
||||
if(nullptr == screenTexture)
|
||||
{
|
||||
logGlobal->error("Unable to create screen texture");
|
||||
logGlobal->error(SDL_GetError());
|
||||
throw std::runtime_error("Unable to create screen texture");
|
||||
}
|
||||
|
||||
screen2 = CSDL_Ext::copySurface(screen);
|
||||
|
||||
|
||||
if(nullptr == screen2)
|
||||
{
|
||||
throw std::runtime_error("Unable to copy surface\n");
|
||||
}
|
||||
|
||||
screenBuf = bufOnScreen ? screen : screen2;
|
||||
|
||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(mainRenderer);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
|
||||
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::init(mainWindow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//used only once during initialization
|
||||
static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayIndex, bool resetVideo)
|
||||
{
|
||||
if(!recreateWindow(w, h, bpp, fullscreen, displayIndex))
|
||||
{
|
||||
throw std::runtime_error("Requested screen resolution is not available\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fullScreenChanged()
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
const bool toFullscreen = full->Bool();
|
||||
|
||||
auto bitsPerPixel = screen->format->BitsPerPixel;
|
||||
|
||||
auto w = screen->w;
|
||||
auto h = screen->h;
|
||||
|
||||
if(!recreateWindow(w, h, bitsPerPixel, toFullscreen, -1))
|
||||
{
|
||||
//will return false and report error if video mode is not supported
|
||||
return;
|
||||
}
|
||||
|
||||
GH.totalRedraw();
|
||||
}
|
||||
|
||||
static void handleEvent(SDL_Event & ev)
|
||||
{
|
||||
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
|
||||
@ -979,14 +524,14 @@ static void handleEvent(SDL_Event & ev)
|
||||
{
|
||||
if(ourCampaign->mapsRemaining.size())
|
||||
{
|
||||
GH.pushInt(CMM);
|
||||
GH.pushInt(CMM->menu);
|
||||
GH.windows().pushWindow(CMM);
|
||||
GH.windows().pushWindow(CMM->menu);
|
||||
CMM->openCampaignLobby(ourCampaign);
|
||||
}
|
||||
};
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
GH.pushIntT<CPrologEpilogVideo>(epilogue, finisher);
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1001,8 +546,11 @@ static void handleEvent(SDL_Event & ev)
|
||||
CMM->menu->switchToTab("load");
|
||||
break;
|
||||
case EUserEvent::FULLSCREEN_TOGGLED:
|
||||
fullScreenChanged();
|
||||
break;
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logGlobal->error("Unknown user event. Code %d", ev.user.code);
|
||||
break;
|
||||
@ -1015,7 +563,10 @@ static void handleEvent(SDL_Event & ev)
|
||||
switch (ev.window.event) {
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
#ifndef VCMI_IOS
|
||||
fullScreenChanged();
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.onScreenResize();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1039,18 +590,16 @@ static void handleEvent(SDL_Event & ev)
|
||||
boost::unique_lock<boost::mutex> lock(eventsM);
|
||||
SDLEventsQueue.push(ev);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void mainLoop()
|
||||
{
|
||||
SettingsListener resChanged = settings.listen["video"]["fullscreen"];
|
||||
SettingsListener resChanged = settings.listen["video"]["resolution"];
|
||||
SettingsListener fsChanged = settings.listen["video"]["fullscreen"];
|
||||
resChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
|
||||
inGuiThread.reset(new bool(true));
|
||||
assert(GH.mainFPSmng);
|
||||
GH.mainFPSmng->init(settings["video"]["targetfps"].Integer());
|
||||
|
||||
while(1) //main SDL events loop
|
||||
{
|
||||
@ -1063,7 +612,6 @@ static void mainLoop()
|
||||
|
||||
CSH->applyPacksOnLobbyScreen();
|
||||
GH.renderFrame();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1075,8 +623,7 @@ static void quitApplication()
|
||||
CSH->endGameplay();
|
||||
}
|
||||
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.windows().clear();
|
||||
|
||||
CMM.reset();
|
||||
|
||||
@ -1100,29 +647,9 @@ static void quitApplication()
|
||||
vstd::clear_pointer(console);// should be removed after everything else since used by logging
|
||||
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(750));//???
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::destroy();
|
||||
}
|
||||
|
||||
cleanupRenderer();
|
||||
|
||||
if(nullptr != mainRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(mainRenderer);
|
||||
mainRenderer = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != mainWindow)
|
||||
{
|
||||
SDL_DestroyWindow(mainWindow);
|
||||
mainWindow = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
GH.screenHandler().close();
|
||||
|
||||
if(logConfig != nullptr)
|
||||
{
|
||||
@ -1137,7 +664,6 @@ static void quitApplication()
|
||||
|
||||
void handleQuit(bool ask)
|
||||
{
|
||||
|
||||
if(CSH->client && LOCPLINT && ask)
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
|
@ -2,9 +2,10 @@ set(client_SRCS
|
||||
StdInc.cpp
|
||||
../CCallback.cpp
|
||||
|
||||
adventureMap/CAdvMapPanel.cpp
|
||||
adventureMap/CAdventureMapInterface.cpp
|
||||
adventureMap/CAdventureOptions.cpp
|
||||
adventureMap/AdventureMapInterface.cpp
|
||||
adventureMap/AdventureMapShortcuts.cpp
|
||||
adventureMap/AdventureMapWidget.cpp
|
||||
adventureMap/AdventureOptions.cpp
|
||||
adventureMap/CInGameConsole.cpp
|
||||
adventureMap/CInfoBar.cpp
|
||||
adventureMap/CList.cpp
|
||||
@ -30,8 +31,10 @@ set(client_SRCS
|
||||
gui/CIntObject.cpp
|
||||
gui/CursorHandler.cpp
|
||||
gui/InterfaceObjectConfigurable.cpp
|
||||
gui/FramerateManager.cpp
|
||||
gui/NotificationHandler.cpp
|
||||
gui/ShortcutHandler.cpp
|
||||
gui/WindowHandler.cpp
|
||||
|
||||
lobby/CBonusSelection.cpp
|
||||
lobby/CCampaignInfoScreen.cpp
|
||||
@ -75,6 +78,7 @@ set(client_SRCS
|
||||
renderSDL/SDLImage.cpp
|
||||
renderSDL/SDLImageLoader.cpp
|
||||
renderSDL/SDLRWwrapper.cpp
|
||||
renderSDL/ScreenHandler.cpp
|
||||
renderSDL/SDL_Extensions.cpp
|
||||
|
||||
widgets/Buttons.cpp
|
||||
@ -129,9 +133,11 @@ set(client_SRCS
|
||||
set(client_HEADERS
|
||||
StdInc.h
|
||||
|
||||
adventureMap/CAdvMapPanel.h
|
||||
adventureMap/CAdventureMapInterface.h
|
||||
adventureMap/CAdventureOptions.h
|
||||
adventureMap/AdventureMapInterface.h
|
||||
adventureMap/AdventureMapShortcuts.h
|
||||
adventureMap/AdventureMapWidget.h
|
||||
adventureMap/AdventureState.h
|
||||
adventureMap/AdventureOptions.h
|
||||
adventureMap/CInGameConsole.h
|
||||
adventureMap/CInfoBar.h
|
||||
adventureMap/CList.h
|
||||
@ -158,11 +164,13 @@ set(client_HEADERS
|
||||
gui/CIntObject.h
|
||||
gui/CursorHandler.h
|
||||
gui/InterfaceObjectConfigurable.h
|
||||
gui/FramerateManager.h
|
||||
gui/MouseButton.h
|
||||
gui/NotificationHandler.h
|
||||
gui/Shortcut.h
|
||||
gui/ShortcutHandler.h
|
||||
gui/TextAlignment.h
|
||||
gui/WindowHandler.h
|
||||
|
||||
lobby/CBonusSelection.h
|
||||
lobby/CCampaignInfoScreen.h
|
||||
@ -202,6 +210,7 @@ set(client_HEADERS
|
||||
render/IFont.h
|
||||
render/IImage.h
|
||||
render/IImageLoader.h
|
||||
render/IScreenHandler.h
|
||||
|
||||
renderSDL/CBitmapFont.h
|
||||
renderSDL/CBitmapHanFont.h
|
||||
@ -211,6 +220,7 @@ set(client_HEADERS
|
||||
renderSDL/SDLImage.h
|
||||
renderSDL/SDLImageLoader.h
|
||||
renderSDL/SDLRWwrapper.h
|
||||
renderSDL/ScreenHandler.h
|
||||
renderSDL/SDL_Extensions.h
|
||||
renderSDL/SDL_PixelAccess.h
|
||||
|
||||
|
@ -335,7 +335,7 @@ CMusicHandler::CMusicHandler():
|
||||
|
||||
auto mp3files = CResourceHandler::get()->getFilteredFiles([](const ResourceID & id) -> bool
|
||||
{
|
||||
if(id.getType() != EResType::MUSIC)
|
||||
if(id.getType() != EResType::SOUND)
|
||||
return false;
|
||||
|
||||
if(!boost::algorithm::istarts_with(id.getName(), "MUSIC/"))
|
||||
@ -561,7 +561,7 @@ void MusicEntry::load(std::string musicURI)
|
||||
|
||||
try
|
||||
{
|
||||
auto musicFile = MakeSDLRWops(CResourceHandler::get()->load(ResourceID(std::move(musicURI), EResType::MUSIC)));
|
||||
auto musicFile = MakeSDLRWops(CResourceHandler::get()->load(ResourceID(std::move(musicURI), EResType::SOUND)));
|
||||
music = Mix_LoadMUS_RW(musicFile, SDL_TRUE);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
|
@ -12,10 +12,9 @@
|
||||
|
||||
#include <vcmi/Artifact.h>
|
||||
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
#include "adventureMap/CList.h"
|
||||
#include "adventureMap/CInfoBar.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
#include "battle/BattleEffectsController.h"
|
||||
#include "battle/BattleFieldController.h"
|
||||
@ -66,6 +65,7 @@
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "windows/InfoWindows.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
#include "../lib/CPathfinder.h"
|
||||
@ -163,22 +163,23 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
|
||||
initializeHeroTownList();
|
||||
|
||||
// always recreate advmap interface to avoid possible memory-corruption bugs
|
||||
adventureInt.reset(new CAdventureMapInterface());
|
||||
adventureInt.reset(new AdventureMapInterface());
|
||||
}
|
||||
|
||||
void CPlayerInterface::playerStartsTurn(PlayerColor player)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (!vstd::contains (GH.listInt, adventureInt))
|
||||
|
||||
if(GH.windows().findWindows<AdventureMapInterface>().empty())
|
||||
{
|
||||
// after map load - remove all active windows and replace them with adventure map
|
||||
GH.popInts ((int)GH.listInt.size());
|
||||
GH.pushInt (adventureInt);
|
||||
GH.windows().clear();
|
||||
GH.windows().pushWindow(adventureInt);
|
||||
}
|
||||
|
||||
// remove all dialogs that do not expect query answer
|
||||
while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front().get()))
|
||||
GH.popInts(1);
|
||||
while (!GH.windows().topWindow<AdventureMapInterface>() && !GH.windows().topWindow<CInfoWindow>())
|
||||
GH.windows().popWindows(1);
|
||||
|
||||
if (player != playerID && LOCPLINT == this)
|
||||
{
|
||||
@ -246,7 +247,7 @@ void CPlayerInterface::acceptTurn()
|
||||
{
|
||||
if (settings["session"]["autoSkip"].Bool())
|
||||
{
|
||||
while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
|
||||
while(auto iw = GH.windows().topWindow<CInfoWindow>())
|
||||
iw->close();
|
||||
}
|
||||
|
||||
@ -440,7 +441,7 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town)
|
||||
|
||||
auto newCastleInt = std::make_shared<CCastleInterface>(town);
|
||||
|
||||
GH.pushInt(newCastleInt);
|
||||
GH.windows().pushWindow(newCastleInt);
|
||||
}
|
||||
|
||||
void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
|
||||
@ -448,7 +449,7 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (which == 4)
|
||||
{
|
||||
if (CAltarWindow *ctw = dynamic_cast<CAltarWindow *>(GH.topInt().get()))
|
||||
for (auto ctw : GH.windows().findWindows<CAltarWindow>())
|
||||
ctw->setExpToLevel();
|
||||
}
|
||||
else
|
||||
@ -458,11 +459,8 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
|
||||
void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
CUniversityWindow* cuw = dynamic_cast<CUniversityWindow*>(GH.topInt().get());
|
||||
if (cuw) //university window is open
|
||||
{
|
||||
GH.totalRedraw();
|
||||
}
|
||||
for (auto cuw : GH.windows().findWindows<CUniversityWindow>())
|
||||
cuw->redraw();
|
||||
}
|
||||
|
||||
void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
|
||||
@ -481,10 +479,10 @@ void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
|
||||
void CPlayerInterface::receivedResource()
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (CMarketplaceWindow *mw = dynamic_cast<CMarketplaceWindow *>(GH.topInt().get()))
|
||||
for (auto mw : GH.windows().findWindows<CMarketplaceWindow>())
|
||||
mw->resourceChanged();
|
||||
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill>& skills, QueryID queryID)
|
||||
@ -492,7 +490,7 @@ void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::Pr
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
waitWhileDialog();
|
||||
CCS->soundh->playSound(soundBase::heroNewLevel);
|
||||
GH.pushIntT<CLevelWindow>(hero, pskill, skills, [=](ui32 selection)
|
||||
GH.windows().createAndPushWindow<CLevelWindow>(hero, pskill, skills, [=](ui32 selection)
|
||||
{
|
||||
cb->selectionMade(selection, queryID);
|
||||
});
|
||||
@ -503,7 +501,7 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
waitWhileDialog();
|
||||
CCS->soundh->playSound(soundBase::heroNewLevel);
|
||||
GH.pushIntT<CStackWindow>(commander, skills, [=](ui32 selection)
|
||||
GH.windows().createAndPushWindow<CStackWindow>(commander, skills, [=](ui32 selection)
|
||||
{
|
||||
cb->selectionMade(selection, queryID);
|
||||
});
|
||||
@ -538,17 +536,15 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
|
||||
castleInt->heroes->update();
|
||||
castleInt->redraw();
|
||||
}
|
||||
for (auto isa : GH.listInt)
|
||||
|
||||
for (auto ki : GH.windows().findWindows<CKingdomInterface>())
|
||||
{
|
||||
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa.get());
|
||||
if (ki)
|
||||
{
|
||||
ki->townChanged(town);
|
||||
ki->updateGarrisons();
|
||||
ki->redraw();
|
||||
}
|
||||
ki->townChanged(town);
|
||||
ki->updateGarrisons();
|
||||
ki->redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
@ -590,20 +586,16 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
|
||||
adventureInt->onTownChanged(town);
|
||||
}
|
||||
|
||||
for (auto & elem : GH.listInt)
|
||||
{
|
||||
CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(elem.get());
|
||||
if (cgh)
|
||||
cgh->updateGarrisons();
|
||||
for (auto cgh : GH.windows().findWindows<CGarrisonHolder>())
|
||||
cgh->updateGarrisons();
|
||||
|
||||
if (CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(elem.get()))
|
||||
{
|
||||
if (vstd::contains(objs, cmw->hero))
|
||||
cmw->garrisonChanged();
|
||||
}
|
||||
for (auto cmw : GH.windows().findWindows<CTradeWindow>())
|
||||
{
|
||||
if (vstd::contains(objs, cmw->hero))
|
||||
cmw->garrisonChanged();
|
||||
}
|
||||
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) //what: 1 - built, 2 - demolished
|
||||
@ -862,7 +854,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID)
|
||||
{
|
||||
cb->selectionMade(selection, queryID);
|
||||
};
|
||||
GH.pushInt(wnd);
|
||||
GH.windows().pushWindow(wnd);
|
||||
// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
|
||||
// Otherwise NewTurn causes freeze.
|
||||
waitWhileDialog();
|
||||
@ -1016,7 +1008,7 @@ void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &t
|
||||
waitWhileDialog(); //Fix for mantis #98
|
||||
adventureInt->showInfoBoxMessage(components, text, timer);
|
||||
|
||||
if (makingTurn && GH.listInt.size() && LOCPLINT == this)
|
||||
if (makingTurn && GH.windows().count() > 0 && LOCPLINT == this)
|
||||
CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
|
||||
return;
|
||||
}
|
||||
@ -1057,12 +1049,12 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
|
||||
}
|
||||
std::shared_ptr<CInfoWindow> temp = CInfoWindow::create(text, playerID, components);
|
||||
|
||||
if (makingTurn && GH.listInt.size() && LOCPLINT == this)
|
||||
if (makingTurn && GH.windows().count() > 0 && LOCPLINT == this)
|
||||
{
|
||||
CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
|
||||
showingDialog->set(true);
|
||||
stopMovement(); // interrupt movement to show dialog
|
||||
GH.pushInt(temp);
|
||||
GH.windows().pushWindow(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1122,7 +1114,7 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
|
||||
int charperline = 35;
|
||||
if (pom.size() > 1)
|
||||
charperline = 50;
|
||||
GH.pushIntT<CSelWindow>(text, playerID, charperline, intComps, pom, askID);
|
||||
GH.windows().createAndPushWindow<CSelWindow>(text, playerID, charperline, intComps, pom, askID);
|
||||
intComps[0]->clickLeft(true, false);
|
||||
}
|
||||
}
|
||||
@ -1171,7 +1163,7 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
|
||||
|
||||
std::shared_ptr<CObjectListWindow> wnd = std::make_shared<CObjectListWindow>(tempList, localIcon, localTitle, localDescription, selectCallback);
|
||||
wnd->onExit = cancelCallback;
|
||||
GH.pushInt(wnd);
|
||||
GH.windows().pushWindow(wnd);
|
||||
}
|
||||
|
||||
void CPlayerInterface::tileRevealed(const std::unordered_set<int3> &pos)
|
||||
@ -1190,7 +1182,7 @@ void CPlayerInterface::tileHidden(const std::unordered_set<int3> &pos)
|
||||
void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*pim);
|
||||
GH.pushIntT<CHeroWindow>(hero);
|
||||
GH.windows().createAndPushWindow<CHeroWindow>(hero);
|
||||
}
|
||||
|
||||
void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
|
||||
@ -1198,27 +1190,22 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (const CGTownInstance * townObj = dynamic_cast<const CGTownInstance*>(town))
|
||||
{
|
||||
CFortScreen * fortScreen = dynamic_cast<CFortScreen*>(GH.topInt().get());
|
||||
CCastleInterface * castleInterface = dynamic_cast<CCastleInterface*>(GH.topInt().get());
|
||||
|
||||
if (fortScreen)
|
||||
for (auto fortScreen : GH.windows().findWindows<CFortScreen>())
|
||||
fortScreen->creaturesChangedEventHandler();
|
||||
else if(castleInterface)
|
||||
|
||||
for (auto castleInterface : GH.windows().findWindows<CCastleInterface>())
|
||||
castleInterface->creaturesChangedEventHandler();
|
||||
|
||||
for(auto isa : GH.listInt)
|
||||
{
|
||||
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa.get());
|
||||
if (ki && townObj)
|
||||
if (townObj)
|
||||
for (auto ki : GH.windows().findWindows<CKingdomInterface>())
|
||||
ki->townChanged(townObj);
|
||||
}
|
||||
}
|
||||
else if(town && GH.listInt.size() && (town->ID == Obj::CREATURE_GENERATOR1
|
||||
else if(town && GH.windows().count() > 0 && (town->ID == Obj::CREATURE_GENERATOR1
|
||||
|| town->ID == Obj::CREATURE_GENERATOR4 || town->ID == Obj::WAR_MACHINE_FACTORY))
|
||||
{
|
||||
CRecruitmentWindow *crw = dynamic_cast<CRecruitmentWindow*>(GH.topInt().get());
|
||||
if (crw && crw->dwelling == town)
|
||||
crw->availableCreaturesChanged();
|
||||
for (auto crw : GH.windows().findWindows<CRecruitmentWindow>())
|
||||
if (crw->dwelling == town)
|
||||
crw->availableCreaturesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1284,7 +1271,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
|
||||
|
||||
auto cgw = std::make_shared<CGarrisonWindow>(up, down, removableUnits);
|
||||
cgw->quit->addCallback(onEnd);
|
||||
GH.pushInt(cgw);
|
||||
GH.windows().pushWindow(cgw);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1342,7 +1329,7 @@ void CPlayerInterface::showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID
|
||||
void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CExchangeWindow>(hero1, hero2, query);
|
||||
GH.windows().createAndPushWindow<CExchangeWindow>(hero1, hero2, query);
|
||||
}
|
||||
|
||||
void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
|
||||
@ -1403,7 +1390,7 @@ void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const C
|
||||
{
|
||||
LOCPLINT->cb->recruitCreatures(dwelling, dst, id, count, -1);
|
||||
};
|
||||
GH.pushIntT<CRecruitmentWindow>(dwelling, level, dst, recruitCb);
|
||||
GH.windows().createAndPushWindow<CRecruitmentWindow>(dwelling, level, dst, recruitCb);
|
||||
}
|
||||
|
||||
void CPlayerInterface::waitWhileDialog(bool unlockPim)
|
||||
@ -1426,7 +1413,7 @@ void CPlayerInterface::showShipyardDialog(const IShipyard *obj)
|
||||
auto state = obj->shipyardStatus();
|
||||
TResources cost;
|
||||
obj->getBoatCost(cost);
|
||||
GH.pushIntT<CShipyardWindow>(cost, state, obj->getBoatType(), [=](){ cb->buildBoat(obj); });
|
||||
GH.windows().createAndPushWindow<CShipyardWindow>(cost, state, obj->getBoatType(), [=](){ cb->buildBoat(obj); });
|
||||
}
|
||||
|
||||
void CPlayerInterface::newObject( const CGObjectInstance * obj )
|
||||
@ -1450,7 +1437,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
|
||||
adventureInt->centerOnTile(pos);
|
||||
if (focusTime)
|
||||
{
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
{
|
||||
auto unlockPim = vstd::makeUnlockGuard(*pim);
|
||||
IgnoreEvents ignore(*this);
|
||||
@ -1519,7 +1506,7 @@ void CPlayerInterface::update()
|
||||
if ((CSH->howManyPlayerInterfaces() <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get())
|
||||
{
|
||||
showingDialog->set(true);
|
||||
GH.pushInt(dialogs.front());
|
||||
GH.windows().pushWindow(dialogs.front());
|
||||
dialogs.pop_front();
|
||||
}
|
||||
|
||||
@ -1528,7 +1515,7 @@ void CPlayerInterface::update()
|
||||
// Handles mouse and key input
|
||||
GH.updateTime();
|
||||
GH.handleEvents();
|
||||
GH.simpleRedraw();
|
||||
GH.windows().simpleRedraw();
|
||||
}
|
||||
|
||||
int CPlayerInterface::getLastIndex( std::string namePrefix)
|
||||
@ -1594,7 +1581,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
||||
if(adventureInt)
|
||||
{
|
||||
GH.terminate_cond->setn(true);
|
||||
GH.popInts(GH.listInt.size());
|
||||
GH.windows().popWindows(GH.windows().count());
|
||||
adventureInt.reset();
|
||||
}
|
||||
}
|
||||
@ -1640,7 +1627,7 @@ void CPlayerInterface::showPuzzleMap()
|
||||
double ratio = 0;
|
||||
int3 grailPos = cb->getGrailPos(&ratio);
|
||||
|
||||
GH.pushIntT<CPuzzleWindow>(grailPos, ratio);
|
||||
GH.windows().createAndPushWindow<CPuzzleWindow>(grailPos, ratio);
|
||||
}
|
||||
|
||||
void CPlayerInterface::viewWorldMap()
|
||||
@ -1652,8 +1639,8 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
|
||||
if(dynamic_cast<CSpellWindow *>(GH.topInt().get()))
|
||||
GH.popInts(1);
|
||||
if(GH.windows().topWindow<CSpellWindow>())
|
||||
GH.windows().popWindows(1);
|
||||
|
||||
if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
|
||||
localState->erasePath(caster);
|
||||
@ -1715,50 +1702,50 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
|
||||
if(market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL)
|
||||
GH.pushIntT<CAltarWindow>(market, visitor, EMarketMode::ARTIFACT_EXP);
|
||||
GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, EMarketMode::ARTIFACT_EXP);
|
||||
else if(market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD)
|
||||
GH.pushIntT<CAltarWindow>(market, visitor, EMarketMode::CREATURE_EXP);
|
||||
GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, EMarketMode::CREATURE_EXP);
|
||||
else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD))
|
||||
GH.pushIntT<CTransformerWindow>(market, visitor);
|
||||
GH.windows().createAndPushWindow<CTransformerWindow>(market, visitor);
|
||||
else if(!market->availableModes().empty())
|
||||
GH.pushIntT<CMarketplaceWindow>(market, visitor, market->availableModes().front());
|
||||
GH.windows().createAndPushWindow<CMarketplaceWindow>(market, visitor, market->availableModes().front());
|
||||
}
|
||||
|
||||
void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CUniversityWindow>(visitor, market);
|
||||
GH.windows().createAndPushWindow<CUniversityWindow>(visitor, market);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CHillFortWindow>(visitor, object);
|
||||
GH.windows().createAndPushWindow<CHillFortWindow>(visitor, object);
|
||||
}
|
||||
|
||||
void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket * bm)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (CMarketplaceWindow *cmw = dynamic_cast<CMarketplaceWindow*>(GH.topInt().get()))
|
||||
for (auto cmw : GH.windows().findWindows<CMarketplaceWindow>())
|
||||
cmw->artifactsChanged(false);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showTavernWindow(const CGObjectInstance *townOrTavern)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CTavernWindow>(townOrTavern);
|
||||
GH.windows().createAndPushWindow<CTavernWindow>(townOrTavern);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showThievesGuildWindow (const CGObjectInstance * obj)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CThievesGuildWindow>(obj);
|
||||
GH.windows().createAndPushWindow<CThievesGuildWindow>(obj);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showQuestLog()
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
GH.pushIntT<CQuestLog>(LOCPLINT->cb->getMyQuests());
|
||||
GH.windows().createAndPushWindow<CQuestLog>(LOCPLINT->cb->getMyQuests());
|
||||
}
|
||||
|
||||
void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
|
||||
@ -1809,12 +1796,9 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
for(auto isa : GH.listInt)
|
||||
{
|
||||
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
|
||||
if (artWin)
|
||||
artWin->artifactRemoved(al);
|
||||
}
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactRemoved(al);
|
||||
|
||||
waitWhileDialog();
|
||||
}
|
||||
@ -1834,12 +1818,9 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
|
||||
redraw = false;
|
||||
}
|
||||
|
||||
for(auto isa : GH.listInt)
|
||||
{
|
||||
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
|
||||
if (artWin)
|
||||
artWin->artifactMoved(src, dst, redraw);
|
||||
}
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactMoved(src, dst, redraw);
|
||||
|
||||
waitWhileDialog();
|
||||
}
|
||||
|
||||
@ -1853,12 +1834,9 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
for(auto isa : GH.listInt)
|
||||
{
|
||||
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
|
||||
if (artWin)
|
||||
artWin->artifactAssembled(al);
|
||||
}
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactAssembled(al);
|
||||
}
|
||||
|
||||
void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
|
||||
@ -1866,12 +1844,9 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
for(auto isa : GH.listInt)
|
||||
{
|
||||
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
|
||||
if (artWin)
|
||||
artWin->artifactDisassembled(al);
|
||||
}
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactDisassembled(al);
|
||||
}
|
||||
|
||||
void CPlayerInterface::waitForAllDialogs(bool unlockPim)
|
||||
|
@ -31,7 +31,7 @@ struct CPathsInfo;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CButton;
|
||||
class CAdventureMapInterface;
|
||||
class AdventureMapInterface;
|
||||
class CCastleInterface;
|
||||
class BattleInterface;
|
||||
class CComponent;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "CGameInfo.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "lobby/CLobbyScreen.h"
|
||||
@ -123,7 +124,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
extern std::string NAME;
|
||||
static const std::string NAME_AFFIX = "client";
|
||||
static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
|
||||
CServerHandler::CServerHandler()
|
||||
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
|
||||
@ -324,7 +326,7 @@ void CServerHandler::applyPacksOnLobbyScreen()
|
||||
packsForLobbyScreen.pop_front();
|
||||
CBaseForLobbyApply * apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
|
||||
apply->applyOnLobbyScreen(static_cast<CLobbyScreen *>(SEL), this, pack);
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
delete pack;
|
||||
}
|
||||
}
|
||||
@ -748,8 +750,9 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
|
||||
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
||||
|
||||
while(!settings["session"]["headless"].Bool() && !dynamic_cast<CLobbyScreen *>(GH.topInt().get()))
|
||||
while(!settings["session"]["headless"].Bool() && !GH.windows().topWindow<CLobbyScreen>())
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
|
||||
while(!mi || mapInfo->fileURI != CSH->mi->fileURI)
|
||||
{
|
||||
setMapInfo(mapInfo);
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "CMT.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/FramerateManager.h"
|
||||
#include "renderSDL/SDL_Extensions.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
@ -370,7 +371,7 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
|
||||
auto packet_duration = frame->duration;
|
||||
#endif
|
||||
double frameEndTime = (frame->pts + packet_duration) * av_q2d(format->streams[stream]->time_base);
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.0;
|
||||
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.0;
|
||||
|
||||
if (frameTime >= frameEndTime )
|
||||
{
|
||||
@ -450,6 +451,7 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
|
||||
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
frameTime = 0.0;
|
||||
|
||||
while(nextFrame())
|
||||
{
|
||||
@ -461,10 +463,15 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
|
||||
SDL_RenderCopy(mainRenderer, texture, nullptr, &rect);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
|
||||
// Wait 3 frames
|
||||
GH.mainFPSmng->framerateDelay();
|
||||
GH.mainFPSmng->framerateDelay();
|
||||
GH.mainFPSmng->framerateDelay();
|
||||
#if (LIBAVUTIL_VERSION_MAJOR < 58)
|
||||
auto packet_duration = frame->pkt_duration;
|
||||
#else
|
||||
auto packet_duration = frame->duration;
|
||||
#endif
|
||||
double frameDurationSec = packet_duration * av_q2d(format->streams[stream]->time_base);
|
||||
uint32_t timeToSleepMillisec = 1000 * (frameDurationSec);
|
||||
|
||||
boost::this_thread::sleep(boost::posix_time::millisec(timeToSleepMillisec));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -15,9 +15,10 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "ClientNetPackVisitors.h"
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
|
||||
#include "../CCallback.h"
|
||||
@ -766,12 +767,8 @@ void CClient::removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
GH.windows().clear();
|
||||
adventureInt.reset();
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar.reset();
|
||||
logGlobal->info("Removed GUI.");
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "PlayerLocalState.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "ClientNetPackVisitors.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
@ -100,7 +101,7 @@ void ClientCommandManager::handleGoSoloCommand()
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
giveTurn(color);
|
||||
}
|
||||
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
|
||||
@ -141,7 +142,7 @@ void ClientCommandManager::handleControlaiCommand(std::istringstream& singleWord
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
giveTurn(color);
|
||||
}
|
||||
@ -170,7 +171,7 @@ void ClientCommandManager::handleSetBattleAICommand(std::istringstream& singleWo
|
||||
|
||||
void ClientCommandManager::handleRedrawCommand()
|
||||
{
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleScreenCommand()
|
||||
@ -195,14 +196,8 @@ void ClientCommandManager::handleNotDialogCommand()
|
||||
|
||||
void ClientCommandManager::handleGuiCommand()
|
||||
{
|
||||
for(const auto & child : GH.listInt)
|
||||
{
|
||||
const auto childPtr = child.get();
|
||||
if(const CIntObject * obj = dynamic_cast<const CIntObject*>(childPtr))
|
||||
printInfoAboutInterfaceObject(obj, 0);
|
||||
else
|
||||
printCommandMessage(std::string(typeid(childPtr).name()) + "\n");
|
||||
}
|
||||
for(const auto & child : GH.windows().findWindows<CIntObject>())
|
||||
printInfoAboutInterfaceObject(child.get(), 0);
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleConvertTextCommand()
|
||||
@ -387,7 +382,7 @@ void ClientCommandManager::handleBonusesCommand(std::istringstream & singleWordB
|
||||
return ss.str();
|
||||
};
|
||||
printCommandMessage("Bonuses of " + LOCPLINT->localState->getCurrentArmy()->getObjectName() + "\n");
|
||||
printCommandMessage(format(LOCPLINT->localState->getCurrentArmy()->getBonusList()) + "\n");
|
||||
printCommandMessage(format(*LOCPLINT->localState->getCurrentArmy()->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
|
||||
printCommandMessage("\nInherited bonuses:\n");
|
||||
TCNodes parents;
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings
|
||||
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
@ -329,13 +329,12 @@ void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack)
|
||||
case GiveBonus::ETarget::HERO:
|
||||
{
|
||||
const CGHeroInstance *h = gs.getHero(ObjectInstanceID(pack.id));
|
||||
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, *h->getBonusList().back(), true);
|
||||
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, true);
|
||||
}
|
||||
break;
|
||||
case GiveBonus::ETarget::PLAYER:
|
||||
{
|
||||
const PlayerState *p = gs.getPlayerState(PlayerColor(pack.id));
|
||||
callInterfaceIfPresent(cl, PlayerColor(pack.id), &IGameEventsReceiver::playerBonusChanged, *p->getBonusList().back(), true);
|
||||
callInterfaceIfPresent(cl, PlayerColor(pack.id), &IGameEventsReceiver::playerBonusChanged, pack.bonus, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "CServerHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "widgets/Buttons.h"
|
||||
#include "widgets/TextControls.h"
|
||||
|
||||
@ -38,7 +39,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
|
||||
{
|
||||
handler.c->connectionID = pack.clientId;
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
|
||||
GH.windows().createAndPushWindow<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
|
||||
handler.state = EClientState::LOBBY;
|
||||
}
|
||||
}
|
||||
@ -56,8 +57,8 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClient
|
||||
|
||||
void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
|
||||
{
|
||||
if(GH.listInt.size())
|
||||
GH.popInts(1);
|
||||
if(GH.windows().count() > 0)
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
void ApplyOnLobbyScreenNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack)
|
||||
@ -128,7 +129,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack
|
||||
if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
|
||||
return;
|
||||
|
||||
GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, &handler, pack.initializedGameState));
|
||||
GH.windows().createAndPushWindow<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, &handler, pack.initializedGameState));
|
||||
}
|
||||
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)
|
||||
@ -145,7 +146,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState &
|
||||
if(!lobby->bonusSel && handler.si->campState && handler.state == EClientState::LOBBY_CAMPAIGN)
|
||||
{
|
||||
lobby->bonusSel = std::make_shared<CBonusSelection>();
|
||||
GH.pushInt(lobby->bonusSel);
|
||||
GH.windows().pushWindow(lobby->bonusSel);
|
||||
}
|
||||
|
||||
if(lobby->bonusSel)
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/CGTownInstance.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
|
||||
PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
|
||||
: owner(owner)
|
||||
|
822
client/adventureMap/AdventureMapInterface.cpp
Normal file
@ -0,0 +1,822 @@
|
||||
/*
|
||||
* AdventureMapInterface.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "AdventureOptions.h"
|
||||
#include "AdventureState.h"
|
||||
#include "CInGameConsole.h"
|
||||
#include "CMinimap.h"
|
||||
#include "CList.h"
|
||||
#include "CInfoBar.h"
|
||||
#include "MapAudioPlayer.h"
|
||||
#include "AdventureMapWidget.h"
|
||||
#include "AdventureMapShortcuts.h"
|
||||
|
||||
#include "../mapView/mapHandler.h"
|
||||
#include "../mapView/MapView.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../CMT.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
std::shared_ptr<AdventureMapInterface> adventureInt;
|
||||
|
||||
AdventureMapInterface::AdventureMapInterface():
|
||||
mapAudio(new MapAudioPlayer()),
|
||||
spellBeingCasted(nullptr),
|
||||
scrollingWasActive(false),
|
||||
scrollingWasBlocked(false)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
|
||||
|
||||
shortcuts = std::make_shared<AdventureMapShortcuts>(*this);
|
||||
|
||||
widget = std::make_shared<AdventureMapWidget>(shortcuts);
|
||||
shortcuts->setState(EAdventureState::MAKING_TURN);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
|
||||
addUsedEvents(KEYBOARD | TIME);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel)
|
||||
{
|
||||
shortcuts->onMapViewMoved(visibleArea, mapLevel);
|
||||
widget->getMinimap()->onMapViewMoved(visibleArea, mapLevel);
|
||||
widget->onMapViewMoved(visibleArea, mapLevel);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onAudioResumed()
|
||||
{
|
||||
mapAudio->onAudioResumed();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onAudioPaused()
|
||||
{
|
||||
mapAudio->onAudioPaused();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero)
|
||||
{
|
||||
widget->getInfoBar()->popAll();
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
|
||||
{
|
||||
widget->getHeroList()->update(h);
|
||||
|
||||
if (h && h == LOCPLINT->localState->getCurrentHero() && !widget->getInfoBar()->showingComponents())
|
||||
widget->getInfoBar()->showSelection();
|
||||
|
||||
widget->updateActiveState();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTownChanged(const CGTownInstance * town)
|
||||
{
|
||||
widget->getTownList()->update(town);
|
||||
|
||||
if (town && town == LOCPLINT->localState->getCurrentTown() && !widget->getInfoBar()->showingComponents())
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer)
|
||||
{
|
||||
widget->getInfoBar()->pushComponents(components, message, timer);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::activate()
|
||||
{
|
||||
CIntObject::activate();
|
||||
|
||||
adjustActiveness();
|
||||
|
||||
screenBuf = screen;
|
||||
|
||||
if(LOCPLINT)
|
||||
{
|
||||
LOCPLINT->cingconsole->activate();
|
||||
LOCPLINT->cingconsole->pos = this->pos;
|
||||
}
|
||||
|
||||
GH.fakeMouseMove(); //to restore the cursor
|
||||
}
|
||||
|
||||
void AdventureMapInterface::deactivate()
|
||||
{
|
||||
CIntObject::deactivate();
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showAll(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::showAll(to);
|
||||
LOCPLINT->cingconsole->show(to);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::show(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::show(to);
|
||||
LOCPLINT->cingconsole->show(to);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::tick(uint32_t msPassed)
|
||||
{
|
||||
handleMapScrollingUpdate(msPassed);
|
||||
|
||||
// we want animations to be active during enemy turn but map itself to be non-interactive
|
||||
// so call timer update directly on inactive element
|
||||
widget->getMapView()->tick(msPassed);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::handleMapScrollingUpdate(uint32_t timePassed)
|
||||
{
|
||||
/// Width of window border, in pixels, that triggers map scrolling
|
||||
static constexpr uint32_t borderScrollWidth = 15;
|
||||
|
||||
uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float();
|
||||
uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000;
|
||||
|
||||
Point cursorPosition = GH.getCursorPosition();
|
||||
Point scrollDirection;
|
||||
|
||||
if (cursorPosition.x < borderScrollWidth)
|
||||
scrollDirection.x = -1;
|
||||
|
||||
if (cursorPosition.x > GH.screenDimensions().x - borderScrollWidth)
|
||||
scrollDirection.x = +1;
|
||||
|
||||
if (cursorPosition.y < borderScrollWidth)
|
||||
scrollDirection.y = -1;
|
||||
|
||||
if (cursorPosition.y > GH.screenDimensions().y - borderScrollWidth)
|
||||
scrollDirection.y = +1;
|
||||
|
||||
Point scrollDelta = scrollDirection * scrollDistance;
|
||||
|
||||
bool cursorInScrollArea = scrollDelta != Point(0,0);
|
||||
bool scrollingActive = cursorInScrollArea && active && shortcuts->optionSidePanelActive() && !scrollingWasBlocked;
|
||||
bool scrollingBlocked = GH.isKeyboardCtrlDown();
|
||||
|
||||
if (!scrollingWasActive && scrollingBlocked)
|
||||
{
|
||||
scrollingWasBlocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cursorInScrollArea && scrollingWasBlocked)
|
||||
{
|
||||
scrollingWasBlocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollingActive)
|
||||
widget->getMapView()->onMapScrolled(scrollDelta);
|
||||
|
||||
if (!scrollingActive && !scrollingWasActive)
|
||||
return;
|
||||
|
||||
if(scrollDelta.x > 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_EAST);
|
||||
}
|
||||
if(scrollDelta.x < 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_WEST);
|
||||
}
|
||||
|
||||
if (scrollDelta.x == 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTH);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
scrollingWasActive = scrollingActive;
|
||||
}
|
||||
|
||||
void AdventureMapInterface::centerOnTile(int3 on)
|
||||
{
|
||||
widget->getMapView()->onCenteredTile(on);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::centerOnObject(const CGObjectInstance * obj)
|
||||
{
|
||||
widget->getMapView()->onCenteredObject(obj);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::keyPressed(EShortcut key)
|
||||
{
|
||||
if (key == EShortcut::GLOBAL_CANCEL && spellBeingCasted)
|
||||
hotkeyAbortCastingMode();
|
||||
|
||||
//fake mouse use to trigger onTileHovered()
|
||||
GH.fakeMouseMove();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
|
||||
{
|
||||
assert(sel);
|
||||
|
||||
widget->getInfoBar()->popAll();
|
||||
mapAudio->onSelectionChanged(sel);
|
||||
bool centerView = !settings["session"]["autoSkip"].Bool();
|
||||
|
||||
if (centerView)
|
||||
centerOnObject(sel);
|
||||
|
||||
if(sel->ID==Obj::TOWN)
|
||||
{
|
||||
auto town = dynamic_cast<const CGTownInstance*>(sel);
|
||||
|
||||
widget->getInfoBar()->showTownSelection(town);
|
||||
widget->getTownList()->select(town);
|
||||
widget->getHeroList()->select(nullptr);
|
||||
onHeroChanged(nullptr);
|
||||
}
|
||||
else //hero selected
|
||||
{
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(sel);
|
||||
|
||||
widget->getInfoBar()->showHeroSelection(hero);
|
||||
widget->getHeroList()->select(hero);
|
||||
widget->getTownList()->select(nullptr);
|
||||
|
||||
LOCPLINT->localState->verifyPath(hero);
|
||||
onHeroChanged(hero);
|
||||
}
|
||||
|
||||
widget->updateActiveState();
|
||||
widget->getHeroList()->redraw();
|
||||
widget->getTownList()->redraw();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
|
||||
{
|
||||
if (positions)
|
||||
widget->getMinimap()->updateTiles(*positions);
|
||||
else
|
||||
widget->getMinimap()->update();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID)
|
||||
{
|
||||
onCurrentPlayerChanged(playerID);
|
||||
setState(EAdventureState::HOTSEAT_WAIT);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID)
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
mapAudio->onEnemyTurnStarted();
|
||||
widget->getMinimap()->setAIRadar(true);
|
||||
widget->getInfoBar()->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
|
||||
setState(EAdventureState::ENEMY_TURN);
|
||||
|
||||
}
|
||||
|
||||
void AdventureMapInterface::setState(EAdventureState state)
|
||||
{
|
||||
shortcuts->setState(state);
|
||||
adjustActiveness();
|
||||
widget->updateActiveState();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::adjustActiveness()
|
||||
{
|
||||
bool widgetMustBeActive = active && shortcuts->optionSidePanelActive();
|
||||
bool mapViewMustBeActive = active && (shortcuts->optionMapViewActive());
|
||||
|
||||
if (widgetMustBeActive && !widget->active)
|
||||
widget->activate();
|
||||
|
||||
if (!widgetMustBeActive && widget->active)
|
||||
widget->deactivate();
|
||||
|
||||
if (mapViewMustBeActive && !widget->getMapView()->active)
|
||||
widget->getMapView()->activate();
|
||||
|
||||
if (!mapViewMustBeActive && widget->getMapView()->active)
|
||||
widget->getMapView()->deactivate();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(nullptr);
|
||||
|
||||
if (playerID == currentPlayerID)
|
||||
return;
|
||||
|
||||
currentPlayerID = playerID;
|
||||
widget->setPlayer(playerID);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
|
||||
{
|
||||
onCurrentPlayerChanged(playerID);
|
||||
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
|
||||
|| settings["session"]["spectate"].Bool())
|
||||
{
|
||||
widget->getMinimap()->setAIRadar(false);
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
widget->getHeroList()->update();
|
||||
widget->getTownList()->update();
|
||||
|
||||
const CGHeroInstance * heroToSelect = nullptr;
|
||||
|
||||
// find first non-sleeping hero
|
||||
for (auto hero : LOCPLINT->localState->getWanderingHeroes())
|
||||
{
|
||||
if (!LOCPLINT->localState->isHeroSleeping(hero))
|
||||
{
|
||||
heroToSelect = hero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//select first hero if available.
|
||||
if (heroToSelect != nullptr)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(heroToSelect);
|
||||
}
|
||||
else if (LOCPLINT->localState->getOwnedTowns().size())
|
||||
{
|
||||
LOCPLINT->localState->setSelection(LOCPLINT->localState->getOwnedTown(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCPLINT->localState->setSelection(LOCPLINT->localState->getWanderingHero(0));
|
||||
}
|
||||
|
||||
//show new day animation and sound on infobar
|
||||
widget->getInfoBar()->showDate();
|
||||
|
||||
onHeroChanged(nullptr);
|
||||
showAll(screen);
|
||||
mapAudio->onPlayerTurnStarted();
|
||||
|
||||
if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
|
||||
{
|
||||
if(auto iw = GH.windows().topWindow<CInfoWindow>())
|
||||
iw->close();
|
||||
|
||||
hotkeyEndingTurn();
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyEndingTurn()
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
LOCPLINT->makingTurn = false;
|
||||
LOCPLINT->cb->endTurn();
|
||||
mapAudio->onPlayerTurnEnded();
|
||||
}
|
||||
|
||||
const CGObjectInstance* AdventureMapInterface::getActiveObject(const int3 &mapPos)
|
||||
{
|
||||
std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mapPos); //blocking objects at tile
|
||||
|
||||
if (bobjs.empty())
|
||||
return nullptr;
|
||||
|
||||
return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
//FIXME: this line breaks H3 behavior for Dimension Door
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
return;
|
||||
if(!LOCPLINT->makingTurn)
|
||||
return;
|
||||
|
||||
const TerrainTile *tile = LOCPLINT->cb->getTile(mapPos);
|
||||
|
||||
const CGObjectInstance *topBlocking = getActiveObject(mapPos);
|
||||
|
||||
int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
assert(shortcuts->optionSpellcasting());
|
||||
|
||||
if (!isInScreenRange(selPos, mapPos))
|
||||
return;
|
||||
|
||||
const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
|
||||
|
||||
switch(spellBeingCasted->id)
|
||||
{
|
||||
case SpellID::SCUTTLE_BOAT: //Scuttle Boat
|
||||
if(topBlocking && topBlocking->ID == Obj::BOAT)
|
||||
performSpellcasting(mapPos);
|
||||
break;
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
if(!tile || tile->isClear(heroTile))
|
||||
performSpellcasting(mapPos);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
//check if we can select this object
|
||||
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
|
||||
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
|
||||
|
||||
bool isHero = false;
|
||||
if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
|
||||
{
|
||||
if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
|
||||
LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
|
||||
else if(canSelect)
|
||||
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
||||
}
|
||||
else if(const CGHeroInstance * currentHero = LOCPLINT->localState->getCurrentHero()) //hero is selected
|
||||
{
|
||||
isHero = true;
|
||||
|
||||
const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
|
||||
if(currentHero == topBlocking) //clicked selected hero
|
||||
{
|
||||
LOCPLINT->openHeroWindow(currentHero);
|
||||
return;
|
||||
}
|
||||
else if(canSelect && pn->turns == 255 ) //selectable object at inaccessible tile
|
||||
{
|
||||
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
||||
return;
|
||||
}
|
||||
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
|
||||
{
|
||||
if(LOCPLINT->localState->hasPath(currentHero) &&
|
||||
LOCPLINT->localState->getPath(currentHero).endPos() == mapPos)//we'll be moving
|
||||
{
|
||||
if(!CGI->mh->hasOngoingAnimations())
|
||||
LOCPLINT->moveHero(currentHero, LOCPLINT->localState->getPath(currentHero));
|
||||
return;
|
||||
}
|
||||
else //remove old path and find a new one if we clicked on accessible tile
|
||||
{
|
||||
LOCPLINT->localState->setPath(currentHero, mapPos);
|
||||
onHeroChanged(currentHero);
|
||||
}
|
||||
}
|
||||
} //end of hero is selected "case"
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Nothing is selected...");
|
||||
}
|
||||
|
||||
const auto shipyard = ourInaccessibleShipyard(topBlocking);
|
||||
if(isHero && shipyard != nullptr)
|
||||
{
|
||||
LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileHovered(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
//may occur just at the start of game (fake move before full intiialization)
|
||||
if(!LOCPLINT->localState->getCurrentArmy())
|
||||
return;
|
||||
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
GH.statusbar()->clear();
|
||||
return;
|
||||
}
|
||||
auto objRelations = PlayerRelations::ALLIES;
|
||||
const CGObjectInstance *objAtTile = getActiveObject(mapPos);
|
||||
if(objAtTile)
|
||||
{
|
||||
objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
|
||||
std::string text = LOCPLINT->localState->getCurrentHero() ? objAtTile->getHoverText(LOCPLINT->localState->getCurrentHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
|
||||
boost::replace_all(text,"\n"," ");
|
||||
GH.statusbar()->write(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string hlp = CGI->mh->getTerrainDescr(mapPos, false);
|
||||
GH.statusbar()->write(hlp);
|
||||
}
|
||||
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
switch(spellBeingCasted->id)
|
||||
{
|
||||
case SpellID::SCUTTLE_BOAT:
|
||||
{
|
||||
int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
|
||||
if(objAtTile && objAtTile->ID == Obj::BOAT && isInScreenRange(hpos, mapPos))
|
||||
CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
return;
|
||||
}
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
{
|
||||
const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
|
||||
int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
|
||||
CCS->curh->set(Cursor::Map::TELEPORT);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(LOCPLINT->localState->getCurrentArmy()->ID == Obj::TOWN)
|
||||
{
|
||||
if(objAtTile)
|
||||
{
|
||||
if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
|
||||
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);
|
||||
}
|
||||
else if(const CGHeroInstance * hero = LOCPLINT->localState->getCurrentHero())
|
||||
{
|
||||
std::array<Cursor::Map, 4> cursorMove = { Cursor::Map::T1_MOVE, Cursor::Map::T2_MOVE, Cursor::Map::T3_MOVE, Cursor::Map::T4_MOVE, };
|
||||
std::array<Cursor::Map, 4> cursorAttack = { Cursor::Map::T1_ATTACK, Cursor::Map::T2_ATTACK, Cursor::Map::T3_ATTACK, Cursor::Map::T4_ATTACK, };
|
||||
std::array<Cursor::Map, 4> cursorSail = { Cursor::Map::T1_SAIL, Cursor::Map::T2_SAIL, Cursor::Map::T3_SAIL, Cursor::Map::T4_SAIL, };
|
||||
std::array<Cursor::Map, 4> cursorDisembark = { Cursor::Map::T1_DISEMBARK, Cursor::Map::T2_DISEMBARK, Cursor::Map::T3_DISEMBARK, Cursor::Map::T4_DISEMBARK, };
|
||||
std::array<Cursor::Map, 4> cursorExchange = { Cursor::Map::T1_EXCHANGE, Cursor::Map::T2_EXCHANGE, Cursor::Map::T3_EXCHANGE, Cursor::Map::T4_EXCHANGE, };
|
||||
std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, };
|
||||
std::array<Cursor::Map, 4> cursorSailVisit = { Cursor::Map::T1_SAIL_VISIT, Cursor::Map::T2_SAIL_VISIT, Cursor::Map::T3_SAIL_VISIT, Cursor::Map::T4_SAIL_VISIT, };
|
||||
|
||||
const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
|
||||
assert(pathNode);
|
||||
|
||||
if((GH.isKeyboardAltDown() || settings["gameTweaks"]["forceMovementInfo"].Bool()) && pathNode->reachable()) //overwrite status bar text with movement info
|
||||
{
|
||||
showMoveDetailsInStatusbar(*hero, *pathNode);
|
||||
}
|
||||
|
||||
int turns = pathNode->turns;
|
||||
vstd::amin(turns, 3);
|
||||
switch(pathNode->action)
|
||||
{
|
||||
case CGPathNode::NORMAL:
|
||||
case CGPathNode::TELEPORT_NORMAL:
|
||||
if(pathNode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->set(cursorMove[turns]);
|
||||
else
|
||||
CCS->curh->set(cursorSailVisit[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::VISIT:
|
||||
case CGPathNode::BLOCKING_VISIT:
|
||||
case CGPathNode::TELEPORT_BLOCKING_VISIT:
|
||||
if(objAtTile && objAtTile->ID == Obj::HERO)
|
||||
{
|
||||
if(LOCPLINT->localState->getCurrentArmy() == objAtTile)
|
||||
CCS->curh->set(Cursor::Map::HERO);
|
||||
else
|
||||
CCS->curh->set(cursorExchange[turns]);
|
||||
}
|
||||
else if(pathNode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->set(cursorVisit[turns]);
|
||||
else
|
||||
CCS->curh->set(cursorSailVisit[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::BATTLE:
|
||||
case CGPathNode::TELEPORT_BATTLE:
|
||||
CCS->curh->set(cursorAttack[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::EMBARK:
|
||||
CCS->curh->set(cursorSail[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::DISEMBARK:
|
||||
CCS->curh->set(cursorDisembark[turns]);
|
||||
break;
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ourInaccessibleShipyard(objAtTile))
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::T1_SAIL);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
|
||||
{
|
||||
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
|
||||
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
|
||||
const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
|
||||
|
||||
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, "%POINTS", std::to_string(movementPointsLastTurnCost));
|
||||
boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
|
||||
|
||||
GH.statusbar()->write(result);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileRightClicked(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
hotkeyAbortCastingMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
{
|
||||
CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory
|
||||
return;
|
||||
}
|
||||
|
||||
const CGObjectInstance * obj = getActiveObject(mapPos);
|
||||
if(!obj)
|
||||
{
|
||||
// Bare or undiscovered terrain
|
||||
const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
|
||||
if(tile)
|
||||
{
|
||||
std::string hlp = CGI->mh->getTerrainDescr(mapPos, true);
|
||||
CRClickPopup::createAndPush(hlp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::enterCastingMode(const CSpell * sp)
|
||||
{
|
||||
assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
|
||||
spellBeingCasted = sp;
|
||||
Settings config = settings.write["session"]["showSpellRange"];
|
||||
config->Bool() = true;
|
||||
|
||||
setState(EAdventureState::CASTING_SPELL);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::exitCastingMode()
|
||||
{
|
||||
assert(spellBeingCasted);
|
||||
spellBeingCasted = nullptr;
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
|
||||
Settings config = settings.write["session"]["showSpellRange"];
|
||||
config->Bool() = false;
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyAbortCastingMode()
|
||||
{
|
||||
exitCastingMode();
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
|
||||
}
|
||||
|
||||
void AdventureMapInterface::performSpellcasting(const int3 & dest)
|
||||
{
|
||||
SpellID id = spellBeingCasted->id;
|
||||
exitCastingMode();
|
||||
LOCPLINT->cb->castSpell(LOCPLINT->localState->getCurrentHero(), id, dest);
|
||||
}
|
||||
|
||||
Rect AdventureMapInterface::terrainAreaPixels() const
|
||||
{
|
||||
return widget->getMapView()->pos;
|
||||
}
|
||||
|
||||
const IShipyard * AdventureMapInterface::ourInaccessibleShipyard(const CGObjectInstance *obj) const
|
||||
{
|
||||
const IShipyard *ret = IShipyard::castFrom(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()
|
||||
{
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView(int tileSize)
|
||||
{
|
||||
setState(EAdventureState::WORLD_VIEW);
|
||||
widget->getMapView()->onViewWorldActivated(tileSize);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView()
|
||||
{
|
||||
openWorldView(11);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain)
|
||||
{
|
||||
openWorldView(11);
|
||||
widget->getMapView()->onViewSpellActivated(11, objectPositions, showTerrain);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyNextTown()
|
||||
{
|
||||
widget->getTownList()->selectNext();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeySwitchMapLevel()
|
||||
{
|
||||
widget->getMapView()->onMapLevelSwitched();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onScreenResize()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
widget.reset();
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
widget = std::make_shared<AdventureMapWidget>(shortcuts);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
widget->setPlayer(currentPlayerID);
|
||||
widget->updateActiveState();
|
||||
widget->getMinimap()->update();
|
||||
widget->getInfoBar()->showSelection();
|
||||
|
||||
adjustActiveness();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* CAdvMapInt.h, part of VCMI engine
|
||||
* AdventureMapInterface.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -29,8 +29,8 @@ class CButton;
|
||||
class IImage;
|
||||
class CAnimImage;
|
||||
class CGStatusBar;
|
||||
class CAdvMapPanel;
|
||||
class CAdvMapWorldViewPanel;
|
||||
class AdventureMapWidget;
|
||||
class AdventureMapShortcuts;
|
||||
class CAnimation;
|
||||
class MapView;
|
||||
class CResDataBar;
|
||||
@ -39,122 +39,76 @@ class CTownList;
|
||||
class CInfoBar;
|
||||
class CMinimap;
|
||||
class MapAudioPlayer;
|
||||
enum class EAdventureState;
|
||||
|
||||
struct MapDrawingInfo;
|
||||
|
||||
/// That's a huge class which handles general adventure map actions and
|
||||
/// shows the right menu(questlog, spellbook, end turn,..) from where you
|
||||
/// can get to the towns and heroes.
|
||||
class CAdventureMapInterface : public CIntObject
|
||||
class AdventureMapInterface : public CIntObject
|
||||
{
|
||||
private:
|
||||
enum class EGameState
|
||||
{
|
||||
NOT_INITIALIZED,
|
||||
HOTSEAT_WAIT,
|
||||
MAKING_TURN,
|
||||
ENEMY_TURN,
|
||||
WORLD_VIEW
|
||||
};
|
||||
|
||||
EGameState state;
|
||||
|
||||
/// currently acting player
|
||||
PlayerColor currentPlayerID;
|
||||
|
||||
/// uses EDirections enum
|
||||
bool scrollingCursorSet;
|
||||
/// if true, cursor was changed to scrolling and must be reset back once scroll is over
|
||||
bool scrollingWasActive;
|
||||
|
||||
const CSpell *spellBeingCasted; //nullptr if none
|
||||
/// if true, then scrolling was blocked via ctrl and should not restart until player move cursor outside scrolling area
|
||||
bool scrollingWasBlocked;
|
||||
|
||||
std::vector<std::shared_ptr<CAnimImage>> gems;
|
||||
|
||||
std::shared_ptr<IImage> bg;
|
||||
std::shared_ptr<IImage> bgWorldView;
|
||||
std::shared_ptr<CButton> kingOverview;
|
||||
std::shared_ptr<CButton> sleepWake;
|
||||
std::shared_ptr<CButton> underground;
|
||||
std::shared_ptr<CButton> questlog;
|
||||
std::shared_ptr<CButton> moveHero;
|
||||
std::shared_ptr<CButton> spellbook;
|
||||
std::shared_ptr<CButton> advOptions;
|
||||
std::shared_ptr<CButton> sysOptions;
|
||||
std::shared_ptr<CButton> nextHero;
|
||||
std::shared_ptr<CButton> endTurn;
|
||||
std::shared_ptr<CButton> worldViewUnderground;
|
||||
|
||||
std::shared_ptr<MapView> terrain;
|
||||
std::shared_ptr<CMinimap> minimap;
|
||||
std::shared_ptr<CHeroList> heroList;
|
||||
std::shared_ptr<CTownList> townList;
|
||||
std::shared_ptr<CInfoBar> infoBar;
|
||||
std::shared_ptr<CGStatusBar> statusbar;
|
||||
std::shared_ptr<CResDataBar> resdatabar;
|
||||
|
||||
std::shared_ptr<CAdvMapPanel> panelMain; // panel that holds all right-side buttons in normal view
|
||||
std::shared_ptr<CAdvMapWorldViewPanel> panelWorldView; // panel that holds all buttons and other ui in world view
|
||||
std::shared_ptr<CAdvMapPanel> activeMapPanel; // currently active panel (either main or world view, depending on current mode)
|
||||
|
||||
std::shared_ptr<CAnimation> worldViewIcons;// images for world view overlay
|
||||
/// spell for which player is selecting target, or nullptr if none
|
||||
const CSpell *spellBeingCasted;
|
||||
|
||||
std::shared_ptr<MapAudioPlayer> mapAudio;
|
||||
std::shared_ptr<AdventureMapWidget> widget;
|
||||
std::shared_ptr<AdventureMapShortcuts> shortcuts;
|
||||
|
||||
private:
|
||||
//functions bound to buttons
|
||||
void fshowOverview();
|
||||
void fworldViewBack();
|
||||
void fworldViewScale1x();
|
||||
void fworldViewScale2x();
|
||||
void fworldViewScale4x();
|
||||
void fswitchLevel();
|
||||
void fshowQuestlog();
|
||||
void fsleepWake();
|
||||
void fmoveHero();
|
||||
void fshowSpellbok();
|
||||
void fadventureOPtions();
|
||||
void fsystemOptions();
|
||||
void fnextHero();
|
||||
void fendTurn();
|
||||
void setState(EAdventureState state);
|
||||
|
||||
void hotkeyMoveHeroDirectional(Point direction);
|
||||
/// updates active state of game window whenever game state changes
|
||||
void adjustActiveness();
|
||||
|
||||
bool isActive();
|
||||
void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
|
||||
/// checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
|
||||
|
||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
||||
|
||||
// update locked state of buttons
|
||||
void updateButtons();
|
||||
|
||||
void handleMapScrollingUpdate();
|
||||
/// check and if necessary reacts on scrolling by moving cursor to screen edge
|
||||
void handleMapScrollingUpdate(uint32_t msPassed);
|
||||
|
||||
void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
|
||||
|
||||
const CGObjectInstance *getActiveObject(const int3 &tile);
|
||||
|
||||
std::optional<Point> keyToMoveDirection(EShortcut key);
|
||||
|
||||
void endingTurn();
|
||||
|
||||
/// exits currently opened world view mode and returns to normal map
|
||||
void exitWorldView();
|
||||
void exitCastingMode();
|
||||
void leaveCastingMode(const int3 & castTarget);
|
||||
void abortCastingMode();
|
||||
|
||||
/// casts current spell at specified location
|
||||
void performSpellcasting(const int3 & castTarget);
|
||||
|
||||
protected:
|
||||
// CIntObject interface implementation
|
||||
/// CIntObject interface implementation
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void tick(uint32_t msPassed) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
|
||||
void keyPressed(EShortcut key) override;
|
||||
|
||||
void onScreenResize() override;
|
||||
|
||||
public:
|
||||
CAdventureMapInterface();
|
||||
AdventureMapInterface();
|
||||
|
||||
void hotkeyAbortCastingMode();
|
||||
void hotkeyExitWorldView();
|
||||
void hotkeyEndingTurn();
|
||||
void hotkeyNextTown();
|
||||
void hotkeySwitchMapLevel();
|
||||
|
||||
/// Called by PlayerInterface when specified player is ready to start his turn
|
||||
void onHotseatWaitStarted(PlayerColor playerID);
|
||||
@ -225,4 +179,4 @@ public:
|
||||
void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
|
||||
};
|
||||
|
||||
extern std::shared_ptr<CAdventureMapInterface> adventureInt;
|
||||
extern std::shared_ptr<AdventureMapInterface> adventureInt;
|
447
client/adventureMap/AdventureMapShortcuts.cpp
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
* AdventureMapShortcuts.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "AdventureMapShortcuts.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../lobby/CSavingScreen.h"
|
||||
#include "../mapView/mapHandler.h"
|
||||
#include "../windows/CKingdomInterface.h"
|
||||
#include "../windows/CSpellWindow.h"
|
||||
#include "../windows/CTradeWindow.h"
|
||||
#include "../windows/settings/SettingsMainWindow.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
#include "AdventureOptions.h"
|
||||
#include "AdventureState.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
AdventureMapShortcuts::AdventureMapShortcuts(AdventureMapInterface & owner)
|
||||
: owner(owner)
|
||||
, state(EAdventureState::NOT_INITIALIZED)
|
||||
, mapLevel(0)
|
||||
{}
|
||||
|
||||
void AdventureMapShortcuts::setState(EAdventureState newState)
|
||||
{
|
||||
state = newState;
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::onMapViewMoved(const Rect & visibleArea, int newMapLevel)
|
||||
{
|
||||
mapLevel = newMapLevel;
|
||||
}
|
||||
|
||||
std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
|
||||
{
|
||||
std::vector<AdventureMapShortcutState> result = {
|
||||
{ EShortcut::ADVENTURE_KINGDOM_OVERVIEW, optionInMapView(), [this]() { this->showOverview(); } },
|
||||
{ EShortcut::ADVENTURE_EXIT_WORLD_VIEW, optionInWorldView(), [this]() { this->worldViewBack(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD, optionInMapView(), [this]() { this->worldViewScale1x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X1, optionInWorldView(), [this]() { this->worldViewScale1x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X2, optionInWorldView(), [this]() { this->worldViewScale2x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X4, optionInWorldView(), [this]() { this->worldViewScale4x(); } },
|
||||
{ EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL, optionCanToggleLevel(), [this]() { this->switchMapLevel(); } },
|
||||
{ EShortcut::ADVENTURE_QUEST_LOG, optionCanViewQuests(), [this]() { this->showQuestlog(); } },
|
||||
{ EShortcut::ADVENTURE_TOGGLE_SLEEP, optionHeroSelected(), [this]() { this->toggleSleepWake(); } },
|
||||
{ EShortcut::ADVENTURE_SET_HERO_ASLEEP, optionHeroAwake(), [this]() { this->setHeroSleeping(); } },
|
||||
{ EShortcut::ADVENTURE_SET_HERO_AWAKE, optionHeroSleeping(), [this]() { this->setHeroAwake(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO, optionHeroCanMove(), [this]() { this->moveHeroAlongPath(); } },
|
||||
{ EShortcut::ADVENTURE_CAST_SPELL, optionHeroSelected(), [this]() { this->showSpellbook(); } },
|
||||
{ EShortcut::ADVENTURE_GAME_OPTIONS, optionInMapView(), [this]() { this->adventureOptions(); } },
|
||||
{ EShortcut::GLOBAL_OPTIONS, optionInMapView(), [this]() { this->systemOptions(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_HERO, optionHasNextHero(), [this]() { this->nextHero(); } },
|
||||
{ EShortcut::GAME_END_TURN, optionInMapView(), [this]() { this->endTurn(); } },
|
||||
{ EShortcut::ADVENTURE_THIEVES_GUILD, optionInMapView(), [this]() { this->showThievesGuild(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SCENARIO, optionInMapView(), [this]() { this->showScenarioInfo(); } },
|
||||
{ EShortcut::GAME_SAVE_GAME, optionInMapView(), [this]() { this->saveGame(); } },
|
||||
{ EShortcut::GAME_LOAD_GAME, optionInMapView(), [this]() { this->loadGame(); } },
|
||||
{ EShortcut::ADVENTURE_DIG_GRAIL, optionHeroSelected(), [this]() { this->digGrail(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_PUZZLE, optionSidePanelActive(),[this]() { this->viewPuzzleMap(); } },
|
||||
{ EShortcut::GAME_RESTART_GAME, optionInMapView(), [this]() { this->restartGame(); } },
|
||||
{ EShortcut::ADVENTURE_VISIT_OBJECT, optionHeroSelected(), [this]() { this->visitObject(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SELECTED, optionInMapView(), [this]() { this->openObject(); } },
|
||||
{ EShortcut::GAME_OPEN_MARKETPLACE, optionInMapView(), [this]() { this->showMarketplace(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_TOWN, optionInMapView(), [this]() { this->nextTown(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_OBJECT, optionInMapView(), [this]() { this->nextObject(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SS, optionHeroSelected(), [this]() { this->moveHeroDirectional({ 0, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_WW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, 0}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_EE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, 0}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, -1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NN, optionHeroSelected(), [this]() { this->moveHeroDirectional({ 0, -1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, -1}); } }
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showOverview()
|
||||
{
|
||||
GH.windows().createAndPushWindow<CKingdomInterface>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewBack()
|
||||
{
|
||||
owner.hotkeyExitWorldView();
|
||||
|
||||
auto hero = LOCPLINT->localState->getCurrentHero();
|
||||
if (hero)
|
||||
owner.centerOnObject(hero);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale1x()
|
||||
{
|
||||
// TODO set corresponding scale button to "selected" mode
|
||||
owner.openWorldView(7);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale2x()
|
||||
{
|
||||
owner.openWorldView(11);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale4x()
|
||||
{
|
||||
owner.openWorldView(16);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::switchMapLevel()
|
||||
{
|
||||
int maxLevels = LOCPLINT->cb->getMapSize().z;
|
||||
if (maxLevels < 2)
|
||||
return;
|
||||
|
||||
owner.hotkeySwitchMapLevel();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showQuestlog()
|
||||
{
|
||||
LOCPLINT->showQuestLog();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::toggleSleepWake()
|
||||
{
|
||||
if (!optionHeroSelected())
|
||||
return;
|
||||
|
||||
if (optionHeroAwake())
|
||||
setHeroSleeping();
|
||||
else
|
||||
setHeroAwake();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::setHeroSleeping()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (h)
|
||||
{
|
||||
LOCPLINT->localState->setHeroAsleep(h);
|
||||
owner.onHeroChanged(h);
|
||||
nextHero();
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::setHeroAwake()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (h)
|
||||
{
|
||||
LOCPLINT->localState->setHeroAwaken(h);
|
||||
owner.onHeroChanged(h);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::moveHeroAlongPath()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (!h || !LOCPLINT->localState->hasPath(h))
|
||||
return;
|
||||
|
||||
LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showSpellbook()
|
||||
{
|
||||
if (!LOCPLINT->localState->getCurrentHero())
|
||||
return;
|
||||
|
||||
owner.centerOnObject(LOCPLINT->localState->getCurrentHero());
|
||||
|
||||
GH.windows().createAndPushWindow<CSpellWindow>(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::adventureOptions()
|
||||
{
|
||||
GH.windows().createAndPushWindow<AdventureOptions>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::systemOptions()
|
||||
{
|
||||
GH.windows().createAndPushWindow<SettingsMainWindow>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextHero()
|
||||
{
|
||||
const auto * currHero = LOCPLINT->localState->getCurrentHero();
|
||||
const auto * nextHero = LOCPLINT->localState->getNextWanderingHero(currHero);
|
||||
|
||||
if (nextHero)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(nextHero);
|
||||
owner.centerOnObject(nextHero);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::endTurn()
|
||||
{
|
||||
if(!LOCPLINT->makingTurn)
|
||||
return;
|
||||
|
||||
if(settings["adventure"]["heroReminder"].Bool())
|
||||
{
|
||||
for(auto hero : LOCPLINT->localState->getWanderingHeroes())
|
||||
{
|
||||
if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0)
|
||||
{
|
||||
// Only show hero reminder if conditions met:
|
||||
// - There still movement points
|
||||
// - Hero don't have a path or there not points for first step on path
|
||||
LOCPLINT->localState->verifyPath(hero);
|
||||
|
||||
if(!LOCPLINT->localState->hasPath(hero))
|
||||
{
|
||||
LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = LOCPLINT->localState->getPath(hero);
|
||||
if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns)
|
||||
{
|
||||
LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
owner.hotkeyEndingTurn();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showThievesGuild()
|
||||
{
|
||||
//find first town with tavern
|
||||
auto itr = range::find_if(LOCPLINT->localState->getOwnedTowns(), [](const CGTownInstance * town)
|
||||
{
|
||||
return town->hasBuilt(BuildingID::TAVERN);
|
||||
});
|
||||
|
||||
if(itr != LOCPLINT->localState->getOwnedTowns().end())
|
||||
LOCPLINT->showThievesGuildWindow(*itr);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showScenarioInfo()
|
||||
{
|
||||
AdventureOptions::showScenarioInfo();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::saveGame()
|
||||
{
|
||||
GH.windows().createAndPushWindow<CSavingScreen>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::loadGame()
|
||||
{
|
||||
LOCPLINT->proposeLoadingGame();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::digGrail()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
|
||||
if(h && LOCPLINT->makingTurn)
|
||||
LOCPLINT->tryDiggging(h);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::viewPuzzleMap()
|
||||
{
|
||||
LOCPLINT->showPuzzleMap();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::restartGame()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
|
||||
[](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::visitObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
|
||||
if(h)
|
||||
LOCPLINT->cb->moveHero(h,h->pos);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::openObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
const CGTownInstance *t = LOCPLINT->localState->getCurrentTown();
|
||||
if(h)
|
||||
LOCPLINT->openHeroWindow(h);
|
||||
|
||||
if(t)
|
||||
LOCPLINT->openTownWindow(t);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showMarketplace()
|
||||
{
|
||||
//check if we have any marketplace
|
||||
const CGTownInstance *townWithMarket = nullptr;
|
||||
for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo())
|
||||
{
|
||||
if(t->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
townWithMarket = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(townWithMarket) //if any town has marketplace, open window
|
||||
GH.windows().createAndPushWindow<CMarketplaceWindow>(townWithMarket);
|
||||
else //if not - complain
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextTown()
|
||||
{
|
||||
owner.hotkeyNextTown();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
const CGTownInstance *t = LOCPLINT->localState->getCurrentTown();
|
||||
if(h)
|
||||
nextHero();
|
||||
|
||||
if(t)
|
||||
nextTown();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero
|
||||
|
||||
if(!h)
|
||||
return;
|
||||
|
||||
if (CGI->mh->hasOngoingAnimations())
|
||||
return;
|
||||
|
||||
int3 dst = h->visitablePos() + int3(direction.x, direction.y, 0);
|
||||
|
||||
if (!CGI->mh->isInMap((dst)))
|
||||
return;
|
||||
|
||||
if ( !LOCPLINT->localState->setPath(h, dst))
|
||||
return;
|
||||
|
||||
const CGPath & path = LOCPLINT->localState->getPath(h);
|
||||
|
||||
if (path.nodes.size() > 2)
|
||||
owner.onHeroChanged(h);
|
||||
else
|
||||
if(path.nodes[0].turns == 0)
|
||||
LOCPLINT->moveHero(h, path);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionCanViewQuests()
|
||||
{
|
||||
return optionInMapView() && CGI->mh->getMap()->quests.empty();
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionCanToggleLevel()
|
||||
{
|
||||
return optionInMapView() && LOCPLINT->cb->getMapSize().z > 0;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionMapLevelSurface()
|
||||
{
|
||||
return mapLevel == 0;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroSleeping()
|
||||
{
|
||||
const CGHeroInstance *hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && LOCPLINT->localState->isHeroSleeping(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroAwake()
|
||||
{
|
||||
const CGHeroInstance *hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && !LOCPLINT->localState->isHeroSleeping(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroSelected()
|
||||
{
|
||||
return optionInMapView() && LOCPLINT->localState->getCurrentHero() != nullptr;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroCanMove()
|
||||
{
|
||||
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && hero->movement != 0 && LOCPLINT->localState->hasPath(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHasNextHero()
|
||||
{
|
||||
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||
const auto * nextSuitableHero = LOCPLINT->localState->getNextWanderingHero(hero);
|
||||
|
||||
return optionInMapView() && nextSuitableHero != nullptr;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionSpellcasting()
|
||||
{
|
||||
return state == EAdventureState::CASTING_SPELL;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionInMapView()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionInWorldView()
|
||||
{
|
||||
return state == EAdventureState::WORLD_VIEW;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionSidePanelActive()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionMapViewActive()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::CASTING_SPELL;
|
||||
}
|
87
client/adventureMap/AdventureMapShortcuts.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* AdventureMapShortcuts.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
class Rect;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
enum class EShortcut;
|
||||
class AdventureMapInterface;
|
||||
enum class EAdventureState;
|
||||
|
||||
struct AdventureMapShortcutState
|
||||
{
|
||||
EShortcut shortcut;
|
||||
bool isEnabled;
|
||||
std::function<void()> callback;
|
||||
};
|
||||
|
||||
/// Class that contains list of functions for shortcuts available from adventure map
|
||||
class AdventureMapShortcuts
|
||||
{
|
||||
AdventureMapInterface & owner;
|
||||
EAdventureState state;
|
||||
int mapLevel;
|
||||
|
||||
void showOverview();
|
||||
void worldViewBack();
|
||||
void worldViewScale1x();
|
||||
void worldViewScale2x();
|
||||
void worldViewScale4x();
|
||||
void switchMapLevel();
|
||||
void showQuestlog();
|
||||
void toggleSleepWake();
|
||||
void setHeroSleeping();
|
||||
void setHeroAwake();
|
||||
void moveHeroAlongPath();
|
||||
void showSpellbook();
|
||||
void adventureOptions();
|
||||
void systemOptions();
|
||||
void nextHero();
|
||||
void endTurn();
|
||||
void showThievesGuild();
|
||||
void showScenarioInfo();
|
||||
void saveGame();
|
||||
void loadGame();
|
||||
void digGrail();
|
||||
void viewPuzzleMap();
|
||||
void restartGame();
|
||||
void visitObject();
|
||||
void openObject();
|
||||
void showMarketplace();
|
||||
void nextTown();
|
||||
void nextObject();
|
||||
void moveHeroDirectional(const Point & direction);
|
||||
|
||||
public:
|
||||
explicit AdventureMapShortcuts(AdventureMapInterface & owner);
|
||||
|
||||
std::vector<AdventureMapShortcutState> getShortcuts();
|
||||
|
||||
bool optionCanViewQuests();
|
||||
bool optionCanToggleLevel();
|
||||
bool optionMapLevelSurface();
|
||||
bool optionHeroSleeping();
|
||||
bool optionHeroAwake();
|
||||
bool optionHeroSelected();
|
||||
bool optionHeroCanMove();
|
||||
bool optionHasNextHero();
|
||||
bool optionSpellcasting();
|
||||
bool optionInMapView();
|
||||
bool optionInWorldView();
|
||||
bool optionSidePanelActive();
|
||||
bool optionMapViewActive();
|
||||
|
||||
void setState(EAdventureState newState);
|
||||
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
|
||||
};
|
455
client/adventureMap/AdventureMapWidget.cpp
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* CAdventureMapWidget.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "AdventureMapWidget.h"
|
||||
|
||||
#include "AdventureMapShortcuts.h"
|
||||
#include "CInfoBar.h"
|
||||
#include "CList.h"
|
||||
#include "CMinimap.h"
|
||||
#include "CResDataBar.h"
|
||||
#include "AdventureState.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../mapView/MapView.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
|
||||
#include "../../lib/StringConstants.h"
|
||||
#include "../../lib/filesystem/ResourceID.h"
|
||||
|
||||
AdventureMapWidget::AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts )
|
||||
: shortcuts(shortcuts)
|
||||
, mapLevel(0)
|
||||
{
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
REGISTER_BUILDER("adventureInfobar", &AdventureMapWidget::buildInfobox );
|
||||
REGISTER_BUILDER("adventureMapImage", &AdventureMapWidget::buildMapImage );
|
||||
REGISTER_BUILDER("adventureMapButton", &AdventureMapWidget::buildMapButton );
|
||||
REGISTER_BUILDER("adventureMapContainer", &AdventureMapWidget::buildMapContainer );
|
||||
REGISTER_BUILDER("adventureMapGameArea", &AdventureMapWidget::buildMapGameArea );
|
||||
REGISTER_BUILDER("adventureMapHeroList", &AdventureMapWidget::buildMapHeroList );
|
||||
REGISTER_BUILDER("adventureMapIcon", &AdventureMapWidget::buildMapIcon );
|
||||
REGISTER_BUILDER("adventureMapTownList", &AdventureMapWidget::buildMapTownList );
|
||||
REGISTER_BUILDER("adventureMinimap", &AdventureMapWidget::buildMinimap );
|
||||
REGISTER_BUILDER("adventureResourceDateBar", &AdventureMapWidget::buildResourceDateBar );
|
||||
REGISTER_BUILDER("adventureStatusBar", &AdventureMapWidget::buildStatusBar );
|
||||
REGISTER_BUILDER("adventurePlayerTexture", &AdventureMapWidget::buildTexturePlayerColored);
|
||||
|
||||
for (const auto & entry : shortcuts->getShortcuts())
|
||||
addShortcut(entry.shortcut, entry.callback);
|
||||
|
||||
const JsonNode config(ResourceID("config/widgets/adventureMap.json"));
|
||||
|
||||
for(const auto & entry : config["options"]["imagesPlayerColored"].Vector())
|
||||
{
|
||||
ResourceID resourceName(entry.String(), EResType::IMAGE);
|
||||
playerColorerImages.push_back(resourceName.getName());
|
||||
}
|
||||
|
||||
build(config);
|
||||
addUsedEvents(KEYBOARD);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::onMapViewMoved(const Rect & visibleArea, int newMapLevel)
|
||||
{
|
||||
if(mapLevel == newMapLevel)
|
||||
return;
|
||||
|
||||
mapLevel = newMapLevel;
|
||||
updateActiveState();
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readSourceArea(const JsonNode & source, const JsonNode & sourceCommon)
|
||||
{
|
||||
const auto & input = source.isNull() ? sourceCommon : source;
|
||||
|
||||
return readArea(input, Rect(Point(0, 0), Point(800, 600)));
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readTargetArea(const JsonNode & source)
|
||||
{
|
||||
if(subwidgetSizes.empty())
|
||||
return readArea(source, pos);
|
||||
return readArea(source, subwidgetSizes.back());
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readArea(const JsonNode & source, const Rect & boundingBox)
|
||||
{
|
||||
const auto & object = source.Struct();
|
||||
|
||||
if(object.count("left") + object.count("width") + object.count("right") != 2)
|
||||
logGlobal->error("Invalid area definition in widget! Unable to load width!");
|
||||
|
||||
if(object.count("top") + object.count("height") + object.count("bottom") != 2)
|
||||
logGlobal->error("Invalid area definition in widget! Unable to load height!");
|
||||
|
||||
int left = source["left"].Integer();
|
||||
int width = source["width"].Integer();
|
||||
int right = source["right"].Integer();
|
||||
|
||||
int top = source["top"].Integer();
|
||||
int height = source["height"].Integer();
|
||||
int bottom = source["bottom"].Integer();
|
||||
|
||||
Point topLeft(left, top);
|
||||
Point dimensions(width, height);
|
||||
|
||||
if(source["left"].isNull())
|
||||
topLeft.x = boundingBox.w - right - width;
|
||||
|
||||
if(source["width"].isNull())
|
||||
dimensions.x = boundingBox.w - right - left;
|
||||
|
||||
if(source["top"].isNull())
|
||||
topLeft.y = boundingBox.h - bottom - height;
|
||||
|
||||
if(source["height"].isNull())
|
||||
dimensions.y = boundingBox.h - bottom - top;
|
||||
|
||||
return Rect(topLeft + boundingBox.topLeft(), dimensions);
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> AdventureMapWidget::loadImage(const std::string & name)
|
||||
{
|
||||
ResourceID resource(name, EResType::IMAGE);
|
||||
|
||||
if(images.count(resource.getName()) == 0)
|
||||
images[resource.getName()] = IImage::createFromFile(resource.getName());
|
||||
|
||||
return images[resource.getName()];
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> AdventureMapWidget::loadAnimation(const std::string & name)
|
||||
{
|
||||
ResourceID resource(name, EResType::ANIMATION);
|
||||
|
||||
if(animations.count(resource.getName()) == 0)
|
||||
animations[resource.getName()] = std::make_shared<CAnimation>(resource.getName());
|
||||
|
||||
return animations[resource.getName()];
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildInfobox(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
infoBar = std::make_shared<CInfoBar>(area);
|
||||
return infoBar;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapImage(const JsonNode & input)
|
||||
{
|
||||
Rect targetArea = readTargetArea(input["area"]);
|
||||
Rect sourceArea = readSourceArea(input["sourceArea"], input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
return std::make_shared<CFilledTexture>(loadImage(image), targetArea, sourceArea);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapButton(const JsonNode & input)
|
||||
{
|
||||
auto position = readTargetArea(input["area"]);
|
||||
auto image = input["image"].String();
|
||||
auto help = readHintText(input["help"]);
|
||||
bool playerColored = input["playerColored"].Bool();
|
||||
|
||||
auto button = std::make_shared<CButton>(position.topLeft(), image, help, 0, EShortcut::NONE, playerColored);
|
||||
|
||||
loadButtonBorderColor(button, input["borderColor"]);
|
||||
loadButtonHotkey(button, input["hotkey"]);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapContainer(const JsonNode & input)
|
||||
{
|
||||
auto position = readTargetArea(input["area"]);
|
||||
std::shared_ptr<CAdventureMapContainerWidget> result;
|
||||
|
||||
if (!input["exists"].isNull())
|
||||
{
|
||||
if (!input["exists"]["heightMin"].isNull() && input["exists"]["heightMin"].Integer() >= pos.h)
|
||||
return nullptr;
|
||||
if (!input["exists"]["heightMax"].isNull() && input["exists"]["heightMax"].Integer() < pos.h)
|
||||
return nullptr;
|
||||
if (!input["exists"]["widthMin"].isNull() && input["exists"]["widthMin"].Integer() >= pos.w)
|
||||
return nullptr;
|
||||
if (!input["exists"]["widthMax"].isNull() && input["exists"]["widthMax"].Integer() < pos.w)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (input["overlay"].Bool())
|
||||
result = std::make_shared<CAdventureMapOverlayWidget>();
|
||||
else
|
||||
result = std::make_shared<CAdventureMapContainerWidget>();
|
||||
|
||||
result->disableCondition = input["hideWhen"].String();
|
||||
|
||||
result->moveBy(position.topLeft());
|
||||
subwidgetSizes.push_back(position);
|
||||
for(const auto & entry : input["items"].Vector())
|
||||
{
|
||||
auto widget = buildWidget(entry);
|
||||
|
||||
addWidget(entry["name"].String(), widget);
|
||||
result->ownedChildren.push_back(widget);
|
||||
|
||||
// FIXME: remove cast and replace it with better check
|
||||
if (std::dynamic_pointer_cast<CLabel>(widget) || std::dynamic_pointer_cast<CLabelGroup>(widget))
|
||||
result->addChild(widget.get(), true);
|
||||
else
|
||||
result->addChild(widget.get(), false);
|
||||
}
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapGameArea(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
mapView = std::make_shared<MapView>(area.topLeft(), area.dimensions());
|
||||
return mapView;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapHeroList(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
subwidgetSizes.push_back(area);
|
||||
|
||||
Rect item = readTargetArea(input["item"]);
|
||||
|
||||
Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer());
|
||||
int itemsCount = input["itemsCount"].Integer();
|
||||
|
||||
auto result = std::make_shared<CHeroList>(itemsCount, area, item.topLeft() - area.topLeft(), itemOffset, LOCPLINT->localState->getWanderingHeroes().size());
|
||||
|
||||
|
||||
if(!input["scrollUp"].isNull())
|
||||
result->setScrollUpButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollUp"])));
|
||||
|
||||
if(!input["scrollDown"].isNull())
|
||||
result->setScrollDownButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollDown"])));
|
||||
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
heroList = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapIcon(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
size_t index = input["index"].Integer();
|
||||
size_t perPlayer = input["perPlayer"].Integer();
|
||||
std::string image = input["image"].String();
|
||||
|
||||
return std::make_shared<CAdventureMapIcon>(area.topLeft(), loadAnimation(image), index, perPlayer);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapTownList(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
subwidgetSizes.push_back(area);
|
||||
|
||||
Rect item = readTargetArea(input["item"]);
|
||||
Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer());
|
||||
int itemsCount = input["itemsCount"].Integer();
|
||||
|
||||
auto result = std::make_shared<CTownList>(itemsCount, area, item.topLeft() - area.topLeft(), itemOffset, LOCPLINT->localState->getOwnedTowns().size());
|
||||
|
||||
|
||||
if(!input["scrollUp"].isNull())
|
||||
result->setScrollUpButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollUp"])));
|
||||
|
||||
if(!input["scrollDown"].isNull())
|
||||
result->setScrollDownButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollDown"])));
|
||||
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
townList = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMinimap(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
minimap = std::make_shared<CMinimap>(area);
|
||||
return minimap;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildResourceDateBar(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
auto result = std::make_shared<CResDataBar>(image, area.topLeft());
|
||||
|
||||
for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
|
||||
{
|
||||
const auto & node = input[GameConstants::RESOURCE_NAMES[i]];
|
||||
|
||||
if(node.isNull())
|
||||
continue;
|
||||
|
||||
result->setResourcePosition(GameResID(i), Point(node["x"].Integer(), node["y"].Integer()));
|
||||
}
|
||||
|
||||
result->setDatePosition(Point(input["date"]["x"].Integer(), input["date"]["y"].Integer()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildStatusBar(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
auto background = std::make_shared<CFilledTexture>(image, area);
|
||||
|
||||
return CGStatusBar::create(background);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildTexturePlayerColored(const JsonNode & input)
|
||||
{
|
||||
logGlobal->debug("Building widget CFilledTexture");
|
||||
auto image = input["image"].String();
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
return std::make_shared<FilledTexturePlayerColored>(image, area);
|
||||
}
|
||||
|
||||
std::shared_ptr<CHeroList> AdventureMapWidget::getHeroList()
|
||||
{
|
||||
return heroList;
|
||||
}
|
||||
|
||||
std::shared_ptr<CTownList> AdventureMapWidget::getTownList()
|
||||
{
|
||||
return townList;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMinimap> AdventureMapWidget::getMinimap()
|
||||
{
|
||||
return minimap;
|
||||
}
|
||||
|
||||
std::shared_ptr<MapView> AdventureMapWidget::getMapView()
|
||||
{
|
||||
return mapView;
|
||||
}
|
||||
|
||||
std::shared_ptr<CInfoBar> AdventureMapWidget::getInfoBar()
|
||||
{
|
||||
return infoBar;
|
||||
}
|
||||
|
||||
void AdventureMapWidget::setPlayer(const PlayerColor & player)
|
||||
{
|
||||
setPlayerChildren(this, player);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColor & player)
|
||||
{
|
||||
for(auto & entry : widget->children)
|
||||
{
|
||||
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
||||
auto icon = dynamic_cast<CAdventureMapIcon *>(entry);
|
||||
auto button = dynamic_cast<CButton *>(entry);
|
||||
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
|
||||
|
||||
if(button)
|
||||
button->setPlayerColor(player);
|
||||
|
||||
if(icon)
|
||||
icon->setPlayer(player);
|
||||
|
||||
if(container)
|
||||
setPlayerChildren(container, player);
|
||||
|
||||
if (texture)
|
||||
texture->playerColored(player);
|
||||
}
|
||||
|
||||
for(const auto & entry : playerColorerImages)
|
||||
{
|
||||
if(images.count(entry))
|
||||
images[entry]->playerColored(player);
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> animation, size_t index, size_t iconsPerPlayer)
|
||||
: index(index)
|
||||
, iconsPerPlayer(iconsPerPlayer)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos += position;
|
||||
image = std::make_shared<CAnimImage>(animation, index);
|
||||
}
|
||||
|
||||
void CAdventureMapIcon::setPlayer(const PlayerColor & player)
|
||||
{
|
||||
image->setFrame(index + player.getNum() * iconsPerPlayer);
|
||||
}
|
||||
|
||||
void CAdventureMapOverlayWidget::show(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::updateActiveStateChildden(CIntObject * widget)
|
||||
{
|
||||
for(auto & entry : widget->children)
|
||||
{
|
||||
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
||||
|
||||
if (container)
|
||||
{
|
||||
if (container->disableCondition == "heroAwake")
|
||||
container->setEnabled(!shortcuts->optionHeroSleeping());
|
||||
|
||||
if (container->disableCondition == "heroSleeping")
|
||||
container->setEnabled(shortcuts->optionHeroSleeping());
|
||||
|
||||
if (container->disableCondition == "mapLayerSurface")
|
||||
container->setEnabled(shortcuts->optionMapLevelSurface());
|
||||
|
||||
if (container->disableCondition == "mapLayerUnderground")
|
||||
container->setEnabled(!shortcuts->optionMapLevelSurface());
|
||||
|
||||
if (container->disableCondition == "mapViewMode")
|
||||
container->setEnabled(shortcuts->optionInWorldView());
|
||||
|
||||
if (container->disableCondition == "worldViewMode")
|
||||
container->setEnabled(!shortcuts->optionInWorldView());
|
||||
|
||||
updateActiveStateChildden(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapWidget::updateActiveState()
|
||||
{
|
||||
updateActiveStateChildden(this);
|
||||
|
||||
for (auto entry: shortcuts->getShortcuts())
|
||||
setShortcutBlocked(entry.shortcut, !entry.isEnabled);
|
||||
}
|
109
client/adventureMap/AdventureMapWidget.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* CAdventureMapWidget.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../gui/InterfaceObjectConfigurable.h"
|
||||
|
||||
class CHeroList;
|
||||
class CTownList;
|
||||
class CMinimap;
|
||||
class MapView;
|
||||
class CInfoBar;
|
||||
class IImage;
|
||||
class AdventureMapShortcuts;
|
||||
enum class EAdventureState;
|
||||
|
||||
/// Internal class of AdventureMapInterface that contains actual UI elements
|
||||
class AdventureMapWidget : public InterfaceObjectConfigurable
|
||||
{
|
||||
int mapLevel;
|
||||
/// temporary stack of sizes of currently building widgets
|
||||
std::vector<Rect> subwidgetSizes;
|
||||
|
||||
/// list of images on which player-colored palette will be applied
|
||||
std::vector<std::string> playerColorerImages;
|
||||
|
||||
/// list of named images shared between widgets
|
||||
std::map<std::string, std::shared_ptr<IImage>> images;
|
||||
std::map<std::string, std::shared_ptr<CAnimation>> animations;
|
||||
|
||||
/// Widgets that require access from adventure map
|
||||
std::shared_ptr<CHeroList> heroList;
|
||||
std::shared_ptr<CTownList> townList;
|
||||
std::shared_ptr<CMinimap> minimap;
|
||||
std::shared_ptr<MapView> mapView;
|
||||
std::shared_ptr<CInfoBar> infoBar;
|
||||
|
||||
std::shared_ptr<AdventureMapShortcuts> shortcuts;
|
||||
|
||||
Rect readTargetArea(const JsonNode & source);
|
||||
Rect readSourceArea(const JsonNode & source, const JsonNode & sourceCommon);
|
||||
Rect readArea(const JsonNode & source, const Rect & boundingBox);
|
||||
|
||||
std::shared_ptr<IImage> loadImage(const std::string & name);
|
||||
std::shared_ptr<CAnimation> loadAnimation(const std::string & name);
|
||||
|
||||
std::shared_ptr<CIntObject> buildInfobox(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapImage(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapButton(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapContainer(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapGameArea(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapHeroList(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapIcon(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapTownList(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMinimap(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildResourceDateBar(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildStatusBar(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildTexturePlayerColored(const JsonNode &);
|
||||
|
||||
|
||||
void setPlayerChildren(CIntObject * widget, const PlayerColor & player);
|
||||
void updateActiveStateChildden(CIntObject * widget);
|
||||
public:
|
||||
explicit AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts );
|
||||
|
||||
std::shared_ptr<CHeroList> getHeroList();
|
||||
std::shared_ptr<CTownList> getTownList();
|
||||
std::shared_ptr<CMinimap> getMinimap();
|
||||
std::shared_ptr<MapView> getMapView();
|
||||
std::shared_ptr<CInfoBar> getInfoBar();
|
||||
|
||||
void setPlayer(const PlayerColor & player);
|
||||
|
||||
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
|
||||
void updateActiveState();
|
||||
};
|
||||
|
||||
/// Small helper class that provides ownership for shared_ptr's of child elements
|
||||
class CAdventureMapContainerWidget : public CIntObject
|
||||
{
|
||||
friend class AdventureMapWidget;
|
||||
std::vector<std::shared_ptr<CIntObject>> ownedChildren;
|
||||
std::string disableCondition;
|
||||
};
|
||||
|
||||
class CAdventureMapOverlayWidget : public CAdventureMapContainerWidget
|
||||
{
|
||||
public:
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// Small helper class that provides player-colorable icon using animation file
|
||||
class CAdventureMapIcon : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
|
||||
size_t index;
|
||||
size_t iconsPerPlayer;
|
||||
public:
|
||||
CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> image, size_t index, size_t iconsPerPlayer);
|
||||
|
||||
void setPlayer(const PlayerColor & player);
|
||||
};
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "CAdventureOptions.h"
|
||||
#include "AdventureOptions.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -17,13 +17,14 @@
|
||||
#include "../lobby/CCampaignInfoScreen.h"
|
||||
#include "../lobby/CScenarioInfoScreen.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
|
||||
CAdventureOptions::CAdventureOptions()
|
||||
AdventureOptions::AdventureOptions()
|
||||
: CWindowObject(PLAYER_COLORED, "ADVOPTS")
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
@ -31,10 +32,10 @@ CAdventureOptions::CAdventureOptions()
|
||||
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
|
||||
viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
|
||||
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&AdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
|
||||
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
|
||||
scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
|
||||
scenInfo->addCallback(AdventureOptions::showScenarioInfo);
|
||||
|
||||
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE);
|
||||
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
|
||||
@ -46,15 +47,15 @@ CAdventureOptions::CAdventureOptions()
|
||||
dig->block(true);
|
||||
}
|
||||
|
||||
void CAdventureOptions::showScenarioInfo()
|
||||
void AdventureOptions::showScenarioInfo()
|
||||
{
|
||||
if(LOCPLINT->cb->getStartInfo()->campState)
|
||||
{
|
||||
GH.pushIntT<CCampaignInfoScreen>();
|
||||
GH.windows().createAndPushWindow<CCampaignInfoScreen>();
|
||||
}
|
||||
else
|
||||
{
|
||||
GH.pushIntT<CScenarioInfoScreen>();
|
||||
GH.windows().createAndPushWindow<CScenarioInfoScreen>();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
class CButton;
|
||||
|
||||
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
|
||||
class CAdventureOptions : public CWindowObject
|
||||
class AdventureOptions : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<CButton> exit;
|
||||
std::shared_ptr<CButton> viewWorld;
|
||||
@ -24,7 +24,7 @@ class CAdventureOptions : public CWindowObject
|
||||
/*std::shared_ptr<CButton> replay*/
|
||||
|
||||
public:
|
||||
CAdventureOptions();
|
||||
AdventureOptions();
|
||||
|
||||
static void showScenarioInfo();
|
||||
};
|
20
client/adventureMap/AdventureState.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* AdventureState.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
enum class EAdventureState
|
||||
{
|
||||
NOT_INITIALIZED,
|
||||
HOTSEAT_WAIT,
|
||||
MAKING_TURN,
|
||||
ENEMY_TURN,
|
||||
CASTING_SPELL,
|
||||
WORLD_VIEW
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* CAdvMapPanel.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "CAdvMapPanel.h"
|
||||
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
CAdvMapPanel::CAdvMapPanel(std::shared_ptr<IImage> bg, Point position)
|
||||
: CIntObject()
|
||||
, background(bg)
|
||||
{
|
||||
defActions = 255;
|
||||
recActions = 255;
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
if (bg)
|
||||
{
|
||||
pos.w = bg->width();
|
||||
pos.h = bg->height();
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapPanel::addChildColorableButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
colorableButtons.push_back(button);
|
||||
addChildToPanel(button, ACTIVATE | DEACTIVATE);
|
||||
}
|
||||
|
||||
void CAdvMapPanel::setPlayerColor(const PlayerColor & clr)
|
||||
{
|
||||
for(auto & button : colorableButtons)
|
||||
{
|
||||
button->setPlayerColor(clr);
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapPanel::showAll(SDL_Surface * to)
|
||||
{
|
||||
if(background)
|
||||
background->draw(to, pos.x, pos.y);
|
||||
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void CAdvMapPanel::addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions)
|
||||
{
|
||||
otherObjects.push_back(obj);
|
||||
obj->recActions |= actions | SHOWALL;
|
||||
obj->recActions &= ~DISPOSE;
|
||||
addChild(obj.get(), false);
|
||||
}
|
||||
|
||||
CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color)
|
||||
: CAdvMapPanel(bg, position), icons(_icons)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
int fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
|
||||
|
||||
if(fillerHeight > 0)
|
||||
{
|
||||
backgroundFiller = std::make_shared<CFilledTexture>("DIBOXBCK", Rect(0, pos.h, pos.w, fillerHeight));
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset)
|
||||
{
|
||||
assert(iconsData.size() == currentIcons.size());
|
||||
|
||||
for(size_t idx = 0; idx < iconsData.size(); idx++)
|
||||
{
|
||||
const auto & data = iconsData.at(idx);
|
||||
currentIcons[idx]->setFrame(data.first + indexOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOffset)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
iconsData.push_back(data);
|
||||
currentIcons.push_back(std::make_shared<CAnimImage>(icons, data.first + indexOffset, 0, data.second.x, data.second.y));
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* CAdvMapPanel.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class PlayerColor;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CAnimation;
|
||||
class CAnimImage;
|
||||
class CFilledTexture;
|
||||
class CButton;
|
||||
class IImage;
|
||||
|
||||
/// simple panel that contains other displayable elements; used to separate groups of controls
|
||||
class CAdvMapPanel : public CIntObject
|
||||
{
|
||||
std::vector<std::shared_ptr<CButton>> colorableButtons;
|
||||
std::vector<std::shared_ptr<CIntObject>> otherObjects;
|
||||
/// the surface passed to this obj will be freed in dtor
|
||||
std::shared_ptr<IImage> background;
|
||||
public:
|
||||
CAdvMapPanel(std::shared_ptr<IImage> bg, Point position);
|
||||
|
||||
void addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions = 0);
|
||||
void addChildColorableButton(std::shared_ptr<CButton> button);
|
||||
/// recolors all buttons to given player color
|
||||
void setPlayerColor(const PlayerColor & clr);
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// specialized version of CAdvMapPanel that handles recolorable def-based pictures for world view info panel
|
||||
class CAdvMapWorldViewPanel : public CAdvMapPanel
|
||||
{
|
||||
/// data that allows reconstruction of panel info icons
|
||||
std::vector<std::pair<int, Point>> iconsData;
|
||||
/// ptrs to child-pictures constructed from iconsData
|
||||
std::vector<std::shared_ptr<CAnimImage>> currentIcons;
|
||||
/// surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
|
||||
std::shared_ptr<CFilledTexture> backgroundFiller;
|
||||
std::shared_ptr<CAnimation> icons;
|
||||
public:
|
||||
CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color);
|
||||
|
||||
void addChildIcon(std::pair<int, Point> data, int indexOffset);
|
||||
|
||||
/// recreates all pictures from given def to recolor them according to current player color
|
||||
void recolorIcons(const PlayerColor & color, int indexOffset);
|
||||
};
|
||||
|
@ -17,8 +17,11 @@
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../ClientCommandManager.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../windows/CMessage.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
@ -50,7 +53,7 @@ void CInGameConsole::show(SDL_Surface * to)
|
||||
Point leftBottomCorner(0, pos.h);
|
||||
Point textPosition(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number * 20);
|
||||
|
||||
graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, text.text, Colors::GREEN, textPosition );
|
||||
graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, text.text, Colors::GREEN, pos.topLeft() + textPosition );
|
||||
|
||||
number++;
|
||||
}
|
||||
@ -75,7 +78,7 @@ void CInGameConsole::tick(uint32_t msPassed)
|
||||
}
|
||||
|
||||
if(sizeBefore != texts.size())
|
||||
GH.totalRedraw(); // FIXME: ingame console has no parent widget set
|
||||
GH.windows().totalRedraw(); // FIXME: ingame console has no parent widget set
|
||||
}
|
||||
|
||||
void CInGameConsole::print(const std::string & txt)
|
||||
@ -83,30 +86,23 @@ void CInGameConsole::print(const std::string & txt)
|
||||
// boost::unique_lock scope
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(texts_mx);
|
||||
int lineLen = conf.go()->ac.outputLineLength;
|
||||
|
||||
if(txt.size() < lineLen)
|
||||
{
|
||||
texts.push_back({txt, 0});
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(lineLen);
|
||||
for(int g = 0; g < txt.size() / lineLen + 1; ++g)
|
||||
{
|
||||
std::string part = txt.substr(g * lineLen, lineLen);
|
||||
if(part.empty())
|
||||
break;
|
||||
// Maximum width for a text line is limited by:
|
||||
// 1) width of adventure map terrain area, for when in-game console is on top of advmap
|
||||
// 2) width of castle/battle window (fixed to 800) when this window is open
|
||||
// 3) arbitrary selected left and right margins
|
||||
int maxWidth = std::min( 800, adventureInt->terrainAreaPixels().w) - 100;
|
||||
|
||||
texts.push_back({part, 0});
|
||||
}
|
||||
}
|
||||
auto splitText = CMessage::breakText(txt, maxWidth, FONT_MEDIUM);
|
||||
|
||||
for (auto const & entry : splitText)
|
||||
texts.push_back({entry, 0});
|
||||
|
||||
while(texts.size() > maxDisplayedTexts)
|
||||
texts.erase(texts.begin());
|
||||
}
|
||||
|
||||
GH.totalRedraw(); // FIXME: ingame console has no parent widget set
|
||||
GH.windows().totalRedraw(); // FIXME: ingame console has no parent widget set
|
||||
}
|
||||
|
||||
void CInGameConsole::keyPressed (EShortcut key)
|
||||
@ -221,16 +217,15 @@ void CInGameConsole::startEnteringText()
|
||||
if (captureAllKeys)
|
||||
return;
|
||||
|
||||
assert(GH.statusbar);
|
||||
assert(currentStatusBar.expired());//effectively, nullptr check
|
||||
|
||||
currentStatusBar = GH.statusbar;
|
||||
currentStatusBar = GH.statusbar();
|
||||
|
||||
captureAllKeys = true;
|
||||
enteredText = "_";
|
||||
|
||||
GH.statusbar->setEnteringMode(true);
|
||||
GH.statusbar->setEnteredText(enteredText);
|
||||
GH.statusbar()->setEnteringMode(true);
|
||||
GH.statusbar()->setEnteredText(enteredText);
|
||||
}
|
||||
|
||||
void CInGameConsole::endEnteringText(bool processEnteredText)
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CInfoBar.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -24,6 +24,7 @@
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
@ -261,7 +262,7 @@ void CInfoBar::tick(uint32_t msPassed)
|
||||
{
|
||||
timerCounter = 0;
|
||||
removeUsedEvents(TIME);
|
||||
if(GH.topInt() == adventureInt)
|
||||
if(GH.windows().isTopWindow(adventureInt))
|
||||
popComponents(true);
|
||||
}
|
||||
else
|
||||
@ -292,9 +293,9 @@ void CInfoBar::clickRight(tribool down, bool previousState)
|
||||
void CInfoBar::hover(bool on)
|
||||
{
|
||||
if(on)
|
||||
GH.statusbar->write(CGI->generaltexth->zelp[292].first);
|
||||
GH.statusbar()->write(CGI->generaltexth->zelp[292].first);
|
||||
else
|
||||
GH.statusbar->clear();
|
||||
GH.statusbar()->clear();
|
||||
}
|
||||
|
||||
CInfoBar::CInfoBar(const Rect & position)
|
||||
|
@ -11,15 +11,17 @@
|
||||
#include "StdInc.h"
|
||||
#include "CList.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
@ -66,9 +68,9 @@ void CList::CListItem::clickLeft(tribool down, bool previousState)
|
||||
void CList::CListItem::hover(bool on)
|
||||
{
|
||||
if (on)
|
||||
GH.statusbar->write(getHoverText());
|
||||
GH.statusbar()->write(getHoverText());
|
||||
else
|
||||
GH.statusbar->clear();
|
||||
GH.statusbar()->clear();
|
||||
}
|
||||
|
||||
void CList::CListItem::onSelect(bool on)
|
||||
@ -81,24 +83,44 @@ void CList::CListItem::onSelect(bool on)
|
||||
redraw();
|
||||
}
|
||||
|
||||
CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create)
|
||||
: CIntObject(0, position),
|
||||
CList::CList(int Size, Rect widgetDimensions)
|
||||
: CIntObject(0, widgetDimensions.topLeft()),
|
||||
size(Size),
|
||||
selected(nullptr)
|
||||
{
|
||||
pos.w = widgetDimensions.w;
|
||||
pos.h = widgetDimensions.h;
|
||||
}
|
||||
|
||||
void CList::showAll(SDL_Surface * to)
|
||||
{
|
||||
CSDL_Ext::fillRect(to, pos, Colors::BLACK);
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void CList::createList(Point firstItemPosition, Point itemPositionDelta, size_t listAmount)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
scrollUp = std::make_shared<CButton>(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]);
|
||||
scrollDown = std::make_shared<CButton>(Point(0, scrollUp->pos.h + 32*(int)size), btnDown, CGI->generaltexth->zelp[helpDown]);
|
||||
listBox = std::make_shared<CListBox>(std::bind(&CList::createItem, this, _1), firstItemPosition, itemPositionDelta, size, listAmount);
|
||||
}
|
||||
|
||||
listBox = std::make_shared<CListBox>(create, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
|
||||
void CList::setScrollUpButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
addChild(button.get());
|
||||
|
||||
//assign callback only after list was created
|
||||
scrollUp = button;
|
||||
scrollUp->addCallback(std::bind(&CListBox::moveToPrev, listBox));
|
||||
scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox));
|
||||
|
||||
scrollUp->addCallback(std::bind(&CList::update, this));
|
||||
scrollDown->addCallback(std::bind(&CList::update, this));
|
||||
update();
|
||||
}
|
||||
|
||||
void CList::setScrollDownButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
addChild(button.get());
|
||||
|
||||
scrollDown = button;
|
||||
scrollDown->addCallback(std::bind(&CList::update, this));
|
||||
scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox));
|
||||
update();
|
||||
}
|
||||
|
||||
@ -107,8 +129,11 @@ void CList::update()
|
||||
bool onTop = listBox->getPos() == 0;
|
||||
bool onBottom = listBox->getPos() + size >= listBox->size();
|
||||
|
||||
scrollUp->block(onTop);
|
||||
scrollDown->block(onBottom);
|
||||
if (scrollUp)
|
||||
scrollUp->block(onTop);
|
||||
|
||||
if (scrollDown)
|
||||
scrollDown->block(onBottom);
|
||||
}
|
||||
|
||||
void CList::select(std::shared_ptr<CListItem> which)
|
||||
@ -223,16 +248,17 @@ std::string CHeroList::CHeroItem::getHoverText()
|
||||
return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->type->heroClass->getNameTranslated());
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
|
||||
std::shared_ptr<CIntObject> CHeroList::createItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->localState->getWanderingHeroes().size() > index)
|
||||
return std::make_shared<CHeroItem>(this, LOCPLINT->localState->getWanderingHero(index));
|
||||
return std::make_shared<CEmptyHeroItem>();
|
||||
}
|
||||
|
||||
CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getWanderingHeroes().size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
|
||||
CHeroList::CHeroList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount)
|
||||
: CList(visibleItemsCount, widgetPosition)
|
||||
{
|
||||
createList(firstItemOffset, itemOffsetDelta, initialItemsCount);
|
||||
}
|
||||
|
||||
void CHeroList::select(const CGHeroInstance * hero)
|
||||
@ -261,7 +287,7 @@ void CHeroList::update(const CGHeroInstance * hero)
|
||||
CList::update();
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
|
||||
std::shared_ptr<CIntObject> CTownList::createItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->localState->getOwnedTowns().size() > index)
|
||||
return std::make_shared<CTownItem>(this, LOCPLINT->localState->getOwnedTown(index));
|
||||
@ -312,9 +338,10 @@ std::string CTownList::CTownItem::getHoverText()
|
||||
return town->getObjectName();
|
||||
}
|
||||
|
||||
CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getOwnedTowns().size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
|
||||
CTownList::CTownList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount)
|
||||
: CList(visibleItemsCount, widgetPosition)
|
||||
{
|
||||
createList(firstItemOffset, itemOffsetDelta, initialItemsCount);
|
||||
}
|
||||
|
||||
void CTownList::select(const CGTownInstance * town)
|
||||
|
@ -10,8 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../../lib/FunctionList.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -21,7 +19,9 @@ class CGTownInstance;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CListBox;
|
||||
class CButton;
|
||||
class CAnimImage;
|
||||
|
||||
/// Base UI Element for hero\town lists
|
||||
class CList : public CIntObject
|
||||
@ -53,23 +53,9 @@ protected:
|
||||
virtual std::string getHoverText()=0;
|
||||
};
|
||||
|
||||
std::shared_ptr<CListBox> listBox;
|
||||
private:
|
||||
const size_t size;
|
||||
|
||||
/**
|
||||
* @brief CList - protected constructor
|
||||
* @param size - maximal amount of visible at once items
|
||||
* @param position - cordinates
|
||||
* @param btnUp - path to image to use as top button
|
||||
* @param btnDown - path to image to use as bottom button
|
||||
* @param listAmount - amount of items in the list
|
||||
* @param helpUp - index in zelp.txt for button help tooltip
|
||||
* @param helpDown - index in zelp.txt for button help tooltip
|
||||
* @param create - function for creating items in listbox
|
||||
* @param destroy - function for deleting items in listbox
|
||||
*/
|
||||
CList(int size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create);
|
||||
|
||||
//for selection\deselection
|
||||
std::shared_ptr<CListItem> selected;
|
||||
void select(std::shared_ptr<CListItem> which);
|
||||
@ -78,8 +64,14 @@ protected:
|
||||
std::shared_ptr<CButton> scrollUp;
|
||||
std::shared_ptr<CButton> scrollDown;
|
||||
|
||||
/// should be called when list is invalidated
|
||||
void update();
|
||||
protected:
|
||||
std::shared_ptr<CListBox> listBox;
|
||||
|
||||
CList(int size, Rect widgetDimensions);
|
||||
|
||||
void createList(Point firstItemPosition, Point itemPositionDelta, size_t listAmount);
|
||||
|
||||
virtual std::shared_ptr<CIntObject> createItem(size_t index) = 0;
|
||||
|
||||
public:
|
||||
/// functions that will be called when selection changes
|
||||
@ -88,10 +80,18 @@ public:
|
||||
/// return index of currently selected element
|
||||
int getSelectedIndex();
|
||||
|
||||
void setScrollUpButton(std::shared_ptr<CButton> button);
|
||||
void setScrollDownButton(std::shared_ptr<CButton> button);
|
||||
|
||||
/// should be called when list is invalidated
|
||||
void update();
|
||||
|
||||
/// set of methods to switch selection
|
||||
void selectIndex(int which);
|
||||
void selectNext();
|
||||
void selectPrev();
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// List of heroes which is shown at the right of the adventure map screen
|
||||
@ -125,13 +125,9 @@ class CHeroList : public CList
|
||||
std::string getHoverText() override;
|
||||
};
|
||||
|
||||
std::shared_ptr<CIntObject> createHeroItem(size_t index);
|
||||
std::shared_ptr<CIntObject> createItem(size_t index);
|
||||
public:
|
||||
/**
|
||||
* @brief CHeroList
|
||||
* @param size, position, btnUp, btnDown @see CList::CList
|
||||
*/
|
||||
CHeroList(int size, Point position, std::string btnUp, std::string btnDown);
|
||||
CHeroList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount);
|
||||
|
||||
/// Select specific hero and scroll if needed
|
||||
void select(const CGHeroInstance * hero = nullptr);
|
||||
@ -159,13 +155,9 @@ class CTownList : public CList
|
||||
std::string getHoverText() override;
|
||||
};
|
||||
|
||||
std::shared_ptr<CIntObject> createTownItem(size_t index);
|
||||
std::shared_ptr<CIntObject> createItem(size_t index) override;
|
||||
public:
|
||||
/**
|
||||
* @brief CTownList
|
||||
* @param size, position, btnUp, btnDown @see CList::CList
|
||||
*/
|
||||
CTownList(int size, Point position, std::string btnUp, std::string btnDown);
|
||||
CTownList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount);
|
||||
|
||||
/// Select specific town and scroll if needed
|
||||
void select(const CGTownInstance * town = nullptr);
|
||||
|
@ -11,12 +11,13 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMinimap.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../render/Canvas.h"
|
||||
@ -131,7 +132,7 @@ void CMinimap::moveAdvMapSelection()
|
||||
adventureInt->centerOnTile(newLocation);
|
||||
|
||||
if (!(adventureInt->active & GENERAL))
|
||||
GH.totalRedraw(); //redraw this as well as inactive adventure map
|
||||
GH.windows().totalRedraw(); //redraw this as well as inactive adventure map
|
||||
else
|
||||
redraw();//redraw only this
|
||||
}
|
||||
@ -151,9 +152,9 @@ void CMinimap::clickRight(tribool down, bool previousState)
|
||||
void CMinimap::hover(bool on)
|
||||
{
|
||||
if(on)
|
||||
GH.statusbar->write(CGI->generaltexth->zelp[291].first);
|
||||
GH.statusbar()->write(CGI->generaltexth->zelp[291].first);
|
||||
else
|
||||
GH.statusbar->clear();
|
||||
GH.statusbar()->clear();
|
||||
}
|
||||
|
||||
void CMinimap::mouseMoved(const Point & cursorPosition)
|
||||
|
@ -19,50 +19,40 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/ResourceSet.h"
|
||||
|
||||
#define ADVOPT (conf.go()->ac)
|
||||
|
||||
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
|
||||
CResDataBar::CResDataBar(const std::string & imageName, const Point & position)
|
||||
{
|
||||
pos.x += x;
|
||||
pos.y += y;
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
background = std::make_shared<CPicture>(defname, 0, 0);
|
||||
background = std::make_shared<CPicture>(imageName, 0, 0);
|
||||
background->colorize(LOCPLINT->playerID);
|
||||
|
||||
pos.w = background->pos.w;
|
||||
pos.h = background->pos.h;
|
||||
|
||||
txtpos.resize(8);
|
||||
for (int i = 0; i < 8 ; i++)
|
||||
{
|
||||
txtpos[i].first = pos.x + offx + resdist*i;
|
||||
txtpos[i].second = pos.y + offy;
|
||||
}
|
||||
txtpos[7].first = txtpos[6].first + datedist;
|
||||
addUsedEvents(RCLICK);
|
||||
}
|
||||
|
||||
CResDataBar::CResDataBar()
|
||||
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist):
|
||||
CResDataBar(defname, Point(x,y))
|
||||
{
|
||||
pos.x += ADVOPT.resdatabarX;
|
||||
pos.y += ADVOPT.resdatabarY;
|
||||
for (int i = 0; i < 7 ; i++)
|
||||
resourcePositions[GameResID(i)] = Point( offx + resdist*i, offy );
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
background = std::make_shared<CPicture>(ADVOPT.resdatabarG, 0, 0);
|
||||
background->colorize(LOCPLINT->playerID);
|
||||
datePosition = resourcePositions[EGameResID::GOLD] + Point(datedist, 0);
|
||||
}
|
||||
|
||||
pos.w = background->pos.w;
|
||||
pos.h = background->pos.h;
|
||||
|
||||
txtpos.resize(8);
|
||||
for (int i = 0; i < 8 ; i++)
|
||||
{
|
||||
txtpos[i].first = pos.x + ADVOPT.resOffsetX + ADVOPT.resDist*i;
|
||||
txtpos[i].second = pos.y + ADVOPT.resOffsetY;
|
||||
}
|
||||
txtpos[7].first = txtpos[6].first + ADVOPT.resDateDist;
|
||||
void CResDataBar::setDatePosition(const Point & position)
|
||||
{
|
||||
datePosition = position;
|
||||
}
|
||||
|
||||
void CResDataBar::setResourcePosition(const GameResID & resource, const Point & position)
|
||||
{
|
||||
resourcePositions[resource] = position;
|
||||
}
|
||||
|
||||
std::string CResDataBar::buildDateString()
|
||||
@ -80,13 +70,15 @@ std::string CResDataBar::buildDateString()
|
||||
void CResDataBar::draw(SDL_Surface * to)
|
||||
{
|
||||
//TODO: all this should be labels, but they require proper text update on change
|
||||
for (GameResID i=EGameResID::WOOD; i <= GameResID(EGameResID::GOLD); ++i)
|
||||
for (auto & entry : resourcePositions)
|
||||
{
|
||||
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(i));
|
||||
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(entry.first));
|
||||
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first, txtpos[i].second));
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, pos.topLeft() + entry.second);
|
||||
}
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, buildDateString(), Colors::WHITE, Point(txtpos[7].first, txtpos[7].second));
|
||||
|
||||
if (datePosition)
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, buildDateString(), Colors::WHITE, pos.topLeft() + *datePosition);
|
||||
}
|
||||
|
||||
void CResDataBar::showAll(SDL_Surface * to)
|
||||
|
@ -11,6 +11,11 @@
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
enum class EGameResID : int8_t;
|
||||
using GameResID = Identifier<EGameResID>;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
/// Resources bar which shows information about how many gold, crystals,... you have
|
||||
/// Current date is displayed too
|
||||
class CResDataBar : public CIntObject
|
||||
@ -19,14 +24,21 @@ class CResDataBar : public CIntObject
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
|
||||
std::vector<std::pair<int,int> > txtpos;
|
||||
|
||||
std::map<GameResID, Point> resourcePositions;
|
||||
std::optional<Point> datePosition;
|
||||
|
||||
void draw(SDL_Surface * to);
|
||||
public:
|
||||
CResDataBar();
|
||||
|
||||
/// For dynamically-sized UI windows, e.g. adventure map interface
|
||||
CResDataBar(const std::string & imageName, const Point & position);
|
||||
|
||||
/// For fixed-size UI windows, e.g. CastleInterface
|
||||
CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
|
||||
|
||||
void setDatePosition(const Point & position);
|
||||
void setResourcePosition(const GameResID & resource, const Point & position);
|
||||
|
||||
void colorize(PlayerColor player);
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../windows/CCreatureWindow.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
@ -668,7 +669,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
||||
|
||||
case PossiblePlayerBattleAction::CREATURE_INFO:
|
||||
{
|
||||
GH.pushIntT<CStackWindow>(targetStack, false);
|
||||
GH.windows().createAndPushWindow<CStackWindow>(targetStack, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -772,7 +773,7 @@ void BattleActionsController::onHexHovered(BattleHex hoveredHex)
|
||||
if (owner.openingPlaying())
|
||||
{
|
||||
currentConsoleMsg = VLC->generaltexth->translate("vcmi.battleWindow.pressKeyToSkipIntro");
|
||||
GH.statusbar->write(currentConsoleMsg);
|
||||
GH.statusbar()->write(currentConsoleMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -782,7 +783,7 @@ void BattleActionsController::onHexHovered(BattleHex hoveredHex)
|
||||
if (hoveredHex == BattleHex::INVALID)
|
||||
{
|
||||
if (!currentConsoleMsg.empty())
|
||||
GH.statusbar->clearIfMatching(currentConsoleMsg);
|
||||
GH.statusbar()->clearIfMatching(currentConsoleMsg);
|
||||
|
||||
currentConsoleMsg.clear();
|
||||
CCS->curh->set(Cursor::Combat::BLOCKED);
|
||||
@ -805,10 +806,10 @@ void BattleActionsController::onHexHovered(BattleHex hoveredHex)
|
||||
}
|
||||
|
||||
if (!currentConsoleMsg.empty())
|
||||
GH.statusbar->clearIfMatching(currentConsoleMsg);
|
||||
GH.statusbar()->clearIfMatching(currentConsoleMsg);
|
||||
|
||||
if (!newConsoleMsg.empty())
|
||||
GH.statusbar->write(newConsoleMsg);
|
||||
GH.statusbar()->write(newConsoleMsg);
|
||||
|
||||
currentConsoleMsg = newConsoleMsg;
|
||||
}
|
||||
@ -818,7 +819,7 @@ void BattleActionsController::onHoverEnded()
|
||||
CCS->curh->set(Cursor::Combat::POINTER);
|
||||
|
||||
if (!currentConsoleMsg.empty())
|
||||
GH.statusbar->clearIfMatching(currentConsoleMsg);
|
||||
GH.statusbar()->clearIfMatching(currentConsoleMsg);
|
||||
|
||||
currentConsoleMsg.clear();
|
||||
}
|
||||
@ -849,7 +850,7 @@ void BattleActionsController::onHexLeftClicked(BattleHex clickedHex)
|
||||
{
|
||||
actionRealize(action, clickedHex);
|
||||
|
||||
GH.statusbar->clear();
|
||||
GH.statusbar()->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -973,7 +974,7 @@ void BattleActionsController::onHexRightClicked(BattleHex clickedHex)
|
||||
auto selectedStack = owner.curInt->cb->battleGetStackByPos(clickedHex, true);
|
||||
|
||||
if (selectedStack != nullptr)
|
||||
GH.pushIntT<CStackWindow>(selectedStack, true);
|
||||
GH.windows().createAndPushWindow<CStackWindow>(selectedStack, true);
|
||||
|
||||
if (clickedHex == BattleHex::HERO_ATTACKER && owner.attackingHero)
|
||||
owner.attackingHero->heroRightClicked();
|
||||
|
@ -222,7 +222,7 @@ bool DummyAnimation::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void DummyAnimation::nextFrame()
|
||||
void DummyAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
counter++;
|
||||
if(counter > howMany)
|
||||
@ -300,7 +300,7 @@ ECreatureAnimType MeleeAttackAnimation::selectGroup(bool multiAttack)
|
||||
return mutPosToGroup[mutPos];
|
||||
}
|
||||
|
||||
void MeleeAttackAnimation::nextFrame()
|
||||
void MeleeAttackAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame();
|
||||
size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
|
||||
@ -308,7 +308,7 @@ void MeleeAttackAnimation::nextFrame()
|
||||
if ( currentFrame * 2 >= totalFrames )
|
||||
owner.executeAnimationStage(EAnimationEvents::HIT);
|
||||
|
||||
AttackAnimation::nextFrame();
|
||||
AttackAnimation::tick(msPassed);
|
||||
}
|
||||
|
||||
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack)
|
||||
@ -379,15 +379,15 @@ bool MovementAnimation::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MovementAnimation::nextFrame()
|
||||
void MovementAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * progressPerSecond;
|
||||
progress += float(msPassed) / 1000 * progressPerSecond;
|
||||
|
||||
//moving instructions
|
||||
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
|
||||
myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress );
|
||||
|
||||
BattleAnimation::nextFrame();
|
||||
BattleAnimation::tick(msPassed);
|
||||
|
||||
if(progress >= 1.0)
|
||||
{
|
||||
@ -577,9 +577,9 @@ bool ColorTransformAnimation::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void ColorTransformAnimation::nextFrame()
|
||||
void ColorTransformAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
float elapsed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
float elapsed = msPassed / 1000.f;
|
||||
float fullTime = AnimationControls::getFadeInDuration();
|
||||
float delta = elapsed / fullTime;
|
||||
totalProgress += delta;
|
||||
@ -699,7 +699,7 @@ void RangedAttackAnimation::emitProjectile()
|
||||
projectileEmitted = true;
|
||||
}
|
||||
|
||||
void RangedAttackAnimation::nextFrame()
|
||||
void RangedAttackAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
// animation should be paused if there is an active projectile
|
||||
if (projectileEmitted)
|
||||
@ -716,7 +716,7 @@ void RangedAttackAnimation::nextFrame()
|
||||
else
|
||||
stackAnimation(attackingStack)->playUntil(static_cast<size_t>(-1));
|
||||
|
||||
AttackAnimation::nextFrame();
|
||||
AttackAnimation::tick(msPassed);
|
||||
|
||||
if (!projectileEmitted)
|
||||
{
|
||||
@ -790,9 +790,9 @@ CatapultAnimation::CatapultAnimation(BattleInterface & owner, const CStack * att
|
||||
logAnim->debug("Created shooting anim for %s", stack->getName());
|
||||
}
|
||||
|
||||
void CatapultAnimation::nextFrame()
|
||||
void CatapultAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
ShootingAnimation::nextFrame();
|
||||
ShootingAnimation::tick(msPassed);
|
||||
|
||||
if ( explosionEmitted)
|
||||
return;
|
||||
@ -988,9 +988,9 @@ bool EffectAnimation::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectAnimation::nextFrame()
|
||||
void EffectAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
playEffect();
|
||||
playEffect(msPassed);
|
||||
|
||||
if (effectFinished)
|
||||
{
|
||||
@ -1020,7 +1020,7 @@ void EffectAnimation::onEffectFinished()
|
||||
effectFinished = true;
|
||||
}
|
||||
|
||||
void EffectAnimation::playEffect()
|
||||
void EffectAnimation::playEffect(uint32_t msPassed)
|
||||
{
|
||||
if ( effectFinished )
|
||||
return;
|
||||
@ -1029,7 +1029,7 @@ void EffectAnimation::playEffect()
|
||||
{
|
||||
if(elem.effectID == ID)
|
||||
{
|
||||
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
|
||||
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
|
||||
|
||||
if(elem.currentFrame >= elem.animation->size())
|
||||
{
|
||||
@ -1113,7 +1113,7 @@ void HeroCastAnimation::emitAnimationEvent()
|
||||
owner.executeAnimationStage(EAnimationEvents::HIT);
|
||||
}
|
||||
|
||||
void HeroCastAnimation::nextFrame()
|
||||
void HeroCastAnimation::tick(uint32_t msPassed)
|
||||
{
|
||||
float frame = hero->getFrame();
|
||||
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
|
||||
bool isInitialized();
|
||||
bool tryInitialize();
|
||||
virtual void nextFrame() {} //call every new frame
|
||||
virtual void tick(uint32_t msPassed) {} //call every new frame
|
||||
virtual ~BattleAnimation();
|
||||
|
||||
BattleAnimation(BattleInterface & owner);
|
||||
@ -120,7 +120,7 @@ class ColorTransformAnimation : public BattleStackAnimation
|
||||
float totalProgress;
|
||||
|
||||
bool init() override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
public:
|
||||
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
|
||||
@ -157,7 +157,7 @@ private:
|
||||
|
||||
public:
|
||||
bool init() override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
|
||||
~MovementAnimation();
|
||||
@ -220,7 +220,7 @@ class MeleeAttackAnimation : public AttackAnimation
|
||||
public:
|
||||
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
|
||||
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
};
|
||||
|
||||
|
||||
@ -246,7 +246,7 @@ public:
|
||||
~RangedAttackAnimation();
|
||||
|
||||
bool init() override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
};
|
||||
|
||||
/// Shooting attack
|
||||
@ -275,7 +275,7 @@ public:
|
||||
CatapultAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender, int _catapultDmg = 0);
|
||||
|
||||
void createProjectile(const Point & from, const Point & dest) const override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
};
|
||||
|
||||
class CastAnimation : public RangedAttackAnimation
|
||||
@ -300,7 +300,7 @@ private:
|
||||
int howMany;
|
||||
public:
|
||||
bool init() override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
DummyAnimation(BattleInterface & owner, int howManyFrames);
|
||||
};
|
||||
@ -324,7 +324,7 @@ class EffectAnimation : public BattleAnimation
|
||||
|
||||
void onEffectFinished();
|
||||
void clearEffect();
|
||||
void playEffect();
|
||||
void playEffect(uint32_t msPassed);
|
||||
|
||||
public:
|
||||
enum EEffectFlags
|
||||
@ -349,7 +349,7 @@ public:
|
||||
~EffectAnimation();
|
||||
|
||||
bool init() override;
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
};
|
||||
|
||||
class HeroCastAnimation : public BattleAnimation
|
||||
@ -367,6 +367,6 @@ class HeroCastAnimation : public BattleAnimation
|
||||
public:
|
||||
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
|
||||
|
||||
void nextFrame() override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
bool init() override;
|
||||
};
|
||||
|
@ -68,8 +68,13 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
||||
backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height()));
|
||||
|
||||
updateAccessibleHexes();
|
||||
addUsedEvents(LCLICK | RCLICK | MOVE);
|
||||
addUsedEvents(LCLICK | RCLICK | MOVE | TIME);
|
||||
}
|
||||
|
||||
void BattleFieldController::activate()
|
||||
{
|
||||
LOCPLINT->cingconsole->pos = this->pos;
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
void BattleFieldController::createHeroes()
|
||||
@ -129,7 +134,7 @@ void BattleFieldController::renderBattlefield(Canvas & canvas)
|
||||
|
||||
renderer.execute(clippedCanvas);
|
||||
|
||||
owner.projectilesController->showProjectiles(clippedCanvas);
|
||||
owner.projectilesController->render(clippedCanvas);
|
||||
}
|
||||
|
||||
void BattleFieldController::showBackground(Canvas & canvas)
|
||||
@ -606,12 +611,16 @@ void BattleFieldController::showAll(SDL_Surface * to)
|
||||
show(to);
|
||||
}
|
||||
|
||||
void BattleFieldController::show(SDL_Surface * to)
|
||||
void BattleFieldController::tick(uint32_t msPassed)
|
||||
{
|
||||
updateAccessibleHexes();
|
||||
owner.stacksController->update();
|
||||
owner.obstacleController->update();
|
||||
owner.stacksController->tick(msPassed);
|
||||
owner.obstacleController->tick(msPassed);
|
||||
owner.projectilesController->tick(msPassed);
|
||||
}
|
||||
|
||||
void BattleFieldController::show(SDL_Surface * to)
|
||||
{
|
||||
Canvas canvas(to);
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
|
||||
|
@ -66,9 +66,11 @@ class BattleFieldController : public CIntObject
|
||||
void mouseMoved(const Point & cursorPosition) override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void activate() override;
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
public:
|
||||
BattleFieldController(BattleInterface & owner);
|
||||
|
||||
|
@ -28,8 +28,9 @@
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CStack.h"
|
||||
@ -95,7 +96,7 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *
|
||||
adventureInt->onAudioPaused();
|
||||
ongoingAnimationsState.set(true);
|
||||
|
||||
GH.pushInt(windowObject);
|
||||
GH.windows().pushWindow(windowObject);
|
||||
windowObject->blockUI(true);
|
||||
windowObject->updateQueue();
|
||||
|
||||
@ -167,7 +168,7 @@ BattleInterface::~BattleInterface()
|
||||
void BattleInterface::redrawBattlefield()
|
||||
{
|
||||
fieldController->redrawBackgroundWithHexes();
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void BattleInterface::stackReset(const CStack * stack)
|
||||
@ -328,7 +329,7 @@ void BattleInterface::battleFinished(const BattleResult& br, QueryID queryID)
|
||||
{
|
||||
curInt->cb->selectionMade(selection, queryID);
|
||||
};
|
||||
GH.pushInt(wnd);
|
||||
GH.windows().pushWindow(wnd);
|
||||
|
||||
curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
|
||||
CPlayerInterface::battleInt = nullptr;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
@ -202,6 +203,25 @@ const CGHeroInstance * BattleHero::instance()
|
||||
return hero;
|
||||
}
|
||||
|
||||
void BattleHero::tick(uint32_t msPassed)
|
||||
{
|
||||
size_t groupIndex = static_cast<size_t>(phase);
|
||||
|
||||
float timePassed = msPassed / 1000.f;
|
||||
|
||||
flagCurrentFrame += currentSpeed * timePassed;
|
||||
currentFrame += currentSpeed * timePassed;
|
||||
|
||||
if(flagCurrentFrame >= flagAnimation->size(0))
|
||||
flagCurrentFrame -= flagAnimation->size(0);
|
||||
|
||||
if(currentFrame >= animation->size(groupIndex))
|
||||
{
|
||||
currentFrame -= animation->size(groupIndex);
|
||||
switchToNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void BattleHero::render(Canvas & canvas)
|
||||
{
|
||||
size_t groupIndex = static_cast<size_t>(phase);
|
||||
@ -219,20 +239,6 @@ void BattleHero::render(Canvas & canvas)
|
||||
|
||||
canvas.draw(flagFrame, flagPosition);
|
||||
canvas.draw(heroFrame, heroPosition);
|
||||
|
||||
float timePassed = float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000.f;
|
||||
|
||||
flagCurrentFrame += currentSpeed * timePassed;
|
||||
currentFrame += currentSpeed * timePassed;
|
||||
|
||||
if(flagCurrentFrame >= flagAnimation->size(0))
|
||||
flagCurrentFrame -= flagAnimation->size(0);
|
||||
|
||||
if(currentFrame >= animation->size(groupIndex))
|
||||
{
|
||||
currentFrame -= animation->size(groupIndex);
|
||||
switchToNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void BattleHero::pause()
|
||||
@ -284,7 +290,7 @@ void BattleHero::heroLeftClicked()
|
||||
if(owner.getCurrentPlayerInterface()->cb->battleCanCastSpell(hero, spells::Mode::HERO) == ESpellCastProblem::OK) //check conditions
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
GH.pushIntT<CSpellWindow>(hero, owner.getCurrentPlayerInterface());
|
||||
GH.windows().createAndPushWindow<CSpellWindow>(hero, owner.getCurrentPlayerInterface());
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +305,7 @@ void BattleHero::heroRightClicked()
|
||||
{
|
||||
auto h = defender ? owner.defendingHeroInstance : owner.attackingHeroInstance;
|
||||
targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
|
||||
GH.pushIntT<HeroInfoWindow>(targetHero, &windowPosition);
|
||||
GH.windows().createAndPushWindow<HeroInfoWindow>(targetHero, &windowPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,6 +360,8 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
|
||||
|
||||
switchToNextPhase();
|
||||
play();
|
||||
|
||||
addUsedEvents(TIME);
|
||||
}
|
||||
|
||||
HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
|
||||
@ -584,8 +592,8 @@ void BattleResultWindow::buttonPressed(int button)
|
||||
|
||||
close();
|
||||
|
||||
if(dynamic_cast<BattleWindow*>(GH.topInt().get()))
|
||||
GH.popInts(1); //pop battle interface if present
|
||||
if(GH.windows().topWindow<BattleWindow>())
|
||||
GH.windows().popWindows(1); //pop battle interface if present
|
||||
|
||||
//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
|
||||
//so we can be sure that there is no dialogs left on GUI stack.
|
||||
|
@ -114,6 +114,7 @@ public:
|
||||
void setPhase(EHeroAnimType newPhase); //sets phase of hero animation
|
||||
|
||||
void collectRenderableObjects(BattleRenderer & renderer);
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
float getFrame() const;
|
||||
void onPhaseFinished(const std::function<void()> &);
|
||||
|
@ -159,9 +159,9 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere
|
||||
}
|
||||
}
|
||||
|
||||
void BattleObstacleController::update()
|
||||
void BattleObstacleController::tick(uint32_t msPassed)
|
||||
{
|
||||
timePassed += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
timePassed += msPassed / 1000.f;
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
BattleObstacleController(BattleInterface & owner);
|
||||
|
||||
/// called every frame
|
||||
void update();
|
||||
void tick(uint32_t msPassed);
|
||||
|
||||
/// call-in from network pack, add newly placed obstacles with any required animations
|
||||
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & oi);
|
||||
|
@ -58,15 +58,18 @@ void ProjectileMissile::show(Canvas & canvas)
|
||||
|
||||
canvas.draw(image, pos);
|
||||
}
|
||||
}
|
||||
|
||||
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
void ProjectileMissile::tick(uint32_t msPassed)
|
||||
{
|
||||
float timePassed = msPassed / 1000.f;
|
||||
progress += timePassed * speed;
|
||||
}
|
||||
|
||||
void ProjectileAnimatedMissile::show(Canvas & canvas)
|
||||
void ProjectileAnimatedMissile::tick(uint32_t msPassed)
|
||||
{
|
||||
ProjectileMissile::show(canvas);
|
||||
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
|
||||
ProjectileMissile::tick(msPassed);
|
||||
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
|
||||
size_t animationSize = animation->size(reverse ? 1 : 0);
|
||||
while (frameProgress > animationSize)
|
||||
frameProgress -= animationSize;
|
||||
@ -74,9 +77,15 @@ void ProjectileAnimatedMissile::show(Canvas & canvas)
|
||||
frameNum = std::floor(frameProgress);
|
||||
}
|
||||
|
||||
void ProjectileCatapult::tick(uint32_t msPassed)
|
||||
{
|
||||
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
|
||||
float timePassed = msPassed / 1000.f;
|
||||
progress += timePassed * speed;
|
||||
}
|
||||
|
||||
void ProjectileCatapult::show(Canvas & canvas)
|
||||
{
|
||||
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
|
||||
int frameCounter = std::floor(frameProgress);
|
||||
int frameIndex = (frameCounter + 1) % animation->size(0);
|
||||
|
||||
@ -90,9 +99,6 @@ void ProjectileCatapult::show(Canvas & canvas)
|
||||
|
||||
canvas.draw(image, pos);
|
||||
}
|
||||
|
||||
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
progress += timePassed * speed;
|
||||
}
|
||||
|
||||
void ProjectileRay::show(Canvas & canvas)
|
||||
@ -135,8 +141,11 @@ void ProjectileRay::show(Canvas & canvas)
|
||||
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
void ProjectileRay::tick(uint32_t msPassed)
|
||||
{
|
||||
float timePassed = msPassed / 1000.f;
|
||||
progress += timePassed * speed;
|
||||
}
|
||||
|
||||
@ -217,13 +226,22 @@ void BattleProjectileController::emitStackProjectile(const CStack * stack)
|
||||
}
|
||||
}
|
||||
|
||||
void BattleProjectileController::showProjectiles(Canvas & canvas)
|
||||
void BattleProjectileController::render(Canvas & canvas)
|
||||
{
|
||||
for ( auto projectile: projectiles)
|
||||
{
|
||||
if ( projectile->playing )
|
||||
projectile->show(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void BattleProjectileController::tick(uint32_t msPassed)
|
||||
{
|
||||
for ( auto projectile: projectiles)
|
||||
{
|
||||
if ( projectile->playing )
|
||||
projectile->tick(msPassed);
|
||||
}
|
||||
|
||||
vstd::erase_if(projectiles, [&](const std::shared_ptr<ProjectileBase> & projectile){
|
||||
return projectile->progress > 1.0f;
|
||||
|
@ -28,6 +28,7 @@ struct ProjectileBase
|
||||
{
|
||||
virtual ~ProjectileBase() = default;
|
||||
virtual void show(Canvas & canvas) = 0;
|
||||
virtual void tick(uint32_t msPassed) = 0;
|
||||
|
||||
Point from; // initial position on the screen
|
||||
Point dest; // target position on the screen
|
||||
@ -42,6 +43,7 @@ struct ProjectileBase
|
||||
struct ProjectileMissile : ProjectileBase
|
||||
{
|
||||
void show(Canvas & canvas) override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
int frameNum; // frame to display from projectile animation
|
||||
@ -51,7 +53,7 @@ struct ProjectileMissile : ProjectileBase
|
||||
/// Projectile for spell - render animation moving in straight line from origin to destination
|
||||
struct ProjectileAnimatedMissile : ProjectileMissile
|
||||
{
|
||||
void show(Canvas & canvas) override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
float frameProgress;
|
||||
};
|
||||
|
||||
@ -59,6 +61,7 @@ struct ProjectileAnimatedMissile : ProjectileMissile
|
||||
struct ProjectileCatapult : ProjectileBase
|
||||
{
|
||||
void show(Canvas & canvas) override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
float frameProgress;
|
||||
@ -68,6 +71,7 @@ struct ProjectileCatapult : ProjectileBase
|
||||
struct ProjectileRay : ProjectileBase
|
||||
{
|
||||
void show(Canvas & canvas) override;
|
||||
void tick(uint32_t msPassed) override;
|
||||
|
||||
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
|
||||
};
|
||||
@ -102,7 +106,10 @@ public:
|
||||
BattleProjectileController(BattleInterface & owner);
|
||||
|
||||
/// renders all currently active projectiles
|
||||
void showProjectiles(Canvas & canvas);
|
||||
void render(Canvas & canvas);
|
||||
|
||||
/// updates positioning / animations of all projectiles
|
||||
void tick(uint32_t msPassed);
|
||||
|
||||
/// returns true if stack has projectile that is yet to hit target
|
||||
bool hasActiveProjectile(const CStack * stack, bool emittedOnly) const;
|
||||
|
@ -335,13 +335,12 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
|
||||
}
|
||||
|
||||
stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
|
||||
stackAnimation[stack->unitId()]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
}
|
||||
|
||||
void BattleStacksController::update()
|
||||
void BattleStacksController::tick(uint32_t msPassed)
|
||||
{
|
||||
updateHoveredStacks();
|
||||
updateBattleAnimations();
|
||||
updateBattleAnimations(msPassed);
|
||||
}
|
||||
|
||||
void BattleStacksController::initializeBattleAnimations()
|
||||
@ -352,21 +351,30 @@ void BattleStacksController::initializeBattleAnimations()
|
||||
elem->tryInitialize();
|
||||
}
|
||||
|
||||
void BattleStacksController::stepFrameBattleAnimations()
|
||||
void BattleStacksController::tickFrameBattleAnimations(uint32_t msPassed)
|
||||
{
|
||||
for (auto stack : owner.curInt->cb->battleGetAllStacks(false))
|
||||
{
|
||||
if (stackAnimation.find(stack->unitId()) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
|
||||
stackAnimation[stack->unitId()]->incrementFrame(msPassed / 1000.f);
|
||||
}
|
||||
|
||||
// operate on copy - to prevent potential iterator invalidation due to push_back's
|
||||
// FIXME? : remove remaining calls to addNewAnim from BattleAnimation::nextFrame (only Catapult explosion at the time of writing)
|
||||
|
||||
auto copiedVector = currentAnimations;
|
||||
for (auto & elem : copiedVector)
|
||||
if (elem && elem->isInitialized())
|
||||
elem->nextFrame();
|
||||
elem->tick(msPassed);
|
||||
}
|
||||
|
||||
void BattleStacksController::updateBattleAnimations()
|
||||
void BattleStacksController::updateBattleAnimations(uint32_t msPassed)
|
||||
{
|
||||
bool hadAnimations = !currentAnimations.empty();
|
||||
initializeBattleAnimations();
|
||||
stepFrameBattleAnimations();
|
||||
tickFrameBattleAnimations(msPassed);
|
||||
vstd::erase(currentAnimations, nullptr);
|
||||
|
||||
if (hadAnimations && currentAnimations.empty())
|
||||
|
@ -91,9 +91,9 @@ class BattleStacksController
|
||||
void removeExpiredColorFilters();
|
||||
|
||||
void initializeBattleAnimations();
|
||||
void stepFrameBattleAnimations();
|
||||
void tickFrameBattleAnimations(uint32_t msPassed);
|
||||
|
||||
void updateBattleAnimations();
|
||||
void updateBattleAnimations(uint32_t msPassed);
|
||||
void updateHoveredStacks();
|
||||
|
||||
std::vector<const CStack *> selectHoveredStacks();
|
||||
@ -138,7 +138,7 @@ public:
|
||||
const CStack* getActiveStack() const;
|
||||
const std::vector<uint32_t> getHoveredStacksUnitIds() const;
|
||||
|
||||
void update();
|
||||
void tick(uint32_t msPassed);
|
||||
|
||||
/// returns position of animation needed to place stack in specific hex
|
||||
Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../windows/CSpellWindow.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -72,7 +73,6 @@ BattleWindow::BattleWindow(BattleInterface & owner):
|
||||
|
||||
console = widget<BattleConsole>("console");
|
||||
|
||||
GH.statusbar = console;
|
||||
owner.console = console;
|
||||
|
||||
owner.fieldController.reset( new BattleFieldController(owner));
|
||||
@ -153,7 +153,7 @@ void BattleWindow::hideQueue()
|
||||
pos.h -= queue->pos.h;
|
||||
pos = center();
|
||||
}
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void BattleWindow::showQueue()
|
||||
@ -166,7 +166,7 @@ void BattleWindow::showQueue()
|
||||
|
||||
createQueue();
|
||||
updateQueue();
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void BattleWindow::updateQueue()
|
||||
@ -176,17 +176,23 @@ void BattleWindow::updateQueue()
|
||||
|
||||
void BattleWindow::activate()
|
||||
{
|
||||
GH.statusbar = console;
|
||||
GH.setStatusbar(console);
|
||||
CIntObject::activate();
|
||||
LOCPLINT->cingconsole->activate();
|
||||
}
|
||||
|
||||
void BattleWindow::deactivate()
|
||||
{
|
||||
GH.setStatusbar(nullptr);
|
||||
CIntObject::deactivate();
|
||||
LOCPLINT->cingconsole->deactivate();
|
||||
}
|
||||
|
||||
bool BattleWindow::captureThisKey(EShortcut key)
|
||||
{
|
||||
return owner.openingPlaying();
|
||||
}
|
||||
|
||||
void BattleWindow::keyPressed(EShortcut key)
|
||||
{
|
||||
if (owner.openingPlaying())
|
||||
@ -252,7 +258,7 @@ void BattleWindow::bOptionsf()
|
||||
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
|
||||
GH.pushIntT<SettingsMainWindow>(&owner);
|
||||
GH.windows().createAndPushWindow<SettingsMainWindow>(&owner);
|
||||
}
|
||||
|
||||
void BattleWindow::bSurrenderf()
|
||||
@ -359,7 +365,7 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
|
||||
}
|
||||
|
||||
auto anim = std::make_shared<CAnimation>(iconName);
|
||||
w->setImage(anim, false);
|
||||
w->setImage(anim);
|
||||
w->redraw();
|
||||
}
|
||||
|
||||
@ -420,7 +426,7 @@ void BattleWindow::bSpellf()
|
||||
|
||||
if(spellCastProblem == ESpellCastProblem::OK)
|
||||
{
|
||||
GH.pushIntT<CSpellWindow>(myHero, owner.curInt.get());
|
||||
GH.windows().createAndPushWindow<CSpellWindow>(myHero, owner.curInt.get());
|
||||
}
|
||||
else if (spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
|
||||
{
|
||||
@ -565,7 +571,7 @@ void BattleWindow::show(SDL_Surface *to)
|
||||
|
||||
void BattleWindow::close()
|
||||
{
|
||||
if(GH.topInt().get() != this)
|
||||
if(!GH.windows().isTopWindow(this))
|
||||
logGlobal->error("Only top interface must be closed");
|
||||
GH.popInts(1);
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ public:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
bool captureThisKey(EShortcut key) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void show(SDL_Surface *to) override;
|
||||
void showAll(SDL_Surface *to) override;
|
||||
|
@ -14,10 +14,13 @@
|
||||
#include "CIntObject.h"
|
||||
#include "CursorHandler.h"
|
||||
#include "ShortcutHandler.h"
|
||||
#include "FramerateManager.h"
|
||||
#include "WindowHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../renderSDL/ScreenHandler.h"
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../battle/BattleInterface.h"
|
||||
@ -38,6 +41,8 @@
|
||||
#include "ios/utils.h"
|
||||
#endif
|
||||
|
||||
CGuiHandler GH;
|
||||
|
||||
extern std::queue<SDL_Event> SDLEventsQueue;
|
||||
extern boost::mutex eventsM;
|
||||
|
||||
@ -95,9 +100,11 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
|
||||
|
||||
void CGuiHandler::init()
|
||||
{
|
||||
windowHandlerInstance = std::make_unique<WindowHandler>();
|
||||
screenHandlerInstance = std::make_unique<ScreenHandler>();
|
||||
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
|
||||
mainFPSmng = new CFramerateManager();
|
||||
mainFPSmng->init(settings["video"]["targetfps"].Integer());
|
||||
framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
|
||||
|
||||
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
||||
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||
}
|
||||
@ -120,77 +127,9 @@ void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
|
||||
elem->active_m &= ~activityFlag;
|
||||
}
|
||||
|
||||
void CGuiHandler::popInt(std::shared_ptr<IShowActivatable> top)
|
||||
{
|
||||
assert(listInt.front() == top);
|
||||
top->deactivate();
|
||||
disposed.push_back(top);
|
||||
listInt.pop_front();
|
||||
objsToBlit -= top;
|
||||
if(!listInt.empty())
|
||||
listInt.front()->activate();
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
void CGuiHandler::pushInt(std::shared_ptr<IShowActivatable> newInt)
|
||||
{
|
||||
assert(newInt);
|
||||
assert(!vstd::contains(listInt, newInt)); // do not add same object twice
|
||||
|
||||
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
|
||||
screenBuf = screen2;
|
||||
|
||||
if(!listInt.empty())
|
||||
listInt.front()->deactivate();
|
||||
listInt.push_front(newInt);
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
newInt->activate();
|
||||
objsToBlit.push_back(newInt);
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
void CGuiHandler::popInts(int howMany)
|
||||
{
|
||||
if(!howMany) return; //senseless but who knows...
|
||||
|
||||
assert(listInt.size() >= howMany);
|
||||
listInt.front()->deactivate();
|
||||
for(int i=0; i < howMany; i++)
|
||||
{
|
||||
objsToBlit -= listInt.front();
|
||||
disposed.push_back(listInt.front());
|
||||
listInt.pop_front();
|
||||
}
|
||||
|
||||
if(!listInt.empty())
|
||||
{
|
||||
listInt.front()->activate();
|
||||
totalRedraw();
|
||||
}
|
||||
fakeMouseMove();
|
||||
}
|
||||
|
||||
std::shared_ptr<IShowActivatable> CGuiHandler::topInt()
|
||||
{
|
||||
if(listInt.empty())
|
||||
return std::shared_ptr<IShowActivatable>();
|
||||
else
|
||||
return listInt.front();
|
||||
}
|
||||
|
||||
void CGuiHandler::totalRedraw()
|
||||
{
|
||||
#ifdef VCMI_ANDROID
|
||||
SDL_FillRect(screen2, NULL, SDL_MapRGB(screen2->format, 0, 0, 0));
|
||||
#endif
|
||||
for(auto & elem : objsToBlit)
|
||||
elem->showAll(screen2);
|
||||
CSDL_Ext::blitAt(screen2,0,0,screen);
|
||||
}
|
||||
|
||||
void CGuiHandler::updateTime()
|
||||
{
|
||||
int ms = mainFPSmng->getElapsedMilliseconds();
|
||||
int ms = framerateManager().getElapsedMilliseconds();
|
||||
std::list<CIntObject*> hlp = timeinterested;
|
||||
for (auto & elem : hlp)
|
||||
{
|
||||
@ -393,7 +332,7 @@ void CGuiHandler::handleCurrentEvent( SDL_Event & current )
|
||||
//not working yet since CClient::run remain locked after BattleInterface removal
|
||||
// if(LOCPLINT->battleInt)
|
||||
// {
|
||||
// GH.popInts(1);
|
||||
// GH.windows().popInts(1);
|
||||
// vstd::clear_pointer(LOCPLINT->battleInt);
|
||||
// }
|
||||
break;
|
||||
@ -631,15 +570,6 @@ void CGuiHandler::handleMouseMotion(const SDL_Event & current)
|
||||
handleMoveInterested(current.motion);
|
||||
}
|
||||
|
||||
void CGuiHandler::simpleRedraw()
|
||||
{
|
||||
//update only top interface and draw background
|
||||
if(objsToBlit.size() > 1)
|
||||
CSDL_Ext::blitAt(screen2,0,0,screen); //blit background
|
||||
if(!objsToBlit.empty())
|
||||
objsToBlit.back()->show(screen); //blit active interface/window
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
|
||||
{
|
||||
//sending active, MotionInterested objects mouseMoved() call
|
||||
@ -686,13 +616,12 @@ void CGuiHandler::renderFrame()
|
||||
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
|
||||
disposed.clear();
|
||||
windows().onFrameRendered();
|
||||
}
|
||||
|
||||
mainFPSmng->framerateDelay(); // holds a constant FPS
|
||||
framerateManager().framerateDelay(); // holds a constant FPS
|
||||
}
|
||||
|
||||
|
||||
CGuiHandler::CGuiHandler()
|
||||
: lastClick(-500, -500)
|
||||
, lastClickTime(0)
|
||||
@ -702,23 +631,28 @@ CGuiHandler::CGuiHandler()
|
||||
, mouseButtonsMask(0)
|
||||
, continueEventHandling(true)
|
||||
, curInt(nullptr)
|
||||
, mainFPSmng(nullptr)
|
||||
, statusbar(nullptr)
|
||||
, fakeStatusBar(std::make_shared<EmptyStatusBar>())
|
||||
, terminate_cond (new CondSh<bool>(false))
|
||||
{
|
||||
terminate_cond = new CondSh<bool>(false);
|
||||
}
|
||||
|
||||
CGuiHandler::~CGuiHandler()
|
||||
{
|
||||
delete mainFPSmng;
|
||||
delete terminate_cond;
|
||||
}
|
||||
|
||||
ShortcutHandler & CGuiHandler::shortcutsHandler()
|
||||
{
|
||||
assert(shortcutsHandlerInstance);
|
||||
return *shortcutsHandlerInstance;
|
||||
}
|
||||
|
||||
FramerateManager & CGuiHandler::framerateManager()
|
||||
{
|
||||
assert(framerateManagerInstance);
|
||||
return *framerateManagerInstance;
|
||||
}
|
||||
|
||||
void CGuiHandler::moveCursorToPosition(const Point & position)
|
||||
{
|
||||
SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
|
||||
@ -780,7 +714,7 @@ void CGuiHandler::drawFPSCounter()
|
||||
static SDL_Rect overlay = { 0, 0, 64, 32};
|
||||
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
|
||||
SDL_FillRect(screen, &overlay, black);
|
||||
std::string fps = std::to_string(mainFPSmng->getFramerate());
|
||||
std::string fps = std::to_string(framerateManager().getFramerate());
|
||||
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
|
||||
}
|
||||
|
||||
@ -803,51 +737,34 @@ void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
CFramerateManager::CFramerateManager()
|
||||
: rate(0)
|
||||
, rateticks(0)
|
||||
, fps(0)
|
||||
, accumulatedFrames(0)
|
||||
, accumulatedTime(0)
|
||||
, lastticks(0)
|
||||
, timeElapsed(0)
|
||||
{}
|
||||
|
||||
void CFramerateManager::init(int newRate)
|
||||
IScreenHandler & CGuiHandler::screenHandler()
|
||||
{
|
||||
rate = newRate;
|
||||
rateticks = 1000.0 / rate;
|
||||
this->lastticks = SDL_GetTicks();
|
||||
return *screenHandlerInstance;
|
||||
}
|
||||
|
||||
void CFramerateManager::framerateDelay()
|
||||
WindowHandler & CGuiHandler::windows()
|
||||
{
|
||||
ui32 currentTicks = SDL_GetTicks();
|
||||
|
||||
timeElapsed = currentTicks - lastticks;
|
||||
accumulatedFrames++;
|
||||
|
||||
// FPS is higher than it should be, then wait some time
|
||||
if(timeElapsed < rateticks)
|
||||
{
|
||||
int timeToSleep = (uint32_t)ceil(this->rateticks) - timeElapsed;
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(timeToSleep));
|
||||
}
|
||||
|
||||
currentTicks = SDL_GetTicks();
|
||||
// recalculate timeElapsed for external calls via getElapsed()
|
||||
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
|
||||
timeElapsed = std::min<ui32>(currentTicks - lastticks, 100);
|
||||
|
||||
lastticks = SDL_GetTicks();
|
||||
|
||||
accumulatedTime += timeElapsed;
|
||||
|
||||
if(accumulatedFrames >= 100)
|
||||
{
|
||||
//about 2 second should be passed
|
||||
fps = static_cast<int>(ceil(1000.0 / (accumulatedTime / accumulatedFrames)));
|
||||
accumulatedTime = 0;
|
||||
accumulatedFrames = 0;
|
||||
}
|
||||
assert(windowHandlerInstance);
|
||||
return *windowHandlerInstance;
|
||||
}
|
||||
|
||||
std::shared_ptr<IStatusBar> CGuiHandler::statusbar()
|
||||
{
|
||||
auto locked = currentStatusBar.lock();
|
||||
|
||||
if (!locked)
|
||||
return fakeStatusBar;
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
void CGuiHandler::setStatusbar(std::shared_ptr<IStatusBar> newStatusBar)
|
||||
{
|
||||
currentStatusBar = newStatusBar;
|
||||
}
|
||||
|
||||
void CGuiHandler::onScreenResize()
|
||||
{
|
||||
screenHandler().onScreenResize();
|
||||
windows().onScreenResize();
|
||||
}
|
||||
|
@ -23,12 +23,14 @@ union SDL_Event;
|
||||
struct SDL_MouseMotionEvent;
|
||||
|
||||
class ShortcutHandler;
|
||||
class CFramerateManager;
|
||||
class FramerateManager;
|
||||
class IStatusBar;
|
||||
class CIntObject;
|
||||
class IUpdateable;
|
||||
class IShowActivatable;
|
||||
class IShowable;
|
||||
class IScreenHandler;
|
||||
class WindowHandler;
|
||||
|
||||
// TODO: event handling need refactoring
|
||||
enum class EUserEvent
|
||||
@ -43,42 +45,24 @@ enum class EUserEvent
|
||||
FORCE_QUIT, //quit client without question
|
||||
};
|
||||
|
||||
// A fps manager which holds game updates at a constant rate
|
||||
class CFramerateManager
|
||||
{
|
||||
private:
|
||||
double rateticks;
|
||||
ui32 lastticks;
|
||||
ui32 timeElapsed;
|
||||
int rate;
|
||||
int fps; // the actual fps value
|
||||
ui32 accumulatedTime;
|
||||
ui32 accumulatedFrames;
|
||||
|
||||
public:
|
||||
CFramerateManager(); // initializes the manager with a given fps rate
|
||||
void init(int newRate); // needs to be called directly before the main game loop to reset the internal timer
|
||||
void framerateDelay(); // needs to be called every game update cycle
|
||||
ui32 getElapsedMilliseconds() const {return this->timeElapsed;}
|
||||
ui32 getFrameNumber() const { return accumulatedFrames; }
|
||||
ui32 getFramerate() const { return fps; };
|
||||
};
|
||||
|
||||
// Handles GUI logic and drawing
|
||||
class CGuiHandler
|
||||
{
|
||||
public:
|
||||
CFramerateManager * mainFPSmng; //to keep const framerate
|
||||
std::list<std::shared_ptr<IShowActivatable>> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
|
||||
std::shared_ptr<IStatusBar> statusbar;
|
||||
|
||||
|
||||
private:
|
||||
/// Fake no-op version status bar, for use in windows that have no status bar
|
||||
std::shared_ptr<IStatusBar> fakeStatusBar;
|
||||
|
||||
/// Status bar of current window, if any. Uses weak_ptr to allow potential hanging reference after owned window has been deleted
|
||||
std::weak_ptr<IStatusBar> currentStatusBar;
|
||||
|
||||
Point cursorPosition;
|
||||
uint32_t mouseButtonsMask;
|
||||
|
||||
std::vector<std::shared_ptr<IShowActivatable>> disposed;
|
||||
|
||||
std::unique_ptr<ShortcutHandler> shortcutsHandlerInstance;
|
||||
std::unique_ptr<WindowHandler> windowHandlerInstance;
|
||||
|
||||
std::atomic<bool> continueEventHandling;
|
||||
using CIntObjectList = std::list<CIntObject *>;
|
||||
@ -95,6 +79,8 @@ private:
|
||||
CIntObjectList doubleClickInterested;
|
||||
CIntObjectList textInterested;
|
||||
|
||||
std::unique_ptr<IScreenHandler> screenHandlerInstance;
|
||||
std::unique_ptr<FramerateManager> framerateManagerInstance;
|
||||
|
||||
void handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed);
|
||||
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
|
||||
@ -109,13 +95,15 @@ public:
|
||||
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
|
||||
void handleElementDeActivate(CIntObject * elem, ui16 activityFlag);
|
||||
public:
|
||||
//objs to blit
|
||||
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
|
||||
|
||||
/// returns current position of mouse cursor, relative to vcmi window
|
||||
const Point & getCursorPosition() const;
|
||||
|
||||
ShortcutHandler & shortcutsHandler();
|
||||
FramerateManager & framerateManager();
|
||||
|
||||
/// Returns current logical screen dimensions
|
||||
/// May not match size of window if user has UI scaling different from 100%
|
||||
Point screenDimensions() const;
|
||||
|
||||
/// returns true if at least one mouse button is pressed
|
||||
@ -135,6 +123,16 @@ public:
|
||||
/// moves mouse pointer into specified position inside vcmi window
|
||||
void moveCursorToPosition(const Point & position);
|
||||
|
||||
IScreenHandler & screenHandler();
|
||||
|
||||
WindowHandler & windows();
|
||||
|
||||
/// Returns currently active status bar. Guaranteed to be non-null
|
||||
std::shared_ptr<IStatusBar> statusbar();
|
||||
|
||||
/// Set currently active status bar
|
||||
void setStatusbar(std::shared_ptr<IStatusBar>);
|
||||
|
||||
IUpdateable *curInt;
|
||||
|
||||
Point lastClick;
|
||||
@ -153,22 +151,8 @@ public:
|
||||
void init();
|
||||
void renderFrame();
|
||||
|
||||
void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering
|
||||
void simpleRedraw(); //update only top interface and draw background from buffer, sets a flag, method gets called at the end of the rendering
|
||||
|
||||
void pushInt(std::shared_ptr<IShowActivatable> newInt); //deactivate old top interface, activates this one and pushes to the top
|
||||
template <typename T, typename ... Args>
|
||||
void pushIntT(Args && ... args)
|
||||
{
|
||||
auto newInt = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
pushInt(newInt);
|
||||
}
|
||||
|
||||
void popInts(int howMany); //pops one or more interfaces - deactivates top, deletes and removes given number of interfaces, activates new front
|
||||
|
||||
void popInt(std::shared_ptr<IShowActivatable> top); //removes given interface from the top and activates next
|
||||
|
||||
std::shared_ptr<IShowActivatable> topInt(); //returns top interface
|
||||
/// called whenever user selects different resolution, requiring to center/resize all windows
|
||||
void onScreenResize();
|
||||
|
||||
void updateTime(); //handles timeInterested
|
||||
void handleEvents(); //takes events from queue and calls interested objects
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "CIntObject.h"
|
||||
|
||||
#include "CGuiHandler.h"
|
||||
#include "WindowHandler.h"
|
||||
#include "Shortcut.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../windows/CMessage.h"
|
||||
@ -159,16 +160,6 @@ void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFon
|
||||
graphics->fonts[font]->renderTextCenter(dst, text, kolor, pos.topLeft() + p);
|
||||
}
|
||||
|
||||
void CIntObject::blitAtLoc( SDL_Surface * src, int x, int y, SDL_Surface * dst )
|
||||
{
|
||||
CSDL_Ext::blitAt(src, pos.x + x, pos.y + y, dst);
|
||||
}
|
||||
|
||||
void CIntObject::blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst)
|
||||
{
|
||||
blitAtLoc(src, p.x, p.y, dst);
|
||||
}
|
||||
|
||||
void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst)
|
||||
{
|
||||
graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, Point(pos.x + x, pos.y + y));
|
||||
@ -199,11 +190,22 @@ void CIntObject::disable()
|
||||
void CIntObject::enable()
|
||||
{
|
||||
if(!active_m && (!parent_m || parent_m->active))
|
||||
{
|
||||
activate();
|
||||
redraw();
|
||||
}
|
||||
|
||||
recActions = 255;
|
||||
}
|
||||
|
||||
void CIntObject::setEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
enable();
|
||||
else
|
||||
disable();
|
||||
}
|
||||
|
||||
void CIntObject::fitToScreen(int borderWidth, bool propagate)
|
||||
{
|
||||
Point newPos = pos.topLeft();
|
||||
@ -242,7 +244,7 @@ void CIntObject::addChild(CIntObject * child, bool adjustPosition)
|
||||
children.push_back(child);
|
||||
child->parent_m = this;
|
||||
if(adjustPosition)
|
||||
child->pos += pos.topLeft();
|
||||
child->moveBy(pos.topLeft(), adjustPosition);
|
||||
|
||||
if (!active && child->active)
|
||||
child->deactivate();
|
||||
@ -286,6 +288,11 @@ void CIntObject::redraw()
|
||||
}
|
||||
}
|
||||
|
||||
void CIntObject::onScreenResize()
|
||||
{
|
||||
center(pos, true);
|
||||
}
|
||||
|
||||
const Rect & CIntObject::center( const Rect &r, bool propagate )
|
||||
{
|
||||
pos.w = r.w;
|
||||
@ -350,9 +357,9 @@ WindowBase::WindowBase(int used_, Point pos_)
|
||||
|
||||
void WindowBase::close()
|
||||
{
|
||||
if(GH.topInt().get() != this)
|
||||
if(!GH.windows().isTopWindow(this))
|
||||
logGlobal->error("Only top interface must be closed");
|
||||
GH.popInts(1);
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
IStatusBar::~IStatusBar()
|
||||
|
@ -53,7 +53,7 @@ class IShowActivatable : public IShowable, public IActivatable
|
||||
{
|
||||
public:
|
||||
//redraw parent flag - this int may be semi-transparent and require redraw of parent window
|
||||
enum {BLOCK_ADV_HOTKEYS = 2, REDRAW_PARENT=8};
|
||||
enum {REDRAW_PARENT=8};
|
||||
int type; //bin flags using etype
|
||||
IShowActivatable();
|
||||
virtual ~IShowActivatable(){};
|
||||
@ -140,8 +140,13 @@ public:
|
||||
ui8 defActions; //which calls will be tried to be redirected to children
|
||||
ui8 recActions; //which calls we allow to receive from parent
|
||||
|
||||
void disable(); //deactivates if needed, blocks all automatic activity, allows only disposal
|
||||
void enable(); //activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
|
||||
/// deactivates if needed, blocks all automatic activity, allows only disposal
|
||||
void disable();
|
||||
/// activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
|
||||
void enable();
|
||||
/// deactivates or activates UI element based on flag
|
||||
void setEnabled(bool on);
|
||||
|
||||
|
||||
// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
|
||||
// usually used automatically by parent
|
||||
@ -155,6 +160,10 @@ public:
|
||||
//request complete redraw of this object
|
||||
void redraw() override;
|
||||
|
||||
/// called only for windows whenever screen size changes
|
||||
/// default behavior is to re-center, can be overriden
|
||||
virtual void onScreenResize();
|
||||
|
||||
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
|
||||
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
|
||||
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position
|
||||
@ -177,10 +186,6 @@ public:
|
||||
void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color color, SDL_Surface * dst);
|
||||
void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charsPerLine, SDL_Color color, SDL_Surface * dst);
|
||||
|
||||
//image blitting. If possible use CPicture or CAnimImage instead
|
||||
void blitAtLoc(SDL_Surface * src, int x, int y, SDL_Surface * dst);
|
||||
void blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst);
|
||||
|
||||
friend class CGuiHandler;
|
||||
};
|
||||
|
||||
@ -226,3 +231,12 @@ public:
|
||||
virtual void setEnteredText(const std::string & text) = 0;
|
||||
|
||||
};
|
||||
|
||||
class EmptyStatusBar : public IStatusBar
|
||||
{
|
||||
virtual void write(const std::string & text){};
|
||||
virtual void clear(){};
|
||||
virtual void clearIfMatching(const std::string & testedText){};
|
||||
virtual void setEnteringMode(bool on){};
|
||||
virtual void setEnteredText(const std::string & text){};
|
||||
};
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CursorHandler.h"
|
||||
|
||||
#include "CGuiHandler.h"
|
||||
#include "FramerateManager.h"
|
||||
#include "../renderSDL/CursorSoftware.h"
|
||||
#include "../renderSDL/CursorHardware.h"
|
||||
#include "../render/CAnimation.h"
|
||||
@ -250,7 +251,7 @@ void CursorHandler::updateSpellcastCursor()
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
||||
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime >= frameDisplayDuration)
|
||||
|
57
client/gui/FramerateManager.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* FramerateManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "FramerateManager.h"
|
||||
|
||||
FramerateManager::FramerateManager(int targetFrameRate)
|
||||
: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
|
||||
, lastFrameIndex(0)
|
||||
, lastFrameTimes({})
|
||||
, lastTimePoint (Clock::now())
|
||||
{
|
||||
boost::range::fill(lastFrameTimes, targetFrameTime);
|
||||
}
|
||||
|
||||
void FramerateManager::framerateDelay()
|
||||
{
|
||||
Duration timeSpentBusy = Clock::now() - lastTimePoint;
|
||||
|
||||
// FPS is higher than it should be, then wait some time
|
||||
if(timeSpentBusy < targetFrameTime)
|
||||
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
|
||||
|
||||
// compute actual timeElapsed taking into account actual sleep interval
|
||||
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
|
||||
TimePoint currentTicks = Clock::now();
|
||||
Duration timeElapsed = currentTicks - lastTimePoint;
|
||||
if(timeElapsed > boost::chrono::milliseconds(100))
|
||||
timeElapsed = boost::chrono::milliseconds(100);
|
||||
|
||||
lastTimePoint = currentTicks;
|
||||
lastFrameIndex = (lastFrameIndex + 1) % lastFrameTimes.size();
|
||||
lastFrameTimes[lastFrameIndex] = timeElapsed;
|
||||
}
|
||||
|
||||
ui32 FramerateManager::getElapsedMilliseconds() const
|
||||
{
|
||||
return lastFrameTimes[lastFrameIndex] / boost::chrono::milliseconds(1);
|
||||
}
|
||||
|
||||
ui32 FramerateManager::getFramerate() const
|
||||
{
|
||||
Duration accumulatedTime = std::accumulate(lastFrameTimes.begin(), lastFrameTimes.end(), Duration());
|
||||
|
||||
auto actualFrameTime = accumulatedTime / lastFrameTimes.size();
|
||||
if(actualFrameTime == actualFrameTime.zero())
|
||||
return 0;
|
||||
|
||||
return std::round(boost::chrono::duration<double>(1) / actualFrameTime);
|
||||
};
|
40
client/gui/FramerateManager.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* FramerateManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/// Framerate manager controls current game frame rate by constantly trying to reach targeted frame rate
|
||||
class FramerateManager
|
||||
{
|
||||
using Clock = boost::chrono::high_resolution_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = Clock::duration;
|
||||
|
||||
/// cyclic buffer of durations of last frames
|
||||
std::array<Duration, 60> lastFrameTimes;
|
||||
|
||||
Duration targetFrameTime;
|
||||
TimePoint lastTimePoint;
|
||||
|
||||
/// index of last measured frome in lastFrameTimes array
|
||||
ui32 lastFrameIndex;
|
||||
|
||||
public:
|
||||
FramerateManager(int targetFramerate);
|
||||
|
||||
/// must be called every frame
|
||||
/// updates framerate calculations and executes sleep to maintain target frame rate
|
||||
void framerateDelay();
|
||||
|
||||
/// returns duration of last frame in seconds
|
||||
ui32 getElapsedMilliseconds() const;
|
||||
|
||||
/// returns current estimation of frame rate
|
||||
ui32 getFramerate() const;
|
||||
};
|
@ -82,15 +82,26 @@ void InterfaceObjectConfigurable::build(const JsonNode &config)
|
||||
items = &config["items"];
|
||||
}
|
||||
|
||||
const std::string unnamedObjectPrefix = "__widget_";
|
||||
for(const auto & item : items->Vector())
|
||||
{
|
||||
std::string name = item["name"].isNull()
|
||||
? unnamedObjectPrefix + std::to_string(unnamedObjectId++)
|
||||
: item["name"].String();
|
||||
logGlobal->debug("Building widget with name %s", name);
|
||||
widgets[name] = buildWidget(item);
|
||||
}
|
||||
addWidget(item["name"].String(), buildWidget(item));
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::addWidget(const std::string & namePreferred, std::shared_ptr<CIntObject> widget)
|
||||
{
|
||||
static const std::string unnamedObjectPrefix = "__widget_";
|
||||
|
||||
std::string nameActual;
|
||||
|
||||
if (widgets.count(namePreferred) == 0)
|
||||
nameActual = namePreferred;
|
||||
else
|
||||
logGlobal->error("Duplicated widget name: '%s'", namePreferred);
|
||||
|
||||
if (nameActual.empty())
|
||||
nameActual = unnamedObjectPrefix + std::to_string(unnamedObjectId++);
|
||||
|
||||
logGlobal->debug("Building widget with name %s", nameActual);
|
||||
widgets[nameActual] = widget;
|
||||
}
|
||||
|
||||
std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const
|
||||
@ -174,6 +185,8 @@ EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const
|
||||
return EFonts::FONT_SMALL;
|
||||
if(config.String() == "tiny")
|
||||
return EFonts::FONT_TINY;
|
||||
if(config.String() == "calisto")
|
||||
return EFonts::FONT_CALLI;
|
||||
}
|
||||
logGlobal->debug("Uknown font attribute");
|
||||
return EFonts::FONT_TIMES;
|
||||
@ -287,15 +300,7 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co
|
||||
assert(imgOrder.size() >= 4);
|
||||
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
|
||||
}
|
||||
if(!config["callback"].isNull())
|
||||
{
|
||||
std::string callbackName = config["callback"].String();
|
||||
|
||||
if (callbacks.count(callbackName))
|
||||
button->addCallback(callbacks.at(callbackName));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
loadToggleButtonCallback(button, config["callback"]);
|
||||
return button;
|
||||
}
|
||||
|
||||
@ -319,32 +324,69 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode
|
||||
assert(imgOrder.size() >= 4);
|
||||
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
|
||||
}
|
||||
if(!config["callback"].isNull())
|
||||
{
|
||||
std::string callbackName = config["callback"].String();
|
||||
|
||||
if (callbacks.count(callbackName) > 0)
|
||||
button->addCallback(std::bind(callbacks.at(callbackName), 0));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
if(!config["hotkey"].isNull())
|
||||
{
|
||||
if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING)
|
||||
{
|
||||
button->assignedKey = readHotkey(config["hotkey"]);
|
||||
|
||||
auto target = shortcuts.find(button->assignedKey);
|
||||
if (target != shortcuts.end())
|
||||
{
|
||||
button->addCallback(target->second.callback);
|
||||
target->second.assignedToButton = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
loadButtonBorderColor(button, config["borderColor"]);
|
||||
loadButtonCallback(button, config["callback"]);
|
||||
loadButtonHotkey(button, config["hotkey"]);
|
||||
return button;
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonBorderColor(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if (config.isNull())
|
||||
return;
|
||||
|
||||
auto color = readColor(config);
|
||||
button->setBorderColor(color);
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadToggleButtonCallback(std::shared_ptr<CToggleButton> button, const JsonNode & config) const
|
||||
{
|
||||
if(config.isNull())
|
||||
return;
|
||||
|
||||
std::string callbackName = config.String();
|
||||
|
||||
if (callbacks.count(callbackName) > 0)
|
||||
button->addCallback(callbacks.at(callbackName));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonCallback(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if(config.isNull())
|
||||
return;
|
||||
|
||||
std::string callbackName = config.String();
|
||||
|
||||
if (callbacks.count(callbackName) > 0)
|
||||
button->addCallback(std::bind(callbacks.at(callbackName), 0));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonHotkey(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if(config.isNull())
|
||||
return;
|
||||
|
||||
if(config.getType() != JsonNode::JsonType::DATA_STRING)
|
||||
{
|
||||
logGlobal->error("Invalid shortcut format - string expected!");
|
||||
return;
|
||||
}
|
||||
|
||||
button->assignedKey = readHotkey(config);
|
||||
|
||||
auto target = shortcuts.find(button->assignedKey);
|
||||
if (target == shortcuts.end())
|
||||
return;
|
||||
|
||||
button->addCallback(target->second.callback);
|
||||
target->second.assignedToButton = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const
|
||||
{
|
||||
logGlobal->debug("Building widget CLabelGroup");
|
||||
@ -374,7 +416,8 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
|
||||
auto itemsTotal = config["itemsTotal"].Integer();
|
||||
auto value = config["selected"].Integer();
|
||||
bool horizontal = config["orientation"].String() == "horizontal";
|
||||
auto const & result = std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
|
||||
const auto & result =
|
||||
std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
|
||||
|
||||
if (!config["scrollBounds"].isNull())
|
||||
{
|
||||
|
@ -48,6 +48,8 @@ protected:
|
||||
|
||||
//must be called after adding callbacks
|
||||
void build(const JsonNode & config);
|
||||
|
||||
void addWidget(const std::string & name, std::shared_ptr<CIntObject> widget);
|
||||
|
||||
void addCallback(const std::string & callbackName, std::function<void(int)> callback);
|
||||
JsonNode variables;
|
||||
@ -73,6 +75,11 @@ protected:
|
||||
std::pair<std::string, std::string> readHintText(const JsonNode &) const;
|
||||
EShortcut readHotkey(const JsonNode &) const;
|
||||
|
||||
void loadToggleButtonCallback(std::shared_ptr<CToggleButton> button, const JsonNode & config) const;
|
||||
void loadButtonCallback(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
void loadButtonHotkey(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
void loadButtonBorderColor(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
|
||||
//basic widgets
|
||||
std::shared_ptr<CPicture> buildPicture(const JsonNode &) const;
|
||||
std::shared_ptr<CLabel> buildLabel(const JsonNode &) const;
|
||||
|
@ -81,7 +81,9 @@ enum class EShortcut
|
||||
// Adventure map screen
|
||||
ADVENTURE_GAME_OPTIONS, // 'o', Open CAdventureOptions window
|
||||
ADVENTURE_TOGGLE_GRID, // F6, Toggles map grid
|
||||
ADVENTURE_TOGGLE_SLEEP, // z,w, Toggles hero sleep status
|
||||
ADVENTURE_TOGGLE_SLEEP, // Toggles hero sleep status
|
||||
ADVENTURE_SET_HERO_ASLEEP, // Moves hero to sleep state
|
||||
ADVENTURE_SET_HERO_AWAKE, // Move hero to awake state
|
||||
ADVENTURE_MOVE_HERO, // Moves hero alongside set path
|
||||
ADVENTURE_VISIT_OBJECT, // Revisits object hero is standing on
|
||||
ADVENTURE_VIEW_SELECTED,// Open window with currently selected hero/town
|
||||
@ -94,12 +96,15 @@ enum class EShortcut
|
||||
ADVENTURE_DIG_GRAIL,
|
||||
ADVENTURE_VIEW_PUZZLE,
|
||||
ADVENTURE_VIEW_WORLD,
|
||||
ADVENTURE_VIEW_WORLD_X1,
|
||||
ADVENTURE_VIEW_WORLD_X2,
|
||||
ADVENTURE_VIEW_WORLD_X4,
|
||||
ADVENTURE_TOGGLE_MAP_LEVEL,
|
||||
ADVENTURE_KINGDOM_OVERVIEW,
|
||||
ADVENTURE_QUEST_LOG,
|
||||
ADVENTURE_CAST_SPELL,
|
||||
ADVENTURE_END_TURN,
|
||||
ADVENTURE_THIEVES_GUILD,
|
||||
ADVENTURE_EXIT_WORLD_VIEW,
|
||||
|
||||
// Move hero one tile in specified direction. Bound to cursors & numpad buttons
|
||||
ADVENTURE_MOVE_HERO_SW,
|
||||
@ -153,4 +158,3 @@ enum class EShortcut
|
||||
|
||||
AFTER_LAST
|
||||
};
|
||||
|
||||
|
@ -64,6 +64,8 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_RETURN, EShortcut::LOBBY_LOAD_GAME },
|
||||
{SDLK_KP_ENTER, EShortcut::LOBBY_LOAD_GAME },
|
||||
{SDLK_s, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_RETURN, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_KP_ENTER, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_r, EShortcut::LOBBY_RANDOM_MAP },
|
||||
{SDLK_h, EShortcut::LOBBY_HIDE_CHAT },
|
||||
{SDLK_a, EShortcut::LOBBY_ADDITIONAL_OPTIONS },
|
||||
@ -79,8 +81,8 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_TAB, EShortcut::GAME_ACTIVATE_CONSOLE },
|
||||
{SDLK_o, EShortcut::ADVENTURE_GAME_OPTIONS },
|
||||
{SDLK_F6, EShortcut::ADVENTURE_TOGGLE_GRID },
|
||||
{SDLK_z, EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{SDLK_w, EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{SDLK_z, EShortcut::ADVENTURE_SET_HERO_ASLEEP },
|
||||
{SDLK_w, EShortcut::ADVENTURE_SET_HERO_AWAKE },
|
||||
{SDLK_m, EShortcut::ADVENTURE_MOVE_HERO },
|
||||
{SDLK_SPACE, EShortcut::ADVENTURE_VISIT_OBJECT },
|
||||
{SDLK_KP_1, EShortcut::ADVENTURE_MOVE_HERO_SW },
|
||||
@ -106,11 +108,13 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_d, EShortcut::ADVENTURE_DIG_GRAIL },
|
||||
{SDLK_p, EShortcut::ADVENTURE_VIEW_PUZZLE },
|
||||
{SDLK_v, EShortcut::ADVENTURE_VIEW_WORLD },
|
||||
{SDLK_1, EShortcut::ADVENTURE_VIEW_WORLD_X1 },
|
||||
{SDLK_2, EShortcut::ADVENTURE_VIEW_WORLD_X2 },
|
||||
{SDLK_4, EShortcut::ADVENTURE_VIEW_WORLD_X4 },
|
||||
{SDLK_u, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
|
||||
{SDLK_k, EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
|
||||
{SDLK_q, EShortcut::ADVENTURE_QUEST_LOG },
|
||||
{SDLK_c, EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{SDLK_e, EShortcut::ADVENTURE_END_TURN },
|
||||
{SDLK_g, EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{SDLK_q, EShortcut::BATTLE_TOGGLE_QUEUE },
|
||||
{SDLK_c, EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
@ -218,6 +222,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"adventureGameOptions", EShortcut::ADVENTURE_GAME_OPTIONS },
|
||||
{"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID },
|
||||
{"adventureToggleSleep", EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{"adventureSetHeroAsleep", EShortcut::ADVENTURE_SET_HERO_ASLEEP },
|
||||
{"adventureSetHeroAwake", EShortcut::ADVENTURE_SET_HERO_AWAKE },
|
||||
{"adventureMoveHero", EShortcut::ADVENTURE_MOVE_HERO },
|
||||
{"adventureVisitObject", EShortcut::ADVENTURE_VISIT_OBJECT },
|
||||
{"adventureMoveHeroSW", EShortcut::ADVENTURE_MOVE_HERO_SW },
|
||||
@ -238,12 +244,15 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"adventureDigGrail", EShortcut::ADVENTURE_DIG_GRAIL },
|
||||
{"adventureViewPuzzle", EShortcut::ADVENTURE_VIEW_PUZZLE },
|
||||
{"adventureViewWorld", EShortcut::ADVENTURE_VIEW_WORLD },
|
||||
{"adventureViewWorld1", EShortcut::ADVENTURE_VIEW_WORLD_X1 },
|
||||
{"adventureViewWorld2", EShortcut::ADVENTURE_VIEW_WORLD_X2 },
|
||||
{"adventureViewWorld4", EShortcut::ADVENTURE_VIEW_WORLD_X4 },
|
||||
{"adventureToggleMapLevel", EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
|
||||
{"adventureKingdomOverview", EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
|
||||
{"adventureQuestLog", EShortcut::ADVENTURE_QUEST_LOG },
|
||||
{"adventureCastSpell", EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{"adventureEndTurn", EShortcut::ADVENTURE_END_TURN },
|
||||
{"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{"adventureExitWorldView", EShortcut::ADVENTURE_EXIT_WORLD_VIEW },
|
||||
{"battleToggleQueue", EShortcut::BATTLE_TOGGLE_QUEUE },
|
||||
{"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
{"battleSurrender", EShortcut::BATTLE_SURRENDER },
|
||||
@ -278,85 +287,3 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
return shortcutNames.at(identifier);
|
||||
return EShortcut::NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
138
client/gui/WindowHandler.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* WindowHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "WindowHandler.h"
|
||||
|
||||
#include "CGuiHandler.h"
|
||||
#include "CIntObject.h"
|
||||
#include "CursorHandler.h"
|
||||
|
||||
#include "../CMT.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
||||
void WindowHandler::popWindow(std::shared_ptr<IShowActivatable> top)
|
||||
{
|
||||
assert(windowsStack.back() == top);
|
||||
top->deactivate();
|
||||
disposed.push_back(top);
|
||||
windowsStack.pop_back();
|
||||
if(!windowsStack.empty())
|
||||
windowsStack.back()->activate();
|
||||
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
void WindowHandler::pushWindow(std::shared_ptr<IShowActivatable> newInt)
|
||||
{
|
||||
assert(newInt);
|
||||
assert(!vstd::contains(windowsStack, newInt)); // do not add same object twice
|
||||
|
||||
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
|
||||
screenBuf = screen2;
|
||||
|
||||
if(!windowsStack.empty())
|
||||
windowsStack.back()->deactivate();
|
||||
windowsStack.push_back(newInt);
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
newInt->activate();
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
void WindowHandler::popWindows(int howMany)
|
||||
{
|
||||
if(!howMany)
|
||||
return; //senseless but who knows...
|
||||
|
||||
assert(windowsStack.size() >= howMany);
|
||||
windowsStack.back()->deactivate();
|
||||
for(int i = 0; i < howMany; i++)
|
||||
{
|
||||
disposed.push_back(windowsStack.back());
|
||||
windowsStack.pop_back();
|
||||
}
|
||||
|
||||
if(!windowsStack.empty())
|
||||
{
|
||||
windowsStack.back()->activate();
|
||||
totalRedraw();
|
||||
}
|
||||
GH.fakeMouseMove();
|
||||
}
|
||||
|
||||
std::shared_ptr<IShowActivatable> WindowHandler::topWindowImpl() const
|
||||
{
|
||||
if(windowsStack.empty())
|
||||
return nullptr;
|
||||
|
||||
return windowsStack.back();
|
||||
}
|
||||
|
||||
bool WindowHandler::isTopWindow(std::shared_ptr<IShowActivatable> window) const
|
||||
{
|
||||
assert(window != nullptr);
|
||||
return !windowsStack.empty() && windowsStack.back() == window;
|
||||
}
|
||||
|
||||
bool WindowHandler::isTopWindow(IShowActivatable * window) const
|
||||
{
|
||||
assert(window != nullptr);
|
||||
return !windowsStack.empty() && windowsStack.back().get() == window;
|
||||
}
|
||||
|
||||
void WindowHandler::totalRedraw()
|
||||
{
|
||||
CSDL_Ext::fillSurface(screen2, Colors::BLACK);
|
||||
|
||||
for(auto & elem : windowsStack)
|
||||
elem->showAll(screen2);
|
||||
CSDL_Ext::blitAt(screen2, 0, 0, screen);
|
||||
}
|
||||
|
||||
void WindowHandler::simpleRedraw()
|
||||
{
|
||||
//update only top interface and draw background
|
||||
if(windowsStack.size() > 1)
|
||||
CSDL_Ext::blitAt(screen2, 0, 0, screen); //blit background
|
||||
if(!windowsStack.empty())
|
||||
windowsStack.back()->show(screen); //blit active interface/window
|
||||
}
|
||||
|
||||
void WindowHandler::onScreenResize()
|
||||
{
|
||||
for(const auto & entry : windowsStack)
|
||||
{
|
||||
auto intObject = std::dynamic_pointer_cast<CIntObject>(entry);
|
||||
|
||||
if(intObject)
|
||||
intObject->onScreenResize();
|
||||
}
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
void WindowHandler::onFrameRendered()
|
||||
{
|
||||
disposed.clear();
|
||||
}
|
||||
|
||||
size_t WindowHandler::count() const
|
||||
{
|
||||
return windowsStack.size();
|
||||
}
|
||||
|
||||
void WindowHandler::clear()
|
||||
{
|
||||
if(!windowsStack.empty())
|
||||
windowsStack.back()->deactivate();
|
||||
|
||||
windowsStack.clear();
|
||||
disposed.clear();
|
||||
}
|
97
client/gui/WindowHandler.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* WindowHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class IShowActivatable;
|
||||
|
||||
class WindowHandler
|
||||
{
|
||||
/// list of windows. front = bottom-most (background), back = top-most (foreground)
|
||||
/// (includes adventure map, window windows, all kind of active dialogs, and so on)
|
||||
std::vector<std::shared_ptr<IShowActivatable>> windowsStack;
|
||||
|
||||
/// Temporary list of recently popped windows
|
||||
std::vector<std::shared_ptr<IShowActivatable>> disposed;
|
||||
|
||||
/// returns top windows
|
||||
std::shared_ptr<IShowActivatable> topWindowImpl() const;
|
||||
|
||||
public:
|
||||
/// forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering
|
||||
void totalRedraw();
|
||||
|
||||
/// update only top windows and draw background from buffer, sets a flag, method gets called at the end of the rendering
|
||||
void simpleRedraw();
|
||||
|
||||
/// called whenever user selects different resolution, requiring to center/resize all windows
|
||||
void onScreenResize();
|
||||
|
||||
/// deactivate old top windows, activates this one and pushes to the top
|
||||
void pushWindow(std::shared_ptr<IShowActivatable> newInt);
|
||||
|
||||
/// creates window of class T and pushes it to the top
|
||||
template <typename T, typename ... Args>
|
||||
void createAndPushWindow(Args && ... args);
|
||||
|
||||
/// pops one or more windows - deactivates top, deletes and removes given number of windows, activates new front
|
||||
void popWindows(int howMany);
|
||||
|
||||
/// removes given windows from the top and activates next
|
||||
void popWindow(std::shared_ptr<IShowActivatable> top);
|
||||
|
||||
/// returns true if selected interface is on top
|
||||
bool isTopWindow(std::shared_ptr<IShowActivatable> window) const;
|
||||
bool isTopWindow(IShowActivatable * window) const;
|
||||
|
||||
/// returns top window if it matches requested class
|
||||
template <typename T>
|
||||
std::shared_ptr<T> topWindow() const;
|
||||
|
||||
/// should be called after frame has been rendered to screen
|
||||
void onFrameRendered();
|
||||
|
||||
/// returns current number of windows in the stack
|
||||
size_t count() const;
|
||||
|
||||
/// erases all currently existing windows from the stack
|
||||
void clear();
|
||||
|
||||
/// returns all existing windows of selected type
|
||||
template <typename T>
|
||||
std::vector<std::shared_ptr<T>> findWindows() const;
|
||||
};
|
||||
|
||||
template <typename T, typename ... Args>
|
||||
void WindowHandler::createAndPushWindow(Args && ... args)
|
||||
{
|
||||
auto newWindow = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
pushWindow(newWindow);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::shared_ptr<T>> WindowHandler::findWindows() const
|
||||
{
|
||||
std::vector<std::shared_ptr<T>> result;
|
||||
|
||||
for(const auto & window : windowsStack)
|
||||
{
|
||||
std::shared_ptr<T> casted = std::dynamic_pointer_cast<T>(window);
|
||||
|
||||
if (casted)
|
||||
result.push_back(casted);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> WindowHandler::topWindow() const
|
||||
{
|
||||
return std::dynamic_pointer_cast<T>(topWindowImpl());
|
||||
}
|
@ -34,6 +34,7 @@
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
@ -367,7 +368,7 @@ void CBonusSelection::goBack()
|
||||
{
|
||||
if(CSH->state != EClientState::GAMEPLAY)
|
||||
{
|
||||
GH.popInts(2);
|
||||
GH.windows().popWindows(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -397,7 +398,7 @@ void CBonusSelection::startMap()
|
||||
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap];
|
||||
if(scenario.prolog.hasPrologEpilog)
|
||||
{
|
||||
GH.pushIntT<CPrologEpilogVideo>(scenario.prolog, exitCb);
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(scenario.prolog, exitCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
|
||||
buttonOptions = std::make_shared<CButton>(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), EShortcut::LOBBY_ADDITIONAL_OPTIONS);
|
||||
};
|
||||
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 80), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
|
||||
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL);
|
||||
|
||||
switch(screenType)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
@ -71,7 +72,6 @@ CSelectionBase::CSelectionBase(ESelectionScreen type)
|
||||
: CWindowObject(BORDERED | SHADOW_DISABLED), ISelectionScreenInfo(type)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
IShowActivatable::type = BLOCK_ADV_HOTKEYS;
|
||||
pos.w = 762;
|
||||
pos.h = 584;
|
||||
if(screenType == ESelectionScreen::campaignList)
|
||||
@ -106,7 +106,7 @@ void CSelectionBase::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
{
|
||||
curTab.reset();
|
||||
}
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
InfoCard::InfoCard()
|
||||
@ -300,7 +300,7 @@ void InfoCard::setChat(bool activateChat)
|
||||
}
|
||||
|
||||
showChat = activateChat;
|
||||
GH.totalRedraw();
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
CChatBox::CChatBox(const Rect & rect)
|
||||
@ -379,7 +379,7 @@ void CFlagBox::recreate()
|
||||
void CFlagBox::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(down && SEL->getMapInfo())
|
||||
GH.pushIntT<CFlagBoxTooltipBox>(iconsTeamFlags);
|
||||
GH.windows().createAndPushWindow<CFlagBoxTooltipBox>(iconsTeamFlags);
|
||||
}
|
||||
|
||||
CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox(std::shared_ptr<CAnimation> icons)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
@ -435,7 +436,7 @@ void OptionsTab::SelectedBox::clickRight(tribool down, bool previousState)
|
||||
if(settings.hero == -2 && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
|
||||
return;
|
||||
|
||||
GH.pushIntT<CPlayerOptionTooltipBox>(*this);
|
||||
GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
@ -102,12 +103,12 @@ RandomMapTab::RandomMapTab():
|
||||
//new callbacks available only from mod
|
||||
addCallback("templateSelection", [&](int)
|
||||
{
|
||||
GH.pushIntT<TemplatesDropBox>(*this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()});
|
||||
GH.windows().createAndPushWindow<TemplatesDropBox>(*this, int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), 1 + mapGenOptions->getHasTwoLevels()});
|
||||
});
|
||||
|
||||
addCallback("teamAlignments", [&](int)
|
||||
{
|
||||
GH.pushIntT<TeamAlignmentsWidget>(*this);
|
||||
GH.windows().createAndPushWindow<TeamAlignmentsWidget>(*this);
|
||||
});
|
||||
|
||||
for(auto road : VLC->roadTypeHandler->objects)
|
||||
@ -482,8 +483,8 @@ void TemplatesDropBox::clickLeft(tribool down, bool previousState)
|
||||
// pop the interface only if the mouse is not clicking on the slider
|
||||
if (!w || !w->mouseState(MouseButton::LEFT))
|
||||
{
|
||||
assert(GH.topInt().get() == this);
|
||||
GH.popInt(GH.topInt());
|
||||
assert(GH.windows().isTopWindow(this));
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -511,8 +512,8 @@ void TemplatesDropBox::updateListItems()
|
||||
void TemplatesDropBox::setTemplate(const CRmgTemplate * tmpl)
|
||||
{
|
||||
randomMapTab.setTemplate(tmpl);
|
||||
assert(GH.topInt().get() == this);
|
||||
GH.popInt(GH.topInt());
|
||||
assert(GH.windows().isTopWindow(this));
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
|
||||
@ -547,14 +548,14 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
|
||||
randomMapTab.obtainMapGenOptions().setPlayerTeam(PlayerColor(plId), TeamID(players[plId]->getSelected()));
|
||||
}
|
||||
randomMapTab.updateMapInfoByHost();
|
||||
assert(GH.topInt().get() == this);
|
||||
GH.popInt(GH.topInt());
|
||||
assert(GH.windows().isTopWindow(this));
|
||||
GH.windows().popWindows(1);
|
||||
});
|
||||
|
||||
addCallback("cancel", [&](int)
|
||||
{
|
||||
assert(GH.topInt().get() == this);
|
||||
GH.popInt(GH.topInt());
|
||||
assert(GH.windows().isTopWindow(this));
|
||||
GH.windows().popWindows(1);
|
||||
});
|
||||
|
||||
build(config);
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/GameSettings.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/ShortcutHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
@ -177,7 +178,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
|
||||
case 1:
|
||||
return []() { GH.pushIntT<CMultiMode>(ESelectionScreen::newGame); };
|
||||
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::newGame); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::campaignList, true, nullptr, ELoadMode::NONE);
|
||||
case 3:
|
||||
@ -192,7 +193,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::SINGLE);
|
||||
case 1:
|
||||
return []() { GH.pushIntT<CMultiMode>(ESelectionScreen::loadGame); };
|
||||
return []() { GH.windows().createAndPushWindow<CMultiMode>(ESelectionScreen::loadGame); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::CAMPAIGN);
|
||||
case 3:
|
||||
@ -299,15 +300,35 @@ CMainMenu::~CMainMenu()
|
||||
GH.curInt = nullptr;
|
||||
}
|
||||
|
||||
void CMainMenu::activate()
|
||||
{
|
||||
// check if screen was resized while main menu was inactive - e.g. in gameplay mode
|
||||
if (pos.dimensions() != GH.screenDimensions())
|
||||
onScreenResize();
|
||||
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
void CMainMenu::onScreenResize()
|
||||
{
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
menu = nullptr;
|
||||
menu = std::make_shared<CMenuScreen>(CMainMenuConfig::get().getConfig()["window"]);
|
||||
|
||||
backgroundAroundMenu->pos = pos;
|
||||
}
|
||||
|
||||
void CMainMenu::update()
|
||||
{
|
||||
if(CMM != this->shared_from_this()) //don't update if you are not a main interface
|
||||
return;
|
||||
|
||||
if(GH.listInt.empty())
|
||||
if(GH.windows().count() == 0)
|
||||
{
|
||||
GH.pushInt(CMM);
|
||||
GH.pushInt(menu);
|
||||
GH.windows().pushWindow(CMM);
|
||||
GH.windows().pushWindow(menu);
|
||||
menu->switchToTab(menu->getActiveTab());
|
||||
}
|
||||
|
||||
@ -316,9 +337,9 @@ void CMainMenu::update()
|
||||
GH.handleEvents();
|
||||
|
||||
// check for null othervice crash on finishing a campaign
|
||||
// /FIXME: find out why GH.listInt is empty to begin with
|
||||
if(GH.topInt())
|
||||
GH.topInt()->show(screen);
|
||||
// /FIXME: find out why GH.windows().listInt is empty to begin with
|
||||
if(GH.windows().topWindow<CIntObject>())
|
||||
GH.windows().topWindow<CIntObject>()->show(screen);
|
||||
}
|
||||
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode)
|
||||
@ -327,7 +348,7 @@ void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vec
|
||||
CSH->screenType = screenType;
|
||||
CSH->loadMode = loadMode;
|
||||
|
||||
GH.pushIntT<CSimpleJoinScreen>(host);
|
||||
GH.windows().createAndPushWindow<CSimpleJoinScreen>(host);
|
||||
}
|
||||
|
||||
void CMainMenu::openCampaignLobby(const std::string & campaignFileName)
|
||||
@ -341,14 +362,14 @@ void CMainMenu::openCampaignLobby(std::shared_ptr<CCampaignState> campaign)
|
||||
CSH->resetStateForLobby(StartInfo::CAMPAIGN);
|
||||
CSH->screenType = ESelectionScreen::campaignList;
|
||||
CSH->campaignStateToSend = campaign;
|
||||
GH.pushIntT<CSimpleJoinScreen>();
|
||||
GH.windows().createAndPushWindow<CSimpleJoinScreen>();
|
||||
}
|
||||
|
||||
void CMainMenu::openCampaignScreen(std::string name)
|
||||
{
|
||||
if(vstd::contains(CMainMenuConfig::get().getCampaigns().Struct(), name))
|
||||
{
|
||||
GH.pushIntT<CCampaignScreen>(CMainMenuConfig::get().getCampaigns()[name]);
|
||||
GH.windows().createAndPushWindow<CCampaignScreen>(CMainMenuConfig::get().getCampaigns()[name]);
|
||||
return;
|
||||
}
|
||||
logGlobal->error("Unknown campaign set: %s", name);
|
||||
@ -393,14 +414,14 @@ void CMultiMode::hostTCP()
|
||||
{
|
||||
auto savedScreenType = screenType;
|
||||
close();
|
||||
GH.pushIntT<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, true, ELoadMode::MULTI);
|
||||
GH.windows().createAndPushWindow<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, true, ELoadMode::MULTI);
|
||||
}
|
||||
|
||||
void CMultiMode::joinTCP()
|
||||
{
|
||||
auto savedScreenType = screenType;
|
||||
close();
|
||||
GH.pushIntT<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, false, ELoadMode::MULTI);
|
||||
GH.windows().createAndPushWindow<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, false, ELoadMode::MULTI);
|
||||
}
|
||||
|
||||
void CMultiMode::onNameChange(std::string newText)
|
||||
@ -502,7 +523,7 @@ void CSimpleJoinScreen::leaveScreen()
|
||||
textTitle->setText("Closing...");
|
||||
CSH->state = EClientState::CONNECTION_CANCELLED;
|
||||
}
|
||||
else if(GH.listInt.size() && GH.listInt.front().get() == this)
|
||||
else if(GH.windows().isTopWindow(this))
|
||||
{
|
||||
close();
|
||||
}
|
||||
@ -532,7 +553,7 @@ void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port)
|
||||
else
|
||||
CSH->justConnectToServer(addr, port);
|
||||
|
||||
if(GH.listInt.size() && GH.listInt.front().get() == this)
|
||||
if(GH.windows().isTopWindow(this))
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
@ -141,6 +141,8 @@ public:
|
||||
std::shared_ptr<CMenuScreen> menu;
|
||||
|
||||
~CMainMenu();
|
||||
void activate() override;
|
||||
void onScreenResize() override;
|
||||
void update() override;
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
|
||||
static void openCampaignLobby(const std::string & campaignFileName);
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
@ -64,21 +64,22 @@ void BasicMapView::render(Canvas & target, bool fullUpdate)
|
||||
tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
|
||||
}
|
||||
|
||||
void BasicMapView::tick(uint32_t msPassed)
|
||||
{
|
||||
controller->tick(msPassed);
|
||||
}
|
||||
|
||||
void BasicMapView::show(SDL_Surface * to)
|
||||
{
|
||||
controller->updateBefore(GH.mainFPSmng->getElapsedMilliseconds());
|
||||
|
||||
Canvas target(to);
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
render(target, false);
|
||||
|
||||
controller->updateAfter(GH.mainFPSmng->getElapsedMilliseconds());
|
||||
controller->afterRender();
|
||||
}
|
||||
|
||||
void BasicMapView::showAll(SDL_Surface * to)
|
||||
{
|
||||
controller->updateBefore(0);
|
||||
|
||||
Canvas target(to);
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
render(target, true);
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
BasicMapView(const Point & offset, const Point & dimensions);
|
||||
~BasicMapView() override;
|
||||
|
||||
void tick(uint32_t msPassed) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "MapViewModel.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
@ -154,7 +154,7 @@ void MapViewActions::hover(bool on)
|
||||
{
|
||||
if(!on)
|
||||
{
|
||||
GH.statusbar->clear();
|
||||
GH.statusbar()->clear();
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,9 @@
|
||||
#include "MapViewModel.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
@ -89,7 +90,7 @@ std::shared_ptr<IMapRendererContext> MapViewController::getContext() const
|
||||
return context;
|
||||
}
|
||||
|
||||
void MapViewController::updateBefore(uint32_t timeDelta)
|
||||
void MapViewController::tick(uint32_t timeDelta)
|
||||
{
|
||||
// confirmed to match H3 for
|
||||
// - hero embarking on boat (500 ms)
|
||||
@ -158,7 +159,7 @@ void MapViewController::updateBefore(uint32_t timeDelta)
|
||||
}
|
||||
}
|
||||
|
||||
void MapViewController::updateAfter(uint32_t timeDelta)
|
||||
void MapViewController::afterRender()
|
||||
{
|
||||
if(movementContext)
|
||||
{
|
||||
@ -208,7 +209,7 @@ bool MapViewController::isEventVisible(const CGObjectInstance * obj)
|
||||
if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
|
||||
return false; // enemy move speed set to "hidden/none"
|
||||
|
||||
if(GH.topInt() != adventureInt)
|
||||
if(!GH.windows().isTopWindow(adventureInt))
|
||||
return false;
|
||||
|
||||
if(obj->isVisitable())
|
||||
@ -225,7 +226,7 @@ bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 &
|
||||
if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
|
||||
return false; // enemy move speed set to "hidden/none"
|
||||
|
||||
if(GH.topInt() != adventureInt)
|
||||
if(!GH.windows().isTopWindow(adventureInt))
|
||||
return false;
|
||||
|
||||
if(context->isVisible(obj->convertToVisitablePos(from)))
|
||||
|
@ -83,8 +83,8 @@ public:
|
||||
void setViewCenter(const int3 & position);
|
||||
void setViewCenter(const Point & position, int level);
|
||||
void setTileSize(const Point & tileSize);
|
||||
void updateBefore(uint32_t timeDelta);
|
||||
void updateAfter(uint32_t timeDelta);
|
||||
void tick(uint32_t timePassed);
|
||||
void afterRender();
|
||||
|
||||
void activateAdventureContext(uint32_t animationTime);
|
||||
void activateAdventureContext();
|
||||
|
@ -142,13 +142,13 @@ CDefFile::CDefFile(std::string Name):
|
||||
palette[6] = H3Palette[6];
|
||||
palette[7] = H3Palette[7];
|
||||
break;
|
||||
case DefType::MAP:
|
||||
case DefType::MAP_HERO:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[4] = H3Palette[4];
|
||||
//5 = owner flag, handled separately
|
||||
break;
|
||||
case DefType::MAP:
|
||||
case DefType::TERRAIN:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
|
36
client/render/IScreenHandler.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* IScreenHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class IScreenHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IScreenHandler() = default;
|
||||
|
||||
/// Updates window state after fullscreen state has been changed in settings
|
||||
virtual void onScreenResize() = 0;
|
||||
|
||||
/// De-initializes window state
|
||||
virtual void close() = 0;
|
||||
|
||||
/// Fills screen with black color, erasing any existing content
|
||||
virtual void clearScreen() = 0;
|
||||
|
||||
/// Returns list of resolutions supported by current screen
|
||||
virtual std::vector<Point> getSupportedResolutions() const = 0;
|
||||
|
||||
/// Returns <min, max> range of possible values for screen scaling percentage
|
||||
virtual std::tuple<int, int> getSupportedScalingRange() const = 0;
|
||||
};
|
@ -861,43 +861,6 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other)
|
||||
other = CSDL_Ext::fromSDL(rect);
|
||||
}
|
||||
|
||||
bool CSDL_Ext::isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest )
|
||||
{
|
||||
#if defined(VCMI_MOBILE)
|
||||
// ios can use any resolution
|
||||
// presumably, same goes for Android
|
||||
return true;
|
||||
#else
|
||||
// in fullscreen only resolutions supported by monitor can be used
|
||||
return vstd::contains(resolutions, toTest);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<Point> CSDL_Ext::getSupportedResolutions()
|
||||
{
|
||||
int displayID = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
return getSupportedResolutions(displayID);
|
||||
}
|
||||
|
||||
std::vector<Point> CSDL_Ext::getSupportedResolutions( int displayIndex)
|
||||
{
|
||||
std::vector<Point> result;
|
||||
|
||||
int modesCount = SDL_GetNumDisplayModes(displayIndex);
|
||||
|
||||
for (int i =0; i < modesCount; ++i)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
|
||||
continue;
|
||||
|
||||
Point resolution(mode.w, mode.h);
|
||||
|
||||
result.push_back(resolution);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int);
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);
|
||||
|
@ -105,11 +105,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
|
||||
void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect);
|
||||
void convertToGrayscale(SDL_Surface * surf, const Rect & rect);
|
||||
|
||||
bool isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest);
|
||||
|
||||
std::vector<Point> getSupportedResolutions();
|
||||
std::vector<Point> getSupportedResolutions(int displayIndex);
|
||||
|
||||
void setColorKey(SDL_Surface * surface, SDL_Color color);
|
||||
|
||||
///set key-color to 0,255,255
|
||||
|