1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-08 22:26:51 +02:00

convert line endings from CRLF (Windows) to LF (Linux/Unix)

Mixed line endings cause problems when exporting patches with
git-format-patch and then trying to "git am" a patch with mixed and
non-matching line endings. In such a situation git will fail to apply
the patch.

This commit runs the dos2unix tools on the remaining files with CRLF
(\r\n) line endings to convert them to line-feeds (\n) only.

Files that are Windows specific like *.vcxproj and *.props files were
not converted.

Closes: #3073
This commit is contained in:
Johannes Schauer Marin Rodrigues
2023-10-19 16:19:09 +02:00
parent d295840c64
commit a1a5bc28c2
309 changed files with 98656 additions and 98656 deletions

View File

@ -1,292 +1,292 @@
/*
* BattleAI.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 "BattleAI.h"
#include "BattleEvaluator.h"
#include "BattleExchangeVariant.h"
#include "StackWithBonuses.h"
#include "EnemyInfo.h"
#include "tbb/parallel_for.h"
#include "../../lib/CStopWatch.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/battle/BattleStateInfoForRetreat.h"
#include "../../lib/battle/CObstacleInstance.h"
#include "../../lib/CStack.h" // TODO: remove
// Eventually only IBattleInfoCallback and battle::Unit should be used,
// CUnitState should be private and CStack should be removed completely
#define LOGL(text) print(text)
#define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
CBattleAI::CBattleAI()
: side(-1),
wasWaitingForRealize(false),
wasUnlockingGs(false)
{
}
CBattleAI::~CBattleAI()
{
if(cb)
{
//Restore previous state of CB - it may be shared with the main AI (like VCAI)
cb->waitTillRealize = wasWaitingForRealize;
cb->unlockGsWhenWaiting = wasUnlockingGs;
}
}
void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB)
{
setCbc(CB);
env = ENV;
cb = CB;
playerID = *CB->getPlayerID();
wasWaitingForRealize = CB->waitTillRealize;
wasUnlockingGs = CB->unlockGsWhenWaiting;
CB->waitTillRealize = false;
CB->unlockGsWhenWaiting = false;
movesSkippedByDefense = 0;
}
void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
autobattlePreferences = autocombatPreferences;
}
BattleAction CBattleAI::useHealingTent(const BattleID & battleID, const CStack *stack)
{
auto healingTargets = cb->getBattle(battleID)->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
std::map<int, const CStack*> woundHpToStack;
for(const auto * stack : healingTargets)
{
if(auto woundHp = stack->getMaxHealth() - stack->getFirstHPleft())
woundHpToStack[woundHp] = stack;
}
if(woundHpToStack.empty())
return BattleAction::makeDefend(stack);
else
return BattleAction::makeHeal(stack, woundHpToStack.rbegin()->second); //last element of the woundHpToStack is the most wounded stack
}
void CBattleAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
static float getStrengthRatio(std::shared_ptr<CBattleInfoCallback> cb, int side)
{
auto stacks = cb->battleGetAllStacks();
auto our = 0, enemy = 0;
for(auto stack : stacks)
{
auto creature = stack->creatureId().toCreature();
if(!creature)
continue;
if(stack->unitSide() == side)
our += stack->getCount() * creature->getAIValue();
else
enemy += stack->getCount() * creature->getAIValue();
}
return enemy == 0 ? 1.0f : static_cast<float>(our) / enemy;
}
void CBattleAI::activeStack(const BattleID & battleID, const CStack * stack )
{
LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName());
auto timeElapsed = [](std::chrono::time_point<std::chrono::high_resolution_clock> start) -> uint64_t
{
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
};
BattleAction result = BattleAction::makeDefend(stack);
setCbc(cb); //TODO: make solid sure that AIs always use their callbacks (need to take care of event handlers too)
auto start = std::chrono::high_resolution_clock::now();
try
{
if(stack->creatureId() == CreatureID::CATAPULT)
{
cb->battleMakeUnitAction(battleID, useCatapult(battleID, stack));
return;
}
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->hasBonusOfType(BonusType::HEALER))
{
cb->battleMakeUnitAction(battleID, useHealingTent(battleID, stack));
return;
}
#if BATTLE_TRACE_LEVEL>=1
logAi->trace("Build evaluator and targets");
#endif
BattleEvaluator evaluator(env, cb, stack, playerID, battleID, side, getStrengthRatio(cb->getBattle(battleID), side));
result = evaluator.selectStackAction(stack);
if(!skipCastUntilNextBattle && evaluator.canCastSpell())
{
auto spelCasted = evaluator.attemptCastingSpell(stack);
if(spelCasted)
return;
skipCastUntilNextBattle = true;
}
logAi->trace("Spellcast attempt completed in %lld", timeElapsed(start));
if(auto action = considerFleeingOrSurrendering(battleID))
{
cb->battleMakeUnitAction(battleID, *action);
return;
}
}
catch(boost::thread_interrupted &)
{
throw;
}
catch(std::exception &e)
{
logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
}
if(result.actionType == EActionType::DEFEND)
{
movesSkippedByDefense++;
}
else if(result.actionType != EActionType::WAIT)
{
movesSkippedByDefense = 0;
}
logAi->trace("BattleAI decission made in %lld", timeElapsed(start));
cb->battleMakeUnitAction(battleID, result);
}
BattleAction CBattleAI::useCatapult(const BattleID & battleID, const CStack * stack)
{
BattleAction attack;
BattleHex targetHex = BattleHex::INVALID;
if(cb->getBattle(battleID)->battleGetGateState() == EGateState::CLOSED)
{
targetHex = cb->getBattle(battleID)->wallPartToBattleHex(EWallPart::GATE);
}
else
{
EWallPart wallParts[] = {
EWallPart::KEEP,
EWallPart::BOTTOM_TOWER,
EWallPart::UPPER_TOWER,
EWallPart::BELOW_GATE,
EWallPart::OVER_GATE,
EWallPart::BOTTOM_WALL,
EWallPart::UPPER_WALL
};
for(auto wallPart : wallParts)
{
auto wallState = cb->getBattle(battleID)->battleGetWallState(wallPart);
if(wallState == EWallState::REINFORCED || wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
{
targetHex = cb->getBattle(battleID)->wallPartToBattleHex(wallPart);
break;
}
}
}
if(!targetHex.isValid())
{
return BattleAction::makeDefend(stack);
}
attack.aimToHex(targetHex);
attack.actionType = EActionType::CATAPULT;
attack.side = side;
attack.stackNumber = stack->unitId();
movesSkippedByDefense = 0;
return attack;
}
void CBattleAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
LOG_TRACE(logAi);
side = Side;
skipCastUntilNextBattle = false;
}
void CBattleAI::print(const std::string &text) const
{
logAi->trace("%s Battle AI[%p]: %s", playerID.toString(), this, text);
}
std::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering(const BattleID & battleID)
{
BattleStateInfoForRetreat bs;
bs.canFlee = cb->getBattle(battleID)->battleCanFlee();
bs.canSurrender = cb->getBattle(battleID)->battleCanSurrender(playerID);
bs.ourSide = cb->getBattle(battleID)->battleGetMySide();
bs.ourHero = cb->getBattle(battleID)->battleGetMyHero();
bs.enemyHero = nullptr;
for(auto stack : cb->getBattle(battleID)->battleGetAllStacks(false))
{
if(stack->alive())
{
if(stack->unitSide() == bs.ourSide)
bs.ourStacks.push_back(stack);
else
{
bs.enemyStacks.push_back(stack);
bs.enemyHero = cb->getBattle(battleID)->battleGetOwnerHero(stack);
}
}
}
bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size();
if(!bs.canFlee && !bs.canSurrender)
{
return std::nullopt;
}
auto result = cb->makeSurrenderRetreatDecision(battleID, bs);
if(!result && bs.canFlee && bs.turnsSkippedByDefense > 30)
{
return BattleAction::makeRetreat(bs.ourSide);
}
return result;
}
/*
* BattleAI.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 "BattleAI.h"
#include "BattleEvaluator.h"
#include "BattleExchangeVariant.h"
#include "StackWithBonuses.h"
#include "EnemyInfo.h"
#include "tbb/parallel_for.h"
#include "../../lib/CStopWatch.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/battle/BattleStateInfoForRetreat.h"
#include "../../lib/battle/CObstacleInstance.h"
#include "../../lib/CStack.h" // TODO: remove
// Eventually only IBattleInfoCallback and battle::Unit should be used,
// CUnitState should be private and CStack should be removed completely
#define LOGL(text) print(text)
#define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
CBattleAI::CBattleAI()
: side(-1),
wasWaitingForRealize(false),
wasUnlockingGs(false)
{
}
CBattleAI::~CBattleAI()
{
if(cb)
{
//Restore previous state of CB - it may be shared with the main AI (like VCAI)
cb->waitTillRealize = wasWaitingForRealize;
cb->unlockGsWhenWaiting = wasUnlockingGs;
}
}
void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB)
{
setCbc(CB);
env = ENV;
cb = CB;
playerID = *CB->getPlayerID();
wasWaitingForRealize = CB->waitTillRealize;
wasUnlockingGs = CB->unlockGsWhenWaiting;
CB->waitTillRealize = false;
CB->unlockGsWhenWaiting = false;
movesSkippedByDefense = 0;
}
void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
autobattlePreferences = autocombatPreferences;
}
BattleAction CBattleAI::useHealingTent(const BattleID & battleID, const CStack *stack)
{
auto healingTargets = cb->getBattle(battleID)->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
std::map<int, const CStack*> woundHpToStack;
for(const auto * stack : healingTargets)
{
if(auto woundHp = stack->getMaxHealth() - stack->getFirstHPleft())
woundHpToStack[woundHp] = stack;
}
if(woundHpToStack.empty())
return BattleAction::makeDefend(stack);
else
return BattleAction::makeHeal(stack, woundHpToStack.rbegin()->second); //last element of the woundHpToStack is the most wounded stack
}
void CBattleAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
static float getStrengthRatio(std::shared_ptr<CBattleInfoCallback> cb, int side)
{
auto stacks = cb->battleGetAllStacks();
auto our = 0, enemy = 0;
for(auto stack : stacks)
{
auto creature = stack->creatureId().toCreature();
if(!creature)
continue;
if(stack->unitSide() == side)
our += stack->getCount() * creature->getAIValue();
else
enemy += stack->getCount() * creature->getAIValue();
}
return enemy == 0 ? 1.0f : static_cast<float>(our) / enemy;
}
void CBattleAI::activeStack(const BattleID & battleID, const CStack * stack )
{
LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName());
auto timeElapsed = [](std::chrono::time_point<std::chrono::high_resolution_clock> start) -> uint64_t
{
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
};
BattleAction result = BattleAction::makeDefend(stack);
setCbc(cb); //TODO: make solid sure that AIs always use their callbacks (need to take care of event handlers too)
auto start = std::chrono::high_resolution_clock::now();
try
{
if(stack->creatureId() == CreatureID::CATAPULT)
{
cb->battleMakeUnitAction(battleID, useCatapult(battleID, stack));
return;
}
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->hasBonusOfType(BonusType::HEALER))
{
cb->battleMakeUnitAction(battleID, useHealingTent(battleID, stack));
return;
}
#if BATTLE_TRACE_LEVEL>=1
logAi->trace("Build evaluator and targets");
#endif
BattleEvaluator evaluator(env, cb, stack, playerID, battleID, side, getStrengthRatio(cb->getBattle(battleID), side));
result = evaluator.selectStackAction(stack);
if(!skipCastUntilNextBattle && evaluator.canCastSpell())
{
auto spelCasted = evaluator.attemptCastingSpell(stack);
if(spelCasted)
return;
skipCastUntilNextBattle = true;
}
logAi->trace("Spellcast attempt completed in %lld", timeElapsed(start));
if(auto action = considerFleeingOrSurrendering(battleID))
{
cb->battleMakeUnitAction(battleID, *action);
return;
}
}
catch(boost::thread_interrupted &)
{
throw;
}
catch(std::exception &e)
{
logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
}
if(result.actionType == EActionType::DEFEND)
{
movesSkippedByDefense++;
}
else if(result.actionType != EActionType::WAIT)
{
movesSkippedByDefense = 0;
}
logAi->trace("BattleAI decission made in %lld", timeElapsed(start));
cb->battleMakeUnitAction(battleID, result);
}
BattleAction CBattleAI::useCatapult(const BattleID & battleID, const CStack * stack)
{
BattleAction attack;
BattleHex targetHex = BattleHex::INVALID;
if(cb->getBattle(battleID)->battleGetGateState() == EGateState::CLOSED)
{
targetHex = cb->getBattle(battleID)->wallPartToBattleHex(EWallPart::GATE);
}
else
{
EWallPart wallParts[] = {
EWallPart::KEEP,
EWallPart::BOTTOM_TOWER,
EWallPart::UPPER_TOWER,
EWallPart::BELOW_GATE,
EWallPart::OVER_GATE,
EWallPart::BOTTOM_WALL,
EWallPart::UPPER_WALL
};
for(auto wallPart : wallParts)
{
auto wallState = cb->getBattle(battleID)->battleGetWallState(wallPart);
if(wallState == EWallState::REINFORCED || wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
{
targetHex = cb->getBattle(battleID)->wallPartToBattleHex(wallPart);
break;
}
}
}
if(!targetHex.isValid())
{
return BattleAction::makeDefend(stack);
}
attack.aimToHex(targetHex);
attack.actionType = EActionType::CATAPULT;
attack.side = side;
attack.stackNumber = stack->unitId();
movesSkippedByDefense = 0;
return attack;
}
void CBattleAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
LOG_TRACE(logAi);
side = Side;
skipCastUntilNextBattle = false;
}
void CBattleAI::print(const std::string &text) const
{
logAi->trace("%s Battle AI[%p]: %s", playerID.toString(), this, text);
}
std::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering(const BattleID & battleID)
{
BattleStateInfoForRetreat bs;
bs.canFlee = cb->getBattle(battleID)->battleCanFlee();
bs.canSurrender = cb->getBattle(battleID)->battleCanSurrender(playerID);
bs.ourSide = cb->getBattle(battleID)->battleGetMySide();
bs.ourHero = cb->getBattle(battleID)->battleGetMyHero();
bs.enemyHero = nullptr;
for(auto stack : cb->getBattle(battleID)->battleGetAllStacks(false))
{
if(stack->alive())
{
if(stack->unitSide() == bs.ourSide)
bs.ourStacks.push_back(stack);
else
{
bs.enemyStacks.push_back(stack);
bs.enemyHero = cb->getBattle(battleID)->battleGetOwnerHero(stack);
}
}
}
bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size();
if(!bs.canFlee && !bs.canSurrender)
{
return std::nullopt;
}
auto result = cb->makeSurrenderRetreatDecision(battleID, bs);
if(!result && bs.canFlee && bs.turnsSkippedByDefense > 30)
{
return BattleAction::makeRetreat(bs.ourSide);
}
return result;
}

View File

@ -1,11 +1,11 @@
/*
* StdInc.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
*
*/
// Creates the precompiled header
#include "StdInc.h"
/*
* StdInc.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
*
*/
// Creates the precompiled header
#include "StdInc.h"

View File

@ -1,17 +1,17 @@
/*
* StdInc.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 "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE
/*
* StdInc.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 "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE

View File

@ -1,33 +1,33 @@
/*
* main.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 "../../lib/AI_Base.h"
#include "BattleAI.h"
#ifdef __GNUC__
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
static const char *g_cszAiName = "Battle AI";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
}
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
{
out = std::make_shared<CBattleAI>();
}
/*
* main.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 "../../lib/AI_Base.h"
#include "BattleAI.h"
#ifdef __GNUC__
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
static const char *g_cszAiName = "Battle AI";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
}
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
{
out = std::make_shared<CBattleAI>();
}

View File

@ -1,82 +1,82 @@
/*
* CEmptyAI.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 "CEmptyAI.h"
#include "../../lib/CRandomGenerator.h"
#include "../../lib/CStack.h"
#include "../../lib/battle/BattleAction.h"
void CEmptyAI::saveGame(BinarySerializer & h, const int version)
{
}
void CEmptyAI::loadGame(BinaryDeserializer & h, const int version)
{
}
void CEmptyAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB)
{
cb = CB;
env = ENV;
human=false;
playerID = *cb->getPlayerID();
}
void CEmptyAI::yourTurn(QueryID queryID)
{
cb->selectionMade(0, queryID);
cb->endTurn();
}
void CEmptyAI::activeStack(const BattleID & battleID, const CStack * stack)
{
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
}
void CEmptyAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
void CEmptyAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)
{
cb->selectionMade(CRandomGenerator::getDefault().nextInt((int)skills.size() - 1), queryID);
}
void CEmptyAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
{
cb->selectionMade(CRandomGenerator::getDefault().nextInt((int)skills.size() - 1), queryID);
}
void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel)
{
cb->selectionMade(0, askID);
}
void CEmptyAI::showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
{
cb->selectionMade(0, askID);
}
void CEmptyAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
{
cb->selectionMade(0, queryID);
}
void CEmptyAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects)
{
cb->selectionMade(0, askID);
}
std::optional<BattleAction> CEmptyAI::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
{
return std::nullopt;
}
/*
* CEmptyAI.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 "CEmptyAI.h"
#include "../../lib/CRandomGenerator.h"
#include "../../lib/CStack.h"
#include "../../lib/battle/BattleAction.h"
void CEmptyAI::saveGame(BinarySerializer & h, const int version)
{
}
void CEmptyAI::loadGame(BinaryDeserializer & h, const int version)
{
}
void CEmptyAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB)
{
cb = CB;
env = ENV;
human=false;
playerID = *cb->getPlayerID();
}
void CEmptyAI::yourTurn(QueryID queryID)
{
cb->selectionMade(0, queryID);
cb->endTurn();
}
void CEmptyAI::activeStack(const BattleID & battleID, const CStack * stack)
{
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
}
void CEmptyAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
void CEmptyAI::heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)
{
cb->selectionMade(CRandomGenerator::getDefault().nextInt((int)skills.size() - 1), queryID);
}
void CEmptyAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
{
cb->selectionMade(CRandomGenerator::getDefault().nextInt((int)skills.size() - 1), queryID);
}
void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel)
{
cb->selectionMade(0, askID);
}
void CEmptyAI::showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
{
cb->selectionMade(0, askID);
}
void CEmptyAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
{
cb->selectionMade(0, queryID);
}
void CEmptyAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects)
{
cb->selectionMade(0, askID);
}
std::optional<BattleAction> CEmptyAI::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
{
return std::nullopt;
}

View File

@ -1,38 +1,38 @@
/*
* CEmptyAI.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 "../../lib/AI_Base.h"
#include "../../CCallback.h"
struct HeroMoveDetails;
class CEmptyAI : public CGlobalAI
{
std::shared_ptr<CCallback> cb;
public:
virtual void saveGame(BinarySerializer & h, const int version) override;
virtual void loadGame(BinaryDeserializer & h, const int version) override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void yourTurn(QueryID queryID) override;
void yourTacticPhase(const BattleID & battleID, int distance) override;
void activeStack(const BattleID & battleID, const CStack * stack) override;
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
};
#define NAME "EmptyAI 0.1"
/*
* CEmptyAI.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 "../../lib/AI_Base.h"
#include "../../CCallback.h"
struct HeroMoveDetails;
class CEmptyAI : public CGlobalAI
{
std::shared_ptr<CCallback> cb;
public:
virtual void saveGame(BinarySerializer & h, const int version) override;
virtual void loadGame(BinaryDeserializer & h, const int version) override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void yourTurn(QueryID queryID) override;
void yourTacticPhase(const BattleID & battleID, int distance) override;
void activeStack(const BattleID & battleID, const CStack * stack) override;
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
};
#define NAME "EmptyAI 0.1"

View File

@ -1,2 +1,2 @@
// Creates the precompiled header
// Creates the precompiled header
#include "StdInc.h"

View File

@ -1,9 +1,9 @@
#pragma once
#include "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE
#pragma once
#include "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE

View File

@ -1,28 +1,28 @@
/*
* main.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 "CEmptyAI.h"
std::set<CGlobalAI*> ais;
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy(name,NAME);
}
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
{
out = std::make_shared<CEmptyAI>();
}
/*
* main.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 "CEmptyAI.h"
std::set<CGlobalAI*> ais;
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy(name,NAME);
}
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
{
out = std::make_shared<CEmptyAI>();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
// Creates the precompiled header
// Creates the precompiled header
#include "StdInc.h"

View File

@ -1,9 +1,9 @@
#pragma once
#include "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE
#pragma once
#include "../../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE

View File

@ -1,333 +1,333 @@
/*
* StupidAI.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 "../../lib/AI_Base.h"
#include "StupidAI.h"
#include "../../lib/CStack.h"
#include "../../CCallback.h"
#include "../../lib/CCreatureHandler.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/battle/BattleInfo.h"
static std::shared_ptr<CBattleCallback> cbc;
CStupidAI::CStupidAI()
: side(-1)
, wasWaitingForRealize(false)
, wasUnlockingGs(false)
{
print("created");
}
CStupidAI::~CStupidAI()
{
print("destroyed");
if(cb)
{
//Restore previous state of CB - it may be shared with the main AI (like VCAI)
cb->waitTillRealize = wasWaitingForRealize;
cb->unlockGsWhenWaiting = wasUnlockingGs;
}
}
void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB)
{
print("init called, saving ptr to IBattleCallback");
env = ENV;
cbc = cb = CB;
wasWaitingForRealize = CB->waitTillRealize;
wasUnlockingGs = CB->unlockGsWhenWaiting;
CB->waitTillRealize = false;
CB->unlockGsWhenWaiting = false;
}
void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
}
void CStupidAI::actionFinished(const BattleID & battleID, const BattleAction &action)
{
print("actionFinished called");
}
void CStupidAI::actionStarted(const BattleID & battleID, const BattleAction &action)
{
print("actionStarted called");
}
class EnemyInfo
{
public:
const CStack * s;
int adi, adr;
std::vector<BattleHex> attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{}
void calcDmg(const BattleID & battleID, const CStack * ourStack)
{
// FIXME: provide distance info for Jousting bonus
DamageEstimation retal;
DamageEstimation dmg = cbc->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
adi = static_cast<int>((dmg.damage.min + dmg.damage.max) / 2);
adr = static_cast<int>((retal.damage.min + retal.damage.max) / 2);
}
bool operator==(const EnemyInfo& ei) const
{
return s == ei.s;
}
};
bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
{
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
}
static bool willSecondHexBlockMoreEnemyShooters(const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
{
int shooters[2] = {0}; //count of shooters on hexes
for(int i = 0; i < 2; i++)
{
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
if(const auto * s = cbc->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(s->isShooter())
shooters[i]++;
}
return shooters[0] < shooters[1];
}
void CStupidAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
{
//boost::this_thread::sleep_for(boost::chrono::seconds(2));
print("activeStack called for " + stack->nodeName());
ReachabilityInfo dists = cb->getBattle(battleID)->getReachability(stack);
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
if(stack->creatureId() == CreatureID::CATAPULT)
{
BattleAction attack;
static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
auto seletectedHex = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
attack.aimToHex(seletectedHex);
attack.actionType = EActionType::CATAPULT;
attack.side = side;
attack.stackNumber = stack->unitId();
cb->battleMakeUnitAction(battleID, attack);
return;
}
else if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON))
{
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
return;
}
for (const CStack *s : cb->getBattle(battleID)->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY))
{
if(cb->getBattle(battleID)->battleCanShoot(stack, s->getPosition()))
{
enemiesShootable.push_back(s);
}
else
{
std::vector<BattleHex> avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
for (BattleHex hex : avHexes)
{
if(CStack::isMeleeAttackPossible(stack, s, hex))
{
std::vector<EnemyInfo>::iterator i = std::find(enemiesReachable.begin(), enemiesReachable.end(), s);
if(i == enemiesReachable.end())
{
enemiesReachable.push_back(s);
i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
}
i->attackFrom.push_back(hex);
}
}
if(!vstd::contains(enemiesReachable, s) && s->getPosition().isValid())
enemiesUnreachable.push_back(s);
}
}
for ( auto & enemy : enemiesReachable )
enemy.calcDmg(battleID, stack);
for ( auto & enemy : enemiesShootable )
enemy.calcDmg(battleID, stack);
if(enemiesShootable.size())
{
const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
cb->battleMakeUnitAction(battleID, BattleAction::makeShotAttack(stack, ei.s));
return;
}
else if(enemiesReachable.size())
{
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(battleID, a, b);});
cb->battleMakeUnitAction(battleID, BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), targetHex));
return;
}
else if(enemiesUnreachable.size()) //due to #955 - a buggy battle may occur when there are no enemies
{
auto closestEnemy = vstd::minElementByFun(enemiesUnreachable, [&](const EnemyInfo & ei) -> int
{
return dists.distToNearestNeighbour(stack, ei.s);
});
if(dists.distToNearestNeighbour(stack, closestEnemy->s) < GameConstants::BFIELD_SIZE)
{
cb->battleMakeUnitAction(battleID, goTowards(battleID, stack, closestEnemy->s->getAttackableHexes(stack)));
return;
}
}
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
return;
}
void CStupidAI::battleAttack(const BattleID & battleID, const BattleAttack *ba)
{
print("battleAttack called");
}
void CStupidAI::battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged)
{
print("battleStacksAttacked called");
}
void CStupidAI::battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID)
{
print("battleEnd called");
}
// void CStupidAI::battleResultsApplied()
// {
// print("battleResultsApplied called");
// }
void CStupidAI::battleNewRoundFirst(const BattleID & battleID)
{
print("battleNewRoundFirst called");
}
void CStupidAI::battleNewRound(const BattleID & battleID)
{
print("battleNewRound called");
}
void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport)
{
print("battleStackMoved called");
}
void CStupidAI::battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc)
{
print("battleSpellCast called");
}
void CStupidAI::battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse)
{
print("battleStacksEffectsSet called");
}
void CStupidAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
print("battleStart called");
side = Side;
}
void CStupidAI::battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca)
{
print("battleCatapultAttacked called");
}
void CStupidAI::print(const std::string &text) const
{
logAi->trace("CStupidAI [%p]: %s", this, text);
}
BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
{
return BattleAction::makeDefend(stack);
}
std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{
return reachability.distances[h1] < reachability.distances[h2];
});
for(auto hex : hexes)
{
if(vstd::contains(avHexes, hex))
return BattleAction::makeMove(stack, hex);
if(stack->coversPos(hex))
{
logAi->warn("Warning: already standing on neighbouring tile!");
//We shouldn't even be here...
return BattleAction::makeDefend(stack);
}
}
BattleHex bestNeighbor = hexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
{
return BattleAction::makeDefend(stack);
}
if(stack->hasBonusOfType(BonusType::FLYING))
{
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
// We just check all available hexes and pick the one closest to the target.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
{
return BattleHex::getDistance(bestNeighbor, hex);
});
return BattleAction::makeMove(stack, *nearestAvailableHex);
}
else
{
BattleHex currentDest = bestNeighbor;
while(1)
{
if(!currentDest.isValid())
{
logAi->error("CBattleAI::goTowards: internal error");
return BattleAction::makeDefend(stack);
}
if(vstd::contains(avHexes, currentDest))
return BattleAction::makeMove(stack, currentDest);
currentDest = reachability.predecessors[currentDest];
}
}
}
/*
* StupidAI.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 "../../lib/AI_Base.h"
#include "StupidAI.h"
#include "../../lib/CStack.h"
#include "../../CCallback.h"
#include "../../lib/CCreatureHandler.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/battle/BattleInfo.h"
static std::shared_ptr<CBattleCallback> cbc;
CStupidAI::CStupidAI()
: side(-1)
, wasWaitingForRealize(false)
, wasUnlockingGs(false)
{
print("created");
}
CStupidAI::~CStupidAI()
{
print("destroyed");
if(cb)
{
//Restore previous state of CB - it may be shared with the main AI (like VCAI)
cb->waitTillRealize = wasWaitingForRealize;
cb->unlockGsWhenWaiting = wasUnlockingGs;
}
}
void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB)
{
print("init called, saving ptr to IBattleCallback");
env = ENV;
cbc = cb = CB;
wasWaitingForRealize = CB->waitTillRealize;
wasUnlockingGs = CB->unlockGsWhenWaiting;
CB->waitTillRealize = false;
CB->unlockGsWhenWaiting = false;
}
void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
}
void CStupidAI::actionFinished(const BattleID & battleID, const BattleAction &action)
{
print("actionFinished called");
}
void CStupidAI::actionStarted(const BattleID & battleID, const BattleAction &action)
{
print("actionStarted called");
}
class EnemyInfo
{
public:
const CStack * s;
int adi, adr;
std::vector<BattleHex> attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{}
void calcDmg(const BattleID & battleID, const CStack * ourStack)
{
// FIXME: provide distance info for Jousting bonus
DamageEstimation retal;
DamageEstimation dmg = cbc->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
adi = static_cast<int>((dmg.damage.min + dmg.damage.max) / 2);
adr = static_cast<int>((retal.damage.min + retal.damage.max) / 2);
}
bool operator==(const EnemyInfo& ei) const
{
return s == ei.s;
}
};
bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
{
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
}
static bool willSecondHexBlockMoreEnemyShooters(const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
{
int shooters[2] = {0}; //count of shooters on hexes
for(int i = 0; i < 2; i++)
{
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
if(const auto * s = cbc->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(s->isShooter())
shooters[i]++;
}
return shooters[0] < shooters[1];
}
void CStupidAI::yourTacticPhase(const BattleID & battleID, int distance)
{
cb->battleMakeTacticAction(battleID, BattleAction::makeEndOFTacticPhase(cb->getBattle(battleID)->battleGetTacticsSide()));
}
void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
{
//boost::this_thread::sleep_for(boost::chrono::seconds(2));
print("activeStack called for " + stack->nodeName());
ReachabilityInfo dists = cb->getBattle(battleID)->getReachability(stack);
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
if(stack->creatureId() == CreatureID::CATAPULT)
{
BattleAction attack;
static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
auto seletectedHex = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
attack.aimToHex(seletectedHex);
attack.actionType = EActionType::CATAPULT;
attack.side = side;
attack.stackNumber = stack->unitId();
cb->battleMakeUnitAction(battleID, attack);
return;
}
else if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON))
{
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
return;
}
for (const CStack *s : cb->getBattle(battleID)->battleGetStacks(CBattleInfoEssentials::ONLY_ENEMY))
{
if(cb->getBattle(battleID)->battleCanShoot(stack, s->getPosition()))
{
enemiesShootable.push_back(s);
}
else
{
std::vector<BattleHex> avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
for (BattleHex hex : avHexes)
{
if(CStack::isMeleeAttackPossible(stack, s, hex))
{
std::vector<EnemyInfo>::iterator i = std::find(enemiesReachable.begin(), enemiesReachable.end(), s);
if(i == enemiesReachable.end())
{
enemiesReachable.push_back(s);
i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
}
i->attackFrom.push_back(hex);
}
}
if(!vstd::contains(enemiesReachable, s) && s->getPosition().isValid())
enemiesUnreachable.push_back(s);
}
}
for ( auto & enemy : enemiesReachable )
enemy.calcDmg(battleID, stack);
for ( auto & enemy : enemiesShootable )
enemy.calcDmg(battleID, stack);
if(enemiesShootable.size())
{
const EnemyInfo &ei= *std::max_element(enemiesShootable.begin(), enemiesShootable.end(), isMoreProfitable);
cb->battleMakeUnitAction(battleID, BattleAction::makeShotAttack(stack, ei.s));
return;
}
else if(enemiesReachable.size())
{
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(battleID, a, b);});
cb->battleMakeUnitAction(battleID, BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), targetHex));
return;
}
else if(enemiesUnreachable.size()) //due to #955 - a buggy battle may occur when there are no enemies
{
auto closestEnemy = vstd::minElementByFun(enemiesUnreachable, [&](const EnemyInfo & ei) -> int
{
return dists.distToNearestNeighbour(stack, ei.s);
});
if(dists.distToNearestNeighbour(stack, closestEnemy->s) < GameConstants::BFIELD_SIZE)
{
cb->battleMakeUnitAction(battleID, goTowards(battleID, stack, closestEnemy->s->getAttackableHexes(stack)));
return;
}
}
cb->battleMakeUnitAction(battleID, BattleAction::makeDefend(stack));
return;
}
void CStupidAI::battleAttack(const BattleID & battleID, const BattleAttack *ba)
{
print("battleAttack called");
}
void CStupidAI::battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged)
{
print("battleStacksAttacked called");
}
void CStupidAI::battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID)
{
print("battleEnd called");
}
// void CStupidAI::battleResultsApplied()
// {
// print("battleResultsApplied called");
// }
void CStupidAI::battleNewRoundFirst(const BattleID & battleID)
{
print("battleNewRoundFirst called");
}
void CStupidAI::battleNewRound(const BattleID & battleID)
{
print("battleNewRound called");
}
void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport)
{
print("battleStackMoved called");
}
void CStupidAI::battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc)
{
print("battleSpellCast called");
}
void CStupidAI::battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse)
{
print("battleStacksEffectsSet called");
}
void CStupidAI::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
print("battleStart called");
side = Side;
}
void CStupidAI::battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca)
{
print("battleCatapultAttacked called");
}
void CStupidAI::print(const std::string &text) const
{
logAi->trace("CStupidAI [%p]: %s", this, text);
}
BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
{
return BattleAction::makeDefend(stack);
}
std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{
return reachability.distances[h1] < reachability.distances[h2];
});
for(auto hex : hexes)
{
if(vstd::contains(avHexes, hex))
return BattleAction::makeMove(stack, hex);
if(stack->coversPos(hex))
{
logAi->warn("Warning: already standing on neighbouring tile!");
//We shouldn't even be here...
return BattleAction::makeDefend(stack);
}
}
BattleHex bestNeighbor = hexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
{
return BattleAction::makeDefend(stack);
}
if(stack->hasBonusOfType(BonusType::FLYING))
{
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
// We just check all available hexes and pick the one closest to the target.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
{
return BattleHex::getDistance(bestNeighbor, hex);
});
return BattleAction::makeMove(stack, *nearestAvailableHex);
}
else
{
BattleHex currentDest = bestNeighbor;
while(1)
{
if(!currentDest.isValid())
{
logAi->error("CBattleAI::goTowards: internal error");
return BattleAction::makeDefend(stack);
}
if(vstd::contains(avHexes, currentDest))
return BattleAction::makeMove(stack, currentDest);
currentDest = reachability.predecessors[currentDest];
}
}
}

View File

@ -1,56 +1,56 @@
/*
* StupidAI.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 "../../lib/battle/BattleHex.h"
#include "../../lib/battle/ReachabilityInfo.h"
#include "../../lib/CGameInterface.h"
class EnemyInfo;
class CStupidAI : public CBattleGameInterface
{
int side;
std::shared_ptr<CBattleCallback> cb;
std::shared_ptr<Environment> env;
bool wasWaitingForRealize;
bool wasUnlockingGs;
void print(const std::string &text) const;
public:
CStupidAI();
~CStupidAI();
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override;
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override;
void actionFinished(const BattleID & battleID, const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
void actionStarted(const BattleID & battleID, const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack
void yourTacticPhase(const BattleID & battleID, int distance) override;
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //called when stack is performing attack
void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;
//void battleResultsApplied() override; //called when all effects of last battle are applied
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied;
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack
private:
BattleAction goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const;
};
/*
* StupidAI.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 "../../lib/battle/BattleHex.h"
#include "../../lib/battle/ReachabilityInfo.h"
#include "../../lib/CGameInterface.h"
class EnemyInfo;
class CStupidAI : public CBattleGameInterface
{
int side;
std::shared_ptr<CBattleCallback> cb;
std::shared_ptr<Environment> env;
bool wasWaitingForRealize;
bool wasUnlockingGs;
void print(const std::string &text) const;
public:
CStupidAI();
~CStupidAI();
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override;
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override;
void actionFinished(const BattleID & battleID, const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
void actionStarted(const BattleID & battleID, const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack
void yourTacticPhase(const BattleID & battleID, int distance) override;
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //called when stack is performing attack
void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;
//void battleResultsApplied() override; //called when all effects of last battle are applied
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied;
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack
private:
BattleAction goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const;
};

View File

@ -1,34 +1,34 @@
/*
* main.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 "../../lib/AI_Base.h"
#include "StupidAI.h"
#ifdef __GNUC__
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
static const char *g_cszAiName = "Stupid AI 0.1";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
}
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
{
out = std::make_shared<CStupidAI>();
}
/*
* main.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 "../../lib/AI_Base.h"
#include "StupidAI.h"
#ifdef __GNUC__
#define strcpy_s(a, b, c) strncpy(a, c, b)
#endif
static const char *g_cszAiName = "Stupid AI 0.1";
extern "C" DLL_EXPORT int GetGlobalAiVersion()
{
return AI_INTERFACE_VER;
}
extern "C" DLL_EXPORT void GetAiName(char* name)
{
strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
}
extern "C" DLL_EXPORT void GetNewBattleAI(std::shared_ptr<CBattleGameInterface> &out)
{
out = std::make_shared<CStupidAI>();
}

View File

@ -1,259 +1,259 @@
/*
* AIUtility.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 "AIUtility.h"
#include "VCAI.h"
#include "FuzzyHelper.h"
#include "Goals/Goals.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/mapObjects/CBank.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/CQuest.h"
#include "../../lib/mapping/CMapDefines.h"
extern FuzzyHelper * fh;
const CGObjectInstance * ObjectIdRef::operator->() const
{
return cb->getObj(id, false);
}
ObjectIdRef::operator const CGObjectInstance *() const
{
return cb->getObj(id, false);
}
ObjectIdRef::operator bool() const
{
return cb->getObj(id, false);
}
ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
: id(_id)
{
}
ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
: id(obj->id)
{
}
bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
{
return id < rhs.id;
}
HeroPtr::HeroPtr(const CGHeroInstance * H)
{
if(!H)
{
//init from nullptr should equal to default init
*this = HeroPtr();
return;
}
h = H;
name = h->getNameTranslated();
hid = H->id;
// infosCount[ai->playerID][hid]++;
}
HeroPtr::HeroPtr()
{
h = nullptr;
hid = ObjectInstanceID();
}
HeroPtr::~HeroPtr()
{
// if(hid >= 0)
// infosCount[ai->playerID][hid]--;
}
bool HeroPtr::operator<(const HeroPtr & rhs) const
{
return hid < rhs.hid;
}
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
{
//TODO? check if these all assertions every time we get info about hero affect efficiency
//
//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
assert(doWeExpectNull || h);
if(h)
{
auto obj = cb->getObj(hid);
const bool owned = obj && obj->tempOwner == ai->playerID;
if(doWeExpectNull && !owned)
{
return nullptr;
}
else
{
assert(obj);
assert(owned);
}
}
return h;
}
const CGHeroInstance * HeroPtr::operator->() const
{
return get();
}
bool HeroPtr::validAndSet() const
{
return get(true);
}
const CGHeroInstance * HeroPtr::operator*() const
{
return get();
}
bool HeroPtr::operator==(const HeroPtr & rhs) const
{
return h == rhs.get(true);
}
bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
{
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
return ln->getCost() < rn->getCost();
}
bool isSafeToVisit(HeroPtr h, crint3 tile)
{
return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
}
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
{
const ui64 heroStrength = h->getTotalStrength();
if(dangerStrength)
{
return heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength;
}
return true; //there's no danger
}
bool isObjectRemovable(const CGObjectInstance * obj)
{
//FIXME: move logic to object property!
switch (obj->ID)
{
case Obj::MONSTER:
case Obj::RESOURCE:
case Obj::CAMPFIRE:
case Obj::TREASURE_CHEST:
case Obj::ARTIFACT:
case Obj::BORDERGUARD:
case Obj::FLOTSAM:
case Obj::PANDORAS_BOX:
case Obj::OCEAN_BOTTLE:
case Obj::SEA_CHEST:
case Obj::SHIPWRECK_SURVIVOR:
case Obj::SPELL_SCROLL:
return true;
break;
default:
return false;
break;
}
}
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
{
// TODO: Such information should be provided by pathfinder
// Tile must be free or with unoccupied boat
if(!t->blocked)
{
return true;
}
else if(!fromWater) // do not try to board when in water sector
{
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
return true;
}
return false;
}
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
{
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
return false;
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
return !gate->passableFor(ai->playerID);
}
bool isBlockVisitObj(const int3 & pos)
{
if(auto obj = cb->getTopObj(pos))
{
if(obj->isBlockedVisitable()) //we can't stand on that object
return true;
}
return false;
}
creInfo infoFromDC(const dwellingContent & dc)
{
creInfo ci;
ci.count = dc.first;
ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
if (ci.creID != CreatureID::NONE)
{
ci.cre = VLC->creatures()->getById(ci.creID);
ci.level = ci.cre->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
}
else
{
ci.cre = nullptr;
ci.level = 0;
}
return ci;
}
bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
{
return h1->getTotalStrength() < h2->getTotalStrength();
}
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
{
return a1->getArmyStrength() < a2->getArmyStrength();
}
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
{
auto art1 = a1->artType;
auto art2 = a2->artType;
if(art1->getPrice() == art2->getPrice())
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
else
return art1->getPrice() > art2->getPrice();
}
/*
* AIUtility.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 "AIUtility.h"
#include "VCAI.h"
#include "FuzzyHelper.h"
#include "Goals/Goals.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/mapObjects/CBank.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/CQuest.h"
#include "../../lib/mapping/CMapDefines.h"
extern FuzzyHelper * fh;
const CGObjectInstance * ObjectIdRef::operator->() const
{
return cb->getObj(id, false);
}
ObjectIdRef::operator const CGObjectInstance *() const
{
return cb->getObj(id, false);
}
ObjectIdRef::operator bool() const
{
return cb->getObj(id, false);
}
ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
: id(_id)
{
}
ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
: id(obj->id)
{
}
bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
{
return id < rhs.id;
}
HeroPtr::HeroPtr(const CGHeroInstance * H)
{
if(!H)
{
//init from nullptr should equal to default init
*this = HeroPtr();
return;
}
h = H;
name = h->getNameTranslated();
hid = H->id;
// infosCount[ai->playerID][hid]++;
}
HeroPtr::HeroPtr()
{
h = nullptr;
hid = ObjectInstanceID();
}
HeroPtr::~HeroPtr()
{
// if(hid >= 0)
// infosCount[ai->playerID][hid]--;
}
bool HeroPtr::operator<(const HeroPtr & rhs) const
{
return hid < rhs.hid;
}
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
{
//TODO? check if these all assertions every time we get info about hero affect efficiency
//
//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
assert(doWeExpectNull || h);
if(h)
{
auto obj = cb->getObj(hid);
const bool owned = obj && obj->tempOwner == ai->playerID;
if(doWeExpectNull && !owned)
{
return nullptr;
}
else
{
assert(obj);
assert(owned);
}
}
return h;
}
const CGHeroInstance * HeroPtr::operator->() const
{
return get();
}
bool HeroPtr::validAndSet() const
{
return get(true);
}
const CGHeroInstance * HeroPtr::operator*() const
{
return get();
}
bool HeroPtr::operator==(const HeroPtr & rhs) const
{
return h == rhs.get(true);
}
bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
{
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
return ln->getCost() < rn->getCost();
}
bool isSafeToVisit(HeroPtr h, crint3 tile)
{
return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
}
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
{
const ui64 heroStrength = h->getTotalStrength();
if(dangerStrength)
{
return heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength;
}
return true; //there's no danger
}
bool isObjectRemovable(const CGObjectInstance * obj)
{
//FIXME: move logic to object property!
switch (obj->ID)
{
case Obj::MONSTER:
case Obj::RESOURCE:
case Obj::CAMPFIRE:
case Obj::TREASURE_CHEST:
case Obj::ARTIFACT:
case Obj::BORDERGUARD:
case Obj::FLOTSAM:
case Obj::PANDORAS_BOX:
case Obj::OCEAN_BOTTLE:
case Obj::SEA_CHEST:
case Obj::SHIPWRECK_SURVIVOR:
case Obj::SPELL_SCROLL:
return true;
break;
default:
return false;
break;
}
}
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
{
// TODO: Such information should be provided by pathfinder
// Tile must be free or with unoccupied boat
if(!t->blocked)
{
return true;
}
else if(!fromWater) // do not try to board when in water sector
{
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
return true;
}
return false;
}
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
{
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
return false;
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
return !gate->passableFor(ai->playerID);
}
bool isBlockVisitObj(const int3 & pos)
{
if(auto obj = cb->getTopObj(pos))
{
if(obj->isBlockedVisitable()) //we can't stand on that object
return true;
}
return false;
}
creInfo infoFromDC(const dwellingContent & dc)
{
creInfo ci;
ci.count = dc.first;
ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
if (ci.creID != CreatureID::NONE)
{
ci.cre = VLC->creatures()->getById(ci.creID);
ci.level = ci.cre->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
}
else
{
ci.cre = nullptr;
ci.level = 0;
}
return ci;
}
bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
{
return h1->getTotalStrength() < h2->getTotalStrength();
}
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
{
return a1->getArmyStrength() < a2->getArmyStrength();
}
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
{
auto art1 = a1->artType;
auto art2 = a2->artType;
if(art1->getPrice() == art2->getPrice())
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
else
return art1->getPrice() > art2->getPrice();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,407 +1,407 @@
/*
* VCAI.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 "AIUtility.h"
#include "Goals/AbstractGoal.h"
#include "../../lib/AI_Base.h"
#include "../../CCallback.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/CBuildingHandler.h"
#include "../../lib/CCreatureHandler.h"
#include "../../lib/CTownHandler.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/CondSh.h"
#include "Pathfinding/AIPathfinder.h"
VCMI_LIB_NAMESPACE_BEGIN
struct QuestInfo;
VCMI_LIB_NAMESPACE_END
class AIhelper;
class AIStatus
{
boost::mutex mx;
boost::condition_variable cv;
BattleState battle;
std::map<QueryID, std::string> remainingQueries;
std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
std::vector<const CGObjectInstance *> objectsBeingVisited;
bool ongoingHeroMovement;
bool ongoingChannelProbing; // true if AI currently explore bidirectional teleport channel exits
bool havingTurn;
public:
AIStatus();
~AIStatus();
void setBattle(BattleState BS);
void setMove(bool ongoing);
void setChannelProbing(bool ongoing);
bool channelProbing();
BattleState getBattle();
void addQuery(QueryID ID, std::string description);
void removeQuery(QueryID ID);
int getQueriesCount();
void startedTurn();
void madeTurn();
void waitTillFree();
bool haveTurn();
void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
void receivedAnswerConfirmation(int answerRequestID, int result);
void heroVisit(const CGObjectInstance * obj, bool started);
template<typename Handler> void serialize(Handler & h, const int version)
{
h & battle;
h & remainingQueries;
h & requestToQueryID;
h & havingTurn;
}
};
class DLL_EXPORT VCAI : public CAdventureAI
{
public:
friend class FuzzyHelper;
friend class ResourceManager;
friend class BuildingManager;
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
ObjectInstanceID destinationTeleport;
int3 destinationTeleportPos;
std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
//part of mainLoop, but accessible from outisde
std::vector<Goals::TSubgoal> basicGoals;
Goals::TGoalVec goalsToRemove;
Goals::TGoalVec goalsToAdd;
std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
//sets are faster to search, also do not contain duplicates
std::set<const CGObjectInstance *> visitableObjs;
std::set<const CGObjectInstance *> alreadyVisited;
std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
AIStatus status;
std::string battlename;
std::shared_ptr<CCallback> myCb;
std::unique_ptr<boost::thread> makingTurn;
private:
boost::mutex turnInterruptionMutex;
public:
ObjectInstanceID selectedObject;
AIhelper * ah;
VCAI();
virtual ~VCAI();
//TODO: use only smart pointers?
void tryRealize(Goals::Explore & g);
void tryRealize(Goals::RecruitHero & g);
void tryRealize(Goals::VisitTile & g);
void tryRealize(Goals::VisitObj & g);
void tryRealize(Goals::VisitHero & g);
void tryRealize(Goals::BuildThis & g);
void tryRealize(Goals::DigAtTile & g);
void tryRealize(Goals::Trade & g);
void tryRealize(Goals::BuyArmy & g);
void tryRealize(Goals::Invalid & g);
void tryRealize(Goals::AbstractGoal & g);
bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
std::string getBattleAIName() const override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void yourTurn(QueryID queryID) override;
void heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void finish() override;
void availableCreaturesChanged(const CGDwelling * town) override;
void heroMoved(const TryMoveHero & details, bool verbose = true) override;
void heroInGarrisonChange(const CGTownInstance * town) override;
void centerView(int3 pos, int focusTime) override;
void tileHidden(const std::unordered_set<int3> & pos) override;
void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
void artifactAssembled(const ArtifactLocation & al) override;
void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
void showThievesGuildWindow(const CGObjectInstance * obj) override;
void playerBlocked(int reason, bool start) override;
void showPuzzleMap() override;
void showShipyardDialog(const IShipyard * obj) override;
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void artifactPut(const ArtifactLocation & al) override;
void artifactRemoved(const ArtifactLocation & al) override;
void artifactDisassembled(const ArtifactLocation & al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
void tileRevealed(const std::unordered_set<int3> & pos) override;
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID) override;
void heroMovePointsChanged(const CGHeroInstance * hero) override;
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
void newObject(const CGObjectInstance * obj) override;
void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
void playerBonusChanged(const Bonus & bonus, bool gain) override;
void heroCreated(const CGHeroInstance *) override;
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void requestRealized(PackageApplied * pa) override;
void receivedResource() override;
void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void heroManaPointsChanged(const CGHeroInstance * hero) override;
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
void battleResultsApplied() override;
void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
void objectPropertyChanged(const SetObjectProperty * sop) override;
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override;
void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
void makeTurn();
void mainLoop();
void performTypicalActions();
void buildArmyIn(const CGTownInstance * t);
void striveToGoal(Goals::TSubgoal ultimateGoal);
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
void endTurn();
void wander(HeroPtr h);
void setGoal(HeroPtr h, Goals::TSubgoal goal);
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, std::optional<float> movementCostLimit = std::nullopt);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
//void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
void moveCreaturesToHero(const CGTownInstance * t);
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
bool moveHeroToTile(int3 dst, HeroPtr h);
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
void waitTillFree();
void addVisitableObj(const CGObjectInstance * obj);
void markObjectVisited(const CGObjectInstance * obj);
void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
void markHeroUnableToExplore(HeroPtr h);
void markHeroAbleToExplore(HeroPtr h);
bool isAbleToExplore(HeroPtr h);
void clearPathsInfo();
void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
void validateVisitableObjs();
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
void retrieveVisitableObjs();
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
const CGObjectInstance * lookForArt(int aid) const;
bool isAccessible(const int3 & pos) const;
HeroPtr getHeroWithGrail() const;
const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
//optimization - use one SM for every hero call
const CGTownInstance * findTownWithTavern() const;
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
Goals::TSubgoal getGoal(HeroPtr h) const;
bool canAct(HeroPtr h) const;
std::vector<HeroPtr> getUnblockedHeroes() const;
std::vector<HeroPtr> getMyHeroes() const;
HeroPtr primaryHero() const;
void checkHeroArmy(HeroPtr h);
void requestSent(const CPackForServer * pack, int requestID) override;
void answerQuery(QueryID queryID, int selection);
//special function that can be called ONLY from game events handling thread and will send request ASAP
void requestActionASAP(std::function<void()> whatToDo);
#if 0
//disabled due to issue 2890
template<typename Handler> void registerGoals(Handler & h)
{
//h.template registerType<Goals::AbstractGoal, Goals::BoostHero>();
h.template registerType<Goals::AbstractGoal, Goals::Build>();
h.template registerType<Goals::AbstractGoal, Goals::BuildThis>();
//h.template registerType<Goals::AbstractGoal, Goals::CIssueCommand>();
h.template registerType<Goals::AbstractGoal, Goals::ClearWayTo>();
h.template registerType<Goals::AbstractGoal, Goals::CollectRes>();
h.template registerType<Goals::AbstractGoal, Goals::Conquer>();
h.template registerType<Goals::AbstractGoal, Goals::DigAtTile>();
h.template registerType<Goals::AbstractGoal, Goals::Explore>();
h.template registerType<Goals::AbstractGoal, Goals::FindObj>();
h.template registerType<Goals::AbstractGoal, Goals::GatherArmy>();
h.template registerType<Goals::AbstractGoal, Goals::GatherTroops>();
h.template registerType<Goals::AbstractGoal, Goals::GetArtOfType>();
h.template registerType<Goals::AbstractGoal, Goals::VisitObj>();
h.template registerType<Goals::AbstractGoal, Goals::Invalid>();
//h.template registerType<Goals::AbstractGoal, Goals::NotLose>();
h.template registerType<Goals::AbstractGoal, Goals::RecruitHero>();
h.template registerType<Goals::AbstractGoal, Goals::VisitHero>();
h.template registerType<Goals::AbstractGoal, Goals::VisitTile>();
h.template registerType<Goals::AbstractGoal, Goals::Win>();
}
#endif
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & knownTeleportChannels;
h & knownSubterraneanGates;
h & destinationTeleport;
h & townVisitsThisWeek;
#if 0
//disabled due to issue 2890
h & lockedHeroes;
#else
{
ui32 length = 0;
h & length;
if(!h.saving)
{
std::set<ui32> loadedPointers;
lockedHeroes.clear();
for(ui32 index = 0; index < length; index++)
{
HeroPtr ignored1;
h & ignored1;
ui8 flag = 0;
h & flag;
if(flag)
{
ui32 pid = 0xffffffff;
h & pid;
if(!vstd::contains(loadedPointers, pid))
{
loadedPointers.insert(pid);
ui16 typeId = 0;
//this is the problem requires such hack
//we have to explicitly ignore invalid goal class type id
h & typeId;
Goals::AbstractGoal ignored2;
ignored2.serialize(h, version);
}
}
}
}
}
#endif
h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
h & visitableObjs;
h & alreadyVisited;
h & reservedObjs;
h & status;
h & battlename;
h & heroesUnableToExplore;
//myCB is restored after load by init call
}
};
class cannotFulfillGoalException : public std::exception
{
std::string msg;
public:
explicit cannotFulfillGoalException(crstring _Message)
: msg(_Message)
{
}
virtual ~cannotFulfillGoalException() throw ()
{
};
const char * what() const throw () override
{
return msg.c_str();
}
};
class goalFulfilledException : public std::exception
{
std::string msg;
public:
Goals::TSubgoal goal;
explicit goalFulfilledException(Goals::TSubgoal Goal)
: goal(Goal)
{
msg = goal->name();
}
virtual ~goalFulfilledException() throw ()
{
};
const char * what() const throw () override
{
return msg.c_str();
}
};
void makePossibleUpgrades(const CArmedInstance * obj);
/*
* VCAI.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 "AIUtility.h"
#include "Goals/AbstractGoal.h"
#include "../../lib/AI_Base.h"
#include "../../CCallback.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/CBuildingHandler.h"
#include "../../lib/CCreatureHandler.h"
#include "../../lib/CTownHandler.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/CondSh.h"
#include "Pathfinding/AIPathfinder.h"
VCMI_LIB_NAMESPACE_BEGIN
struct QuestInfo;
VCMI_LIB_NAMESPACE_END
class AIhelper;
class AIStatus
{
boost::mutex mx;
boost::condition_variable cv;
BattleState battle;
std::map<QueryID, std::string> remainingQueries;
std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
std::vector<const CGObjectInstance *> objectsBeingVisited;
bool ongoingHeroMovement;
bool ongoingChannelProbing; // true if AI currently explore bidirectional teleport channel exits
bool havingTurn;
public:
AIStatus();
~AIStatus();
void setBattle(BattleState BS);
void setMove(bool ongoing);
void setChannelProbing(bool ongoing);
bool channelProbing();
BattleState getBattle();
void addQuery(QueryID ID, std::string description);
void removeQuery(QueryID ID);
int getQueriesCount();
void startedTurn();
void madeTurn();
void waitTillFree();
bool haveTurn();
void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
void receivedAnswerConfirmation(int answerRequestID, int result);
void heroVisit(const CGObjectInstance * obj, bool started);
template<typename Handler> void serialize(Handler & h, const int version)
{
h & battle;
h & remainingQueries;
h & requestToQueryID;
h & havingTurn;
}
};
class DLL_EXPORT VCAI : public CAdventureAI
{
public:
friend class FuzzyHelper;
friend class ResourceManager;
friend class BuildingManager;
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
ObjectInstanceID destinationTeleport;
int3 destinationTeleportPos;
std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
//part of mainLoop, but accessible from outisde
std::vector<Goals::TSubgoal> basicGoals;
Goals::TGoalVec goalsToRemove;
Goals::TGoalVec goalsToAdd;
std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
//sets are faster to search, also do not contain duplicates
std::set<const CGObjectInstance *> visitableObjs;
std::set<const CGObjectInstance *> alreadyVisited;
std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
AIStatus status;
std::string battlename;
std::shared_ptr<CCallback> myCb;
std::unique_ptr<boost::thread> makingTurn;
private:
boost::mutex turnInterruptionMutex;
public:
ObjectInstanceID selectedObject;
AIhelper * ah;
VCAI();
virtual ~VCAI();
//TODO: use only smart pointers?
void tryRealize(Goals::Explore & g);
void tryRealize(Goals::RecruitHero & g);
void tryRealize(Goals::VisitTile & g);
void tryRealize(Goals::VisitObj & g);
void tryRealize(Goals::VisitHero & g);
void tryRealize(Goals::BuildThis & g);
void tryRealize(Goals::DigAtTile & g);
void tryRealize(Goals::Trade & g);
void tryRealize(Goals::BuyArmy & g);
void tryRealize(Goals::Invalid & g);
void tryRealize(Goals::AbstractGoal & g);
bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
std::string getBattleAIName() const override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void yourTurn(QueryID queryID) override;
void heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void finish() override;
void availableCreaturesChanged(const CGDwelling * town) override;
void heroMoved(const TryMoveHero & details, bool verbose = true) override;
void heroInGarrisonChange(const CGTownInstance * town) override;
void centerView(int3 pos, int focusTime) override;
void tileHidden(const std::unordered_set<int3> & pos) override;
void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
void artifactAssembled(const ArtifactLocation & al) override;
void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
void showThievesGuildWindow(const CGObjectInstance * obj) override;
void playerBlocked(int reason, bool start) override;
void showPuzzleMap() override;
void showShipyardDialog(const IShipyard * obj) override;
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void artifactPut(const ArtifactLocation & al) override;
void artifactRemoved(const ArtifactLocation & al) override;
void artifactDisassembled(const ArtifactLocation & al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
void tileRevealed(const std::unordered_set<int3> & pos) override;
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID) override;
void heroMovePointsChanged(const CGHeroInstance * hero) override;
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
void newObject(const CGObjectInstance * obj) override;
void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
void playerBonusChanged(const Bonus & bonus, bool gain) override;
void heroCreated(const CGHeroInstance *) override;
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void requestRealized(PackageApplied * pa) override;
void receivedResource() override;
void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void heroManaPointsChanged(const CGHeroInstance * hero) override;
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
void battleResultsApplied() override;
void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
void objectPropertyChanged(const SetObjectProperty * sop) override;
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override;
void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
void makeTurn();
void mainLoop();
void performTypicalActions();
void buildArmyIn(const CGTownInstance * t);
void striveToGoal(Goals::TSubgoal ultimateGoal);
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
void endTurn();
void wander(HeroPtr h);
void setGoal(HeroPtr h, Goals::TSubgoal goal);
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, std::optional<float> movementCostLimit = std::nullopt);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
//void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
void moveCreaturesToHero(const CGTownInstance * t);
void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
bool moveHeroToTile(int3 dst, HeroPtr h);
void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
void waitTillFree();
void addVisitableObj(const CGObjectInstance * obj);
void markObjectVisited(const CGObjectInstance * obj);
void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
void markHeroUnableToExplore(HeroPtr h);
void markHeroAbleToExplore(HeroPtr h);
bool isAbleToExplore(HeroPtr h);
void clearPathsInfo();
void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
void validateVisitableObjs();
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
void retrieveVisitableObjs();
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
const CGObjectInstance * lookForArt(int aid) const;
bool isAccessible(const int3 & pos) const;
HeroPtr getHeroWithGrail() const;
const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
//optimization - use one SM for every hero call
const CGTownInstance * findTownWithTavern() const;
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
Goals::TSubgoal getGoal(HeroPtr h) const;
bool canAct(HeroPtr h) const;
std::vector<HeroPtr> getUnblockedHeroes() const;
std::vector<HeroPtr> getMyHeroes() const;
HeroPtr primaryHero() const;
void checkHeroArmy(HeroPtr h);
void requestSent(const CPackForServer * pack, int requestID) override;
void answerQuery(QueryID queryID, int selection);
//special function that can be called ONLY from game events handling thread and will send request ASAP
void requestActionASAP(std::function<void()> whatToDo);
#if 0
//disabled due to issue 2890
template<typename Handler> void registerGoals(Handler & h)
{
//h.template registerType<Goals::AbstractGoal, Goals::BoostHero>();
h.template registerType<Goals::AbstractGoal, Goals::Build>();
h.template registerType<Goals::AbstractGoal, Goals::BuildThis>();
//h.template registerType<Goals::AbstractGoal, Goals::CIssueCommand>();
h.template registerType<Goals::AbstractGoal, Goals::ClearWayTo>();
h.template registerType<Goals::AbstractGoal, Goals::CollectRes>();
h.template registerType<Goals::AbstractGoal, Goals::Conquer>();
h.template registerType<Goals::AbstractGoal, Goals::DigAtTile>();
h.template registerType<Goals::AbstractGoal, Goals::Explore>();
h.template registerType<Goals::AbstractGoal, Goals::FindObj>();
h.template registerType<Goals::AbstractGoal, Goals::GatherArmy>();
h.template registerType<Goals::AbstractGoal, Goals::GatherTroops>();
h.template registerType<Goals::AbstractGoal, Goals::GetArtOfType>();
h.template registerType<Goals::AbstractGoal, Goals::VisitObj>();
h.template registerType<Goals::AbstractGoal, Goals::Invalid>();
//h.template registerType<Goals::AbstractGoal, Goals::NotLose>();
h.template registerType<Goals::AbstractGoal, Goals::RecruitHero>();
h.template registerType<Goals::AbstractGoal, Goals::VisitHero>();
h.template registerType<Goals::AbstractGoal, Goals::VisitTile>();
h.template registerType<Goals::AbstractGoal, Goals::Win>();
}
#endif
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & knownTeleportChannels;
h & knownSubterraneanGates;
h & destinationTeleport;
h & townVisitsThisWeek;
#if 0
//disabled due to issue 2890
h & lockedHeroes;
#else
{
ui32 length = 0;
h & length;
if(!h.saving)
{
std::set<ui32> loadedPointers;
lockedHeroes.clear();
for(ui32 index = 0; index < length; index++)
{
HeroPtr ignored1;
h & ignored1;
ui8 flag = 0;
h & flag;
if(flag)
{
ui32 pid = 0xffffffff;
h & pid;
if(!vstd::contains(loadedPointers, pid))
{
loadedPointers.insert(pid);
ui16 typeId = 0;
//this is the problem requires such hack
//we have to explicitly ignore invalid goal class type id
h & typeId;
Goals::AbstractGoal ignored2;
ignored2.serialize(h, version);
}
}
}
}
}
#endif
h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
h & visitableObjs;
h & alreadyVisited;
h & reservedObjs;
h & status;
h & battlename;
h & heroesUnableToExplore;
//myCB is restored after load by init call
}
};
class cannotFulfillGoalException : public std::exception
{
std::string msg;
public:
explicit cannotFulfillGoalException(crstring _Message)
: msg(_Message)
{
}
virtual ~cannotFulfillGoalException() throw ()
{
};
const char * what() const throw () override
{
return msg.c_str();
}
};
class goalFulfilledException : public std::exception
{
std::string msg;
public:
Goals::TSubgoal goal;
explicit goalFulfilledException(Goals::TSubgoal Goal)
: goal(Goal)
{
msg = goal->name();
}
virtual ~goalFulfilledException() throw ()
{
};
const char * what() const throw () override
{
return msg.c_str();
}
};
void makePossibleUpgrades(const CArmedInstance * obj);

106
AUTHORS.h
View File

@ -1,53 +1,53 @@
/*
* AUTHORS.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 PROJECT CODE CONTRIBUTORS:
std::vector<std::vector<std::string>> contributors = {
// Task Name Aka E-Mail
{ "Idea", "Michał Urbańczyk", "Tow", "impono@gmail.com" },
{ "Idea", "Mateusz B.", "Tow dragon", "matcio1@gmail.com" },
{ "Developing", "Andrea Palmate", "afxgroup", "andrea@amigasoft.net" },
{ "Developing", "Alexander Shishkin", "alexvins", "" },
{ "Developing", "Rafal R.", "ambtrip", "ambtrip@wp.pl" },
{ "Developing", "Andrii Danylchenko", "", "" },
{ "Developing", "Benjamin Gentner", "beegee", "" },
{ "Developing", "Piotr Wójcik", "Chocimier", "chocimier@tlen.pl" },
{ "Developing", "Dmitry Orlov", "", "shubus.corporation@gmail.com" },
{ "Developing", "", "Dydzio", "blood990@gmail.com" },
{ "Developing", "Andrzej Żak", "godric3", "" },
{ "Developing", "Henning Koehler", "henningkoehlernz", "henning.koehler.nz@gmail.com" },
{ "Developing", "Ivan Savenko", "", "saven.ivan@gmail.com" },
{ "Developing", "", "kambala-decapitator", "decapitator@ukr.net" },
{ "Developing", "", "krs0", "" },
{ "Developing", "", "Laserlicht", "" },
{ "Developing", "Alexey", "Macron1Robot", "" },
{ "Developing", "Michał Kalinowski", "", "feniks_fire@o2.pl" },
{ "Developing", "Vadim Glazunov", "neweagle", "neweagle@gmail.com" },
{ "Developing", "Andrey Cherkas", "nordsoft", "nordsoft@yahoo.com" },
{ "Developing", "Rickard Westerlund", "Onion Knight", "onionknigh@gmail.com" },
{ "Developing", "Yifeng Sun", "phoebus118", "pkusunyifeng@gmail.com" },
{ "Developing", "", "rilian-la-te", "" },
{ "Developing", "", "SoundSSGood", "" },
{ "Developing", "Stefan Pavlov", "Ste", "mailste@gmail.com" },
{ "Developing", "Arseniy Shestakov", "SXX", "me@arseniyshestakov.com" },
{ "Developing", "Lukasz Wychrystenko", "tezeriusz", "t0@czlug.icis.pcz.pl" },
{ "Developing", "Trevor Standley", "tstandley", "" },
{ "Developing", "Vadim Markovtsev", "", "gmarkhor@gmail.com" },
{ "Developing", "Frank Zago", "ubuntux", "" },
{ "Developing", "", "vmarkovtsev", "" },
{ "Developing", "Tom Zielinski", "Warmonger", "Warmonger@vp.pl" },
{ "Developing", "Xiaomin Ding", "", "dingding303@gmail.com" },
{ "Testing", "Ben Yan", "by003", "benyan9110@gmail.com," },
{ "Testing", "", "Misiokles", "" },
{ "Testing", "", "Povelitel", "" },
};
/*
* AUTHORS.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 PROJECT CODE CONTRIBUTORS:
std::vector<std::vector<std::string>> contributors = {
// Task Name Aka E-Mail
{ "Idea", "Michał Urbańczyk", "Tow", "impono@gmail.com" },
{ "Idea", "Mateusz B.", "Tow dragon", "matcio1@gmail.com" },
{ "Developing", "Andrea Palmate", "afxgroup", "andrea@amigasoft.net" },
{ "Developing", "Alexander Shishkin", "alexvins", "" },
{ "Developing", "Rafal R.", "ambtrip", "ambtrip@wp.pl" },
{ "Developing", "Andrii Danylchenko", "", "" },
{ "Developing", "Benjamin Gentner", "beegee", "" },
{ "Developing", "Piotr Wójcik", "Chocimier", "chocimier@tlen.pl" },
{ "Developing", "Dmitry Orlov", "", "shubus.corporation@gmail.com" },
{ "Developing", "", "Dydzio", "blood990@gmail.com" },
{ "Developing", "Andrzej Żak", "godric3", "" },
{ "Developing", "Henning Koehler", "henningkoehlernz", "henning.koehler.nz@gmail.com" },
{ "Developing", "Ivan Savenko", "", "saven.ivan@gmail.com" },
{ "Developing", "", "kambala-decapitator", "decapitator@ukr.net" },
{ "Developing", "", "krs0", "" },
{ "Developing", "", "Laserlicht", "" },
{ "Developing", "Alexey", "Macron1Robot", "" },
{ "Developing", "Michał Kalinowski", "", "feniks_fire@o2.pl" },
{ "Developing", "Vadim Glazunov", "neweagle", "neweagle@gmail.com" },
{ "Developing", "Andrey Cherkas", "nordsoft", "nordsoft@yahoo.com" },
{ "Developing", "Rickard Westerlund", "Onion Knight", "onionknigh@gmail.com" },
{ "Developing", "Yifeng Sun", "phoebus118", "pkusunyifeng@gmail.com" },
{ "Developing", "", "rilian-la-te", "" },
{ "Developing", "", "SoundSSGood", "" },
{ "Developing", "Stefan Pavlov", "Ste", "mailste@gmail.com" },
{ "Developing", "Arseniy Shestakov", "SXX", "me@arseniyshestakov.com" },
{ "Developing", "Lukasz Wychrystenko", "tezeriusz", "t0@czlug.icis.pcz.pl" },
{ "Developing", "Trevor Standley", "tstandley", "" },
{ "Developing", "Vadim Markovtsev", "", "gmarkhor@gmail.com" },
{ "Developing", "Frank Zago", "ubuntux", "" },
{ "Developing", "", "vmarkovtsev", "" },
{ "Developing", "Tom Zielinski", "Warmonger", "Warmonger@vp.pl" },
{ "Developing", "Xiaomin Ding", "", "dingding303@gmail.com" },
{ "Testing", "Ben Yan", "by003", "benyan9110@gmail.com," },
{ "Testing", "", "Misiokles", "" },
{ "Testing", "", "Povelitel", "" },
};

View File

@ -1,423 +1,423 @@
/*
* CCallback.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 "CCallback.h"
#include "lib/CCreatureHandler.h"
#include "lib/gameState/CGameState.h"
#include "client/CPlayerInterface.h"
#include "client/Client.h"
#include "lib/mapping/CMap.h"
#include "lib/CBuildingHandler.h"
#include "lib/CGeneralTextHandler.h"
#include "lib/CHeroHandler.h"
#include "lib/NetPacks.h"
#include "lib/CArtHandler.h"
#include "lib/GameConstants.h"
#include "lib/CPlayerState.h"
#include "lib/UnlockGuard.h"
#include "lib/battle/BattleInfo.h"
bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where)
{
CastleTeleportHero pack(who->id, where->id, 1);
sendRequest(&pack);
return true;
}
bool CCallback::moveHero(const CGHeroInstance *h, int3 dst, bool transit)
{
MoveHero pack(dst,h->id,transit);
sendRequest(&pack);
return true;
}
int CCallback::selectionMade(int selection, QueryID queryID)
{
return sendQueryReply(selection, queryID);
}
int CCallback::sendQueryReply(std::optional<int32_t> reply, QueryID queryID)
{
ASSERT_IF_CALLED_WITH_PLAYER
if(queryID == QueryID(-1))
{
logGlobal->error("Cannot answer the query -1!");
return -1;
}
QueryReply pack(queryID, reply);
pack.player = *player;
return sendRequest(&pack);
}
void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level)
{
// TODO exception for neutral dwellings shouldn't be hardcoded
if(player != obj->tempOwner && obj->ID != Obj::WAR_MACHINE_FACTORY && obj->ID != Obj::REFUGEE_CAMP)
return;
RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
sendRequest(&pack);
}
bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
{
if((player && obj->tempOwner != player) || (obj->stacksCount()<2 && obj->needsLastStack()))
return false;
DisbandCreature pack(stackPos,obj->id);
sendRequest(&pack);
return true;
}
bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID)
{
UpgradeCreature pack(stackPos,obj->id,newID);
sendRequest(&pack);
return false;
}
void CCallback::endTurn()
{
logGlobal->trace("Player %d ended his turn.", player->getNum());
EndTurn pack;
sendRequest(&pack);
}
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
return 0;
}
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
return 0;
}
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
{
ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
sendRequest(&pack);
return 0;
}
int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot)
{
BulkMoveArmy pack(srcArmy, destArmy, srcSlot);
sendRequest(&pack);
return 0;
}
int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany)
{
BulkSplitStack pack(armyId, srcSlot, howMany);
sendRequest(&pack);
return 0;
}
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkSmartSplitStack pack(armyId, srcSlot);
sendRequest(&pack);
return 0;
}
int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkMergeStacks pack(armyId, srcSlot);
sendRequest(&pack);
return 0;
}
bool CCallback::dismissHero(const CGHeroInstance *hero)
{
if(player!=hero->tempOwner) return false;
DismissHero pack(hero->id);
sendRequest(&pack);
return true;
}
bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)
{
ExchangeArtifacts ea;
ea.src = l1;
ea.dst = l2;
sendRequest(&ea);
return true;
}
/**
* Assembles or disassembles a combination artifact.
* @param hero Hero holding the artifact(s).
* @param artifactSlot The worn slot ID of the combination- or constituent artifact.
* @param assemble True for assembly operation, false for disassembly.
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
* artifact to assemble to. Otherwise it's not used.
*/
void CCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
{
AssembleArtifacts aa(hero->id, artifactSlot, assemble, assembleTo);
sendRequest(&aa);
}
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
{
BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
sendRequest(&bma);
}
void CCallback::eraseArtifactByClient(const ArtifactLocation & al)
{
EraseArtifactByClient ea(al);
sendRequest(&ea);
}
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
{
if(town->tempOwner!=player)
return false;
if(canBuildStructure(town, buildingID) != EBuildingState::ALLOWED)
return false;
BuildStructure pack(town->id,buildingID);
sendRequest(&pack);
return true;
}
void CBattleCallback::battleMakeSpellAction(const BattleID & battleID, const BattleAction & action)
{
assert(action.actionType == EActionType::HERO_SPELL);
MakeAction mca(action);
mca.battleID = battleID;
sendRequest(&mca);
}
int CBattleCallback::sendRequest(const CPackForServer * request)
{
int requestID = cl->sendRequest(request, *getPlayerID());
if(waitTillRealize)
{
logGlobal->trace("We'll wait till request %d is answered.\n", requestID);
auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting);
CClient::waitingRequest.waitWhileContains(requestID);
}
boost::this_thread::interruption_point();
return requestID;
}
void CCallback::swapGarrisonHero( const CGTownInstance *town )
{
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
{
GarrisonHeroSwap pack(town->id);
sendRequest(&pack);
}
}
void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
{
if(hero->tempOwner != *player) return;
BuyArtifact pack(hero->id,aid);
sendRequest(&pack);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero)
{
trade(market, mode, std::vector<ui32>(1, id1), std::vector<ui32>(1, id2), std::vector<ui32>(1, val1), hero);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
{
TradeOnMarketplace pack;
pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
pack.heroId = hero ? hero->id : ObjectInstanceID();
pack.mode = mode;
pack.r1 = id1;
pack.r2 = id2;
pack.val = val1;
sendRequest(&pack);
}
void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
{
SetFormation pack(hero->id,tight);
sendRequest(&pack);
}
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)
{
assert(townOrTavern);
assert(hero);
HireHero pack(HeroTypeID(hero->subID), townOrTavern->id);
pack.player = *player;
sendRequest(&pack);
}
void CCallback::save( const std::string &fname )
{
cl->save(fname);
}
void CCallback::gamePause(bool pause)
{
if(pause)
{
GamePause pack;
pack.player = *player;
sendRequest(&pack);
}
else
{
sendQueryReply(0, QueryID::CLIENT);
}
}
void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * currentObject)
{
ASSERT_IF_CALLED_WITH_PLAYER
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
if(player)
pm.player = *player;
sendRequest(&pm);
}
void CCallback::buildBoat( const IShipyard *obj )
{
BuildBoat bb;
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
sendRequest(&bb);
}
CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
: CBattleCallback(Player, C)
{
gs = GS;
waitTillRealize = false;
unlockGsWhenWaiting = false;
}
CCallback::~CCallback() = default;
bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
{
//bidirectional
return gs->map->canMoveBetween(a, b);
}
std::shared_ptr<const CPathsInfo> CCallback::getPathsInfo(const CGHeroInstance * h)
{
return cl->getPathsInfo(h);
}
std::optional<PlayerColor> CCallback::getPlayerID() const
{
return CBattleCallback::getPlayerID();
}
int3 CCallback::getGuardingCreaturePosition(int3 tile)
{
if (!gs->map->isInTheMap(tile))
return int3(-1,-1,-1);
return gs->map->guardingCreaturePositions[tile.z][tile.x][tile.y];
}
void CCallback::dig( const CGObjectInstance *hero )
{
DigWithHero dwh;
dwh.id = hero->id;
sendRequest(&dwh);
}
void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos)
{
CastAdvSpell cas;
cas.hid = hero->id;
cas.sid = spellID;
cas.pos = pos;
sendRequest(&cas);
}
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
if(s1->getCreature(p1) == s2->getCreature(p2))
return mergeStacks(s1, s2, p1, p2);
else
return swapCreatures(s1, s2, p1, p2);
}
void CCallback::registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
{
cl->additionalBattleInts[*player].push_back(battleEvents);
}
void CCallback::unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
{
cl->additionalBattleInts[*player] -= battleEvents;
}
CBattleCallback::CBattleCallback(std::optional<PlayerColor> player, CClient * C):
cl(C),
player(player)
{
}
void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const BattleAction & action)
{
assert(!cl->gs->getBattle(battleID)->tacticDistance);
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
}
void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
{
assert(cl->gs->getBattle(battleID)->tacticDistance);
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
}
std::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
{
return cl->playerint[getPlayerID().value()]->makeSurrenderRetreatDecision(battleID, battleState);
}
std::shared_ptr<CPlayerBattleCallback> CBattleCallback::getBattle(const BattleID & battleID)
{
return activeBattles.at(battleID);
}
std::optional<PlayerColor> CBattleCallback::getPlayerID() const
{
return player;
}
void CBattleCallback::onBattleStarted(const IBattleInfo * info)
{
activeBattles[info->getBattleID()] = std::make_shared<CPlayerBattleCallback>(info, *getPlayerID());
}
void CBattleCallback::onBattleEnded(const BattleID & battleID)
{
activeBattles.erase(battleID);
}
/*
* CCallback.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 "CCallback.h"
#include "lib/CCreatureHandler.h"
#include "lib/gameState/CGameState.h"
#include "client/CPlayerInterface.h"
#include "client/Client.h"
#include "lib/mapping/CMap.h"
#include "lib/CBuildingHandler.h"
#include "lib/CGeneralTextHandler.h"
#include "lib/CHeroHandler.h"
#include "lib/NetPacks.h"
#include "lib/CArtHandler.h"
#include "lib/GameConstants.h"
#include "lib/CPlayerState.h"
#include "lib/UnlockGuard.h"
#include "lib/battle/BattleInfo.h"
bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where)
{
CastleTeleportHero pack(who->id, where->id, 1);
sendRequest(&pack);
return true;
}
bool CCallback::moveHero(const CGHeroInstance *h, int3 dst, bool transit)
{
MoveHero pack(dst,h->id,transit);
sendRequest(&pack);
return true;
}
int CCallback::selectionMade(int selection, QueryID queryID)
{
return sendQueryReply(selection, queryID);
}
int CCallback::sendQueryReply(std::optional<int32_t> reply, QueryID queryID)
{
ASSERT_IF_CALLED_WITH_PLAYER
if(queryID == QueryID(-1))
{
logGlobal->error("Cannot answer the query -1!");
return -1;
}
QueryReply pack(queryID, reply);
pack.player = *player;
return sendRequest(&pack);
}
void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level)
{
// TODO exception for neutral dwellings shouldn't be hardcoded
if(player != obj->tempOwner && obj->ID != Obj::WAR_MACHINE_FACTORY && obj->ID != Obj::REFUGEE_CAMP)
return;
RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
sendRequest(&pack);
}
bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
{
if((player && obj->tempOwner != player) || (obj->stacksCount()<2 && obj->needsLastStack()))
return false;
DisbandCreature pack(stackPos,obj->id);
sendRequest(&pack);
return true;
}
bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID)
{
UpgradeCreature pack(stackPos,obj->id,newID);
sendRequest(&pack);
return false;
}
void CCallback::endTurn()
{
logGlobal->trace("Player %d ended his turn.", player->getNum());
EndTurn pack;
sendRequest(&pack);
}
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
return 0;
}
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
return 0;
}
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
{
ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
sendRequest(&pack);
return 0;
}
int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot)
{
BulkMoveArmy pack(srcArmy, destArmy, srcSlot);
sendRequest(&pack);
return 0;
}
int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany)
{
BulkSplitStack pack(armyId, srcSlot, howMany);
sendRequest(&pack);
return 0;
}
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkSmartSplitStack pack(armyId, srcSlot);
sendRequest(&pack);
return 0;
}
int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkMergeStacks pack(armyId, srcSlot);
sendRequest(&pack);
return 0;
}
bool CCallback::dismissHero(const CGHeroInstance *hero)
{
if(player!=hero->tempOwner) return false;
DismissHero pack(hero->id);
sendRequest(&pack);
return true;
}
bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)
{
ExchangeArtifacts ea;
ea.src = l1;
ea.dst = l2;
sendRequest(&ea);
return true;
}
/**
* Assembles or disassembles a combination artifact.
* @param hero Hero holding the artifact(s).
* @param artifactSlot The worn slot ID of the combination- or constituent artifact.
* @param assemble True for assembly operation, false for disassembly.
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
* artifact to assemble to. Otherwise it's not used.
*/
void CCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
{
AssembleArtifacts aa(hero->id, artifactSlot, assemble, assembleTo);
sendRequest(&aa);
}
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
{
BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
sendRequest(&bma);
}
void CCallback::eraseArtifactByClient(const ArtifactLocation & al)
{
EraseArtifactByClient ea(al);
sendRequest(&ea);
}
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
{
if(town->tempOwner!=player)
return false;
if(canBuildStructure(town, buildingID) != EBuildingState::ALLOWED)
return false;
BuildStructure pack(town->id,buildingID);
sendRequest(&pack);
return true;
}
void CBattleCallback::battleMakeSpellAction(const BattleID & battleID, const BattleAction & action)
{
assert(action.actionType == EActionType::HERO_SPELL);
MakeAction mca(action);
mca.battleID = battleID;
sendRequest(&mca);
}
int CBattleCallback::sendRequest(const CPackForServer * request)
{
int requestID = cl->sendRequest(request, *getPlayerID());
if(waitTillRealize)
{
logGlobal->trace("We'll wait till request %d is answered.\n", requestID);
auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting);
CClient::waitingRequest.waitWhileContains(requestID);
}
boost::this_thread::interruption_point();
return requestID;
}
void CCallback::swapGarrisonHero( const CGTownInstance *town )
{
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
{
GarrisonHeroSwap pack(town->id);
sendRequest(&pack);
}
}
void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
{
if(hero->tempOwner != *player) return;
BuyArtifact pack(hero->id,aid);
sendRequest(&pack);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero)
{
trade(market, mode, std::vector<ui32>(1, id1), std::vector<ui32>(1, id2), std::vector<ui32>(1, val1), hero);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
{
TradeOnMarketplace pack;
pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
pack.heroId = hero ? hero->id : ObjectInstanceID();
pack.mode = mode;
pack.r1 = id1;
pack.r2 = id2;
pack.val = val1;
sendRequest(&pack);
}
void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
{
SetFormation pack(hero->id,tight);
sendRequest(&pack);
}
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)
{
assert(townOrTavern);
assert(hero);
HireHero pack(HeroTypeID(hero->subID), townOrTavern->id);
pack.player = *player;
sendRequest(&pack);
}
void CCallback::save( const std::string &fname )
{
cl->save(fname);
}
void CCallback::gamePause(bool pause)
{
if(pause)
{
GamePause pack;
pack.player = *player;
sendRequest(&pack);
}
else
{
sendQueryReply(0, QueryID::CLIENT);
}
}
void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * currentObject)
{
ASSERT_IF_CALLED_WITH_PLAYER
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
if(player)
pm.player = *player;
sendRequest(&pm);
}
void CCallback::buildBoat( const IShipyard *obj )
{
BuildBoat bb;
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
sendRequest(&bb);
}
CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
: CBattleCallback(Player, C)
{
gs = GS;
waitTillRealize = false;
unlockGsWhenWaiting = false;
}
CCallback::~CCallback() = default;
bool CCallback::canMoveBetween(const int3 &a, const int3 &b)
{
//bidirectional
return gs->map->canMoveBetween(a, b);
}
std::shared_ptr<const CPathsInfo> CCallback::getPathsInfo(const CGHeroInstance * h)
{
return cl->getPathsInfo(h);
}
std::optional<PlayerColor> CCallback::getPlayerID() const
{
return CBattleCallback::getPlayerID();
}
int3 CCallback::getGuardingCreaturePosition(int3 tile)
{
if (!gs->map->isInTheMap(tile))
return int3(-1,-1,-1);
return gs->map->guardingCreaturePositions[tile.z][tile.x][tile.y];
}
void CCallback::dig( const CGObjectInstance *hero )
{
DigWithHero dwh;
dwh.id = hero->id;
sendRequest(&dwh);
}
void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos)
{
CastAdvSpell cas;
cas.hid = hero->id;
cas.sid = spellID;
cas.pos = pos;
sendRequest(&cas);
}
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
if(s1->getCreature(p1) == s2->getCreature(p2))
return mergeStacks(s1, s2, p1, p2);
else
return swapCreatures(s1, s2, p1, p2);
}
void CCallback::registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
{
cl->additionalBattleInts[*player].push_back(battleEvents);
}
void CCallback::unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents)
{
cl->additionalBattleInts[*player] -= battleEvents;
}
CBattleCallback::CBattleCallback(std::optional<PlayerColor> player, CClient * C):
cl(C),
player(player)
{
}
void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const BattleAction & action)
{
assert(!cl->gs->getBattle(battleID)->tacticDistance);
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
}
void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
{
assert(cl->gs->getBattle(battleID)->tacticDistance);
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
}
std::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
{
return cl->playerint[getPlayerID().value()]->makeSurrenderRetreatDecision(battleID, battleState);
}
std::shared_ptr<CPlayerBattleCallback> CBattleCallback::getBattle(const BattleID & battleID)
{
return activeBattles.at(battleID);
}
std::optional<PlayerColor> CBattleCallback::getPlayerID() const
{
return player;
}
void CBattleCallback::onBattleStarted(const IBattleInfo * info)
{
activeBattles[info->getBattleID()] = std::make_shared<CPlayerBattleCallback>(info, *getPlayerID());
}
void CBattleCallback::onBattleEnded(const BattleID & battleID)
{
activeBattles.erase(battleID);
}

View File

@ -1,197 +1,197 @@
/*
* CCallback.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 "lib/CGameInfoCallback.h"
#include "lib/battle/CPlayerBattleCallback.h"
#include "lib/int3.h" // for int3
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CGameState;
struct CPath;
class CGObjectInstance;
class CArmedInstance;
class BattleAction;
class CGTownInstance;
class IShipyard;
struct CGPathNode;
struct CGPath;
struct CPathsInfo;
class PathfinderConfig;
struct CPack;
struct CPackForServer;
class IBattleEventsReceiver;
class IGameEventsReceiver;
struct ArtifactLocation;
class BattleStateInfoForRetreat;
class IMarket;
VCMI_LIB_NAMESPACE_END
// in static AI build this file gets included into libvcmi
#ifdef STATIC_AI
VCMI_LIB_USING_NAMESPACE
#endif
class CClient;
struct lua_State;
class IBattleCallback
{
public:
virtual ~IBattleCallback() = default;
bool waitTillRealize = false; //if true, request functions will return after they are realized by server
bool unlockGsWhenWaiting = false;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback!
//battle
virtual void battleMakeSpellAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual void battleMakeUnitAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual void battleMakeTacticAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) = 0;
virtual std::shared_ptr<CPlayerBattleCallback> getBattle(const BattleID & battleID) = 0;
virtual std::optional<PlayerColor> getPlayerID() const = 0;
};
class IGameActionCallback
{
public:
//hero
virtual bool moveHero(const CGHeroInstance *h, int3 dst, bool transit) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
virtual void dig(const CGObjectInstance *hero)=0;
virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
//town
virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)=0;
virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0;
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
virtual void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
virtual int selectionMade(int selection, QueryID queryID) =0;
virtual int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) =0;
virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//swaps creatures between two possibly different garrisons // TODO: AI-unsafe code - fix it!
virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//joins first stack to the second (creatures must be same type)
virtual int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) =0; //first goes to the second
virtual int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)=0;//split creatures from the first stack
//virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
virtual bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
virtual void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
virtual bool dismissCreature(const CArmedInstance *obj, SlotID stackPos)=0;
virtual void endTurn()=0;
virtual void buyArtifact(const CGHeroInstance *hero, ArtifactID aid)=0; //used to buy artifacts in towns (including spell book in the guild and war machines in blacksmith)
virtual void setFormation(const CGHeroInstance * hero, bool tight)=0;
virtual void save(const std::string &fname) = 0;
virtual void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) = 0;
virtual void gamePause(bool pause) = 0;
virtual void buildBoat(const IShipyard *obj) = 0;
// To implement high-level army management bulk actions
virtual int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) = 0;
virtual int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) = 0;
virtual int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) = 0;
virtual int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) = 0;
// Moves all artifacts from one hero to another
virtual void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack) = 0;
};
class CBattleCallback : public IBattleCallback
{
std::map<BattleID, std::shared_ptr<CPlayerBattleCallback>> activeBattles;
std::optional<PlayerColor> player;
protected:
int sendRequest(const CPackForServer * request); //returns requestID (that'll be matched to requestID in PackageApplied)
CClient *cl;
public:
CBattleCallback(std::optional<PlayerColor> player, CClient * C);
void battleMakeSpellAction(const BattleID & battleID, const BattleAction & action) override;//for casting spells by hero - DO NOT use it for moving active stack
void battleMakeUnitAction(const BattleID & battleID, const BattleAction & action) override;
void battleMakeTacticAction(const BattleID & battleID, const BattleAction & action) override; // performs tactic phase actions
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
std::shared_ptr<CPlayerBattleCallback> getBattle(const BattleID & battleID) override;
std::optional<PlayerColor> getPlayerID() const override;
void onBattleStarted(const IBattleInfo * info);
void onBattleEnded(const BattleID & battleID);
friend class CCallback;
friend class CClient;
};
class CCallback : public CPlayerSpecificInfoCallback, public CBattleCallback, public IGameActionCallback
{
public:
CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C);
virtual ~CCallback();
//client-specific functionalities (pathfinding)
virtual bool canMoveBetween(const int3 &a, const int3 &b);
virtual int3 getGuardingCreaturePosition(int3 tile);
virtual std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h);
std::optional<PlayerColor> getPlayerID() const override;
//Set of metrhods that allows adding more interfaces for this player that'll receive game event call-ins.
void registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
//commands
bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile)
bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
int selectionMade(int selection, QueryID queryID) override;
int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) override;
int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override;
int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val) override;
int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) override;
int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) override;
int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) override;
int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) override;
bool dismissHero(const CGHeroInstance * hero) override;
bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
void eraseArtifactByClient(const ArtifactLocation & al) override;
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
void endTurn() override;
void swapGarrisonHero(const CGTownInstance *town) override;
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
void setFormation(const CGHeroInstance * hero, bool tight) override;
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override;
void save(const std::string &fname) override;
void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override;
void gamePause(bool pause) override;
void buildBoat(const IShipyard *obj) override;
void dig(const CGObjectInstance *hero) override;
void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1)) override;
//friends
friend class CClient;
};
/*
* CCallback.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 "lib/CGameInfoCallback.h"
#include "lib/battle/CPlayerBattleCallback.h"
#include "lib/int3.h" // for int3
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CGameState;
struct CPath;
class CGObjectInstance;
class CArmedInstance;
class BattleAction;
class CGTownInstance;
class IShipyard;
struct CGPathNode;
struct CGPath;
struct CPathsInfo;
class PathfinderConfig;
struct CPack;
struct CPackForServer;
class IBattleEventsReceiver;
class IGameEventsReceiver;
struct ArtifactLocation;
class BattleStateInfoForRetreat;
class IMarket;
VCMI_LIB_NAMESPACE_END
// in static AI build this file gets included into libvcmi
#ifdef STATIC_AI
VCMI_LIB_USING_NAMESPACE
#endif
class CClient;
struct lua_State;
class IBattleCallback
{
public:
virtual ~IBattleCallback() = default;
bool waitTillRealize = false; //if true, request functions will return after they are realized by server
bool unlockGsWhenWaiting = false;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback!
//battle
virtual void battleMakeSpellAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual void battleMakeUnitAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual void battleMakeTacticAction(const BattleID & battleID, const BattleAction & action) = 0;
virtual std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) = 0;
virtual std::shared_ptr<CPlayerBattleCallback> getBattle(const BattleID & battleID) = 0;
virtual std::optional<PlayerColor> getPlayerID() const = 0;
};
class IGameActionCallback
{
public:
//hero
virtual bool moveHero(const CGHeroInstance *h, int3 dst, bool transit) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
virtual void dig(const CGObjectInstance *hero)=0;
virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
//town
virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)=0;
virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0;
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
virtual void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
virtual int selectionMade(int selection, QueryID queryID) =0;
virtual int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) =0;
virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//swaps creatures between two possibly different garrisons // TODO: AI-unsafe code - fix it!
virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//joins first stack to the second (creatures must be same type)
virtual int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) =0; //first goes to the second
virtual int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)=0;//split creatures from the first stack
//virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
virtual bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
virtual void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
virtual bool dismissCreature(const CArmedInstance *obj, SlotID stackPos)=0;
virtual void endTurn()=0;
virtual void buyArtifact(const CGHeroInstance *hero, ArtifactID aid)=0; //used to buy artifacts in towns (including spell book in the guild and war machines in blacksmith)
virtual void setFormation(const CGHeroInstance * hero, bool tight)=0;
virtual void save(const std::string &fname) = 0;
virtual void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) = 0;
virtual void gamePause(bool pause) = 0;
virtual void buildBoat(const IShipyard *obj) = 0;
// To implement high-level army management bulk actions
virtual int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) = 0;
virtual int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) = 0;
virtual int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) = 0;
virtual int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) = 0;
// Moves all artifacts from one hero to another
virtual void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack) = 0;
};
class CBattleCallback : public IBattleCallback
{
std::map<BattleID, std::shared_ptr<CPlayerBattleCallback>> activeBattles;
std::optional<PlayerColor> player;
protected:
int sendRequest(const CPackForServer * request); //returns requestID (that'll be matched to requestID in PackageApplied)
CClient *cl;
public:
CBattleCallback(std::optional<PlayerColor> player, CClient * C);
void battleMakeSpellAction(const BattleID & battleID, const BattleAction & action) override;//for casting spells by hero - DO NOT use it for moving active stack
void battleMakeUnitAction(const BattleID & battleID, const BattleAction & action) override;
void battleMakeTacticAction(const BattleID & battleID, const BattleAction & action) override; // performs tactic phase actions
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
std::shared_ptr<CPlayerBattleCallback> getBattle(const BattleID & battleID) override;
std::optional<PlayerColor> getPlayerID() const override;
void onBattleStarted(const IBattleInfo * info);
void onBattleEnded(const BattleID & battleID);
friend class CCallback;
friend class CClient;
};
class CCallback : public CPlayerSpecificInfoCallback, public CBattleCallback, public IGameActionCallback
{
public:
CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C);
virtual ~CCallback();
//client-specific functionalities (pathfinding)
virtual bool canMoveBetween(const int3 &a, const int3 &b);
virtual int3 getGuardingCreaturePosition(int3 tile);
virtual std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h);
std::optional<PlayerColor> getPlayerID() const override;
//Set of metrhods that allows adding more interfaces for this player that'll receive game event call-ins.
void registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
//commands
bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile)
bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
int selectionMade(int selection, QueryID queryID) override;
int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) override;
int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override;
int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second
int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val) override;
int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) override;
int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) override;
int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) override;
int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) override;
bool dismissHero(const CGHeroInstance * hero) override;
bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
void eraseArtifactByClient(const ArtifactLocation & al) override;
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
void endTurn() override;
void swapGarrisonHero(const CGTownInstance *town) override;
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
void setFormation(const CGHeroInstance * hero, bool tight) override;
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override;
void save(const std::string &fname) override;
void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override;
void gamePause(bool pause) override;
void buildBoat(const IShipyard *obj) override;
void dig(const CGObjectInstance *hero) override;
void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1)) override;
//friends
friend class CClient;
};

File diff suppressed because it is too large Load Diff

1358
Global.h

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +1,40 @@
VERM
; standard verm file, global engine things should be put here
!?PI;
; example 1 --- Hello World
![print ^Hello world!^]
; example 2 --- simple arithmetics
![defun add [x y] [+ x y]]
![print [add 2 3]]
; example 3 --- semantic macros
![defmacro do-n-times [times body]
`[progn
[setq do-counter 0]
[setq do-max ,times]
[do [< do-counter do-max]
[progn
[setq do-counter [+ do-counter 1]]
,body
]
]
]
]
![do-n-times 4 [print ^tekst\n^]]
; example 4 --- conditional expression
![if [> 2 1] [print ^Wieksze^] [print ^Mniejsze^]]
; example 5 --- lambda expressions
![[lambda [x y] [if [> x y] [print ^wieksze^] [print ^mniejsze^]]] 2 3]
; example 6 --- resursion
![defun factorial [n]
[if [= n 0] 1
[* n [factorial [- n 1]]]
]
]
VERM
; standard verm file, global engine things should be put here
!?PI;
; example 1 --- Hello World
![print ^Hello world!^]
; example 2 --- simple arithmetics
![defun add [x y] [+ x y]]
![print [add 2 3]]
; example 3 --- semantic macros
![defmacro do-n-times [times body]
`[progn
[setq do-counter 0]
[setq do-max ,times]
[do [< do-counter do-max]
[progn
[setq do-counter [+ do-counter 1]]
,body
]
]
]
]
![do-n-times 4 [print ^tekst\n^]]
; example 4 --- conditional expression
![if [> 2 1] [print ^Wieksze^] [print ^Mniejsze^]]
; example 5 --- lambda expressions
![[lambda [x y] [if [> x y] [print ^wieksze^] [print ^mniejsze^]]] 2 3]
; example 6 --- resursion
![defun factorial [n]
[if [= n 0] 1
[* n [factorial [- n 1]]]
]
]
![print [factorial 8]]

View File

@ -1,14 +1,14 @@
ZVSE
!?PI;
!!VRv2777:S4;
!!DO1/0/5/1&v2777<>1:P0;
!?FU1;
!!VRv2778:Sx16%2;
!!IF&x16>3:M^Hello world number %X16! To duza liczba^;
!!IF&v2778==0&x16<=3:M^Hello world number %X16! To mala parzysta liczba^;
!!IF&v2778==1&x16<=3:M^Hello world number %X16! To mala nieparzysta liczba^;
!?PI;
!!VRz10:S^Composed hello ^;
ZVSE
!?PI;
!!VRv2777:S4;
!!DO1/0/5/1&v2777<>1:P0;
!?FU1;
!!VRv2778:Sx16%2;
!!IF&x16>3:M^Hello world number %X16! To duza liczba^;
!!IF&v2778==0&x16<=3:M^Hello world number %X16! To mala parzysta liczba^;
!!IF&v2778==1&x16<=3:M^Hello world number %X16! To mala nieparzysta liczba^;
!?PI;
!!VRz10:S^Composed hello ^;
!!IF:M^%Z10%%world%%, v2777=%V2777, v2778=%V2778!^;

View File

@ -1,369 +1,369 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Bedrohung: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Mühelos",
"vcmi.adventureMap.monsterThreat.levels.1" : "Sehr schwach",
"vcmi.adventureMap.monsterThreat.levels.2" : "Schwach",
"vcmi.adventureMap.monsterThreat.levels.3" : "Ein bisschen schwächer",
"vcmi.adventureMap.monsterThreat.levels.4" : "Gleichauf",
"vcmi.adventureMap.monsterThreat.levels.5" : "Ein bisschen stärker",
"vcmi.adventureMap.monsterThreat.levels.6" : "Stark",
"vcmi.adventureMap.monsterThreat.levels.7" : "Sehr Stark",
"vcmi.adventureMap.monsterThreat.levels.8" : "Herausfordernd",
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
"vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich",
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich",
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
"vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!",
"vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.",
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
"vcmi.adventureMap.moveCostDetails" : "Bewegungspunkte - Kosten: %TURNS Runden + %POINTS Punkte, Verbleibende Punkte: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Bewegungspunkte - Kosten: %POINTS Punkte, Verbleibende Punkte: %REMAINING",
"vcmi.capitalColors.0" : "Rot",
"vcmi.capitalColors.1" : "Blau",
"vcmi.capitalColors.2" : "Braun",
"vcmi.capitalColors.3" : "Grün",
"vcmi.capitalColors.4" : "Orange",
"vcmi.capitalColors.5" : "Violett",
"vcmi.capitalColors.6" : "Türkis",
"vcmi.capitalColors.7" : "Rosa",
"vcmi.heroOverview.startingArmy" : "Starteinheiten",
"vcmi.heroOverview.warMachine" : "Kriegsmaschinen",
"vcmi.heroOverview.secondarySkills" : "Sekundäre Skills",
"vcmi.heroOverview.spells" : "Zaubersprüche",
"vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen",
"vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen",
"vcmi.radialWheel.splitUnitEqually" : "Gleichmäßiges trennen der Kreaturen",
"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",
"vcmi.radialWheel.splitUnit" : "Aufsplitten der Kreatur in anderen Slot",
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen",
"vcmi.mainMenu.serverClosing" : "Trenne...",
"vcmi.mainMenu.hostTCP" : "Hoste TCP/IP Spiel",
"vcmi.mainMenu.joinTCP" : "Trete TCP/IP Spiel bei",
"vcmi.mainMenu.playerName" : "Spieler",
"vcmi.lobby.filepath" : "Dateipfad",
"vcmi.lobby.creationDate" : "Erstellungsdatum",
"vcmi.lobby.scenarioName" : "Szenario-Name",
"vcmi.lobby.mapPreview" : "Kartenvorschau",
"vcmi.lobby.noPreview" : "Keine Vorschau",
"vcmi.lobby.noUnderground" : "Kein Untergrund",
"vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
"vcmi.server.errors.modsToEnable" : "{Erforderliche Mods um das Spiel zu laden}",
"vcmi.server.confirmReconnect" : "Mit der letzten Sitzung verbinden?",
"vcmi.settingsMainWindow.generalTab.hover" : "Allgemein",
"vcmi.settingsMainWindow.generalTab.help" : "Wechselt zur Registerkarte Allgemeine Optionen, die Einstellungen zum allgemeinen Verhalten des Spielclients enthält.",
"vcmi.settingsMainWindow.battleTab.hover" : "Kampf",
"vcmi.settingsMainWindow.battleTab.help" : "Wechselt zur Registerkarte Kampfoptionen, auf der das Spielverhalten während eines Kampfes konfiguriert werden kann.",
"vcmi.settingsMainWindow.adventureTab.hover" : "Abenteuer-Karte",
"vcmi.settingsMainWindow.adventureTab.help" : "Wechselt zur Registerkarte Abenteuerkartenoptionen - die Abenteuerkarte ist der Teil des Spiels, in dem du deine Helden bewegen kannst.",
"vcmi.systemOptions.videoGroup" : "Video-Einstellungen",
"vcmi.systemOptions.audioGroup" : "Audio-Einstellungen",
"vcmi.systemOptions.otherGroup" : "Andere Einstellungen", // unused right now
"vcmi.systemOptions.townsGroup" : "Stadt-Bildschirm",
"vcmi.systemOptions.fullscreenBorderless.hover" : "Vollbild (randlos)",
"vcmi.systemOptions.fullscreenBorderless.help" : "{Randloses Vollbild}\n\nWenn diese Option ausgewählt ist, wird VCMI im randlosen Vollbildmodus ausgeführt. In diesem Modus wird das Spiel immer dieselbe Auflösung wie der Desktop verwenden und die gewählte Auflösung ignorieren.",
"vcmi.systemOptions.fullscreenExclusive.hover" : "Vollbild (exklusiv)",
"vcmi.systemOptions.fullscreenExclusive.help" : "{Vollbild}\n\nWenn diese Option ausgewählt ist, wird VCMI im exklusiven Vollbildmodus ausgeführt. In diesem Modus ändert das Spiel die Auflösung des Monitors auf die ausgewählte Auflösung.",
"vcmi.systemOptions.resolutionButton.hover" : "Auflösung: %wx%h",
"vcmi.systemOptions.resolutionButton.help" : "{Wähle Auflösung}\n\n Ändert die Spielauflösung.",
"vcmi.systemOptions.resolutionMenu.hover" : "Wähle Auflösung",
"vcmi.systemOptions.resolutionMenu.help" : "Ändere die Spielauflösung.",
"vcmi.systemOptions.scalingButton.hover" : "Interface-Skalierung: %p%",
"vcmi.systemOptions.scalingButton.help" : "{Interface-Skalierung}\n\nÄndern der Skalierung des Interfaces im Spiel",
"vcmi.systemOptions.scalingMenu.hover" : "Skalierung des Interfaces auswählen",
"vcmi.systemOptions.scalingMenu.help" : "Ändern der Skalierung des Interfaces im Spiel.",
"vcmi.systemOptions.longTouchButton.hover" : "Berührungsdauer für langer Touch: %d ms", // Translation note: "ms" = "milliseconds"
"vcmi.systemOptions.longTouchButton.help" : "{Berührungsdauer für langer Touch}\n\nBei Verwendung des Touchscreens erscheinen Popup-Fenster nach Berührung des Bildschirms für die angegebene Dauer (in Millisekunden)",
"vcmi.systemOptions.longTouchMenu.hover" : "Wähle Berührungsdauer für langer Touch",
"vcmi.systemOptions.longTouchMenu.help" : "Ändere die Berührungsdauer für den langen Touch",
"vcmi.systemOptions.longTouchMenu.entry" : "%d Millisekunden",
"vcmi.systemOptions.framerateButton.hover" : "FPS anzeigen",
"vcmi.systemOptions.framerateButton.help" : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.",
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Haptisches Feedback",
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Haptisches Feedback}\n\nHaptisches Feedback bei Touch-Eingaben.",
"vcmi.systemOptions.enableUiEnhancementsButton.hover" : "Interface Verbesserungen",
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Verbesserungen}\n\nSchaltet verschiedene Interface Verbesserungen um. Wie z.B. ein größeres Zauberbuch, Rucksack, etc. Deaktivieren, um ein klassischeres Erlebnis zu haben.",
"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",
"vcmi.adventureOptions.numericQuantities.hover" : "Numerische Kreaturenmengen",
"vcmi.adventureOptions.numericQuantities.help" : "{Numerische Kreaturenmengen}\n\n Zeigt die ungefähre Menge der feindlichen Kreaturen im numerischen Format A-B an.",
"vcmi.adventureOptions.forceMovementInfo.hover" : "Bewegungskosten immer anzeigen",
"vcmi.adventureOptions.forceMovementInfo.help" : "{Bewegungskosten immer anzeigen}\n\n Ersetzt die Standardinformationen in der Statusleiste durch die Daten der Bewegungspunkte, ohne dass die ALT-Taste gedrückt werden muss.",
"vcmi.adventureOptions.showGrid.hover" : "Raster anzeigen",
"vcmi.adventureOptions.showGrid.help" : "{Raster anzeigen}\n\n Zeigt eine Rasterüberlagerung, die die Grenzen zwischen den Kacheln der Abenteuerkarte anzeigt.",
"vcmi.adventureOptions.borderScroll.hover" : "Scrollen am Rand",
"vcmi.adventureOptions.borderScroll.help" : "{Scrollen am Rand}\n\nScrollt die Abenteuerkarte, wenn sich der Cursor neben dem Fensterrand befindet. Kann mit gedrückter STRG-Taste deaktiviert werden.",
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info-Panel Kreaturenmanagement",
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info-Panel Kreaturenmanagement}\n\nErmöglicht die Neuanordnung von Kreaturen im Info-Panel, anstatt zwischen den Standardkomponenten zu wechseln",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Ziehen der Karte mit Links",
"vcmi.adventureOptions.leftButtonDrag.help" : "{Ziehen der Karte mit Links}\n\nWenn aktiviert, wird die Maus bei gedrückter linker Taste in die Kartenansicht gezogen",
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
"vcmi.adventureOptions.mapScrollSpeed1.help": "Geschwindigkeit des Kartenbildlaufs auf sehr langsam einstellen",
"vcmi.adventureOptions.mapScrollSpeed5.help": "Geschwindigkeit des Kartenbildlaufs auf sehr schnell einstellen",
"vcmi.adventureOptions.mapScrollSpeed6.help": "Geschwindigkeit des Kartenbildlaufs auf sofort einstellen",
"vcmi.battleOptions.queueSizeLabel.hover": "Reihenfolge der Kreaturen anzeigen",
"vcmi.battleOptions.queueSizeNoneButton.hover": "AUS",
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
"vcmi.battleOptions.queueSizeSmallButton.hover": "KLEIN",
"vcmi.battleOptions.queueSizeBigButton.hover": "GROß",
"vcmi.battleOptions.queueSizeNoneButton.help": "Vollständige Deaktivierung der Sichtbarkeit der Reihenfolge der Kreaturen im Kampf",
"vcmi.battleOptions.queueSizeAutoButton.help": "Stellt die Größe der Zugreihenfolge abhängig von der Spielauflösung ein (klein, wenn mit einer Bildschirmauflösung unter 700 Pixeln gespielt wird, ansonsten groß)",
"vcmi.battleOptions.queueSizeSmallButton.help": "Setzt die Zugreihenfolge auf klein",
"vcmi.battleOptions.queueSizeBigButton.help": "Setzt die Größe der Zugreihenfolge auf groß (nicht unterstützt, wenn die Spielauflösung weniger als 700 Pixel hoch ist)",
"vcmi.battleOptions.animationsSpeed1.hover": "",
"vcmi.battleOptions.animationsSpeed5.hover": "",
"vcmi.battleOptions.animationsSpeed6.hover": "",
"vcmi.battleOptions.animationsSpeed1.help": "Setzt die Animationsgeschwindigkeit auf sehr langsam",
"vcmi.battleOptions.animationsSpeed5.help": "Setzt die Animationsgeschwindigkeit auf sehr schnell",
"vcmi.battleOptions.animationsSpeed6.help": "Setzt die Animationsgeschwindigkeit auf sofort",
"vcmi.battleOptions.movementHighlightOnHover.hover": "Hervorhebung der Bewegung bei Hover",
"vcmi.battleOptions.movementHighlightOnHover.help": "{Hervorhebung der Bewegung bei Hover}\n\nHebt die Bewegungsreichweite der Einheit hervor, wenn man mit dem Mauszeiger über sie fährt.",
"vcmi.battleOptions.rangeLimitHighlightOnHover.hover": "Bereichsgrenzen für Schützen anzeigen",
"vcmi.battleOptions.rangeLimitHighlightOnHover.help": "{Bereichsgrenzen für Schützen anzeigen}\n\nZeigt die Entfernungsgrenzen des Schützen an, wenn man mit dem Mauszeiger über ihn fährt.",
"vcmi.battleOptions.showStickyHeroInfoWindows.hover": "Statistikfenster für Helden anzeigen",
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Statistikfenster für Helden anzeigen}\n\nDauerhaftes Einschalten des Statistikfenster für Helden, das die primären Werte und Zauberpunkte anzeigt.",
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Intro-Musik überspringen",
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Intro-Musik überspringen}\n\n Überspringe die kurze Musik, die zu Beginn eines jeden Kampfes gespielt wird, bevor die Action beginnt. Kann auch durch Drücken der ESC-Taste übersprungen werden.",
"vcmi.battleWindow.pressKeyToSkipIntro" : "Beliebige Taste drücken, um das Kampf-Intro zu überspringen",
"vcmi.battleWindow.damageEstimation.melee" : "Angriff auf %CREATURE (%DAMAGE).",
"vcmi.battleWindow.damageEstimation.meleeKills" : "Angriff auf %CREATURE (%DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.ranged" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE).",
"vcmi.battleWindow.damageEstimation.rangedKills" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.shots" : "%d Schüsse verbleibend",
"vcmi.battleWindow.damageEstimation.shots.1" : "%d Schüsse verbleibend",
"vcmi.battleWindow.damageEstimation.damage" : "%d Schaden",
"vcmi.battleWindow.damageEstimation.damage.1" : "%d Schaden",
"vcmi.battleWindow.damageEstimation.kills" : "%d werden verenden",
"vcmi.battleWindow.damageEstimation.kills.1" : "%d werden verenden",
"vcmi.battleResultsWindow.applyResultsLabel" : "Kampfergebnis übernehmen",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Verfügbare Kreaturen anzeigen",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Verfügbare Kreaturen anzeigen}\n\n Zeigt in der Stadtübersicht (linke untere Ecke) die zum Kauf verfügbaren Kreaturen anstelle ihres Wachstums an.",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Wöchentl. Wachstum der Kreaturen anz.",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Wöchentliches Wachstum der Kreaturen anzeigen}\n\n Zeigt das wöchentliche Wachstum der Kreaturen anstelle der verfügbaren Menge in der Stadtübersicht (unten links).",
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Kompakte Kreatur-Infos",
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Kompakte Kreatur-Infos}\n\n Kleinere Stadt-Kreaturen Informationen in der Stadtübersicht.",
"vcmi.townHall.missingBase" : "Basis Gebäude %s muss als erstes gebaut werden",
"vcmi.townHall.noCreaturesToRecruit" : "Es gibt keine Kreaturen zu rekrutieren!",
"vcmi.townHall.greetingManaVortex" : "Wenn Ihr Euch den %s nähert, wird Euer Körper mit neuer Energie gefüllt. Ihr habt Eure normalen Zauberpunkte verdoppelt.",
"vcmi.townHall.greetingKnowledge" : "Ihr studiert die Glyphen auf dem %s und erhaltet Einblick in die Funktionsweise verschiedener Magie (+1 Wissen).",
"vcmi.townHall.greetingSpellPower" : "Der %s lehrt Euch neue Wege, Eure magischen Kräfte zu bündeln (+1 Kraft).",
"vcmi.townHall.greetingExperience" : "Ein Besuch bei den %s bringt Euch viele neue Fähigkeiten bei (+1000 Erfahrung).",
"vcmi.townHall.greetingAttack" : "Nach einiger Zeit im %s könnt Ihr effizientere Kampffertigkeiten erlernen (+1 Angriffsfertigkeit).",
"vcmi.townHall.greetingDefence" : "Wenn Ihr Zeit im %s verbringt, bringen Euch die erfahrenen Krieger dort zusätzliche Verteidigungsfähigkeiten bei (+1 Verteidigung).",
"vcmi.townHall.hasNotProduced" : "Die %s hat noch nichts produziert.",
"vcmi.townHall.hasProduced" : "Die %s hat diese Woche %d %s produziert.",
"vcmi.townHall.greetingCustomBonus" : "%s gibt Ihnen +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " bis zur nächsten Schlacht.",
"vcmi.townHall.greetingInTownMagicWell" : "%s hat Eure Zauberpunkte wieder auf das Maximum erhöht.",
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
"vcmi.heroWindow.openCommander.hover" : "Öffne Kommandanten-Fenster",
"vcmi.heroWindow.openCommander.help" : "Zeige Informationen über Kommandanten dieses Helden",
"vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?",
"vcmi.creatureWindow.showBonuses.hover" : "Wechsle zur Bonus-Ansicht",
"vcmi.creatureWindow.showBonuses.help" : "Zeige alle aktiven Boni des Kommandanten",
"vcmi.creatureWindow.showSkills.hover" : "Wechsle zur Fertigkeits-Ansicht",
"vcmi.creatureWindow.showSkills.help" : "Zeige alle erlernten Fertigkeiten des Kommandanten",
"vcmi.creatureWindow.returnArtifact.hover" : "Artefekt zurückgeben",
"vcmi.creatureWindow.returnArtifact.help" : "Nutze diese Schaltfläche, um Stapel-Artefakt in den Rucksack des Helden zurückzugeben",
"vcmi.questLog.hideComplete.hover" : "Verstecke abgeschlossene Quests",
"vcmi.questLog.hideComplete.help" : "Verstecke alle Quests die bereits abgeschlossen sind",
"vcmi.randomMapTab.widgets.randomTemplate" : "(Zufällig)",
"vcmi.randomMapTab.widgets.templateLabel" : "Template",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Einrichtung...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team-Zuordnungen",
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Straßentypen",
// Custom victory conditions for H3 campaigns and HotA maps
"vcmi.map.victoryCondition.daysPassed.toOthers" : "Der Feind hat es geschafft, bis zum heutigen Tag zu überleben. Der Sieg gehört ihm!",
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Herzlichen Glückwunsch! Ihr habt es geschafft, zu überleben. Der Sieg ist euer!",
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "Der Feind hat alle Monster besiegt, die das Land heimsuchen, und fordert den Sieg!",
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Herzlichen Glückwunsch! Ihr habt alle Monster besiegt, die dieses Land plagen, und könnt den Sieg für euch beanspruchen!",
"vcmi.map.victoryCondition.collectArtifacts.message" : "Sammelt drei Artefakte",
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Herzlichen Glückwunsch! Alle eure Feinde wurden besiegt und ihr habt die Engelsallianz! Der Sieg ist euer!",
"vcmi.map.victoryCondition.angelicAlliance.message" : "Besiege alle Feinde und gründe eine Engelsallianz",
// few strings from WoG used by vcmi
"vcmi.stackExperience.description" : "» D e t a i l s z u r S t a p e l e r f a h r u n g «\n\nKreatur-Typ ................... : %s\nErfahrungsrang ................. : %s (%i)\nErfahrungspunkte ............... : %i\nErfahrungspunkte für den nächsten Rang .. : %i\nMaximale Erfahrung pro Kampf ... : %i%% (%i)\nAnzahl der Kreaturen im Stapel .... : %i\nMaximale Anzahl neuer Rekruten\n ohne Verlust von aktuellem Rang .... : %i\nErfahrungs-Multiplikator ........... : %.2f\nUpgrade-Multiplikator .............. : %.2f\nErfahrung nach Rang 10 ........ : %i\nMaximale Anzahl der neuen Rekruten, die bei\n Rang 10 bei maximaler Erfahrung übrig sind : %i",
"vcmi.stackExperience.rank.0" : "Grundlagen",
"vcmi.stackExperience.rank.1" : "Neuling",
"vcmi.stackExperience.rank.2" : "Ausgebildet",
"vcmi.stackExperience.rank.3" : "Kompetent",
"vcmi.stackExperience.rank.4" : "Bewährt",
"vcmi.stackExperience.rank.5" : "Veteran",
"vcmi.stackExperience.rank.6" : "Gekonnt",
"vcmi.stackExperience.rank.7" : "Experte",
"vcmi.stackExperience.rank.8" : "Elite",
"vcmi.stackExperience.rank.9" : "Meister",
"vcmi.stackExperience.rank.10" : "Ass",
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
"core.bonus.ADDITIONAL_RETALIATION.description": "Kann ${val} zusätzliche Male vergelten",
"core.bonus.AIR_IMMUNITY.name": "Luftimmunität",
"core.bonus.AIR_IMMUNITY.description": "Immun gegen alle Luftschulzauber",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Rundum angreifen",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Greift alle benachbarten Gegner an",
"core.bonus.BLOCKS_RETALIATION.name": "Keine Vergeltung",
"core.bonus.BLOCKS_RETALIATION.description": "Feind kann nicht vergelten",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Keine Reichweitenverschiebung",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Feind kann nicht durch Schießen vergelten",
"core.bonus.CATAPULT.name": "Katapult",
"core.bonus.CATAPULT.description": "Greift Belagerungsmauern an",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduziere Zauberkosten (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduziert die Zauberkosten für den Helden",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Zauberdämpfer (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Erhöht die Kosten von gegnerischen Zaubern",
"core.bonus.CHARGE_IMMUNITY.name": "Immun gegen Aufladung",
"core.bonus.CHARGE_IMMUNITY.description": "Immun gegen Aufladung",
"core.bonus.DARKNESS.name": "Abdeckung der Dunkelheit",
"core.bonus.DARKNESS.description": "Fügt ${val} Dunkelheitsradius hinzu",
"core.bonus.DEATH_STARE.name": "Todesstarren (${val}%)",
"core.bonus.DEATH_STARE.description": "${val}% Chance, eine einzelne Kreatur zu töten",
"core.bonus.DEFENSIVE_STANCE.name": "Verteidigungsbonus",
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Verteidigung beim Verteidigen",
"core.bonus.DESTRUCTION.name": "Zerstörung",
"core.bonus.DESTRUCTION.description": "Hat ${val}% Chance, zusätzliche Einheiten nach dem Angriff zu töten",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Todesstoß",
"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.EARTH_IMMUNITY.name": "Erdimmunität",
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
"core.bonus.ENCHANTER.name": "Verzauberer",
"core.bonus.ENCHANTER.description": "Kann jede Runde eine Masse von ${subtype.spell} zaubern",
"core.bonus.ENCHANTED.name": "Verzaubert",
"core.bonus.ENCHANTED.description": "Beeinflusst von permanentem ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignoriere Verteidigung (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignoriert einen Teil der Verteidigung für den Angriff",
"core.bonus.FIRE_IMMUNITY.name": "Feuerimmunität",
"core.bonus.FIRE_IMMUNITY.description": "Immun gegen alle Zauber der Schule des Feuers",
"core.bonus.FIRE_SHIELD.name": "Feuerschild (${val}%)",
"core.bonus.FIRE_SHIELD.description": "Reflektiert einen Teil des Nahkampfschadens",
"core.bonus.FIRST_STRIKE.name": "Erstschlag",
"core.bonus.FIRST_STRIKE.description": "Diese Kreatur greift zuerst an, anstatt zu vergelten",
"core.bonus.FEAR.name": "Furcht",
"core.bonus.FEAR.description": "Verursacht Furcht bei einem gegnerischen Stapel",
"core.bonus.FEARLESS.name": "Furchtlos",
"core.bonus.FEARLESS.description": "immun gegen die Fähigkeit Furcht",
"core.bonus.FLYING.name": "Fliegen",
"core.bonus.FLYING.description": "Kann fliegen (ignoriert Hindernisse)",
"core.bonus.FREE_SHOOTING.name": "Nah schießen",
"core.bonus.FREE_SHOOTING.description": "Kann im Nahkampf schießen",
"core.bonus.GARGOYLE.name": "Gargoyle",
"core.bonus.GARGOYLE.description": "Kann nicht aufgerichtet oder geheilt werden",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Schaden vermindern (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduziert physischen Schaden aus dem Fern- oder Nahkampf",
"core.bonus.HATE.name": "Hasst ${subtype.creature}",
"core.bonus.HATE.description": "Macht ${val}% mehr Schaden",
"core.bonus.HEALER.name": "Heiler",
"core.bonus.HEALER.description": "Heilt verbündete Einheiten",
"core.bonus.HP_REGENERATION.name": "Regeneration",
"core.bonus.HP_REGENERATION.description": "Heilt ${val} Trefferpunkte jede Runde",
"core.bonus.JOUSTING.name": "Champion Charge",
"core.bonus.JOUSTING.description": "+${val}% Schaden pro zurückgelegtem Feld",
"core.bonus.KING.name": "König",
"core.bonus.KING.description": "Anfällig für SLAYER Level ${val} oder höher",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Zauberimmunität 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immun gegen Zaubersprüche der Stufen 1-${val}",
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Begrenzte Schussweite",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Kann nicht auf Ziele schießen, die weiter als ${val} Felder entfernt sind",
"core.bonus.LIFE_DRAIN.name": "Leben entziehen (${val}%)",
"core.bonus.LIFE_DRAIN.description": "Drainiert ${val}% des zugefügten Schadens",
"core.bonus.MANA_CHANNELING.name": "Magiekanal ${val}%",
"core.bonus.MANA_CHANNELING.description": "Gibt Ihrem Helden Mana, das vom Gegner ausgegeben wird",
"core.bonus.MANA_DRAIN.name": "Mana-Entzug",
"core.bonus.MANA_DRAIN.description": "Entzieht ${val} Mana jede Runde",
"core.bonus.MAGIC_MIRROR.name": "Zauberspiegel (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "${val}% Chance, einen Angriffszauber auf den Gegner umzulenken",
"core.bonus.MAGIC_RESISTANCE.name": "Magie-Widerstand(${val}%)",
"core.bonus.MAGIC_RESISTANCE.description": "${val}% Chance, gegnerischem Zauber zu widerstehen",
"core.bonus.MIND_IMMUNITY.name": "Geist-Zauber-Immunität",
"core.bonus.MIND_IMMUNITY.description": "Immun gegen Zauber vom Typ Geist",
"core.bonus.NO_DISTANCE_PENALTY.name": "Keine Entfernungsstrafe",
"core.bonus.NO_DISTANCE_PENALTY.description": "Voller Schaden aus beliebiger Entfernung",
"core.bonus.NO_MELEE_PENALTY.name": "Keine Nahkampf-Strafe",
"core.bonus.NO_MELEE_PENALTY.description": "Kreatur hat keinen Nahkampf-Malus",
"core.bonus.NO_MORALE.name": "Neutrale Moral",
"core.bonus.NO_MORALE.description": "Kreatur ist immun gegen Moral-Effekte",
"core.bonus.NO_WALL_PENALTY.name": "Keine Wand-Strafe",
"core.bonus.NO_WALL_PENALTY.description": "Voller Schaden bei Belagerung",
"core.bonus.NON_LIVING.name": "Nicht lebend",
"core.bonus.NON_LIVING.description": "Immunität gegen viele Effekte",
"core.bonus.RANDOM_SPELLCASTER.name": "Zufälliger Zauberwirker",
"core.bonus.RANDOM_SPELLCASTER.description": "Kann einen zufälligen Zauberspruch wirken",
"core.bonus.RANGED_RETALIATION.name": "Fernkampf-Vergeltung",
"core.bonus.RANGED_RETALIATION.description": "Kann einen Fernkampf-Gegenangriff durchführen",
"core.bonus.RECEPTIVE.name": "Empfänglich",
"core.bonus.RECEPTIVE.description": "Keine Immunität gegen Freundschaftszauber",
"core.bonus.REBIRTH.name": "Wiedergeburt (${val}%)",
"core.bonus.REBIRTH.description": "${val}% des Stacks wird nach dem Tod auferstehen",
"core.bonus.RETURN_AFTER_STRIKE.name": "Angriff und Rückkehr",
"core.bonus.RETURN_AFTER_STRIKE.description": "Kehrt nach Nahkampfangriff zurück",
"core.bonus.SHOOTER.name": "Fernkämpfer",
"core.bonus.SHOOTER.description": "Kreatur kann schießen",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Schießt rundherum",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Die Fernkampfangriffe dieser Kreatur treffen alle Ziele in einem kleinen Bereich",
"core.bonus.SOUL_STEAL.name": "Seelenraub",
"core.bonus.SOUL_STEAL.description": "Gewinnt ${val} neue Kreaturen für jeden getöteten Gegner",
"core.bonus.SPELLCASTER.name": "Zauberer",
"core.bonus.SPELLCASTER.description": "Kann ${subtype.spell} zaubern",
"core.bonus.SPELL_AFTER_ATTACK.name": "Nach Angriff zaubern",
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}%, um ${subtype.spell} nach dem Angriff zu wirken",
"core.bonus.SPELL_BEFORE_ATTACK.name": "Zauber vor Angriff",
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% um ${subtype.spell} vor dem Angriff zu wirken",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Schaden von Zaubern reduziert ${val}%.",
"core.bonus.SPELL_IMMUNITY.name": "Zauberimmunität",
"core.bonus.SPELL_IMMUNITY.description": "Immun gegen ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "zauberähnlicher Angriff",
"core.bonus.SPELL_LIKE_ATTACK.description": "Angriffe mit ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura des Widerstands",
"core.bonus.SPELL_RESISTANCE_AURA.description": "Stapel in der Nähe erhalten ${val}% Widerstand",
"core.bonus.SUMMON_GUARDIANS.name": "Wächter beschwören",
"core.bonus.SUMMON_GUARDIANS.description": "Beschwört bei Kampfbeginn ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "Synergierbar",
"core.bonus.SYNERGY_TARGET.description": "Diese Kreatur ist anfällig für Synergieeffekte",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Atem",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Atem-Angriff (2-Hex-Bereich)",
"core.bonus.THREE_HEADED_ATTACK.name": "Dreiköpfiger Angriff",
"core.bonus.THREE_HEADED_ATTACK.description": "Greift drei benachbarte Einheiten an",
"core.bonus.TRANSMUTATION.name": "Transmutation",
"core.bonus.TRANSMUTATION.description": "${val}% Chance, angegriffene Einheit in einen anderen Typ zu verwandeln",
"core.bonus.UNDEAD.name": "Untot",
"core.bonus.UNDEAD.description": "Kreatur ist untot",
"core.bonus.UNLIMITED_RETALIATIONS.name": "Unbegrenzte Vergeltungsmaßnahmen",
"core.bonus.UNLIMITED_RETALIATIONS.description": "Vergeltungen für eine beliebige Anzahl von Angriffen",
"core.bonus.WATER_IMMUNITY.name": "Wasser-Immunität",
"core.bonus.WATER_IMMUNITY.description": "Immun gegen alle Zauber der Wasserschule",
"core.bonus.WIDE_BREATH.name": "Breiter Atem",
"core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)"
}
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Bedrohung: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Mühelos",
"vcmi.adventureMap.monsterThreat.levels.1" : "Sehr schwach",
"vcmi.adventureMap.monsterThreat.levels.2" : "Schwach",
"vcmi.adventureMap.monsterThreat.levels.3" : "Ein bisschen schwächer",
"vcmi.adventureMap.monsterThreat.levels.4" : "Gleichauf",
"vcmi.adventureMap.monsterThreat.levels.5" : "Ein bisschen stärker",
"vcmi.adventureMap.monsterThreat.levels.6" : "Stark",
"vcmi.adventureMap.monsterThreat.levels.7" : "Sehr Stark",
"vcmi.adventureMap.monsterThreat.levels.8" : "Herausfordernd",
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
"vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich",
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich",
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
"vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!",
"vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.",
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
"vcmi.adventureMap.moveCostDetails" : "Bewegungspunkte - Kosten: %TURNS Runden + %POINTS Punkte, Verbleibende Punkte: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Bewegungspunkte - Kosten: %POINTS Punkte, Verbleibende Punkte: %REMAINING",
"vcmi.capitalColors.0" : "Rot",
"vcmi.capitalColors.1" : "Blau",
"vcmi.capitalColors.2" : "Braun",
"vcmi.capitalColors.3" : "Grün",
"vcmi.capitalColors.4" : "Orange",
"vcmi.capitalColors.5" : "Violett",
"vcmi.capitalColors.6" : "Türkis",
"vcmi.capitalColors.7" : "Rosa",
"vcmi.heroOverview.startingArmy" : "Starteinheiten",
"vcmi.heroOverview.warMachine" : "Kriegsmaschinen",
"vcmi.heroOverview.secondarySkills" : "Sekundäre Skills",
"vcmi.heroOverview.spells" : "Zaubersprüche",
"vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen",
"vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen",
"vcmi.radialWheel.splitUnitEqually" : "Gleichmäßiges trennen der Kreaturen",
"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",
"vcmi.radialWheel.splitUnit" : "Aufsplitten der Kreatur in anderen Slot",
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen",
"vcmi.mainMenu.serverClosing" : "Trenne...",
"vcmi.mainMenu.hostTCP" : "Hoste TCP/IP Spiel",
"vcmi.mainMenu.joinTCP" : "Trete TCP/IP Spiel bei",
"vcmi.mainMenu.playerName" : "Spieler",
"vcmi.lobby.filepath" : "Dateipfad",
"vcmi.lobby.creationDate" : "Erstellungsdatum",
"vcmi.lobby.scenarioName" : "Szenario-Name",
"vcmi.lobby.mapPreview" : "Kartenvorschau",
"vcmi.lobby.noPreview" : "Keine Vorschau",
"vcmi.lobby.noUnderground" : "Kein Untergrund",
"vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
"vcmi.server.errors.modsToEnable" : "{Erforderliche Mods um das Spiel zu laden}",
"vcmi.server.confirmReconnect" : "Mit der letzten Sitzung verbinden?",
"vcmi.settingsMainWindow.generalTab.hover" : "Allgemein",
"vcmi.settingsMainWindow.generalTab.help" : "Wechselt zur Registerkarte Allgemeine Optionen, die Einstellungen zum allgemeinen Verhalten des Spielclients enthält.",
"vcmi.settingsMainWindow.battleTab.hover" : "Kampf",
"vcmi.settingsMainWindow.battleTab.help" : "Wechselt zur Registerkarte Kampfoptionen, auf der das Spielverhalten während eines Kampfes konfiguriert werden kann.",
"vcmi.settingsMainWindow.adventureTab.hover" : "Abenteuer-Karte",
"vcmi.settingsMainWindow.adventureTab.help" : "Wechselt zur Registerkarte Abenteuerkartenoptionen - die Abenteuerkarte ist der Teil des Spiels, in dem du deine Helden bewegen kannst.",
"vcmi.systemOptions.videoGroup" : "Video-Einstellungen",
"vcmi.systemOptions.audioGroup" : "Audio-Einstellungen",
"vcmi.systemOptions.otherGroup" : "Andere Einstellungen", // unused right now
"vcmi.systemOptions.townsGroup" : "Stadt-Bildschirm",
"vcmi.systemOptions.fullscreenBorderless.hover" : "Vollbild (randlos)",
"vcmi.systemOptions.fullscreenBorderless.help" : "{Randloses Vollbild}\n\nWenn diese Option ausgewählt ist, wird VCMI im randlosen Vollbildmodus ausgeführt. In diesem Modus wird das Spiel immer dieselbe Auflösung wie der Desktop verwenden und die gewählte Auflösung ignorieren.",
"vcmi.systemOptions.fullscreenExclusive.hover" : "Vollbild (exklusiv)",
"vcmi.systemOptions.fullscreenExclusive.help" : "{Vollbild}\n\nWenn diese Option ausgewählt ist, wird VCMI im exklusiven Vollbildmodus ausgeführt. In diesem Modus ändert das Spiel die Auflösung des Monitors auf die ausgewählte Auflösung.",
"vcmi.systemOptions.resolutionButton.hover" : "Auflösung: %wx%h",
"vcmi.systemOptions.resolutionButton.help" : "{Wähle Auflösung}\n\n Ändert die Spielauflösung.",
"vcmi.systemOptions.resolutionMenu.hover" : "Wähle Auflösung",
"vcmi.systemOptions.resolutionMenu.help" : "Ändere die Spielauflösung.",
"vcmi.systemOptions.scalingButton.hover" : "Interface-Skalierung: %p%",
"vcmi.systemOptions.scalingButton.help" : "{Interface-Skalierung}\n\nÄndern der Skalierung des Interfaces im Spiel",
"vcmi.systemOptions.scalingMenu.hover" : "Skalierung des Interfaces auswählen",
"vcmi.systemOptions.scalingMenu.help" : "Ändern der Skalierung des Interfaces im Spiel.",
"vcmi.systemOptions.longTouchButton.hover" : "Berührungsdauer für langer Touch: %d ms", // Translation note: "ms" = "milliseconds"
"vcmi.systemOptions.longTouchButton.help" : "{Berührungsdauer für langer Touch}\n\nBei Verwendung des Touchscreens erscheinen Popup-Fenster nach Berührung des Bildschirms für die angegebene Dauer (in Millisekunden)",
"vcmi.systemOptions.longTouchMenu.hover" : "Wähle Berührungsdauer für langer Touch",
"vcmi.systemOptions.longTouchMenu.help" : "Ändere die Berührungsdauer für den langen Touch",
"vcmi.systemOptions.longTouchMenu.entry" : "%d Millisekunden",
"vcmi.systemOptions.framerateButton.hover" : "FPS anzeigen",
"vcmi.systemOptions.framerateButton.help" : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.",
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Haptisches Feedback",
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Haptisches Feedback}\n\nHaptisches Feedback bei Touch-Eingaben.",
"vcmi.systemOptions.enableUiEnhancementsButton.hover" : "Interface Verbesserungen",
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Verbesserungen}\n\nSchaltet verschiedene Interface Verbesserungen um. Wie z.B. ein größeres Zauberbuch, Rucksack, etc. Deaktivieren, um ein klassischeres Erlebnis zu haben.",
"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",
"vcmi.adventureOptions.numericQuantities.hover" : "Numerische Kreaturenmengen",
"vcmi.adventureOptions.numericQuantities.help" : "{Numerische Kreaturenmengen}\n\n Zeigt die ungefähre Menge der feindlichen Kreaturen im numerischen Format A-B an.",
"vcmi.adventureOptions.forceMovementInfo.hover" : "Bewegungskosten immer anzeigen",
"vcmi.adventureOptions.forceMovementInfo.help" : "{Bewegungskosten immer anzeigen}\n\n Ersetzt die Standardinformationen in der Statusleiste durch die Daten der Bewegungspunkte, ohne dass die ALT-Taste gedrückt werden muss.",
"vcmi.adventureOptions.showGrid.hover" : "Raster anzeigen",
"vcmi.adventureOptions.showGrid.help" : "{Raster anzeigen}\n\n Zeigt eine Rasterüberlagerung, die die Grenzen zwischen den Kacheln der Abenteuerkarte anzeigt.",
"vcmi.adventureOptions.borderScroll.hover" : "Scrollen am Rand",
"vcmi.adventureOptions.borderScroll.help" : "{Scrollen am Rand}\n\nScrollt die Abenteuerkarte, wenn sich der Cursor neben dem Fensterrand befindet. Kann mit gedrückter STRG-Taste deaktiviert werden.",
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info-Panel Kreaturenmanagement",
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info-Panel Kreaturenmanagement}\n\nErmöglicht die Neuanordnung von Kreaturen im Info-Panel, anstatt zwischen den Standardkomponenten zu wechseln",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Ziehen der Karte mit Links",
"vcmi.adventureOptions.leftButtonDrag.help" : "{Ziehen der Karte mit Links}\n\nWenn aktiviert, wird die Maus bei gedrückter linker Taste in die Kartenansicht gezogen",
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
"vcmi.adventureOptions.mapScrollSpeed1.help": "Geschwindigkeit des Kartenbildlaufs auf sehr langsam einstellen",
"vcmi.adventureOptions.mapScrollSpeed5.help": "Geschwindigkeit des Kartenbildlaufs auf sehr schnell einstellen",
"vcmi.adventureOptions.mapScrollSpeed6.help": "Geschwindigkeit des Kartenbildlaufs auf sofort einstellen",
"vcmi.battleOptions.queueSizeLabel.hover": "Reihenfolge der Kreaturen anzeigen",
"vcmi.battleOptions.queueSizeNoneButton.hover": "AUS",
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
"vcmi.battleOptions.queueSizeSmallButton.hover": "KLEIN",
"vcmi.battleOptions.queueSizeBigButton.hover": "GROß",
"vcmi.battleOptions.queueSizeNoneButton.help": "Vollständige Deaktivierung der Sichtbarkeit der Reihenfolge der Kreaturen im Kampf",
"vcmi.battleOptions.queueSizeAutoButton.help": "Stellt die Größe der Zugreihenfolge abhängig von der Spielauflösung ein (klein, wenn mit einer Bildschirmauflösung unter 700 Pixeln gespielt wird, ansonsten groß)",
"vcmi.battleOptions.queueSizeSmallButton.help": "Setzt die Zugreihenfolge auf klein",
"vcmi.battleOptions.queueSizeBigButton.help": "Setzt die Größe der Zugreihenfolge auf groß (nicht unterstützt, wenn die Spielauflösung weniger als 700 Pixel hoch ist)",
"vcmi.battleOptions.animationsSpeed1.hover": "",
"vcmi.battleOptions.animationsSpeed5.hover": "",
"vcmi.battleOptions.animationsSpeed6.hover": "",
"vcmi.battleOptions.animationsSpeed1.help": "Setzt die Animationsgeschwindigkeit auf sehr langsam",
"vcmi.battleOptions.animationsSpeed5.help": "Setzt die Animationsgeschwindigkeit auf sehr schnell",
"vcmi.battleOptions.animationsSpeed6.help": "Setzt die Animationsgeschwindigkeit auf sofort",
"vcmi.battleOptions.movementHighlightOnHover.hover": "Hervorhebung der Bewegung bei Hover",
"vcmi.battleOptions.movementHighlightOnHover.help": "{Hervorhebung der Bewegung bei Hover}\n\nHebt die Bewegungsreichweite der Einheit hervor, wenn man mit dem Mauszeiger über sie fährt.",
"vcmi.battleOptions.rangeLimitHighlightOnHover.hover": "Bereichsgrenzen für Schützen anzeigen",
"vcmi.battleOptions.rangeLimitHighlightOnHover.help": "{Bereichsgrenzen für Schützen anzeigen}\n\nZeigt die Entfernungsgrenzen des Schützen an, wenn man mit dem Mauszeiger über ihn fährt.",
"vcmi.battleOptions.showStickyHeroInfoWindows.hover": "Statistikfenster für Helden anzeigen",
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Statistikfenster für Helden anzeigen}\n\nDauerhaftes Einschalten des Statistikfenster für Helden, das die primären Werte und Zauberpunkte anzeigt.",
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Intro-Musik überspringen",
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Intro-Musik überspringen}\n\n Überspringe die kurze Musik, die zu Beginn eines jeden Kampfes gespielt wird, bevor die Action beginnt. Kann auch durch Drücken der ESC-Taste übersprungen werden.",
"vcmi.battleWindow.pressKeyToSkipIntro" : "Beliebige Taste drücken, um das Kampf-Intro zu überspringen",
"vcmi.battleWindow.damageEstimation.melee" : "Angriff auf %CREATURE (%DAMAGE).",
"vcmi.battleWindow.damageEstimation.meleeKills" : "Angriff auf %CREATURE (%DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.ranged" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE).",
"vcmi.battleWindow.damageEstimation.rangedKills" : "Schuss auf %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.shots" : "%d Schüsse verbleibend",
"vcmi.battleWindow.damageEstimation.shots.1" : "%d Schüsse verbleibend",
"vcmi.battleWindow.damageEstimation.damage" : "%d Schaden",
"vcmi.battleWindow.damageEstimation.damage.1" : "%d Schaden",
"vcmi.battleWindow.damageEstimation.kills" : "%d werden verenden",
"vcmi.battleWindow.damageEstimation.kills.1" : "%d werden verenden",
"vcmi.battleResultsWindow.applyResultsLabel" : "Kampfergebnis übernehmen",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Verfügbare Kreaturen anzeigen",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Verfügbare Kreaturen anzeigen}\n\n Zeigt in der Stadtübersicht (linke untere Ecke) die zum Kauf verfügbaren Kreaturen anstelle ihres Wachstums an.",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Wöchentl. Wachstum der Kreaturen anz.",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Wöchentliches Wachstum der Kreaturen anzeigen}\n\n Zeigt das wöchentliche Wachstum der Kreaturen anstelle der verfügbaren Menge in der Stadtübersicht (unten links).",
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Kompakte Kreatur-Infos",
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Kompakte Kreatur-Infos}\n\n Kleinere Stadt-Kreaturen Informationen in der Stadtübersicht.",
"vcmi.townHall.missingBase" : "Basis Gebäude %s muss als erstes gebaut werden",
"vcmi.townHall.noCreaturesToRecruit" : "Es gibt keine Kreaturen zu rekrutieren!",
"vcmi.townHall.greetingManaVortex" : "Wenn Ihr Euch den %s nähert, wird Euer Körper mit neuer Energie gefüllt. Ihr habt Eure normalen Zauberpunkte verdoppelt.",
"vcmi.townHall.greetingKnowledge" : "Ihr studiert die Glyphen auf dem %s und erhaltet Einblick in die Funktionsweise verschiedener Magie (+1 Wissen).",
"vcmi.townHall.greetingSpellPower" : "Der %s lehrt Euch neue Wege, Eure magischen Kräfte zu bündeln (+1 Kraft).",
"vcmi.townHall.greetingExperience" : "Ein Besuch bei den %s bringt Euch viele neue Fähigkeiten bei (+1000 Erfahrung).",
"vcmi.townHall.greetingAttack" : "Nach einiger Zeit im %s könnt Ihr effizientere Kampffertigkeiten erlernen (+1 Angriffsfertigkeit).",
"vcmi.townHall.greetingDefence" : "Wenn Ihr Zeit im %s verbringt, bringen Euch die erfahrenen Krieger dort zusätzliche Verteidigungsfähigkeiten bei (+1 Verteidigung).",
"vcmi.townHall.hasNotProduced" : "Die %s hat noch nichts produziert.",
"vcmi.townHall.hasProduced" : "Die %s hat diese Woche %d %s produziert.",
"vcmi.townHall.greetingCustomBonus" : "%s gibt Ihnen +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " bis zur nächsten Schlacht.",
"vcmi.townHall.greetingInTownMagicWell" : "%s hat Eure Zauberpunkte wieder auf das Maximum erhöht.",
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
"vcmi.heroWindow.openCommander.hover" : "Öffne Kommandanten-Fenster",
"vcmi.heroWindow.openCommander.help" : "Zeige Informationen über Kommandanten dieses Helden",
"vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?",
"vcmi.creatureWindow.showBonuses.hover" : "Wechsle zur Bonus-Ansicht",
"vcmi.creatureWindow.showBonuses.help" : "Zeige alle aktiven Boni des Kommandanten",
"vcmi.creatureWindow.showSkills.hover" : "Wechsle zur Fertigkeits-Ansicht",
"vcmi.creatureWindow.showSkills.help" : "Zeige alle erlernten Fertigkeiten des Kommandanten",
"vcmi.creatureWindow.returnArtifact.hover" : "Artefekt zurückgeben",
"vcmi.creatureWindow.returnArtifact.help" : "Nutze diese Schaltfläche, um Stapel-Artefakt in den Rucksack des Helden zurückzugeben",
"vcmi.questLog.hideComplete.hover" : "Verstecke abgeschlossene Quests",
"vcmi.questLog.hideComplete.help" : "Verstecke alle Quests die bereits abgeschlossen sind",
"vcmi.randomMapTab.widgets.randomTemplate" : "(Zufällig)",
"vcmi.randomMapTab.widgets.templateLabel" : "Template",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Einrichtung...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team-Zuordnungen",
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Straßentypen",
// Custom victory conditions for H3 campaigns and HotA maps
"vcmi.map.victoryCondition.daysPassed.toOthers" : "Der Feind hat es geschafft, bis zum heutigen Tag zu überleben. Der Sieg gehört ihm!",
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Herzlichen Glückwunsch! Ihr habt es geschafft, zu überleben. Der Sieg ist euer!",
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "Der Feind hat alle Monster besiegt, die das Land heimsuchen, und fordert den Sieg!",
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Herzlichen Glückwunsch! Ihr habt alle Monster besiegt, die dieses Land plagen, und könnt den Sieg für euch beanspruchen!",
"vcmi.map.victoryCondition.collectArtifacts.message" : "Sammelt drei Artefakte",
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Herzlichen Glückwunsch! Alle eure Feinde wurden besiegt und ihr habt die Engelsallianz! Der Sieg ist euer!",
"vcmi.map.victoryCondition.angelicAlliance.message" : "Besiege alle Feinde und gründe eine Engelsallianz",
// few strings from WoG used by vcmi
"vcmi.stackExperience.description" : "» D e t a i l s z u r S t a p e l e r f a h r u n g «\n\nKreatur-Typ ................... : %s\nErfahrungsrang ................. : %s (%i)\nErfahrungspunkte ............... : %i\nErfahrungspunkte für den nächsten Rang .. : %i\nMaximale Erfahrung pro Kampf ... : %i%% (%i)\nAnzahl der Kreaturen im Stapel .... : %i\nMaximale Anzahl neuer Rekruten\n ohne Verlust von aktuellem Rang .... : %i\nErfahrungs-Multiplikator ........... : %.2f\nUpgrade-Multiplikator .............. : %.2f\nErfahrung nach Rang 10 ........ : %i\nMaximale Anzahl der neuen Rekruten, die bei\n Rang 10 bei maximaler Erfahrung übrig sind : %i",
"vcmi.stackExperience.rank.0" : "Grundlagen",
"vcmi.stackExperience.rank.1" : "Neuling",
"vcmi.stackExperience.rank.2" : "Ausgebildet",
"vcmi.stackExperience.rank.3" : "Kompetent",
"vcmi.stackExperience.rank.4" : "Bewährt",
"vcmi.stackExperience.rank.5" : "Veteran",
"vcmi.stackExperience.rank.6" : "Gekonnt",
"vcmi.stackExperience.rank.7" : "Experte",
"vcmi.stackExperience.rank.8" : "Elite",
"vcmi.stackExperience.rank.9" : "Meister",
"vcmi.stackExperience.rank.10" : "Ass",
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
"core.bonus.ADDITIONAL_RETALIATION.description": "Kann ${val} zusätzliche Male vergelten",
"core.bonus.AIR_IMMUNITY.name": "Luftimmunität",
"core.bonus.AIR_IMMUNITY.description": "Immun gegen alle Luftschulzauber",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Rundum angreifen",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Greift alle benachbarten Gegner an",
"core.bonus.BLOCKS_RETALIATION.name": "Keine Vergeltung",
"core.bonus.BLOCKS_RETALIATION.description": "Feind kann nicht vergelten",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Keine Reichweitenverschiebung",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Feind kann nicht durch Schießen vergelten",
"core.bonus.CATAPULT.name": "Katapult",
"core.bonus.CATAPULT.description": "Greift Belagerungsmauern an",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduziere Zauberkosten (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduziert die Zauberkosten für den Helden",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Zauberdämpfer (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Erhöht die Kosten von gegnerischen Zaubern",
"core.bonus.CHARGE_IMMUNITY.name": "Immun gegen Aufladung",
"core.bonus.CHARGE_IMMUNITY.description": "Immun gegen Aufladung",
"core.bonus.DARKNESS.name": "Abdeckung der Dunkelheit",
"core.bonus.DARKNESS.description": "Fügt ${val} Dunkelheitsradius hinzu",
"core.bonus.DEATH_STARE.name": "Todesstarren (${val}%)",
"core.bonus.DEATH_STARE.description": "${val}% Chance, eine einzelne Kreatur zu töten",
"core.bonus.DEFENSIVE_STANCE.name": "Verteidigungsbonus",
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Verteidigung beim Verteidigen",
"core.bonus.DESTRUCTION.name": "Zerstörung",
"core.bonus.DESTRUCTION.description": "Hat ${val}% Chance, zusätzliche Einheiten nach dem Angriff zu töten",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Todesstoß",
"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.EARTH_IMMUNITY.name": "Erdimmunität",
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
"core.bonus.ENCHANTER.name": "Verzauberer",
"core.bonus.ENCHANTER.description": "Kann jede Runde eine Masse von ${subtype.spell} zaubern",
"core.bonus.ENCHANTED.name": "Verzaubert",
"core.bonus.ENCHANTED.description": "Beeinflusst von permanentem ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignoriere Verteidigung (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignoriert einen Teil der Verteidigung für den Angriff",
"core.bonus.FIRE_IMMUNITY.name": "Feuerimmunität",
"core.bonus.FIRE_IMMUNITY.description": "Immun gegen alle Zauber der Schule des Feuers",
"core.bonus.FIRE_SHIELD.name": "Feuerschild (${val}%)",
"core.bonus.FIRE_SHIELD.description": "Reflektiert einen Teil des Nahkampfschadens",
"core.bonus.FIRST_STRIKE.name": "Erstschlag",
"core.bonus.FIRST_STRIKE.description": "Diese Kreatur greift zuerst an, anstatt zu vergelten",
"core.bonus.FEAR.name": "Furcht",
"core.bonus.FEAR.description": "Verursacht Furcht bei einem gegnerischen Stapel",
"core.bonus.FEARLESS.name": "Furchtlos",
"core.bonus.FEARLESS.description": "immun gegen die Fähigkeit Furcht",
"core.bonus.FLYING.name": "Fliegen",
"core.bonus.FLYING.description": "Kann fliegen (ignoriert Hindernisse)",
"core.bonus.FREE_SHOOTING.name": "Nah schießen",
"core.bonus.FREE_SHOOTING.description": "Kann im Nahkampf schießen",
"core.bonus.GARGOYLE.name": "Gargoyle",
"core.bonus.GARGOYLE.description": "Kann nicht aufgerichtet oder geheilt werden",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Schaden vermindern (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduziert physischen Schaden aus dem Fern- oder Nahkampf",
"core.bonus.HATE.name": "Hasst ${subtype.creature}",
"core.bonus.HATE.description": "Macht ${val}% mehr Schaden",
"core.bonus.HEALER.name": "Heiler",
"core.bonus.HEALER.description": "Heilt verbündete Einheiten",
"core.bonus.HP_REGENERATION.name": "Regeneration",
"core.bonus.HP_REGENERATION.description": "Heilt ${val} Trefferpunkte jede Runde",
"core.bonus.JOUSTING.name": "Champion Charge",
"core.bonus.JOUSTING.description": "+${val}% Schaden pro zurückgelegtem Feld",
"core.bonus.KING.name": "König",
"core.bonus.KING.description": "Anfällig für SLAYER Level ${val} oder höher",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Zauberimmunität 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immun gegen Zaubersprüche der Stufen 1-${val}",
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Begrenzte Schussweite",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Kann nicht auf Ziele schießen, die weiter als ${val} Felder entfernt sind",
"core.bonus.LIFE_DRAIN.name": "Leben entziehen (${val}%)",
"core.bonus.LIFE_DRAIN.description": "Drainiert ${val}% des zugefügten Schadens",
"core.bonus.MANA_CHANNELING.name": "Magiekanal ${val}%",
"core.bonus.MANA_CHANNELING.description": "Gibt Ihrem Helden Mana, das vom Gegner ausgegeben wird",
"core.bonus.MANA_DRAIN.name": "Mana-Entzug",
"core.bonus.MANA_DRAIN.description": "Entzieht ${val} Mana jede Runde",
"core.bonus.MAGIC_MIRROR.name": "Zauberspiegel (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "${val}% Chance, einen Angriffszauber auf den Gegner umzulenken",
"core.bonus.MAGIC_RESISTANCE.name": "Magie-Widerstand(${val}%)",
"core.bonus.MAGIC_RESISTANCE.description": "${val}% Chance, gegnerischem Zauber zu widerstehen",
"core.bonus.MIND_IMMUNITY.name": "Geist-Zauber-Immunität",
"core.bonus.MIND_IMMUNITY.description": "Immun gegen Zauber vom Typ Geist",
"core.bonus.NO_DISTANCE_PENALTY.name": "Keine Entfernungsstrafe",
"core.bonus.NO_DISTANCE_PENALTY.description": "Voller Schaden aus beliebiger Entfernung",
"core.bonus.NO_MELEE_PENALTY.name": "Keine Nahkampf-Strafe",
"core.bonus.NO_MELEE_PENALTY.description": "Kreatur hat keinen Nahkampf-Malus",
"core.bonus.NO_MORALE.name": "Neutrale Moral",
"core.bonus.NO_MORALE.description": "Kreatur ist immun gegen Moral-Effekte",
"core.bonus.NO_WALL_PENALTY.name": "Keine Wand-Strafe",
"core.bonus.NO_WALL_PENALTY.description": "Voller Schaden bei Belagerung",
"core.bonus.NON_LIVING.name": "Nicht lebend",
"core.bonus.NON_LIVING.description": "Immunität gegen viele Effekte",
"core.bonus.RANDOM_SPELLCASTER.name": "Zufälliger Zauberwirker",
"core.bonus.RANDOM_SPELLCASTER.description": "Kann einen zufälligen Zauberspruch wirken",
"core.bonus.RANGED_RETALIATION.name": "Fernkampf-Vergeltung",
"core.bonus.RANGED_RETALIATION.description": "Kann einen Fernkampf-Gegenangriff durchführen",
"core.bonus.RECEPTIVE.name": "Empfänglich",
"core.bonus.RECEPTIVE.description": "Keine Immunität gegen Freundschaftszauber",
"core.bonus.REBIRTH.name": "Wiedergeburt (${val}%)",
"core.bonus.REBIRTH.description": "${val}% des Stacks wird nach dem Tod auferstehen",
"core.bonus.RETURN_AFTER_STRIKE.name": "Angriff und Rückkehr",
"core.bonus.RETURN_AFTER_STRIKE.description": "Kehrt nach Nahkampfangriff zurück",
"core.bonus.SHOOTER.name": "Fernkämpfer",
"core.bonus.SHOOTER.description": "Kreatur kann schießen",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Schießt rundherum",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Die Fernkampfangriffe dieser Kreatur treffen alle Ziele in einem kleinen Bereich",
"core.bonus.SOUL_STEAL.name": "Seelenraub",
"core.bonus.SOUL_STEAL.description": "Gewinnt ${val} neue Kreaturen für jeden getöteten Gegner",
"core.bonus.SPELLCASTER.name": "Zauberer",
"core.bonus.SPELLCASTER.description": "Kann ${subtype.spell} zaubern",
"core.bonus.SPELL_AFTER_ATTACK.name": "Nach Angriff zaubern",
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}%, um ${subtype.spell} nach dem Angriff zu wirken",
"core.bonus.SPELL_BEFORE_ATTACK.name": "Zauber vor Angriff",
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% um ${subtype.spell} vor dem Angriff zu wirken",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Schaden von Zaubern reduziert ${val}%.",
"core.bonus.SPELL_IMMUNITY.name": "Zauberimmunität",
"core.bonus.SPELL_IMMUNITY.description": "Immun gegen ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "zauberähnlicher Angriff",
"core.bonus.SPELL_LIKE_ATTACK.description": "Angriffe mit ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura des Widerstands",
"core.bonus.SPELL_RESISTANCE_AURA.description": "Stapel in der Nähe erhalten ${val}% Widerstand",
"core.bonus.SUMMON_GUARDIANS.name": "Wächter beschwören",
"core.bonus.SUMMON_GUARDIANS.description": "Beschwört bei Kampfbeginn ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "Synergierbar",
"core.bonus.SYNERGY_TARGET.description": "Diese Kreatur ist anfällig für Synergieeffekte",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Atem",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Atem-Angriff (2-Hex-Bereich)",
"core.bonus.THREE_HEADED_ATTACK.name": "Dreiköpfiger Angriff",
"core.bonus.THREE_HEADED_ATTACK.description": "Greift drei benachbarte Einheiten an",
"core.bonus.TRANSMUTATION.name": "Transmutation",
"core.bonus.TRANSMUTATION.description": "${val}% Chance, angegriffene Einheit in einen anderen Typ zu verwandeln",
"core.bonus.UNDEAD.name": "Untot",
"core.bonus.UNDEAD.description": "Kreatur ist untot",
"core.bonus.UNLIMITED_RETALIATIONS.name": "Unbegrenzte Vergeltungsmaßnahmen",
"core.bonus.UNLIMITED_RETALIATIONS.description": "Vergeltungen für eine beliebige Anzahl von Angriffen",
"core.bonus.WATER_IMMUNITY.name": "Wasser-Immunität",
"core.bonus.WATER_IMMUNITY.description": "Immun gegen alle Zauber der Wasserschule",
"core.bonus.WIDE_BREATH.name": "Breiter Atem",
"core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)"
}

View File

@ -1,372 +1,372 @@
{
"Blockbuster M" :
//(ban fly/DD, 2 player, 15-Jun-03, midnight design)
{
"minSize" : "m", "maxSize" : "m",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 6000, "density" : 4 },
{ "min" : 800, "max" : 2000, "density" : 12 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9800, "density" : 3 },
{ "min" : 3500, "max" : 8000, "density" : 10 },
{ "min" : 800, "max" : 1200, "density" : 5 }
]
},
"4" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 9000, "max" : 9800, "density" : 2 },
{ "min" : 3500, "max" : 8999, "density" : 8 },
{ "min" : 800, "max" : 1200, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "gold" : 2 },
"treasure" :
[
{ "min" : 18000, "max" : 25000, "density" : 2 },
{ "min" : 9000, "max" : 9800, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 3 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 50000 },
{ "a" : "1", "b" : "3", "guard" : 4000 },
{ "a" : "1", "b" : "5", "guard" : 8000 },
{ "a" : "2", "b" : "4", "guard" : 4000 },
{ "a" : "2", "b" : "6", "guard" : 8000 },
{ "a" : "5", "b" : "7", "guard" : 16000 },
{ "a" : "6", "b" : "7", "guard" : 16000 }
]
},
"Blockbuster L" :
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 5500, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 5 },
{ "min" : 320, "max" : 1000, "density" : 3 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9100, "density" : 4 },
{ "min" : 3500, "max" : 8000, "density" : 5 },
{ "min" : 800, "max" : 2000, "density" : 7 }
]
},
"4" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 6000, "max" : 9300, "density" : 8 },
{ "min" : 800, "max" : 1200, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 18000, "max" : 25000, "density" : 2 },
{ "min" : 0, "max" : 45000, "density" : 6 },
{ "min" : 8000, "max" : 9300, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 7,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 110000 },
{ "a" : "1", "b" : "3", "guard" : 4500 },
{ "a" : "1", "b" : "5", "guard" : 15000 },
{ "a" : "2", "b" : "4", "guard" : 4500 },
{ "a" : "2", "b" : "6", "guard" : 15000 },
{ "a" : "5", "b" : "7", "guard" : 24000 },
{ "a" : "6", "b" : "8", "guard" : 24000 },
{ "a" : "7", "b" : "8", "guard" : 40000 }
]
},
"Blockbuster XL" :
{
"minSize" : "xl", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 5500, "density" : 4 },
{ "min" : 1000, "max" : 2000, "density" : 3 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9100, "density" : 3 },
{ "min" : 3500, "max" : 8000, "density" : 4 },
{ "min" : 800, "max" : 2000, "density" : 6 }
]
},
"4" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 6000, "max" : 9200, "density" : 6 },
{ "min" : 800, "max" : 2000, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 28000, "max" : 29000, "density" : 1 },
{ "min" : 0, "max" : 50000, "density" : 5 },
{ "min" : 7500, "max" : 9200, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 7,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 140000 },
{ "a" : "1", "b" : "3", "guard" : 5000 },
{ "a" : "1", "b" : "5", "guard" : 17000 },
{ "a" : "2", "b" : "4", "guard" : 5000 },
{ "a" : "2", "b" : "6", "guard" : 17000 },
{ "a" : "5", "b" : "7", "guard" : 30000 },
{ "a" : "6", "b" : "8", "guard" : 30000 },
{ "a" : "7", "b" : "8", "guard" : 50000 }
]
}
}
{
"Blockbuster M" :
//(ban fly/DD, 2 player, 15-Jun-03, midnight design)
{
"minSize" : "m", "maxSize" : "m",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 6000, "density" : 4 },
{ "min" : 800, "max" : 2000, "density" : 12 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9800, "density" : 3 },
{ "min" : 3500, "max" : 8000, "density" : 10 },
{ "min" : 800, "max" : 1200, "density" : 5 }
]
},
"4" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 9000, "max" : 9800, "density" : 2 },
{ "min" : 3500, "max" : 8999, "density" : 8 },
{ "min" : 800, "max" : 1200, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "gold" : 2 },
"treasure" :
[
{ "min" : 18000, "max" : 25000, "density" : 2 },
{ "min" : 9000, "max" : 9800, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 3 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 50000 },
{ "a" : "1", "b" : "3", "guard" : 4000 },
{ "a" : "1", "b" : "5", "guard" : 8000 },
{ "a" : "2", "b" : "4", "guard" : 4000 },
{ "a" : "2", "b" : "6", "guard" : 8000 },
{ "a" : "5", "b" : "7", "guard" : 16000 },
{ "a" : "6", "b" : "7", "guard" : 16000 }
]
},
"Blockbuster L" :
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 5500, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 5 },
{ "min" : 320, "max" : 1000, "density" : 3 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9100, "density" : 4 },
{ "min" : 3500, "max" : 8000, "density" : 5 },
{ "min" : 800, "max" : 2000, "density" : 7 }
]
},
"4" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 6000, "max" : 9300, "density" : 8 },
{ "min" : 800, "max" : 1200, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 18000, "max" : 25000, "density" : 2 },
{ "min" : 0, "max" : 45000, "density" : 6 },
{ "min" : 8000, "max" : 9300, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 7,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 110000 },
{ "a" : "1", "b" : "3", "guard" : 4500 },
{ "a" : "1", "b" : "5", "guard" : 15000 },
{ "a" : "2", "b" : "4", "guard" : 4500 },
{ "a" : "2", "b" : "6", "guard" : 15000 },
{ "a" : "5", "b" : "7", "guard" : 24000 },
{ "a" : "6", "b" : "8", "guard" : 24000 },
{ "a" : "7", "b" : "8", "guard" : 40000 }
]
},
"Blockbuster XL" :
{
"minSize" : "xl", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 5500, "density" : 4 },
{ "min" : 1000, "max" : 2000, "density" : 3 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 8000, "max" : 9100, "density" : 3 },
{ "min" : 3500, "max" : 8000, "density" : 4 },
{ "min" : 800, "max" : 2000, "density" : 6 }
]
},
"4" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasure" :
[
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 6000, "max" : 9200, "density" : 6 },
{ "min" : 800, "max" : 2000, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 4,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand", "snow" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 28000, "max" : 29000, "density" : 1 },
{ "min" : 0, "max" : 50000, "density" : 5 },
{ "min" : 7500, "max" : 9200, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 7,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 140000 },
{ "a" : "1", "b" : "3", "guard" : 5000 },
{ "a" : "1", "b" : "5", "guard" : 17000 },
{ "a" : "2", "b" : "4", "guard" : 5000 },
{ "a" : "2", "b" : "6", "guard" : 17000 },
{ "a" : "5", "b" : "7", "guard" : 30000 },
{ "a" : "6", "b" : "8", "guard" : 30000 },
{ "a" : "7", "b" : "8", "guard" : 50000 }
]
}
}

View File

@ -1,362 +1,362 @@
{
"Extreme L" :
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 3400, "max" : 3500, "density" : 3 },
{ "min" : 1000, "max" : 2000, "density" : 10 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 3500, "max" : 9600, "density" : 8 },
{ "min" : 300, "max" : 1000, "density" : 4 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "wood" : 1 },
"treasure" :
[
{ "min" : 40000, "max" : 42000, "density" : 1 },
{ "min" : 25000, "max" : 27000, "density" : 2 },
{ "min" : 6000, "max" : 15000, "density" : 3 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 14,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 30000, "max" : 60000, "density" : 1 },
{ "min" : 20000, "max" : 29000, "density" : 2 },
{ "min" : 3500, "max" : 20000, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 14,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
},
"9" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 1 },
{ "min" : 50000, "max" : 70000, "density" : 9 }
]
},
"10" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"11" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"12" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 15000 },
{ "a" : "3", "b" : "7", "guard" : 20000 },
{ "a" : "4", "b" : "6", "guard" : 15000 },
{ "a" : "4", "b" : "8", "guard" : 20000 },
{ "a" : "3", "b" : "11", "guard" : 50000 },
{ "a" : "4", "b" : "12", "guard" : 50000 },
{ "a" : "5", "b" : "9", "guard" : 40000 },
{ "a" : "7", "b" : "11", "guard" : 40000 },
{ "a" : "6", "b" : "10", "guard" : 40000 },
{ "a" : "8", "b" : "12", "guard" : 40000 },
{ "a" : "1", "b" : "9", "guard" : 50000 },
{ "a" : "2", "b" : "10", "guard" : 50000 },
{ "a" : "9", "b" : "12", "guard" : 100000 },
{ "a" : "10", "b" : "11", "guard" : 100000 }
]
},
"Extreme XL" :
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xh",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 3400, "max" : 3500, "density" : 3 },
{ "min" : 1000, "max" : 2000, "density" : 6 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 3500, "max" : 9500, "density" : 5 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"4" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "wood" : 1 },
"treasure" :
[
{ "min" : 40000, "max" : 42000, "density" : 1 },
{ "min" : 25000, "max" : 27000, "density" : 1 },
{ "min" : 6000, "max" : 15000, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 30000, "max" : 60000, "density" : 1 },
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 3500, "max" : 20000, "density" : 2 }
]
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
},
"9" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 1 },
{ "min" : 50000, "max" : 80000, "density" : 6 }
]
},
"10" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"11" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"12" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "3", "b" : "5", "guard" : 18000 },
{ "a" : "3", "b" : "7", "guard" : 22000 },
{ "a" : "4", "b" : "6", "guard" : 18000 },
{ "a" : "4", "b" : "8", "guard" : 22000 },
{ "a" : "3", "b" : "11", "guard" : 60000 },
{ "a" : "4", "b" : "12", "guard" : 60000 },
{ "a" : "5", "b" : "9", "guard" : 50000 },
{ "a" : "7", "b" : "11", "guard" : 50000 },
{ "a" : "6", "b" : "10", "guard" : 50000 },
{ "a" : "8", "b" : "12", "guard" : 50000 },
{ "a" : "1", "b" : "9", "guard" : 60000 },
{ "a" : "2", "b" : "10", "guard" : 60000 },
{ "a" : "9", "b" : "12", "guard" : 140000 },
{ "a" : "10", "b" : "11", "guard" : 140000 }
]
}
}
{
"Extreme L" :
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 3400, "max" : 3500, "density" : 3 },
{ "min" : 1000, "max" : 2000, "density" : 10 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 3500, "max" : 9600, "density" : 8 },
{ "min" : 300, "max" : 1000, "density" : 4 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "wood" : 1 },
"treasure" :
[
{ "min" : 40000, "max" : 42000, "density" : 1 },
{ "min" : 25000, "max" : 27000, "density" : 2 },
{ "min" : 6000, "max" : 15000, "density" : 3 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 14,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 30000, "max" : 60000, "density" : 1 },
{ "min" : 20000, "max" : 29000, "density" : 2 },
{ "min" : 3500, "max" : 20000, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 14,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
},
"9" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 1 },
{ "min" : 50000, "max" : 70000, "density" : 9 }
]
},
"10" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"11" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"12" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 15000 },
{ "a" : "3", "b" : "7", "guard" : 20000 },
{ "a" : "4", "b" : "6", "guard" : 15000 },
{ "a" : "4", "b" : "8", "guard" : 20000 },
{ "a" : "3", "b" : "11", "guard" : 50000 },
{ "a" : "4", "b" : "12", "guard" : 50000 },
{ "a" : "5", "b" : "9", "guard" : 40000 },
{ "a" : "7", "b" : "11", "guard" : 40000 },
{ "a" : "6", "b" : "10", "guard" : 40000 },
{ "a" : "8", "b" : "12", "guard" : 40000 },
{ "a" : "1", "b" : "9", "guard" : 50000 },
{ "a" : "2", "b" : "10", "guard" : 50000 },
{ "a" : "9", "b" : "12", "guard" : 100000 },
{ "a" : "10", "b" : "11", "guard" : 100000 }
]
},
"Extreme XL" :
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xh",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 3400, "max" : 3500, "density" : 3 },
{ "min" : 1000, "max" : 2000, "density" : 6 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 3500, "max" : 9500, "density" : 5 },
{ "min" : 300, "max" : 1000, "density" : 2 }
]
},
"4" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "wood" : 1 },
"treasure" :
[
{ "min" : 40000, "max" : 42000, "density" : 1 },
{ "min" : 25000, "max" : 27000, "density" : 1 },
{ "min" : 6000, "max" : 15000, "density" : 2 }
]
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 30000, "max" : 60000, "density" : 1 },
{ "min" : 20000, "max" : 29000, "density" : 1 },
{ "min" : 3500, "max" : 20000, "density" : 2 }
]
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 7,
"treasureLikeZone" : 7
},
"9" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 1 },
{ "min" : 50000, "max" : 80000, "density" : 6 }
]
},
"10" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"11" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
},
"12" :
{
"type" : "treasure",
"size" : 17,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 9,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "3", "b" : "5", "guard" : 18000 },
{ "a" : "3", "b" : "7", "guard" : 22000 },
{ "a" : "4", "b" : "6", "guard" : 18000 },
{ "a" : "4", "b" : "8", "guard" : 22000 },
{ "a" : "3", "b" : "11", "guard" : 60000 },
{ "a" : "4", "b" : "12", "guard" : 60000 },
{ "a" : "5", "b" : "9", "guard" : 50000 },
{ "a" : "7", "b" : "11", "guard" : 50000 },
{ "a" : "6", "b" : "10", "guard" : 50000 },
{ "a" : "8", "b" : "12", "guard" : 50000 },
{ "a" : "1", "b" : "9", "guard" : 60000 },
{ "a" : "2", "b" : "10", "guard" : 60000 },
{ "a" : "9", "b" : "12", "guard" : 140000 },
{ "a" : "10", "b" : "11", "guard" : 140000 }
]
}
}

View File

@ -1,310 +1,310 @@
{
"Extreme II L":
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)"
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 120,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 16000, "max" : 90000, "density" : 1 },
{ "min" : 300, "max" : 16000, "density" : 2 },
{ "min" : 370, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 120,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 22,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass", "subterra" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 9000, "density" : 4 },
{ "min" : 300, "max" : 1000, "density" : 6 }
]
},
"4" :
{
"type" : "treasure",
"size" : 22,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "subterra", "lava" ],
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 70000, "max" : 90000, "density" : 6 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"8" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"9" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasure" :
[
{ "min" : 90000, "max" : 120000, "density" : 6 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 65000 },
{ "a" : "3", "b" : "7", "guard" : 65000 },
{ "a" : "4", "b" : "6", "guard" : 65000 },
{ "a" : "4", "b" : "8", "guard" : 65000 },
{ "a" : "5", "b" : "9", "guard" : 135000 },
{ "a" : "6", "b" : "9", "guard" : 135000 },
{ "a" : "7", "b" : "10", "guard" : 135000 },
{ "a" : "8", "b" : "10", "guard" : 135000 },
{ "a" : "3", "b" : "5", "guard" : 60000 },
{ "a" : "3", "b" : "7", "guard" : 60000 },
{ "a" : "4", "b" : "6", "guard" : 60000 },
{ "a" : "4", "b" : "8", "guard" : 60000 }
]
},
"Extreme II XL":
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xh",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 90,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 16000, "max" : 120000, "density" : 1 },
{ "min" : 300, "max" : 16000, "density" : 2 },
{ "min" : 370, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 90,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass", "subterra" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 9000, "density" : 4 },
{ "min" : 300, "max" : 1000, "density" : 4 }
]
},
"4" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "subterra", "lava" ],
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 85000, "max" : 100000, "density" : 4 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"8" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"9" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 4 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 80000 },
{ "a" : "3", "b" : "7", "guard" : 80000 },
{ "a" : "4", "b" : "6", "guard" : 80000 },
{ "a" : "4", "b" : "8", "guard" : 80000 },
{ "a" : "5", "b" : "9", "guard" : 160000 },
{ "a" : "6", "b" : "9", "guard" : 160000 },
{ "a" : "7", "b" : "10", "guard" : 160000 },
{ "a" : "8", "b" : "10", "guard" : 160000 },
{ "a" : "3", "b" : "5", "guard" : 70000 },
{ "a" : "3", "b" : "7", "guard" : 70000 },
{ "a" : "4", "b" : "6", "guard" : 70000 },
{ "a" : "4", "b" : "8", "guard" : 70000 }
]
}
}
{
"Extreme II L":
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)"
{
"minSize" : "l", "maxSize" : "l",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 120,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 16000, "max" : 90000, "density" : 1 },
{ "min" : 300, "max" : 16000, "density" : 2 },
{ "min" : 370, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 120,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 22,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass", "subterra" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 9000, "density" : 4 },
{ "min" : 300, "max" : 1000, "density" : 6 }
]
},
"4" :
{
"type" : "treasure",
"size" : 22,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "subterra", "lava" ],
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 70000, "max" : 90000, "density" : 6 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"8" :
{
"type" : "treasure",
"size" : 18,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"9" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasure" :
[
{ "min" : 90000, "max" : 120000, "density" : 6 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 65000 },
{ "a" : "3", "b" : "7", "guard" : 65000 },
{ "a" : "4", "b" : "6", "guard" : 65000 },
{ "a" : "4", "b" : "8", "guard" : 65000 },
{ "a" : "5", "b" : "9", "guard" : 135000 },
{ "a" : "6", "b" : "9", "guard" : 135000 },
{ "a" : "7", "b" : "10", "guard" : 135000 },
{ "a" : "8", "b" : "10", "guard" : 135000 },
{ "a" : "3", "b" : "5", "guard" : 60000 },
{ "a" : "3", "b" : "7", "guard" : 60000 },
{ "a" : "4", "b" : "6", "guard" : 60000 },
{ "a" : "4", "b" : "8", "guard" : 60000 }
]
},
"Extreme II XL":
//(ban fly/DD/orb inhibition/Castle-Necro-Conflux towns, 2 player, 3-Aug-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xh",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 90,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"mines" : { "wood" : 1, "ore" : 1, "gold" : 3 },
"treasure" :
[
{ "min" : 16000, "max" : 120000, "density" : 1 },
{ "min" : 300, "max" : 16000, "density" : 2 },
{ "min" : 370, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 90,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass", "subterra" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 9000, "density" : 4 },
{ "min" : 300, "max" : 1000, "density" : 4 }
]
},
"4" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "subterra", "lava" ],
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"treasure" :
[
{ "min" : 85000, "max" : 100000, "density" : 4 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"8" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 5
},
"9" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasure" :
[
{ "min" : 115000, "max" : 120000, "density" : 4 },
{ "min" : 20000, "max" : 20000, "density" : 2 },
{ "min" : 300, "max" : 400, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 5,
"treasureLikeZone" : 9
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 6500 },
{ "a" : "1", "b" : "3", "guard" : 6000 },
{ "a" : "1", "b" : "3", "guard" : 5500 },
{ "a" : "2", "b" : "4", "guard" : 6500 },
{ "a" : "2", "b" : "4", "guard" : 6000 },
{ "a" : "2", "b" : "4", "guard" : 5500 },
{ "a" : "3", "b" : "5", "guard" : 80000 },
{ "a" : "3", "b" : "7", "guard" : 80000 },
{ "a" : "4", "b" : "6", "guard" : 80000 },
{ "a" : "4", "b" : "8", "guard" : 80000 },
{ "a" : "5", "b" : "9", "guard" : 160000 },
{ "a" : "6", "b" : "9", "guard" : 160000 },
{ "a" : "7", "b" : "10", "guard" : 160000 },
{ "a" : "8", "b" : "10", "guard" : 160000 },
{ "a" : "3", "b" : "5", "guard" : 70000 },
{ "a" : "3", "b" : "7", "guard" : 70000 },
{ "a" : "4", "b" : "6", "guard" : 70000 },
{ "a" : "4", "b" : "8", "guard" : 70000 }
]
}
}

View File

@ -1,83 +1,83 @@
{
"Poor Jebus" :
//(made by Bjorn190, modified by Maretti and Angelito)
{
"minSize" : "m", "maxSize" : "xl+u",
"players" : "2-4",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"mines" : { "wood" : 4, "mercury" : 1, "ore" : 4, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 12000, "max" : 22000, "density" : 1 },
{ "min" : 5000, "max" : 16000, "density" : 6 },
{ "min" : 400, "max" : 3000, "density" : 4 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 3,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"4" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 4,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"5" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "castles" : 2 },
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "gold" : 4 },
"treasure" :
[
{ "min" : 35000, "max" : 55000, "density" : 3 },
{ "min" : 25000, "max" : 35000, "density" : 10 },
{ "min" : 10000, "max" : 25000, "density" : 10 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "5", "guard" : 45000 },
{ "a" : "2", "b" : "5", "guard" : 45000 },
{ "a" : "3", "b" : "5", "guard" : 45000 },
{ "a" : "4", "b" : "5", "guard" : 45000 }
]
}
}
{
"Poor Jebus" :
//(made by Bjorn190, modified by Maretti and Angelito)
{
"minSize" : "m", "maxSize" : "xl+u",
"players" : "2-4",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"mines" : { "wood" : 4, "mercury" : 1, "ore" : 4, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 2 },
"treasure" :
[
{ "min" : 12000, "max" : 22000, "density" : 1 },
{ "min" : 5000, "max" : 16000, "density" : 6 },
{ "min" : 400, "max" : 3000, "density" : 4 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 3,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"4" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 4,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 2 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"5" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "castles" : 2 },
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "gold" : 4 },
"treasure" :
[
{ "min" : 35000, "max" : 55000, "density" : 3 },
{ "min" : 25000, "max" : 35000, "density" : 10 },
{ "min" : 10000, "max" : 25000, "density" : 10 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "5", "guard" : 45000 },
{ "a" : "2", "b" : "5", "guard" : 45000 },
{ "a" : "3", "b" : "5", "guard" : 45000 },
{ "a" : "4", "b" : "5", "guard" : 45000 }
]
}
}

View File

@ -1,140 +1,140 @@
{
"Reckless" :
//(2 player, 6-Jan-03, midnight design)
{
"minSize" : "l", "maxSize" : "xl+u",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 4500, "max" : 9800, "density" : 1 },
{ "min" : 3500, "max" : 4500, "density" : 5 },
{ "min" : 1500, "max" : 2000, "density" : 7 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 20000, "max" : 25000, "density" : 1 },
{ "min" : 3500, "max" : 9800, "density" : 8 },
{ "min" : 800, "max" : 1500, "density" : 5 }
]
},
"4" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"terrainTypeLikeZone" : 3,
"mines" : { "mercury" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 3
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 4,
"treasureLikeZone" : 3
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 3
},
"9" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "castles" : 4 },
"bannedTowns" : ["necropolis", "conflux"],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "wood" : 2, "ore" : 2, "gold" : 3 },
"treasure" :
[
{ "min" : 40000, "max" : 60000, "density" : 1 },
{ "min" : 500, "max" : 29000, "density" : 12 },
{ "min" : 800, "max" : 2000, "density" : 8 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 5000 },
{ "a" : "1", "b" : "4", "guard" : 5000 },
{ "a" : "1", "b" : "5", "guard" : 5000 },
{ "a" : "2", "b" : "6", "guard" : 5000 },
{ "a" : "2", "b" : "7", "guard" : 5000 },
{ "a" : "2", "b" : "8", "guard" : 5000 },
{ "a" : "3", "b" : "9", "guard" : 11000 },
{ "a" : "4", "b" : "9", "guard" : 11000 },
{ "a" : "5", "b" : "9", "guard" : 11000 },
{ "a" : "6", "b" : "9", "guard" : 11000 },
{ "a" : "7", "b" : "9", "guard" : 11000 },
{ "a" : "8", "b" : "9", "guard" : 11000 }
]
}
}
{
"Reckless" :
//(2 player, 6-Jan-03, midnight design)
{
"minSize" : "l", "maxSize" : "xl+u",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 4500, "max" : 9800, "density" : 1 },
{ "min" : 3500, "max" : 4500, "density" : 5 },
{ "min" : 1500, "max" : 2000, "density" : 7 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 30,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 20000, "max" : 25000, "density" : 1 },
{ "min" : 3500, "max" : 9800, "density" : 8 },
{ "min" : 800, "max" : 1500, "density" : 5 }
]
},
"4" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"mines" : { "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"terrainTypeLikeZone" : 3,
"mines" : { "mercury" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 3
},
"6" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"neutralTowns" : { "towns" : 1 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 4,
"treasureLikeZone" : 3
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "strong",
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 3
},
"9" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "castles" : 4 },
"bannedTowns" : ["necropolis", "conflux"],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "wood" : 2, "ore" : 2, "gold" : 3 },
"treasure" :
[
{ "min" : 40000, "max" : 60000, "density" : 1 },
{ "min" : 500, "max" : 29000, "density" : 12 },
{ "min" : 800, "max" : 2000, "density" : 8 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 5000 },
{ "a" : "1", "b" : "4", "guard" : 5000 },
{ "a" : "1", "b" : "5", "guard" : 5000 },
{ "a" : "2", "b" : "6", "guard" : 5000 },
{ "a" : "2", "b" : "7", "guard" : 5000 },
{ "a" : "2", "b" : "8", "guard" : 5000 },
{ "a" : "3", "b" : "9", "guard" : 11000 },
{ "a" : "4", "b" : "9", "guard" : 11000 },
{ "a" : "5", "b" : "9", "guard" : 11000 },
{ "a" : "6", "b" : "9", "guard" : 11000 },
{ "a" : "7", "b" : "9", "guard" : 11000 },
{ "a" : "8", "b" : "9", "guard" : 11000 }
]
}
}

View File

@ -1,139 +1,139 @@
{
"Roadrunner" :
//(ban fly/DD, 2 player, 31-May-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 6000, "density" : 4 },
{ "min" : 300, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "gold" : 1 },
"treasure" :
[
{ "min" : 6000, "max" : 9500, "density" : 2 },
{ "min" : 3500, "max" : 6000, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 3 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"terrainTypeLikeZone" : 3,
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 45000, "max" : 50000, "density" : 1 },
{ "min" : 15000, "max" : 22000, "density" : 1 },
{ "min" : 8000, "max" : 9200, "density" : 12 }
]
},
"6" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"bannedTowns" : ["necropolis"],
"matchTerrainToTown" : false,
"treasure" :
[
{ "min" : 100000, "max" : 120000, "density" : 1 },
{ "min" : 25000, "max" : 29000, "density" : 1 },
{ "min" : 8000, "max" : 9200, "density" : 12 }
]
},
"8" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"bannedTowns" : ["necropolis"],
"matchTerrainToTown" : false,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 0 },
{ "a" : "1", "b" : "3", "guard" : 0 },
{ "a" : "2", "b" : "4", "guard" : 0 },
{ "a" : "2", "b" : "4", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "7", "guard" : 0 },
{ "a" : "5", "b" : "7", "guard" : 0 },
{ "a" : "6", "b" : "8", "guard" : 0 },
{ "a" : "6", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 }
]
}
}
{
"Roadrunner" :
//(ban fly/DD, 2 player, 31-May-03, midnight design)
{
"minSize" : "xl", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 3500, "max" : 6000, "density" : 4 },
{ "min" : 300, "max" : 2000, "density" : 5 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"neutralTowns" : { "towns" : 1 },
"bannedTowns" : ["necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "gold" : 1 },
"treasure" :
[
{ "min" : 6000, "max" : 9500, "density" : 2 },
{ "min" : 3500, "max" : 6000, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 3 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"terrainTypeLikeZone" : 3,
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 45000, "max" : 50000, "density" : 1 },
{ "min" : 15000, "max" : 22000, "density" : 1 },
{ "min" : 8000, "max" : 9200, "density" : 12 }
]
},
"6" :
{
"type" : "treasure",
"size" : 40,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 5,
"treasureLikeZone" : 5
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"bannedTowns" : ["necropolis"],
"matchTerrainToTown" : false,
"treasure" :
[
{ "min" : 100000, "max" : 120000, "density" : 1 },
{ "min" : 25000, "max" : 29000, "density" : 1 },
{ "min" : 8000, "max" : 9200, "density" : 12 }
]
},
"8" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"bannedTowns" : ["necropolis"],
"matchTerrainToTown" : false,
"treasureLikeZone" : 7
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 0 },
{ "a" : "1", "b" : "3", "guard" : 0 },
{ "a" : "2", "b" : "4", "guard" : 0 },
{ "a" : "2", "b" : "4", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "7", "guard" : 0 },
{ "a" : "5", "b" : "7", "guard" : 0 },
{ "a" : "6", "b" : "8", "guard" : 0 },
{ "a" : "6", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 }
]
}
}

View File

@ -1,111 +1,111 @@
{
"SuperSlam" :
//(2 player, Large or XL no under) For powermonger players, meet by SLAMMING thru super zones. Your chances are not over until the fat lady sings! Should ban spec log along with normal random rules
{
"minSize" : "l", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 400, "max" : 2000, "density" : 6 },
{ "min" : 3500, "max" : 5000, "density" : 5 },
{ "min" : 2100, "max" : 3000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "normal",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1, "sulfur" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 6000, "max" : 9500, "density" : 2 },
{ "min" : 3500, "max" : 6000, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 3 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "normal",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "rampart", "tower", "inferno", "dungeon", "stronghold" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "mercury" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 25000, "max" : 29000, "density" : 5 },
{ "min" : 10000, "max" : 22000, "density" : 3 },
{ "min" : 1000, "max" : 1700, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "rampart", "tower", "inferno", "dungeon", "stronghold" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "snow" ],
"minesLikeZone" : 5,
"treasure" :
[
{ "min" : 10000, "max" : 22000, "density" : 3 },
{ "min" : 25000, "max" : 29000, "density" : 5 },
{ "min" : 1000, "max" : 1700, "density" : 1 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 10 },
{ "a" : "2", "b" : "4", "guard" : 10 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "6", "guard" : 120000 },
{ "a" : "3", "b" : "4", "guard" : 120000 }
]
}
}
{
"SuperSlam" :
//(2 player, Large or XL no under) For powermonger players, meet by SLAMMING thru super zones. Your chances are not over until the fat lady sings! Should ban spec log along with normal random rules
{
"minSize" : "l", "maxSize" : "xl",
"players" : "2",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 400, "max" : 2000, "density" : 6 },
{ "min" : 3500, "max" : 5000, "density" : 5 },
{ "min" : 2100, "max" : 3000, "density" : 2 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 20,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"bannedTowns" : ["castle", "necropolis", "conflux"],
"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ],
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "normal",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ],
"mines" : { "wood" : 1, "ore" : 1, "sulfur" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 6000, "max" : 9500, "density" : 2 },
{ "min" : 3500, "max" : 6000, "density" : 5 },
{ "min" : 1000, "max" : 2000, "density" : 3 }
]
},
"4" :
{
"type" : "treasure",
"size" : 25,
"monsters" : "normal",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "necropolis" ],
"terrainTypeLikeZone" : 3,
"minesLikeZone" : 3,
"treasureLikeZone" : 3
},
"5" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "rampart", "tower", "inferno", "dungeon", "stronghold" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "sand" ],
"mines" : { "mercury" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 25000, "max" : 29000, "density" : 5 },
{ "min" : 10000, "max" : 22000, "density" : 3 },
{ "min" : 1000, "max" : 1700, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 50,
"monsters" : "strong",
"neutralTowns" : { "towns" : 2 },
"allowedTowns" : [ "rampart", "tower", "inferno", "dungeon", "stronghold" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "snow" ],
"minesLikeZone" : 5,
"treasure" :
[
{ "min" : 10000, "max" : 22000, "density" : 3 },
{ "min" : 25000, "max" : 29000, "density" : 5 },
{ "min" : 1000, "max" : 1700, "density" : 1 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "3", "guard" : 10 },
{ "a" : "2", "b" : "4", "guard" : 10 },
{ "a" : "3", "b" : "5", "guard" : 0 },
{ "a" : "4", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "6", "guard" : 120000 },
{ "a" : "3", "b" : "4", "guard" : 120000 }
]
}
}

View File

@ -1,146 +1,146 @@
{
"Ready or Not" :
{
"minSize" : "m", "maxSize" : "xl+u",
"players" : "2-3",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "fortress" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 3000, "max" : 6000, "density" : 1 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "inferno" ],
"mines" : { "wood" : 1, "ore" : 1, "sulfur" : 1 },
"treasureLikeZone" : 1
},
"3" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "rampart" ],
"mines" : { "wood" : 1, "ore" : 1, "crystal" : 1 },
"treasureLikeZone" : 1
},
"4" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "rampart" ],
"allowedMonsters" : [ "inferno", "fortress", "neutral" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"5" :
{
"type" : "treasure",
"size" : 5,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "sand", "snow", "swamp", "rough", "subterra" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"6" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "fortress" ],
"allowedMonsters" : [ "rampart", "inferno", "neutral" ],
"minesLikeZone" : 4,
"treasureLikeZone" : 4
},
"7" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "inferno" ],
"allowedMonsters" : [ "rampart", "fortress", "neutral" ],
"minesLikeZone" : 4,
"treasureLikeZone" : 4
},
"8" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt" ],
"treasure" :
[
{ "min" : 20000, "max" : 30000, "density" : 1 },
{ "min" : 15000, "max" : 20000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 9 }
]
},
"9" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"terrainTypeLikeZone" : 8,
"treasureLikeZone" : 8
},
"10" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"terrainTypeLikeZone" : 8,
"treasureLikeZone" : 8
}
},
"connections" :
[
{ "a" : "1", "b" : "5", "guard" : 6000 },
{ "a" : "2", "b" : "5", "guard" : 6000 },
{ "a" : "3", "b" : "5", "guard" : 6000 },
{ "a" : "4", "b" : "5", "guard" : 0 },
{ "a" : "6", "b" : "5", "guard" : 0 },
{ "a" : "7", "b" : "5", "guard" : 0 },
{ "a" : "8", "b" : "1", "guard" : 12500 }, // Border guard replaced by monster
{ "a" : "9", "b" : "2", "guard" : 12500 }, // Border guard replaced by monster
{ "a" : "10", "b" : "3", "guard" : 12500 } // Border guard replaced by monster
]
}
}
{
"Ready or Not" :
{
"minSize" : "m", "maxSize" : "xl+u",
"players" : "2-3",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "fortress" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 3000, "max" : 6000, "density" : 1 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "inferno" ],
"mines" : { "wood" : 1, "ore" : 1, "sulfur" : 1 },
"treasureLikeZone" : 1
},
"3" :
{
"type" : "playerStart",
"size" : 10,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"townsAreSameType" : true,
"allowedTowns" : [ "rampart" ],
"mines" : { "wood" : 1, "ore" : 1, "crystal" : 1 },
"treasureLikeZone" : 1
},
"4" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "rampart" ],
"allowedMonsters" : [ "inferno", "fortress", "neutral" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"5" :
{
"type" : "treasure",
"size" : 5,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "sand", "snow", "swamp", "rough", "subterra" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"6" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "fortress" ],
"allowedMonsters" : [ "rampart", "inferno", "neutral" ],
"minesLikeZone" : 4,
"treasureLikeZone" : 4
},
"7" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"allowedTowns" : [ "inferno" ],
"allowedMonsters" : [ "rampart", "fortress", "neutral" ],
"minesLikeZone" : 4,
"treasureLikeZone" : 4
},
"8" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt" ],
"treasure" :
[
{ "min" : 20000, "max" : 30000, "density" : 1 },
{ "min" : 15000, "max" : 20000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 9 }
]
},
"9" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"terrainTypeLikeZone" : 8,
"treasureLikeZone" : 8
},
"10" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "strong",
"terrainTypeLikeZone" : 8,
"treasureLikeZone" : 8
}
},
"connections" :
[
{ "a" : "1", "b" : "5", "guard" : 6000 },
{ "a" : "2", "b" : "5", "guard" : 6000 },
{ "a" : "3", "b" : "5", "guard" : 6000 },
{ "a" : "4", "b" : "5", "guard" : 0 },
{ "a" : "6", "b" : "5", "guard" : 0 },
{ "a" : "7", "b" : "5", "guard" : 0 },
{ "a" : "8", "b" : "1", "guard" : 12500 }, // Border guard replaced by monster
{ "a" : "9", "b" : "2", "guard" : 12500 }, // Border guard replaced by monster
{ "a" : "10", "b" : "3", "guard" : 12500 } // Border guard replaced by monster
]
}
}

View File

@ -1,232 +1,232 @@
{
"Worlds at War" :
{
"minSize" : "m+u", "maxSize" : "xl+u",
"players" : "2", "cpu" : "3",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"4" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 4,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"5" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 5,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasureLikeZone" : 1
},
"6" :
{
"type" : "treasure",
"size" : 30,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "swamp" ],
"mines" : { "sulfur" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 8000, "density" : 8 },
{ "min" : 3000, "max" : 15000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 }
]
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 15000, "max" : 20000, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 30,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "rough" ],
"minesLikeZone" : 6,
"treasureLikeZone" : 6
},
"9" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass" ],
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 3 },
{ "min" : 10000, "max" : 20000, "density" : 2 },
{ "min" : 20000, "max" : 30000, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasure" :
[
{ "min" : 10000, "max" : 20000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 2 }
]
},
"11" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 10
},
"12" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 4 },
{ "min" : 10000, "max" : 20000, "density" : 3 }
]
},
"13" :
{
"type" : "treasure",
"size" : 35,
"monsters" : "strong",
"neutralTowns" : { "castles" : 1 },
"mines" : { "gold" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 20000, "density" : 6 },
{ "min" : 10000, "max" : 20000, "density" : 6 },
{ "min" : 500, "max" : 3000, "density" : 10 }
]
},
"14" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"15" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"16" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"17" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"terrainTypeLikeZone" : 9,
"minesLikeZone" : 13,
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 20000, "max" : 30000, "density" : 1 },
{ "min" : 10000, "max" : 20000, "density" : 4 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "6", "guard" : 3000 },
{ "a" : "2", "b" : "8", "guard" : 3000 },
{ "a" : "3", "b" : "12", "guard" : 4500 },
{ "a" : "4", "b" : "14", "guard" : 4500 },
{ "a" : "5", "b" : "9", "guard" : 6000 },
{ "a" : "5", "b" : "15", "guard" : 3000 },
{ "a" : "5", "b" : "16", "guard" : 3000 },
{ "a" : "6", "b" : "7", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "6", "b" : "8", "guard" : 6000 },
{ "a" : "6", "b" : "10", "guard" : 4500 },
{ "a" : "7", "b" : "8", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "8", "b" : "11", "guard" : 4500 },
{ "a" : "9", "b" : "13", "guard" : 6000 },
{ "a" : "12", "b" : "13", "guard" : 4500 },
{ "a" : "13", "b" : "14", "guard" : 4500 },
{ "a" : "13", "b" : "17", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "6", "b" : "13", "guard" : 6000 },
{ "a" : "8", "b" : "13", "guard" : 6000 }
]
}
}
{
"Worlds at War" :
{
"minSize" : "m+u", "maxSize" : "xl+u",
"players" : "2", "cpu" : "3",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 1,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 }
]
},
"2" :
{
"type" : "playerStart",
"size" : 15,
"owner" : 2,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"3" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"4" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 4,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 1
},
"5" :
{
"type" : "cpuStart",
"size" : 15,
"owner" : 5,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasureLikeZone" : 1
},
"6" :
{
"type" : "treasure",
"size" : 30,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "swamp" ],
"mines" : { "sulfur" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 8000, "density" : 8 },
{ "min" : 3000, "max" : 15000, "density" : 6 },
{ "min" : 10000, "max" : 15000, "density" : 1 }
]
},
"7" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt" ],
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 3000, "max" : 6000, "density" : 6 },
{ "min" : 15000, "max" : 20000, "density" : 3 }
]
},
"8" :
{
"type" : "treasure",
"size" : 30,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "rough" ],
"minesLikeZone" : 6,
"treasureLikeZone" : 6
},
"9" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"matchTerrainToTown" : false,
"terrainTypes" : [ "grass" ],
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 3 },
{ "min" : 10000, "max" : 20000, "density" : 2 },
{ "min" : 20000, "max" : 30000, "density" : 1 }
]
},
"10" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasure" :
[
{ "min" : 10000, "max" : 20000, "density" : 1 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 2 }
]
},
"11" :
{
"type" : "treasure",
"size" : 15,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 1,
"treasureLikeZone" : 10
},
"12" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 3000, "max" : 6000, "density" : 4 },
{ "min" : 10000, "max" : 20000, "density" : 3 }
]
},
"13" :
{
"type" : "treasure",
"size" : 35,
"monsters" : "strong",
"neutralTowns" : { "castles" : 1 },
"mines" : { "gold" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 20000, "density" : 6 },
{ "min" : 10000, "max" : 20000, "density" : 6 },
{ "min" : 500, "max" : 3000, "density" : 10 }
]
},
"14" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"15" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"16" :
{
"type" : "treasure",
"size" : 20,
"monsters" : "normal",
"neutralTowns" : { "castles" : 1 },
"minesLikeZone" : 12,
"treasureLikeZone" : 12
},
"17" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"terrainTypeLikeZone" : 9,
"minesLikeZone" : 13,
"treasure" :
[
{ "min" : 500, "max" : 3000, "density" : 6 },
{ "min" : 20000, "max" : 30000, "density" : 1 },
{ "min" : 10000, "max" : 20000, "density" : 4 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "6", "guard" : 3000 },
{ "a" : "2", "b" : "8", "guard" : 3000 },
{ "a" : "3", "b" : "12", "guard" : 4500 },
{ "a" : "4", "b" : "14", "guard" : 4500 },
{ "a" : "5", "b" : "9", "guard" : 6000 },
{ "a" : "5", "b" : "15", "guard" : 3000 },
{ "a" : "5", "b" : "16", "guard" : 3000 },
{ "a" : "6", "b" : "7", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "6", "b" : "8", "guard" : 6000 },
{ "a" : "6", "b" : "10", "guard" : 4500 },
{ "a" : "7", "b" : "8", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "8", "b" : "11", "guard" : 4500 },
{ "a" : "9", "b" : "13", "guard" : 6000 },
{ "a" : "12", "b" : "13", "guard" : 4500 },
{ "a" : "13", "b" : "14", "guard" : 4500 },
{ "a" : "13", "b" : "17", "guard" : 12500 }, // Border guard replaced by 12.5k guard
{ "a" : "6", "b" : "13", "guard" : 6000 },
{ "a" : "8", "b" : "13", "guard" : 6000 }
]
}
}

View File

@ -1,361 +1,361 @@
{
"Gauntlet" :
{
"minSize" : "m+u", "maxSize" : "xl+u",
"players" : "1", "cpu" : "5",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 6,
"owner" : 1,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "necropolis" ],
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "snow" ],
"treasure" :
[
{ "min" : 5000, "max" : 8000, "density" : 1 },
{ "min" : 3000, "max" : 6000, "density" : 2 },
{ "min" : 500, "max" : 3000, "density" : 10 }
]
},
"2" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 10 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"3" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"treasure" :
[
{ "min" : 10000, "max" : 14000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"4" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"mines" : { "wood" : 1 },
"treasureLikeZone" : 2
},
"5" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "sand", "rough" ],
"treasure" :
[
{ "min" : 100, "max" : 2000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 5,
"mines" : { "mercury" : 1, "sulfur" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "swamp" ],
"treasure" :
[
{ "min" : 12000, "max" : 18000, "density" : 1 },
{ "min" : 5000, "max" : 10000, "density" : 5 },
{ "min" : 1000, "max" : 4000, "density" : 7 }
]
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 7,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 6 },
{ "min" : 1000, "max" : 4000, "density" : 7 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"9" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass" ],
"mines" : { "crystal" : 1 },
"treasure" :
[
{ "min" : 15000, "max" : 20000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"10" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "rough" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 4000, "max" : 8000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"11" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 2,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 4 },
{ "min" : 1000, "max" : 4000, "density" : 8 }
]
},
"12" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 3
},
"13" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"14" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"minesLikeZone" : 6,
"treasure" :
[
{ "min" : 4000, "max" : 8000, "density" : 5 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"15" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 4,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"mines" : { "wood" : 1, "ore" : 1, "crystal" : 1 },
"treasureLikeZone" : 11
},
"16" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasure" :
[
{ "min" : 10000, "max" : 14000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 5 },
{ "min" : 500, "max" : 3000, "density" : 7 }
]
},
"17" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 6,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"mines" : { "wood" : 1, "ore" : 1, "gems" : 1 },
"treasureLikeZone" : 14
},
"18" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"19" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"20" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"mines" : { "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 18
},
"21" :
{
"type" : "cpuStart",
"size" : 8,
"owner" : 5,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"minesLikeZone" : 10,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 10 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"22" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"23" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"minesLikeZone" : 18,
"treasure" :
[
{ "min" : 8000, "max" : 12000, "density" : 2 },
{ "min" : 6000, "max" : 10000, "density" : 4 },
{ "min" : 1000, "max" : 5000, "density" : 8 }
]
},
"24" :
{
"type" : "cpuStart",
"size" : 8,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"minesLikeZone" : 17,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 1000, "max" : 4000, "density" : 8 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 0 },
{ "a" : "1", "b" : "4", "guard" : 0 },
{ "a" : "1", "b" : "5", "guard" : 12500 }, // Border guard replaced with monster
{ "a" : "1", "b" : "15", "guard" : 0 },
{ "a" : "2", "b" : "3", "guard" : 0 },
{ "a" : "5", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "21", "guard" : 12500 }, // Border guard replaced with monster
{ "a" : "6", "b" : "7", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "8", "b" : "9", "guard" : 0 },
{ "a" : "9", "b" : "10", "guard" : 0 },
{ "a" : "10", "b" : "11", "guard" : 0 },
{ "a" : "10", "b" : "12", "guard" : 0 },
{ "a" : "12", "b" : "13", "guard" : 0 },
{ "a" : "13", "b" : "14", "guard" : 0 },
{ "a" : "14", "b" : "15", "guard" : 0 },
{ "a" : "15", "b" : "16", "guard" : 0 },
{ "a" : "16", "b" : "17", "guard" : 0 },
{ "a" : "17", "b" : "18", "guard" : 0 },
{ "a" : "18", "b" : "19", "guard" : 0 },
{ "a" : "18", "b" : "22", "guard" : 0 },
{ "a" : "19", "b" : "20", "guard" : 0 },
{ "a" : "20", "b" : "21", "guard" : 0 },
{ "a" : "22", "b" : "23", "guard" : 0 },
{ "a" : "23", "b" : "24", "guard" : 0 }
]
}
}
{
"Gauntlet" :
{
"minSize" : "m+u", "maxSize" : "xl+u",
"players" : "1", "cpu" : "5",
"zones" :
{
"1" :
{
"type" : "playerStart",
"size" : 6,
"owner" : 1,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "necropolis" ],
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "snow" ],
"treasure" :
[
{ "min" : 5000, "max" : 8000, "density" : 1 },
{ "min" : 3000, "max" : 6000, "density" : 2 },
{ "min" : 500, "max" : 3000, "density" : 10 }
]
},
"2" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"mines" : { "ore" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 10 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"3" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"treasure" :
[
{ "min" : 10000, "max" : 14000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"4" :
{
"type" : "treasure",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 1,
"mines" : { "wood" : 1 },
"treasureLikeZone" : 2
},
"5" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "sand", "rough" ],
"treasure" :
[
{ "min" : 100, "max" : 2000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"6" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 5,
"mines" : { "mercury" : 1, "sulfur" : 1, "gems" : 1 },
"treasure" :
[
{ "min" : 3000, "max" : 6000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"7" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "swamp" ],
"treasure" :
[
{ "min" : 12000, "max" : 18000, "density" : 1 },
{ "min" : 5000, "max" : 10000, "density" : 5 },
{ "min" : 1000, "max" : 4000, "density" : 7 }
]
},
"8" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 7,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 6 },
{ "min" : 1000, "max" : 4000, "density" : 7 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"9" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass" ],
"mines" : { "crystal" : 1 },
"treasure" :
[
{ "min" : 15000, "max" : 20000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 9 }
]
},
"10" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"matchTerrainToTown" : false,
"terrainTypes" : [ "dirt", "grass", "rough" ],
"mines" : { "wood" : 1, "mercury" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 4000, "max" : 8000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"11" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 2,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart" ],
"mines" : { "wood" : 1, "ore" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 4 },
{ "min" : 1000, "max" : 4000, "density" : 8 }
]
},
"12" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 3
},
"13" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"14" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"minesLikeZone" : 6,
"treasure" :
[
{ "min" : 4000, "max" : 8000, "density" : 5 },
{ "min" : 500, "max" : 3000, "density" : 8 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"15" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 4,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"mines" : { "wood" : 1, "ore" : 1, "crystal" : 1 },
"treasureLikeZone" : 11
},
"16" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasure" :
[
{ "min" : 10000, "max" : 14000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 5 },
{ "min" : 500, "max" : 3000, "density" : 7 }
]
},
"17" :
{
"type" : "cpuStart",
"size" : 10,
"owner" : 6,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"mines" : { "wood" : 1, "ore" : 1, "gems" : 1 },
"treasureLikeZone" : 14
},
"18" :
{
"type" : "treasure",
"size" : 10,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1 },
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 4 },
{ "min" : 500, "max" : 3000, "density" : 9 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"19" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"20" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "normal",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"mines" : { "sulfur" : 1, "crystal" : 1, "gems" : 1 },
"treasureLikeZone" : 18
},
"21" :
{
"type" : "cpuStart",
"size" : 8,
"owner" : 5,
"monsters" : "weak",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"minesLikeZone" : 10,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 3 },
{ "min" : 500, "max" : 3000, "density" : 10 },
{ "min" : 0, "max" : 0, "density" : 1 }
]
},
"22" :
{
"type" : "junction",
"size" : 6,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"treasureLikeZone" : 5
},
"23" :
{
"type" : "treasure",
"size" : 8,
"monsters" : "weak",
"allowedMonsters" : [ "castle", "rampart", "tower", "stronghold", "fortress", "neutral" ],
"terrainTypeLikeZone" : 10,
"minesLikeZone" : 18,
"treasure" :
[
{ "min" : 8000, "max" : 12000, "density" : 2 },
{ "min" : 6000, "max" : 10000, "density" : 4 },
{ "min" : 1000, "max" : 5000, "density" : 8 }
]
},
"24" :
{
"type" : "cpuStart",
"size" : 8,
"owner" : 3,
"monsters" : "normal",
"playerTowns" : { "castles" : 1 },
"allowedTowns" : [ "castle", "rampart", "stronghold" ],
"minesLikeZone" : 17,
"treasure" :
[
{ "min" : 5000, "max" : 10000, "density" : 1 },
{ "min" : 4000, "max" : 8000, "density" : 3 },
{ "min" : 1000, "max" : 4000, "density" : 8 }
]
}
},
"connections" :
[
{ "a" : "1", "b" : "2", "guard" : 0 },
{ "a" : "1", "b" : "4", "guard" : 0 },
{ "a" : "1", "b" : "5", "guard" : 12500 }, // Border guard replaced with monster
{ "a" : "1", "b" : "15", "guard" : 0 },
{ "a" : "2", "b" : "3", "guard" : 0 },
{ "a" : "5", "b" : "6", "guard" : 0 },
{ "a" : "5", "b" : "21", "guard" : 12500 }, // Border guard replaced with monster
{ "a" : "6", "b" : "7", "guard" : 0 },
{ "a" : "7", "b" : "8", "guard" : 0 },
{ "a" : "8", "b" : "9", "guard" : 0 },
{ "a" : "9", "b" : "10", "guard" : 0 },
{ "a" : "10", "b" : "11", "guard" : 0 },
{ "a" : "10", "b" : "12", "guard" : 0 },
{ "a" : "12", "b" : "13", "guard" : 0 },
{ "a" : "13", "b" : "14", "guard" : 0 },
{ "a" : "14", "b" : "15", "guard" : 0 },
{ "a" : "15", "b" : "16", "guard" : 0 },
{ "a" : "16", "b" : "17", "guard" : 0 },
{ "a" : "17", "b" : "18", "guard" : 0 },
{ "a" : "18", "b" : "19", "guard" : 0 },
{ "a" : "18", "b" : "22", "guard" : 0 },
{ "a" : "19", "b" : "20", "guard" : 0 },
{ "a" : "20", "b" : "21", "guard" : 0 },
{ "a" : "22", "b" : "23", "guard" : 0 },
{ "a" : "23", "b" : "24", "guard" : 0 }
]
}
}

View File

@ -1,115 +1,115 @@
/*
* CGameInfo.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 "CGameInfo.h"
#include "../lib/VCMI_Lib.h"
const CGameInfo * CGI;
CClientState * CCS = nullptr;
CServerHandler * CSH;
CGameInfo::CGameInfo()
{
generaltexth = nullptr;
mh = nullptr;
townh = nullptr;
globalServices = nullptr;
}
void CGameInfo::setFromLib()
{
globalServices = VLC;
modh = VLC->modh;
generaltexth = VLC->generaltexth;
creh = VLC->creh;
townh = VLC->townh;
heroh = VLC->heroh;
objh = VLC->objh;
spellh = VLC->spellh;
skillh = VLC->skillh;
objtypeh = VLC->objtypeh;
terrainTypeHandler = VLC->terrainTypeHandler;
battleFieldHandler = VLC->battlefieldsHandler;
obstacleHandler = VLC->obstacleHandler;
}
const ArtifactService * CGameInfo::artifacts() const
{
return globalServices->artifacts();
}
const BattleFieldService * CGameInfo::battlefields() const
{
return globalServices->battlefields();
}
const CreatureService * CGameInfo::creatures() const
{
return globalServices->creatures();
}
const FactionService * CGameInfo::factions() const
{
return globalServices->factions();
}
const HeroClassService * CGameInfo::heroClasses() const
{
return globalServices->heroClasses();
}
const HeroTypeService * CGameInfo::heroTypes() const
{
return globalServices->heroTypes();
}
#if SCRIPTING_ENABLED
const scripting::Service * CGameInfo::scripts() const
{
return globalServices->scripts();
}
#endif
const spells::Service * CGameInfo::spells() const
{
return globalServices->spells();
}
const SkillService * CGameInfo::skills() const
{
return globalServices->skills();
}
const ObstacleService * CGameInfo::obstacles() const
{
return globalServices->obstacles();
}
const IGameSettings * CGameInfo::settings() const
{
return globalServices->settings();
}
void CGameInfo::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
{
logGlobal->error("CGameInfo::updateEntity call is not expected.");
}
spells::effects::Registry * CGameInfo::spellEffects()
{
return nullptr;
}
const spells::effects::Registry * CGameInfo::spellEffects() const
{
return globalServices->spellEffects();
}
/*
* CGameInfo.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 "CGameInfo.h"
#include "../lib/VCMI_Lib.h"
const CGameInfo * CGI;
CClientState * CCS = nullptr;
CServerHandler * CSH;
CGameInfo::CGameInfo()
{
generaltexth = nullptr;
mh = nullptr;
townh = nullptr;
globalServices = nullptr;
}
void CGameInfo::setFromLib()
{
globalServices = VLC;
modh = VLC->modh;
generaltexth = VLC->generaltexth;
creh = VLC->creh;
townh = VLC->townh;
heroh = VLC->heroh;
objh = VLC->objh;
spellh = VLC->spellh;
skillh = VLC->skillh;
objtypeh = VLC->objtypeh;
terrainTypeHandler = VLC->terrainTypeHandler;
battleFieldHandler = VLC->battlefieldsHandler;
obstacleHandler = VLC->obstacleHandler;
}
const ArtifactService * CGameInfo::artifacts() const
{
return globalServices->artifacts();
}
const BattleFieldService * CGameInfo::battlefields() const
{
return globalServices->battlefields();
}
const CreatureService * CGameInfo::creatures() const
{
return globalServices->creatures();
}
const FactionService * CGameInfo::factions() const
{
return globalServices->factions();
}
const HeroClassService * CGameInfo::heroClasses() const
{
return globalServices->heroClasses();
}
const HeroTypeService * CGameInfo::heroTypes() const
{
return globalServices->heroTypes();
}
#if SCRIPTING_ENABLED
const scripting::Service * CGameInfo::scripts() const
{
return globalServices->scripts();
}
#endif
const spells::Service * CGameInfo::spells() const
{
return globalServices->spells();
}
const SkillService * CGameInfo::skills() const
{
return globalServices->skills();
}
const ObstacleService * CGameInfo::obstacles() const
{
return globalServices->obstacles();
}
const IGameSettings * CGameInfo::settings() const
{
return globalServices->settings();
}
void CGameInfo::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
{
logGlobal->error("CGameInfo::updateEntity call is not expected.");
}
spells::effects::Registry * CGameInfo::spellEffects()
{
return nullptr;
}
const spells::effects::Registry * CGameInfo::spellEffects() const
{
return globalServices->spellEffects();
}

View File

@ -1,101 +1,101 @@
/*
* CGameInfo.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 <vcmi/Services.h>
#include "../lib/ConstTransitivePtr.h"
VCMI_LIB_NAMESPACE_BEGIN
class CModHandler;
class CHeroHandler;
class CCreatureHandler;
class CSpellHandler;
class CSkillHandler;
class CBuildingHandler;
class CObjectHandler;
class CObjectClassesHandler;
class CTownHandler;
class CGeneralTextHandler;
class CConsoleHandler;
class CGameState;
class BattleFieldHandler;
class ObstacleHandler;
class TerrainTypeHandler;
class CMap;
VCMI_LIB_NAMESPACE_END
class CMapHandler;
class CSoundHandler;
class CMusicHandler;
class CursorHandler;
class IMainVideoPlayer;
class CServerHandler;
//a class for non-mechanical client GUI classes
class CClientState
{
public:
CSoundHandler * soundh;
CMusicHandler * musich;
CConsoleHandler * consoleh;
CursorHandler * curh;
IMainVideoPlayer * videoh;
};
extern CClientState * CCS;
/// CGameInfo class
/// for allowing different functions for accessing game informations
class CGameInfo : public Services
{
public:
const ArtifactService * artifacts() const override;
const CreatureService * creatures() const override;
const FactionService * factions() const override;
const HeroClassService * heroClasses() const override;
const HeroTypeService * heroTypes() const override;
#if SCRIPTING_ENABLED
const scripting::Service * scripts() const override;
#endif
const spells::Service * spells() const override;
const SkillService * skills() const override;
const BattleFieldService * battlefields() const override;
const ObstacleService * obstacles() const override;
const IGameSettings * settings() const override;
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
const spells::effects::Registry * spellEffects() const override;
spells::effects::Registry * spellEffects() override;
ConstTransitivePtr<CModHandler> modh; //public?
ConstTransitivePtr<BattleFieldHandler> battleFieldHandler;
ConstTransitivePtr<CHeroHandler> heroh;
ConstTransitivePtr<CCreatureHandler> creh;
ConstTransitivePtr<CSpellHandler> spellh;
ConstTransitivePtr<CSkillHandler> skillh;
ConstTransitivePtr<CObjectHandler> objh;
ConstTransitivePtr<TerrainTypeHandler> terrainTypeHandler;
ConstTransitivePtr<CObjectClassesHandler> objtypeh;
ConstTransitivePtr<ObstacleHandler> obstacleHandler;
CGeneralTextHandler * generaltexth;
CMapHandler * mh;
CTownHandler * townh;
void setFromLib();
CGameInfo();
private:
const Services * globalServices;
};
extern const CGameInfo* CGI;
/*
* CGameInfo.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 <vcmi/Services.h>
#include "../lib/ConstTransitivePtr.h"
VCMI_LIB_NAMESPACE_BEGIN
class CModHandler;
class CHeroHandler;
class CCreatureHandler;
class CSpellHandler;
class CSkillHandler;
class CBuildingHandler;
class CObjectHandler;
class CObjectClassesHandler;
class CTownHandler;
class CGeneralTextHandler;
class CConsoleHandler;
class CGameState;
class BattleFieldHandler;
class ObstacleHandler;
class TerrainTypeHandler;
class CMap;
VCMI_LIB_NAMESPACE_END
class CMapHandler;
class CSoundHandler;
class CMusicHandler;
class CursorHandler;
class IMainVideoPlayer;
class CServerHandler;
//a class for non-mechanical client GUI classes
class CClientState
{
public:
CSoundHandler * soundh;
CMusicHandler * musich;
CConsoleHandler * consoleh;
CursorHandler * curh;
IMainVideoPlayer * videoh;
};
extern CClientState * CCS;
/// CGameInfo class
/// for allowing different functions for accessing game informations
class CGameInfo : public Services
{
public:
const ArtifactService * artifacts() const override;
const CreatureService * creatures() const override;
const FactionService * factions() const override;
const HeroClassService * heroClasses() const override;
const HeroTypeService * heroTypes() const override;
#if SCRIPTING_ENABLED
const scripting::Service * scripts() const override;
#endif
const spells::Service * spells() const override;
const SkillService * skills() const override;
const BattleFieldService * battlefields() const override;
const ObstacleService * obstacles() const override;
const IGameSettings * settings() const override;
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
const spells::effects::Registry * spellEffects() const override;
spells::effects::Registry * spellEffects() override;
ConstTransitivePtr<CModHandler> modh; //public?
ConstTransitivePtr<BattleFieldHandler> battleFieldHandler;
ConstTransitivePtr<CHeroHandler> heroh;
ConstTransitivePtr<CCreatureHandler> creh;
ConstTransitivePtr<CSpellHandler> spellh;
ConstTransitivePtr<CSkillHandler> skillh;
ConstTransitivePtr<CObjectHandler> objh;
ConstTransitivePtr<TerrainTypeHandler> terrainTypeHandler;
ConstTransitivePtr<CObjectClassesHandler> objtypeh;
ConstTransitivePtr<ObstacleHandler> obstacleHandler;
CGeneralTextHandler * generaltexth;
CMapHandler * mh;
CTownHandler * townh;
void setFromLib();
CGameInfo();
private:
const Services * globalServices;
};
extern const CGameInfo* CGI;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +1,167 @@
/*
* CMusicHandler.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 "../lib/CConfigHandler.h"
#include "../lib/CSoundBase.h"
struct _Mix_Music;
struct SDL_RWops;
using Mix_Music = struct _Mix_Music;
struct Mix_Chunk;
class CAudioBase {
protected:
boost::mutex mutex;
bool initialized;
int volume; // from 0 (mute) to 100
public:
CAudioBase(): initialized(false), volume(0) {};
virtual void init() = 0;
virtual void release() = 0;
virtual void setVolume(ui32 percent);
ui32 getVolume() const { return volume; };
};
class CSoundHandler: public CAudioBase
{
private:
//update volume on configuration change
SettingsListener listener;
void onVolumeChange(const JsonNode &volumeNode);
using CachedChunk = std::pair<Mix_Chunk *, std::unique_ptr<ui8[]>>;
std::map<AudioPath, CachedChunk> soundChunks;
std::map<std::vector<ui8>, CachedChunk> soundChunksRaw;
Mix_Chunk *GetSoundChunk(const AudioPath & sound, bool cache);
Mix_Chunk *GetSoundChunk(std::pair<std::unique_ptr<ui8 []>, si64> & data, bool cache);
/// have entry for every currently active channel
/// vector will be empty if callback was not set
std::map<int, std::vector<std::function<void()>> > callbacks;
/// Protects access to callbacks member to avoid data races:
/// SDL calls sound finished callbacks from audio thread
boost::mutex mutexCallbacks;
int ambientDistToVolume(int distance) const;
void ambientStopSound(const AudioPath & soundId);
void updateChannelVolume(int channel);
const JsonNode ambientConfig;
std::map<AudioPath, int> ambientChannels;
std::map<int, int> channelVolumes;
void initCallback(int channel, const std::function<void()> & function);
void initCallback(int channel);
public:
CSoundHandler();
void init() override;
void release() override;
void setVolume(ui32 percent) override;
void setChannelVolume(int channel, ui32 percent);
// Sounds
int playSound(soundBase::soundID soundID, int repeats=0);
int playSound(const AudioPath & sound, int repeats=0, bool cache=false);
int playSound(std::pair<std::unique_ptr<ui8 []>, si64> & data, int repeats=0, bool cache=false);
int playSoundFromSet(std::vector<soundBase::soundID> &sound_vec);
void stopSound(int handler);
void setCallback(int channel, std::function<void()> function);
void soundFinishedCallback(int channel);
int ambientGetRange() const;
void ambientUpdateChannels(std::map<AudioPath, int> currentSounds);
void ambientStopAllChannels();
// Sets
std::vector<soundBase::soundID> battleIntroSounds;
};
class CMusicHandler;
//Class for handling one music file
class MusicEntry
{
CMusicHandler *owner;
Mix_Music *music;
int loop; // -1 = indefinite
bool fromStart;
bool playing;
uint32_t startTime;
uint32_t startPosition;
//if not null - set from which music will be randomly selected
std::string setName;
AudioPath currentName;
void load(const AudioPath & musicURI);
public:
MusicEntry(CMusicHandler *owner, std::string setName, const AudioPath & musicURI, bool looped, bool fromStart);
~MusicEntry();
bool isSet(std::string setName);
bool isTrack(const AudioPath & trackName);
bool isPlaying();
bool play();
bool stop(int fade_ms=0);
};
class CMusicHandler: public CAudioBase
{
private:
//update volume on configuration change
SettingsListener listener;
void onVolumeChange(const JsonNode &volumeNode);
std::unique_ptr<MusicEntry> current;
std::unique_ptr<MusicEntry> next;
void queueNext(CMusicHandler *owner, const std::string & setName, const AudioPath & musicURI, bool looped, bool fromStart);
void queueNext(std::unique_ptr<MusicEntry> queued);
void musicFinishedCallback();
/// map <set name> -> <list of URI's to tracks belonging to the said set>
std::map<std::string, std::vector<AudioPath>> musicsSet;
/// stored position, in seconds at which music player should resume playing this track
std::map<AudioPath, float> trackPositions;
public:
CMusicHandler();
/// add entry with URI musicURI in set. Track will have ID musicID
void addEntryToSet(const std::string & set, const AudioPath & musicURI);
void init() override;
void loadTerrainMusicThemes();
void release() override;
void setVolume(ui32 percent) override;
/// play track by URI, if loop = true music will be looped
void playMusic(const AudioPath & musicURI, bool loop, bool fromStart);
/// play random track from this set
void playMusicFromSet(const std::string & musicSet, bool loop, bool fromStart);
/// play random track from set (musicSet, entryID)
void playMusicFromSet(const std::string & musicSet, const std::string & entryID, bool loop, bool fromStart);
/// stops currently playing music by fading out it over fade_ms and starts next scheduled track, if any
void stopMusic(int fade_ms=1000);
friend class MusicEntry;
};
/*
* CMusicHandler.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 "../lib/CConfigHandler.h"
#include "../lib/CSoundBase.h"
struct _Mix_Music;
struct SDL_RWops;
using Mix_Music = struct _Mix_Music;
struct Mix_Chunk;
class CAudioBase {
protected:
boost::mutex mutex;
bool initialized;
int volume; // from 0 (mute) to 100
public:
CAudioBase(): initialized(false), volume(0) {};
virtual void init() = 0;
virtual void release() = 0;
virtual void setVolume(ui32 percent);
ui32 getVolume() const { return volume; };
};
class CSoundHandler: public CAudioBase
{
private:
//update volume on configuration change
SettingsListener listener;
void onVolumeChange(const JsonNode &volumeNode);
using CachedChunk = std::pair<Mix_Chunk *, std::unique_ptr<ui8[]>>;
std::map<AudioPath, CachedChunk> soundChunks;
std::map<std::vector<ui8>, CachedChunk> soundChunksRaw;
Mix_Chunk *GetSoundChunk(const AudioPath & sound, bool cache);
Mix_Chunk *GetSoundChunk(std::pair<std::unique_ptr<ui8 []>, si64> & data, bool cache);
/// have entry for every currently active channel
/// vector will be empty if callback was not set
std::map<int, std::vector<std::function<void()>> > callbacks;
/// Protects access to callbacks member to avoid data races:
/// SDL calls sound finished callbacks from audio thread
boost::mutex mutexCallbacks;
int ambientDistToVolume(int distance) const;
void ambientStopSound(const AudioPath & soundId);
void updateChannelVolume(int channel);
const JsonNode ambientConfig;
std::map<AudioPath, int> ambientChannels;
std::map<int, int> channelVolumes;
void initCallback(int channel, const std::function<void()> & function);
void initCallback(int channel);
public:
CSoundHandler();
void init() override;
void release() override;
void setVolume(ui32 percent) override;
void setChannelVolume(int channel, ui32 percent);
// Sounds
int playSound(soundBase::soundID soundID, int repeats=0);
int playSound(const AudioPath & sound, int repeats=0, bool cache=false);
int playSound(std::pair<std::unique_ptr<ui8 []>, si64> & data, int repeats=0, bool cache=false);
int playSoundFromSet(std::vector<soundBase::soundID> &sound_vec);
void stopSound(int handler);
void setCallback(int channel, std::function<void()> function);
void soundFinishedCallback(int channel);
int ambientGetRange() const;
void ambientUpdateChannels(std::map<AudioPath, int> currentSounds);
void ambientStopAllChannels();
// Sets
std::vector<soundBase::soundID> battleIntroSounds;
};
class CMusicHandler;
//Class for handling one music file
class MusicEntry
{
CMusicHandler *owner;
Mix_Music *music;
int loop; // -1 = indefinite
bool fromStart;
bool playing;
uint32_t startTime;
uint32_t startPosition;
//if not null - set from which music will be randomly selected
std::string setName;
AudioPath currentName;
void load(const AudioPath & musicURI);
public:
MusicEntry(CMusicHandler *owner, std::string setName, const AudioPath & musicURI, bool looped, bool fromStart);
~MusicEntry();
bool isSet(std::string setName);
bool isTrack(const AudioPath & trackName);
bool isPlaying();
bool play();
bool stop(int fade_ms=0);
};
class CMusicHandler: public CAudioBase
{
private:
//update volume on configuration change
SettingsListener listener;
void onVolumeChange(const JsonNode &volumeNode);
std::unique_ptr<MusicEntry> current;
std::unique_ptr<MusicEntry> next;
void queueNext(CMusicHandler *owner, const std::string & setName, const AudioPath & musicURI, bool looped, bool fromStart);
void queueNext(std::unique_ptr<MusicEntry> queued);
void musicFinishedCallback();
/// map <set name> -> <list of URI's to tracks belonging to the said set>
std::map<std::string, std::vector<AudioPath>> musicsSet;
/// stored position, in seconds at which music player should resume playing this track
std::map<AudioPath, float> trackPositions;
public:
CMusicHandler();
/// add entry with URI musicURI in set. Track will have ID musicID
void addEntryToSet(const std::string & set, const AudioPath & musicURI);
void init() override;
void loadTerrainMusicThemes();
void release() override;
void setVolume(ui32 percent) override;
/// play track by URI, if loop = true music will be looped
void playMusic(const AudioPath & musicURI, bool loop, bool fromStart);
/// play random track from this set
void playMusicFromSet(const std::string & musicSet, bool loop, bool fromStart);
/// play random track from set (musicSet, entryID)
void playMusicFromSet(const std::string & musicSet, const std::string & entryID, bool loop, bool fromStart);
/// stops currently playing music by fading out it over fade_ms and starts next scheduled track, if any
void stopMusic(int fade_ms=1000);
friend class MusicEntry;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,234 +1,234 @@
/*
* CPlayerInterface.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 "../lib/FunctionList.h"
#include "../lib/CGameInterface.h"
#include "gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class Artifact;
struct TryMoveHero;
class CGHeroInstance;
class CStack;
class CCreature;
struct CGPath;
class CCreatureSet;
class CGObjectInstance;
struct UpgradeInfo;
template <typename T> struct CondSh;
struct CPathsInfo;
VCMI_LIB_NAMESPACE_END
class CButton;
class AdventureMapInterface;
class CCastleInterface;
class BattleInterface;
class CComponent;
class CSelectableComponent;
class CSlider;
class CInGameConsole;
class CInfoWindow;
class IShowActivatable;
class ClickableL;
class ClickableR;
class Hoverable;
class KeyInterested;
class MotionInterested;
class PlayerLocalState;
class TimeInterested;
class HeroMovementController;
namespace boost
{
class mutex;
class recursive_mutex;
}
/// Central class for managing user interface logic
class CPlayerInterface : public CGameInterface, public IUpdateable
{
bool ignoreEvents;
size_t numOfMovedArts;
// -1 - just loaded game; 1 - just started game; 0 otherwise
int firstCall;
int autosaveCount;
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
std::unique_ptr<HeroMovementController> movementController;
public: // TODO: make private
std::shared_ptr<Environment> env;
std::unique_ptr<PlayerLocalState> localState;
//minor interfaces
CondSh<bool> *showingDialog; //indicates if dialog box is displayed
bool makingTurn; //if player is already making his turn
CCastleInterface * castleInt; //nullptr if castle window isn't opened
static std::shared_ptr<BattleInterface> battleInt; //nullptr if no battle
CInGameConsole * cingconsole;
std::shared_ptr<CCallback> cb; //to communicate with engine
//During battle is quick combat mode is used
std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
protected: // Call-ins from server, should not be called directly, but only via GameInterface
void update() override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) override; //what: 1 - built, 2 - demolished
void artifactPut(const ArtifactLocation &al) override;
void artifactRemoved(const ArtifactLocation &al) override;
void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) override;
void bulkArtMovementStart(size_t numOfArts) override;
void artifactAssembled(const ArtifactLocation &al) override;
void askToAssembleArtifact(const ArtifactLocation & dst) override;
void artifactDisassembled(const ArtifactLocation &al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
void heroCreated(const CGHeroInstance* hero) override;
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
void heroInGarrisonChange(const CGTownInstance *town) override;
void heroMoved(const TryMoveHero & details, bool verbose = true) override;
void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
void heroManaPointsChanged(const CGHeroInstance * hero) override;
void heroMovePointsChanged(const CGHeroInstance * hero) override;
void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) override;
void receivedResource() override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level, QueryID queryID) override;
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell
void tileHidden(const std::unordered_set<int3> &pos) override; //called when given tiles become hidden under fog of war
void tileRevealed(const std::unordered_set<int3> &pos) override; //called when fog of war disappears from given tiles
void newObject(const CGObjectInstance * obj) override;
void availableArtifactsChanged(const CGBlackMarket *bm = nullptr) override; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
void yourTurn(QueryID queryID) override;
void availableCreaturesChanged(const CGDwelling *town) override;
void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain) override;//if gain hero received bonus, else he lost it
void playerBonusChanged(const Bonus &bonus, bool gain) override;
void requestRealized(PackageApplied *pa) override;
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
void centerView (int3 pos, int focusTime) override;
void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
void objectPropertyChanged(const SetObjectProperty * sop) override;
void objectRemoved(const CGObjectInstance *obj, const PlayerColor & initiator) override;
void objectRemovedAfter() override;
void playerBlocked(int reason, bool start) override;
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
void playerEndsTurn(PlayerColor player) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
//for battles
void actionFinished(const BattleID & battleID, const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero
void actionStarted(const BattleID & battleID, const BattleAction& action) override;//occurs BEFORE action taken by active stack or by the hero
void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //stack performs attack
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override; //end of battle
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override; //called when a specific effect is set to stacks
void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect
void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
void battleStartBefore(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleUnitsChanged(const BattleID & battleID, const std::vector<UnitChanges> & units) override;
void battleObstaclesChanged(const BattleID & battleID, const std::vector<ObstacleChanges> & obstacles) override;
void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack
void battleGateStateChanged(const BattleID & battleID, const EGateState state) override;
void yourTacticPhase(const BattleID & battleID, int distance) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
public: // public interface for use by client via LOCPLINT access
// part of GameInterface that is also used by client code
void showPuzzleMap() override;
void viewWorldMap() override;
void showQuestLog() override;
void showThievesGuildWindow (const CGObjectInstance * obj) override;
void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2);
void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<void()> onYes);
void waitWhileDialog();
void waitForAllDialogs();
void openTownWindow(const CGTownInstance * town); //shows townscreen
void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
void showInfoDialog(const std::string &text, std::shared_ptr<CComponent> component);
void showInfoDialog(const std::string &text, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>(), int soundID = 0);
void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
void showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>());
void moveHero(const CGHeroInstance *h, const CGPath& path);
void tryDigging(const CGHeroInstance *h);
void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
void proposeLoadingGame();
void performAutosave();
void gamePause(bool pause);
///returns true if all events are processed internally
bool capturedAllEvents();
CPlayerInterface(PlayerColor Player);
~CPlayerInterface();
private:
struct IgnoreEvents
{
CPlayerInterface & owner;
IgnoreEvents(CPlayerInterface & Owner):owner(Owner)
{
owner.ignoreEvents = true;
};
~IgnoreEvents()
{
owner.ignoreEvents = false;
};
};
void heroKilled(const CGHeroInstance* hero);
void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
void requestReturningToMainMenu(bool won);
void acceptTurn(QueryID queryID); //used during hot seat after your turn message is close
void initializeHeroTownList();
int getLastIndex(std::string namePrefix);
};
/// Provides global access to instance of interface of currently active player
extern CPlayerInterface * LOCPLINT;
/*
* CPlayerInterface.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 "../lib/FunctionList.h"
#include "../lib/CGameInterface.h"
#include "gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class Artifact;
struct TryMoveHero;
class CGHeroInstance;
class CStack;
class CCreature;
struct CGPath;
class CCreatureSet;
class CGObjectInstance;
struct UpgradeInfo;
template <typename T> struct CondSh;
struct CPathsInfo;
VCMI_LIB_NAMESPACE_END
class CButton;
class AdventureMapInterface;
class CCastleInterface;
class BattleInterface;
class CComponent;
class CSelectableComponent;
class CSlider;
class CInGameConsole;
class CInfoWindow;
class IShowActivatable;
class ClickableL;
class ClickableR;
class Hoverable;
class KeyInterested;
class MotionInterested;
class PlayerLocalState;
class TimeInterested;
class HeroMovementController;
namespace boost
{
class mutex;
class recursive_mutex;
}
/// Central class for managing user interface logic
class CPlayerInterface : public CGameInterface, public IUpdateable
{
bool ignoreEvents;
size_t numOfMovedArts;
// -1 - just loaded game; 1 - just started game; 0 otherwise
int firstCall;
int autosaveCount;
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
std::unique_ptr<HeroMovementController> movementController;
public: // TODO: make private
std::shared_ptr<Environment> env;
std::unique_ptr<PlayerLocalState> localState;
//minor interfaces
CondSh<bool> *showingDialog; //indicates if dialog box is displayed
bool makingTurn; //if player is already making his turn
CCastleInterface * castleInt; //nullptr if castle window isn't opened
static std::shared_ptr<BattleInterface> battleInt; //nullptr if no battle
CInGameConsole * cingconsole;
std::shared_ptr<CCallback> cb; //to communicate with engine
//During battle is quick combat mode is used
std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
protected: // Call-ins from server, should not be called directly, but only via GameInterface
void update() override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) override; //what: 1 - built, 2 - demolished
void artifactPut(const ArtifactLocation &al) override;
void artifactRemoved(const ArtifactLocation &al) override;
void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) override;
void bulkArtMovementStart(size_t numOfArts) override;
void artifactAssembled(const ArtifactLocation &al) override;
void askToAssembleArtifact(const ArtifactLocation & dst) override;
void artifactDisassembled(const ArtifactLocation &al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
void heroCreated(const CGHeroInstance* hero) override;
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
void heroInGarrisonChange(const CGTownInstance *town) override;
void heroMoved(const TryMoveHero & details, bool verbose = true) override;
void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
void heroManaPointsChanged(const CGHeroInstance * hero) override;
void heroMovePointsChanged(const CGHeroInstance * hero) override;
void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) override;
void receivedResource() override;
void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level, QueryID queryID) override;
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell
void tileHidden(const std::unordered_set<int3> &pos) override; //called when given tiles become hidden under fog of war
void tileRevealed(const std::unordered_set<int3> &pos) override; //called when fog of war disappears from given tiles
void newObject(const CGObjectInstance * obj) override;
void availableArtifactsChanged(const CGBlackMarket *bm = nullptr) override; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
void yourTurn(QueryID queryID) override;
void availableCreaturesChanged(const CGDwelling *town) override;
void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain) override;//if gain hero received bonus, else he lost it
void playerBonusChanged(const Bonus &bonus, bool gain) override;
void requestRealized(PackageApplied *pa) override;
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
void centerView (int3 pos, int focusTime) override;
void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
void objectPropertyChanged(const SetObjectProperty * sop) override;
void objectRemoved(const CGObjectInstance *obj, const PlayerColor & initiator) override;
void objectRemovedAfter() override;
void playerBlocked(int reason, bool start) override;
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
void playerEndsTurn(PlayerColor player) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
//for battles
void actionFinished(const BattleID & battleID, const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero
void actionStarted(const BattleID & battleID, const BattleAction& action) override;//occurs BEFORE action taken by active stack or by the hero
void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //stack performs attack
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override; //end of battle
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override; //called when a specific effect is set to stacks
void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect
void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
void battleStartBefore(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleUnitsChanged(const BattleID & battleID, const std::vector<UnitChanges> & units) override;
void battleObstaclesChanged(const BattleID & battleID, const std::vector<ObstacleChanges> & obstacles) override;
void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack
void battleGateStateChanged(const BattleID & battleID, const EGateState state) override;
void yourTacticPhase(const BattleID & battleID, int distance) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
public: // public interface for use by client via LOCPLINT access
// part of GameInterface that is also used by client code
void showPuzzleMap() override;
void viewWorldMap() override;
void showQuestLog() override;
void showThievesGuildWindow (const CGObjectInstance * obj) override;
void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2);
void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<void()> onYes);
void waitWhileDialog();
void waitForAllDialogs();
void openTownWindow(const CGTownInstance * town); //shows townscreen
void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
void showInfoDialog(const std::string &text, std::shared_ptr<CComponent> component);
void showInfoDialog(const std::string &text, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>(), int soundID = 0);
void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
void showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>());
void moveHero(const CGHeroInstance *h, const CGPath& path);
void tryDigging(const CGHeroInstance *h);
void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
void proposeLoadingGame();
void performAutosave();
void gamePause(bool pause);
///returns true if all events are processed internally
bool capturedAllEvents();
CPlayerInterface(PlayerColor Player);
~CPlayerInterface();
private:
struct IgnoreEvents
{
CPlayerInterface & owner;
IgnoreEvents(CPlayerInterface & Owner):owner(Owner)
{
owner.ignoreEvents = true;
};
~IgnoreEvents()
{
owner.ignoreEvents = false;
};
};
void heroKilled(const CGHeroInstance* hero);
void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
void requestReturningToMainMenu(bool won);
void acceptTurn(QueryID queryID); //used during hot seat after your turn message is close
void initializeHeroTownList();
int getLastIndex(std::string namePrefix);
};
/// Provides global access to instance of interface of currently active player
extern CPlayerInterface * LOCPLINT;

File diff suppressed because it is too large Load Diff

View File

@ -1,121 +1,121 @@
/*
* CVideoHandler.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 "../lib/Rect.h"
#include "../lib/filesystem/ResourcePath.h"
struct SDL_Surface;
struct SDL_Texture;
class IVideoPlayer : boost::noncopyable
{
public:
virtual bool open(const VideoPath & name, bool scale = false)=0; //true - succes
virtual void close()=0;
virtual bool nextFrame()=0;
virtual void show(int x, int y, SDL_Surface *dst, bool update = true)=0;
virtual void redraw(int x, int y, SDL_Surface *dst, bool update = true)=0; //reblits buffer
virtual bool wait()=0;
virtual int curFrame() const =0;
virtual int frameCount() const =0;
};
class IMainVideoPlayer : public IVideoPlayer
{
public:
virtual void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> restart = 0){}
virtual bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false)
{
return false;
}
virtual std::pair<std::unique_ptr<ui8 []>, si64> getAudio(const VideoPath & videoToOpen) { return std::make_pair(nullptr, 0); };
};
class CEmptyVideoPlayer : public IMainVideoPlayer
{
public:
int curFrame() const override {return -1;};
int frameCount() const override {return -1;};
void redraw( int x, int y, SDL_Surface *dst, bool update = true ) override {};
void show( int x, int y, SDL_Surface *dst, bool update = true ) override {};
bool nextFrame() override {return false;};
void close() override {};
bool wait() override {return false;};
bool open(const VideoPath & name, bool scale = false) override {return false;};
};
#ifndef DISABLE_VIDEO
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFrame;
struct AVIOContext;
VCMI_LIB_NAMESPACE_BEGIN
class CInputStream;
VCMI_LIB_NAMESPACE_END
class CVideoPlayer : public IMainVideoPlayer
{
int stream; // stream index in video
AVFormatContext *format;
AVCodecContext *codecContext; // codec context for stream
const AVCodec *codec;
AVFrame *frame;
struct SwsContext *sws;
AVIOContext * context;
VideoPath fname; //name of current video file (empty if idle)
// Destination. Either overlay or dest.
SDL_Texture *texture;
SDL_Surface *dest;
Rect destRect; // valid when dest is used
Rect pos; // destination on screen
/// video playback currnet progress, in seconds
double frameTime;
bool doLoop; // loop through video
bool playVideo(int x, int y, bool stopOnKey);
bool open(const VideoPath & fname, bool loop, bool useOverlay = false, bool scale = false);
public:
CVideoPlayer();
~CVideoPlayer();
bool init();
bool open(const VideoPath & fname, bool scale = false) override;
void close() override;
bool nextFrame() override; // display next frame
void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame
void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer
void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> onVideoRestart = nullptr) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false) override;
std::pair<std::unique_ptr<ui8 []>, si64> getAudio(const VideoPath & videoToOpen) override;
//TODO:
bool wait() override {return false;};
int curFrame() const override {return -1;};
int frameCount() const override {return -1;};
// public to allow access from ffmpeg IO functions
std::unique_ptr<CInputStream> data;
std::unique_ptr<CInputStream> dataAudio;
};
#endif
/*
* CVideoHandler.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 "../lib/Rect.h"
#include "../lib/filesystem/ResourcePath.h"
struct SDL_Surface;
struct SDL_Texture;
class IVideoPlayer : boost::noncopyable
{
public:
virtual bool open(const VideoPath & name, bool scale = false)=0; //true - succes
virtual void close()=0;
virtual bool nextFrame()=0;
virtual void show(int x, int y, SDL_Surface *dst, bool update = true)=0;
virtual void redraw(int x, int y, SDL_Surface *dst, bool update = true)=0; //reblits buffer
virtual bool wait()=0;
virtual int curFrame() const =0;
virtual int frameCount() const =0;
};
class IMainVideoPlayer : public IVideoPlayer
{
public:
virtual void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> restart = 0){}
virtual bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false)
{
return false;
}
virtual std::pair<std::unique_ptr<ui8 []>, si64> getAudio(const VideoPath & videoToOpen) { return std::make_pair(nullptr, 0); };
};
class CEmptyVideoPlayer : public IMainVideoPlayer
{
public:
int curFrame() const override {return -1;};
int frameCount() const override {return -1;};
void redraw( int x, int y, SDL_Surface *dst, bool update = true ) override {};
void show( int x, int y, SDL_Surface *dst, bool update = true ) override {};
bool nextFrame() override {return false;};
void close() override {};
bool wait() override {return false;};
bool open(const VideoPath & name, bool scale = false) override {return false;};
};
#ifndef DISABLE_VIDEO
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFrame;
struct AVIOContext;
VCMI_LIB_NAMESPACE_BEGIN
class CInputStream;
VCMI_LIB_NAMESPACE_END
class CVideoPlayer : public IMainVideoPlayer
{
int stream; // stream index in video
AVFormatContext *format;
AVCodecContext *codecContext; // codec context for stream
const AVCodec *codec;
AVFrame *frame;
struct SwsContext *sws;
AVIOContext * context;
VideoPath fname; //name of current video file (empty if idle)
// Destination. Either overlay or dest.
SDL_Texture *texture;
SDL_Surface *dest;
Rect destRect; // valid when dest is used
Rect pos; // destination on screen
/// video playback currnet progress, in seconds
double frameTime;
bool doLoop; // loop through video
bool playVideo(int x, int y, bool stopOnKey);
bool open(const VideoPath & fname, bool loop, bool useOverlay = false, bool scale = false);
public:
CVideoPlayer();
~CVideoPlayer();
bool init();
bool open(const VideoPath & fname, bool scale = false) override;
void close() override;
bool nextFrame() override; // display next frame
void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame
void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer
void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> onVideoRestart = nullptr) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false) override;
std::pair<std::unique_ptr<ui8 []>, si64> getAudio(const VideoPath & videoToOpen) override;
//TODO:
bool wait() override {return false;};
int curFrame() const override {return -1;};
int frameCount() const override {return -1;};
// public to allow access from ffmpeg IO functions
std::unique_ptr<CInputStream> data;
std::unique_ptr<CInputStream> dataAudio;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,240 +1,240 @@
/*
* Client.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 <memory>
#include <vcmi/Environment.h>
#include "../lib/IGameCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CPack;
struct CPackForServer;
class IBattleEventsReceiver;
class CBattleGameInterface;
class CGameInterface;
class BinaryDeserializer;
class BinarySerializer;
class BattleAction;
class BattleInfo;
template<typename T> class CApplier;
#if SCRIPTING_ENABLED
namespace scripting
{
class PoolImpl;
}
#endif
namespace events
{
class EventBus;
}
VCMI_LIB_NAMESPACE_END
class CBattleCallback;
class CCallback;
class CClient;
class CBaseForCLApply;
namespace boost { class thread; }
template<typename T>
class ThreadSafeVector
{
using TLock = boost::unique_lock<boost::mutex>;
std::vector<T> items;
boost::mutex mx;
boost::condition_variable cond;
public:
void clear()
{
TLock lock(mx);
items.clear();
cond.notify_all();
}
void pushBack(const T & item)
{
TLock lock(mx);
items.push_back(item);
cond.notify_all();
}
void waitWhileContains(const T & item)
{
TLock lock(mx);
while(vstd::contains(items, item))
cond.wait(lock);
}
bool tryRemovingElement(const T & item) //returns false if element was not present
{
TLock lock(mx);
auto itr = vstd::find(items, item);
if(itr == items.end()) //not in container
{
return false;
}
items.erase(itr);
cond.notify_all();
return true;
}
};
class CPlayerEnvironment : public Environment
{
public:
PlayerColor player;
CClient * cl;
std::shared_ptr<CCallback> mainCallback;
CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_);
const Services * services() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;
const BattleCb * battle(const BattleID & battle) const override;
const GameCb * game() const override;
};
/// Class which handles client - server logic
class CClient : public IGameCallback, public Environment
{
public:
std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
std::map<PlayerColor, std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
std::unique_ptr<BattleAction> currentBattleAction;
CClient();
~CClient();
const Services * services() const override;
const BattleCb * battle(const BattleID & battle) const override;
const GameCb * game() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;
void newGame(CGameState * gameState);
void loadGame(CGameState * gameState);
void serialize(BinarySerializer & h, const int version);
void serialize(BinaryDeserializer & h, const int version);
void save(const std::string & fname);
void endGame();
void initMapHandler();
void initPlayerEnvironments();
void initPlayerInterfaces();
std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman); //empty means no AI -> human
std::string aiNameForPlayer(bool battleAI, bool alliedToHuman);
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
void handlePack(CPack * pack); //applies the given pack and deletes it
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
void battleStarted(const BattleInfo * info);
void battleFinished(const BattleID & battleID);
void startPlayerBattleAction(const BattleID & battleID, PlayerColor color);
void invalidatePaths();
std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h);
friend class CCallback; //handling players actions
friend class CBattleCallback; //handling players actions
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype ) override {};
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {};
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};
void showBlockingDialog(BlockingDialog * iw) override {};
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
void showTeleportDialog(TeleportDialog * iw) override {};
void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override {};
void giveResource(PlayerColor player, GameResID which, int val) override {};
virtual void giveResources(PlayerColor player, TResources resources) override {};
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet & creatures, bool remove) override {};
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures) override {};
bool changeStackType(const StackLocation & sl, const CCreature * c) override {return false;};
bool changeStackCount(const StackLocation & sl, TQuantity count, bool absoluteValue = false) override {return false;};
bool insertNewStack(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;};
bool eraseStack(const StackLocation & sl, bool forceRemoval = false) override {return false;};
bool swapStacks(const StackLocation & sl1, const StackLocation & sl2) override {return false;}
bool addToSlot(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;}
void tryJoiningArmy(const CArmedInstance * src, const CArmedInstance * dst, bool removeObjWhenFinished, bool allowMerging) override {}
bool moveStack(const StackLocation & src, const StackLocation & dst, TQuantity count = -1) override {return false;}
void removeAfterVisit(const CGObjectInstance * object) override {};
bool swapGarrisonOnSiege(ObjectInstanceID tid) override {return false;};
bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) override {return false;}
bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override {return false;}
void putArtifact(const ArtifactLocation & al, const CArtifactInstance * a) override {};
void removeArtifact(const ArtifactLocation & al) override {};
bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;};
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void startBattlePrimary(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank = false, const CGTownInstance * town = nullptr) override {}; //use hero=nullptr for no hero
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;};
void giveHeroBonus(GiveBonus * bonus) override {};
void setMovePoints(SetMovePoints * smp) override {};
void setManaPoints(ObjectInstanceID hid, int val) override {};
void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {};
void sendAndApply(CPackForClient * pack) override {};
void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {}
void changeFogOfWar(std::unordered_set<int3> & tiles, PlayerColor player, bool hide) override {}
void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {}
void showInfoDialog(InfoWindow * iw) override {};
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
void removeGUI();
#if SCRIPTING_ENABLED
scripting::Pool * getGlobalContextPool() const override;
#endif
private:
std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
std::map<PlayerColor, std::shared_ptr<CPlayerEnvironment>> playerEnvironments;
#if SCRIPTING_ENABLED
std::shared_ptr<scripting::PoolImpl> clientScripts;
#endif
std::unique_ptr<events::EventBus> clientEventBus;
std::shared_ptr<CApplier<CBaseForCLApply>> applier;
mutable boost::mutex pathCacheMutex;
std::map<const CGHeroInstance *, std::shared_ptr<CPathsInfo>> pathCache;
void reinitScripting();
};
/*
* Client.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 <memory>
#include <vcmi/Environment.h>
#include "../lib/IGameCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CPack;
struct CPackForServer;
class IBattleEventsReceiver;
class CBattleGameInterface;
class CGameInterface;
class BinaryDeserializer;
class BinarySerializer;
class BattleAction;
class BattleInfo;
template<typename T> class CApplier;
#if SCRIPTING_ENABLED
namespace scripting
{
class PoolImpl;
}
#endif
namespace events
{
class EventBus;
}
VCMI_LIB_NAMESPACE_END
class CBattleCallback;
class CCallback;
class CClient;
class CBaseForCLApply;
namespace boost { class thread; }
template<typename T>
class ThreadSafeVector
{
using TLock = boost::unique_lock<boost::mutex>;
std::vector<T> items;
boost::mutex mx;
boost::condition_variable cond;
public:
void clear()
{
TLock lock(mx);
items.clear();
cond.notify_all();
}
void pushBack(const T & item)
{
TLock lock(mx);
items.push_back(item);
cond.notify_all();
}
void waitWhileContains(const T & item)
{
TLock lock(mx);
while(vstd::contains(items, item))
cond.wait(lock);
}
bool tryRemovingElement(const T & item) //returns false if element was not present
{
TLock lock(mx);
auto itr = vstd::find(items, item);
if(itr == items.end()) //not in container
{
return false;
}
items.erase(itr);
cond.notify_all();
return true;
}
};
class CPlayerEnvironment : public Environment
{
public:
PlayerColor player;
CClient * cl;
std::shared_ptr<CCallback> mainCallback;
CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_);
const Services * services() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;
const BattleCb * battle(const BattleID & battle) const override;
const GameCb * game() const override;
};
/// Class which handles client - server logic
class CClient : public IGameCallback, public Environment
{
public:
std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
std::map<PlayerColor, std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
std::unique_ptr<BattleAction> currentBattleAction;
CClient();
~CClient();
const Services * services() const override;
const BattleCb * battle(const BattleID & battle) const override;
const GameCb * game() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;
void newGame(CGameState * gameState);
void loadGame(CGameState * gameState);
void serialize(BinarySerializer & h, const int version);
void serialize(BinaryDeserializer & h, const int version);
void save(const std::string & fname);
void endGame();
void initMapHandler();
void initPlayerEnvironments();
void initPlayerInterfaces();
std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman); //empty means no AI -> human
std::string aiNameForPlayer(bool battleAI, bool alliedToHuman);
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
void handlePack(CPack * pack); //applies the given pack and deletes it
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
void battleStarted(const BattleInfo * info);
void battleFinished(const BattleID & battleID);
void startPlayerBattleAction(const BattleID & battleID, PlayerColor color);
void invalidatePaths();
std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h);
friend class CCallback; //handling players actions
friend class CBattleCallback; //handling players actions
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
void createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype ) override {};
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {};
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};
void showBlockingDialog(BlockingDialog * iw) override {};
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
void showTeleportDialog(TeleportDialog * iw) override {};
void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override {};
void giveResource(PlayerColor player, GameResID which, int val) override {};
virtual void giveResources(PlayerColor player, TResources resources) override {};
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet & creatures, bool remove) override {};
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures) override {};
bool changeStackType(const StackLocation & sl, const CCreature * c) override {return false;};
bool changeStackCount(const StackLocation & sl, TQuantity count, bool absoluteValue = false) override {return false;};
bool insertNewStack(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;};
bool eraseStack(const StackLocation & sl, bool forceRemoval = false) override {return false;};
bool swapStacks(const StackLocation & sl1, const StackLocation & sl2) override {return false;}
bool addToSlot(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;}
void tryJoiningArmy(const CArmedInstance * src, const CArmedInstance * dst, bool removeObjWhenFinished, bool allowMerging) override {}
bool moveStack(const StackLocation & src, const StackLocation & dst, TQuantity count = -1) override {return false;}
void removeAfterVisit(const CGObjectInstance * object) override {};
bool swapGarrisonOnSiege(ObjectInstanceID tid) override {return false;};
bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) override {return false;}
bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override {return false;}
void putArtifact(const ArtifactLocation & al, const CArtifactInstance * a) override {};
void removeArtifact(const ArtifactLocation & al) override {};
bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;};
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
void startBattlePrimary(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank = false, const CGTownInstance * town = nullptr) override {}; //use hero=nullptr for no hero
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;};
void giveHeroBonus(GiveBonus * bonus) override {};
void setMovePoints(SetMovePoints * smp) override {};
void setManaPoints(ObjectInstanceID hid, int val) override {};
void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {};
void sendAndApply(CPackForClient * pack) override {};
void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {}
void changeFogOfWar(std::unordered_set<int3> & tiles, PlayerColor player, bool hide) override {}
void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {}
void showInfoDialog(InfoWindow * iw) override {};
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
void removeGUI();
#if SCRIPTING_ENABLED
scripting::Pool * getGlobalContextPool() const override;
#endif
private:
std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
std::map<PlayerColor, std::shared_ptr<CPlayerEnvironment>> playerEnvironments;
#if SCRIPTING_ENABLED
std::shared_ptr<scripting::PoolImpl> clientScripts;
#endif
std::unique_ptr<events::EventBus> clientEventBus;
std::shared_ptr<CApplier<CBaseForCLApply>> applier;
mutable boost::mutex pathCacheMutex;
std::map<const CGHeroInstance *, std::shared_ptr<CPathsInfo>> pathCache;
void reinitScripting();
};

File diff suppressed because it is too large Load Diff

View File

@ -1,271 +1,271 @@
/*
* PlayerLocalState.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 "PlayerLocalState.h"
#include "../CCallback.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/pathfinder/CGPathNode.h"
#include "CPlayerInterface.h"
#include "adventureMap/AdventureMapInterface.h"
PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
: owner(owner)
, currentSelection(nullptr)
{
}
void PlayerLocalState::saveHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
{
for(auto & p : paths)
{
if(p.second.nodes.size())
pathsMap[p.first] = p.second.endPos();
else
logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->getNameTranslated());
}
}
void PlayerLocalState::loadHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
{
if(owner.cb)
{
for(auto & p : pathsMap)
{
CGPath path;
owner.cb->getPathsInfo(p.first)->getPath(path, p.second);
paths[p.first] = path;
logGlobal->trace("Restored path for hero %s leading to %s with %d nodes", p.first->nodeName(), p.second.toString(), path.nodes.size());
}
}
}
void PlayerLocalState::setPath(const CGHeroInstance * h, const CGPath & path)
{
paths[h] = path;
}
const CGPath & PlayerLocalState::getPath(const CGHeroInstance * h) const
{
assert(hasPath(h));
return paths.at(h);
}
bool PlayerLocalState::hasPath(const CGHeroInstance * h) const
{
return paths.count(h) > 0;
}
bool PlayerLocalState::setPath(const CGHeroInstance * h, const int3 & destination)
{
CGPath path;
if(!owner.cb->getPathsInfo(h)->getPath(path, destination))
{
paths.erase(h); //invalidate previously possible path if selected (before other hero blocked only path / fly spell expired)
return false;
}
setPath(h, path);
return true;
}
void PlayerLocalState::removeLastNode(const CGHeroInstance * h)
{
assert(hasPath(h));
if(!hasPath(h))
return;
auto & path = paths[h];
path.nodes.pop_back();
if(path.nodes.size() < 2) //if it was the last one, remove entire path and path with only one tile is not a real path
erasePath(h);
}
void PlayerLocalState::erasePath(const CGHeroInstance * h)
{
paths.erase(h);
adventureInt->onHeroChanged(h);
}
void PlayerLocalState::verifyPath(const CGHeroInstance * h)
{
if(!hasPath(h))
return;
setPath(h, getPath(h).endPos());
}
const CGHeroInstance * PlayerLocalState::getCurrentHero() const
{
if(currentSelection && currentSelection->ID == Obj::HERO)
return dynamic_cast<const CGHeroInstance *>(currentSelection);
else
return nullptr;
}
const CGHeroInstance * PlayerLocalState::getNextWanderingHero(const CGHeroInstance * currentHero)
{
bool currentHeroFound = false;
const CGHeroInstance * firstSuitable = nullptr;
const CGHeroInstance * nextSuitable = nullptr;
for(const auto * hero : getWanderingHeroes())
{
if (hero == currentHero)
{
currentHeroFound = true;
continue;
}
if (isHeroSleeping(hero))
continue;
if (hero->movementPointsRemaining() == 0)
continue;
if (!firstSuitable)
firstSuitable = hero;
if (!nextSuitable && currentHeroFound)
nextSuitable = hero;
}
// if we found suitable hero after currently selected hero -> return this hero
if (nextSuitable)
return nextSuitable;
// othervice -> loop over and return first suitable hero in the list (or null if none)
return firstSuitable;
}
const CGTownInstance * PlayerLocalState::getCurrentTown() const
{
if(currentSelection && currentSelection->ID == Obj::TOWN)
return dynamic_cast<const CGTownInstance *>(currentSelection);
else
return nullptr;
}
const CArmedInstance * PlayerLocalState::getCurrentArmy() const
{
if(currentSelection)
return dynamic_cast<const CArmedInstance *>(currentSelection);
else
return nullptr;
}
void PlayerLocalState::setSelection(const CArmedInstance * selection)
{
if (currentSelection == selection)
return;
currentSelection = selection;
if (selection)
adventureInt->onSelectionChanged(selection);
}
bool PlayerLocalState::isHeroSleeping(const CGHeroInstance * hero) const
{
return vstd::contains(sleepingHeroes, hero);
}
void PlayerLocalState::setHeroAsleep(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
assert(!vstd::contains(sleepingHeroes, hero));
sleepingHeroes.push_back(hero);
}
void PlayerLocalState::setHeroAwaken(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
assert(vstd::contains(sleepingHeroes, hero));
vstd::erase(sleepingHeroes, hero);
}
const std::vector<const CGHeroInstance *> & PlayerLocalState::getWanderingHeroes()
{
return wanderingHeroes;
}
const CGHeroInstance * PlayerLocalState::getWanderingHero(size_t index)
{
if(index < wanderingHeroes.size())
return wanderingHeroes[index];
return nullptr;
}
void PlayerLocalState::addWanderingHero(const CGHeroInstance * hero)
{
assert(hero);
assert(!vstd::contains(wanderingHeroes, hero));
wanderingHeroes.push_back(hero);
}
void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
if (hero == currentSelection)
{
auto const * nextHero = getNextWanderingHero(hero);
setSelection(nextHero);
}
vstd::erase(wanderingHeroes, hero);
vstd::erase(sleepingHeroes, hero);
if (currentSelection == nullptr && !wanderingHeroes.empty())
setSelection(wanderingHeroes.front());
if (currentSelection == nullptr && !ownedTowns.empty())
setSelection(ownedTowns.front());
}
const std::vector<const CGTownInstance *> & PlayerLocalState::getOwnedTowns()
{
return ownedTowns;
}
const CGTownInstance * PlayerLocalState::getOwnedTown(size_t index)
{
if(index < ownedTowns.size())
return ownedTowns[index];
return nullptr;
}
void PlayerLocalState::addOwnedTown(const CGTownInstance * town)
{
assert(town);
assert(!vstd::contains(ownedTowns, town));
ownedTowns.push_back(town);
}
void PlayerLocalState::removeOwnedTown(const CGTownInstance * town)
{
assert(town);
assert(vstd::contains(ownedTowns, town));
vstd::erase(ownedTowns, town);
if (town == currentSelection)
setSelection(nullptr);
if (currentSelection == nullptr && !wanderingHeroes.empty())
setSelection(wanderingHeroes.front());
if (currentSelection == nullptr && !ownedTowns.empty())
setSelection(ownedTowns.front());
}
/*
* PlayerLocalState.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 "PlayerLocalState.h"
#include "../CCallback.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/pathfinder/CGPathNode.h"
#include "CPlayerInterface.h"
#include "adventureMap/AdventureMapInterface.h"
PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
: owner(owner)
, currentSelection(nullptr)
{
}
void PlayerLocalState::saveHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
{
for(auto & p : paths)
{
if(p.second.nodes.size())
pathsMap[p.first] = p.second.endPos();
else
logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->getNameTranslated());
}
}
void PlayerLocalState::loadHeroPaths(std::map<const CGHeroInstance *, int3> & pathsMap)
{
if(owner.cb)
{
for(auto & p : pathsMap)
{
CGPath path;
owner.cb->getPathsInfo(p.first)->getPath(path, p.second);
paths[p.first] = path;
logGlobal->trace("Restored path for hero %s leading to %s with %d nodes", p.first->nodeName(), p.second.toString(), path.nodes.size());
}
}
}
void PlayerLocalState::setPath(const CGHeroInstance * h, const CGPath & path)
{
paths[h] = path;
}
const CGPath & PlayerLocalState::getPath(const CGHeroInstance * h) const
{
assert(hasPath(h));
return paths.at(h);
}
bool PlayerLocalState::hasPath(const CGHeroInstance * h) const
{
return paths.count(h) > 0;
}
bool PlayerLocalState::setPath(const CGHeroInstance * h, const int3 & destination)
{
CGPath path;
if(!owner.cb->getPathsInfo(h)->getPath(path, destination))
{
paths.erase(h); //invalidate previously possible path if selected (before other hero blocked only path / fly spell expired)
return false;
}
setPath(h, path);
return true;
}
void PlayerLocalState::removeLastNode(const CGHeroInstance * h)
{
assert(hasPath(h));
if(!hasPath(h))
return;
auto & path = paths[h];
path.nodes.pop_back();
if(path.nodes.size() < 2) //if it was the last one, remove entire path and path with only one tile is not a real path
erasePath(h);
}
void PlayerLocalState::erasePath(const CGHeroInstance * h)
{
paths.erase(h);
adventureInt->onHeroChanged(h);
}
void PlayerLocalState::verifyPath(const CGHeroInstance * h)
{
if(!hasPath(h))
return;
setPath(h, getPath(h).endPos());
}
const CGHeroInstance * PlayerLocalState::getCurrentHero() const
{
if(currentSelection && currentSelection->ID == Obj::HERO)
return dynamic_cast<const CGHeroInstance *>(currentSelection);
else
return nullptr;
}
const CGHeroInstance * PlayerLocalState::getNextWanderingHero(const CGHeroInstance * currentHero)
{
bool currentHeroFound = false;
const CGHeroInstance * firstSuitable = nullptr;
const CGHeroInstance * nextSuitable = nullptr;
for(const auto * hero : getWanderingHeroes())
{
if (hero == currentHero)
{
currentHeroFound = true;
continue;
}
if (isHeroSleeping(hero))
continue;
if (hero->movementPointsRemaining() == 0)
continue;
if (!firstSuitable)
firstSuitable = hero;
if (!nextSuitable && currentHeroFound)
nextSuitable = hero;
}
// if we found suitable hero after currently selected hero -> return this hero
if (nextSuitable)
return nextSuitable;
// othervice -> loop over and return first suitable hero in the list (or null if none)
return firstSuitable;
}
const CGTownInstance * PlayerLocalState::getCurrentTown() const
{
if(currentSelection && currentSelection->ID == Obj::TOWN)
return dynamic_cast<const CGTownInstance *>(currentSelection);
else
return nullptr;
}
const CArmedInstance * PlayerLocalState::getCurrentArmy() const
{
if(currentSelection)
return dynamic_cast<const CArmedInstance *>(currentSelection);
else
return nullptr;
}
void PlayerLocalState::setSelection(const CArmedInstance * selection)
{
if (currentSelection == selection)
return;
currentSelection = selection;
if (selection)
adventureInt->onSelectionChanged(selection);
}
bool PlayerLocalState::isHeroSleeping(const CGHeroInstance * hero) const
{
return vstd::contains(sleepingHeroes, hero);
}
void PlayerLocalState::setHeroAsleep(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
assert(!vstd::contains(sleepingHeroes, hero));
sleepingHeroes.push_back(hero);
}
void PlayerLocalState::setHeroAwaken(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
assert(vstd::contains(sleepingHeroes, hero));
vstd::erase(sleepingHeroes, hero);
}
const std::vector<const CGHeroInstance *> & PlayerLocalState::getWanderingHeroes()
{
return wanderingHeroes;
}
const CGHeroInstance * PlayerLocalState::getWanderingHero(size_t index)
{
if(index < wanderingHeroes.size())
return wanderingHeroes[index];
return nullptr;
}
void PlayerLocalState::addWanderingHero(const CGHeroInstance * hero)
{
assert(hero);
assert(!vstd::contains(wanderingHeroes, hero));
wanderingHeroes.push_back(hero);
}
void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
{
assert(hero);
assert(vstd::contains(wanderingHeroes, hero));
if (hero == currentSelection)
{
auto const * nextHero = getNextWanderingHero(hero);
setSelection(nextHero);
}
vstd::erase(wanderingHeroes, hero);
vstd::erase(sleepingHeroes, hero);
if (currentSelection == nullptr && !wanderingHeroes.empty())
setSelection(wanderingHeroes.front());
if (currentSelection == nullptr && !ownedTowns.empty())
setSelection(ownedTowns.front());
}
const std::vector<const CGTownInstance *> & PlayerLocalState::getOwnedTowns()
{
return ownedTowns;
}
const CGTownInstance * PlayerLocalState::getOwnedTown(size_t index)
{
if(index < ownedTowns.size())
return ownedTowns[index];
return nullptr;
}
void PlayerLocalState::addOwnedTown(const CGTownInstance * town)
{
assert(town);
assert(!vstd::contains(ownedTowns, town));
ownedTowns.push_back(town);
}
void PlayerLocalState::removeOwnedTown(const CGTownInstance * town)
{
assert(town);
assert(vstd::contains(ownedTowns, town));
vstd::erase(ownedTowns, town);
if (town == currentSelection)
setSelection(nullptr);
if (currentSelection == nullptr && !wanderingHeroes.empty())
setSelection(wanderingHeroes.front());
if (currentSelection == nullptr && !ownedTowns.empty())
setSelection(ownedTowns.front());
}

View File

@ -1,111 +1,111 @@
/*
* PlayerLocalState.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 CGHeroInstance;
class CGTownInstance;
class CArmedInstance;
struct CGPath;
class int3;
VCMI_LIB_NAMESPACE_END
class CPlayerInterface;
/// Class that contains potentially serializeable state of a local player
class PlayerLocalState
{
CPlayerInterface & owner;
/// Currently selected object, can be town, hero or null
const CArmedInstance * currentSelection;
std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
std::vector<const CGTownInstance *> ownedTowns; //our towns on the adventure map
void saveHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
void loadHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
public:
struct SpellbookLastSetting
{
//on which page we left spellbook
int spellbookLastPageBattle = 0;
int spellbokLastPageAdvmap = 0;
int spellbookLastTabBattle = 4;
int spellbookLastTabAdvmap = 4;
template<typename Handler>
void serialize(Handler & h, const int version)
{
h & spellbookLastPageBattle;
h & spellbokLastPageAdvmap;
h & spellbookLastTabBattle;
h & spellbookLastTabAdvmap;
}
} spellbookSettings;
explicit PlayerLocalState(CPlayerInterface & owner);
bool isHeroSleeping(const CGHeroInstance * hero) const;
void setHeroAsleep(const CGHeroInstance * hero);
void setHeroAwaken(const CGHeroInstance * hero);
const std::vector<const CGTownInstance *> & getOwnedTowns();
const CGTownInstance * getOwnedTown(size_t index);
void addOwnedTown(const CGTownInstance * hero);
void removeOwnedTown(const CGTownInstance * hero);
const std::vector<const CGHeroInstance *> & getWanderingHeroes();
const CGHeroInstance * getWanderingHero(size_t index);
const CGHeroInstance * getNextWanderingHero(const CGHeroInstance * hero);
void addWanderingHero(const CGHeroInstance * hero);
void removeWanderingHero(const CGHeroInstance * hero);
void setPath(const CGHeroInstance * h, const CGPath & path);
bool setPath(const CGHeroInstance * h, const int3 & destination);
const CGPath & getPath(const CGHeroInstance * h) const;
bool hasPath(const CGHeroInstance * h) const;
void removeLastNode(const CGHeroInstance * h);
void erasePath(const CGHeroInstance * h);
void verifyPath(const CGHeroInstance * h);
/// Returns currently selected object
const CGHeroInstance * getCurrentHero() const;
const CGTownInstance * getCurrentTown() const;
const CArmedInstance * getCurrentArmy() const;
/// Changes currently selected object
void setSelection(const CArmedInstance *sel);
template<typename Handler>
void serialize(Handler & h, int version)
{
//WARNING: this code is broken and not used. See CClient::loadGame
std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
if(h.saving)
saveHeroPaths(pathsMap);
h & pathsMap;
if(!h.saving)
loadHeroPaths(pathsMap);
h & ownedTowns;
h & wanderingHeroes;
h & sleepingHeroes;
}
};
/*
* PlayerLocalState.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 CGHeroInstance;
class CGTownInstance;
class CArmedInstance;
struct CGPath;
class int3;
VCMI_LIB_NAMESPACE_END
class CPlayerInterface;
/// Class that contains potentially serializeable state of a local player
class PlayerLocalState
{
CPlayerInterface & owner;
/// Currently selected object, can be town, hero or null
const CArmedInstance * currentSelection;
std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
std::vector<const CGTownInstance *> ownedTowns; //our towns on the adventure map
void saveHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
void loadHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
public:
struct SpellbookLastSetting
{
//on which page we left spellbook
int spellbookLastPageBattle = 0;
int spellbokLastPageAdvmap = 0;
int spellbookLastTabBattle = 4;
int spellbookLastTabAdvmap = 4;
template<typename Handler>
void serialize(Handler & h, const int version)
{
h & spellbookLastPageBattle;
h & spellbokLastPageAdvmap;
h & spellbookLastTabBattle;
h & spellbookLastTabAdvmap;
}
} spellbookSettings;
explicit PlayerLocalState(CPlayerInterface & owner);
bool isHeroSleeping(const CGHeroInstance * hero) const;
void setHeroAsleep(const CGHeroInstance * hero);
void setHeroAwaken(const CGHeroInstance * hero);
const std::vector<const CGTownInstance *> & getOwnedTowns();
const CGTownInstance * getOwnedTown(size_t index);
void addOwnedTown(const CGTownInstance * hero);
void removeOwnedTown(const CGTownInstance * hero);
const std::vector<const CGHeroInstance *> & getWanderingHeroes();
const CGHeroInstance * getWanderingHero(size_t index);
const CGHeroInstance * getNextWanderingHero(const CGHeroInstance * hero);
void addWanderingHero(const CGHeroInstance * hero);
void removeWanderingHero(const CGHeroInstance * hero);
void setPath(const CGHeroInstance * h, const CGPath & path);
bool setPath(const CGHeroInstance * h, const int3 & destination);
const CGPath & getPath(const CGHeroInstance * h) const;
bool hasPath(const CGHeroInstance * h) const;
void removeLastNode(const CGHeroInstance * h);
void erasePath(const CGHeroInstance * h);
void verifyPath(const CGHeroInstance * h);
/// Returns currently selected object
const CGHeroInstance * getCurrentHero() const;
const CGTownInstance * getCurrentTown() const;
const CArmedInstance * getCurrentArmy() const;
/// Changes currently selected object
void setSelection(const CArmedInstance *sel);
template<typename Handler>
void serialize(Handler & h, int version)
{
//WARNING: this code is broken and not used. See CClient::loadGame
std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
if(h.saving)
saveHeroPaths(pathsMap);
h & pathsMap;
if(!h.saving)
loadHeroPaths(pathsMap);
h & ownedTowns;
h & wanderingHeroes;
h & sleepingHeroes;
}
};

View File

@ -1,2 +1,2 @@
// Creates the precompiled header
// Creates the precompiled header
#include "StdInc.h"

View File

@ -1,9 +1,9 @@
#pragma once
#include "../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE
#pragma once
#include "../Global.h"
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@ -1,191 +1,191 @@
/*
* AdventureMapInterface.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 CGObjectInstance;
class CGHeroInstance;
class CGTownInstance;
class CArmedInstance;
class IShipyard;
struct CGPathNode;
struct ObjectPosInfo;
struct Component;
class int3;
VCMI_LIB_NAMESPACE_END
class CButton;
class IImage;
class CAnimImage;
class CGStatusBar;
class AdventureMapWidget;
class AdventureMapShortcuts;
class CAnimation;
class MapView;
class CResDataBar;
class CHeroList;
class CTownList;
class CInfoBar;
class CMinimap;
class MapAudioPlayer;
class TurnTimerWidget;
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 AdventureMapInterface : public CIntObject
{
private:
/// currently acting player
PlayerColor currentPlayerID;
/// if true, cursor was changed to scrolling and must be reset back once scroll is over
bool scrollingWasActive;
/// if true, then scrolling was blocked via ctrl and should not restart until player move cursor outside scrolling area
bool scrollingWasBlocked;
/// how much should the background dimmed, when windows are on the top
int backgroundDimLevel;
/// 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;
std::shared_ptr<TurnTimerWidget> watches;
private:
void setState(EAdventureState state);
/// updates active state of game window whenever game state changes
void adjustActiveness();
/// checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
/// check and if necessary reacts on scrolling by moving cursor to screen edge
void handleMapScrollingUpdate(uint32_t msPassed);
void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
const CGObjectInstance *getActiveObject(const int3 &tile);
/// exits currently opened world view mode and returns to normal map
void exitCastingMode();
/// casts current spell at specified location
void performSpellcasting(const int3 & castTarget);
/// dim interface if some windows opened
void dim(Canvas & to);
protected:
/// CIntObject interface implementation
void activate() override;
void deactivate() override;
void tick(uint32_t msPassed) override;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
void keyPressed(EShortcut key) override;
void onScreenResize() override;
public:
AdventureMapInterface();
void hotkeyAbortCastingMode();
void hotkeyExitWorldView();
void hotkeyEndingTurn();
void hotkeyNextTown();
void hotkeySwitchMapLevel();
void hotkeyZoom(int delta);
/// Called by PlayerInterface when specified player is ready to start his turn
void onHotseatWaitStarted(PlayerColor playerID);
/// Called by PlayerInterface when AI or remote human player starts his turn
void onEnemyTurnStarted(PlayerColor playerID, bool isHuman);
/// Called by PlayerInterface when local human player starts his turn
void onPlayerTurnStarted(PlayerColor playerID);
/// Called by PlayerInterface when interface should be switched to specified player without starting turn
void onCurrentPlayerChanged(PlayerColor playerID);
/// Called by PlayerInterface when specific map tile changed and must be updated on minimap
void onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions);
/// Called by PlayerInterface when hero starts movement
void onHeroMovementStarted(const CGHeroInstance * hero);
/// Called by PlayerInterface when hero state changed and hero list must be updated
void onHeroChanged(const CGHeroInstance * hero);
/// Called by PlayerInterface when town state changed and town list must be updated
void onTownChanged(const CGTownInstance * town);
/// Called when currently selected object changes
void onSelectionChanged(const CArmedInstance *sel);
/// Called when map audio should be paused, e.g. on combat or town screen access
void onAudioPaused();
/// Called when map audio should be resume, opposite to onPaused
void onAudioResumed();
/// Requests to display provided information inside infobox
void showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer);
/// Changes position on map to center selected location
void centerOnTile(int3 on);
void centerOnObject(const CGObjectInstance *obj);
/// called by MapView whenever currently visible area changes
/// visibleArea describes now visible map section measured in tiles
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
/// called by MapView whenever tile is clicked
void onTileLeftClicked(const int3 & mapPos);
/// called by MapView whenever tile is hovered
void onTileHovered(const int3 & mapPos);
/// called by MapView whenever tile is clicked
void onTileRightClicked(const int3 & mapPos);
/// called by spell window when spell to cast has been selected
void enterCastingMode(const CSpell * sp);
/// returns area of screen covered by terrain (main game area)
Rect terrainAreaPixels() const;
/// opens world view at default scale
void openWorldView();
/// opens world view at specific scale
void openWorldView(int tileSize);
/// opens world view with specific info, e.g. after View Earth/Air is shown
void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
};
extern std::shared_ptr<AdventureMapInterface> adventureInt;
/*
* AdventureMapInterface.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 CGObjectInstance;
class CGHeroInstance;
class CGTownInstance;
class CArmedInstance;
class IShipyard;
struct CGPathNode;
struct ObjectPosInfo;
struct Component;
class int3;
VCMI_LIB_NAMESPACE_END
class CButton;
class IImage;
class CAnimImage;
class CGStatusBar;
class AdventureMapWidget;
class AdventureMapShortcuts;
class CAnimation;
class MapView;
class CResDataBar;
class CHeroList;
class CTownList;
class CInfoBar;
class CMinimap;
class MapAudioPlayer;
class TurnTimerWidget;
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 AdventureMapInterface : public CIntObject
{
private:
/// currently acting player
PlayerColor currentPlayerID;
/// if true, cursor was changed to scrolling and must be reset back once scroll is over
bool scrollingWasActive;
/// if true, then scrolling was blocked via ctrl and should not restart until player move cursor outside scrolling area
bool scrollingWasBlocked;
/// how much should the background dimmed, when windows are on the top
int backgroundDimLevel;
/// 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;
std::shared_ptr<TurnTimerWidget> watches;
private:
void setState(EAdventureState state);
/// updates active state of game window whenever game state changes
void adjustActiveness();
/// checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
/// check and if necessary reacts on scrolling by moving cursor to screen edge
void handleMapScrollingUpdate(uint32_t msPassed);
void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
const CGObjectInstance *getActiveObject(const int3 &tile);
/// exits currently opened world view mode and returns to normal map
void exitCastingMode();
/// casts current spell at specified location
void performSpellcasting(const int3 & castTarget);
/// dim interface if some windows opened
void dim(Canvas & to);
protected:
/// CIntObject interface implementation
void activate() override;
void deactivate() override;
void tick(uint32_t msPassed) override;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
void keyPressed(EShortcut key) override;
void onScreenResize() override;
public:
AdventureMapInterface();
void hotkeyAbortCastingMode();
void hotkeyExitWorldView();
void hotkeyEndingTurn();
void hotkeyNextTown();
void hotkeySwitchMapLevel();
void hotkeyZoom(int delta);
/// Called by PlayerInterface when specified player is ready to start his turn
void onHotseatWaitStarted(PlayerColor playerID);
/// Called by PlayerInterface when AI or remote human player starts his turn
void onEnemyTurnStarted(PlayerColor playerID, bool isHuman);
/// Called by PlayerInterface when local human player starts his turn
void onPlayerTurnStarted(PlayerColor playerID);
/// Called by PlayerInterface when interface should be switched to specified player without starting turn
void onCurrentPlayerChanged(PlayerColor playerID);
/// Called by PlayerInterface when specific map tile changed and must be updated on minimap
void onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions);
/// Called by PlayerInterface when hero starts movement
void onHeroMovementStarted(const CGHeroInstance * hero);
/// Called by PlayerInterface when hero state changed and hero list must be updated
void onHeroChanged(const CGHeroInstance * hero);
/// Called by PlayerInterface when town state changed and town list must be updated
void onTownChanged(const CGTownInstance * town);
/// Called when currently selected object changes
void onSelectionChanged(const CArmedInstance *sel);
/// Called when map audio should be paused, e.g. on combat or town screen access
void onAudioPaused();
/// Called when map audio should be resume, opposite to onPaused
void onAudioResumed();
/// Requests to display provided information inside infobox
void showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer);
/// Changes position on map to center selected location
void centerOnTile(int3 on);
void centerOnObject(const CGObjectInstance *obj);
/// called by MapView whenever currently visible area changes
/// visibleArea describes now visible map section measured in tiles
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
/// called by MapView whenever tile is clicked
void onTileLeftClicked(const int3 & mapPos);
/// called by MapView whenever tile is hovered
void onTileHovered(const int3 & mapPos);
/// called by MapView whenever tile is clicked
void onTileRightClicked(const int3 & mapPos);
/// called by spell window when spell to cast has been selected
void enterCastingMode(const CSpell * sp);
/// returns area of screen covered by terrain (main game area)
Rect terrainAreaPixels() const;
/// opens world view at default scale
void openWorldView();
/// opens world view at specific scale
void openWorldView(int tileSize);
/// opens world view with specific info, e.g. after View Earth/Air is shown
void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
};
extern std::shared_ptr<AdventureMapInterface> adventureInt;

View File

@ -1,455 +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 "../render/IRenderHandler.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
#include "../CPlayerInterface.h"
#include "../PlayerLocalState.h"
#include "../../lib/constants/StringConstants.h"
#include "../../lib/filesystem/ResourcePath.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(JsonPath::builtin("config/widgets/adventureMap.json"));
for(const auto & entry : config["options"]["imagesPlayerColored"].Vector())
playerColorerImages.push_back(ImagePath::fromJson(entry));
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 JsonNode & name)
{
ImagePath resource = ImagePath::fromJson(name);
if(images.count(resource) == 0)
images[resource] = GH.renderHandler().loadImage(resource);
return images[resource];
}
std::shared_ptr<CAnimation> AdventureMapWidget::loadAnimation(const JsonNode & name)
{
AnimationPath resource = AnimationPath::fromJson(name);
if(animations.count(resource) == 0)
animations[resource] = GH.renderHandler().loadAnimation(resource);
return animations[resource];
}
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"]);
return std::make_shared<CFilledTexture>(loadImage(input["image"]), targetArea, sourceArea);
}
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapButton(const JsonNode & input)
{
auto position = readTargetArea(input["area"]);
auto image = AnimationPath::fromJson(input["image"]);
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();
return std::make_shared<CAdventureMapIcon>(area.topLeft(), loadAnimation(input["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"]);
auto image = ImagePath::fromJson(input["image"]);
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"]);
auto image = ImagePath::fromJson(input["image"]);
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 = ImagePath::fromJson(input["image"]);
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 resDataBar = dynamic_cast<CResDataBar *>(entry);
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
if(button)
button->setPlayerColor(player);
if(resDataBar)
resDataBar->colorize(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(Canvas & 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);
}
/*
* 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 "../render/IRenderHandler.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
#include "../CPlayerInterface.h"
#include "../PlayerLocalState.h"
#include "../../lib/constants/StringConstants.h"
#include "../../lib/filesystem/ResourcePath.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(JsonPath::builtin("config/widgets/adventureMap.json"));
for(const auto & entry : config["options"]["imagesPlayerColored"].Vector())
playerColorerImages.push_back(ImagePath::fromJson(entry));
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 JsonNode & name)
{
ImagePath resource = ImagePath::fromJson(name);
if(images.count(resource) == 0)
images[resource] = GH.renderHandler().loadImage(resource);
return images[resource];
}
std::shared_ptr<CAnimation> AdventureMapWidget::loadAnimation(const JsonNode & name)
{
AnimationPath resource = AnimationPath::fromJson(name);
if(animations.count(resource) == 0)
animations[resource] = GH.renderHandler().loadAnimation(resource);
return animations[resource];
}
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"]);
return std::make_shared<CFilledTexture>(loadImage(input["image"]), targetArea, sourceArea);
}
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapButton(const JsonNode & input)
{
auto position = readTargetArea(input["area"]);
auto image = AnimationPath::fromJson(input["image"]);
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();
return std::make_shared<CAdventureMapIcon>(area.topLeft(), loadAnimation(input["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"]);
auto image = ImagePath::fromJson(input["image"]);
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"]);
auto image = ImagePath::fromJson(input["image"]);
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 = ImagePath::fromJson(input["image"]);
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 resDataBar = dynamic_cast<CResDataBar *>(entry);
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
if(button)
button->setPlayerColor(player);
if(resDataBar)
resDataBar->colorize(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(Canvas & 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);
}

View File

@ -1,110 +1,110 @@
/*
* 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 CAnimation;
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<ImagePath> playerColorerImages;
/// list of named images shared between widgets
std::map<ImagePath, std::shared_ptr<IImage>> images;
std::map<AnimationPath, 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 JsonNode & name);
std::shared_ptr<CAnimation> loadAnimation(const JsonNode & 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(Canvas & 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);
};
/*
* 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 CAnimation;
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<ImagePath> playerColorerImages;
/// list of named images shared between widgets
std::map<ImagePath, std::shared_ptr<IImage>> images;
std::map<AnimationPath, 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 JsonNode & name);
std::shared_ptr<CAnimation> loadAnimation(const JsonNode & 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(Canvas & 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);
};

View File

@ -1,61 +1,61 @@
/*
* CAdventureOptions.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 "AdventureOptions.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../PlayerLocalState.h"
#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"
AdventureOptions::AdventureOptions()
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("ADVOPTS"))
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
viewWorld = std::make_shared<CButton>(Point(24, 23), AnimationPath::builtin("ADVVIEW.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
exit = std::make_shared<CButton>(Point(204, 313), AnimationPath::builtin("IOK6432.DEF"), CButton::tooltip(), std::bind(&AdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
scenInfo = std::make_shared<CButton>(Point(24, 198), AnimationPath::builtin("ADVINFO.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
scenInfo->addCallback(AdventureOptions::showScenarioInfo);
puzzle = std::make_shared<CButton>(Point(24, 81), AnimationPath::builtin("ADVPUZ.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE);
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
dig = std::make_shared<CButton>(Point(24, 139), AnimationPath::builtin("ADVDIG.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_DIG_GRAIL);
if(const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero())
dig->addCallback(std::bind(&CPlayerInterface::tryDigging, LOCPLINT, h));
else
dig->block(true);
}
void AdventureOptions::showScenarioInfo()
{
if(LOCPLINT->cb->getStartInfo()->campState)
{
GH.windows().createAndPushWindow<CCampaignInfoScreen>();
}
else
{
GH.windows().createAndPushWindow<CScenarioInfoScreen>();
}
}
/*
* CAdventureOptions.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 "AdventureOptions.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../PlayerLocalState.h"
#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"
AdventureOptions::AdventureOptions()
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("ADVOPTS"))
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
viewWorld = std::make_shared<CButton>(Point(24, 23), AnimationPath::builtin("ADVVIEW.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
exit = std::make_shared<CButton>(Point(204, 313), AnimationPath::builtin("IOK6432.DEF"), CButton::tooltip(), std::bind(&AdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
scenInfo = std::make_shared<CButton>(Point(24, 198), AnimationPath::builtin("ADVINFO.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
scenInfo->addCallback(AdventureOptions::showScenarioInfo);
puzzle = std::make_shared<CButton>(Point(24, 81), AnimationPath::builtin("ADVPUZ.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE);
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
dig = std::make_shared<CButton>(Point(24, 139), AnimationPath::builtin("ADVDIG.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_DIG_GRAIL);
if(const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero())
dig->addCallback(std::bind(&CPlayerInterface::tryDigging, LOCPLINT, h));
else
dig->block(true);
}
void AdventureOptions::showScenarioInfo()
{
if(LOCPLINT->cb->getStartInfo()->campState)
{
GH.windows().createAndPushWindow<CCampaignInfoScreen>();
}
else
{
GH.windows().createAndPushWindow<CScenarioInfoScreen>();
}
}

View File

@ -1,31 +1,31 @@
/*
* CAdventureOptions.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 "../windows/CWindowObject.h"
class CButton;
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
class AdventureOptions : public CWindowObject
{
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> viewWorld;
std::shared_ptr<CButton> puzzle;
std::shared_ptr<CButton> dig;
std::shared_ptr<CButton> scenInfo;
/*std::shared_ptr<CButton> replay*/
public:
AdventureOptions();
static void showScenarioInfo();
};
/*
* CAdventureOptions.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 "../windows/CWindowObject.h"
class CButton;
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
class AdventureOptions : public CWindowObject
{
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> viewWorld;
std::shared_ptr<CButton> puzzle;
std::shared_ptr<CButton> dig;
std::shared_ptr<CButton> scenInfo;
/*std::shared_ptr<CButton> replay*/
public:
AdventureOptions();
static void showScenarioInfo();
};

View File

@ -1,90 +1,90 @@
/*
* CResDataBar.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 "CResDataBar.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../render/Colors.h"
#include "../render/EFont.h"
#include "../gui/CGuiHandler.h"
#include "../gui/TextAlignment.h"
#include "../widgets/Images.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/ResourceSet.h"
CResDataBar::CResDataBar(const ImagePath & imageName, const Point & position)
{
pos.x += position.x;
pos.y += position.y;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(imageName, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->pos.w;
pos.h = background->pos.h;
}
CResDataBar::CResDataBar(const ImagePath & defname, int x, int y, int offx, int offy, int resdist, int datedist):
CResDataBar(defname, Point(x,y))
{
for (int i = 0; i < 7 ; i++)
resourcePositions[GameResID(i)] = Point( offx + resdist*i, offy );
datePosition = resourcePositions[EGameResID::GOLD] + Point(datedist, 0);
}
void CResDataBar::setDatePosition(const Point & position)
{
datePosition = position;
}
void CResDataBar::setResourcePosition(const GameResID & resource, const Point & position)
{
resourcePositions[resource] = position;
}
std::string CResDataBar::buildDateString()
{
std::string pattern = "%s: %d, %s: %d, %s: %d";
auto formatted = boost::format(pattern)
% CGI->generaltexth->translate("core.genrltxt.62") % LOCPLINT->cb->getDate(Date::MONTH)
% CGI->generaltexth->translate("core.genrltxt.63") % LOCPLINT->cb->getDate(Date::WEEK)
% CGI->generaltexth->translate("core.genrltxt.64") % LOCPLINT->cb->getDate(Date::DAY_OF_WEEK);
return boost::str(formatted);
}
void CResDataBar::showAll(Canvas & to)
{
CIntObject::showAll(to);
//TODO: all this should be labels, but they require proper text update on change
for (auto & entry : resourcePositions)
{
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(entry.first));
to.drawText(pos.topLeft() + entry.second, FONT_SMALL, Colors::WHITE, ETextAlignment::TOPLEFT, text);
}
if (datePosition)
to.drawText(pos.topLeft() + *datePosition, FONT_SMALL, Colors::WHITE, ETextAlignment::TOPLEFT, buildDateString());
}
void CResDataBar::colorize(PlayerColor player)
{
background->colorize(player);
}
/*
* CResDataBar.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 "CResDataBar.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../render/Colors.h"
#include "../render/EFont.h"
#include "../gui/CGuiHandler.h"
#include "../gui/TextAlignment.h"
#include "../widgets/Images.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/ResourceSet.h"
CResDataBar::CResDataBar(const ImagePath & imageName, const Point & position)
{
pos.x += position.x;
pos.y += position.y;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(imageName, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->pos.w;
pos.h = background->pos.h;
}
CResDataBar::CResDataBar(const ImagePath & defname, int x, int y, int offx, int offy, int resdist, int datedist):
CResDataBar(defname, Point(x,y))
{
for (int i = 0; i < 7 ; i++)
resourcePositions[GameResID(i)] = Point( offx + resdist*i, offy );
datePosition = resourcePositions[EGameResID::GOLD] + Point(datedist, 0);
}
void CResDataBar::setDatePosition(const Point & position)
{
datePosition = position;
}
void CResDataBar::setResourcePosition(const GameResID & resource, const Point & position)
{
resourcePositions[resource] = position;
}
std::string CResDataBar::buildDateString()
{
std::string pattern = "%s: %d, %s: %d, %s: %d";
auto formatted = boost::format(pattern)
% CGI->generaltexth->translate("core.genrltxt.62") % LOCPLINT->cb->getDate(Date::MONTH)
% CGI->generaltexth->translate("core.genrltxt.63") % LOCPLINT->cb->getDate(Date::WEEK)
% CGI->generaltexth->translate("core.genrltxt.64") % LOCPLINT->cb->getDate(Date::DAY_OF_WEEK);
return boost::str(formatted);
}
void CResDataBar::showAll(Canvas & to)
{
CIntObject::showAll(to);
//TODO: all this should be labels, but they require proper text update on change
for (auto & entry : resourcePositions)
{
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(entry.first));
to.drawText(pos.topLeft() + entry.second, FONT_SMALL, Colors::WHITE, ETextAlignment::TOPLEFT, text);
}
if (datePosition)
to.drawText(pos.topLeft() + *datePosition, FONT_SMALL, Colors::WHITE, ETextAlignment::TOPLEFT, buildDateString());
}
void CResDataBar::colorize(PlayerColor player)
{
background->colorize(player);
}

View File

@ -1,40 +1,40 @@
/*
* CResDataBar.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"
#include "../../lib/filesystem/ResourcePath.h"
/// Resources bar which shows information about how many gold, crystals,... you have
/// Current date is displayed too
class CResDataBar : public CIntObject
{
std::string buildDateString();
std::shared_ptr<CPicture> background;
std::map<GameResID, Point> resourcePositions;
std::optional<Point> datePosition;
public:
/// For dynamically-sized UI windows, e.g. adventure map interface
CResDataBar(const ImagePath & imageName, const Point & position);
/// For fixed-size UI windows, e.g. CastleInterface
CResDataBar(const ImagePath & 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(Canvas & to) override;
};
/*
* CResDataBar.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"
#include "../../lib/filesystem/ResourcePath.h"
/// Resources bar which shows information about how many gold, crystals,... you have
/// Current date is displayed too
class CResDataBar : public CIntObject
{
std::string buildDateString();
std::shared_ptr<CPicture> background;
std::map<GameResID, Point> resourcePositions;
std::optional<Point> datePosition;
public:
/// For dynamically-sized UI windows, e.g. adventure map interface
CResDataBar(const ImagePath & imageName, const Point & position);
/// For fixed-size UI windows, e.g. CastleInterface
CResDataBar(const ImagePath & 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(Canvas & to) override;
};

View File

@ -1,252 +1,252 @@
/*
* MapAudioPlayer.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 "MapAudioPlayer.h"
#include "../CCallback.h"
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../mapView/mapHandler.h"
#include "../../lib/TerrainHandler.h"
#include "../../lib/mapObjects/CArmedInstance.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMap.h"
bool MapAudioPlayer::hasOngoingAnimations()
{
return false;
}
void MapAudioPlayer::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator)
{
addObject(obj);
}
void MapAudioPlayer::onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator)
{
removeObject(obj);
}
void MapAudioPlayer::onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator)
{
addObject(obj);
}
void MapAudioPlayer::onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator)
{
removeObject(obj);
}
void MapAudioPlayer::addObject(const CGObjectInstance * obj)
{
if(obj->isTile2Terrain())
{
// terrain overlay - all covering tiles act as sound source
for(int fx = 0; fx < obj->getWidth(); ++fx)
{
for(int fy = 0; fy < obj->getHeight(); ++fy)
{
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
}
}
return;
}
if(obj->isVisitable())
{
// visitable object - visitable tile acts as sound source
int3 currTile = obj->visitablePos();
if(LOCPLINT->cb->isInTheMap(currTile))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
return;
}
if(!obj->isVisitable())
{
// static object - blocking tiles act as sound source
auto tiles = obj->getBlockedOffsets();
for(const auto & tile : tiles)
{
int3 currTile = obj->pos + tile;
if(LOCPLINT->cb->isInTheMap(currTile))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
}
return;
}
}
void MapAudioPlayer::removeObject(const CGObjectInstance * obj)
{
for(int z = 0; z < LOCPLINT->cb->getMapSize().z; z++)
for(int x = 0; x < LOCPLINT->cb->getMapSize().x; x++)
for(int y = 0; y < LOCPLINT->cb->getMapSize().y; y++)
vstd::erase(objects[z][x][y], obj->id);
}
std::vector<AudioPath> MapAudioPlayer::getAmbientSounds(const int3 & tile)
{
std::vector<AudioPath> result;
for(auto & objectID : objects[tile.z][tile.x][tile.y])
{
const auto & object = CGI->mh->getMap()->objects[objectID.getNum()];
assert(object);
if (!object)
logGlobal->warn("Already removed object %d found on tile! (%d %d %d)", objectID.getNum(), tile.x, tile.y, tile.z);
if(object && object->getAmbientSound())
result.push_back(object->getAmbientSound().value());
}
if(CGI->mh->getMap()->isCoastalTile(tile))
result.emplace_back(AudioPath::builtin("LOOPOCEA"));
return result;
}
void MapAudioPlayer::updateAmbientSounds()
{
std::map<AudioPath, int> currentSounds;
auto updateSounds = [&](const AudioPath& soundId, int distance) -> void
{
if(vstd::contains(currentSounds, soundId))
currentSounds[soundId] = std::min(currentSounds[soundId], distance);
else
currentSounds.insert(std::make_pair(soundId, distance));
};
int3 pos = currentSelection->getSightCenter();
std::unordered_set<int3> tiles;
LOCPLINT->cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV);
for(int3 tile : tiles)
{
int dist = pos.dist(tile, int3::DIST_CHEBYSHEV);
for(auto & soundName : getAmbientSounds(tile))
updateSounds(soundName, dist);
}
CCS->soundh->ambientUpdateChannels(currentSounds);
}
void MapAudioPlayer::updateMusic()
{
if(audioPlaying && playerMakingTurn && currentSelection)
{
const auto * terrain = LOCPLINT->cb->getTile(currentSelection->visitablePos())->terType;
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
}
if(audioPlaying && enemyMakingTurn)
{
CCS->musich->playMusicFromSet("enemy-turn", true, false);
}
}
void MapAudioPlayer::update()
{
updateMusic();
if(audioPlaying && playerMakingTurn && currentSelection)
updateAmbientSounds();
}
MapAudioPlayer::MapAudioPlayer()
{
auto mapSize = LOCPLINT->cb->getMapSize();
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : CGI->mh->getMap()->objects)
{
if (obj)
addObject(obj);
}
}
MapAudioPlayer::~MapAudioPlayer()
{
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}
void MapAudioPlayer::onSelectionChanged(const CArmedInstance * newSelection)
{
currentSelection = newSelection;
update();
}
void MapAudioPlayer::onAudioPaused()
{
audioPlaying = false;
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}
void MapAudioPlayer::onAudioResumed()
{
audioPlaying = true;
update();
}
void MapAudioPlayer::onPlayerTurnStarted()
{
enemyMakingTurn = false;
playerMakingTurn = true;
update();
}
void MapAudioPlayer::onEnemyTurnStarted()
{
playerMakingTurn = false;
enemyMakingTurn = true;
update();
}
void MapAudioPlayer::onPlayerTurnEnded()
{
playerMakingTurn = false;
enemyMakingTurn = false;
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}
/*
* MapAudioPlayer.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 "MapAudioPlayer.h"
#include "../CCallback.h"
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../mapView/mapHandler.h"
#include "../../lib/TerrainHandler.h"
#include "../../lib/mapObjects/CArmedInstance.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMap.h"
bool MapAudioPlayer::hasOngoingAnimations()
{
return false;
}
void MapAudioPlayer::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if(obj == currentSelection)
update();
}
void MapAudioPlayer::onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator)
{
addObject(obj);
}
void MapAudioPlayer::onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator)
{
removeObject(obj);
}
void MapAudioPlayer::onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator)
{
addObject(obj);
}
void MapAudioPlayer::onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator)
{
removeObject(obj);
}
void MapAudioPlayer::addObject(const CGObjectInstance * obj)
{
if(obj->isTile2Terrain())
{
// terrain overlay - all covering tiles act as sound source
for(int fx = 0; fx < obj->getWidth(); ++fx)
{
for(int fy = 0; fy < obj->getHeight(); ++fy)
{
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
}
}
return;
}
if(obj->isVisitable())
{
// visitable object - visitable tile acts as sound source
int3 currTile = obj->visitablePos();
if(LOCPLINT->cb->isInTheMap(currTile))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
return;
}
if(!obj->isVisitable())
{
// static object - blocking tiles act as sound source
auto tiles = obj->getBlockedOffsets();
for(const auto & tile : tiles)
{
int3 currTile = obj->pos + tile;
if(LOCPLINT->cb->isInTheMap(currTile))
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
}
return;
}
}
void MapAudioPlayer::removeObject(const CGObjectInstance * obj)
{
for(int z = 0; z < LOCPLINT->cb->getMapSize().z; z++)
for(int x = 0; x < LOCPLINT->cb->getMapSize().x; x++)
for(int y = 0; y < LOCPLINT->cb->getMapSize().y; y++)
vstd::erase(objects[z][x][y], obj->id);
}
std::vector<AudioPath> MapAudioPlayer::getAmbientSounds(const int3 & tile)
{
std::vector<AudioPath> result;
for(auto & objectID : objects[tile.z][tile.x][tile.y])
{
const auto & object = CGI->mh->getMap()->objects[objectID.getNum()];
assert(object);
if (!object)
logGlobal->warn("Already removed object %d found on tile! (%d %d %d)", objectID.getNum(), tile.x, tile.y, tile.z);
if(object && object->getAmbientSound())
result.push_back(object->getAmbientSound().value());
}
if(CGI->mh->getMap()->isCoastalTile(tile))
result.emplace_back(AudioPath::builtin("LOOPOCEA"));
return result;
}
void MapAudioPlayer::updateAmbientSounds()
{
std::map<AudioPath, int> currentSounds;
auto updateSounds = [&](const AudioPath& soundId, int distance) -> void
{
if(vstd::contains(currentSounds, soundId))
currentSounds[soundId] = std::min(currentSounds[soundId], distance);
else
currentSounds.insert(std::make_pair(soundId, distance));
};
int3 pos = currentSelection->getSightCenter();
std::unordered_set<int3> tiles;
LOCPLINT->cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV);
for(int3 tile : tiles)
{
int dist = pos.dist(tile, int3::DIST_CHEBYSHEV);
for(auto & soundName : getAmbientSounds(tile))
updateSounds(soundName, dist);
}
CCS->soundh->ambientUpdateChannels(currentSounds);
}
void MapAudioPlayer::updateMusic()
{
if(audioPlaying && playerMakingTurn && currentSelection)
{
const auto * terrain = LOCPLINT->cb->getTile(currentSelection->visitablePos())->terType;
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
}
if(audioPlaying && enemyMakingTurn)
{
CCS->musich->playMusicFromSet("enemy-turn", true, false);
}
}
void MapAudioPlayer::update()
{
updateMusic();
if(audioPlaying && playerMakingTurn && currentSelection)
updateAmbientSounds();
}
MapAudioPlayer::MapAudioPlayer()
{
auto mapSize = LOCPLINT->cb->getMapSize();
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : CGI->mh->getMap()->objects)
{
if (obj)
addObject(obj);
}
}
MapAudioPlayer::~MapAudioPlayer()
{
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}
void MapAudioPlayer::onSelectionChanged(const CArmedInstance * newSelection)
{
currentSelection = newSelection;
update();
}
void MapAudioPlayer::onAudioPaused()
{
audioPlaying = false;
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}
void MapAudioPlayer::onAudioResumed()
{
audioPlaying = true;
update();
}
void MapAudioPlayer::onPlayerTurnStarted()
{
enemyMakingTurn = false;
playerMakingTurn = true;
update();
}
void MapAudioPlayer::onEnemyTurnStarted()
{
playerMakingTurn = false;
enemyMakingTurn = true;
update();
}
void MapAudioPlayer::onPlayerTurnEnded()
{
playerMakingTurn = false;
enemyMakingTurn = false;
CCS->soundh->ambientStopAllChannels();
CCS->musich->stopMusic(1000);
}

View File

@ -1,77 +1,77 @@
/*
* MapAudioPlayer.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 "../mapView/IMapRendererObserver.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
class ObjectInstanceID;
class CArmedInstance;
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class MapAudioPlayer : public IMapObjectObserver
{
using MapObjectsList = std::vector<ObjectInstanceID>;
boost::multi_array<MapObjectsList, 3> objects;
const CArmedInstance * currentSelection = nullptr;
bool playerMakingTurn = false;
bool enemyMakingTurn = false;
bool audioPlaying = true;
void addObject(const CGObjectInstance * obj);
void removeObject(const CGObjectInstance * obj);
std::vector<AudioPath> getAmbientSounds(const int3 & tile);
void updateAmbientSounds();
void updateMusic();
void update();
protected:
// IMapObjectObserver impl
bool hasOngoingAnimations() override;
void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
public:
MapAudioPlayer();
~MapAudioPlayer() override;
/// Called whenever current adventure map selection changes
void onSelectionChanged(const CArmedInstance * newSelection);
/// Called when local player starts his turn
void onPlayerTurnStarted();
/// Called when AI or non-local player start his turn
void onEnemyTurnStarted();
/// Called when local player ends his turn
void onPlayerTurnEnded();
/// Called when map audio should be paused, e.g. on combat or town scren access
void onAudioPaused();
/// Called when map audio should be resume, opposite to onPaused
void onAudioResumed();
};
/*
* MapAudioPlayer.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 "../mapView/IMapRendererObserver.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
class ObjectInstanceID;
class CArmedInstance;
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class MapAudioPlayer : public IMapObjectObserver
{
using MapObjectsList = std::vector<ObjectInstanceID>;
boost::multi_array<MapObjectsList, 3> objects;
const CArmedInstance * currentSelection = nullptr;
bool playerMakingTurn = false;
bool enemyMakingTurn = false;
bool audioPlaying = true;
void addObject(const CGObjectInstance * obj);
void removeObject(const CGObjectInstance * obj);
std::vector<AudioPath> getAmbientSounds(const int3 & tile);
void updateAmbientSounds();
void updateMusic();
void update();
protected:
// IMapObjectObserver impl
bool hasOngoingAnimations() override;
void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) override;
void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
public:
MapAudioPlayer();
~MapAudioPlayer() override;
/// Called whenever current adventure map selection changes
void onSelectionChanged(const CArmedInstance * newSelection);
/// Called when local player starts his turn
void onPlayerTurnStarted();
/// Called when AI or non-local player start his turn
void onEnemyTurnStarted();
/// Called when local player ends his turn
void onPlayerTurnEnded();
/// Called when map audio should be paused, e.g. on combat or town scren access
void onAudioPaused();
/// Called when map audio should be resume, opposite to onPaused
void onAudioResumed();
};

File diff suppressed because it is too large Load Diff

View File

@ -1,125 +1,125 @@
/*
* BattleActionsController.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 "../../lib/battle/CBattleInfoCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
class BattleAction;
namespace spells {
class Caster;
enum class Mode;
}
VCMI_LIB_NAMESPACE_END
class BattleInterface;
/// Class that controls actions that can be performed by player, e.g. moving stacks, attacking, etc
/// As well as all relevant feedback for these actions in user interface
class BattleActionsController
{
BattleInterface & owner;
/// all actions possible to call at the moment by player
std::vector<PossiblePlayerBattleAction> possibleActions;
/// spell for which player's hero is choosing destination
std::shared_ptr<BattleAction> heroSpellToCast;
/// cached message that was set by this class in status bar
std::string currentConsoleMsg;
/// if true, active stack could possibly cast some target spell
std::vector<const CSpell *> creatureSpells;
/// stack that has been selected as first target for multi-target spells (Teleport & Sacrifice)
const CStack * selectedStack;
bool isCastingPossibleHere (const CSpell * spell, const CStack *shere, BattleHex myNumber);
bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn
void reorderPossibleActionsPriority(const CStack * stack, const CStack * targetStack);
bool actionIsLegal(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionSetCursor(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionSetCursorBlocked(PossiblePlayerBattleAction action, BattleHex hoveredHex);
std::string actionGetStatusMessage(PossiblePlayerBattleAction action, BattleHex hoveredHex);
std::string actionGetStatusMessageBlocked(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionRealize(PossiblePlayerBattleAction action, BattleHex hoveredHex);
PossiblePlayerBattleAction selectAction(BattleHex myNumber);
const CStack * getStackForHex(BattleHex myNumber) ;
/// attempts to initialize spellcasting action for stack
/// will silently return if stack is not a spellcaster
void tryActivateStackSpellcasting(const CStack *casterStack);
/// returns spell that is currently being cast by hero or nullptr if none
const CSpell * getHeroSpellToCast() const;
/// if current stack is spellcaster, returns spell being cast, or null othervice
const CSpell * getStackSpellToCast(BattleHex hoveredHex);
/// returns true if current stack is a spellcaster
bool isActiveStackSpellcaster() const;
public:
BattleActionsController(BattleInterface & owner);
/// initialize list of potential actions for new active stack
void activateStack();
/// returns true if UI is currently in target selection mode
bool spellcastingModeActive() const;
/// returns true if one of the following is true:
/// - we are casting spell by hero
/// - we are casting spell by creature in targeted mode (F hotkey)
/// - current creature is spellcaster and preferred action for current hex is spellcast
bool currentActionSpellcasting(BattleHex hoveredHex);
/// enter targeted spellcasting mode for creature, e.g. via "F" hotkey
void enterCreatureCastingMode();
/// initialize hero spellcasting mode, e.g. on selecting spell in spellbook
void castThisSpell(SpellID spellID);
/// ends casting spell (eg. when spell has been cast or canceled)
void endCastingSpell();
/// update cursor and status bar according to new active hex
void onHexHovered(BattleHex hoveredHex);
/// called when cursor is no longer over battlefield and cursor/battle log should be reset
void onHoverEnded();
/// performs action according to selected hex
void onHexLeftClicked(BattleHex clickedHex);
/// performs action according to selected hex
void onHexRightClicked(BattleHex clickedHex);
const spells::Caster * getCurrentSpellcaster() const;
const CSpell * getCurrentSpell(BattleHex hoveredHex);
spells::Mode getCurrentCastMode() const;
/// methods to work with array of possible actions, needed to control special creatures abilities
const std::vector<PossiblePlayerBattleAction> & getPossibleActions() const;
void removePossibleAction(PossiblePlayerBattleAction);
/// inserts possible action in the beggining in order to prioritize it
void pushFrontPossibleAction(PossiblePlayerBattleAction);
};
/*
* BattleActionsController.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 "../../lib/battle/CBattleInfoCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
class BattleAction;
namespace spells {
class Caster;
enum class Mode;
}
VCMI_LIB_NAMESPACE_END
class BattleInterface;
/// Class that controls actions that can be performed by player, e.g. moving stacks, attacking, etc
/// As well as all relevant feedback for these actions in user interface
class BattleActionsController
{
BattleInterface & owner;
/// all actions possible to call at the moment by player
std::vector<PossiblePlayerBattleAction> possibleActions;
/// spell for which player's hero is choosing destination
std::shared_ptr<BattleAction> heroSpellToCast;
/// cached message that was set by this class in status bar
std::string currentConsoleMsg;
/// if true, active stack could possibly cast some target spell
std::vector<const CSpell *> creatureSpells;
/// stack that has been selected as first target for multi-target spells (Teleport & Sacrifice)
const CStack * selectedStack;
bool isCastingPossibleHere (const CSpell * spell, const CStack *shere, BattleHex myNumber);
bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn
void reorderPossibleActionsPriority(const CStack * stack, const CStack * targetStack);
bool actionIsLegal(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionSetCursor(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionSetCursorBlocked(PossiblePlayerBattleAction action, BattleHex hoveredHex);
std::string actionGetStatusMessage(PossiblePlayerBattleAction action, BattleHex hoveredHex);
std::string actionGetStatusMessageBlocked(PossiblePlayerBattleAction action, BattleHex hoveredHex);
void actionRealize(PossiblePlayerBattleAction action, BattleHex hoveredHex);
PossiblePlayerBattleAction selectAction(BattleHex myNumber);
const CStack * getStackForHex(BattleHex myNumber) ;
/// attempts to initialize spellcasting action for stack
/// will silently return if stack is not a spellcaster
void tryActivateStackSpellcasting(const CStack *casterStack);
/// returns spell that is currently being cast by hero or nullptr if none
const CSpell * getHeroSpellToCast() const;
/// if current stack is spellcaster, returns spell being cast, or null othervice
const CSpell * getStackSpellToCast(BattleHex hoveredHex);
/// returns true if current stack is a spellcaster
bool isActiveStackSpellcaster() const;
public:
BattleActionsController(BattleInterface & owner);
/// initialize list of potential actions for new active stack
void activateStack();
/// returns true if UI is currently in target selection mode
bool spellcastingModeActive() const;
/// returns true if one of the following is true:
/// - we are casting spell by hero
/// - we are casting spell by creature in targeted mode (F hotkey)
/// - current creature is spellcaster and preferred action for current hex is spellcast
bool currentActionSpellcasting(BattleHex hoveredHex);
/// enter targeted spellcasting mode for creature, e.g. via "F" hotkey
void enterCreatureCastingMode();
/// initialize hero spellcasting mode, e.g. on selecting spell in spellbook
void castThisSpell(SpellID spellID);
/// ends casting spell (eg. when spell has been cast or canceled)
void endCastingSpell();
/// update cursor and status bar according to new active hex
void onHexHovered(BattleHex hoveredHex);
/// called when cursor is no longer over battlefield and cursor/battle log should be reset
void onHoverEnded();
/// performs action according to selected hex
void onHexLeftClicked(BattleHex clickedHex);
/// performs action according to selected hex
void onHexRightClicked(BattleHex clickedHex);
const spells::Caster * getCurrentSpellcaster() const;
const CSpell * getCurrentSpell(BattleHex hoveredHex);
spells::Mode getCurrentCastMode() const;
/// methods to work with array of possible actions, needed to control special creatures abilities
const std::vector<PossiblePlayerBattleAction> & getPossibleActions() const;
void removePossibleAction(PossiblePlayerBattleAction);
/// inserts possible action in the beggining in order to prioritize it
void pushFrontPossibleAction(PossiblePlayerBattleAction);
};

File diff suppressed because it is too large Load Diff

View File

@ -1,372 +1,372 @@
/*
* BattleAnimations.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 "../../lib/battle/BattleHex.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CCreature;
class CSpell;
class Point;
VCMI_LIB_NAMESPACE_END
class ColorFilter;
class BattleHero;
class CAnimation;
class BattleInterface;
class CreatureAnimation;
struct StackAttackedInfo;
/// Base class of battle animations
class BattleAnimation
{
protected:
BattleInterface & owner;
bool initialized;
std::vector<BattleAnimation *> & pendingAnimations();
std::shared_ptr<CreatureAnimation> stackAnimation(const CStack * stack) const;
bool stackFacingRight(const CStack * stack);
void setStackFacingRight(const CStack * stack, bool facingRight);
virtual bool init() = 0; //to be called - if returned false, call again until returns true
public:
ui32 ID; //unique identifier
bool isInitialized();
bool tryInitialize();
virtual void tick(uint32_t msPassed) {} //call every new frame
virtual ~BattleAnimation();
BattleAnimation(BattleInterface & owner);
};
/// Sub-class which is responsible for managing the battle stack animation.
class BattleStackAnimation : public BattleAnimation
{
public:
std::shared_ptr<CreatureAnimation> myAnim; //animation for our stack, managed by BattleInterface
const CStack * stack; //id of stack whose animation it is
BattleStackAnimation(BattleInterface & owner, const CStack * _stack);
void rotateStack(BattleHex hex);
};
class StackActionAnimation : public BattleStackAnimation
{
ECreatureAnimType nextGroup;
ECreatureAnimType currGroup;
AudioPath sound;
public:
void setNextGroup( ECreatureAnimType group );
void setGroup( ECreatureAnimType group );
void setSound( const AudioPath & sound );
ECreatureAnimType getGroup() const;
StackActionAnimation(BattleInterface & owner, const CStack * _stack);
~StackActionAnimation();
bool init() override;
};
/// Animation of a defending unit
class DefenceAnimation : public StackActionAnimation
{
public:
DefenceAnimation(BattleInterface & owner, const CStack * stack);
};
/// Animation of a hit unit
class HittedAnimation : public StackActionAnimation
{
public:
HittedAnimation(BattleInterface & owner, const CStack * stack);
};
/// Animation of a dying unit
class DeathAnimation : public StackActionAnimation
{
public:
DeathAnimation(BattleInterface & owner, const CStack * stack, bool ranged);
};
/// Resurrects stack from dead state
class ResurrectionAnimation : public StackActionAnimation
{
public:
ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
};
class ColorTransformAnimation : public BattleStackAnimation
{
std::vector<ColorFilter> steps;
std::vector<float> timePoints;
const CSpell * spell;
float totalProgress;
bool init() override;
void tick(uint32_t msPassed) override;
public:
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
};
/// Base class for all animations that play during stack movement
class StackMoveAnimation : public BattleStackAnimation
{
public:
BattleHex nextHex;
BattleHex prevHex;
protected:
StackMoveAnimation(BattleInterface & owner, const CStack * _stack, BattleHex prevHex, BattleHex nextHex);
};
/// Move animation of a creature
class MovementAnimation : public StackMoveAnimation
{
private:
int moveSoundHander; // sound handler used when moving a unit
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
ui32 curentMoveIndex; // index of nextHex in destTiles
double begX, begY; // starting position
double distanceX, distanceY; // full movement distance, may be negative if creture moves topleft
/// progress gain per second
double progressPerSecond;
/// range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
double progress;
public:
bool init() override;
void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
~MovementAnimation();
};
/// Move end animation of a creature
class MovementEndAnimation : public StackMoveAnimation
{
public:
bool init() override;
MovementEndAnimation(BattleInterface & owner, const CStack * _stack, BattleHex destTile);
~MovementEndAnimation();
};
/// Move start animation of a creature
class MovementStartAnimation : public StackMoveAnimation
{
public:
bool init() override;
MovementStartAnimation(BattleInterface & owner, const CStack * _stack);
};
/// Class responsible for animation of stack chaning direction (left <-> right)
class ReverseAnimation : public StackMoveAnimation
{
void setupSecondPart();
public:
bool init() override;
ReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest);
};
/// This class is responsible for managing the battle attack animation
class AttackAnimation : public StackActionAnimation
{
protected:
BattleHex dest; //attacked hex
const CStack *defendingStack;
const CStack *attackingStack;
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
const CCreature * getCreature() const;
ECreatureAnimType findValidGroup( const std::vector<ECreatureAnimType> candidates ) const;
public:
AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
};
/// Hand-to-hand attack
class MeleeAttackAnimation : public AttackAnimation
{
ECreatureAnimType getUpwardsGroup(bool multiAttack) const;
ECreatureAnimType getForwardGroup(bool multiAttack) const;
ECreatureAnimType getDownwardsGroup(bool multiAttack) const;
ECreatureAnimType selectGroup(bool multiAttack);
public:
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
void tick(uint32_t msPassed) override;
};
class RangedAttackAnimation : public AttackAnimation
{
void setAnimationGroup();
void initializeProjectile();
void emitProjectile();
void emitExplosion();
protected:
bool projectileEmitted;
virtual ECreatureAnimType getUpwardsGroup() const = 0;
virtual ECreatureAnimType getForwardGroup() const = 0;
virtual ECreatureAnimType getDownwardsGroup() const = 0;
virtual void createProjectile(const Point & from, const Point & dest) const = 0;
virtual uint32_t getAttackClimaxFrame() const = 0;
public:
RangedAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
~RangedAttackAnimation();
bool init() override;
void tick(uint32_t msPassed) override;
};
/// Shooting attack
class ShootingAnimation : public RangedAttackAnimation
{
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
uint32_t getAttackClimaxFrame() const override;
public:
ShootingAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
};
/// Catapult attack
class CatapultAnimation : public ShootingAnimation
{
private:
bool explosionEmitted;
int catapultDamage;
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 tick(uint32_t msPassed) override;
};
class CastAnimation : public RangedAttackAnimation
{
const CSpell * spell;
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
uint32_t getAttackClimaxFrame() const override;
public:
CastAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell);
};
class DummyAnimation : public BattleAnimation
{
private:
int counter;
int howMany;
public:
bool init() override;
void tick(uint32_t msPassed) override;
DummyAnimation(BattleInterface & owner, int howManyFrames);
};
/// Class that plays effect at one or more positions along with (single) sound effect
class EffectAnimation : public BattleAnimation
{
std::string soundName;
bool effectFinished;
bool reversed;
int effectFlags;
std::shared_ptr<CAnimation> animation;
std::vector<Point> positions;
std::vector<BattleHex> battlehexes;
bool alignToBottom() const;
bool waitForSound() const;
bool forceOnTop() const;
bool screenFill() const;
void onEffectFinished();
void clearEffect();
void playEffect(uint32_t msPassed);
public:
enum EEffectFlags
{
ALIGN_TO_BOTTOM = 1,
FORCE_ON_TOP = 2,
SCREEN_FILL = 4,
};
/// Create animation with screen-wide effect
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, bool reversed = false);
/// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos , int effects = 0, bool reversed = false);
/// Create animation positioned at certain hex(es)
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
~EffectAnimation();
bool init() override;
void tick(uint32_t msPassed) override;
};
class HeroCastAnimation : public BattleAnimation
{
std::shared_ptr<BattleHero> hero;
const CStack * target;
const CSpell * spell;
BattleHex tile;
bool projectileEmitted;
void initializeProjectile();
void emitProjectile();
void emitAnimationEvent();
public:
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
void tick(uint32_t msPassed) override;
bool init() override;
};
/*
* BattleAnimations.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 "../../lib/battle/BattleHex.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CCreature;
class CSpell;
class Point;
VCMI_LIB_NAMESPACE_END
class ColorFilter;
class BattleHero;
class CAnimation;
class BattleInterface;
class CreatureAnimation;
struct StackAttackedInfo;
/// Base class of battle animations
class BattleAnimation
{
protected:
BattleInterface & owner;
bool initialized;
std::vector<BattleAnimation *> & pendingAnimations();
std::shared_ptr<CreatureAnimation> stackAnimation(const CStack * stack) const;
bool stackFacingRight(const CStack * stack);
void setStackFacingRight(const CStack * stack, bool facingRight);
virtual bool init() = 0; //to be called - if returned false, call again until returns true
public:
ui32 ID; //unique identifier
bool isInitialized();
bool tryInitialize();
virtual void tick(uint32_t msPassed) {} //call every new frame
virtual ~BattleAnimation();
BattleAnimation(BattleInterface & owner);
};
/// Sub-class which is responsible for managing the battle stack animation.
class BattleStackAnimation : public BattleAnimation
{
public:
std::shared_ptr<CreatureAnimation> myAnim; //animation for our stack, managed by BattleInterface
const CStack * stack; //id of stack whose animation it is
BattleStackAnimation(BattleInterface & owner, const CStack * _stack);
void rotateStack(BattleHex hex);
};
class StackActionAnimation : public BattleStackAnimation
{
ECreatureAnimType nextGroup;
ECreatureAnimType currGroup;
AudioPath sound;
public:
void setNextGroup( ECreatureAnimType group );
void setGroup( ECreatureAnimType group );
void setSound( const AudioPath & sound );
ECreatureAnimType getGroup() const;
StackActionAnimation(BattleInterface & owner, const CStack * _stack);
~StackActionAnimation();
bool init() override;
};
/// Animation of a defending unit
class DefenceAnimation : public StackActionAnimation
{
public:
DefenceAnimation(BattleInterface & owner, const CStack * stack);
};
/// Animation of a hit unit
class HittedAnimation : public StackActionAnimation
{
public:
HittedAnimation(BattleInterface & owner, const CStack * stack);
};
/// Animation of a dying unit
class DeathAnimation : public StackActionAnimation
{
public:
DeathAnimation(BattleInterface & owner, const CStack * stack, bool ranged);
};
/// Resurrects stack from dead state
class ResurrectionAnimation : public StackActionAnimation
{
public:
ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
};
class ColorTransformAnimation : public BattleStackAnimation
{
std::vector<ColorFilter> steps;
std::vector<float> timePoints;
const CSpell * spell;
float totalProgress;
bool init() override;
void tick(uint32_t msPassed) override;
public:
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
};
/// Base class for all animations that play during stack movement
class StackMoveAnimation : public BattleStackAnimation
{
public:
BattleHex nextHex;
BattleHex prevHex;
protected:
StackMoveAnimation(BattleInterface & owner, const CStack * _stack, BattleHex prevHex, BattleHex nextHex);
};
/// Move animation of a creature
class MovementAnimation : public StackMoveAnimation
{
private:
int moveSoundHander; // sound handler used when moving a unit
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
ui32 curentMoveIndex; // index of nextHex in destTiles
double begX, begY; // starting position
double distanceX, distanceY; // full movement distance, may be negative if creture moves topleft
/// progress gain per second
double progressPerSecond;
/// range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
double progress;
public:
bool init() override;
void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
~MovementAnimation();
};
/// Move end animation of a creature
class MovementEndAnimation : public StackMoveAnimation
{
public:
bool init() override;
MovementEndAnimation(BattleInterface & owner, const CStack * _stack, BattleHex destTile);
~MovementEndAnimation();
};
/// Move start animation of a creature
class MovementStartAnimation : public StackMoveAnimation
{
public:
bool init() override;
MovementStartAnimation(BattleInterface & owner, const CStack * _stack);
};
/// Class responsible for animation of stack chaning direction (left <-> right)
class ReverseAnimation : public StackMoveAnimation
{
void setupSecondPart();
public:
bool init() override;
ReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest);
};
/// This class is responsible for managing the battle attack animation
class AttackAnimation : public StackActionAnimation
{
protected:
BattleHex dest; //attacked hex
const CStack *defendingStack;
const CStack *attackingStack;
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
const CCreature * getCreature() const;
ECreatureAnimType findValidGroup( const std::vector<ECreatureAnimType> candidates ) const;
public:
AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
};
/// Hand-to-hand attack
class MeleeAttackAnimation : public AttackAnimation
{
ECreatureAnimType getUpwardsGroup(bool multiAttack) const;
ECreatureAnimType getForwardGroup(bool multiAttack) const;
ECreatureAnimType getDownwardsGroup(bool multiAttack) const;
ECreatureAnimType selectGroup(bool multiAttack);
public:
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
void tick(uint32_t msPassed) override;
};
class RangedAttackAnimation : public AttackAnimation
{
void setAnimationGroup();
void initializeProjectile();
void emitProjectile();
void emitExplosion();
protected:
bool projectileEmitted;
virtual ECreatureAnimType getUpwardsGroup() const = 0;
virtual ECreatureAnimType getForwardGroup() const = 0;
virtual ECreatureAnimType getDownwardsGroup() const = 0;
virtual void createProjectile(const Point & from, const Point & dest) const = 0;
virtual uint32_t getAttackClimaxFrame() const = 0;
public:
RangedAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
~RangedAttackAnimation();
bool init() override;
void tick(uint32_t msPassed) override;
};
/// Shooting attack
class ShootingAnimation : public RangedAttackAnimation
{
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
uint32_t getAttackClimaxFrame() const override;
public:
ShootingAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
};
/// Catapult attack
class CatapultAnimation : public ShootingAnimation
{
private:
bool explosionEmitted;
int catapultDamage;
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 tick(uint32_t msPassed) override;
};
class CastAnimation : public RangedAttackAnimation
{
const CSpell * spell;
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
uint32_t getAttackClimaxFrame() const override;
public:
CastAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell);
};
class DummyAnimation : public BattleAnimation
{
private:
int counter;
int howMany;
public:
bool init() override;
void tick(uint32_t msPassed) override;
DummyAnimation(BattleInterface & owner, int howManyFrames);
};
/// Class that plays effect at one or more positions along with (single) sound effect
class EffectAnimation : public BattleAnimation
{
std::string soundName;
bool effectFinished;
bool reversed;
int effectFlags;
std::shared_ptr<CAnimation> animation;
std::vector<Point> positions;
std::vector<BattleHex> battlehexes;
bool alignToBottom() const;
bool waitForSound() const;
bool forceOnTop() const;
bool screenFill() const;
void onEffectFinished();
void clearEffect();
void playEffect(uint32_t msPassed);
public:
enum EEffectFlags
{
ALIGN_TO_BOTTOM = 1,
FORCE_ON_TOP = 2,
SCREEN_FILL = 4,
};
/// Create animation with screen-wide effect
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, bool reversed = false);
/// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos , int effects = 0, bool reversed = false);
/// Create animation positioned at certain hex(es)
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
~EffectAnimation();
bool init() override;
void tick(uint32_t msPassed) override;
};
class HeroCastAnimation : public BattleAnimation
{
std::shared_ptr<BattleHero> hero;
const CStack * target;
const CSpell * spell;
BattleHex tile;
bool projectileEmitted;
void initializeProjectile();
void emitProjectile();
void emitAnimationEvent();
public:
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
void tick(uint32_t msPassed) override;
bool init() override;
};

View File

@ -1,100 +1,100 @@
/*
* BattleConstants.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 EBattleEffect
{
// list of battle effects that have hardcoded triggers
MAGIC_MIRROR = 3,
FIRE_SHIELD = 11,
FEAR = 15,
GOOD_LUCK = 18,
GOOD_MORALE = 20,
BAD_MORALE = 30,
BAD_LUCK = 48,
RESURRECT = 50,
DRAIN_LIFE = 52,
POISON = 67,
DEATH_BLOW = 73,
REGENERATION = 74,
MANA_DRAIN = 77,
RESISTANCE = 78,
INVALID = -1,
};
enum class EAnimationEvents
{
// any action
ROTATE, // stacks rotate before action
// movement action
MOVE_START, // stack starts movement
MOVEMENT, // movement animation loop starts
MOVE_END, // stack end movement
// attack/spellcast action
BEFORE_HIT, // attack and defence effects play, e.g. luck/death blow
ATTACK, // attack and defence animations are playing
HIT, // hit & death animations are playing
AFTER_HIT, // post-attack effect, e.g. phoenix rebirth
COUNT
};
enum class EHeroAnimType
{
HOLDING = 0,
IDLE = 1, // idling movement that happens from time to time
DEFEAT = 2, // played when army loses stack or on friendly fire
VICTORY = 3, // when enemy stack killed or huge damage is dealt
CAST_SPELL = 4 // spellcasting
};
enum class ECreatureAnimType
{
INVALID = -1,
MOVING = 0,
MOUSEON = 1,
HOLDING = 2, // base idling animation
HITTED = 3, // base animation for when stack is taking damage
DEFENCE = 4, // alternative animation for defending in melee if stack spent its action on defending
DEATH = 5,
DEATH_RANGED = 6, // Optional, alternative animation for when stack is killed by ranged attack
TURN_L = 7,
TURN_R = 8,
//TURN_L2 = 9, //unused - identical to TURN_L
//TURN_R2 = 10, //unused - identical to TURN_R
ATTACK_UP = 11,
ATTACK_FRONT = 12,
ATTACK_DOWN = 13,
SHOOT_UP = 14, // Shooters only
SHOOT_FRONT = 15, // Shooters only
SHOOT_DOWN = 16, // Shooters only
SPECIAL_UP = 17, // If empty, fallback to SPECIAL_FRONT
SPECIAL_FRONT = 18, // Used for any special moves - dragon breath, spellcasting, Pit Lord/Ogre Mage ability
SPECIAL_DOWN = 19, // If empty, fallback to SPECIAL_FRONT
MOVE_START = 20, // small animation to be played before MOVING
MOVE_END = 21, // small animation to be played after MOVING
DEAD = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
DEAD_RANGED = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copied here
FROZEN = 25, // new group, used when stack animation is paused (e.g. petrified). If empty - consist of first frame from HOLDING animation
CAST_UP = 30,
CAST_FRONT = 31,
CAST_DOWN = 32,
GROUP_ATTACK_UP = 40,
GROUP_ATTACK_FRONT = 41,
GROUP_ATTACK_DOWN = 42
};
/*
* BattleConstants.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 EBattleEffect
{
// list of battle effects that have hardcoded triggers
MAGIC_MIRROR = 3,
FIRE_SHIELD = 11,
FEAR = 15,
GOOD_LUCK = 18,
GOOD_MORALE = 20,
BAD_MORALE = 30,
BAD_LUCK = 48,
RESURRECT = 50,
DRAIN_LIFE = 52,
POISON = 67,
DEATH_BLOW = 73,
REGENERATION = 74,
MANA_DRAIN = 77,
RESISTANCE = 78,
INVALID = -1,
};
enum class EAnimationEvents
{
// any action
ROTATE, // stacks rotate before action
// movement action
MOVE_START, // stack starts movement
MOVEMENT, // movement animation loop starts
MOVE_END, // stack end movement
// attack/spellcast action
BEFORE_HIT, // attack and defence effects play, e.g. luck/death blow
ATTACK, // attack and defence animations are playing
HIT, // hit & death animations are playing
AFTER_HIT, // post-attack effect, e.g. phoenix rebirth
COUNT
};
enum class EHeroAnimType
{
HOLDING = 0,
IDLE = 1, // idling movement that happens from time to time
DEFEAT = 2, // played when army loses stack or on friendly fire
VICTORY = 3, // when enemy stack killed or huge damage is dealt
CAST_SPELL = 4 // spellcasting
};
enum class ECreatureAnimType
{
INVALID = -1,
MOVING = 0,
MOUSEON = 1,
HOLDING = 2, // base idling animation
HITTED = 3, // base animation for when stack is taking damage
DEFENCE = 4, // alternative animation for defending in melee if stack spent its action on defending
DEATH = 5,
DEATH_RANGED = 6, // Optional, alternative animation for when stack is killed by ranged attack
TURN_L = 7,
TURN_R = 8,
//TURN_L2 = 9, //unused - identical to TURN_L
//TURN_R2 = 10, //unused - identical to TURN_R
ATTACK_UP = 11,
ATTACK_FRONT = 12,
ATTACK_DOWN = 13,
SHOOT_UP = 14, // Shooters only
SHOOT_FRONT = 15, // Shooters only
SHOOT_DOWN = 16, // Shooters only
SPECIAL_UP = 17, // If empty, fallback to SPECIAL_FRONT
SPECIAL_FRONT = 18, // Used for any special moves - dragon breath, spellcasting, Pit Lord/Ogre Mage ability
SPECIAL_DOWN = 19, // If empty, fallback to SPECIAL_FRONT
MOVE_START = 20, // small animation to be played before MOVING
MOVE_END = 21, // small animation to be played after MOVING
DEAD = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
DEAD_RANGED = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copied here
FROZEN = 25, // new group, used when stack animation is paused (e.g. petrified). If empty - consist of first frame from HOLDING animation
CAST_UP = 30,
CAST_FRONT = 31,
CAST_DOWN = 32,
GROUP_ATTACK_UP = 40,
GROUP_ATTACK_FRONT = 41,
GROUP_ATTACK_DOWN = 42
};

View File

@ -1,160 +1,160 @@
/*
* BattleEffectsController.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 "BattleEffectsController.h"
#include "BattleAnimationClasses.h"
#include "BattleWindow.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleFieldController.h"
#include "BattleStacksController.h"
#include "BattleRenderer.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../render/CAnimation.h"
#include "../render/Graphics.h"
#include "../../CCallback.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "../../lib/NetPacks.h"
#include "../../lib/CStack.h"
#include "../../lib/IGameEventsReceiver.h"
#include "../../lib/CGeneralTextHandler.h"
BattleEffectsController::BattleEffectsController(BattleInterface & owner):
owner(owner)
{
loadColorMuxers();
}
void BattleEffectsController::displayEffect(EBattleEffect effect, const BattleHex & destTile)
{
displayEffect(effect, AudioPath(), destTile);
}
void BattleEffectsController::displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile)
{
size_t effectID = static_cast<size_t>(effect);
AnimationPath customAnim = AnimationPath::builtinTODO(graphics->battleACToDef[effectID][0]);
CCS->soundh->playSound( soundFile );
owner.stacksController->addNewAnim(new EffectAnimation(owner, customAnim, destTile));
}
void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bte)
{
owner.checkForAnimations();
const CStack * stack = owner.getBattle()->battleGetStackByID(bte.stackID);
if(!stack)
{
logGlobal->error("Invalid stack ID %d", bte.stackID);
return;
}
//don't show animation when no HP is regenerated
switch(static_cast<BonusType>(bte.effect))
{
case BonusType::HP_REGENERATION:
displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition());
break;
case BonusType::MANA_DRAIN:
displayEffect(EBattleEffect::MANA_DRAIN, AudioPath::builtin("MANADRAI"), stack->getPosition());
break;
case BonusType::POISON:
displayEffect(EBattleEffect::POISON, AudioPath::builtin("POISON"), stack->getPosition());
break;
case BonusType::FEAR:
displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition());
break;
case BonusType::MORALE:
{
std::string hlp = CGI->generaltexth->allTexts[33];
boost::algorithm::replace_first(hlp,"%s",(stack->getName()));
displayEffect(EBattleEffect::GOOD_MORALE, AudioPath::builtin("GOODMRLE"), stack->getPosition());
owner.appendBattleLog(hlp);
break;
}
default:
return;
}
owner.waitForAnimations();
}
void BattleEffectsController::startAction(const BattleAction & action)
{
owner.checkForAnimations();
const CStack *stack = owner.getBattle()->battleGetStackByID(action.stackNumber);
switch(action.actionType)
{
case EActionType::WAIT:
owner.appendBattleLog(stack->formatGeneralMessage(136));
break;
case EActionType::BAD_MORALE:
owner.appendBattleLog(stack->formatGeneralMessage(-34));
displayEffect(EBattleEffect::BAD_MORALE, AudioPath::builtin("BADMRLE"), stack->getPosition());
break;
}
owner.waitForAnimations();
}
void BattleEffectsController::collectRenderableObjects(BattleRenderer & renderer)
{
for (auto & elem : battleEffects)
{
renderer.insert( EBattleFieldLayer::EFFECTS, elem.tile, [&elem](BattleRenderer::RendererRef canvas)
{
int currentFrame = static_cast<int>(floor(elem.currentFrame));
currentFrame %= elem.animation->size();
auto img = elem.animation->getImage(currentFrame, static_cast<size_t>(elem.type));
canvas.draw(img, elem.pos);
});
}
}
void BattleEffectsController::loadColorMuxers()
{
const JsonNode config(JsonPath::builtin("config/battleEffects.json"));
for(auto & muxer : config["colorMuxers"].Struct())
{
ColorMuxerEffect effect;
std::string identifier = muxer.first;
for (const JsonNode & entry : muxer.second.Vector() )
{
effect.timePoints.push_back(entry["time"].Float());
effect.filters.push_back(ColorFilter::genFromJson(entry));
}
colorMuxerEffects[identifier] = effect;
}
}
const ColorMuxerEffect & BattleEffectsController::getMuxerEffect(const std::string & name)
{
static const ColorMuxerEffect emptyEffect;
if (colorMuxerEffects.count(name))
return colorMuxerEffects[name];
logAnim->error("Failed to find color muxer effect named '%s'!", name);
return emptyEffect;
}
/*
* BattleEffectsController.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 "BattleEffectsController.h"
#include "BattleAnimationClasses.h"
#include "BattleWindow.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleFieldController.h"
#include "BattleStacksController.h"
#include "BattleRenderer.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../render/CAnimation.h"
#include "../render/Graphics.h"
#include "../../CCallback.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "../../lib/NetPacks.h"
#include "../../lib/CStack.h"
#include "../../lib/IGameEventsReceiver.h"
#include "../../lib/CGeneralTextHandler.h"
BattleEffectsController::BattleEffectsController(BattleInterface & owner):
owner(owner)
{
loadColorMuxers();
}
void BattleEffectsController::displayEffect(EBattleEffect effect, const BattleHex & destTile)
{
displayEffect(effect, AudioPath(), destTile);
}
void BattleEffectsController::displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile)
{
size_t effectID = static_cast<size_t>(effect);
AnimationPath customAnim = AnimationPath::builtinTODO(graphics->battleACToDef[effectID][0]);
CCS->soundh->playSound( soundFile );
owner.stacksController->addNewAnim(new EffectAnimation(owner, customAnim, destTile));
}
void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bte)
{
owner.checkForAnimations();
const CStack * stack = owner.getBattle()->battleGetStackByID(bte.stackID);
if(!stack)
{
logGlobal->error("Invalid stack ID %d", bte.stackID);
return;
}
//don't show animation when no HP is regenerated
switch(static_cast<BonusType>(bte.effect))
{
case BonusType::HP_REGENERATION:
displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition());
break;
case BonusType::MANA_DRAIN:
displayEffect(EBattleEffect::MANA_DRAIN, AudioPath::builtin("MANADRAI"), stack->getPosition());
break;
case BonusType::POISON:
displayEffect(EBattleEffect::POISON, AudioPath::builtin("POISON"), stack->getPosition());
break;
case BonusType::FEAR:
displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition());
break;
case BonusType::MORALE:
{
std::string hlp = CGI->generaltexth->allTexts[33];
boost::algorithm::replace_first(hlp,"%s",(stack->getName()));
displayEffect(EBattleEffect::GOOD_MORALE, AudioPath::builtin("GOODMRLE"), stack->getPosition());
owner.appendBattleLog(hlp);
break;
}
default:
return;
}
owner.waitForAnimations();
}
void BattleEffectsController::startAction(const BattleAction & action)
{
owner.checkForAnimations();
const CStack *stack = owner.getBattle()->battleGetStackByID(action.stackNumber);
switch(action.actionType)
{
case EActionType::WAIT:
owner.appendBattleLog(stack->formatGeneralMessage(136));
break;
case EActionType::BAD_MORALE:
owner.appendBattleLog(stack->formatGeneralMessage(-34));
displayEffect(EBattleEffect::BAD_MORALE, AudioPath::builtin("BADMRLE"), stack->getPosition());
break;
}
owner.waitForAnimations();
}
void BattleEffectsController::collectRenderableObjects(BattleRenderer & renderer)
{
for (auto & elem : battleEffects)
{
renderer.insert( EBattleFieldLayer::EFFECTS, elem.tile, [&elem](BattleRenderer::RendererRef canvas)
{
int currentFrame = static_cast<int>(floor(elem.currentFrame));
currentFrame %= elem.animation->size();
auto img = elem.animation->getImage(currentFrame, static_cast<size_t>(elem.type));
canvas.draw(img, elem.pos);
});
}
}
void BattleEffectsController::loadColorMuxers()
{
const JsonNode config(JsonPath::builtin("config/battleEffects.json"));
for(auto & muxer : config["colorMuxers"].Struct())
{
ColorMuxerEffect effect;
std::string identifier = muxer.first;
for (const JsonNode & entry : muxer.second.Vector() )
{
effect.timePoints.push_back(entry["time"].Float());
effect.filters.push_back(ColorFilter::genFromJson(entry));
}
colorMuxerEffects[identifier] = effect;
}
}
const ColorMuxerEffect & BattleEffectsController::getMuxerEffect(const std::string & name)
{
static const ColorMuxerEffect emptyEffect;
if (colorMuxerEffects.count(name))
return colorMuxerEffects[name];
logAnim->error("Failed to find color muxer effect named '%s'!", name);
return emptyEffect;
}

View File

@ -1,75 +1,75 @@
/*
* BattleEffectsController.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 "../../lib/battle/BattleHex.h"
#include "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class BattleAction;
struct BattleTriggerEffect;
VCMI_LIB_NAMESPACE_END
struct ColorMuxerEffect;
class CAnimation;
class Canvas;
class BattleInterface;
class BattleRenderer;
class EffectAnimation;
/// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,...
struct BattleEffect
{
enum class AnimType : ui8
{
DEFAULT = 0, //If we have such animation
REVERSE = 1 //Reverse DEFAULT will be used
};
AnimType type;
Point pos; //position on the screen
float currentFrame;
std::shared_ptr<CAnimation> animation;
int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
BattleHex tile; //Indicates if effect which hex the effect is drawn on
};
/// Controls rendering of effects in battle, e.g. from spells, abilities and various other actions like morale
class BattleEffectsController
{
BattleInterface & owner;
/// list of current effects that are being displayed on screen (spells & creature abilities)
std::vector<BattleEffect> battleEffects;
std::map<std::string, ColorMuxerEffect> colorMuxerEffects;
void loadColorMuxers();
public:
const ColorMuxerEffect &getMuxerEffect(const std::string & name);
BattleEffectsController(BattleInterface & owner);
void startAction(const BattleAction & action);
//displays custom effect on the battlefield
void displayEffect(EBattleEffect effect, const BattleHex & destTile);
void displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile);
void battleTriggerEffect(const BattleTriggerEffect & bte);
void collectRenderableObjects(BattleRenderer & renderer);
friend class EffectAnimation;
};
/*
* BattleEffectsController.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 "../../lib/battle/BattleHex.h"
#include "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class BattleAction;
struct BattleTriggerEffect;
VCMI_LIB_NAMESPACE_END
struct ColorMuxerEffect;
class CAnimation;
class Canvas;
class BattleInterface;
class BattleRenderer;
class EffectAnimation;
/// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,...
struct BattleEffect
{
enum class AnimType : ui8
{
DEFAULT = 0, //If we have such animation
REVERSE = 1 //Reverse DEFAULT will be used
};
AnimType type;
Point pos; //position on the screen
float currentFrame;
std::shared_ptr<CAnimation> animation;
int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
BattleHex tile; //Indicates if effect which hex the effect is drawn on
};
/// Controls rendering of effects in battle, e.g. from spells, abilities and various other actions like morale
class BattleEffectsController
{
BattleInterface & owner;
/// list of current effects that are being displayed on screen (spells & creature abilities)
std::vector<BattleEffect> battleEffects;
std::map<std::string, ColorMuxerEffect> colorMuxerEffects;
void loadColorMuxers();
public:
const ColorMuxerEffect &getMuxerEffect(const std::string & name);
BattleEffectsController(BattleInterface & owner);
void startAction(const BattleAction & action);
//displays custom effect on the battlefield
void displayEffect(EBattleEffect effect, const BattleHex & destTile);
void displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile);
void battleTriggerEffect(const BattleTriggerEffect & bte);
void collectRenderableObjects(BattleRenderer & renderer);
friend class EffectAnimation;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,144 +1,144 @@
/*
* BattleFieldController.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 "../../lib/battle/BattleHex.h"
#include "../../lib/Point.h"
#include "../gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class Rect;
VCMI_LIB_NAMESPACE_END
class BattleHero;
class CAnimation;
class Canvas;
class IImage;
class BattleInterface;
/// Handles battlefield grid as well as rendering of background layer of battle interface
class BattleFieldController : public CIntObject
{
BattleInterface & owner;
std::shared_ptr<IImage> background;
std::shared_ptr<IImage> cellBorder;
std::shared_ptr<IImage> cellUnitMovementHighlight;
std::shared_ptr<IImage> cellUnitMaxMovementHighlight;
std::shared_ptr<IImage> cellShade;
std::shared_ptr<CAnimation> rangedFullDamageLimitImages;
std::shared_ptr<CAnimation> shootingRangeLimitImages;
std::shared_ptr<CAnimation> attackCursors;
std::shared_ptr<CAnimation> spellCursors;
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
std::unique_ptr<Canvas> backgroundWithHexes;
/// direction which will be used to perform attack with current cursor position
Point currentAttackOriginPoint;
/// hex currently under mouse hover
BattleHex hoveredHex;
/// hexes to which currently active stack can move
std::vector<BattleHex> occupiableHexes;
/// hexes that when in front of a unit cause it's amount box to move back
std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes;
void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder);
std::set<BattleHex> getHighlightedHexesForActiveStack();
std::set<BattleHex> getMovementRangeForHoveredStack();
std::set<BattleHex> getHighlightedHexesForSpellRange();
std::set<BattleHex> getHighlightedHexesForMovementTarget();
// Range limit highlight helpers
/// get all hexes within a certain distance of given hex
std::vector<BattleHex> getRangeHexes(BattleHex sourceHex, uint8_t distance);
/// get only hexes at the limit of a range
std::vector<BattleHex> getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> hexRange, uint8_t distanceToLimit);
/// calculate if a hex is in range limit and return its index in range
bool IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & rangeLimitHexes, int * hexIndexInRangeLimit);
/// get an array that has for each hex in range, an aray with all directions where an ouside neighbour hex exists
std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> rangeHexes, std::vector<BattleHex> rangeLimitHexes);
/// calculates what image to use as range limit, depending on the direction of neighbors
/// a mask is used internally to mark the directions of all neighbours
/// based on this mask the corresponding image is selected
std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages);
/// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes
void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, std::vector<BattleHex> & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighligts);
/// to reduce the number of source images used, some images will be used as flipped versions of preloaded ones
void flipRangeLimitImagesIntoPositions(std::shared_ptr<CAnimation> images);
void showBackground(Canvas & canvas);
void showBackgroundImage(Canvas & canvas);
void showBackgroundImageWithHexes(Canvas & canvas);
void showHighlightedHexes(Canvas & canvas);
void updateAccessibleHexes();
BattleHex getHexAtPosition(Point hoverPosition);
/// Checks whether selected pixel is transparent, uses local coordinates of a hex
bool isPixelInHex(Point const & position);
size_t selectBattleCursor(BattleHex myNumber);
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override;
void clickPressed(const Point & cursorPosition) override;
void showPopupWindow(const Point & cursorPosition) override;
void activate() override;
void showAll(Canvas & to) override;
void show(Canvas & to) override;
void tick(uint32_t msPassed) override;
bool receiveEvent(const Point & position, int eventType) const override;
public:
BattleFieldController(BattleInterface & owner);
void createHeroes();
void redrawBackgroundWithHexes();
void renderBattlefield(Canvas & canvas);
/// Returns position of hex relative to owner (BattleInterface)
Rect hexPositionLocal(BattleHex hex) const;
/// Returns position of hex relative to game window
Rect hexPositionAbsolute(BattleHex hex) const;
/// Returns ID of currently hovered hex or BattleHex::INVALID if none
BattleHex getHoveredHex();
/// Returns the currently hovered stack
const CStack* getHoveredStack();
/// returns true if selected tile can be attacked in melee by current stack
bool isTileAttackable(const BattleHex & number) const;
/// returns true if stack should render its stack count image in default position - outside own hex
bool stackCountOutsideHex(const BattleHex & number) const;
BattleHex::EDir selectAttackDirection(BattleHex myNumber);
BattleHex fromWhichHexAttack(BattleHex myNumber);
};
/*
* BattleFieldController.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 "../../lib/battle/BattleHex.h"
#include "../../lib/Point.h"
#include "../gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class Rect;
VCMI_LIB_NAMESPACE_END
class BattleHero;
class CAnimation;
class Canvas;
class IImage;
class BattleInterface;
/// Handles battlefield grid as well as rendering of background layer of battle interface
class BattleFieldController : public CIntObject
{
BattleInterface & owner;
std::shared_ptr<IImage> background;
std::shared_ptr<IImage> cellBorder;
std::shared_ptr<IImage> cellUnitMovementHighlight;
std::shared_ptr<IImage> cellUnitMaxMovementHighlight;
std::shared_ptr<IImage> cellShade;
std::shared_ptr<CAnimation> rangedFullDamageLimitImages;
std::shared_ptr<CAnimation> shootingRangeLimitImages;
std::shared_ptr<CAnimation> attackCursors;
std::shared_ptr<CAnimation> spellCursors;
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
std::unique_ptr<Canvas> backgroundWithHexes;
/// direction which will be used to perform attack with current cursor position
Point currentAttackOriginPoint;
/// hex currently under mouse hover
BattleHex hoveredHex;
/// hexes to which currently active stack can move
std::vector<BattleHex> occupiableHexes;
/// hexes that when in front of a unit cause it's amount box to move back
std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes;
void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder);
std::set<BattleHex> getHighlightedHexesForActiveStack();
std::set<BattleHex> getMovementRangeForHoveredStack();
std::set<BattleHex> getHighlightedHexesForSpellRange();
std::set<BattleHex> getHighlightedHexesForMovementTarget();
// Range limit highlight helpers
/// get all hexes within a certain distance of given hex
std::vector<BattleHex> getRangeHexes(BattleHex sourceHex, uint8_t distance);
/// get only hexes at the limit of a range
std::vector<BattleHex> getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> hexRange, uint8_t distanceToLimit);
/// calculate if a hex is in range limit and return its index in range
bool IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & rangeLimitHexes, int * hexIndexInRangeLimit);
/// get an array that has for each hex in range, an aray with all directions where an ouside neighbour hex exists
std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> rangeHexes, std::vector<BattleHex> rangeLimitHexes);
/// calculates what image to use as range limit, depending on the direction of neighbors
/// a mask is used internally to mark the directions of all neighbours
/// based on this mask the corresponding image is selected
std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages);
/// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes
void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, std::vector<BattleHex> & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighligts);
/// to reduce the number of source images used, some images will be used as flipped versions of preloaded ones
void flipRangeLimitImagesIntoPositions(std::shared_ptr<CAnimation> images);
void showBackground(Canvas & canvas);
void showBackgroundImage(Canvas & canvas);
void showBackgroundImageWithHexes(Canvas & canvas);
void showHighlightedHexes(Canvas & canvas);
void updateAccessibleHexes();
BattleHex getHexAtPosition(Point hoverPosition);
/// Checks whether selected pixel is transparent, uses local coordinates of a hex
bool isPixelInHex(Point const & position);
size_t selectBattleCursor(BattleHex myNumber);
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override;
void clickPressed(const Point & cursorPosition) override;
void showPopupWindow(const Point & cursorPosition) override;
void activate() override;
void showAll(Canvas & to) override;
void show(Canvas & to) override;
void tick(uint32_t msPassed) override;
bool receiveEvent(const Point & position, int eventType) const override;
public:
BattleFieldController(BattleInterface & owner);
void createHeroes();
void redrawBackgroundWithHexes();
void renderBattlefield(Canvas & canvas);
/// Returns position of hex relative to owner (BattleInterface)
Rect hexPositionLocal(BattleHex hex) const;
/// Returns position of hex relative to game window
Rect hexPositionAbsolute(BattleHex hex) const;
/// Returns ID of currently hovered hex or BattleHex::INVALID if none
BattleHex getHoveredHex();
/// Returns the currently hovered stack
const CStack* getHoveredStack();
/// returns true if selected tile can be attacked in melee by current stack
bool isTileAttackable(const BattleHex & number) const;
/// returns true if stack should render its stack count image in default position - outside own hex
bool stackCountOutsideHex(const BattleHex & number) const;
BattleHex::EDir selectAttackDirection(BattleHex myNumber);
BattleHex fromWhichHexAttack(BattleHex myNumber);
};

File diff suppressed because it is too large Load Diff

View File

@ -1,230 +1,230 @@
/*
* BattleInterface.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 "BattleConstants.h"
#include "../gui/CIntObject.h"
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
#include "../../lib/CondSh.h"
VCMI_LIB_NAMESPACE_BEGIN
class CCreatureSet;
class CGHeroInstance;
class CStack;
struct BattleResult;
struct BattleSpellCast;
struct CObstacleInstance;
struct SetStackEffect;
class BattleAction;
class CGTownInstance;
struct CatapultAttack;
struct BattleTriggerEffect;
struct BattleHex;
struct InfoAboutHero;
class ObstacleChanges;
class CPlayerBattleCallback;
VCMI_LIB_NAMESPACE_END
class BattleHero;
class Canvas;
class BattleResultWindow;
class StackQueue;
class CPlayerInterface;
class CAnimation;
struct BattleEffect;
class IImage;
class StackQueue;
class BattleProjectileController;
class BattleSiegeController;
class BattleObstacleController;
class BattleFieldController;
class BattleRenderer;
class BattleWindow;
class BattleStacksController;
class BattleActionsController;
class BattleEffectsController;
class BattleConsole;
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
struct StackAttackedInfo
{
const CStack *defender;
const CStack *attacker;
int64_t damageDealt;
uint32_t amountKilled;
SpellID spellEffect;
bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
bool killed; //if true, stack has been killed
bool rebirth; //if true, play rebirth animation after all
bool cloneKilled;
bool fireShield;
};
struct StackAttackInfo
{
const CStack *attacker;
const CStack *defender;
std::vector< const CStack *> secondaryDefender;
SpellID spellEffect;
BattleHex tile;
bool indirectAttack;
bool lucky;
bool unlucky;
bool deathBlow;
bool lifeDrain;
};
/// Main class for battles, responsible for relaying information from server to various battle entities
class BattleInterface
{
using AwaitingAnimationAction = std::function<void()>;
struct AwaitingAnimationEvents {
AwaitingAnimationAction action;
EAnimationEvents event;
};
/// Conditional variables that are set depending on ongoing animations on the battlefield
CondSh<bool> ongoingAnimationsState;
/// List of events that are waiting to be triggered
std::vector<AwaitingAnimationEvents> awaitingEvents;
/// used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
std::shared_ptr<CPlayerInterface> tacticianInterface;
/// attacker interface, not null if attacker is human in our vcmiclient
std::shared_ptr<CPlayerInterface> attackerInt;
/// defender interface, not null if attacker is human in our vcmiclient
std::shared_ptr<CPlayerInterface> defenderInt;
/// if set to true, battle is still starting and waiting for intro sound to end / key press from player
bool battleOpeningDelayActive;
/// ID of ongoing battle
BattleID battleID;
void playIntroSoundAndUnlockInterface();
void onIntroSoundPlayed();
public:
/// copy of initial armies (for result window)
const CCreatureSet *army1;
const CCreatureSet *army2;
std::shared_ptr<BattleWindow> windowObject;
std::shared_ptr<BattleConsole> console;
/// currently active player interface
std::shared_ptr<CPlayerInterface> curInt;
const CGHeroInstance *attackingHeroInstance;
const CGHeroInstance *defendingHeroInstance;
bool tacticsMode;
std::unique_ptr<BattleProjectileController> projectilesController;
std::unique_ptr<BattleSiegeController> siegeController;
std::unique_ptr<BattleObstacleController> obstacleController;
std::unique_ptr<BattleFieldController> fieldController;
std::unique_ptr<BattleStacksController> stacksController;
std::unique_ptr<BattleActionsController> actionsController;
std::unique_ptr<BattleEffectsController> effectsController;
std::shared_ptr<BattleHero> attackingHero;
std::shared_ptr<BattleHero> defendingHero;
bool openingPlaying();
void openingEnd();
bool makingTurn() const;
BattleID getBattleID() const;
std::shared_ptr<CPlayerBattleCallback> getBattle() const;
BattleInterface(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr);
~BattleInterface();
void trySetActivePlayer( PlayerColor player ); // if in hotseat, will activate interface of chosen player
void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all
void requestAutofightingAIToTakeAction();
void giveCommand(EActionType action, BattleHex tile = BattleHex(), SpellID spell = SpellID::NONE);
void sendCommand(BattleAction command, const CStack * actor = nullptr);
const CGHeroInstance *getActiveHero(); //returns hero that can currently cast a spell
void showInterface(Canvas & to);
void setHeroAnimation(ui8 side, EHeroAnimType phase);
void executeSpellCast(); //called when a hero casts a spell
void appendBattleLog(const std::string & newEntry);
void redrawBattlefield(); //refresh GUI after changing stack range / grid settings
CPlayerInterface *getCurrentPlayerInterface() const;
void tacticNextStack(const CStack *current);
void tacticPhaseEnd();
void setBattleQueueVisibility(bool visible);
void setStickyHeroWindowsVisibility(bool visible);
void executeStagedAnimations();
void executeAnimationStage( EAnimationEvents event);
void onAnimationsStarted();
void onAnimationsFinished();
void waitForAnimations();
bool hasAnimations();
void checkForAnimations();
void addToAnimationStage( EAnimationEvents event, const AwaitingAnimationAction & action);
//call-ins
void startAction(const BattleAction & action);
void stackReset(const CStack * stack);
void stackAdded(const CStack * stack); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance, bool teleport); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest
void newRoundFirst();
void newRound(); //caled when round is ended;
void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
void displayBattleLog(const std::vector<MetaString> & battleLog);
void displaySpellAnimationQueue(const CSpell * spell, const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit);
void displaySpellCast(const CSpell * spell, BattleHex destinationTile); //displays spell`s cast animation
void displaySpellEffect(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation
void displaySpellHit(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation
void endAction(const BattleAction & action);
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> oi);
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
void gateStateChanged(const EGateState state);
const CGHeroInstance *currentHero() const;
InfoAboutHero enemyHero() const;
};
/*
* BattleInterface.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 "BattleConstants.h"
#include "../gui/CIntObject.h"
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
#include "../../lib/CondSh.h"
VCMI_LIB_NAMESPACE_BEGIN
class CCreatureSet;
class CGHeroInstance;
class CStack;
struct BattleResult;
struct BattleSpellCast;
struct CObstacleInstance;
struct SetStackEffect;
class BattleAction;
class CGTownInstance;
struct CatapultAttack;
struct BattleTriggerEffect;
struct BattleHex;
struct InfoAboutHero;
class ObstacleChanges;
class CPlayerBattleCallback;
VCMI_LIB_NAMESPACE_END
class BattleHero;
class Canvas;
class BattleResultWindow;
class StackQueue;
class CPlayerInterface;
class CAnimation;
struct BattleEffect;
class IImage;
class StackQueue;
class BattleProjectileController;
class BattleSiegeController;
class BattleObstacleController;
class BattleFieldController;
class BattleRenderer;
class BattleWindow;
class BattleStacksController;
class BattleActionsController;
class BattleEffectsController;
class BattleConsole;
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
struct StackAttackedInfo
{
const CStack *defender;
const CStack *attacker;
int64_t damageDealt;
uint32_t amountKilled;
SpellID spellEffect;
bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
bool killed; //if true, stack has been killed
bool rebirth; //if true, play rebirth animation after all
bool cloneKilled;
bool fireShield;
};
struct StackAttackInfo
{
const CStack *attacker;
const CStack *defender;
std::vector< const CStack *> secondaryDefender;
SpellID spellEffect;
BattleHex tile;
bool indirectAttack;
bool lucky;
bool unlucky;
bool deathBlow;
bool lifeDrain;
};
/// Main class for battles, responsible for relaying information from server to various battle entities
class BattleInterface
{
using AwaitingAnimationAction = std::function<void()>;
struct AwaitingAnimationEvents {
AwaitingAnimationAction action;
EAnimationEvents event;
};
/// Conditional variables that are set depending on ongoing animations on the battlefield
CondSh<bool> ongoingAnimationsState;
/// List of events that are waiting to be triggered
std::vector<AwaitingAnimationEvents> awaitingEvents;
/// used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
std::shared_ptr<CPlayerInterface> tacticianInterface;
/// attacker interface, not null if attacker is human in our vcmiclient
std::shared_ptr<CPlayerInterface> attackerInt;
/// defender interface, not null if attacker is human in our vcmiclient
std::shared_ptr<CPlayerInterface> defenderInt;
/// if set to true, battle is still starting and waiting for intro sound to end / key press from player
bool battleOpeningDelayActive;
/// ID of ongoing battle
BattleID battleID;
void playIntroSoundAndUnlockInterface();
void onIntroSoundPlayed();
public:
/// copy of initial armies (for result window)
const CCreatureSet *army1;
const CCreatureSet *army2;
std::shared_ptr<BattleWindow> windowObject;
std::shared_ptr<BattleConsole> console;
/// currently active player interface
std::shared_ptr<CPlayerInterface> curInt;
const CGHeroInstance *attackingHeroInstance;
const CGHeroInstance *defendingHeroInstance;
bool tacticsMode;
std::unique_ptr<BattleProjectileController> projectilesController;
std::unique_ptr<BattleSiegeController> siegeController;
std::unique_ptr<BattleObstacleController> obstacleController;
std::unique_ptr<BattleFieldController> fieldController;
std::unique_ptr<BattleStacksController> stacksController;
std::unique_ptr<BattleActionsController> actionsController;
std::unique_ptr<BattleEffectsController> effectsController;
std::shared_ptr<BattleHero> attackingHero;
std::shared_ptr<BattleHero> defendingHero;
bool openingPlaying();
void openingEnd();
bool makingTurn() const;
BattleID getBattleID() const;
std::shared_ptr<CPlayerBattleCallback> getBattle() const;
BattleInterface(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr);
~BattleInterface();
void trySetActivePlayer( PlayerColor player ); // if in hotseat, will activate interface of chosen player
void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all
void requestAutofightingAIToTakeAction();
void giveCommand(EActionType action, BattleHex tile = BattleHex(), SpellID spell = SpellID::NONE);
void sendCommand(BattleAction command, const CStack * actor = nullptr);
const CGHeroInstance *getActiveHero(); //returns hero that can currently cast a spell
void showInterface(Canvas & to);
void setHeroAnimation(ui8 side, EHeroAnimType phase);
void executeSpellCast(); //called when a hero casts a spell
void appendBattleLog(const std::string & newEntry);
void redrawBattlefield(); //refresh GUI after changing stack range / grid settings
CPlayerInterface *getCurrentPlayerInterface() const;
void tacticNextStack(const CStack *current);
void tacticPhaseEnd();
void setBattleQueueVisibility(bool visible);
void setStickyHeroWindowsVisibility(bool visible);
void executeStagedAnimations();
void executeAnimationStage( EAnimationEvents event);
void onAnimationsStarted();
void onAnimationsFinished();
void waitForAnimations();
bool hasAnimations();
void checkForAnimations();
void addToAnimationStage( EAnimationEvents event, const AwaitingAnimationAction & action);
//call-ins
void startAction(const BattleAction & action);
void stackReset(const CStack * stack);
void stackAdded(const CStack * stack); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance, bool teleport); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest
void newRoundFirst();
void newRound(); //caled when round is ended;
void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
void displayBattleLog(const std::vector<MetaString> & battleLog);
void displaySpellAnimationQueue(const CSpell * spell, const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit);
void displaySpellCast(const CSpell * spell, BattleHex destinationTile); //displays spell`s cast animation
void displaySpellEffect(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation
void displaySpellHit(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation
void endAction(const BattleAction & action);
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> oi);
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
void gateStateChanged(const EGateState state);
const CGHeroInstance *currentHero() const;
InfoAboutHero enemyHero() const;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,238 +1,238 @@
/*
* BattleInterfaceClasses.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 "BattleConstants.h"
#include "../gui/CIntObject.h"
#include "../../lib/FunctionList.h"
#include "../../lib/battle/BattleHex.h"
#include "../windows/CWindowObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
struct BattleResult;
struct InfoAboutHero;
class CStack;
namespace battle
{
class Unit;
}
VCMI_LIB_NAMESPACE_END
class CAnimation;
class Canvas;
class BattleInterface;
class CPicture;
class CFilledTexture;
class CButton;
class CToggleButton;
class CLabel;
class CTextBox;
class CAnimImage;
class CPlayerInterface;
class BattleRenderer;
/// Class which shows the console at the bottom of the battle screen and manages the text of the console
class BattleConsole : public CIntObject, public IStatusBar
{
private:
std::shared_ptr<CPicture> background;
/// List of all texts added during battle, essentially - log of entire battle
std::vector< std::string > logEntries;
/// Current scrolling position, to allow showing older entries via scroll buttons
int scrollPosition;
/// current hover text set on mouse move, takes priority over log entries
std::string hoverText;
/// current text entered via in-game console, takes priority over both log entries and hover text
std::string consoleText;
/// if true then we are currently entering console text
bool enteringText;
/// splits text into individual strings for battle log
std::vector<std::string> splitText(const std::string &text);
/// select line(s) that will be visible in UI
std::vector<std::string> getVisibleText();
public:
BattleConsole(std::shared_ptr<CPicture> backgroundSource, const Point & objectPos, const Point & imagePos, const Point &size);
void showAll(Canvas & to) override;
void deactivate() override;
bool addText(const std::string &text); //adds text at the last position; returns false if failed (e.g. text longer than 70 characters)
void scrollUp(ui32 by = 1); //scrolls console up by 'by' positions
void scrollDown(ui32 by = 1); //scrolls console up by 'by' positions
// IStatusBar interface
void write(const std::string & Text) override;
void clearIfMatching(const std::string & Text) override;
void clear() override;
void setEnteringMode(bool on) override;
void setEnteredText(const std::string & text) override;
};
/// Hero battle animation
class BattleHero : public CIntObject
{
bool defender;
CFunctionList<void()> phaseFinishedCallback;
std::shared_ptr<CAnimation> animation;
std::shared_ptr<CAnimation> flagAnimation;
const CGHeroInstance * hero; //this animation's hero instance
const BattleInterface & owner; //battle interface to which this animation is assigned
EHeroAnimType phase; //stage of animation
EHeroAnimType nextPhase; //stage of animation to be set after current phase is fully displayed
float currentSpeed;
float currentFrame; //frame of animation
float flagCurrentFrame;
void switchToNextPhase();
void render(Canvas & canvas); //prints next frame of animation to to
public:
const CGHeroInstance * instance();
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()> &);
void pause();
void play();
void heroLeftClicked();
void heroRightClicked();
BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender);
};
class HeroInfoBasicPanel : public CIntObject //extracted from InfoWindow to fit better as non-popup embed element
{
private:
std::shared_ptr<CPicture> background;
std::vector<std::shared_ptr<CLabel>> labels;
std::vector<std::shared_ptr<CAnimImage>> icons;
public:
HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground = true);
void show(Canvas & to) override;
void initializeData(const InfoAboutHero & hero);
void update(const InfoAboutHero & updatedInfo);
};
class HeroInfoWindow : public CWindowObject
{
private:
std::shared_ptr<HeroInfoBasicPanel> content;
public:
HeroInfoWindow(const InfoAboutHero & hero, Point * position);
};
/// Class which is responsible for showing the battle result window
class BattleResultWindow : public WindowBase
{
private:
std::shared_ptr<CPicture> background;
std::vector<std::shared_ptr<CLabel>> labels;
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> repeat;
std::vector<std::shared_ptr<CAnimImage>> icons;
std::shared_ptr<CTextBox> description;
CPlayerInterface & owner;
enum BattleResultVideo
{
NONE,
WIN,
SURRENDER,
RETREAT,
RETREAT_LOOP,
DEFEAT,
DEFEAT_LOOP,
DEFEAT_SIEGE,
DEFEAT_SIEGE_LOOP,
WIN_SIEGE,
WIN_SIEGE_LOOP,
};
BattleResultVideo currentVideo;
void playVideo(bool startLoop = false);
void buttonPressed(int button); //internal function for button callbacks
public:
BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay = false);
void bExitf(); //exit button callback
void bRepeatf(); //repeat button callback
std::function<void(int result)> resultCallback; //callback receiving which button was pressed
void activate() override;
void show(Canvas & to) override;
};
/// Shows the stack queue
class StackQueue : public CIntObject
{
class StackBox : public CIntObject
{
StackQueue * owner;
std::optional<uint32_t> boundUnitID;
std::shared_ptr<CPicture> background;
std::shared_ptr<CAnimImage> icon;
std::shared_ptr<CLabel> amount;
std::shared_ptr<CAnimImage> stateIcon;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
bool isBoundUnitHighlighted() const;
public:
StackBox(StackQueue * owner);
void setUnit(const battle::Unit * unit, size_t turn = 0);
std::optional<uint32_t> getBoundUnitID() const;
};
static const int QUEUE_SIZE = 10;
std::shared_ptr<CFilledTexture> background;
std::vector<std::shared_ptr<StackBox>> stackBoxes;
BattleInterface & owner;
std::shared_ptr<CAnimation> icons;
std::shared_ptr<CAnimation> stateIcons;
int32_t getSiegeShooterIconID();
public:
const bool embedded;
StackQueue(bool Embedded, BattleInterface & owner);
void update();
std::optional<uint32_t> getHoveredUnitIdIfAny() const;
void show(Canvas & to) override;
};
/*
* BattleInterfaceClasses.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 "BattleConstants.h"
#include "../gui/CIntObject.h"
#include "../../lib/FunctionList.h"
#include "../../lib/battle/BattleHex.h"
#include "../windows/CWindowObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
struct BattleResult;
struct InfoAboutHero;
class CStack;
namespace battle
{
class Unit;
}
VCMI_LIB_NAMESPACE_END
class CAnimation;
class Canvas;
class BattleInterface;
class CPicture;
class CFilledTexture;
class CButton;
class CToggleButton;
class CLabel;
class CTextBox;
class CAnimImage;
class CPlayerInterface;
class BattleRenderer;
/// Class which shows the console at the bottom of the battle screen and manages the text of the console
class BattleConsole : public CIntObject, public IStatusBar
{
private:
std::shared_ptr<CPicture> background;
/// List of all texts added during battle, essentially - log of entire battle
std::vector< std::string > logEntries;
/// Current scrolling position, to allow showing older entries via scroll buttons
int scrollPosition;
/// current hover text set on mouse move, takes priority over log entries
std::string hoverText;
/// current text entered via in-game console, takes priority over both log entries and hover text
std::string consoleText;
/// if true then we are currently entering console text
bool enteringText;
/// splits text into individual strings for battle log
std::vector<std::string> splitText(const std::string &text);
/// select line(s) that will be visible in UI
std::vector<std::string> getVisibleText();
public:
BattleConsole(std::shared_ptr<CPicture> backgroundSource, const Point & objectPos, const Point & imagePos, const Point &size);
void showAll(Canvas & to) override;
void deactivate() override;
bool addText(const std::string &text); //adds text at the last position; returns false if failed (e.g. text longer than 70 characters)
void scrollUp(ui32 by = 1); //scrolls console up by 'by' positions
void scrollDown(ui32 by = 1); //scrolls console up by 'by' positions
// IStatusBar interface
void write(const std::string & Text) override;
void clearIfMatching(const std::string & Text) override;
void clear() override;
void setEnteringMode(bool on) override;
void setEnteredText(const std::string & text) override;
};
/// Hero battle animation
class BattleHero : public CIntObject
{
bool defender;
CFunctionList<void()> phaseFinishedCallback;
std::shared_ptr<CAnimation> animation;
std::shared_ptr<CAnimation> flagAnimation;
const CGHeroInstance * hero; //this animation's hero instance
const BattleInterface & owner; //battle interface to which this animation is assigned
EHeroAnimType phase; //stage of animation
EHeroAnimType nextPhase; //stage of animation to be set after current phase is fully displayed
float currentSpeed;
float currentFrame; //frame of animation
float flagCurrentFrame;
void switchToNextPhase();
void render(Canvas & canvas); //prints next frame of animation to to
public:
const CGHeroInstance * instance();
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()> &);
void pause();
void play();
void heroLeftClicked();
void heroRightClicked();
BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender);
};
class HeroInfoBasicPanel : public CIntObject //extracted from InfoWindow to fit better as non-popup embed element
{
private:
std::shared_ptr<CPicture> background;
std::vector<std::shared_ptr<CLabel>> labels;
std::vector<std::shared_ptr<CAnimImage>> icons;
public:
HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground = true);
void show(Canvas & to) override;
void initializeData(const InfoAboutHero & hero);
void update(const InfoAboutHero & updatedInfo);
};
class HeroInfoWindow : public CWindowObject
{
private:
std::shared_ptr<HeroInfoBasicPanel> content;
public:
HeroInfoWindow(const InfoAboutHero & hero, Point * position);
};
/// Class which is responsible for showing the battle result window
class BattleResultWindow : public WindowBase
{
private:
std::shared_ptr<CPicture> background;
std::vector<std::shared_ptr<CLabel>> labels;
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> repeat;
std::vector<std::shared_ptr<CAnimImage>> icons;
std::shared_ptr<CTextBox> description;
CPlayerInterface & owner;
enum BattleResultVideo
{
NONE,
WIN,
SURRENDER,
RETREAT,
RETREAT_LOOP,
DEFEAT,
DEFEAT_LOOP,
DEFEAT_SIEGE,
DEFEAT_SIEGE_LOOP,
WIN_SIEGE,
WIN_SIEGE_LOOP,
};
BattleResultVideo currentVideo;
void playVideo(bool startLoop = false);
void buttonPressed(int button); //internal function for button callbacks
public:
BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay = false);
void bExitf(); //exit button callback
void bRepeatf(); //repeat button callback
std::function<void(int result)> resultCallback; //callback receiving which button was pressed
void activate() override;
void show(Canvas & to) override;
};
/// Shows the stack queue
class StackQueue : public CIntObject
{
class StackBox : public CIntObject
{
StackQueue * owner;
std::optional<uint32_t> boundUnitID;
std::shared_ptr<CPicture> background;
std::shared_ptr<CAnimImage> icon;
std::shared_ptr<CLabel> amount;
std::shared_ptr<CAnimImage> stateIcon;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
bool isBoundUnitHighlighted() const;
public:
StackBox(StackQueue * owner);
void setUnit(const battle::Unit * unit, size_t turn = 0);
std::optional<uint32_t> getBoundUnitID() const;
};
static const int QUEUE_SIZE = 10;
std::shared_ptr<CFilledTexture> background;
std::vector<std::shared_ptr<StackBox>> stackBoxes;
BattleInterface & owner;
std::shared_ptr<CAnimation> icons;
std::shared_ptr<CAnimation> stateIcons;
int32_t getSiegeShooterIconID();
public:
const bool embedded;
StackQueue(bool Embedded, BattleInterface & owner);
void update();
std::optional<uint32_t> getHoveredUnitIdIfAny() const;
void show(Canvas & to) override;
};

View File

@ -1,228 +1,228 @@
/*
* BattleObstacleController.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 "BattleObstacleController.h"
#include "BattleInterface.h"
#include "BattleFieldController.h"
#include "BattleAnimationClasses.h"
#include "BattleStacksController.h"
#include "BattleRenderer.h"
#include "CreatureAnimation.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/IRenderHandler.h"
#include "../../CCallback.h"
#include "../../lib/battle/CObstacleInstance.h"
#include "../../lib/ObstacleHandler.h"
BattleObstacleController::BattleObstacleController(BattleInterface & owner):
owner(owner),
timePassed(0.f)
{
auto obst = owner.getBattle()->battleGetAllObstacles();
for(auto & elem : obst)
{
if ( elem->obstacleType == CObstacleInstance::MOAT )
continue; // handled by siege controller;
loadObstacleImage(*elem);
}
}
void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
{
AnimationPath animationName = oi.getAnimation();
if (animationsCache.count(animationName) == 0)
{
if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
{
// obstacle uses single bitmap image for animations
auto animation = GH.renderHandler().createAnimation();
animation->setCustom(animationName.getName(), 0, 0);
animationsCache[animationName] = animation;
animation->preload();
}
else
{
auto animation = GH.renderHandler().loadAnimation(animationName);
animationsCache[animationName] = animation;
animation->preload();
}
}
obstacleAnimations[oi.uniqueID] = animationsCache[animationName];
}
void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges> & obstacles)
{
for(const auto & oi : obstacles)
{
auto & obstacle = oi.data["obstacle"];
if (!obstacle.isStruct())
{
logGlobal->error("I don't know how to animate removal of this obstacle");
continue;
}
auto animation = GH.renderHandler().loadAnimation(AnimationPath::fromJson(obstacle["appearAnimation"]));
animation->preload();
auto first = animation->getImage(0, 0);
if(!first)
continue;
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = getObstaclePosition(first, obstacle);
//AFAIK, in H3 there is no sound of obstacle removal
owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::fromJson(obstacle["appearAnimation"]), whereTo, obstacle["position"].Integer(), 0, true));
obstacleAnimations.erase(oi.id);
//so when multiple obstacles are removed, they show up one after another
owner.waitForAnimations();
}
}
void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & obstacles)
{
for(const auto & oi : obstacles)
{
auto side = owner.getBattle()->playerToSide(owner.curInt->playerID);
if(!oi->visibleForSide(side.value(), owner.getBattle()->battleHasNativeStack(side.value())))
continue;
auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation());
animation->preload();
auto first = animation->getImage(0, 0);
if(!first)
continue;
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = getObstaclePosition(first, *oi);
CCS->soundh->playSound( oi->getAppearSound() );
owner.stacksController->addNewAnim(new EffectAnimation(owner, oi->getAppearAnimation(), whereTo, oi->pos));
//so when multiple obstacles are added, they show up one after another
owner.waitForAnimations();
loadObstacleImage(*oi);
}
}
void BattleObstacleController::showAbsoluteObstacles(Canvas & canvas)
{
//Blit absolute obstacles
for(auto & obstacle : owner.getBattle()->battleGetAllObstacles())
{
if(obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
{
auto img = getObstacleImage(*obstacle);
if(img)
canvas.draw(img, Point(obstacle->getInfo().width, obstacle->getInfo().height));
}
if (obstacle->obstacleType == CObstacleInstance::USUAL)
{
if (obstacle->getInfo().isForegroundObstacle)
continue;
auto img = getObstacleImage(*obstacle);
if(img)
{
Point p = getObstaclePosition(img, *obstacle);
canvas.draw(img, p);
}
}
}
}
void BattleObstacleController::collectRenderableObjects(BattleRenderer & renderer)
{
for (auto obstacle : owner.getBattle()->battleGetAllObstacles())
{
if (obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
continue;
if (obstacle->obstacleType == CObstacleInstance::MOAT)
continue;
if (obstacle->obstacleType == CObstacleInstance::USUAL && !obstacle->getInfo().isForegroundObstacle)
continue;
renderer.insert(EBattleFieldLayer::OBSTACLES, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){
auto img = getObstacleImage(*obstacle);
if(img)
{
Point p = getObstaclePosition(img, *obstacle);
canvas.draw(img, p);
}
});
}
}
void BattleObstacleController::tick(uint32_t msPassed)
{
timePassed += msPassed / 1000.f;
}
std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)
{
int framesCount = timePassed * AnimationControls::getObstaclesSpeed();
std::shared_ptr<CAnimation> animation;
// obstacle is not loaded yet, don't show anything
if (obstacleAnimations.count(oi.uniqueID) == 0)
return nullptr;
animation = obstacleAnimations[oi.uniqueID];
assert(animation);
if(animation)
{
int frameIndex = framesCount % animation->size(0);
return animation->getImage(frameIndex, 0);
}
return nullptr;
}
Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle)
{
int offset = obstacle.getAnimationYOffset(image->height());
Rect r = owner.fieldController->hexPositionLocal(obstacle.pos);
r.y += 42 - image->height() + offset;
return r.topLeft();
}
Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle)
{
auto animationYOffset = obstacle["animationYOffset"].Integer();
auto offset = image->height() % 42;
if(obstacle["needAnimationOffsetFix"].Bool() && offset > 37)
animationYOffset -= 42;
offset += animationYOffset;
Rect r = owner.fieldController->hexPositionLocal(obstacle["position"].Integer());
r.y += 42 - image->height() + offset;
return r.topLeft();
}
/*
* BattleObstacleController.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 "BattleObstacleController.h"
#include "BattleInterface.h"
#include "BattleFieldController.h"
#include "BattleAnimationClasses.h"
#include "BattleStacksController.h"
#include "BattleRenderer.h"
#include "CreatureAnimation.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/IRenderHandler.h"
#include "../../CCallback.h"
#include "../../lib/battle/CObstacleInstance.h"
#include "../../lib/ObstacleHandler.h"
BattleObstacleController::BattleObstacleController(BattleInterface & owner):
owner(owner),
timePassed(0.f)
{
auto obst = owner.getBattle()->battleGetAllObstacles();
for(auto & elem : obst)
{
if ( elem->obstacleType == CObstacleInstance::MOAT )
continue; // handled by siege controller;
loadObstacleImage(*elem);
}
}
void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
{
AnimationPath animationName = oi.getAnimation();
if (animationsCache.count(animationName) == 0)
{
if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
{
// obstacle uses single bitmap image for animations
auto animation = GH.renderHandler().createAnimation();
animation->setCustom(animationName.getName(), 0, 0);
animationsCache[animationName] = animation;
animation->preload();
}
else
{
auto animation = GH.renderHandler().loadAnimation(animationName);
animationsCache[animationName] = animation;
animation->preload();
}
}
obstacleAnimations[oi.uniqueID] = animationsCache[animationName];
}
void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges> & obstacles)
{
for(const auto & oi : obstacles)
{
auto & obstacle = oi.data["obstacle"];
if (!obstacle.isStruct())
{
logGlobal->error("I don't know how to animate removal of this obstacle");
continue;
}
auto animation = GH.renderHandler().loadAnimation(AnimationPath::fromJson(obstacle["appearAnimation"]));
animation->preload();
auto first = animation->getImage(0, 0);
if(!first)
continue;
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = getObstaclePosition(first, obstacle);
//AFAIK, in H3 there is no sound of obstacle removal
owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::fromJson(obstacle["appearAnimation"]), whereTo, obstacle["position"].Integer(), 0, true));
obstacleAnimations.erase(oi.id);
//so when multiple obstacles are removed, they show up one after another
owner.waitForAnimations();
}
}
void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & obstacles)
{
for(const auto & oi : obstacles)
{
auto side = owner.getBattle()->playerToSide(owner.curInt->playerID);
if(!oi->visibleForSide(side.value(), owner.getBattle()->battleHasNativeStack(side.value())))
continue;
auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation());
animation->preload();
auto first = animation->getImage(0, 0);
if(!first)
continue;
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = getObstaclePosition(first, *oi);
CCS->soundh->playSound( oi->getAppearSound() );
owner.stacksController->addNewAnim(new EffectAnimation(owner, oi->getAppearAnimation(), whereTo, oi->pos));
//so when multiple obstacles are added, they show up one after another
owner.waitForAnimations();
loadObstacleImage(*oi);
}
}
void BattleObstacleController::showAbsoluteObstacles(Canvas & canvas)
{
//Blit absolute obstacles
for(auto & obstacle : owner.getBattle()->battleGetAllObstacles())
{
if(obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
{
auto img = getObstacleImage(*obstacle);
if(img)
canvas.draw(img, Point(obstacle->getInfo().width, obstacle->getInfo().height));
}
if (obstacle->obstacleType == CObstacleInstance::USUAL)
{
if (obstacle->getInfo().isForegroundObstacle)
continue;
auto img = getObstacleImage(*obstacle);
if(img)
{
Point p = getObstaclePosition(img, *obstacle);
canvas.draw(img, p);
}
}
}
}
void BattleObstacleController::collectRenderableObjects(BattleRenderer & renderer)
{
for (auto obstacle : owner.getBattle()->battleGetAllObstacles())
{
if (obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
continue;
if (obstacle->obstacleType == CObstacleInstance::MOAT)
continue;
if (obstacle->obstacleType == CObstacleInstance::USUAL && !obstacle->getInfo().isForegroundObstacle)
continue;
renderer.insert(EBattleFieldLayer::OBSTACLES, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){
auto img = getObstacleImage(*obstacle);
if(img)
{
Point p = getObstaclePosition(img, *obstacle);
canvas.draw(img, p);
}
});
}
}
void BattleObstacleController::tick(uint32_t msPassed)
{
timePassed += msPassed / 1000.f;
}
std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)
{
int framesCount = timePassed * AnimationControls::getObstaclesSpeed();
std::shared_ptr<CAnimation> animation;
// obstacle is not loaded yet, don't show anything
if (obstacleAnimations.count(oi.uniqueID) == 0)
return nullptr;
animation = obstacleAnimations[oi.uniqueID];
assert(animation);
if(animation)
{
int frameIndex = framesCount % animation->size(0);
return animation->getImage(frameIndex, 0);
}
return nullptr;
}
Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle)
{
int offset = obstacle.getAnimationYOffset(image->height());
Rect r = owner.fieldController->hexPositionLocal(obstacle.pos);
r.y += 42 - image->height() + offset;
return r.topLeft();
}
Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle)
{
auto animationYOffset = obstacle["animationYOffset"].Integer();
auto offset = image->height() % 42;
if(obstacle["needAnimationOffsetFix"].Bool() && offset > 37)
animationYOffset -= 42;
offset += animationYOffset;
Rect r = owner.fieldController->hexPositionLocal(obstacle["position"].Integer());
r.y += 42 - image->height() + offset;
return r.topLeft();
}

View File

@ -1,68 +1,68 @@
/*
* BattleObstacleController.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 "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
struct CObstacleInstance;
class JsonNode;
class ObstacleChanges;
class Point;
VCMI_LIB_NAMESPACE_END
class IImage;
class Canvas;
class CAnimation;
class BattleInterface;
class BattleRenderer;
/// Controls all currently active projectiles on the battlefield
/// (with exception of moat, which is apparently handled by siege controller)
class BattleObstacleController
{
BattleInterface & owner;
/// total time, in seconds, since start of battle. Used for animating obstacles
float timePassed;
/// cached animations of all obstacles in current battle
std::map<AnimationPath, std::shared_ptr<CAnimation>> animationsCache;
/// list of all obstacles that are currently being rendered
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
void loadObstacleImage(const CObstacleInstance & oi);
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
Point getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle);
public:
BattleObstacleController(BattleInterface & owner);
/// called every frame
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);
/// call-in from network pack, remove required obstacles with any required animations
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
/// renders all "absolute" obstacles
void showAbsoluteObstacles(Canvas & canvas);
/// adds all non-"absolute" visible obstacles for rendering
void collectRenderableObjects(BattleRenderer & renderer);
};
/*
* BattleObstacleController.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 "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
struct CObstacleInstance;
class JsonNode;
class ObstacleChanges;
class Point;
VCMI_LIB_NAMESPACE_END
class IImage;
class Canvas;
class CAnimation;
class BattleInterface;
class BattleRenderer;
/// Controls all currently active projectiles on the battlefield
/// (with exception of moat, which is apparently handled by siege controller)
class BattleObstacleController
{
BattleInterface & owner;
/// total time, in seconds, since start of battle. Used for animating obstacles
float timePassed;
/// cached animations of all obstacles in current battle
std::map<AnimationPath, std::shared_ptr<CAnimation>> animationsCache;
/// list of all obstacles that are currently being rendered
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
void loadObstacleImage(const CObstacleInstance & oi);
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
Point getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle);
public:
BattleObstacleController(BattleInterface & owner);
/// called every frame
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);
/// call-in from network pack, remove required obstacles with any required animations
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
/// renders all "absolute" obstacles
void showAbsoluteObstacles(Canvas & canvas);
/// adds all non-"absolute" visible obstacles for rendering
void collectRenderableObjects(BattleRenderer & renderer);
};

View File

@ -1,386 +1,386 @@
/*
* BattleProjectileController.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 "BattleProjectileController.h"
#include "BattleInterface.h"
#include "BattleSiegeController.h"
#include "BattleStacksController.h"
#include "CreatureAnimation.h"
#include "../render/Canvas.h"
#include "../render/IRenderHandler.h"
#include "../gui/CGuiHandler.h"
#include "../CGameInfo.h"
#include "../../lib/CStack.h"
#include "../../lib/mapObjects/CGTownInstance.h"
static double calculateCatapultParabolaY(const Point & from, const Point & dest, int x)
{
double facA = 0.005; // seems to be constant
// system of 2 linear equations, solutions of which are missing coefficients
// for quadratic equation a*x*x + b*x + c
double eq[2][3] = {
{ static_cast<double>(from.x), 1.0, from.y - facA*from.x*from.x },
{ static_cast<double>(dest.x), 1.0, dest.y - facA*dest.x*dest.x }
};
// solve system via determinants
double det = eq[0][0] *eq[1][1] - eq[1][0] *eq[0][1];
double detB = eq[0][2] *eq[1][1] - eq[1][2] *eq[0][1];
double detC = eq[0][0] *eq[1][2] - eq[1][0] *eq[0][2];
double facB = detB / det;
double facC = detC / det;
return facA *pow(x, 2.0) + facB *x + facC;
}
void ProjectileMissile::show(Canvas & canvas)
{
size_t group = reverse ? 1 : 0;
auto image = animation->getImage(frameNum, group, true);
if(image)
{
Point pos {
vstd::lerp(from.x, dest.x, progress) - image->width() / 2,
vstd::lerp(from.y, dest.y, progress) - image->height() / 2,
};
canvas.draw(image, pos);
}
}
void ProjectileMissile::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
void ProjectileAnimatedMissile::tick(uint32_t msPassed)
{
ProjectileMissile::tick(msPassed);
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
size_t animationSize = animation->size(reverse ? 1 : 0);
while (frameProgress > animationSize)
frameProgress -= animationSize;
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)
{
int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0);
auto image = animation->getImage(frameIndex, 0, true);
if(image)
{
int posX = vstd::lerp(from.x, dest.x, progress);
int posY = calculateCatapultParabolaY(from, dest, posX);
Point pos(posX, posY);
canvas.draw(image, pos);
}
}
void ProjectileRay::show(Canvas & canvas)
{
Point curr {
vstd::lerp(from.x, dest.x, progress),
vstd::lerp(from.y, dest.y, progress),
};
Point length = curr - from;
//select axis to draw ray on, we want angle to be less than 45 degrees so individual sub-rays won't overlap each other
if (std::abs(length.x) > std::abs(length.y)) // draw in horizontal axis
{
int y1 = from.y - rayConfig.size() / 2;
int y2 = curr.y - rayConfig.size() / 2;
int x1 = from.x;
int x2 = curr.x;
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
canvas.drawLine(Point(x1, y1 + i), Point(x2, y2+i), ray.start, ray.end);
}
}
else // draw in vertical axis
{
int x1 = from.x - rayConfig.size() / 2;
int x2 = curr.x - rayConfig.size() / 2;
int y1 = from.y;
int y2 = curr.y;
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
}
}
}
void ProjectileRay::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
BattleProjectileController::BattleProjectileController(BattleInterface & owner):
owner(owner)
{}
const CCreature & BattleProjectileController::getShooter(const CStack * stack) const
{
const CCreature * creature = stack->unitType();
if(creature->getId() == CreatureID::ARROW_TOWERS)
creature = owner.siegeController->getTurretCreature();
if(creature->animation.missleFrameAngles.empty())
{
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated());
creature = CGI->creh->objects[CreatureID::ARCHER];
}
return *creature;
}
bool BattleProjectileController::stackUsesRayProjectile(const CStack * stack) const
{
return !getShooter(stack).animation.projectileRay.empty();
}
bool BattleProjectileController::stackUsesMissileProjectile(const CStack * stack) const
{
return !getShooter(stack).animation.projectileImageName.empty();
}
void BattleProjectileController::initStackProjectile(const CStack * stack)
{
if (!stackUsesMissileProjectile(stack))
return;
const CCreature & creature = getShooter(stack);
projectilesCache[creature.animation.projectileImageName] = createProjectileImage(creature.animation.projectileImageName);
}
std::shared_ptr<CAnimation> BattleProjectileController::createProjectileImage(const AnimationPath & path )
{
std::shared_ptr<CAnimation> projectile = GH.renderHandler().loadAnimation(path);
projectile->preload();
if(projectile->size(1) != 0)
logAnim->error("Expected empty group 1 in stack projectile");
else
projectile->createFlippedGroup(0, 1);
return projectile;
}
std::shared_ptr<CAnimation> BattleProjectileController::getProjectileImage(const CStack * stack)
{
const CCreature & creature = getShooter(stack);
AnimationPath imageName = creature.animation.projectileImageName;
if (!projectilesCache.count(imageName))
initStackProjectile(stack);
return projectilesCache[imageName];
}
void BattleProjectileController::emitStackProjectile(const CStack * stack)
{
int stackID = stack ? stack->unitId() : -1;
for (auto projectile : projectiles)
{
if ( !projectile->playing && projectile->shooterID == stackID)
{
projectile->playing = true;
return;
}
}
}
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;
});
}
bool BattleProjectileController::hasActiveProjectile(const CStack * stack, bool emittedOnly) const
{
int stackID = stack ? stack->unitId() : -1;
for(auto const & instance : projectiles)
{
if(instance->shooterID == stackID && (instance->playing || !emittedOnly))
{
return true;
}
}
return false;
}
float BattleProjectileController::computeProjectileFlightTime( Point from, Point dest, double animSpeed)
{
float distanceSquared = (dest.x - from.x) * (dest.x - from.x) + (dest.y - from.y) * (dest.y - from.y);
float distance = sqrt(distanceSquared);
assert(distance > 1.f);
return animSpeed / std::max( 1.f, distance);
}
int BattleProjectileController::computeProjectileFrameID( Point from, Point dest, const CStack * stack)
{
const CCreature & creature = getShooter(stack);
auto & angles = creature.animation.missleFrameAngles;
auto animation = getProjectileImage(stack);
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
size_t maxFrame = std::min<size_t>(angles.size(), animation->size(0));
assert(maxFrame > 0);
double projectileAngle = -atan2(dest.y - from.y, std::abs(dest.x - from.x));
// values in angles array indicate position from which this frame was rendered, in degrees.
// possible range is 90 ... -90, where projectile for +90 will be used for shooting upwards, +0 for shots towards right and -90 for downwards shots
// find frame that has closest angle to one that we need for this shot
int bestID = 0;
double bestDiff = fabs( angles[0] / 180 * M_PI - projectileAngle );
for (int i=1; i<maxFrame; i++)
{
double currentDiff = fabs( angles[i] / 180 * M_PI - projectileAngle );
if (currentDiff < bestDiff)
{
bestID = i;
bestDiff = currentDiff;
}
}
return bestID;
}
void BattleProjectileController::createCatapultProjectile(const CStack * shooter, Point from, Point dest)
{
auto catapultProjectile = new ProjectileCatapult();
catapultProjectile->animation = getProjectileImage(shooter);
catapultProjectile->progress = 0;
catapultProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getCatapultSpeed());
catapultProjectile->from = from;
catapultProjectile->dest = dest;
catapultProjectile->shooterID = shooter->unitId();
catapultProjectile->playing = false;
catapultProjectile->frameProgress = 0.f;
projectiles.push_back(std::shared_ptr<ProjectileBase>(catapultProjectile));
}
void BattleProjectileController::createProjectile(const CStack * shooter, Point from, Point dest)
{
const CCreature & shooterInfo = getShooter(shooter);
std::shared_ptr<ProjectileBase> projectile;
if (stackUsesRayProjectile(shooter) && stackUsesMissileProjectile(shooter))
{
logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.getNameSingularTranslated());
}
if (stackUsesRayProjectile(shooter))
{
auto rayProjectile = new ProjectileRay();
projectile.reset(rayProjectile);
rayProjectile->rayConfig = shooterInfo.animation.projectileRay;
rayProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getRayProjectileSpeed());
}
else if (stackUsesMissileProjectile(shooter))
{
auto missileProjectile = new ProjectileMissile();
projectile.reset(missileProjectile);
missileProjectile->animation = getProjectileImage(shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
}
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter->unitId();
projectile->progress = 0;
projectile->playing = false;
projectiles.push_back(projectile);
}
void BattleProjectileController::createSpellProjectile(const CStack * shooter, Point from, Point dest, const CSpell * spell)
{
double projectileAngle = std::abs(atan2(dest.x - from.x, dest.y - from.y));
AnimationPath animToDisplay = spell->animationInfo.selectProjectile(projectileAngle);
assert(!animToDisplay.empty());
if(!animToDisplay.empty())
{
auto projectile = new ProjectileAnimatedMissile();
projectile->animation = createProjectileImage(animToDisplay);
projectile->frameProgress = 0;
projectile->frameNum = 0;
projectile->reverse = from.x > dest.x;
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter ? shooter->unitId() : -1;
projectile->progress = 0;
projectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
projectile->playing = false;
projectiles.push_back(std::shared_ptr<ProjectileBase>(projectile));
}
}
/*
* BattleProjectileController.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 "BattleProjectileController.h"
#include "BattleInterface.h"
#include "BattleSiegeController.h"
#include "BattleStacksController.h"
#include "CreatureAnimation.h"
#include "../render/Canvas.h"
#include "../render/IRenderHandler.h"
#include "../gui/CGuiHandler.h"
#include "../CGameInfo.h"
#include "../../lib/CStack.h"
#include "../../lib/mapObjects/CGTownInstance.h"
static double calculateCatapultParabolaY(const Point & from, const Point & dest, int x)
{
double facA = 0.005; // seems to be constant
// system of 2 linear equations, solutions of which are missing coefficients
// for quadratic equation a*x*x + b*x + c
double eq[2][3] = {
{ static_cast<double>(from.x), 1.0, from.y - facA*from.x*from.x },
{ static_cast<double>(dest.x), 1.0, dest.y - facA*dest.x*dest.x }
};
// solve system via determinants
double det = eq[0][0] *eq[1][1] - eq[1][0] *eq[0][1];
double detB = eq[0][2] *eq[1][1] - eq[1][2] *eq[0][1];
double detC = eq[0][0] *eq[1][2] - eq[1][0] *eq[0][2];
double facB = detB / det;
double facC = detC / det;
return facA *pow(x, 2.0) + facB *x + facC;
}
void ProjectileMissile::show(Canvas & canvas)
{
size_t group = reverse ? 1 : 0;
auto image = animation->getImage(frameNum, group, true);
if(image)
{
Point pos {
vstd::lerp(from.x, dest.x, progress) - image->width() / 2,
vstd::lerp(from.y, dest.y, progress) - image->height() / 2,
};
canvas.draw(image, pos);
}
}
void ProjectileMissile::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
void ProjectileAnimatedMissile::tick(uint32_t msPassed)
{
ProjectileMissile::tick(msPassed);
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
size_t animationSize = animation->size(reverse ? 1 : 0);
while (frameProgress > animationSize)
frameProgress -= animationSize;
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)
{
int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0);
auto image = animation->getImage(frameIndex, 0, true);
if(image)
{
int posX = vstd::lerp(from.x, dest.x, progress);
int posY = calculateCatapultParabolaY(from, dest, posX);
Point pos(posX, posY);
canvas.draw(image, pos);
}
}
void ProjectileRay::show(Canvas & canvas)
{
Point curr {
vstd::lerp(from.x, dest.x, progress),
vstd::lerp(from.y, dest.y, progress),
};
Point length = curr - from;
//select axis to draw ray on, we want angle to be less than 45 degrees so individual sub-rays won't overlap each other
if (std::abs(length.x) > std::abs(length.y)) // draw in horizontal axis
{
int y1 = from.y - rayConfig.size() / 2;
int y2 = curr.y - rayConfig.size() / 2;
int x1 = from.x;
int x2 = curr.x;
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
canvas.drawLine(Point(x1, y1 + i), Point(x2, y2+i), ray.start, ray.end);
}
}
else // draw in vertical axis
{
int x1 = from.x - rayConfig.size() / 2;
int x2 = curr.x - rayConfig.size() / 2;
int y1 = from.y;
int y2 = curr.y;
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
}
}
}
void ProjectileRay::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
BattleProjectileController::BattleProjectileController(BattleInterface & owner):
owner(owner)
{}
const CCreature & BattleProjectileController::getShooter(const CStack * stack) const
{
const CCreature * creature = stack->unitType();
if(creature->getId() == CreatureID::ARROW_TOWERS)
creature = owner.siegeController->getTurretCreature();
if(creature->animation.missleFrameAngles.empty())
{
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated());
creature = CGI->creh->objects[CreatureID::ARCHER];
}
return *creature;
}
bool BattleProjectileController::stackUsesRayProjectile(const CStack * stack) const
{
return !getShooter(stack).animation.projectileRay.empty();
}
bool BattleProjectileController::stackUsesMissileProjectile(const CStack * stack) const
{
return !getShooter(stack).animation.projectileImageName.empty();
}
void BattleProjectileController::initStackProjectile(const CStack * stack)
{
if (!stackUsesMissileProjectile(stack))
return;
const CCreature & creature = getShooter(stack);
projectilesCache[creature.animation.projectileImageName] = createProjectileImage(creature.animation.projectileImageName);
}
std::shared_ptr<CAnimation> BattleProjectileController::createProjectileImage(const AnimationPath & path )
{
std::shared_ptr<CAnimation> projectile = GH.renderHandler().loadAnimation(path);
projectile->preload();
if(projectile->size(1) != 0)
logAnim->error("Expected empty group 1 in stack projectile");
else
projectile->createFlippedGroup(0, 1);
return projectile;
}
std::shared_ptr<CAnimation> BattleProjectileController::getProjectileImage(const CStack * stack)
{
const CCreature & creature = getShooter(stack);
AnimationPath imageName = creature.animation.projectileImageName;
if (!projectilesCache.count(imageName))
initStackProjectile(stack);
return projectilesCache[imageName];
}
void BattleProjectileController::emitStackProjectile(const CStack * stack)
{
int stackID = stack ? stack->unitId() : -1;
for (auto projectile : projectiles)
{
if ( !projectile->playing && projectile->shooterID == stackID)
{
projectile->playing = true;
return;
}
}
}
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;
});
}
bool BattleProjectileController::hasActiveProjectile(const CStack * stack, bool emittedOnly) const
{
int stackID = stack ? stack->unitId() : -1;
for(auto const & instance : projectiles)
{
if(instance->shooterID == stackID && (instance->playing || !emittedOnly))
{
return true;
}
}
return false;
}
float BattleProjectileController::computeProjectileFlightTime( Point from, Point dest, double animSpeed)
{
float distanceSquared = (dest.x - from.x) * (dest.x - from.x) + (dest.y - from.y) * (dest.y - from.y);
float distance = sqrt(distanceSquared);
assert(distance > 1.f);
return animSpeed / std::max( 1.f, distance);
}
int BattleProjectileController::computeProjectileFrameID( Point from, Point dest, const CStack * stack)
{
const CCreature & creature = getShooter(stack);
auto & angles = creature.animation.missleFrameAngles;
auto animation = getProjectileImage(stack);
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
size_t maxFrame = std::min<size_t>(angles.size(), animation->size(0));
assert(maxFrame > 0);
double projectileAngle = -atan2(dest.y - from.y, std::abs(dest.x - from.x));
// values in angles array indicate position from which this frame was rendered, in degrees.
// possible range is 90 ... -90, where projectile for +90 will be used for shooting upwards, +0 for shots towards right and -90 for downwards shots
// find frame that has closest angle to one that we need for this shot
int bestID = 0;
double bestDiff = fabs( angles[0] / 180 * M_PI - projectileAngle );
for (int i=1; i<maxFrame; i++)
{
double currentDiff = fabs( angles[i] / 180 * M_PI - projectileAngle );
if (currentDiff < bestDiff)
{
bestID = i;
bestDiff = currentDiff;
}
}
return bestID;
}
void BattleProjectileController::createCatapultProjectile(const CStack * shooter, Point from, Point dest)
{
auto catapultProjectile = new ProjectileCatapult();
catapultProjectile->animation = getProjectileImage(shooter);
catapultProjectile->progress = 0;
catapultProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getCatapultSpeed());
catapultProjectile->from = from;
catapultProjectile->dest = dest;
catapultProjectile->shooterID = shooter->unitId();
catapultProjectile->playing = false;
catapultProjectile->frameProgress = 0.f;
projectiles.push_back(std::shared_ptr<ProjectileBase>(catapultProjectile));
}
void BattleProjectileController::createProjectile(const CStack * shooter, Point from, Point dest)
{
const CCreature & shooterInfo = getShooter(shooter);
std::shared_ptr<ProjectileBase> projectile;
if (stackUsesRayProjectile(shooter) && stackUsesMissileProjectile(shooter))
{
logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.getNameSingularTranslated());
}
if (stackUsesRayProjectile(shooter))
{
auto rayProjectile = new ProjectileRay();
projectile.reset(rayProjectile);
rayProjectile->rayConfig = shooterInfo.animation.projectileRay;
rayProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getRayProjectileSpeed());
}
else if (stackUsesMissileProjectile(shooter))
{
auto missileProjectile = new ProjectileMissile();
projectile.reset(missileProjectile);
missileProjectile->animation = getProjectileImage(shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
}
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter->unitId();
projectile->progress = 0;
projectile->playing = false;
projectiles.push_back(projectile);
}
void BattleProjectileController::createSpellProjectile(const CStack * shooter, Point from, Point dest, const CSpell * spell)
{
double projectileAngle = std::abs(atan2(dest.x - from.x, dest.y - from.y));
AnimationPath animToDisplay = spell->animationInfo.selectProjectile(projectileAngle);
assert(!animToDisplay.empty());
if(!animToDisplay.empty())
{
auto projectile = new ProjectileAnimatedMissile();
projectile->animation = createProjectileImage(animToDisplay);
projectile->frameProgress = 0;
projectile->frameNum = 0;
projectile->reverse = from.x > dest.x;
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter ? shooter->unitId() : -1;
projectile->progress = 0;
projectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
projectile->playing = false;
projectiles.push_back(std::shared_ptr<ProjectileBase>(projectile));
}
}

View File

@ -1,125 +1,125 @@
/*
* BattleSiegeController.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 "../../lib/CCreatureHandler.h"
#include "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CSpell;
VCMI_LIB_NAMESPACE_END
class CAnimation;
class Canvas;
class BattleInterface;
/// Base class for projectiles
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
float progress; // current position of projectile on from->dest line
float speed; // how much progress is gained per second
int shooterID; // ID of shooter stack
bool playing; // if set to true, projectile animation is playing, e.g. flying to target
};
/// Projectile for most shooters - render pre-selected frame moving in straight line from origin to destination
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
bool reverse; // if true, projectile will be flipped by vertical axis
};
/// Projectile for spell - render animation moving in straight line from origin to destination
struct ProjectileAnimatedMissile : ProjectileMissile
{
void tick(uint32_t msPassed) override;
float frameProgress;
};
/// Projectile for catapult - render spinning projectile moving at parabolic trajectory to its destination
struct ProjectileCatapult : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation;
float frameProgress;
};
/// Projectile for mages/evil eye - render ray expanding from origin position to destination
struct ProjectileRay : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
};
/// Class that manages all ongoing projectiles in the game
/// ... even though in H3 only 1 projectile can be on screen at any point of time
class BattleProjectileController
{
BattleInterface & owner;
/// all projectiles loaded during current battle
std::map<AnimationPath, std::shared_ptr<CAnimation>> projectilesCache;
/// projectiles currently flying on battlefield
std::vector<std::shared_ptr<ProjectileBase>> projectiles;
std::shared_ptr<CAnimation> getProjectileImage(const CStack * stack);
std::shared_ptr<CAnimation> createProjectileImage(const AnimationPath & path );
void initStackProjectile(const CStack * stack);
bool stackUsesRayProjectile(const CStack * stack) const;
bool stackUsesMissileProjectile(const CStack * stack) const;
void showProjectile(Canvas & canvas, std::shared_ptr<ProjectileBase> projectile);
const CCreature & getShooter(const CStack * stack) const;
int computeProjectileFrameID( Point from, Point dest, const CStack * stack);
float computeProjectileFlightTime( Point from, Point dest, double speed);
public:
BattleProjectileController(BattleInterface & owner);
/// renders all currently active projectiles
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;
/// starts rendering previously created projectile
void emitStackProjectile(const CStack * stack);
/// creates (but not emits!) projectile and initializes it based on parameters
void createProjectile(const CStack * shooter, Point from, Point dest);
void createSpellProjectile(const CStack * shooter, Point from, Point dest, const CSpell * spell);
void createCatapultProjectile(const CStack * shooter, Point from, Point dest);
};
/*
* BattleSiegeController.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 "../../lib/CCreatureHandler.h"
#include "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CSpell;
VCMI_LIB_NAMESPACE_END
class CAnimation;
class Canvas;
class BattleInterface;
/// Base class for projectiles
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
float progress; // current position of projectile on from->dest line
float speed; // how much progress is gained per second
int shooterID; // ID of shooter stack
bool playing; // if set to true, projectile animation is playing, e.g. flying to target
};
/// Projectile for most shooters - render pre-selected frame moving in straight line from origin to destination
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
bool reverse; // if true, projectile will be flipped by vertical axis
};
/// Projectile for spell - render animation moving in straight line from origin to destination
struct ProjectileAnimatedMissile : ProjectileMissile
{
void tick(uint32_t msPassed) override;
float frameProgress;
};
/// Projectile for catapult - render spinning projectile moving at parabolic trajectory to its destination
struct ProjectileCatapult : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation;
float frameProgress;
};
/// Projectile for mages/evil eye - render ray expanding from origin position to destination
struct ProjectileRay : ProjectileBase
{
void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
};
/// Class that manages all ongoing projectiles in the game
/// ... even though in H3 only 1 projectile can be on screen at any point of time
class BattleProjectileController
{
BattleInterface & owner;
/// all projectiles loaded during current battle
std::map<AnimationPath, std::shared_ptr<CAnimation>> projectilesCache;
/// projectiles currently flying on battlefield
std::vector<std::shared_ptr<ProjectileBase>> projectiles;
std::shared_ptr<CAnimation> getProjectileImage(const CStack * stack);
std::shared_ptr<CAnimation> createProjectileImage(const AnimationPath & path );
void initStackProjectile(const CStack * stack);
bool stackUsesRayProjectile(const CStack * stack) const;
bool stackUsesMissileProjectile(const CStack * stack) const;
void showProjectile(Canvas & canvas, std::shared_ptr<ProjectileBase> projectile);
const CCreature & getShooter(const CStack * stack) const;
int computeProjectileFrameID( Point from, Point dest, const CStack * stack);
float computeProjectileFlightTime( Point from, Point dest, double speed);
public:
BattleProjectileController(BattleInterface & owner);
/// renders all currently active projectiles
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;
/// starts rendering previously created projectile
void emitStackProjectile(const CStack * stack);
/// creates (but not emits!) projectile and initializes it based on parameters
void createProjectile(const CStack * shooter, Point from, Point dest);
void createSpellProjectile(const CStack * shooter, Point from, Point dest, const CSpell * spell);
void createCatapultProjectile(const CStack * shooter, Point from, Point dest);
};

View File

@ -1,76 +1,76 @@
/*
* BattleFieldController.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 "BattleRenderer.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleEffectsController.h"
#include "BattleWindow.h"
#include "BattleSiegeController.h"
#include "BattleStacksController.h"
#include "BattleObstacleController.h"
void BattleRenderer::collectObjects()
{
owner.effectsController->collectRenderableObjects(*this);
owner.obstacleController->collectRenderableObjects(*this);
owner.stacksController->collectRenderableObjects(*this);
if (owner.siegeController)
owner.siegeController->collectRenderableObjects(*this);
if (owner.defendingHero)
owner.defendingHero->collectRenderableObjects(*this);
if (owner.attackingHero)
owner.attackingHero->collectRenderableObjects(*this);
}
void BattleRenderer::sortObjects()
{
auto getRow = [](const RenderableInstance & object) -> int
{
if (object.tile.isValid())
return object.tile.getY();
if ( object.tile == BattleHex::HEX_BEFORE_ALL )
return -1;
assert( object.tile == BattleHex::HEX_AFTER_ALL || object.tile == BattleHex::INVALID);
return GameConstants::BFIELD_HEIGHT;
};
std::stable_sort(objects.begin(), objects.end(), [&](const RenderableInstance & left, const RenderableInstance & right){
if ( getRow(left) != getRow(right))
return getRow(left) < getRow(right);
return left.layer < right.layer;
});
}
void BattleRenderer::renderObjects(BattleRenderer::RendererRef targetCanvas)
{
for (auto const & object : objects)
object.functor(targetCanvas);
}
BattleRenderer::BattleRenderer(BattleInterface & owner):
owner(owner)
{
}
void BattleRenderer::insert(EBattleFieldLayer layer, BattleHex tile, BattleRenderer::RenderFunctor functor)
{
objects.push_back({functor, layer, tile});
}
void BattleRenderer::execute(BattleRenderer::RendererRef targetCanvas)
{
collectObjects();
sortObjects();
renderObjects(targetCanvas);
}
/*
* BattleFieldController.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 "BattleRenderer.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleEffectsController.h"
#include "BattleWindow.h"
#include "BattleSiegeController.h"
#include "BattleStacksController.h"
#include "BattleObstacleController.h"
void BattleRenderer::collectObjects()
{
owner.effectsController->collectRenderableObjects(*this);
owner.obstacleController->collectRenderableObjects(*this);
owner.stacksController->collectRenderableObjects(*this);
if (owner.siegeController)
owner.siegeController->collectRenderableObjects(*this);
if (owner.defendingHero)
owner.defendingHero->collectRenderableObjects(*this);
if (owner.attackingHero)
owner.attackingHero->collectRenderableObjects(*this);
}
void BattleRenderer::sortObjects()
{
auto getRow = [](const RenderableInstance & object) -> int
{
if (object.tile.isValid())
return object.tile.getY();
if ( object.tile == BattleHex::HEX_BEFORE_ALL )
return -1;
assert( object.tile == BattleHex::HEX_AFTER_ALL || object.tile == BattleHex::INVALID);
return GameConstants::BFIELD_HEIGHT;
};
std::stable_sort(objects.begin(), objects.end(), [&](const RenderableInstance & left, const RenderableInstance & right){
if ( getRow(left) != getRow(right))
return getRow(left) < getRow(right);
return left.layer < right.layer;
});
}
void BattleRenderer::renderObjects(BattleRenderer::RendererRef targetCanvas)
{
for (auto const & object : objects)
object.functor(targetCanvas);
}
BattleRenderer::BattleRenderer(BattleInterface & owner):
owner(owner)
{
}
void BattleRenderer::insert(EBattleFieldLayer layer, BattleHex tile, BattleRenderer::RenderFunctor functor)
{
objects.push_back({functor, layer, tile});
}
void BattleRenderer::execute(BattleRenderer::RendererRef targetCanvas)
{
collectObjects();
sortObjects();
renderObjects(targetCanvas);
}

View File

@ -1,53 +1,53 @@
/*
* BattleFieldController.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 "../../lib/battle/BattleHex.h"
class Canvas;
class BattleInterface;
enum class EBattleFieldLayer {
// confirmed ordering requirements:
CORPSES = 0,
WALLS = 1,
HEROES = 2,
STACKS = 2, // after corpses, obstacles, walls
OBSTACLES = 3, // after stacks
STACK_AMOUNTS = 3, // after stacks, obstacles, corpses
EFFECTS = 4, // after obstacles, battlements
};
class BattleRenderer
{
public:
using RendererRef = Canvas &;
using RenderFunctor = std::function<void(RendererRef)>;
private:
BattleInterface & owner;
struct RenderableInstance
{
RenderFunctor functor;
EBattleFieldLayer layer;
BattleHex tile;
};
std::vector<RenderableInstance> objects;
void collectObjects();
void sortObjects();
void renderObjects(RendererRef targetCanvas);
public:
BattleRenderer(BattleInterface & owner);
void insert(EBattleFieldLayer layer, BattleHex tile, RenderFunctor functor);
void execute(RendererRef targetCanvas);
};
/*
* BattleFieldController.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 "../../lib/battle/BattleHex.h"
class Canvas;
class BattleInterface;
enum class EBattleFieldLayer {
// confirmed ordering requirements:
CORPSES = 0,
WALLS = 1,
HEROES = 2,
STACKS = 2, // after corpses, obstacles, walls
OBSTACLES = 3, // after stacks
STACK_AMOUNTS = 3, // after stacks, obstacles, corpses
EFFECTS = 4, // after obstacles, battlements
};
class BattleRenderer
{
public:
using RendererRef = Canvas &;
using RenderFunctor = std::function<void(RendererRef)>;
private:
BattleInterface & owner;
struct RenderableInstance
{
RenderFunctor functor;
EBattleFieldLayer layer;
BattleHex tile;
};
std::vector<RenderableInstance> objects;
void collectObjects();
void sortObjects();
void renderObjects(RendererRef targetCanvas);
public:
BattleRenderer(BattleInterface & owner);
void insert(EBattleFieldLayer layer, BattleHex tile, RenderFunctor functor);
void execute(RendererRef targetCanvas);
};

View File

@ -1,367 +1,367 @@
/*
* BattleSiegeController.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 "BattleSiegeController.h"
#include "BattleAnimationClasses.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleStacksController.h"
#include "BattleFieldController.h"
#include "BattleRenderer.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../render/IRenderHandler.h"
#include "../../CCallback.h"
#include "../../lib/NetPacks.h"
#include "../../lib/CStack.h"
#include "../../lib/mapObjects/CGTownInstance.h"
ImagePath BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const
{
auto getImageIndex = [&]() -> int
{
bool isTower = (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER);
switch (state)
{
case EWallState::REINFORCED :
return 1;
case EWallState::INTACT :
if (town->hasBuilt(BuildingID::CASTLE))
return 2; // reinforced walls were damaged
else
return 1;
case EWallState::DAMAGED :
// towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
if (isTower)
return 1;
else
return 2;
case EWallState::DESTROYED :
if (isTower)
return 2;
else
return 3;
}
return 1;
};
const std::string & prefix = town->town->clientInfo.siegePrefix;
std::string addit = std::to_string(getImageIndex());
switch(what)
{
case EWallVisual::BACKGROUND_WALL:
{
auto faction = town->town->faction->getIndex();
if (faction == ETownType::RAMPART || faction == ETownType::NECROPOLIS || faction == ETownType::DUNGEON || faction == ETownType::STRONGHOLD)
return ImagePath::builtinTODO(prefix + "TPW1.BMP");
else
return ImagePath::builtinTODO(prefix + "TPWL.BMP");
}
case EWallVisual::KEEP:
return ImagePath::builtinTODO(prefix + "MAN" + addit + ".BMP");
case EWallVisual::BOTTOM_TOWER:
return ImagePath::builtinTODO(prefix + "TW1" + addit + ".BMP");
case EWallVisual::BOTTOM_WALL:
return ImagePath::builtinTODO(prefix + "WA1" + addit + ".BMP");
case EWallVisual::WALL_BELLOW_GATE:
return ImagePath::builtinTODO(prefix + "WA3" + addit + ".BMP");
case EWallVisual::WALL_OVER_GATE:
return ImagePath::builtinTODO(prefix + "WA4" + addit + ".BMP");
case EWallVisual::UPPER_WALL:
return ImagePath::builtinTODO(prefix + "WA6" + addit + ".BMP");
case EWallVisual::UPPER_TOWER:
return ImagePath::builtinTODO(prefix + "TW2" + addit + ".BMP");
case EWallVisual::GATE:
return ImagePath::builtinTODO(prefix + "DRW" + addit + ".BMP");
case EWallVisual::GATE_ARCH:
return ImagePath::builtinTODO(prefix + "ARCH.BMP");
case EWallVisual::BOTTOM_STATIC_WALL:
return ImagePath::builtinTODO(prefix + "WA2.BMP");
case EWallVisual::UPPER_STATIC_WALL:
return ImagePath::builtinTODO(prefix + "WA5.BMP");
case EWallVisual::MOAT:
return ImagePath::builtinTODO(prefix + "MOAT.BMP");
case EWallVisual::MOAT_BANK:
return ImagePath::builtinTODO(prefix + "MLIP.BMP");
case EWallVisual::KEEP_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "MANC.BMP");
case EWallVisual::BOTTOM_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "TW1C.BMP");
case EWallVisual::UPPER_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "TW2C.BMP");
default:
return ImagePath();
}
}
void BattleSiegeController::showWallPiece(Canvas & canvas, EWallVisual::EWallVisual what)
{
auto & ci = town->town->clientInfo;
auto const & pos = ci.siegePositions[what];
if ( wallPieceImages[what] && pos.isValid())
canvas.draw(wallPieceImages[what], Point(pos.x, pos.y));
}
ImagePath BattleSiegeController::getBattleBackgroundName() const
{
const std::string & prefix = town->town->clientInfo.siegePrefix;
return ImagePath::builtinTODO(prefix + "BACK.BMP");
}
bool BattleSiegeController::getWallPieceExistance(EWallVisual::EWallVisual what) const
{
//FIXME: use this instead of buildings test?
//ui8 siegeLevel = owner.curInt->cb->battleGetSiegeLevel();
switch (what)
{
case EWallVisual::MOAT: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
case EWallVisual::MOAT_BANK: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
case EWallVisual::KEEP_BATTLEMENT: return town->hasBuilt(BuildingID::CITADEL) && owner.getBattle()->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
case EWallVisual::UPPER_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
default: return true;
}
}
BattleHex BattleSiegeController::getWallPiecePosition(EWallVisual::EWallVisual what) const
{
static const std::array<BattleHex, 18> wallsPositions = {
BattleHex::INVALID, // BACKGROUND, // handled separately
BattleHex::HEX_BEFORE_ALL, // BACKGROUND_WALL,
135, // KEEP,
BattleHex::HEX_AFTER_ALL, // BOTTOM_TOWER,
182, // BOTTOM_WALL,
130, // WALL_BELLOW_GATE,
62, // WALL_OVER_GATE,
12, // UPPER_WALL,
BattleHex::HEX_BEFORE_ALL, // UPPER_TOWER,
BattleHex::HEX_BEFORE_ALL, // GATE, // 94
112, // GATE_ARCH,
165, // BOTTOM_STATIC_WALL,
45, // UPPER_STATIC_WALL,
BattleHex::INVALID, // MOAT, // printed as absolute obstacle
BattleHex::INVALID, // MOAT_BANK, // printed as absolute obstacle
135, // KEEP_BATTLEMENT,
BattleHex::HEX_AFTER_ALL, // BOTTOM_BATTLEMENT,
BattleHex::HEX_BEFORE_ALL, // UPPER_BATTLEMENT,
};
return wallsPositions[what];
}
BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTownInstance *siegeTown):
owner(owner),
town(siegeTown)
{
assert(owner.fieldController.get() == nullptr); // must be created after this
for (int g = 0; g < wallPieceImages.size(); ++g)
{
if ( g == EWallVisual::GATE ) // gate is initially closed and has no image to display in this state
continue;
if ( !getWallPieceExistance(EWallVisual::EWallVisual(g)) )
continue;
wallPieceImages[g] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::REINFORCED));
}
}
const CCreature *BattleSiegeController::getTurretCreature() const
{
return CGI->creh->objects[town->town->clientInfo.siegeShooter];
}
Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
{
// Turret positions are read out of the config/wall_pos.txt
int posID = 0;
switch (position)
{
case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature
posID = EWallVisual::CREATURE_KEEP;
break;
case BattleHex::CASTLE_BOTTOM_TOWER: // bottom creature
posID = EWallVisual::CREATURE_BOTTOM_TOWER;
break;
case BattleHex::CASTLE_UPPER_TOWER: // upper creature
posID = EWallVisual::CREATURE_UPPER_TOWER;
break;
}
if (posID != 0)
{
return {
town->town->clientInfo.siegePositions[posID].x,
town->town->clientInfo.siegePositions[posID].y
};
}
assert(0);
return Point(0,0);
}
void BattleSiegeController::gateStateChanged(const EGateState state)
{
auto oldState = owner.getBattle()->battleGetGateState();
bool playSound = false;
auto stateId = EWallState::NONE;
switch(state)
{
case EGateState::CLOSED:
if (oldState != EGateState::BLOCKED)
playSound = true;
break;
case EGateState::BLOCKED:
if (oldState != EGateState::CLOSED)
playSound = true;
break;
case EGateState::OPENED:
playSound = true;
stateId = EWallState::DAMAGED;
break;
case EGateState::DESTROYED:
stateId = EWallState::DESTROYED;
break;
}
if (oldState != EGateState::NONE && oldState != EGateState::CLOSED && oldState != EGateState::BLOCKED)
wallPieceImages[EWallVisual::GATE] = nullptr;
if (stateId != EWallState::NONE)
wallPieceImages[EWallVisual::GATE] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::GATE, stateId));
if (playSound)
CCS->soundh->playSound(soundBase::DRAWBRG);
}
void BattleSiegeController::showAbsoluteObstacles(Canvas & canvas)
{
if (getWallPieceExistance(EWallVisual::MOAT))
showWallPiece(canvas, EWallVisual::MOAT);
if (getWallPieceExistance(EWallVisual::MOAT_BANK))
showWallPiece(canvas, EWallVisual::MOAT_BANK);
}
BattleHex BattleSiegeController::getTurretBattleHex(EWallVisual::EWallVisual wallPiece) const
{
switch(wallPiece)
{
case EWallVisual::KEEP_BATTLEMENT: return BattleHex::CASTLE_CENTRAL_TOWER;
case EWallVisual::BOTTOM_BATTLEMENT: return BattleHex::CASTLE_BOTTOM_TOWER;
case EWallVisual::UPPER_BATTLEMENT: return BattleHex::CASTLE_UPPER_TOWER;
}
assert(0);
return BattleHex::INVALID;
}
const CStack * BattleSiegeController::getTurretStack(EWallVisual::EWallVisual wallPiece) const
{
for (auto & stack : owner.getBattle()->battleGetAllStacks(true))
{
if ( stack->initialPosition == getTurretBattleHex(wallPiece))
return stack;
}
assert(0);
return nullptr;
}
void BattleSiegeController::collectRenderableObjects(BattleRenderer & renderer)
{
for (int i = EWallVisual::WALL_FIRST; i <= EWallVisual::WALL_LAST; ++i)
{
auto wallPiece = EWallVisual::EWallVisual(i);
if ( !getWallPieceExistance(wallPiece))
continue;
if ( getWallPiecePosition(wallPiece) == BattleHex::INVALID)
continue;
if (wallPiece == EWallVisual::KEEP_BATTLEMENT ||
wallPiece == EWallVisual::BOTTOM_BATTLEMENT ||
wallPiece == EWallVisual::UPPER_BATTLEMENT)
{
renderer.insert( EBattleFieldLayer::STACKS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
owner.stacksController->showStack(canvas, getTurretStack(wallPiece));
});
renderer.insert( EBattleFieldLayer::OBSTACLES, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
showWallPiece(canvas, wallPiece);
});
}
renderer.insert( EBattleFieldLayer::WALLS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
showWallPiece(canvas, wallPiece);
});
}
}
bool BattleSiegeController::isAttackableByCatapult(BattleHex hex) const
{
if (owner.tacticsMode)
return false;
auto wallPart = owner.getBattle()->battleHexToWallPart(hex);
return owner.getBattle()->isWallPartAttackable(wallPart);
}
void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca)
{
if (ca.attacker != -1)
{
const CStack *stack = owner.getBattle()->battleGetStackByID(ca.attacker);
for (auto attackInfo : ca.attackedParts)
{
owner.stacksController->addNewAnim(new CatapultAnimation(owner, stack, attackInfo.destinationTile, nullptr, attackInfo.damageDealt));
}
}
else
{
std::vector<Point> positions;
//no attacker stack, assume spell-related (earthquake) - only hit animation
for (auto attackInfo : ca.attackedParts)
positions.push_back(owner.stacksController->getStackPositionAtHex(attackInfo.destinationTile, nullptr) + Point(99, 120));
CCS->soundh->playSound( AudioPath::builtin("WALLHIT") );
owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::builtin("SGEXPL.DEF"), positions));
}
owner.waitForAnimations();
for (auto attackInfo : ca.attackedParts)
{
int wallId = static_cast<int>(attackInfo.attackedPart) + EWallVisual::DESTRUCTIBLE_FIRST;
//gate state changing handled separately
if (wallId == EWallVisual::GATE)
continue;
auto wallState = EWallState(owner.getBattle()->battleGetWallState(attackInfo.attackedPart));
wallPieceImages[wallId] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState));
}
}
const CGTownInstance *BattleSiegeController::getSiegedTown() const
{
return town;
}
/*
* BattleSiegeController.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 "BattleSiegeController.h"
#include "BattleAnimationClasses.h"
#include "BattleInterface.h"
#include "BattleInterfaceClasses.h"
#include "BattleStacksController.h"
#include "BattleFieldController.h"
#include "BattleRenderer.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../render/IRenderHandler.h"
#include "../../CCallback.h"
#include "../../lib/NetPacks.h"
#include "../../lib/CStack.h"
#include "../../lib/mapObjects/CGTownInstance.h"
ImagePath BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const
{
auto getImageIndex = [&]() -> int
{
bool isTower = (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER);
switch (state)
{
case EWallState::REINFORCED :
return 1;
case EWallState::INTACT :
if (town->hasBuilt(BuildingID::CASTLE))
return 2; // reinforced walls were damaged
else
return 1;
case EWallState::DAMAGED :
// towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
if (isTower)
return 1;
else
return 2;
case EWallState::DESTROYED :
if (isTower)
return 2;
else
return 3;
}
return 1;
};
const std::string & prefix = town->town->clientInfo.siegePrefix;
std::string addit = std::to_string(getImageIndex());
switch(what)
{
case EWallVisual::BACKGROUND_WALL:
{
auto faction = town->town->faction->getIndex();
if (faction == ETownType::RAMPART || faction == ETownType::NECROPOLIS || faction == ETownType::DUNGEON || faction == ETownType::STRONGHOLD)
return ImagePath::builtinTODO(prefix + "TPW1.BMP");
else
return ImagePath::builtinTODO(prefix + "TPWL.BMP");
}
case EWallVisual::KEEP:
return ImagePath::builtinTODO(prefix + "MAN" + addit + ".BMP");
case EWallVisual::BOTTOM_TOWER:
return ImagePath::builtinTODO(prefix + "TW1" + addit + ".BMP");
case EWallVisual::BOTTOM_WALL:
return ImagePath::builtinTODO(prefix + "WA1" + addit + ".BMP");
case EWallVisual::WALL_BELLOW_GATE:
return ImagePath::builtinTODO(prefix + "WA3" + addit + ".BMP");
case EWallVisual::WALL_OVER_GATE:
return ImagePath::builtinTODO(prefix + "WA4" + addit + ".BMP");
case EWallVisual::UPPER_WALL:
return ImagePath::builtinTODO(prefix + "WA6" + addit + ".BMP");
case EWallVisual::UPPER_TOWER:
return ImagePath::builtinTODO(prefix + "TW2" + addit + ".BMP");
case EWallVisual::GATE:
return ImagePath::builtinTODO(prefix + "DRW" + addit + ".BMP");
case EWallVisual::GATE_ARCH:
return ImagePath::builtinTODO(prefix + "ARCH.BMP");
case EWallVisual::BOTTOM_STATIC_WALL:
return ImagePath::builtinTODO(prefix + "WA2.BMP");
case EWallVisual::UPPER_STATIC_WALL:
return ImagePath::builtinTODO(prefix + "WA5.BMP");
case EWallVisual::MOAT:
return ImagePath::builtinTODO(prefix + "MOAT.BMP");
case EWallVisual::MOAT_BANK:
return ImagePath::builtinTODO(prefix + "MLIP.BMP");
case EWallVisual::KEEP_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "MANC.BMP");
case EWallVisual::BOTTOM_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "TW1C.BMP");
case EWallVisual::UPPER_BATTLEMENT:
return ImagePath::builtinTODO(prefix + "TW2C.BMP");
default:
return ImagePath();
}
}
void BattleSiegeController::showWallPiece(Canvas & canvas, EWallVisual::EWallVisual what)
{
auto & ci = town->town->clientInfo;
auto const & pos = ci.siegePositions[what];
if ( wallPieceImages[what] && pos.isValid())
canvas.draw(wallPieceImages[what], Point(pos.x, pos.y));
}
ImagePath BattleSiegeController::getBattleBackgroundName() const
{
const std::string & prefix = town->town->clientInfo.siegePrefix;
return ImagePath::builtinTODO(prefix + "BACK.BMP");
}
bool BattleSiegeController::getWallPieceExistance(EWallVisual::EWallVisual what) const
{
//FIXME: use this instead of buildings test?
//ui8 siegeLevel = owner.curInt->cb->battleGetSiegeLevel();
switch (what)
{
case EWallVisual::MOAT: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
case EWallVisual::MOAT_BANK: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
case EWallVisual::KEEP_BATTLEMENT: return town->hasBuilt(BuildingID::CITADEL) && owner.getBattle()->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
case EWallVisual::UPPER_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
default: return true;
}
}
BattleHex BattleSiegeController::getWallPiecePosition(EWallVisual::EWallVisual what) const
{
static const std::array<BattleHex, 18> wallsPositions = {
BattleHex::INVALID, // BACKGROUND, // handled separately
BattleHex::HEX_BEFORE_ALL, // BACKGROUND_WALL,
135, // KEEP,
BattleHex::HEX_AFTER_ALL, // BOTTOM_TOWER,
182, // BOTTOM_WALL,
130, // WALL_BELLOW_GATE,
62, // WALL_OVER_GATE,
12, // UPPER_WALL,
BattleHex::HEX_BEFORE_ALL, // UPPER_TOWER,
BattleHex::HEX_BEFORE_ALL, // GATE, // 94
112, // GATE_ARCH,
165, // BOTTOM_STATIC_WALL,
45, // UPPER_STATIC_WALL,
BattleHex::INVALID, // MOAT, // printed as absolute obstacle
BattleHex::INVALID, // MOAT_BANK, // printed as absolute obstacle
135, // KEEP_BATTLEMENT,
BattleHex::HEX_AFTER_ALL, // BOTTOM_BATTLEMENT,
BattleHex::HEX_BEFORE_ALL, // UPPER_BATTLEMENT,
};
return wallsPositions[what];
}
BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTownInstance *siegeTown):
owner(owner),
town(siegeTown)
{
assert(owner.fieldController.get() == nullptr); // must be created after this
for (int g = 0; g < wallPieceImages.size(); ++g)
{
if ( g == EWallVisual::GATE ) // gate is initially closed and has no image to display in this state
continue;
if ( !getWallPieceExistance(EWallVisual::EWallVisual(g)) )
continue;
wallPieceImages[g] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::REINFORCED));
}
}
const CCreature *BattleSiegeController::getTurretCreature() const
{
return CGI->creh->objects[town->town->clientInfo.siegeShooter];
}
Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
{
// Turret positions are read out of the config/wall_pos.txt
int posID = 0;
switch (position)
{
case BattleHex::CASTLE_CENTRAL_TOWER: // keep creature
posID = EWallVisual::CREATURE_KEEP;
break;
case BattleHex::CASTLE_BOTTOM_TOWER: // bottom creature
posID = EWallVisual::CREATURE_BOTTOM_TOWER;
break;
case BattleHex::CASTLE_UPPER_TOWER: // upper creature
posID = EWallVisual::CREATURE_UPPER_TOWER;
break;
}
if (posID != 0)
{
return {
town->town->clientInfo.siegePositions[posID].x,
town->town->clientInfo.siegePositions[posID].y
};
}
assert(0);
return Point(0,0);
}
void BattleSiegeController::gateStateChanged(const EGateState state)
{
auto oldState = owner.getBattle()->battleGetGateState();
bool playSound = false;
auto stateId = EWallState::NONE;
switch(state)
{
case EGateState::CLOSED:
if (oldState != EGateState::BLOCKED)
playSound = true;
break;
case EGateState::BLOCKED:
if (oldState != EGateState::CLOSED)
playSound = true;
break;
case EGateState::OPENED:
playSound = true;
stateId = EWallState::DAMAGED;
break;
case EGateState::DESTROYED:
stateId = EWallState::DESTROYED;
break;
}
if (oldState != EGateState::NONE && oldState != EGateState::CLOSED && oldState != EGateState::BLOCKED)
wallPieceImages[EWallVisual::GATE] = nullptr;
if (stateId != EWallState::NONE)
wallPieceImages[EWallVisual::GATE] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::GATE, stateId));
if (playSound)
CCS->soundh->playSound(soundBase::DRAWBRG);
}
void BattleSiegeController::showAbsoluteObstacles(Canvas & canvas)
{
if (getWallPieceExistance(EWallVisual::MOAT))
showWallPiece(canvas, EWallVisual::MOAT);
if (getWallPieceExistance(EWallVisual::MOAT_BANK))
showWallPiece(canvas, EWallVisual::MOAT_BANK);
}
BattleHex BattleSiegeController::getTurretBattleHex(EWallVisual::EWallVisual wallPiece) const
{
switch(wallPiece)
{
case EWallVisual::KEEP_BATTLEMENT: return BattleHex::CASTLE_CENTRAL_TOWER;
case EWallVisual::BOTTOM_BATTLEMENT: return BattleHex::CASTLE_BOTTOM_TOWER;
case EWallVisual::UPPER_BATTLEMENT: return BattleHex::CASTLE_UPPER_TOWER;
}
assert(0);
return BattleHex::INVALID;
}
const CStack * BattleSiegeController::getTurretStack(EWallVisual::EWallVisual wallPiece) const
{
for (auto & stack : owner.getBattle()->battleGetAllStacks(true))
{
if ( stack->initialPosition == getTurretBattleHex(wallPiece))
return stack;
}
assert(0);
return nullptr;
}
void BattleSiegeController::collectRenderableObjects(BattleRenderer & renderer)
{
for (int i = EWallVisual::WALL_FIRST; i <= EWallVisual::WALL_LAST; ++i)
{
auto wallPiece = EWallVisual::EWallVisual(i);
if ( !getWallPieceExistance(wallPiece))
continue;
if ( getWallPiecePosition(wallPiece) == BattleHex::INVALID)
continue;
if (wallPiece == EWallVisual::KEEP_BATTLEMENT ||
wallPiece == EWallVisual::BOTTOM_BATTLEMENT ||
wallPiece == EWallVisual::UPPER_BATTLEMENT)
{
renderer.insert( EBattleFieldLayer::STACKS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
owner.stacksController->showStack(canvas, getTurretStack(wallPiece));
});
renderer.insert( EBattleFieldLayer::OBSTACLES, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
showWallPiece(canvas, wallPiece);
});
}
renderer.insert( EBattleFieldLayer::WALLS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){
showWallPiece(canvas, wallPiece);
});
}
}
bool BattleSiegeController::isAttackableByCatapult(BattleHex hex) const
{
if (owner.tacticsMode)
return false;
auto wallPart = owner.getBattle()->battleHexToWallPart(hex);
return owner.getBattle()->isWallPartAttackable(wallPart);
}
void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca)
{
if (ca.attacker != -1)
{
const CStack *stack = owner.getBattle()->battleGetStackByID(ca.attacker);
for (auto attackInfo : ca.attackedParts)
{
owner.stacksController->addNewAnim(new CatapultAnimation(owner, stack, attackInfo.destinationTile, nullptr, attackInfo.damageDealt));
}
}
else
{
std::vector<Point> positions;
//no attacker stack, assume spell-related (earthquake) - only hit animation
for (auto attackInfo : ca.attackedParts)
positions.push_back(owner.stacksController->getStackPositionAtHex(attackInfo.destinationTile, nullptr) + Point(99, 120));
CCS->soundh->playSound( AudioPath::builtin("WALLHIT") );
owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::builtin("SGEXPL.DEF"), positions));
}
owner.waitForAnimations();
for (auto attackInfo : ca.attackedParts)
{
int wallId = static_cast<int>(attackInfo.attackedPart) + EWallVisual::DESTRUCTIBLE_FIRST;
//gate state changing handled separately
if (wallId == EWallVisual::GATE)
continue;
auto wallState = EWallState(owner.getBattle()->battleGetWallState(attackInfo.attackedPart));
wallPieceImages[wallId] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState));
}
}
const CGTownInstance *BattleSiegeController::getSiegedTown() const
{
return town;
}

View File

@ -1,111 +1,111 @@
/*
* BattleObstacleController.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 "../../lib/GameConstants.h"
#include "../../lib/battle/BattleHex.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CatapultAttack;
class CCreature;
class CStack;
class CGTownInstance;
class Point;
VCMI_LIB_NAMESPACE_END
class Canvas;
class BattleInterface;
class BattleRenderer;
class IImage;
namespace EWallVisual
{
enum EWallVisual
{
BACKGROUND,
BACKGROUND_WALL,
KEEP,
BOTTOM_TOWER,
BOTTOM_WALL,
WALL_BELLOW_GATE,
WALL_OVER_GATE,
UPPER_WALL,
UPPER_TOWER,
GATE,
GATE_ARCH,
BOTTOM_STATIC_WALL,
UPPER_STATIC_WALL,
MOAT,
MOAT_BANK,
KEEP_BATTLEMENT,
BOTTOM_BATTLEMENT,
UPPER_BATTLEMENT,
CREATURE_KEEP,
CREATURE_BOTTOM_TOWER,
CREATURE_UPPER_TOWER,
WALL_FIRST = BACKGROUND_WALL,
WALL_LAST = UPPER_BATTLEMENT,
// these entries are mapped to EWallPart enum
DESTRUCTIBLE_FIRST = KEEP,
DESTRUCTIBLE_LAST = GATE,
};
}
class BattleSiegeController
{
BattleInterface & owner;
/// besieged town
const CGTownInstance *town;
/// sections of castle walls, in their currently visible state
std::array<std::shared_ptr<IImage>, EWallVisual::WALL_LAST + 1> wallPieceImages;
/// return URI for image for a wall piece
ImagePath getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const;
/// returns BattleHex to which chosen wall piece is bound
BattleHex getWallPiecePosition(EWallVisual::EWallVisual what) const;
/// returns true if chosen wall piece should be present in current battle
bool getWallPieceExistance(EWallVisual::EWallVisual what) const;
void showWallPiece(Canvas & canvas, EWallVisual::EWallVisual what);
BattleHex getTurretBattleHex(EWallVisual::EWallVisual wallPiece) const;
const CStack * getTurretStack(EWallVisual::EWallVisual wallPiece) const;
public:
BattleSiegeController(BattleInterface & owner, const CGTownInstance *siegeTown);
/// call-ins from server
void gateStateChanged(const EGateState state);
void stackIsCatapulting(const CatapultAttack & ca);
/// call-ins from other battle controllers
void showAbsoluteObstacles(Canvas & canvas);
void collectRenderableObjects(BattleRenderer & renderer);
/// queries from other battle controllers
bool isAttackableByCatapult(BattleHex hex) const;
ImagePath getBattleBackgroundName() const;
const CCreature *getTurretCreature() const;
Point getTurretCreaturePosition( BattleHex position ) const;
const CGTownInstance *getSiegedTown() const;
};
/*
* BattleObstacleController.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 "../../lib/GameConstants.h"
#include "../../lib/battle/BattleHex.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CatapultAttack;
class CCreature;
class CStack;
class CGTownInstance;
class Point;
VCMI_LIB_NAMESPACE_END
class Canvas;
class BattleInterface;
class BattleRenderer;
class IImage;
namespace EWallVisual
{
enum EWallVisual
{
BACKGROUND,
BACKGROUND_WALL,
KEEP,
BOTTOM_TOWER,
BOTTOM_WALL,
WALL_BELLOW_GATE,
WALL_OVER_GATE,
UPPER_WALL,
UPPER_TOWER,
GATE,
GATE_ARCH,
BOTTOM_STATIC_WALL,
UPPER_STATIC_WALL,
MOAT,
MOAT_BANK,
KEEP_BATTLEMENT,
BOTTOM_BATTLEMENT,
UPPER_BATTLEMENT,
CREATURE_KEEP,
CREATURE_BOTTOM_TOWER,
CREATURE_UPPER_TOWER,
WALL_FIRST = BACKGROUND_WALL,
WALL_LAST = UPPER_BATTLEMENT,
// these entries are mapped to EWallPart enum
DESTRUCTIBLE_FIRST = KEEP,
DESTRUCTIBLE_LAST = GATE,
};
}
class BattleSiegeController
{
BattleInterface & owner;
/// besieged town
const CGTownInstance *town;
/// sections of castle walls, in their currently visible state
std::array<std::shared_ptr<IImage>, EWallVisual::WALL_LAST + 1> wallPieceImages;
/// return URI for image for a wall piece
ImagePath getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const;
/// returns BattleHex to which chosen wall piece is bound
BattleHex getWallPiecePosition(EWallVisual::EWallVisual what) const;
/// returns true if chosen wall piece should be present in current battle
bool getWallPieceExistance(EWallVisual::EWallVisual what) const;
void showWallPiece(Canvas & canvas, EWallVisual::EWallVisual what);
BattleHex getTurretBattleHex(EWallVisual::EWallVisual wallPiece) const;
const CStack * getTurretStack(EWallVisual::EWallVisual wallPiece) const;
public:
BattleSiegeController(BattleInterface & owner, const CGTownInstance *siegeTown);
/// call-ins from server
void gateStateChanged(const EGateState state);
void stackIsCatapulting(const CatapultAttack & ca);
/// call-ins from other battle controllers
void showAbsoluteObstacles(Canvas & canvas);
void collectRenderableObjects(BattleRenderer & renderer);
/// queries from other battle controllers
bool isAttackableByCatapult(BattleHex hex) const;
ImagePath getBattleBackgroundName() const;
const CCreature *getTurretCreature() const;
Point getTurretCreaturePosition( BattleHex position ) const;
const CGTownInstance *getSiegedTown() const;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,147 +1,147 @@
/*
* BattleStacksController.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 "../render/ColorFilter.h"
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
class BattleAction;
class CStack;
class CSpell;
class SpellID;
class Point;
VCMI_LIB_NAMESPACE_END
struct StackAttackedInfo;
struct StackAttackInfo;
class ColorFilter;
class Canvas;
class BattleInterface;
class BattleAnimation;
class CreatureAnimation;
class BattleAnimation;
class BattleRenderer;
class IImage;
struct BattleStackFilterEffect
{
ColorFilter effect;
const CStack * target;
const CSpell * source;
bool persistent;
};
/// Class responsible for handling stacks in battle
/// Handles ordering of stacks animation
/// As well as rendering of stacks, their amount boxes
/// And any other effect applied to stacks
class BattleStacksController
{
BattleInterface & owner;
std::shared_ptr<IImage> amountNormal;
std::shared_ptr<IImage> amountNegative;
std::shared_ptr<IImage> amountPositive;
std::shared_ptr<IImage> amountEffNeutral;
/// currently displayed animations <anim, initialized>
std::vector<BattleAnimation *> currentAnimations;
/// currently active color effects on stacks, in order of their addition (which corresponds to their apply order)
std::vector<BattleStackFilterEffect> stackFilterEffects;
/// animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map<int32_t, std::shared_ptr<CreatureAnimation>> stackAnimation;
/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
std::map<int, bool> stackFacingRight;
/// Stacks have amount box hidden due to ongoing animations
std::set<int> stackAmountBoxHidden;
/// currently active stack; nullptr - no one
const CStack *activeStack;
/// stacks or their battle queue images below mouse pointer (multiple stacks possible while spellcasting), used for border animation
std::vector<const CStack *> mouseHoveredStacks;
///when animation is playing, we should wait till the end to make the next stack active; nullptr of none
const CStack *stackToActivate;
/// for giving IDs for animations
ui32 animIDhelper;
bool stackNeedsAmountBox(const CStack * stack) const;
void showStackAmountBox(Canvas & canvas, const CStack * stack);
BattleHex getStackCurrentPosition(const CStack * stack) const;
std::shared_ptr<IImage> getStackAmountBox(const CStack * stack);
void removeExpiredColorFilters();
void initializeBattleAnimations();
void tickFrameBattleAnimations(uint32_t msPassed);
void updateBattleAnimations(uint32_t msPassed);
void updateHoveredStacks();
std::vector<const CStack *> selectHoveredStacks();
bool shouldAttackFacingRight(const CStack * attacker, const CStack * defender);
public:
BattleStacksController(BattleInterface & owner);
bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const;
bool facingRight(const CStack * stack) const;
void stackReset(const CStack * stack);
void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
void stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest
void startAction(const BattleAction & action);
void endAction(const BattleAction & action);
void deactivateStack(); //copy activeStack to stackToActivate, then set activeStack to nullptr to temporary disable current stack
void activateStack(); //copy stackToActivate to activeStack to enable controls of the stack
void setActiveStack(const CStack *stack);
void showAliveStack(Canvas & canvas, const CStack * stack);
void showStack(Canvas & canvas, const CStack * stack);
void collectRenderableObjects(BattleRenderer & renderer);
/// Adds new color filter effect targeting stack
/// Effect will last as long as stack is affected by specified spell (unless effect is persistent)
/// If effect from same (target, source) already exists, it will be updated
void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent);
void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims
const CStack* getActiveStack() const;
const std::vector<uint32_t> getHoveredStacksUnitIds() const;
void tick(uint32_t msPassed);
/// returns position of animation needed to place stack in specific hex
Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;
friend class BattleAnimation; // for exposing pendingAnims/creAnims/creDir to animations
};
/*
* BattleStacksController.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 "../render/ColorFilter.h"
VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
class BattleAction;
class CStack;
class CSpell;
class SpellID;
class Point;
VCMI_LIB_NAMESPACE_END
struct StackAttackedInfo;
struct StackAttackInfo;
class ColorFilter;
class Canvas;
class BattleInterface;
class BattleAnimation;
class CreatureAnimation;
class BattleAnimation;
class BattleRenderer;
class IImage;
struct BattleStackFilterEffect
{
ColorFilter effect;
const CStack * target;
const CSpell * source;
bool persistent;
};
/// Class responsible for handling stacks in battle
/// Handles ordering of stacks animation
/// As well as rendering of stacks, their amount boxes
/// And any other effect applied to stacks
class BattleStacksController
{
BattleInterface & owner;
std::shared_ptr<IImage> amountNormal;
std::shared_ptr<IImage> amountNegative;
std::shared_ptr<IImage> amountPositive;
std::shared_ptr<IImage> amountEffNeutral;
/// currently displayed animations <anim, initialized>
std::vector<BattleAnimation *> currentAnimations;
/// currently active color effects on stacks, in order of their addition (which corresponds to their apply order)
std::vector<BattleStackFilterEffect> stackFilterEffects;
/// animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map<int32_t, std::shared_ptr<CreatureAnimation>> stackAnimation;
/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
std::map<int, bool> stackFacingRight;
/// Stacks have amount box hidden due to ongoing animations
std::set<int> stackAmountBoxHidden;
/// currently active stack; nullptr - no one
const CStack *activeStack;
/// stacks or their battle queue images below mouse pointer (multiple stacks possible while spellcasting), used for border animation
std::vector<const CStack *> mouseHoveredStacks;
///when animation is playing, we should wait till the end to make the next stack active; nullptr of none
const CStack *stackToActivate;
/// for giving IDs for animations
ui32 animIDhelper;
bool stackNeedsAmountBox(const CStack * stack) const;
void showStackAmountBox(Canvas & canvas, const CStack * stack);
BattleHex getStackCurrentPosition(const CStack * stack) const;
std::shared_ptr<IImage> getStackAmountBox(const CStack * stack);
void removeExpiredColorFilters();
void initializeBattleAnimations();
void tickFrameBattleAnimations(uint32_t msPassed);
void updateBattleAnimations(uint32_t msPassed);
void updateHoveredStacks();
std::vector<const CStack *> selectHoveredStacks();
bool shouldAttackFacingRight(const CStack * attacker, const CStack * defender);
public:
BattleStacksController(BattleInterface & owner);
bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const;
bool facingRight(const CStack * stack) const;
void stackReset(const CStack * stack);
void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
void stackActivated(const CStack *stack); //active stack has been changed
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
void stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest
void startAction(const BattleAction & action);
void endAction(const BattleAction & action);
void deactivateStack(); //copy activeStack to stackToActivate, then set activeStack to nullptr to temporary disable current stack
void activateStack(); //copy stackToActivate to activeStack to enable controls of the stack
void setActiveStack(const CStack *stack);
void showAliveStack(Canvas & canvas, const CStack * stack);
void showStack(Canvas & canvas, const CStack * stack);
void collectRenderableObjects(BattleRenderer & renderer);
/// Adds new color filter effect targeting stack
/// Effect will last as long as stack is affected by specified spell (unless effect is persistent)
/// If effect from same (target, source) already exists, it will be updated
void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent);
void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims
const CStack* getActiveStack() const;
const std::vector<uint32_t> getHoveredStacksUnitIds() const;
void tick(uint32_t msPassed);
/// returns position of animation needed to place stack in specific hex
Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;
friend class BattleAnimation; // for exposing pendingAnims/creAnims/creDir to animations
};

File diff suppressed because it is too large Load Diff

View File

@ -1,118 +1,118 @@
/*
* BattleWindow.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"
#include "../gui/InterfaceObjectConfigurable.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/PossiblePlayerBattleAction.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
VCMI_LIB_NAMESPACE_END
class CButton;
class BattleInterface;
class BattleConsole;
class BattleRenderer;
class StackQueue;
class HeroInfoBasicPanel;
/// GUI object that handles functionality of panel at the bottom of combat screen
class BattleWindow : public InterfaceObjectConfigurable
{
BattleInterface & owner;
std::shared_ptr<StackQueue> queue;
std::shared_ptr<BattleConsole> console;
std::shared_ptr<HeroInfoBasicPanel> attackerHeroWindow;
std::shared_ptr<HeroInfoBasicPanel> defenderHeroWindow;
/// button press handling functions
void bOptionsf();
void bSurrenderf();
void bFleef();
void bAutofightf();
void bSpellf();
void bWaitf();
void bSwitchActionf();
void bDefencef();
void bConsoleUpf();
void bConsoleDownf();
void bTacticNextStack();
void bTacticPhaseEnd();
/// functions for handling actions after they were confirmed by popup window
void reallyFlee();
void reallySurrender();
/// management of alternative actions
std::list<PossiblePlayerBattleAction> alternativeActions;
PossiblePlayerBattleAction defaultAction;
void showAlternativeActionIcon(PossiblePlayerBattleAction);
/// flip battle queue visibility to opposite
void toggleQueueVisibility();
void createQueue();
void toggleStickyHeroWindowsVisibility();
void createStickyHeroInfoWindows();
std::shared_ptr<BattleConsole> buildBattleConsole(const JsonNode &) const;
public:
BattleWindow(BattleInterface & owner );
~BattleWindow();
/// Closes window once battle finished
void close();
/// Toggle StackQueue visibility
void hideQueue();
void showQueue();
/// Toggle permanent hero info windows visibility (HD mod feature)
void hideStickyHeroWindows();
void showStickyHeroWindows();
/// Event handler for netpack changing hero mana points
void heroManaPointsChanged(const CGHeroInstance * hero);
/// block all UI elements when player is not allowed to act, e.g. during enemy turn
void blockUI(bool on);
/// Refresh queue after turn order changes
void updateQueue();
/// Refresh sticky variant of hero info window after spellcast, side same as in BattleSpellCast::side
void updateHeroInfoWindow(uint8_t side, const InfoAboutHero & hero);
/// Get mouse-hovered battle queue unit ID if any found
std::optional<uint32_t> getQueueHoveredUnitId();
void activate() override;
void deactivate() override;
void keyPressed(EShortcut key) override;
bool captureThisKey(EShortcut key) override;
void clickPressed(const Point & cursorPosition) override;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
/// Toggle UI to displaying tactics phase
void tacticPhaseStarted();
/// Toggle UI to displaying battle log in place of tactics UI
void tacticPhaseEnded();
/// Set possible alternative options. If more than 1 - the last will be considered as default option
void setAlternativeActions(const std::list<PossiblePlayerBattleAction> &);
};
/*
* BattleWindow.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"
#include "../gui/InterfaceObjectConfigurable.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/PossiblePlayerBattleAction.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
VCMI_LIB_NAMESPACE_END
class CButton;
class BattleInterface;
class BattleConsole;
class BattleRenderer;
class StackQueue;
class HeroInfoBasicPanel;
/// GUI object that handles functionality of panel at the bottom of combat screen
class BattleWindow : public InterfaceObjectConfigurable
{
BattleInterface & owner;
std::shared_ptr<StackQueue> queue;
std::shared_ptr<BattleConsole> console;
std::shared_ptr<HeroInfoBasicPanel> attackerHeroWindow;
std::shared_ptr<HeroInfoBasicPanel> defenderHeroWindow;
/// button press handling functions
void bOptionsf();
void bSurrenderf();
void bFleef();
void bAutofightf();
void bSpellf();
void bWaitf();
void bSwitchActionf();
void bDefencef();
void bConsoleUpf();
void bConsoleDownf();
void bTacticNextStack();
void bTacticPhaseEnd();
/// functions for handling actions after they were confirmed by popup window
void reallyFlee();
void reallySurrender();
/// management of alternative actions
std::list<PossiblePlayerBattleAction> alternativeActions;
PossiblePlayerBattleAction defaultAction;
void showAlternativeActionIcon(PossiblePlayerBattleAction);
/// flip battle queue visibility to opposite
void toggleQueueVisibility();
void createQueue();
void toggleStickyHeroWindowsVisibility();
void createStickyHeroInfoWindows();
std::shared_ptr<BattleConsole> buildBattleConsole(const JsonNode &) const;
public:
BattleWindow(BattleInterface & owner );
~BattleWindow();
/// Closes window once battle finished
void close();
/// Toggle StackQueue visibility
void hideQueue();
void showQueue();
/// Toggle permanent hero info windows visibility (HD mod feature)
void hideStickyHeroWindows();
void showStickyHeroWindows();
/// Event handler for netpack changing hero mana points
void heroManaPointsChanged(const CGHeroInstance * hero);
/// block all UI elements when player is not allowed to act, e.g. during enemy turn
void blockUI(bool on);
/// Refresh queue after turn order changes
void updateQueue();
/// Refresh sticky variant of hero info window after spellcast, side same as in BattleSpellCast::side
void updateHeroInfoWindow(uint8_t side, const InfoAboutHero & hero);
/// Get mouse-hovered battle queue unit ID if any found
std::optional<uint32_t> getQueueHoveredUnitId();
void activate() override;
void deactivate() override;
void keyPressed(EShortcut key) override;
bool captureThisKey(EShortcut key) override;
void clickPressed(const Point & cursorPosition) override;
void show(Canvas & to) override;
void showAll(Canvas & to) override;
/// Toggle UI to displaying tactics phase
void tacticPhaseStarted();
/// Toggle UI to displaying battle log in place of tactics UI
void tacticPhaseEnded();
/// Set possible alternative options. If more than 1 - the last will be considered as default option
void setAlternativeActions(const std::list<PossiblePlayerBattleAction> &);
};

View File

@ -1,433 +1,433 @@
/*
* CCreatureAnimation.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 "CreatureAnimation.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CCreatureHandler.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/ColorFilter.h"
#include "../render/IRenderHandler.h"
static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 };
static const ColorRGBA creatureGoldBorder = { 255, 255, 0, 255 };
static const ColorRGBA creatureNoBorder = { 0, 0, 0, 0 };
static ColorRGBA genShadow(ui8 alpha)
{
return ColorRGBA(0, 0, 0, alpha);
}
ColorRGBA AnimationControls::getBlueBorder()
{
return creatureBlueBorder;
}
ColorRGBA AnimationControls::getGoldBorder()
{
return creatureGoldBorder;
}
ColorRGBA AnimationControls::getNoBorder()
{
return creatureNoBorder;
}
std::shared_ptr<CreatureAnimation> AnimationControls::getAnimation(const CCreature * creature)
{
auto func = std::bind(&AnimationControls::getCreatureAnimationSpeed, creature, _1, _2);
return std::make_shared<CreatureAnimation>(creature->animDefName, func);
}
float AnimationControls::getAnimationSpeedFactor()
{
// according to testing, H3 ratios between slow/medium/fast might actually be 36/60/100 (x1.666)
// exact value is hard to tell due to large rounding errors
// however we will assume them to be 33/66/100 since these values are better for standard 60 fps displays:
// with these numbers, base frame display duration will be 100/66/33 ms - exactly 6/4/2 frames
return settings["battle"]["speedFactor"].Float();
}
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType type)
{
assert(creature->animation.walkAnimationTime != 0);
assert(creature->animation.attackAnimationTime != 0);
assert(anim->framesInGroup(type) != 0);
// possible new fields for creature format:
//split "Attack time" into "Shoot Time" and "Cast Time"
// base speed for all H3 animations on slow speed is 10 frames per second (or 100ms per frame)
const float baseSpeed = 10.f;
const float speed = baseSpeed * getAnimationSpeedFactor();
switch (type)
{
case ECreatureAnimType::MOVING:
return speed / creature->animation.walkAnimationTime;
case ECreatureAnimType::MOUSEON:
return baseSpeed;
case ECreatureAnimType::HOLDING:
return creature->animation.idleAnimationTime;
case ECreatureAnimType::SHOOT_UP:
case ECreatureAnimType::SHOOT_FRONT:
case ECreatureAnimType::SHOOT_DOWN:
case ECreatureAnimType::SPECIAL_UP:
case ECreatureAnimType::SPECIAL_FRONT:
case ECreatureAnimType::SPECIAL_DOWN:
case ECreatureAnimType::CAST_DOWN:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::CAST_UP:
return speed / creature->animation.attackAnimationTime;
// as strange as it looks like "attackAnimationTime" does not affects melee attacks
// necessary because length of these animations must be same for all creatures for synchronization
case ECreatureAnimType::ATTACK_UP:
case ECreatureAnimType::ATTACK_FRONT:
case ECreatureAnimType::ATTACK_DOWN:
case ECreatureAnimType::HITTED:
case ECreatureAnimType::DEFENCE:
case ECreatureAnimType::DEATH:
case ECreatureAnimType::DEATH_RANGED:
case ECreatureAnimType::RESURRECTION:
case ECreatureAnimType::GROUP_ATTACK_DOWN:
case ECreatureAnimType::GROUP_ATTACK_FRONT:
case ECreatureAnimType::GROUP_ATTACK_UP:
return speed;
case ECreatureAnimType::TURN_L:
case ECreatureAnimType::TURN_R:
return speed;
case ECreatureAnimType::MOVE_START:
case ECreatureAnimType::MOVE_END:
return speed;
case ECreatureAnimType::DEAD:
case ECreatureAnimType::DEAD_RANGED:
return speed;
default:
return speed;
}
}
float AnimationControls::getProjectileSpeed()
{
// H3 speed: 1250/2500/3750 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 1250);
}
float AnimationControls::getRayProjectileSpeed()
{
// H3 speed: 4000/8000/12000 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 4000);
}
float AnimationControls::getCatapultSpeed()
{
// H3 speed: 200/400/600 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 200);
}
float AnimationControls::getSpellEffectSpeed()
{
// H3 speed: 10/20/30 frames per second
return static_cast<float>(getAnimationSpeedFactor() * 10);
}
float AnimationControls::getMovementDistance(const CCreature * creature)
{
// H3 speed: 2/4/6 tiles per second
return static_cast<float>( 2.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFlightDistance(const CCreature * creature)
{
// Note: for whatever reason, H3 uses "Walk Animation Time" here, even though "Flight Animation Distance" also exists
// H3 speed: 250/500/750 pixels per second
return static_cast<float>( 250.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFadeInDuration()
{
// H3 speed: 500/250/166 ms
return 0.5f / getAnimationSpeedFactor();
}
float AnimationControls::getObstaclesSpeed()
{
// H3 speed: 20 frames per second, irregardless of speed setting.
return 20.f;
}
ECreatureAnimType CreatureAnimation::getType() const
{
return type;
}
void CreatureAnimation::setType(ECreatureAnimType type)
{
this->type = type;
currentFrame = 0;
once = false;
speed = speedController(this, type);
}
CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedController controller)
: name(name_),
speed(0.1f),
shadowAlpha(128),
currentFrame(0),
animationEnd(-1),
elapsedTime(0),
type(ECreatureAnimType::HOLDING),
speedController(controller),
once(false)
{
forward = GH.renderHandler().loadAnimation(name_);
reverse = GH.renderHandler().loadAnimation(name_);
//todo: optimize
forward->preload();
reverse->preload();
// if necessary, add one frame into vcmi-only group DEAD
if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::DEATH), forward->size(size_t(ECreatureAnimType::DEATH))-1, size_t(ECreatureAnimType::DEAD));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH), reverse->size(size_t(ECreatureAnimType::DEATH))-1, size_t(ECreatureAnimType::DEAD));
}
if(forward->size(size_t(ECreatureAnimType::DEAD_RANGED)) == 0 && forward->size(size_t(ECreatureAnimType::DEATH_RANGED)) != 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::DEATH_RANGED), forward->size(size_t(ECreatureAnimType::DEATH_RANGED))-1, size_t(ECreatureAnimType::DEAD_RANGED));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH_RANGED), reverse->size(size_t(ECreatureAnimType::DEATH_RANGED))-1, size_t(ECreatureAnimType::DEAD_RANGED));
}
if(forward->size(size_t(ECreatureAnimType::FROZEN)) == 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::HOLDING), 0, size_t(ECreatureAnimType::FROZEN));
reverse->duplicateImage(size_t(ECreatureAnimType::HOLDING), 0, size_t(ECreatureAnimType::FROZEN));
}
if(forward->size(size_t(ECreatureAnimType::RESURRECTION)) == 0)
{
for (size_t i = 0; i < forward->size(size_t(ECreatureAnimType::DEATH)); ++i)
{
size_t current = forward->size(size_t(ECreatureAnimType::DEATH)) - 1 - i;
forward->duplicateImage(size_t(ECreatureAnimType::DEATH), current, size_t(ECreatureAnimType::RESURRECTION));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH), current, size_t(ECreatureAnimType::RESURRECTION));
}
}
//TODO: get dimensions form CAnimation
auto first = forward->getImage(0, size_t(type), true);
if(!first)
{
fullWidth = 0;
fullHeight = 0;
return;
}
fullWidth = first->width();
fullHeight = first->height();
reverse->verticalFlip();
speed = speedController(this, type);
}
void CreatureAnimation::endAnimation()
{
once = false;
auto copy = onAnimationReset;
onAnimationReset.clear();
copy();
}
bool CreatureAnimation::incrementFrame(float timePassed)
{
elapsedTime += timePassed;
currentFrame += timePassed * speed;
if (animationEnd >= 0)
currentFrame = std::min(currentFrame, animationEnd);
const auto framesNumber = framesInGroup(type);
if(framesNumber <= 0)
{
endAnimation();
}
else if(currentFrame >= float(framesNumber))
{
// just in case of extremely low fps (or insanely high speed)
while(currentFrame >= float(framesNumber))
currentFrame -= framesNumber;
if(once)
setType(ECreatureAnimType::HOLDING);
endAnimation();
return true;
}
return false;
}
void CreatureAnimation::setBorderColor(ColorRGBA palette)
{
border = palette;
}
int CreatureAnimation::getWidth() const
{
return fullWidth;
}
int CreatureAnimation::getHeight() const
{
return fullHeight;
}
float CreatureAnimation::getCurrentFrame() const
{
return currentFrame;
}
void CreatureAnimation::playOnce( ECreatureAnimType type )
{
setType(type);
once = true;
}
inline int getBorderStrength(float time)
{
float borderStrength = fabs(std::round(time) - time) * 2; // generate value in range 0-1
return static_cast<int>(borderStrength * 155 + 100); // scale to 0-255
}
static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base)
{
return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256));
}
static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2)
{
return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256;
}
static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over)
{
return ColorRGBA(
mixChannels(over.r, base.r, over.a, base.a),
mixChannels(over.g, base.g, over.a, base.a),
mixChannels(over.b, base.b, over.a, base.a),
ui8(over.a + base.a * (255 - over.a) / 256)
);
}
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target.resize(8);
target[0] = genShadow(0);
target[1] = genShadow(shadowAlpha / 2);
// colors 2 & 3 are not used in creatures
target[4] = genShadow(shadowAlpha);
target[5] = genBorderColor(getBorderStrength(elapsedTime), border);
target[6] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight)
{
ColorRGBA shadowTest = shifter.shiftColor(genShadow(128));
shadowAlpha = shadowTest.a;
size_t frame = static_cast<size_t>(floor(currentFrame));
std::shared_ptr<IImage> image;
if(facingRight)
image = forward->getImage(frame, size_t(type));
else
image = reverse->getImage(frame, size_t(type));
if(image)
{
IImage::SpecialPalette SpecialPalette;
genSpecialPalette(SpecialPalette);
image->setSpecialPallete(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES);
image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES);
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
}
}
void CreatureAnimation::playUntil(size_t frameIndex)
{
animationEnd = frameIndex;
}
int CreatureAnimation::framesInGroup(ECreatureAnimType group) const
{
return static_cast<int>(forward->size(size_t(group)));
}
bool CreatureAnimation::isDead() const
{
return getType() == ECreatureAnimType::DEAD
|| getType() == ECreatureAnimType::DEAD_RANGED;
}
bool CreatureAnimation::isDying() const
{
return getType() == ECreatureAnimType::DEATH
|| getType() == ECreatureAnimType::DEATH_RANGED;
}
bool CreatureAnimation::isDeadOrDying() const
{
return getType() == ECreatureAnimType::DEAD
|| getType() == ECreatureAnimType::DEATH
|| getType() == ECreatureAnimType::DEAD_RANGED
|| getType() == ECreatureAnimType::DEATH_RANGED;
}
bool CreatureAnimation::isIdle() const
{
return getType() == ECreatureAnimType::HOLDING
|| getType() == ECreatureAnimType::MOUSEON;
}
bool CreatureAnimation::isMoving() const
{
return getType() == ECreatureAnimType::MOVE_START
|| getType() == ECreatureAnimType::MOVING
|| getType() == ECreatureAnimType::MOVE_END
|| getType() == ECreatureAnimType::TURN_L
|| getType() == ECreatureAnimType::TURN_R;
}
bool CreatureAnimation::isShooting() const
{
return getType() == ECreatureAnimType::SHOOT_UP
|| getType() == ECreatureAnimType::SHOOT_FRONT
|| getType() == ECreatureAnimType::SHOOT_DOWN;
}
/*
* CCreatureAnimation.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 "CreatureAnimation.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CCreatureHandler.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../render/ColorFilter.h"
#include "../render/IRenderHandler.h"
static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 };
static const ColorRGBA creatureGoldBorder = { 255, 255, 0, 255 };
static const ColorRGBA creatureNoBorder = { 0, 0, 0, 0 };
static ColorRGBA genShadow(ui8 alpha)
{
return ColorRGBA(0, 0, 0, alpha);
}
ColorRGBA AnimationControls::getBlueBorder()
{
return creatureBlueBorder;
}
ColorRGBA AnimationControls::getGoldBorder()
{
return creatureGoldBorder;
}
ColorRGBA AnimationControls::getNoBorder()
{
return creatureNoBorder;
}
std::shared_ptr<CreatureAnimation> AnimationControls::getAnimation(const CCreature * creature)
{
auto func = std::bind(&AnimationControls::getCreatureAnimationSpeed, creature, _1, _2);
return std::make_shared<CreatureAnimation>(creature->animDefName, func);
}
float AnimationControls::getAnimationSpeedFactor()
{
// according to testing, H3 ratios between slow/medium/fast might actually be 36/60/100 (x1.666)
// exact value is hard to tell due to large rounding errors
// however we will assume them to be 33/66/100 since these values are better for standard 60 fps displays:
// with these numbers, base frame display duration will be 100/66/33 ms - exactly 6/4/2 frames
return settings["battle"]["speedFactor"].Float();
}
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType type)
{
assert(creature->animation.walkAnimationTime != 0);
assert(creature->animation.attackAnimationTime != 0);
assert(anim->framesInGroup(type) != 0);
// possible new fields for creature format:
//split "Attack time" into "Shoot Time" and "Cast Time"
// base speed for all H3 animations on slow speed is 10 frames per second (or 100ms per frame)
const float baseSpeed = 10.f;
const float speed = baseSpeed * getAnimationSpeedFactor();
switch (type)
{
case ECreatureAnimType::MOVING:
return speed / creature->animation.walkAnimationTime;
case ECreatureAnimType::MOUSEON:
return baseSpeed;
case ECreatureAnimType::HOLDING:
return creature->animation.idleAnimationTime;
case ECreatureAnimType::SHOOT_UP:
case ECreatureAnimType::SHOOT_FRONT:
case ECreatureAnimType::SHOOT_DOWN:
case ECreatureAnimType::SPECIAL_UP:
case ECreatureAnimType::SPECIAL_FRONT:
case ECreatureAnimType::SPECIAL_DOWN:
case ECreatureAnimType::CAST_DOWN:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::CAST_UP:
return speed / creature->animation.attackAnimationTime;
// as strange as it looks like "attackAnimationTime" does not affects melee attacks
// necessary because length of these animations must be same for all creatures for synchronization
case ECreatureAnimType::ATTACK_UP:
case ECreatureAnimType::ATTACK_FRONT:
case ECreatureAnimType::ATTACK_DOWN:
case ECreatureAnimType::HITTED:
case ECreatureAnimType::DEFENCE:
case ECreatureAnimType::DEATH:
case ECreatureAnimType::DEATH_RANGED:
case ECreatureAnimType::RESURRECTION:
case ECreatureAnimType::GROUP_ATTACK_DOWN:
case ECreatureAnimType::GROUP_ATTACK_FRONT:
case ECreatureAnimType::GROUP_ATTACK_UP:
return speed;
case ECreatureAnimType::TURN_L:
case ECreatureAnimType::TURN_R:
return speed;
case ECreatureAnimType::MOVE_START:
case ECreatureAnimType::MOVE_END:
return speed;
case ECreatureAnimType::DEAD:
case ECreatureAnimType::DEAD_RANGED:
return speed;
default:
return speed;
}
}
float AnimationControls::getProjectileSpeed()
{
// H3 speed: 1250/2500/3750 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 1250);
}
float AnimationControls::getRayProjectileSpeed()
{
// H3 speed: 4000/8000/12000 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 4000);
}
float AnimationControls::getCatapultSpeed()
{
// H3 speed: 200/400/600 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 200);
}
float AnimationControls::getSpellEffectSpeed()
{
// H3 speed: 10/20/30 frames per second
return static_cast<float>(getAnimationSpeedFactor() * 10);
}
float AnimationControls::getMovementDistance(const CCreature * creature)
{
// H3 speed: 2/4/6 tiles per second
return static_cast<float>( 2.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFlightDistance(const CCreature * creature)
{
// Note: for whatever reason, H3 uses "Walk Animation Time" here, even though "Flight Animation Distance" also exists
// H3 speed: 250/500/750 pixels per second
return static_cast<float>( 250.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFadeInDuration()
{
// H3 speed: 500/250/166 ms
return 0.5f / getAnimationSpeedFactor();
}
float AnimationControls::getObstaclesSpeed()
{
// H3 speed: 20 frames per second, irregardless of speed setting.
return 20.f;
}
ECreatureAnimType CreatureAnimation::getType() const
{
return type;
}
void CreatureAnimation::setType(ECreatureAnimType type)
{
this->type = type;
currentFrame = 0;
once = false;
speed = speedController(this, type);
}
CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedController controller)
: name(name_),
speed(0.1f),
shadowAlpha(128),
currentFrame(0),
animationEnd(-1),
elapsedTime(0),
type(ECreatureAnimType::HOLDING),
speedController(controller),
once(false)
{
forward = GH.renderHandler().loadAnimation(name_);
reverse = GH.renderHandler().loadAnimation(name_);
//todo: optimize
forward->preload();
reverse->preload();
// if necessary, add one frame into vcmi-only group DEAD
if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::DEATH), forward->size(size_t(ECreatureAnimType::DEATH))-1, size_t(ECreatureAnimType::DEAD));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH), reverse->size(size_t(ECreatureAnimType::DEATH))-1, size_t(ECreatureAnimType::DEAD));
}
if(forward->size(size_t(ECreatureAnimType::DEAD_RANGED)) == 0 && forward->size(size_t(ECreatureAnimType::DEATH_RANGED)) != 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::DEATH_RANGED), forward->size(size_t(ECreatureAnimType::DEATH_RANGED))-1, size_t(ECreatureAnimType::DEAD_RANGED));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH_RANGED), reverse->size(size_t(ECreatureAnimType::DEATH_RANGED))-1, size_t(ECreatureAnimType::DEAD_RANGED));
}
if(forward->size(size_t(ECreatureAnimType::FROZEN)) == 0)
{
forward->duplicateImage(size_t(ECreatureAnimType::HOLDING), 0, size_t(ECreatureAnimType::FROZEN));
reverse->duplicateImage(size_t(ECreatureAnimType::HOLDING), 0, size_t(ECreatureAnimType::FROZEN));
}
if(forward->size(size_t(ECreatureAnimType::RESURRECTION)) == 0)
{
for (size_t i = 0; i < forward->size(size_t(ECreatureAnimType::DEATH)); ++i)
{
size_t current = forward->size(size_t(ECreatureAnimType::DEATH)) - 1 - i;
forward->duplicateImage(size_t(ECreatureAnimType::DEATH), current, size_t(ECreatureAnimType::RESURRECTION));
reverse->duplicateImage(size_t(ECreatureAnimType::DEATH), current, size_t(ECreatureAnimType::RESURRECTION));
}
}
//TODO: get dimensions form CAnimation
auto first = forward->getImage(0, size_t(type), true);
if(!first)
{
fullWidth = 0;
fullHeight = 0;
return;
}
fullWidth = first->width();
fullHeight = first->height();
reverse->verticalFlip();
speed = speedController(this, type);
}
void CreatureAnimation::endAnimation()
{
once = false;
auto copy = onAnimationReset;
onAnimationReset.clear();
copy();
}
bool CreatureAnimation::incrementFrame(float timePassed)
{
elapsedTime += timePassed;
currentFrame += timePassed * speed;
if (animationEnd >= 0)
currentFrame = std::min(currentFrame, animationEnd);
const auto framesNumber = framesInGroup(type);
if(framesNumber <= 0)
{
endAnimation();
}
else if(currentFrame >= float(framesNumber))
{
// just in case of extremely low fps (or insanely high speed)
while(currentFrame >= float(framesNumber))
currentFrame -= framesNumber;
if(once)
setType(ECreatureAnimType::HOLDING);
endAnimation();
return true;
}
return false;
}
void CreatureAnimation::setBorderColor(ColorRGBA palette)
{
border = palette;
}
int CreatureAnimation::getWidth() const
{
return fullWidth;
}
int CreatureAnimation::getHeight() const
{
return fullHeight;
}
float CreatureAnimation::getCurrentFrame() const
{
return currentFrame;
}
void CreatureAnimation::playOnce( ECreatureAnimType type )
{
setType(type);
once = true;
}
inline int getBorderStrength(float time)
{
float borderStrength = fabs(std::round(time) - time) * 2; // generate value in range 0-1
return static_cast<int>(borderStrength * 155 + 100); // scale to 0-255
}
static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base)
{
return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256));
}
static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2)
{
return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256;
}
static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over)
{
return ColorRGBA(
mixChannels(over.r, base.r, over.a, base.a),
mixChannels(over.g, base.g, over.a, base.a),
mixChannels(over.b, base.b, over.a, base.a),
ui8(over.a + base.a * (255 - over.a) / 256)
);
}
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target.resize(8);
target[0] = genShadow(0);
target[1] = genShadow(shadowAlpha / 2);
// colors 2 & 3 are not used in creatures
target[4] = genShadow(shadowAlpha);
target[5] = genBorderColor(getBorderStrength(elapsedTime), border);
target[6] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight)
{
ColorRGBA shadowTest = shifter.shiftColor(genShadow(128));
shadowAlpha = shadowTest.a;
size_t frame = static_cast<size_t>(floor(currentFrame));
std::shared_ptr<IImage> image;
if(facingRight)
image = forward->getImage(frame, size_t(type));
else
image = reverse->getImage(frame, size_t(type));
if(image)
{
IImage::SpecialPalette SpecialPalette;
genSpecialPalette(SpecialPalette);
image->setSpecialPallete(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES);
image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES);
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
}
}
void CreatureAnimation::playUntil(size_t frameIndex)
{
animationEnd = frameIndex;
}
int CreatureAnimation::framesInGroup(ECreatureAnimType group) const
{
return static_cast<int>(forward->size(size_t(group)));
}
bool CreatureAnimation::isDead() const
{
return getType() == ECreatureAnimType::DEAD
|| getType() == ECreatureAnimType::DEAD_RANGED;
}
bool CreatureAnimation::isDying() const
{
return getType() == ECreatureAnimType::DEATH
|| getType() == ECreatureAnimType::DEATH_RANGED;
}
bool CreatureAnimation::isDeadOrDying() const
{
return getType() == ECreatureAnimType::DEAD
|| getType() == ECreatureAnimType::DEATH
|| getType() == ECreatureAnimType::DEAD_RANGED
|| getType() == ECreatureAnimType::DEATH_RANGED;
}
bool CreatureAnimation::isIdle() const
{
return getType() == ECreatureAnimType::HOLDING
|| getType() == ECreatureAnimType::MOUSEON;
}
bool CreatureAnimation::isMoving() const
{
return getType() == ECreatureAnimType::MOVE_START
|| getType() == ECreatureAnimType::MOVING
|| getType() == ECreatureAnimType::MOVE_END
|| getType() == ECreatureAnimType::TURN_L
|| getType() == ECreatureAnimType::TURN_R;
}
bool CreatureAnimation::isShooting() const
{
return getType() == ECreatureAnimType::SHOOT_UP
|| getType() == ECreatureAnimType::SHOOT_FRONT
|| getType() == ECreatureAnimType::SHOOT_DOWN;
}

View File

@ -1,158 +1,158 @@
/*
* CCreatureAnimation.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 "../../lib/FunctionList.h"
#include "../../lib/Color.h"
#include "../widgets/Images.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
class CIntObject;
class CreatureAnimation;
class Canvas;
/// Namespace for some common controls of animations
namespace AnimationControls
{
/// get color for creature selection borders
ColorRGBA getBlueBorder();
ColorRGBA getGoldBorder();
ColorRGBA getNoBorder();
/// returns animation speed factor according to game settings,
/// slow speed is considered to be "base speed" and will return 1.0
float getAnimationSpeedFactor();
/// creates animation object with preset speed control
std::shared_ptr<CreatureAnimation> getAnimation(const CCreature * creature);
/// returns animation speed of specific group, taking in mind game setting (in frames per second)
float getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType groupID);
/// returns how far projectile should move per second, in pixels per second
float getProjectileSpeed();
/// returns how far projectile should move per second, in pixels per second
float getRayProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second, on a straight line, without parabola correction
float getCatapultSpeed();
/// returns speed of any spell effects, including any special effects like morale (in frames per second)
float getSpellEffectSpeed();
/// returns speed of movement animation across the screen, in tiles per second
float getMovementDistance(const CCreature * creature);
/// returns speed of movement animation across the screen, in pixels per seconds
float getFlightDistance(const CCreature * creature);
/// Returns total time for full fade-in effect on newly summoned creatures, in seconds
float getFadeInDuration();
/// Returns animation speed for obstacles, in frames per second
float getObstaclesSpeed();
}
/// Class which manages animations of creatures/units inside battles
/// TODO: split into constant image container and class that does *control* of animation
class CreatureAnimation : public CIntObject
{
public:
using TSpeedController = std::function<float(CreatureAnimation *, ECreatureAnimType)>;
private:
AnimationPath name;
/// animation for rendering stack in default orientation - facing right
std::shared_ptr<CAnimation> forward;
/// animation that has all its frames flipped for rendering stack facing left
std::shared_ptr<CAnimation> reverse;
int fullWidth;
int fullHeight;
/// speed of animation, measure in frames per second
float speed;
/// currently displayed frame. Float to allow H3-style animations where frames
/// don't display for integer number of frames
float currentFrame;
float animationEnd;
/// cumulative, real-time duration of animation. Used for effects like selection border
float elapsedTime;
///type of animation being displayed
ECreatureAnimType type;
/// current value of shadow transparency
uint8_t shadowAlpha;
/// border color, disabled if alpha = 0
ColorRGBA border;
TSpeedController speedController;
/// animation will be played once and the reset to idling
bool once;
void endAnimation();
void genSpecialPalette(IImage::SpecialPalette & target);
public:
/// function(s) that will be called when animation ends, after reset to 1st frame
/// NOTE that these functions will be fired only once
CFunctionList<void()> onAnimationReset;
int getWidth() const;
int getHeight() const;
/// Constructor
/// name - path to .def file, relative to SPRITES/ directory
/// controller - function that will return for how long *each* frame
/// in specified group of animation should be played, measured in seconds
CreatureAnimation(const AnimationPath & name_, TSpeedController speedController);
/// sets type of animation and resets framecount
void setType(ECreatureAnimType type);
/// returns currently rendered type of animation
ECreatureAnimType getType() const;
void nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight);
/// should be called every frame, return true when animation was reset to beginning
bool incrementFrame(float timePassed);
void setBorderColor(ColorRGBA palette);
/// Gets the current frame ID within current group.
float getCurrentFrame() const;
/// plays once given type of animation, then resets to idle
void playOnce(ECreatureAnimType type);
/// returns number of frames in selected animation type
int framesInGroup(ECreatureAnimType group) const;
void playUntil(size_t frameIndex);
/// helpers to classify current type of animation
bool isDead() const;
bool isDying() const;
bool isDeadOrDying() const;
bool isIdle() const;
bool isMoving() const;
bool isShooting() const;
};
/*
* CCreatureAnimation.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 "../../lib/FunctionList.h"
#include "../../lib/Color.h"
#include "../widgets/Images.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
class CIntObject;
class CreatureAnimation;
class Canvas;
/// Namespace for some common controls of animations
namespace AnimationControls
{
/// get color for creature selection borders
ColorRGBA getBlueBorder();
ColorRGBA getGoldBorder();
ColorRGBA getNoBorder();
/// returns animation speed factor according to game settings,
/// slow speed is considered to be "base speed" and will return 1.0
float getAnimationSpeedFactor();
/// creates animation object with preset speed control
std::shared_ptr<CreatureAnimation> getAnimation(const CCreature * creature);
/// returns animation speed of specific group, taking in mind game setting (in frames per second)
float getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType groupID);
/// returns how far projectile should move per second, in pixels per second
float getProjectileSpeed();
/// returns how far projectile should move per second, in pixels per second
float getRayProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second, on a straight line, without parabola correction
float getCatapultSpeed();
/// returns speed of any spell effects, including any special effects like morale (in frames per second)
float getSpellEffectSpeed();
/// returns speed of movement animation across the screen, in tiles per second
float getMovementDistance(const CCreature * creature);
/// returns speed of movement animation across the screen, in pixels per seconds
float getFlightDistance(const CCreature * creature);
/// Returns total time for full fade-in effect on newly summoned creatures, in seconds
float getFadeInDuration();
/// Returns animation speed for obstacles, in frames per second
float getObstaclesSpeed();
}
/// Class which manages animations of creatures/units inside battles
/// TODO: split into constant image container and class that does *control* of animation
class CreatureAnimation : public CIntObject
{
public:
using TSpeedController = std::function<float(CreatureAnimation *, ECreatureAnimType)>;
private:
AnimationPath name;
/// animation for rendering stack in default orientation - facing right
std::shared_ptr<CAnimation> forward;
/// animation that has all its frames flipped for rendering stack facing left
std::shared_ptr<CAnimation> reverse;
int fullWidth;
int fullHeight;
/// speed of animation, measure in frames per second
float speed;
/// currently displayed frame. Float to allow H3-style animations where frames
/// don't display for integer number of frames
float currentFrame;
float animationEnd;
/// cumulative, real-time duration of animation. Used for effects like selection border
float elapsedTime;
///type of animation being displayed
ECreatureAnimType type;
/// current value of shadow transparency
uint8_t shadowAlpha;
/// border color, disabled if alpha = 0
ColorRGBA border;
TSpeedController speedController;
/// animation will be played once and the reset to idling
bool once;
void endAnimation();
void genSpecialPalette(IImage::SpecialPalette & target);
public:
/// function(s) that will be called when animation ends, after reset to 1st frame
/// NOTE that these functions will be fired only once
CFunctionList<void()> onAnimationReset;
int getWidth() const;
int getHeight() const;
/// Constructor
/// name - path to .def file, relative to SPRITES/ directory
/// controller - function that will return for how long *each* frame
/// in specified group of animation should be played, measured in seconds
CreatureAnimation(const AnimationPath & name_, TSpeedController speedController);
/// sets type of animation and resets framecount
void setType(ECreatureAnimType type);
/// returns currently rendered type of animation
ECreatureAnimType getType() const;
void nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight);
/// should be called every frame, return true when animation was reset to beginning
bool incrementFrame(float timePassed);
void setBorderColor(ColorRGBA palette);
/// Gets the current frame ID within current group.
float getCurrentFrame() const;
/// plays once given type of animation, then resets to idle
void playOnce(ECreatureAnimType type);
/// returns number of frames in selected animation type
int framesInGroup(ECreatureAnimType group) const;
void playUntil(size_t frameIndex);
/// helpers to classify current type of animation
bool isDead() const;
bool isDying() const;
bool isDeadOrDying() const;
bool isIdle() const;
bool isMoving() const;
bool isShooting() const;
};

View File

@ -1,248 +1,248 @@
/*
* CGuiHandler.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 "CGuiHandler.h"
#include "../lib/CondSh.h"
#include "CIntObject.h"
#include "CursorHandler.h"
#include "ShortcutHandler.h"
#include "FramerateManager.h"
#include "WindowHandler.h"
#include "EventDispatcher.h"
#include "../eventsSDL/InputHandler.h"
#include "../CGameInfo.h"
#include "../render/Colors.h"
#include "../render/Graphics.h"
#include "../render/IFont.h"
#include "../render/EFont.h"
#include "../renderSDL/ScreenHandler.h"
#include "../renderSDL/RenderHandler.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../battle/BattleInterface.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h>
CGuiHandler GH;
static thread_local bool inGuiThread = false;
SObjectConstruction::SObjectConstruction(CIntObject *obj)
:myObj(obj)
{
GH.createdObj.push_front(obj);
GH.captureChildren = true;
}
SObjectConstruction::~SObjectConstruction()
{
assert(GH.createdObj.size());
assert(GH.createdObj.front() == myObj);
GH.createdObj.pop_front();
GH.captureChildren = GH.createdObj.size();
}
SSetCaptureState::SSetCaptureState(bool allow, ui8 actions)
{
previousCapture = GH.captureChildren;
GH.captureChildren = false;
prevActions = GH.defActionsDef;
GH.defActionsDef = actions;
}
SSetCaptureState::~SSetCaptureState()
{
GH.captureChildren = previousCapture;
GH.defActionsDef = prevActions;
}
void CGuiHandler::init()
{
inGuiThread = true;
inputHandlerInstance = std::make_unique<InputHandler>();
eventDispatcherInstance = std::make_unique<EventDispatcher>();
windowHandlerInstance = std::make_unique<WindowHandler>();
screenHandlerInstance = std::make_unique<ScreenHandler>();
renderHandlerInstance = std::make_unique<RenderHandler>();
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
}
void CGuiHandler::handleEvents()
{
events().dispatchTimer(framerate().getElapsedMilliseconds());
//player interface may want special event handling
if(nullptr != LOCPLINT && LOCPLINT->capturedAllEvents())
return;
input().processEvents();
}
void CGuiHandler::fakeMouseMove()
{
dispatchMainThread([](){
GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
});
}
void CGuiHandler::startTextInput(const Rect & whereInput)
{
input().startTextInput(whereInput);
}
void CGuiHandler::stopTextInput()
{
input().stopTextInput();
}
void CGuiHandler::renderFrame()
{
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(nullptr != curInt)
curInt->update();
if(settings["video"]["showfps"].Bool())
drawFPSCounter();
SDL_UpdateTexture(screenTexture, nullptr, screen->pixels, screen->pitch);
SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
CCS->curh->render();
windows().onFrameRendered();
}
SDL_RenderPresent(mainRenderer);
framerate().framerateDelay(); // holds a constant FPS
}
CGuiHandler::CGuiHandler()
: defActionsDef(0)
, captureChildren(false)
, curInt(nullptr)
, fakeStatusBar(std::make_shared<EmptyStatusBar>())
{
}
CGuiHandler::~CGuiHandler() = default;
ShortcutHandler & CGuiHandler::shortcuts()
{
assert(shortcutsHandlerInstance);
return *shortcutsHandlerInstance;
}
FramerateManager & CGuiHandler::framerate()
{
assert(framerateManagerInstance);
return *framerateManagerInstance;
}
bool CGuiHandler::isKeyboardCtrlDown() const
{
return inputHandlerInstance->isKeyboardCtrlDown();
}
bool CGuiHandler::isKeyboardAltDown() const
{
return inputHandlerInstance->isKeyboardAltDown();
}
bool CGuiHandler::isKeyboardShiftDown() const
{
return inputHandlerInstance->isKeyboardShiftDown();
}
const Point & CGuiHandler::getCursorPosition() const
{
return inputHandlerInstance->getCursorPosition();
}
Point CGuiHandler::screenDimensions() const
{
return Point(screen->w, screen->h);
}
void CGuiHandler::drawFPSCounter()
{
static SDL_Rect overlay = { 0, 0, 24, 24};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
std::string fps = std::to_string(framerate().getFramerate());
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(4, 2));
}
bool CGuiHandler::amIGuiThread()
{
return inGuiThread;
}
void CGuiHandler::dispatchMainThread(const std::function<void()> & functor)
{
inputHandlerInstance->dispatchMainThread(functor);
}
IScreenHandler & CGuiHandler::screenHandler()
{
return *screenHandlerInstance;
}
IRenderHandler & CGuiHandler::renderHandler()
{
return *renderHandlerInstance;
}
EventDispatcher & CGuiHandler::events()
{
return *eventDispatcherInstance;
}
InputHandler & CGuiHandler::input()
{
return *inputHandlerInstance;
}
WindowHandler & CGuiHandler::windows()
{
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();
}
/*
* CGuiHandler.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 "CGuiHandler.h"
#include "../lib/CondSh.h"
#include "CIntObject.h"
#include "CursorHandler.h"
#include "ShortcutHandler.h"
#include "FramerateManager.h"
#include "WindowHandler.h"
#include "EventDispatcher.h"
#include "../eventsSDL/InputHandler.h"
#include "../CGameInfo.h"
#include "../render/Colors.h"
#include "../render/Graphics.h"
#include "../render/IFont.h"
#include "../render/EFont.h"
#include "../renderSDL/ScreenHandler.h"
#include "../renderSDL/RenderHandler.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../battle/BattleInterface.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h>
CGuiHandler GH;
static thread_local bool inGuiThread = false;
SObjectConstruction::SObjectConstruction(CIntObject *obj)
:myObj(obj)
{
GH.createdObj.push_front(obj);
GH.captureChildren = true;
}
SObjectConstruction::~SObjectConstruction()
{
assert(GH.createdObj.size());
assert(GH.createdObj.front() == myObj);
GH.createdObj.pop_front();
GH.captureChildren = GH.createdObj.size();
}
SSetCaptureState::SSetCaptureState(bool allow, ui8 actions)
{
previousCapture = GH.captureChildren;
GH.captureChildren = false;
prevActions = GH.defActionsDef;
GH.defActionsDef = actions;
}
SSetCaptureState::~SSetCaptureState()
{
GH.captureChildren = previousCapture;
GH.defActionsDef = prevActions;
}
void CGuiHandler::init()
{
inGuiThread = true;
inputHandlerInstance = std::make_unique<InputHandler>();
eventDispatcherInstance = std::make_unique<EventDispatcher>();
windowHandlerInstance = std::make_unique<WindowHandler>();
screenHandlerInstance = std::make_unique<ScreenHandler>();
renderHandlerInstance = std::make_unique<RenderHandler>();
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
}
void CGuiHandler::handleEvents()
{
events().dispatchTimer(framerate().getElapsedMilliseconds());
//player interface may want special event handling
if(nullptr != LOCPLINT && LOCPLINT->capturedAllEvents())
return;
input().processEvents();
}
void CGuiHandler::fakeMouseMove()
{
dispatchMainThread([](){
GH.events().dispatchMouseMoved(Point(0, 0), GH.getCursorPosition());
});
}
void CGuiHandler::startTextInput(const Rect & whereInput)
{
input().startTextInput(whereInput);
}
void CGuiHandler::stopTextInput()
{
input().stopTextInput();
}
void CGuiHandler::renderFrame()
{
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(nullptr != curInt)
curInt->update();
if(settings["video"]["showfps"].Bool())
drawFPSCounter();
SDL_UpdateTexture(screenTexture, nullptr, screen->pixels, screen->pitch);
SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
CCS->curh->render();
windows().onFrameRendered();
}
SDL_RenderPresent(mainRenderer);
framerate().framerateDelay(); // holds a constant FPS
}
CGuiHandler::CGuiHandler()
: defActionsDef(0)
, captureChildren(false)
, curInt(nullptr)
, fakeStatusBar(std::make_shared<EmptyStatusBar>())
{
}
CGuiHandler::~CGuiHandler() = default;
ShortcutHandler & CGuiHandler::shortcuts()
{
assert(shortcutsHandlerInstance);
return *shortcutsHandlerInstance;
}
FramerateManager & CGuiHandler::framerate()
{
assert(framerateManagerInstance);
return *framerateManagerInstance;
}
bool CGuiHandler::isKeyboardCtrlDown() const
{
return inputHandlerInstance->isKeyboardCtrlDown();
}
bool CGuiHandler::isKeyboardAltDown() const
{
return inputHandlerInstance->isKeyboardAltDown();
}
bool CGuiHandler::isKeyboardShiftDown() const
{
return inputHandlerInstance->isKeyboardShiftDown();
}
const Point & CGuiHandler::getCursorPosition() const
{
return inputHandlerInstance->getCursorPosition();
}
Point CGuiHandler::screenDimensions() const
{
return Point(screen->w, screen->h);
}
void CGuiHandler::drawFPSCounter()
{
static SDL_Rect overlay = { 0, 0, 24, 24};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
std::string fps = std::to_string(framerate().getFramerate());
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(4, 2));
}
bool CGuiHandler::amIGuiThread()
{
return inGuiThread;
}
void CGuiHandler::dispatchMainThread(const std::function<void()> & functor)
{
inputHandlerInstance->dispatchMainThread(functor);
}
IScreenHandler & CGuiHandler::screenHandler()
{
return *screenHandlerInstance;
}
IRenderHandler & CGuiHandler::renderHandler()
{
return *renderHandlerInstance;
}
EventDispatcher & CGuiHandler::events()
{
return *eventDispatcherInstance;
}
InputHandler & CGuiHandler::input()
{
return *inputHandlerInstance;
}
WindowHandler & CGuiHandler::windows()
{
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();
}

View File

@ -1,130 +1,130 @@
/*
* CGuiHandler.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
template <typename T> struct CondSh;
class Point;
class Rect;
VCMI_LIB_NAMESPACE_END
enum class MouseButton;
class ShortcutHandler;
class FramerateManager;
class IStatusBar;
class CIntObject;
class IUpdateable;
class IShowActivatable;
class IRenderHandler;
class IScreenHandler;
class WindowHandler;
class EventDispatcher;
class InputHandler;
// Handles GUI logic and drawing
class CGuiHandler
{
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;
std::unique_ptr<ShortcutHandler> shortcutsHandlerInstance;
std::unique_ptr<WindowHandler> windowHandlerInstance;
std::unique_ptr<IScreenHandler> screenHandlerInstance;
std::unique_ptr<IRenderHandler> renderHandlerInstance;
std::unique_ptr<FramerateManager> framerateManagerInstance;
std::unique_ptr<EventDispatcher> eventDispatcherInstance;
std::unique_ptr<InputHandler> inputHandlerInstance;
public:
boost::mutex interfaceMutex;
/// returns current position of mouse cursor, relative to vcmi window
const Point & getCursorPosition() const;
ShortcutHandler & shortcuts();
FramerateManager & framerate();
EventDispatcher & events();
InputHandler & input();
/// 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 chosen keyboard key is currently pressed down
bool isKeyboardAltDown() const;
bool isKeyboardCtrlDown() const;
bool isKeyboardShiftDown() const;
void startTextInput(const Rect & where);
void stopTextInput();
IScreenHandler & screenHandler();
IRenderHandler & renderHandler();
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;
ui8 defActionsDef; //default auto actions
bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
std::list<CIntObject *> createdObj; //stack of objs being created
CGuiHandler();
~CGuiHandler();
void init();
void renderFrame();
/// called whenever user selects different resolution, requiring to center/resize all windows
void onScreenResize();
void handleEvents(); //takes events from queue and calls interested objects
void fakeMouseMove();
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
bool amIGuiThread();
/// Calls provided functor in main thread on next execution frame
void dispatchMainThread(const std::function<void()> & functor);
};
extern CGuiHandler GH; //global gui handler
struct SObjectConstruction
{
CIntObject *myObj;
SObjectConstruction(CIntObject *obj);
~SObjectConstruction();
};
struct SSetCaptureState
{
bool previousCapture;
ui8 prevActions;
SSetCaptureState(bool allow, ui8 actions);
~SSetCaptureState();
};
#define OBJ_CONSTRUCTION SObjectConstruction obj__i(this)
#define OBJ_CONSTRUCTION_TARGETED(obj) SObjectConstruction obj__i(obj)
#define OBJECT_CONSTRUCTION_CAPTURING(actions) defActions = actions; SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
#define OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(actions) SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
#define OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE defActions = 255 - DISPOSE; SSetCaptureState obj__i1(true, 255 - DISPOSE); SObjectConstruction obj__i(this)
/*
* CGuiHandler.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
template <typename T> struct CondSh;
class Point;
class Rect;
VCMI_LIB_NAMESPACE_END
enum class MouseButton;
class ShortcutHandler;
class FramerateManager;
class IStatusBar;
class CIntObject;
class IUpdateable;
class IShowActivatable;
class IRenderHandler;
class IScreenHandler;
class WindowHandler;
class EventDispatcher;
class InputHandler;
// Handles GUI logic and drawing
class CGuiHandler
{
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;
std::unique_ptr<ShortcutHandler> shortcutsHandlerInstance;
std::unique_ptr<WindowHandler> windowHandlerInstance;
std::unique_ptr<IScreenHandler> screenHandlerInstance;
std::unique_ptr<IRenderHandler> renderHandlerInstance;
std::unique_ptr<FramerateManager> framerateManagerInstance;
std::unique_ptr<EventDispatcher> eventDispatcherInstance;
std::unique_ptr<InputHandler> inputHandlerInstance;
public:
boost::mutex interfaceMutex;
/// returns current position of mouse cursor, relative to vcmi window
const Point & getCursorPosition() const;
ShortcutHandler & shortcuts();
FramerateManager & framerate();
EventDispatcher & events();
InputHandler & input();
/// 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 chosen keyboard key is currently pressed down
bool isKeyboardAltDown() const;
bool isKeyboardCtrlDown() const;
bool isKeyboardShiftDown() const;
void startTextInput(const Rect & where);
void stopTextInput();
IScreenHandler & screenHandler();
IRenderHandler & renderHandler();
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;
ui8 defActionsDef; //default auto actions
bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
std::list<CIntObject *> createdObj; //stack of objs being created
CGuiHandler();
~CGuiHandler();
void init();
void renderFrame();
/// called whenever user selects different resolution, requiring to center/resize all windows
void onScreenResize();
void handleEvents(); //takes events from queue and calls interested objects
void fakeMouseMove();
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
bool amIGuiThread();
/// Calls provided functor in main thread on next execution frame
void dispatchMainThread(const std::function<void()> & functor);
};
extern CGuiHandler GH; //global gui handler
struct SObjectConstruction
{
CIntObject *myObj;
SObjectConstruction(CIntObject *obj);
~SObjectConstruction();
};
struct SSetCaptureState
{
bool previousCapture;
ui8 prevActions;
SSetCaptureState(bool allow, ui8 actions);
~SSetCaptureState();
};
#define OBJ_CONSTRUCTION SObjectConstruction obj__i(this)
#define OBJ_CONSTRUCTION_TARGETED(obj) SObjectConstruction obj__i(obj)
#define OBJECT_CONSTRUCTION_CAPTURING(actions) defActions = actions; SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
#define OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(actions) SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
#define OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE defActions = 255 - DISPOSE; SSetCaptureState obj__i1(true, 255 - DISPOSE); SObjectConstruction obj__i(this)

View File

@ -1,346 +1,346 @@
/*
* CIntObject.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 "CIntObject.h"
#include "CGuiHandler.h"
#include "WindowHandler.h"
#include "EventDispatcher.h"
#include "Shortcut.h"
#include "../render/Canvas.h"
#include "../windows/CMessage.h"
#include "../CMT.h"
CIntObject::CIntObject(int used_, Point pos_):
parent_m(nullptr),
parent(parent_m),
redrawParent(false),
inputEnabled(true),
used(used_),
recActions(GH.defActionsDef),
defActions(GH.defActionsDef),
pos(pos_, Point())
{
if(GH.captureChildren)
GH.createdObj.front()->addChild(this, true);
}
CIntObject::~CIntObject()
{
if(isActive())
deactivate();
while(!children.empty())
{
if((defActions & DISPOSE) && (children.front()->recActions & DISPOSE))
delete children.front();
else
removeChild(children.front());
}
if(parent_m)
parent_m->removeChild(this);
}
void CIntObject::show(Canvas & to)
{
if(defActions & UPDATE)
for(auto & elem : children)
if(elem->recActions & UPDATE)
elem->show(to);
}
void CIntObject::showAll(Canvas & to)
{
if(defActions & SHOWALL)
{
for(auto & elem : children)
if(elem->recActions & SHOWALL)
elem->showAll(to);
}
}
void CIntObject::activate()
{
if (isActive())
return;
if (inputEnabled)
activateEvents(used | GENERAL);
else
activateEvents(GENERAL);
assert(isActive());
if(defActions & ACTIVATE)
for(auto & elem : children)
if(elem->recActions & ACTIVATE)
elem->activate();
}
void CIntObject::deactivate()
{
if (!isActive())
return;
deactivateEvents(used | GENERAL);
assert(!isActive());
if(defActions & DEACTIVATE)
for(auto & elem : children)
if(elem->recActions & DEACTIVATE)
elem->deactivate();
}
void CIntObject::addUsedEvents(ui16 newActions)
{
if (isActive() && inputEnabled)
activateEvents(~used & newActions);
used |= newActions;
}
void CIntObject::removeUsedEvents(ui16 newActions)
{
if (isActive())
deactivateEvents(used & newActions);
used &= ~newActions;
}
void CIntObject::disable()
{
if(isActive())
deactivate();
recActions = DISPOSE;
}
void CIntObject::enable()
{
if(!isActive() && (!parent_m || parent_m->isActive()))
{
activate();
redraw();
}
recActions = 255;
}
void CIntObject::setEnabled(bool on)
{
if (on)
enable();
else
disable();
}
void CIntObject::setInputEnabled(bool on)
{
if (inputEnabled == on)
return;
inputEnabled = on;
if (isActive())
{
assert((used & GENERAL) == 0);
if (on)
activateEvents(used);
else
deactivateEvents(used);
}
for(auto & elem : children)
elem->setInputEnabled(on);
}
void CIntObject::setRedrawParent(bool on)
{
redrawParent = on;
}
void CIntObject::fitToScreen(int borderWidth, bool propagate)
{
Point newPos = pos.topLeft();
vstd::amax(newPos.x, borderWidth);
vstd::amax(newPos.y, borderWidth);
vstd::amin(newPos.x, GH.screenDimensions().x - borderWidth - pos.w);
vstd::amin(newPos.y, GH.screenDimensions().y - borderWidth - pos.h);
if (newPos != pos.topLeft())
moveTo(newPos, propagate);
}
void CIntObject::moveBy(const Point & p, bool propagate)
{
pos.x += p.x;
pos.y += p.y;
if(propagate)
for(auto & elem : children)
elem->moveBy(p, propagate);
}
void CIntObject::moveTo(const Point & p, bool propagate)
{
moveBy(Point(p.x - pos.x, p.y - pos.y), propagate);
}
void CIntObject::addChild(CIntObject * child, bool adjustPosition)
{
if (vstd::contains(children, child))
{
return;
}
if (child->parent_m)
{
child->parent_m->removeChild(child, adjustPosition);
}
children.push_back(child);
child->parent_m = this;
if(adjustPosition)
child->moveBy(pos.topLeft(), adjustPosition);
if (inputEnabled != child->inputEnabled)
child->setInputEnabled(inputEnabled);
if (!isActive() && child->isActive())
child->deactivate();
if (isActive()&& !child->isActive())
child->activate();
}
void CIntObject::removeChild(CIntObject * child, bool adjustPosition)
{
if (!child)
return;
if(!vstd::contains(children, child))
throw std::runtime_error("Wrong child object");
if(child->parent_m != this)
throw std::runtime_error("Wrong child object");
children -= child;
child->parent_m = nullptr;
if(adjustPosition)
child->pos -= pos.topLeft();
}
void CIntObject::redraw()
{
//currently most of calls come from active objects so this check won't affect them
//it should fix glitches when called by inactive elements located below active window
if (isActive())
{
if (parent_m && redrawParent)
{
parent_m->redraw();
}
else
{
Canvas buffer = Canvas::createFromSurface(screenBuf);
showAll(buffer);
if(screenBuf != screen)
{
Canvas screenBuffer = Canvas::createFromSurface(screen);
showAll(screenBuffer);
}
}
}
}
bool CIntObject::receiveEvent(const Point & position, int eventType) const
{
return pos.isInside(position);
}
const Rect & CIntObject::getPosition() const
{
return pos;
}
void CIntObject::onScreenResize()
{
center(pos, true);
}
bool CIntObject::isPopupWindow() const
{
return false;
}
const Rect & CIntObject::center( const Rect &r, bool propagate )
{
pos.w = r.w;
pos.h = r.h;
return center(Point(GH.screenDimensions().x/2, GH.screenDimensions().y/2), propagate);
}
const Rect & CIntObject::center( bool propagate )
{
return center(pos, propagate);
}
const Rect & CIntObject::center(const Point & p, bool propagate)
{
moveBy(Point(p.x - pos.w/2 - pos.x,
p.y - pos.h/2 - pos.y),
propagate);
return pos;
}
bool CIntObject::captureThisKey(EShortcut key)
{
return false;
}
CKeyShortcut::CKeyShortcut()
: assignedKey(EShortcut::NONE)
, shortcutPressed(false)
{}
CKeyShortcut::CKeyShortcut(EShortcut key)
: assignedKey(key)
, shortcutPressed(false)
{
}
void CKeyShortcut::keyPressed(EShortcut key)
{
if( assignedKey == key && assignedKey != EShortcut::NONE && !shortcutPressed)
{
shortcutPressed = true;
clickPressed(GH.getCursorPosition());
}
}
void CKeyShortcut::keyReleased(EShortcut key)
{
if( assignedKey == key && assignedKey != EShortcut::NONE && shortcutPressed)
{
shortcutPressed = false;
clickReleased(GH.getCursorPosition());
}
}
WindowBase::WindowBase(int used_, Point pos_)
: CIntObject(used_, pos_)
{
}
void WindowBase::close()
{
if(!GH.windows().isTopWindow(this))
logGlobal->error("Only top interface must be closed");
GH.windows().popWindows(1);
}
/*
* CIntObject.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 "CIntObject.h"
#include "CGuiHandler.h"
#include "WindowHandler.h"
#include "EventDispatcher.h"
#include "Shortcut.h"
#include "../render/Canvas.h"
#include "../windows/CMessage.h"
#include "../CMT.h"
CIntObject::CIntObject(int used_, Point pos_):
parent_m(nullptr),
parent(parent_m),
redrawParent(false),
inputEnabled(true),
used(used_),
recActions(GH.defActionsDef),
defActions(GH.defActionsDef),
pos(pos_, Point())
{
if(GH.captureChildren)
GH.createdObj.front()->addChild(this, true);
}
CIntObject::~CIntObject()
{
if(isActive())
deactivate();
while(!children.empty())
{
if((defActions & DISPOSE) && (children.front()->recActions & DISPOSE))
delete children.front();
else
removeChild(children.front());
}
if(parent_m)
parent_m->removeChild(this);
}
void CIntObject::show(Canvas & to)
{
if(defActions & UPDATE)
for(auto & elem : children)
if(elem->recActions & UPDATE)
elem->show(to);
}
void CIntObject::showAll(Canvas & to)
{
if(defActions & SHOWALL)
{
for(auto & elem : children)
if(elem->recActions & SHOWALL)
elem->showAll(to);
}
}
void CIntObject::activate()
{
if (isActive())
return;
if (inputEnabled)
activateEvents(used | GENERAL);
else
activateEvents(GENERAL);
assert(isActive());
if(defActions & ACTIVATE)
for(auto & elem : children)
if(elem->recActions & ACTIVATE)
elem->activate();
}
void CIntObject::deactivate()
{
if (!isActive())
return;
deactivateEvents(used | GENERAL);
assert(!isActive());
if(defActions & DEACTIVATE)
for(auto & elem : children)
if(elem->recActions & DEACTIVATE)
elem->deactivate();
}
void CIntObject::addUsedEvents(ui16 newActions)
{
if (isActive() && inputEnabled)
activateEvents(~used & newActions);
used |= newActions;
}
void CIntObject::removeUsedEvents(ui16 newActions)
{
if (isActive())
deactivateEvents(used & newActions);
used &= ~newActions;
}
void CIntObject::disable()
{
if(isActive())
deactivate();
recActions = DISPOSE;
}
void CIntObject::enable()
{
if(!isActive() && (!parent_m || parent_m->isActive()))
{
activate();
redraw();
}
recActions = 255;
}
void CIntObject::setEnabled(bool on)
{
if (on)
enable();
else
disable();
}
void CIntObject::setInputEnabled(bool on)
{
if (inputEnabled == on)
return;
inputEnabled = on;
if (isActive())
{
assert((used & GENERAL) == 0);
if (on)
activateEvents(used);
else
deactivateEvents(used);
}
for(auto & elem : children)
elem->setInputEnabled(on);
}
void CIntObject::setRedrawParent(bool on)
{
redrawParent = on;
}
void CIntObject::fitToScreen(int borderWidth, bool propagate)
{
Point newPos = pos.topLeft();
vstd::amax(newPos.x, borderWidth);
vstd::amax(newPos.y, borderWidth);
vstd::amin(newPos.x, GH.screenDimensions().x - borderWidth - pos.w);
vstd::amin(newPos.y, GH.screenDimensions().y - borderWidth - pos.h);
if (newPos != pos.topLeft())
moveTo(newPos, propagate);
}
void CIntObject::moveBy(const Point & p, bool propagate)
{
pos.x += p.x;
pos.y += p.y;
if(propagate)
for(auto & elem : children)
elem->moveBy(p, propagate);
}
void CIntObject::moveTo(const Point & p, bool propagate)
{
moveBy(Point(p.x - pos.x, p.y - pos.y), propagate);
}
void CIntObject::addChild(CIntObject * child, bool adjustPosition)
{
if (vstd::contains(children, child))
{
return;
}
if (child->parent_m)
{
child->parent_m->removeChild(child, adjustPosition);
}
children.push_back(child);
child->parent_m = this;
if(adjustPosition)
child->moveBy(pos.topLeft(), adjustPosition);
if (inputEnabled != child->inputEnabled)
child->setInputEnabled(inputEnabled);
if (!isActive() && child->isActive())
child->deactivate();
if (isActive()&& !child->isActive())
child->activate();
}
void CIntObject::removeChild(CIntObject * child, bool adjustPosition)
{
if (!child)
return;
if(!vstd::contains(children, child))
throw std::runtime_error("Wrong child object");
if(child->parent_m != this)
throw std::runtime_error("Wrong child object");
children -= child;
child->parent_m = nullptr;
if(adjustPosition)
child->pos -= pos.topLeft();
}
void CIntObject::redraw()
{
//currently most of calls come from active objects so this check won't affect them
//it should fix glitches when called by inactive elements located below active window
if (isActive())
{
if (parent_m && redrawParent)
{
parent_m->redraw();
}
else
{
Canvas buffer = Canvas::createFromSurface(screenBuf);
showAll(buffer);
if(screenBuf != screen)
{
Canvas screenBuffer = Canvas::createFromSurface(screen);
showAll(screenBuffer);
}
}
}
}
bool CIntObject::receiveEvent(const Point & position, int eventType) const
{
return pos.isInside(position);
}
const Rect & CIntObject::getPosition() const
{
return pos;
}
void CIntObject::onScreenResize()
{
center(pos, true);
}
bool CIntObject::isPopupWindow() const
{
return false;
}
const Rect & CIntObject::center( const Rect &r, bool propagate )
{
pos.w = r.w;
pos.h = r.h;
return center(Point(GH.screenDimensions().x/2, GH.screenDimensions().y/2), propagate);
}
const Rect & CIntObject::center( bool propagate )
{
return center(pos, propagate);
}
const Rect & CIntObject::center(const Point & p, bool propagate)
{
moveBy(Point(p.x - pos.w/2 - pos.x,
p.y - pos.h/2 - pos.y),
propagate);
return pos;
}
bool CIntObject::captureThisKey(EShortcut key)
{
return false;
}
CKeyShortcut::CKeyShortcut()
: assignedKey(EShortcut::NONE)
, shortcutPressed(false)
{}
CKeyShortcut::CKeyShortcut(EShortcut key)
: assignedKey(key)
, shortcutPressed(false)
{
}
void CKeyShortcut::keyPressed(EShortcut key)
{
if( assignedKey == key && assignedKey != EShortcut::NONE && !shortcutPressed)
{
shortcutPressed = true;
clickPressed(GH.getCursorPosition());
}
}
void CKeyShortcut::keyReleased(EShortcut key)
{
if( assignedKey == key && assignedKey != EShortcut::NONE && shortcutPressed)
{
shortcutPressed = false;
clickReleased(GH.getCursorPosition());
}
}
WindowBase::WindowBase(int used_, Point pos_)
: CIntObject(used_, pos_)
{
}
void WindowBase::close()
{
if(!GH.windows().isTopWindow(this))
logGlobal->error("Only top interface must be closed");
GH.windows().popWindows(1);
}

View File

@ -1,191 +1,191 @@
/*
* CIntObject.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 "EventsReceiver.h"
#include "../../lib/Rect.h"
#include "../../lib/Color.h"
#include "../../lib/GameConstants.h"
class CGuiHandler;
class CPicture;
class Canvas;
class IUpdateable
{
public:
virtual void update()=0;
virtual ~IUpdateable() = default;
};
class IShowActivatable
{
public:
virtual void activate()=0;
virtual void deactivate()=0;
virtual void redraw()=0;
virtual void show(Canvas & to) = 0;
virtual void showAll(Canvas & to) = 0;
virtual bool isPopupWindow() const = 0;
virtual void onScreenResize() = 0;
virtual ~IShowActivatable() = default;
};
// Base UI element
class CIntObject : public IShowActivatable, public AEventsReceiver //interface object
{
ui16 used;
//non-const versions of fields to allow changing them in CIntObject
CIntObject *parent_m; //parent object
bool inputEnabled;
bool redrawParent;
public:
std::vector<CIntObject *> children;
/// read-only parent access. May not be a "clean" solution but allows some compatibility
CIntObject * const & parent;
/// position of object on the screen. Please do not modify this anywhere but in constructor - use moveBy\moveTo instead
/*const*/ Rect pos;
CIntObject(int used=0, Point offset=Point());
virtual ~CIntObject();
bool captureThisKey(EShortcut key) override;
void addUsedEvents(ui16 newActions);
void removeUsedEvents(ui16 newActions);
enum {ACTIVATE=1, DEACTIVATE=2, UPDATE=4, SHOWALL=8, DISPOSE=16, SHARE_POS=32};
ui8 defActions; //which calls will be tried to be redirected to children
ui8 recActions; //which calls we allow to receive from parent
/// 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);
/// Block (or allow) all user input, e.g. mouse/keyboard/touch without hiding element
void setInputEnabled(bool on);
/// Mark this input as one that requires parent redraw on update,
/// for example if current control might have semi-transparent elements and requires redrawing of background
void setRedrawParent(bool on);
// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
// usually used automatically by parent
void activate() override;
void deactivate() override;
//called each frame to update screen
void show(Canvas & to) override;
//called on complete redraw only
void showAll(Canvas & to) override;
//request complete redraw of this object
void redraw() override;
/// returns true if this element is a popup window
/// called only for windows
bool isPopupWindow() const override;
/// called only for windows whenever screen size changes
/// default behavior is to re-center, can be overriden
void onScreenResize() override;
/// returns true if UI elements wants to handle event of specific type (LCLICK, SHOW_POPUP ...)
/// by default, usedEvents inside UI elements are always handled
bool receiveEvent(const Point & position, int eventType) const override;
const Rect & getPosition() const override;
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
void fitToScreen(int borderWidth, bool propagate = true); //moves window to fit into screen
void moveBy(const Point &p, bool propagate = true);
void moveTo(const Point &p, bool propagate = true);//move this to new position, coordinates are absolute (0,0 is topleft screen corner)
void addChild(CIntObject *child, bool adjustPosition = false);
void removeChild(CIntObject *child, bool adjustPosition = false);
};
/// Class for binding keys to left mouse button clicks
/// Classes wanting use it should have it as one of their base classes
class CKeyShortcut : public virtual CIntObject
{
bool shortcutPressed;
public:
EShortcut assignedKey;
CKeyShortcut();
CKeyShortcut(EShortcut key);
void keyPressed(EShortcut key) override;
void keyReleased(EShortcut key) override;
};
class WindowBase : public CIntObject
{
public:
WindowBase(int used_ = 0, Point pos_ = Point());
protected:
virtual void close();
};
class IGarrisonHolder
{
public:
virtual void updateGarrisons() = 0;
};
class ITownHolder
{
public:
virtual void buildChanged() = 0;
};
class IStatusBar
{
public:
virtual ~IStatusBar() = default;
/// set current text for the status bar
virtual void write(const std::string & text) = 0;
/// remove any current text from the status bar
virtual void clear() = 0;
/// remove text from status bar if current text matches tested text
virtual void clearIfMatching(const std::string & testedText) = 0;
/// enables mode for entering text instead of showing hover text
virtual void setEnteringMode(bool on) = 0;
/// overrides hover text from controls with text entered into in-game console (for chat/cheats)
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){};
};
/*
* CIntObject.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 "EventsReceiver.h"
#include "../../lib/Rect.h"
#include "../../lib/Color.h"
#include "../../lib/GameConstants.h"
class CGuiHandler;
class CPicture;
class Canvas;
class IUpdateable
{
public:
virtual void update()=0;
virtual ~IUpdateable() = default;
};
class IShowActivatable
{
public:
virtual void activate()=0;
virtual void deactivate()=0;
virtual void redraw()=0;
virtual void show(Canvas & to) = 0;
virtual void showAll(Canvas & to) = 0;
virtual bool isPopupWindow() const = 0;
virtual void onScreenResize() = 0;
virtual ~IShowActivatable() = default;
};
// Base UI element
class CIntObject : public IShowActivatable, public AEventsReceiver //interface object
{
ui16 used;
//non-const versions of fields to allow changing them in CIntObject
CIntObject *parent_m; //parent object
bool inputEnabled;
bool redrawParent;
public:
std::vector<CIntObject *> children;
/// read-only parent access. May not be a "clean" solution but allows some compatibility
CIntObject * const & parent;
/// position of object on the screen. Please do not modify this anywhere but in constructor - use moveBy\moveTo instead
/*const*/ Rect pos;
CIntObject(int used=0, Point offset=Point());
virtual ~CIntObject();
bool captureThisKey(EShortcut key) override;
void addUsedEvents(ui16 newActions);
void removeUsedEvents(ui16 newActions);
enum {ACTIVATE=1, DEACTIVATE=2, UPDATE=4, SHOWALL=8, DISPOSE=16, SHARE_POS=32};
ui8 defActions; //which calls will be tried to be redirected to children
ui8 recActions; //which calls we allow to receive from parent
/// 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);
/// Block (or allow) all user input, e.g. mouse/keyboard/touch without hiding element
void setInputEnabled(bool on);
/// Mark this input as one that requires parent redraw on update,
/// for example if current control might have semi-transparent elements and requires redrawing of background
void setRedrawParent(bool on);
// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
// usually used automatically by parent
void activate() override;
void deactivate() override;
//called each frame to update screen
void show(Canvas & to) override;
//called on complete redraw only
void showAll(Canvas & to) override;
//request complete redraw of this object
void redraw() override;
/// returns true if this element is a popup window
/// called only for windows
bool isPopupWindow() const override;
/// called only for windows whenever screen size changes
/// default behavior is to re-center, can be overriden
void onScreenResize() override;
/// returns true if UI elements wants to handle event of specific type (LCLICK, SHOW_POPUP ...)
/// by default, usedEvents inside UI elements are always handled
bool receiveEvent(const Point & position, int eventType) const override;
const Rect & getPosition() const override;
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
void fitToScreen(int borderWidth, bool propagate = true); //moves window to fit into screen
void moveBy(const Point &p, bool propagate = true);
void moveTo(const Point &p, bool propagate = true);//move this to new position, coordinates are absolute (0,0 is topleft screen corner)
void addChild(CIntObject *child, bool adjustPosition = false);
void removeChild(CIntObject *child, bool adjustPosition = false);
};
/// Class for binding keys to left mouse button clicks
/// Classes wanting use it should have it as one of their base classes
class CKeyShortcut : public virtual CIntObject
{
bool shortcutPressed;
public:
EShortcut assignedKey;
CKeyShortcut();
CKeyShortcut(EShortcut key);
void keyPressed(EShortcut key) override;
void keyReleased(EShortcut key) override;
};
class WindowBase : public CIntObject
{
public:
WindowBase(int used_ = 0, Point pos_ = Point());
protected:
virtual void close();
};
class IGarrisonHolder
{
public:
virtual void updateGarrisons() = 0;
};
class ITownHolder
{
public:
virtual void buildChanged() = 0;
};
class IStatusBar
{
public:
virtual ~IStatusBar() = default;
/// set current text for the status bar
virtual void write(const std::string & text) = 0;
/// remove any current text from the status bar
virtual void clear() = 0;
/// remove text from status bar if current text matches tested text
virtual void clearIfMatching(const std::string & testedText) = 0;
/// enables mode for entering text instead of showing hover text
virtual void setEnteringMode(bool on) = 0;
/// overrides hover text from controls with text entered into in-game console (for chat/cheats)
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){};
};

View File

@ -1,292 +1,292 @@
/*
* CCursorHandler.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 "CursorHandler.h"
#include "CGuiHandler.h"
#include "FramerateManager.h"
#include "../renderSDL/CursorSoftware.h"
#include "../renderSDL/CursorHardware.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include "../render/IRenderHandler.h"
#include "../../lib/CConfigHandler.h"
std::unique_ptr<ICursor> CursorHandler::createCursor()
{
#if defined(VCMI_MOBILE)
if (settings["general"]["userRelativePointer"].Bool())
return std::make_unique<CursorSoftware>();
#endif
if (settings["video"]["cursor"].String() == "hardware")
return std::make_unique<CursorHardware>();
assert(settings["video"]["cursor"].String() == "software");
return std::make_unique<CursorSoftware>();
}
CursorHandler::CursorHandler()
: cursor(createCursor())
, frameTime(0.f)
, showing(false)
, pos(0,0)
{
type = Cursor::Type::DEFAULT;
dndObject = nullptr;
cursors =
{
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"))
};
for (auto & cursor : cursors)
cursor->preload();
set(Cursor::Map::POINTER);
}
CursorHandler::~CursorHandler() = default;
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
{
assert(dndObject == nullptr);
if (type == this->type && index == this->frame)
return;
this->type = type;
this->frame = index;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::set(Cursor::Default index)
{
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Map index)
{
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Combat index)
{
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Spellcast index)
{
//Note: this is animated cursor, ignore specified frame and only change type
changeGraphic(Cursor::Type::SPELLBOOK, frame);
}
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
{
dndObject = image;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index)
{
auto anim = GH.renderHandler().loadAnimation(path);
anim->load(index);
dragAndDropCursor(anim->getImage(index));
}
void CursorHandler::cursorMove(const int & x, const int & y)
{
pos.x = x;
pos.y = y;
cursor->setCursorPosition(pos);
}
Point CursorHandler::getPivotOffsetDefault(size_t index)
{
return {0, 0};
}
Point CursorHandler::getPivotOffsetMap(size_t index)
{
static const std::array<Point, 43> offsets = {{
{ 0, 0}, // POINTER = 0,
{ 0, 0}, // HOURGLASS = 1,
{ 12, 10}, // HERO = 2,
{ 12, 12}, // TOWN = 3,
{ 15, 13}, // T1_MOVE = 4,
{ 13, 13}, // T1_ATTACK = 5,
{ 16, 32}, // T1_SAIL = 6,
{ 13, 20}, // T1_DISEMBARK = 7,
{ 8, 9}, // T1_EXCHANGE = 8,
{ 14, 16}, // T1_VISIT = 9,
{ 15, 13}, // T2_MOVE = 10,
{ 13, 13}, // T2_ATTACK = 11,
{ 16, 32}, // T2_SAIL = 12,
{ 13, 20}, // T2_DISEMBARK = 13,
{ 8, 9}, // T2_EXCHANGE = 14,
{ 14, 16}, // T2_VISIT = 15,
{ 15, 13}, // T3_MOVE = 16,
{ 13, 13}, // T3_ATTACK = 17,
{ 16, 32}, // T3_SAIL = 18,
{ 13, 20}, // T3_DISEMBARK = 19,
{ 8, 9}, // T3_EXCHANGE = 20,
{ 14, 16}, // T3_VISIT = 21,
{ 15, 13}, // T4_MOVE = 22,
{ 13, 13}, // T4_ATTACK = 23,
{ 16, 32}, // T4_SAIL = 24,
{ 13, 20}, // T4_DISEMBARK = 25,
{ 8, 9}, // T4_EXCHANGE = 26,
{ 14, 16}, // T4_VISIT = 27,
{ 16, 32}, // T1_SAIL_VISIT = 28,
{ 16, 32}, // T2_SAIL_VISIT = 29,
{ 16, 32}, // T3_SAIL_VISIT = 30,
{ 16, 32}, // T4_SAIL_VISIT = 31,
{ 6, 1}, // SCROLL_NORTH = 32,
{ 16, 2}, // SCROLL_NORTHEAST = 33,
{ 21, 6}, // SCROLL_EAST = 34,
{ 16, 16}, // SCROLL_SOUTHEAST = 35,
{ 6, 21}, // SCROLL_SOUTH = 36,
{ 1, 16}, // SCROLL_SOUTHWEST = 37,
{ 1, 5}, // SCROLL_WEST = 38,
{ 2, 1}, // SCROLL_NORTHWEST = 39,
{ 0, 0}, // POINTER_COPY = 40,
{ 14, 16}, // TELEPORT = 41,
{ 20, 20}, // SCUTTLE_BOAT = 42
}};
assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetCombat(size_t index)
{
static const std::array<Point, 20> offsets = {{
{ 12, 12 }, // BLOCKED = 0,
{ 10, 14 }, // MOVE = 1,
{ 14, 14 }, // FLY = 2,
{ 12, 12 }, // SHOOT = 3,
{ 12, 12 }, // HERO = 4,
{ 8, 12 }, // QUERY = 5,
{ 0, 0 }, // POINTER = 6,
{ 21, 0 }, // HIT_NORTHEAST = 7,
{ 31, 5 }, // HIT_EAST = 8,
{ 21, 21 }, // HIT_SOUTHEAST = 9,
{ 0, 21 }, // HIT_SOUTHWEST = 10,
{ 0, 5 }, // HIT_WEST = 11,
{ 0, 0 }, // HIT_NORTHWEST = 12,
{ 6, 0 }, // HIT_NORTH = 13,
{ 6, 31 }, // HIT_SOUTH = 14,
{ 14, 0 }, // SHOOT_PENALTY = 15,
{ 12, 12 }, // SHOOT_CATAPULT = 16,
{ 12, 12 }, // HEAL = 17,
{ 12, 12 }, // SACRIFICE = 18,
{ 14, 20 }, // TELEPORT = 19
}};
assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetSpellcast()
{
return { 18, 28};
}
Point CursorHandler::getPivotOffset()
{
if (dndObject)
return dndObject->dimensions() / 2;
switch (type) {
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
};
assert(0);
return {0, 0};
}
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
{
if (dndObject)
return dndObject;
return cursors[static_cast<size_t>(type)]->getImage(frame);
}
void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
while (frameTime >= frameDisplayDuration)
{
frameTime -= frameDisplayDuration;
newFrame++;
}
auto & animation = cursors.at(static_cast<size_t>(type));
while (newFrame >= animation->size())
newFrame -= animation->size();
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
}
void CursorHandler::render()
{
if(!showing)
return;
if (type == Cursor::Type::SPELLBOOK)
updateSpellcastCursor();
cursor->render();
}
void CursorHandler::hide()
{
if (!showing)
return;
showing = false;
cursor->setVisible(false);
}
void CursorHandler::show()
{
if (showing)
return;
showing = true;
cursor->setVisible(true);
}
/*
* CCursorHandler.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 "CursorHandler.h"
#include "CGuiHandler.h"
#include "FramerateManager.h"
#include "../renderSDL/CursorSoftware.h"
#include "../renderSDL/CursorHardware.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include "../render/IRenderHandler.h"
#include "../../lib/CConfigHandler.h"
std::unique_ptr<ICursor> CursorHandler::createCursor()
{
#if defined(VCMI_MOBILE)
if (settings["general"]["userRelativePointer"].Bool())
return std::make_unique<CursorSoftware>();
#endif
if (settings["video"]["cursor"].String() == "hardware")
return std::make_unique<CursorHardware>();
assert(settings["video"]["cursor"].String() == "software");
return std::make_unique<CursorSoftware>();
}
CursorHandler::CursorHandler()
: cursor(createCursor())
, frameTime(0.f)
, showing(false)
, pos(0,0)
{
type = Cursor::Type::DEFAULT;
dndObject = nullptr;
cursors =
{
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT")),
GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"))
};
for (auto & cursor : cursors)
cursor->preload();
set(Cursor::Map::POINTER);
}
CursorHandler::~CursorHandler() = default;
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
{
assert(dndObject == nullptr);
if (type == this->type && index == this->frame)
return;
this->type = type;
this->frame = index;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::set(Cursor::Default index)
{
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Map index)
{
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Combat index)
{
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Spellcast index)
{
//Note: this is animated cursor, ignore specified frame and only change type
changeGraphic(Cursor::Type::SPELLBOOK, frame);
}
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
{
dndObject = image;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index)
{
auto anim = GH.renderHandler().loadAnimation(path);
anim->load(index);
dragAndDropCursor(anim->getImage(index));
}
void CursorHandler::cursorMove(const int & x, const int & y)
{
pos.x = x;
pos.y = y;
cursor->setCursorPosition(pos);
}
Point CursorHandler::getPivotOffsetDefault(size_t index)
{
return {0, 0};
}
Point CursorHandler::getPivotOffsetMap(size_t index)
{
static const std::array<Point, 43> offsets = {{
{ 0, 0}, // POINTER = 0,
{ 0, 0}, // HOURGLASS = 1,
{ 12, 10}, // HERO = 2,
{ 12, 12}, // TOWN = 3,
{ 15, 13}, // T1_MOVE = 4,
{ 13, 13}, // T1_ATTACK = 5,
{ 16, 32}, // T1_SAIL = 6,
{ 13, 20}, // T1_DISEMBARK = 7,
{ 8, 9}, // T1_EXCHANGE = 8,
{ 14, 16}, // T1_VISIT = 9,
{ 15, 13}, // T2_MOVE = 10,
{ 13, 13}, // T2_ATTACK = 11,
{ 16, 32}, // T2_SAIL = 12,
{ 13, 20}, // T2_DISEMBARK = 13,
{ 8, 9}, // T2_EXCHANGE = 14,
{ 14, 16}, // T2_VISIT = 15,
{ 15, 13}, // T3_MOVE = 16,
{ 13, 13}, // T3_ATTACK = 17,
{ 16, 32}, // T3_SAIL = 18,
{ 13, 20}, // T3_DISEMBARK = 19,
{ 8, 9}, // T3_EXCHANGE = 20,
{ 14, 16}, // T3_VISIT = 21,
{ 15, 13}, // T4_MOVE = 22,
{ 13, 13}, // T4_ATTACK = 23,
{ 16, 32}, // T4_SAIL = 24,
{ 13, 20}, // T4_DISEMBARK = 25,
{ 8, 9}, // T4_EXCHANGE = 26,
{ 14, 16}, // T4_VISIT = 27,
{ 16, 32}, // T1_SAIL_VISIT = 28,
{ 16, 32}, // T2_SAIL_VISIT = 29,
{ 16, 32}, // T3_SAIL_VISIT = 30,
{ 16, 32}, // T4_SAIL_VISIT = 31,
{ 6, 1}, // SCROLL_NORTH = 32,
{ 16, 2}, // SCROLL_NORTHEAST = 33,
{ 21, 6}, // SCROLL_EAST = 34,
{ 16, 16}, // SCROLL_SOUTHEAST = 35,
{ 6, 21}, // SCROLL_SOUTH = 36,
{ 1, 16}, // SCROLL_SOUTHWEST = 37,
{ 1, 5}, // SCROLL_WEST = 38,
{ 2, 1}, // SCROLL_NORTHWEST = 39,
{ 0, 0}, // POINTER_COPY = 40,
{ 14, 16}, // TELEPORT = 41,
{ 20, 20}, // SCUTTLE_BOAT = 42
}};
assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetCombat(size_t index)
{
static const std::array<Point, 20> offsets = {{
{ 12, 12 }, // BLOCKED = 0,
{ 10, 14 }, // MOVE = 1,
{ 14, 14 }, // FLY = 2,
{ 12, 12 }, // SHOOT = 3,
{ 12, 12 }, // HERO = 4,
{ 8, 12 }, // QUERY = 5,
{ 0, 0 }, // POINTER = 6,
{ 21, 0 }, // HIT_NORTHEAST = 7,
{ 31, 5 }, // HIT_EAST = 8,
{ 21, 21 }, // HIT_SOUTHEAST = 9,
{ 0, 21 }, // HIT_SOUTHWEST = 10,
{ 0, 5 }, // HIT_WEST = 11,
{ 0, 0 }, // HIT_NORTHWEST = 12,
{ 6, 0 }, // HIT_NORTH = 13,
{ 6, 31 }, // HIT_SOUTH = 14,
{ 14, 0 }, // SHOOT_PENALTY = 15,
{ 12, 12 }, // SHOOT_CATAPULT = 16,
{ 12, 12 }, // HEAL = 17,
{ 12, 12 }, // SACRIFICE = 18,
{ 14, 20 }, // TELEPORT = 19
}};
assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetSpellcast()
{
return { 18, 28};
}
Point CursorHandler::getPivotOffset()
{
if (dndObject)
return dndObject->dimensions() / 2;
switch (type) {
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
};
assert(0);
return {0, 0};
}
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
{
if (dndObject)
return dndObject;
return cursors[static_cast<size_t>(type)]->getImage(frame);
}
void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.framerate().getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
while (frameTime >= frameDisplayDuration)
{
frameTime -= frameDisplayDuration;
newFrame++;
}
auto & animation = cursors.at(static_cast<size_t>(type));
while (newFrame >= animation->size())
newFrame -= animation->size();
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
}
void CursorHandler::render()
{
if(!showing)
return;
if (type == Cursor::Type::SPELLBOOK)
updateSpellcastCursor();
cursor->render();
}
void CursorHandler::hide()
{
if (!showing)
return;
showing = false;
cursor->setVisible(false);
}
void CursorHandler::show()
{
if (showing)
return;
showing = true;
cursor->setVisible(true);
}

View File

@ -1,182 +1,182 @@
/*
* CCursorHandler.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 "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
class ICursor;
class IImage;
class CAnimation;
namespace Cursor
{
enum class Type {
ADVENTURE, // set of various cursors for adventure map
COMBAT, // set of various cursors for combat
DEFAULT, // default arrow and hourglass cursors
SPELLBOOK // animated cursor for spellcasting
};
enum class Default {
POINTER = 0,
//ARROW_COPY = 1, // probably unused
HOURGLASS = 2,
};
enum class Combat {
BLOCKED = 0,
MOVE = 1,
FLY = 2,
SHOOT = 3,
HERO = 4,
QUERY = 5,
POINTER = 6,
HIT_NORTHEAST = 7,
HIT_EAST = 8,
HIT_SOUTHEAST = 9,
HIT_SOUTHWEST = 10,
HIT_WEST = 11,
HIT_NORTHWEST = 12,
HIT_NORTH = 13,
HIT_SOUTH = 14,
SHOOT_PENALTY = 15,
SHOOT_CATAPULT = 16,
HEAL = 17,
SACRIFICE = 18,
TELEPORT = 19,
COUNT
};
enum class Map {
POINTER = 0,
HOURGLASS = 1,
HERO = 2,
TOWN = 3,
T1_MOVE = 4,
T1_ATTACK = 5,
T1_SAIL = 6,
T1_DISEMBARK = 7,
T1_EXCHANGE = 8,
T1_VISIT = 9,
T2_MOVE = 10,
T2_ATTACK = 11,
T2_SAIL = 12,
T2_DISEMBARK = 13,
T2_EXCHANGE = 14,
T2_VISIT = 15,
T3_MOVE = 16,
T3_ATTACK = 17,
T3_SAIL = 18,
T3_DISEMBARK = 19,
T3_EXCHANGE = 20,
T3_VISIT = 21,
T4_MOVE = 22,
T4_ATTACK = 23,
T4_SAIL = 24,
T4_DISEMBARK = 25,
T4_EXCHANGE = 26,
T4_VISIT = 27,
T1_SAIL_VISIT = 28,
T2_SAIL_VISIT = 29,
T3_SAIL_VISIT = 30,
T4_SAIL_VISIT = 31,
SCROLL_NORTH = 32,
SCROLL_NORTHEAST = 33,
SCROLL_EAST = 34,
SCROLL_SOUTHEAST = 35,
SCROLL_SOUTH = 36,
SCROLL_SOUTHWEST = 37,
SCROLL_WEST = 38,
SCROLL_NORTHWEST = 39,
//POINTER_COPY = 40, // probably unused
TELEPORT = 41,
SCUTTLE_BOAT = 42,
COUNT
};
enum class Spellcast {
SPELL = 0,
};
}
/// handles mouse cursor
class CursorHandler final
{
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
std::array<std::shared_ptr<CAnimation>, 4> cursors;
bool showing;
/// Current cursor
Cursor::Type type;
size_t frame;
float frameTime;
Point pos;
void changeGraphic(Cursor::Type type, size_t index);
Point getPivotOffset();
void updateSpellcastCursor();
std::shared_ptr<IImage> getCurrentImage();
std::unique_ptr<ICursor> cursor;
static std::unique_ptr<ICursor> createCursor();
public:
CursorHandler();
~CursorHandler();
/// Replaces the cursor with a custom image.
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
void dragAndDropCursor(std::shared_ptr<IImage> image);
void dragAndDropCursor(const AnimationPath & path, size_t index);
/// Changes cursor to specified index
void set(Cursor::Default index);
void set(Cursor::Map index);
void set(Cursor::Combat index);
void set(Cursor::Spellcast index);
/// Returns current index of cursor
template<typename Index>
std::optional<Index> get()
{
bool typeValid = true;
typeValid &= (std::is_same<Index, Cursor::Default>::value )|| type != Cursor::Type::DEFAULT;
typeValid &= (std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE;
typeValid &= (std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT;
typeValid &= (std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK;
if (typeValid)
return static_cast<Index>(frame);
return std::nullopt;
}
Point getPivotOffsetSpellcast();
Point getPivotOffsetDefault(size_t index);
Point getPivotOffsetMap(size_t index);
Point getPivotOffsetCombat(size_t index);
void render();
void hide();
void show();
/// change cursor's positions to (x, y)
void cursorMove(const int & x, const int & y);
};
/*
* CCursorHandler.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 "../../lib/Point.h"
#include "../../lib/filesystem/ResourcePath.h"
class ICursor;
class IImage;
class CAnimation;
namespace Cursor
{
enum class Type {
ADVENTURE, // set of various cursors for adventure map
COMBAT, // set of various cursors for combat
DEFAULT, // default arrow and hourglass cursors
SPELLBOOK // animated cursor for spellcasting
};
enum class Default {
POINTER = 0,
//ARROW_COPY = 1, // probably unused
HOURGLASS = 2,
};
enum class Combat {
BLOCKED = 0,
MOVE = 1,
FLY = 2,
SHOOT = 3,
HERO = 4,
QUERY = 5,
POINTER = 6,
HIT_NORTHEAST = 7,
HIT_EAST = 8,
HIT_SOUTHEAST = 9,
HIT_SOUTHWEST = 10,
HIT_WEST = 11,
HIT_NORTHWEST = 12,
HIT_NORTH = 13,
HIT_SOUTH = 14,
SHOOT_PENALTY = 15,
SHOOT_CATAPULT = 16,
HEAL = 17,
SACRIFICE = 18,
TELEPORT = 19,
COUNT
};
enum class Map {
POINTER = 0,
HOURGLASS = 1,
HERO = 2,
TOWN = 3,
T1_MOVE = 4,
T1_ATTACK = 5,
T1_SAIL = 6,
T1_DISEMBARK = 7,
T1_EXCHANGE = 8,
T1_VISIT = 9,
T2_MOVE = 10,
T2_ATTACK = 11,
T2_SAIL = 12,
T2_DISEMBARK = 13,
T2_EXCHANGE = 14,
T2_VISIT = 15,
T3_MOVE = 16,
T3_ATTACK = 17,
T3_SAIL = 18,
T3_DISEMBARK = 19,
T3_EXCHANGE = 20,
T3_VISIT = 21,
T4_MOVE = 22,
T4_ATTACK = 23,
T4_SAIL = 24,
T4_DISEMBARK = 25,
T4_EXCHANGE = 26,
T4_VISIT = 27,
T1_SAIL_VISIT = 28,
T2_SAIL_VISIT = 29,
T3_SAIL_VISIT = 30,
T4_SAIL_VISIT = 31,
SCROLL_NORTH = 32,
SCROLL_NORTHEAST = 33,
SCROLL_EAST = 34,
SCROLL_SOUTHEAST = 35,
SCROLL_SOUTH = 36,
SCROLL_SOUTHWEST = 37,
SCROLL_WEST = 38,
SCROLL_NORTHWEST = 39,
//POINTER_COPY = 40, // probably unused
TELEPORT = 41,
SCUTTLE_BOAT = 42,
COUNT
};
enum class Spellcast {
SPELL = 0,
};
}
/// handles mouse cursor
class CursorHandler final
{
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
std::array<std::shared_ptr<CAnimation>, 4> cursors;
bool showing;
/// Current cursor
Cursor::Type type;
size_t frame;
float frameTime;
Point pos;
void changeGraphic(Cursor::Type type, size_t index);
Point getPivotOffset();
void updateSpellcastCursor();
std::shared_ptr<IImage> getCurrentImage();
std::unique_ptr<ICursor> cursor;
static std::unique_ptr<ICursor> createCursor();
public:
CursorHandler();
~CursorHandler();
/// Replaces the cursor with a custom image.
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
void dragAndDropCursor(std::shared_ptr<IImage> image);
void dragAndDropCursor(const AnimationPath & path, size_t index);
/// Changes cursor to specified index
void set(Cursor::Default index);
void set(Cursor::Map index);
void set(Cursor::Combat index);
void set(Cursor::Spellcast index);
/// Returns current index of cursor
template<typename Index>
std::optional<Index> get()
{
bool typeValid = true;
typeValid &= (std::is_same<Index, Cursor::Default>::value )|| type != Cursor::Type::DEFAULT;
typeValid &= (std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE;
typeValid &= (std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT;
typeValid &= (std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK;
if (typeValid)
return static_cast<Index>(frame);
return std::nullopt;
}
Point getPivotOffsetSpellcast();
Point getPivotOffsetDefault(size_t index);
Point getPivotOffsetMap(size_t index);
Point getPivotOffsetCombat(size_t index);
void render();
void hide();
void show();
/// change cursor's positions to (x, y)
void cursorMove(const int & x, const int & y);
};

View File

@ -1,19 +1,19 @@
/*
* MouseButton.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 MouseButton
{
LEFT = 1,
MIDDLE = 2,
RIGHT = 3,
EXTRA1 = 4,
EXTRA2 = 5
};
/*
* MouseButton.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 MouseButton
{
LEFT = 1,
MIDDLE = 2,
RIGHT = 3,
EXTRA1 = 4,
EXTRA2 = 5
};

View File

@ -1,12 +1,12 @@
/*
* TextAlignment.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 ETextAlignment {TOPLEFT, TOPCENTER, CENTER, BOTTOMRIGHT};
/*
* TextAlignment.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 ETextAlignment {TOPLEFT, TOPCENTER, CENTER, BOTTOMRIGHT};

View File

@ -1,93 +1,93 @@
/*
* IMapRendererContext.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 int3;
class Point;
class CGObjectInstance;
class ObjectInstanceID;
struct TerrainTile;
struct CGPath;
VCMI_LIB_NAMESPACE_END
class IMapRendererContext
{
public:
using MapObject = ObjectInstanceID;
using MapObjectsList = std::vector<MapObject>;
virtual ~IMapRendererContext() = default;
/// returns dimensions of current map
virtual int3 getMapSize() const = 0;
/// returns true if chosen coordinates exist on map
virtual bool isInMap(const int3 & coordinates) const = 0;
/// returns true if selected tile has animation and should not be cached
virtual bool tileAnimated(const int3 & coordinates) const = 0;
/// returns tile by selected coordinates. Coordinates MUST be valid
virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0;
/// returns all objects visible on specified tile
virtual const MapObjectsList & getObjects(const int3 & coordinates) const = 0;
/// returns specific object by ID, or nullptr if not found
virtual const CGObjectInstance * getObject(ObjectInstanceID objectID) const = 0;
/// returns path of currently active hero, or nullptr if none
virtual const CGPath * currentPath() const = 0;
/// returns true if specified tile is visible in current context
virtual bool isVisible(const int3 & coordinates) const = 0;
/// returns true if specified object is the currently active hero
virtual bool isActiveHero(const CGObjectInstance* obj) const = 0;
virtual size_t objectGroupIndex(ObjectInstanceID objectID) const = 0;
virtual Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const = 0;
/// returns object animation transparency. IF set to 0, object will not be visible
virtual double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const = 0;
/// returns animation frame for selected object
virtual size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const = 0;
/// returns index of image for overlay on specific tile, or numeric_limits::max if none
virtual size_t overlayImageIndex(const int3 & coordinates) const = 0;
/// returns animation frame for terrain
virtual size_t terrainImageIndex(size_t groupSize) const = 0;
virtual double viewTransitionProgress() const = 0;
/// if true, rendered images will be converted to grayscale
virtual bool filterGrayscale() const = 0;
virtual bool showRoads() const = 0;
virtual bool showRivers() const = 0;
virtual bool showBorder() const = 0;
/// if true, world view overlay will be shown
virtual bool showOverlay() const = 0;
/// if true, map grid should be visible on map
virtual bool showGrid() const = 0;
virtual bool showVisitable() const = 0;
virtual bool showBlocked() const = 0;
/// if true, spell range for teleport / scuttle boat will be visible
virtual bool showSpellRange(const int3 & position) const = 0;
};
/*
* IMapRendererContext.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 int3;
class Point;
class CGObjectInstance;
class ObjectInstanceID;
struct TerrainTile;
struct CGPath;
VCMI_LIB_NAMESPACE_END
class IMapRendererContext
{
public:
using MapObject = ObjectInstanceID;
using MapObjectsList = std::vector<MapObject>;
virtual ~IMapRendererContext() = default;
/// returns dimensions of current map
virtual int3 getMapSize() const = 0;
/// returns true if chosen coordinates exist on map
virtual bool isInMap(const int3 & coordinates) const = 0;
/// returns true if selected tile has animation and should not be cached
virtual bool tileAnimated(const int3 & coordinates) const = 0;
/// returns tile by selected coordinates. Coordinates MUST be valid
virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0;
/// returns all objects visible on specified tile
virtual const MapObjectsList & getObjects(const int3 & coordinates) const = 0;
/// returns specific object by ID, or nullptr if not found
virtual const CGObjectInstance * getObject(ObjectInstanceID objectID) const = 0;
/// returns path of currently active hero, or nullptr if none
virtual const CGPath * currentPath() const = 0;
/// returns true if specified tile is visible in current context
virtual bool isVisible(const int3 & coordinates) const = 0;
/// returns true if specified object is the currently active hero
virtual bool isActiveHero(const CGObjectInstance* obj) const = 0;
virtual size_t objectGroupIndex(ObjectInstanceID objectID) const = 0;
virtual Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const = 0;
/// returns object animation transparency. IF set to 0, object will not be visible
virtual double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const = 0;
/// returns animation frame for selected object
virtual size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const = 0;
/// returns index of image for overlay on specific tile, or numeric_limits::max if none
virtual size_t overlayImageIndex(const int3 & coordinates) const = 0;
/// returns animation frame for terrain
virtual size_t terrainImageIndex(size_t groupSize) const = 0;
virtual double viewTransitionProgress() const = 0;
/// if true, rendered images will be converted to grayscale
virtual bool filterGrayscale() const = 0;
virtual bool showRoads() const = 0;
virtual bool showRivers() const = 0;
virtual bool showBorder() const = 0;
/// if true, world view overlay will be shown
virtual bool showOverlay() const = 0;
/// if true, map grid should be visible on map
virtual bool showGrid() const = 0;
virtual bool showVisitable() const = 0;
virtual bool showBlocked() const = 0;
/// if true, spell range for teleport / scuttle boat will be visible
virtual bool showSpellRange(const int3 & position) const = 0;
};

View File

@ -1,53 +1,53 @@
/*
* IMapRendererObserver.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 int3;
class CGObjectInstance;
class CGHeroInstance;
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class IMapObjectObserver
{
public:
IMapObjectObserver();
virtual ~IMapObjectObserver();
virtual bool hasOngoingAnimations() = 0;
/// Plays fade-in animation and adds object to map
virtual void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Plays fade-out animation and removed object from map
virtual void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Adds object to map instantly, with no animation
virtual void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Removes object from map instantly, with no animation
virtual void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Perform hero movement animation, moving hero across terrain
virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
/// Perform initialization of hero teleportation animation with terrain fade animation
virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
};
/*
* IMapRendererObserver.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 int3;
class CGObjectInstance;
class CGHeroInstance;
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class IMapObjectObserver
{
public:
IMapObjectObserver();
virtual ~IMapObjectObserver();
virtual bool hasOngoingAnimations() = 0;
/// Plays fade-in animation and adds object to map
virtual void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Plays fade-out animation and removed object from map
virtual void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Adds object to map instantly, with no animation
virtual void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Removes object from map instantly, with no animation
virtual void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
/// Perform hero movement animation, moving hero across terrain
virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
/// Perform initialization of hero teleportation animation with terrain fade animation
virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
};

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More