mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Merge remote-tracking branch 'origin/develop' into parellel_rmg
# Conflicts: # lib/rmg/CZonePlacer.h # lib/rmg/TreasurePlacer.h
This commit is contained in:
commit
d137f7157c
118
.github/workflows/github.yml
vendored
118
.github/workflows/github.yml
vendored
@ -71,11 +71,11 @@ jobs:
|
||||
- platform: linux-qt6
|
||||
os: ubuntu-22.04
|
||||
test: 0
|
||||
preset: linux-clang-release
|
||||
preset: linux-clang-test
|
||||
- platform: linux
|
||||
os: ubuntu-20.04
|
||||
test: 0
|
||||
preset: linux-gcc-release
|
||||
preset: linux-gcc-test
|
||||
- platform: mac-intel
|
||||
os: macos-12
|
||||
test: 0
|
||||
@ -130,7 +130,7 @@ jobs:
|
||||
preset: android-conan-ninja-release
|
||||
conan_profile: android-64
|
||||
conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
|
||||
artifact_platform: aarch64-v8a
|
||||
artifact_platform: arm64-v8a
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
@ -225,6 +225,7 @@ jobs:
|
||||
name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }}
|
||||
path: |
|
||||
${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
|
||||
|
||||
- name: Android artifacts
|
||||
if: ${{ startsWith(matrix.platform, 'android') }}
|
||||
uses: actions/upload-artifact@v3
|
||||
@ -233,6 +234,14 @@ jobs:
|
||||
path: |
|
||||
${{ env.ANDROID_APK_PATH }}
|
||||
|
||||
- name: Android JNI ${{matrix.platform}}
|
||||
if: ${{ startsWith(matrix.platform, 'android') && github.ref == 'refs/heads/master' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Android JNI ${{matrix.platform}}
|
||||
path: |
|
||||
${{ github.workspace }}/android/vcmi-app/src/main/jniLibs
|
||||
|
||||
- name: Upload build
|
||||
if: ${{ (matrix.pack == 1 || startsWith(matrix.platform, 'android')) && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' }}
|
||||
continue-on-error: true
|
||||
@ -254,3 +263,106 @@ jobs:
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: always()
|
||||
|
||||
# copy-pasted mostly
|
||||
bundle_release:
|
||||
|
||||
needs: build
|
||||
if: always() && github.ref == 'refs/heads/master'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: android-32
|
||||
os: ubuntu-22.04
|
||||
extension: aab
|
||||
preset: android-conan-ninja-release
|
||||
conan_profile: android-32
|
||||
conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
|
||||
artifact_platform: aab
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Dependencies
|
||||
run: source '${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh'
|
||||
env:
|
||||
VCMI_BUILD_PLATFORM: x64
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
if: "${{ matrix.conan_profile != '' }}"
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Conan setup
|
||||
if: "${{ matrix.conan_profile != '' }}"
|
||||
run: |
|
||||
pip3 install 'conan<2.0'
|
||||
conan profile new default --detect
|
||||
conan install . \
|
||||
--install-folder=conan-generated \
|
||||
--no-imports \
|
||||
--build=never \
|
||||
--profile:build=default \
|
||||
--profile:host=CI/conan/${{ matrix.conan_profile }} \
|
||||
${{ matrix.conan_options }}
|
||||
env:
|
||||
GENERATE_ONLY_BUILT_CONFIG: 1
|
||||
|
||||
- name: Git branch name
|
||||
id: git-branch-name
|
||||
uses: EthanSK/git-branch-name-action@v1
|
||||
|
||||
- name: Build Number
|
||||
run: |
|
||||
source '${{github.workspace}}/CI/get_package_name.sh'
|
||||
if [ '${{ matrix.artifact_platform }}' ]; then
|
||||
VCMI_PACKAGE_FILE_NAME+="-${{ matrix.artifact_platform }}"
|
||||
fi
|
||||
echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
|
||||
echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
|
||||
echo VCMI_PACKAGE_GITVERSION="$VCMI_PACKAGE_GITVERSION" >> $GITHUB_ENV
|
||||
env:
|
||||
PULL_REQUEST: ${{ github.event.pull_request.number }}
|
||||
|
||||
- name: CMake Preset
|
||||
run: |
|
||||
cmake --preset ${{ matrix.preset }}
|
||||
|
||||
- name: Build Preset
|
||||
run: |
|
||||
cmake --build --preset ${{matrix.preset}}
|
||||
|
||||
- name: Download libs x64
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: Android JNI android-64
|
||||
path: ${{ github.workspace }}/android/vcmi-app/src/main/jniLibs/
|
||||
|
||||
- name: Create android package
|
||||
run: |
|
||||
cd android
|
||||
./gradlew bundleRelease --info
|
||||
echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/android/vcmi-app/build/outputs/bundle/release/*.aab)" >> $GITHUB_ENV
|
||||
env:
|
||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
|
||||
- name: Android artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.VCMI_PACKAGE_FILE_NAME }}
|
||||
path: |
|
||||
${{ env.ANDROID_APK_PATH }}
|
||||
|
||||
- uses: act10ns/slack@v1
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#notifications'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: always()
|
||||
|
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,6 +1,7 @@
|
||||
[submodule "test/googletest"]
|
||||
path = test/googletest
|
||||
url = https://github.com/google/googletest
|
||||
branch = v1.13.x
|
||||
[submodule "AI/FuzzyLite"]
|
||||
path = AI/FuzzyLite
|
||||
url = https://github.com/fuzzylite/fuzzylite.git
|
||||
|
@ -51,11 +51,11 @@ int64_t AttackPossibility::calculateDamageReduce(
|
||||
|
||||
// FIXME: provide distance info for Jousting bonus
|
||||
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(defender, attacker, 0);
|
||||
auto enemiesKilled = damageDealt / defender->MaxHealth() + (damageDealt % defender->MaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
|
||||
auto enemiesKilled = damageDealt / defender->getMaxHealth() + (damageDealt % defender->getMaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
|
||||
auto enemyDamage = averageDmg(enemyDamageBeforeAttack.damage);
|
||||
auto damagePerEnemy = enemyDamage / (double)defender->getCount();
|
||||
|
||||
return (int64_t)(damagePerEnemy * (enemiesKilled * KILL_BOUNTY + damageDealt * HEALTH_BOUNTY / (double)defender->MaxHealth()));
|
||||
return (int64_t)(damagePerEnemy * (enemiesKilled * KILL_BOUNTY + damageDealt * HEALTH_BOUNTY / (double)defender->getMaxHealth()));
|
||||
}
|
||||
|
||||
int64_t AttackPossibility::evaluateBlockedShootersDmg(const BattleAttackInfo & attackInfo, BattleHex hex, const HypotheticBattle & state)
|
||||
@ -97,7 +97,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf
|
||||
auto attacker = attackInfo.attacker;
|
||||
auto defender = attackInfo.defender;
|
||||
const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION";
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(Bonus::BLOCKS_RETALIATION);
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION);
|
||||
const auto attackerSide = state.playerToSide(state.battleGetOwner(attacker));
|
||||
const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
|
||||
|
||||
|
@ -102,14 +102,14 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
|
||||
try
|
||||
{
|
||||
if(stack->type->getId() == CreatureID::CATAPULT)
|
||||
if(stack->creatureId() == CreatureID::CATAPULT)
|
||||
return useCatapult(stack);
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->hasBonusOfType(Bonus::HEALER))
|
||||
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->hasBonusOfType(BonusType::HEALER))
|
||||
{
|
||||
auto healingTargets = cb->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
|
||||
std::map<int, const CStack*> woundHpToStack;
|
||||
for(auto stack : healingTargets)
|
||||
if(auto woundHp = stack->MaxHealth() - stack->getFirstHPleft())
|
||||
if(auto woundHp = stack->getMaxHealth() - stack->getFirstHPleft())
|
||||
woundHpToStack[woundHp] = stack;
|
||||
if(woundHpToStack.empty())
|
||||
return BattleAction::makeDefend(stack);
|
||||
@ -137,7 +137,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
std::optional<PossibleSpellcast> bestSpellcast(std::nullopt);
|
||||
//TODO: faerie dragon type spell should be selected by server
|
||||
SpellID creatureSpellToCast = cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), stack, CBattleInfoCallback::RANDOM_AIMED);
|
||||
if(stack->hasBonusOfType(Bonus::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
if(stack->hasBonusOfType(BonusType::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
{
|
||||
const CSpell * spell = creatureSpellToCast.toSpell();
|
||||
|
||||
@ -214,7 +214,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
bestAttack.attackerState->unitType()->getJsonKey(),
|
||||
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
|
||||
(int)bestAttack.affectedUnits[0]->getCount(), action, (int)bestAttack.from, (int)bestAttack.attack.attacker->getPosition().hex,
|
||||
bestAttack.attack.chargeDistance, bestAttack.attack.attacker->Speed(0, true),
|
||||
bestAttack.attack.chargeDistance, bestAttack.attack.attacker->speed(0, true),
|
||||
bestAttack.defenderDamageReduce, bestAttack.attackerDamageReduce, bestAttack.attackValue()
|
||||
);
|
||||
}
|
||||
@ -241,7 +241,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
}
|
||||
|
||||
if(score <= EvaluationResult::INEFFECTIVE_SCORE
|
||||
&& !stack->hasBonusOfType(Bonus::FLYING)
|
||||
&& !stack->hasBonusOfType(BonusType::FLYING)
|
||||
&& stack->unitSide() == BattleSide::ATTACKER
|
||||
&& cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
@ -282,7 +282,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes) const
|
||||
{
|
||||
auto reachability = cb->getReachability(stack);
|
||||
auto avHexes = cb->battleGetAvailableHexes(reachability, stack, true);
|
||||
auto avHexes = cb->battleGetAvailableHexes(reachability, stack, false);
|
||||
|
||||
if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
|
||||
{
|
||||
@ -321,7 +321,7 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vector<Battl
|
||||
|
||||
scoreEvaluator.updateReachabilityMap(hb);
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::FLYING))
|
||||
if(stack->hasBonusOfType(BonusType::FLYING))
|
||||
{
|
||||
std::set<BattleHex> obstacleHexes;
|
||||
|
||||
@ -420,7 +420,7 @@ BattleAction CBattleAI::useCatapult(const CStack * stack)
|
||||
attack.aimToHex(targetHex);
|
||||
attack.actionType = EActionType::CATAPULT;
|
||||
attack.side = side;
|
||||
attack.stackNumber = stack->ID;
|
||||
attack.stackNumber = stack->unitId();
|
||||
|
||||
movesSkippedByDefense = 0;
|
||||
|
||||
@ -815,7 +815,7 @@ std::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
|
||||
{
|
||||
if(stack->alive())
|
||||
{
|
||||
if(stack->side == bs.ourSide)
|
||||
if(stack->unitSide() == bs.ourSide)
|
||||
bs.ourStacks.push_back(stack);
|
||||
else
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ struct CurrentOffensivePotential
|
||||
{
|
||||
for(auto stack : cbc->battleGetStacks())
|
||||
{
|
||||
if(stack->side == side)
|
||||
if(stack->unitSide() == side)
|
||||
ourAttacks[stack] = PotentialTargets(stack);
|
||||
else
|
||||
enemyAttacks[stack] = PotentialTargets(stack);
|
||||
|
@ -65,7 +65,7 @@ int64_t BattleExchangeVariant::trackAttack(
|
||||
bool evaluateOnly)
|
||||
{
|
||||
const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION";
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(Bonus::BLOCKS_RETALIATION);
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION);
|
||||
const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
|
||||
|
||||
DamageEstimation retaliation;
|
||||
@ -205,7 +205,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(const battle::Uni
|
||||
if(targets.unreachableEnemies.empty())
|
||||
return result;
|
||||
|
||||
auto speed = activeStack->Speed();
|
||||
auto speed = activeStack->speed();
|
||||
|
||||
if(speed == 0)
|
||||
return result;
|
||||
@ -607,7 +607,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
|
||||
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
{
|
||||
bool reachable = unitReachability.distances[hex] <= unit->Speed(turn);
|
||||
bool reachable = unitReachability.distances[hex] <= unit->speed(turn);
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -617,7 +617,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
|
||||
{
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= unit->Speed(turn);
|
||||
reachable = unitReachability.distances[neighbor] <= unit->speed(turn);
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
@ -665,7 +665,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
{
|
||||
bool enemyUnit = false;
|
||||
bool reachable = unitReachability.distances[hex] <= unit->Speed(turn);
|
||||
bool reachable = unitReachability.distances[hex] <= unit->speed(turn);
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -677,7 +677,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= unit->Speed(turn);
|
||||
reachable = unitReachability.distances[neighbor] <= unit->speed(turn);
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
|
@ -56,10 +56,7 @@ struct EvaluationResult
|
||||
class BattleExchangeVariant
|
||||
{
|
||||
public:
|
||||
BattleExchangeVariant()
|
||||
:dpsScore(0), attackerValue()
|
||||
{
|
||||
}
|
||||
BattleExchangeVariant(): dpsScore(0) {}
|
||||
|
||||
int64_t trackAttack(const AttackPossibility & ap, HypotheticBattle & state);
|
||||
|
||||
@ -92,10 +89,7 @@ private:
|
||||
std::vector<battle::Units> turnOrder;
|
||||
|
||||
public:
|
||||
BattleExchangeEvaluator(std::shared_ptr<CBattleInfoCallback> cb, std::shared_ptr<Environment> env)
|
||||
:cb(cb), reachabilityMap(), env(env), turnOrder()
|
||||
{
|
||||
}
|
||||
BattleExchangeEvaluator(std::shared_ptr<CBattleInfoCallback> cb, std::shared_ptr<Environment> env): cb(cb), env(env) {}
|
||||
|
||||
EvaluationResult findBestTarget(const battle::Unit * activeStack, PotentialTargets & targets, HypotheticBattle & hb);
|
||||
int64_t calculateExchange(const AttackPossibility & ap, PotentialTargets & targets, HypotheticBattle & hb);
|
||||
|
@ -15,14 +15,14 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
|
||||
{
|
||||
auto attackerInfo = state.battleGetUnitByID(attacker->unitId());
|
||||
auto reachability = state.getReachability(attackerInfo);
|
||||
auto avHexes = state.battleGetAvailableHexes(reachability, attackerInfo, true);
|
||||
auto avHexes = state.battleGetAvailableHexes(reachability, attackerInfo, false);
|
||||
|
||||
//FIXME: this should part of battleGetAvailableHexes
|
||||
bool forceTarget = false;
|
||||
const battle::Unit * forcedTarget = nullptr;
|
||||
BattleHex forcedHex;
|
||||
|
||||
if(attackerInfo->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE))
|
||||
if(attackerInfo->hasBonusOfType(BonusType::ATTACKS_NEAREST_CREATURE))
|
||||
{
|
||||
forceTarget = true;
|
||||
auto nearest = state.getNearestStack(attackerInfo);
|
||||
|
@ -24,7 +24,7 @@ void actualizeEffect(TBonusListPtr target, const Bonus & ef)
|
||||
{
|
||||
for(auto & bonus : *target) //TODO: optimize
|
||||
{
|
||||
if(bonus->source == Bonus::SPELL_EFFECT && bonus->type == ef.type && bonus->subtype == ef.subtype)
|
||||
if(bonus->source == BonusSource::SPELL_EFFECT && bonus->type == ef.type && bonus->subtype == ef.subtype)
|
||||
{
|
||||
if(bonus->turnsRemain < ef.turnsRemain)
|
||||
{
|
||||
@ -36,9 +36,9 @@ void actualizeEffect(TBonusListPtr target, const Bonus & ef)
|
||||
}
|
||||
}
|
||||
|
||||
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack)
|
||||
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle::CUnitState * Stack)
|
||||
: battle::CUnitState(),
|
||||
origBearer(Stack),
|
||||
origBearer(Stack->getBonusBearer()),
|
||||
owner(Owner),
|
||||
type(Stack->unitType()),
|
||||
baseAmount(Stack->unitBaseAmount()),
|
||||
@ -126,7 +126,7 @@ TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, c
|
||||
{
|
||||
if(selector(&bonus) && (!limit || !limit(&bonus)))
|
||||
{
|
||||
if(ret->getFirst(Selector::source(Bonus::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||
if(ret->getFirst(Selector::source(BonusSource::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||
{
|
||||
actualizeEffect(ret, bonus);
|
||||
}
|
||||
|
@ -14,16 +14,10 @@
|
||||
#include <vcmi/Environment.h>
|
||||
#include <vcmi/ServerCallback.h>
|
||||
|
||||
#include "../../lib/HeroBonus.h"
|
||||
#include "../../lib/bonuses/Bonus.h"
|
||||
#include "../../lib/battle/BattleProxy.h"
|
||||
#include "../../lib/battle/CUnitState.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CStack;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class HypotheticBattle;
|
||||
|
||||
///Fake random generator, used by AI to evaluate random server behavior
|
||||
@ -54,7 +48,7 @@ public:
|
||||
std::vector<Bonus> bonusesToUpdate;
|
||||
std::set<std::shared_ptr<Bonus>> bonusesToRemove;
|
||||
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack);
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const battle::CUnitState * Stack);
|
||||
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info);
|
||||
|
||||
|
@ -30,7 +30,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
|
||||
for(const CStack *enemy : getCbc()->battleGetStacks())
|
||||
{
|
||||
//Consider only stacks of different owner
|
||||
if(enemy->side == endangered->side)
|
||||
if(enemy->unitSide() == endangered->unitSide())
|
||||
continue;
|
||||
|
||||
//Look-up which tiles can be melee-attacked
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 9751a751a17c0682ed5d02e583c6a0cda8bc88e5
|
||||
Subproject commit 7aee562d6ca17f3cf42588ffb5116e03017c3c50
|
@ -256,7 +256,7 @@ void AIGateway::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance
|
||||
NET_EVENT_HANDLER;
|
||||
}
|
||||
|
||||
void AIGateway::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
void AIGateway::tileHidden(const std::unordered_set<int3> & pos)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
@ -264,7 +264,7 @@ void AIGateway::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
nullkiller->memory->removeInvisibleObjects(myCb.get());
|
||||
}
|
||||
|
||||
void AIGateway::tileRevealed(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
void AIGateway::tileRevealed(const std::unordered_set<int3> & pos)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
@ -1058,27 +1058,6 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re
|
||||
}
|
||||
}
|
||||
|
||||
bool AIGateway::canRecruitAnyHero(const CGTownInstance * t) const
|
||||
{
|
||||
//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
|
||||
if(!t)
|
||||
t = findTownWithTavern();
|
||||
|
||||
if(!t || !townHasFreeTavern(t))
|
||||
return false;
|
||||
|
||||
if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
|
||||
return false;
|
||||
if(!cb->getAvailableHeroes(t).size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
|
||||
{
|
||||
NET_EVENT_HANDLER;
|
||||
@ -1160,16 +1139,6 @@ void AIGateway::addVisitableObj(const CGObjectInstance * obj)
|
||||
}
|
||||
}
|
||||
|
||||
HeroPtr AIGateway::getHeroWithGrail() const
|
||||
{
|
||||
for(const CGHeroInstance * h : cb->getHeroesInfo())
|
||||
{
|
||||
if(h->hasArt(ArtifactID::GRAIL))
|
||||
return h;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
{
|
||||
if(h->inTownGarrison && h->visitedTown)
|
||||
@ -1416,7 +1385,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
||||
//TODO trade only as much as needed
|
||||
if (toGive) //don't try to sell 0 resources
|
||||
{
|
||||
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
|
||||
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
|
||||
accquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
|
||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
|
||||
}
|
||||
@ -1437,15 +1406,6 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
||||
}
|
||||
}
|
||||
|
||||
const CGTownInstance * AIGateway::findTownWithTavern() const
|
||||
{
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
if(townHasFreeTavern(t))
|
||||
return t;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AIGateway::endTurn()
|
||||
{
|
||||
logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
|
||||
|
@ -127,7 +127,7 @@ public:
|
||||
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, ShashInt3> & pos) 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 * townOrTavern) override;
|
||||
@ -142,7 +142,7 @@ public:
|
||||
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, ShashInt3> & pos) override;
|
||||
void tileRevealed(const std::unordered_set<int3> & pos) override;
|
||||
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
|
||||
void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
|
||||
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
|
||||
@ -198,11 +198,6 @@ public:
|
||||
void retrieveVisitableObjs();
|
||||
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||
|
||||
HeroPtr getHeroWithGrail() const;
|
||||
|
||||
const CGTownInstance * findTownWithTavern() const;
|
||||
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
|
||||
|
||||
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
|
||||
|
@ -307,7 +307,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
||||
auto art2 = a2->artType;
|
||||
|
||||
if(art1->price == art2->price)
|
||||
return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
|
||||
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
|
||||
else
|
||||
return art1->price > art2->price;
|
||||
}
|
||||
@ -319,7 +319,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
|
||||
|
||||
//TODO: allow polling of remaining creatures in dwelling
|
||||
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
|
||||
return rewardable->getResetDuration() == 7;
|
||||
return rewardable->configuration.getResetDuration() == 7;
|
||||
|
||||
if(dynamic_cast<const CGDwelling *>(obj))
|
||||
return true;
|
||||
|
@ -54,7 +54,7 @@
|
||||
|
||||
using namespace tbb;
|
||||
|
||||
typedef std::pair<ui32, std::vector<CreatureID>> dwellingContent;
|
||||
using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -305,10 +305,10 @@ public:
|
||||
public:
|
||||
using ptr_type = std::unique_ptr<T, External_Deleter>;
|
||||
|
||||
SharedPool(std::function<std::unique_ptr<T>()> elementFactory)
|
||||
: elementFactory(elementFactory), pool(), sync(), instance_tracker(new SharedPool<T>*(this))
|
||||
SharedPool(std::function<std::unique_ptr<T>()> elementFactory):
|
||||
elementFactory(elementFactory), pool(), instance_tracker(new SharedPool<T> *(this))
|
||||
{}
|
||||
|
||||
|
||||
void add(std::unique_ptr<T> t)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(sync);
|
||||
|
@ -77,7 +77,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
|
||||
if(left.creature->getLevel() != right.creature->getLevel())
|
||||
return left.creature->getLevel() < right.creature->getLevel();
|
||||
|
||||
return left.creature->Speed() > right.creature->Speed();
|
||||
return left.creature->speed() > right.creature->speed();
|
||||
});
|
||||
|
||||
return weakest;
|
||||
@ -108,12 +108,12 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
uint64_t armyValue = 0;
|
||||
|
||||
TemporaryArmy newArmyInstance;
|
||||
auto bonusModifiers = armyCarrier->getBonuses(Selector::type()(Bonus::MORALE));
|
||||
auto bonusModifiers = armyCarrier->getBonuses(Selector::type()(BonusType::MORALE));
|
||||
|
||||
for(auto bonus : *bonusModifiers)
|
||||
{
|
||||
// army bonuses will change and object bonuses are temporary
|
||||
if(bonus->source != Bonus::ARMY && bonus->source != Bonus::OBJECT)
|
||||
if(bonus->source != BonusSource::ARMY && bonus->source != BonusSource::OBJECT)
|
||||
{
|
||||
newArmyInstance.addNewBonus(std::make_shared<Bonus>(*bonus));
|
||||
}
|
||||
@ -150,7 +150,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
|
||||
for(auto & slot : newArmyInstance.Slots())
|
||||
{
|
||||
auto morale = slot.second->MoraleVal();
|
||||
auto morale = slot.second->moraleVal();
|
||||
auto multiplier = 1.0f;
|
||||
|
||||
const float BadMoraleChance = 0.083f;
|
||||
|
@ -32,13 +32,8 @@ struct SlotInfo
|
||||
struct ArmyUpgradeInfo
|
||||
{
|
||||
std::vector<SlotInfo> resultingArmy;
|
||||
uint64_t upgradeValue;
|
||||
uint64_t upgradeValue = 0;
|
||||
TResources upgradeCost;
|
||||
|
||||
ArmyUpgradeInfo()
|
||||
: resultingArmy(), upgradeValue(0), upgradeCost()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT IArmyManager //: public: IAbstractManager
|
||||
|
@ -62,8 +62,11 @@ public:
|
||||
HeroRole townRole;
|
||||
bool hasSomethingToBuild;
|
||||
|
||||
TownDevelopmentInfo(const CGTownInstance* town)
|
||||
:town(town), armyStrength(0), toBuild(), townDevelopmentCost(), requiredResources(), townRole(HeroRole::SCOUT), hasSomethingToBuild(false)
|
||||
TownDevelopmentInfo(const CGTownInstance * town):
|
||||
town(town),
|
||||
armyStrength(0),
|
||||
townRole(HeroRole::SCOUT),
|
||||
hasSomethingToBuild(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../lib/CHeroHandler.h"
|
||||
#include "../../../lib/GameSettings.h"
|
||||
#include "../../../lib/CGameState.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -70,10 +72,10 @@ float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance *
|
||||
|
||||
float HeroManager::evaluateSpeciality(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto heroSpecial = Selector::source(Bonus::HERO_SPECIAL, hero->type->getIndex());
|
||||
auto secondarySkillBonus = Selector::targetSourceType()(Bonus::SECONDARY_SKILL);
|
||||
auto heroSpecial = Selector::source(BonusSource::HERO_SPECIAL, hero->type->getIndex());
|
||||
auto secondarySkillBonus = Selector::targetSourceType()(BonusSource::SECONDARY_SKILL);
|
||||
auto specialSecondarySkillBonuses = hero->getBonuses(heroSpecial.And(secondarySkillBonus));
|
||||
auto secondarySkillBonuses = hero->getBonuses(Selector::sourceTypeSel(Bonus::SECONDARY_SKILL));
|
||||
auto secondarySkillBonuses = hero->getBonuses(Selector::sourceTypeSel(BonusSource::SECONDARY_SKILL));
|
||||
float specialityScore = 0.0f;
|
||||
|
||||
for(auto bonus : *secondarySkillBonuses)
|
||||
@ -179,6 +181,51 @@ float HeroManager::evaluateHero(const CGHeroInstance * hero) const
|
||||
return evaluateFightingStrength(hero);
|
||||
}
|
||||
|
||||
bool HeroManager::canRecruitHero(const CGTownInstance * town) const
|
||||
{
|
||||
if(!town)
|
||||
town = findTownWithTavern();
|
||||
|
||||
if(!town || !townHasFreeTavern(town))
|
||||
return false;
|
||||
|
||||
if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST)
|
||||
return false;
|
||||
|
||||
const bool includeGarnisoned = true;
|
||||
int heroCount = cb->getHeroCount(ai->playerID, includeGarnisoned);
|
||||
|
||||
if(heroCount >= ALLOWED_ROAMING_HEROES)
|
||||
return false;
|
||||
|
||||
if(heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
|
||||
return false;
|
||||
|
||||
if(!cb->getAvailableHeroes(town).size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const CGTownInstance * HeroManager::findTownWithTavern() const
|
||||
{
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
if(townHasFreeTavern(t))
|
||||
return t;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CGHeroInstance * HeroManager::findHeroWithGrail() const
|
||||
{
|
||||
for(const CGHeroInstance * h : cb->getHeroesInfo())
|
||||
{
|
||||
if(h->hasArt(ArtifactID::GRAIL))
|
||||
return h;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SecondarySkillScoreMap::SecondarySkillScoreMap(std::map<SecondarySkill, float> scoreMap)
|
||||
:scoreMap(scoreMap)
|
||||
{
|
||||
|
@ -30,6 +30,8 @@ public:
|
||||
virtual void update() = 0;
|
||||
virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0;
|
||||
virtual float evaluateHero(const CGHeroInstance * hero) const = 0;
|
||||
virtual bool canRecruitHero(const CGTownInstance * t = nullptr) const = 0;
|
||||
virtual const CGHeroInstance * findHeroWithGrail() const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT ISecondarySkillRule
|
||||
@ -57,20 +59,24 @@ private:
|
||||
static SecondarySkillEvaluator scountSkillsScores;
|
||||
|
||||
CCallback * cb; //this is enough, but we downcast from CCallback
|
||||
const Nullkiller * ai;
|
||||
std::map<HeroPtr, HeroRole> heroRoles;
|
||||
|
||||
public:
|
||||
HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB) {}
|
||||
HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {}
|
||||
const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
|
||||
HeroRole getHeroRole(const HeroPtr & hero) const override;
|
||||
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
|
||||
void update() override;
|
||||
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
|
||||
float evaluateHero(const CGHeroInstance * hero) const override;
|
||||
bool canRecruitHero(const CGTownInstance * t = nullptr) const override;
|
||||
const CGHeroInstance * findHeroWithGrail() const override;
|
||||
|
||||
private:
|
||||
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
||||
float evaluateSpeciality(const CGHeroInstance * hero) const;
|
||||
const CGTownInstance * findTownWithTavern() const;
|
||||
};
|
||||
|
||||
// basic skill scores. missing skills will have score of 0
|
||||
|
@ -22,7 +22,7 @@ struct ClusterObjectInfo
|
||||
uint8_t turn;
|
||||
};
|
||||
|
||||
typedef tbb::concurrent_hash_map<const CGObjectInstance *, ClusterObjectInfo> ClusterObjects;
|
||||
using ClusterObjects = tbb::concurrent_hash_map<const CGObjectInstance *, ClusterObjectInfo>;
|
||||
|
||||
struct ObjectCluster
|
||||
{
|
||||
@ -36,11 +36,8 @@ public:
|
||||
}
|
||||
|
||||
void addObject(const CGObjectInstance * object, const AIPath & path, float priority);
|
||||
|
||||
ObjectCluster(const CGObjectInstance * blocker)
|
||||
:objects(), blocker(blocker)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectCluster(const CGObjectInstance * blocker): blocker(blocker) {}
|
||||
|
||||
ObjectCluster() : ObjectCluster(nullptr)
|
||||
{
|
||||
@ -50,7 +47,7 @@ public:
|
||||
const CGObjectInstance * calculateCenter() const;
|
||||
};
|
||||
|
||||
typedef tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> ClusterMap;
|
||||
using ClusterMap = tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>>;
|
||||
|
||||
class ObjectClusterizer
|
||||
{
|
||||
@ -67,10 +64,7 @@ public:
|
||||
std::vector<std::shared_ptr<ObjectCluster>> getLockedClusters() const;
|
||||
const CGObjectInstance * getBlocker(const AIPath & path) const;
|
||||
|
||||
ObjectClusterizer(const Nullkiller * ai)
|
||||
:nearObjects(), farObjects(), blockedObjects(), ai(ai)
|
||||
{
|
||||
}
|
||||
ObjectClusterizer(const Nullkiller * ai): ai(ai) {}
|
||||
|
||||
private:
|
||||
bool shouldVisitObject(const CGObjectInstance * obj) const;
|
||||
|
@ -209,7 +209,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
{
|
||||
captureObjects(ai->nullkiller->objectClusterizer->getNearbyObjects());
|
||||
|
||||
if(tasks.empty())
|
||||
if(tasks.empty() || ai->nullkiller->getScanDepth() == ScanDepth::FULL)
|
||||
captureObjects(ai->nullkiller->objectClusterizer->getFarObjects());
|
||||
}
|
||||
|
||||
|
@ -58,13 +58,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||
auto treats = { treatNode.maximumDanger, treatNode.fastestDanger };
|
||||
|
||||
if(!treatNode.fastestDanger.hero)
|
||||
{
|
||||
logAi->trace("No treat found for town %s", town->getNameTranslated());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||
|
||||
if(town->garrisonHero)
|
||||
@ -91,6 +84,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!treatNode.fastestDanger.hero)
|
||||
{
|
||||
logAi->trace("No treat found for town %s", town->getNameTranslated());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
||||
|
||||
|
@ -53,7 +53,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
||||
|
||||
for(auto town : towns)
|
||||
{
|
||||
if(ai->canRecruitAnyHero(town))
|
||||
if(ai->nullkiller->heroManager->canRecruitHero(town))
|
||||
{
|
||||
auto availableHeroes = cb->getAvailableHeroes(town);
|
||||
|
||||
|
@ -66,7 +66,7 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town)
|
||||
|
||||
bool needToRecruitHero(const CGTownInstance * startupTown)
|
||||
{
|
||||
if(!ai->canRecruitAnyHero(startupTown))
|
||||
if(!ai->nullkiller->heroManager->canRecruitHero(startupTown))
|
||||
return false;
|
||||
|
||||
if(!startupTown->garrisonHero && !startupTown->visitingHero)
|
||||
|
@ -72,10 +72,10 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj)
|
||||
// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
|
||||
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
|
||||
{
|
||||
if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
|
||||
if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero
|
||||
return;
|
||||
|
||||
if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
|
||||
if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_BONUS) //or another time
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ struct GoalHash
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<Goals::TSubgoal, Goals::TGoalVec, GoalHash> TGoalHashSet;
|
||||
using TGoalHashSet = std::unordered_map<Goals::TSubgoal, Goals::TGoalVec, GoalHash>;
|
||||
|
||||
class DeepDecomposer
|
||||
{
|
||||
|
@ -53,14 +53,14 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
||||
double shootersStrength = 0;
|
||||
ui32 maxSpeed = 0;
|
||||
|
||||
static const CSelector selectorSHOOTER = Selector::type()(Bonus::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER);
|
||||
static const CSelector selectorSHOOTER = Selector::type()(BonusType::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)BonusType::SHOOTER);
|
||||
|
||||
static const CSelector selectorFLYING = Selector::type()(Bonus::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING);
|
||||
static const CSelector selectorFLYING = Selector::type()(BonusType::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)BonusType::FLYING);
|
||||
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED);
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
|
||||
|
||||
for(auto s : army->Slots())
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ private:
|
||||
TacticalAdvantageEngine tacticalAdvantageEngine;
|
||||
|
||||
public:
|
||||
FuzzyHelper(const Nullkiller * ai) : ai(ai), tacticalAdvantageEngine() {}
|
||||
FuzzyHelper(const Nullkiller * ai): ai(ai) {}
|
||||
|
||||
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
|
||||
|
||||
|
@ -117,7 +117,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositi
|
||||
void Nullkiller::resetAiState()
|
||||
{
|
||||
lockedResources = TResources();
|
||||
scanDepth = ScanDepth::SMALL;
|
||||
scanDepth = ScanDepth::FULL;
|
||||
playerID = ai->playerID;
|
||||
lockedHeroes.clear();
|
||||
dangerHitMap->reset();
|
||||
|
@ -88,6 +88,7 @@ public:
|
||||
int32_t getFreeGold() const { return getFreeResources()[EGameResID::GOLD]; }
|
||||
void lockResources(const TResources & res);
|
||||
const TResources & getLockedResources() const { return lockedResources; }
|
||||
ScanDepth getScanDepth() const { return scanDepth; }
|
||||
|
||||
private:
|
||||
void resetAiState();
|
||||
|
@ -210,14 +210,14 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
|
||||
return 1500;
|
||||
|
||||
auto statsValue =
|
||||
10 * art->valOfBonuses(Bonus::MOVEMENT, 1)
|
||||
+ 1200 * art->valOfBonuses(Bonus::STACKS_SPEED)
|
||||
+ 700 * art->valOfBonuses(Bonus::MORALE)
|
||||
+ 700 * art->getAttack(false)
|
||||
+ 700 * art->getDefense(false)
|
||||
+ 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::KNOWLEDGE)
|
||||
+ 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::SPELL_POWER)
|
||||
+ 500 * art->valOfBonuses(Bonus::LUCK);
|
||||
10 * art->valOfBonuses(BonusType::MOVEMENT, 1)
|
||||
+ 1200 * art->valOfBonuses(BonusType::STACKS_SPEED)
|
||||
+ 700 * art->valOfBonuses(BonusType::MORALE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::KNOWLEDGE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::SPELL_POWER)
|
||||
+ 500 * art->valOfBonuses(BonusType::LUCK);
|
||||
|
||||
auto classValue = 0;
|
||||
|
||||
@ -297,6 +297,12 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn
|
||||
{
|
||||
if(!target)
|
||||
return 0;
|
||||
|
||||
if(auto * m = dynamic_cast<const IMarket *>(target))
|
||||
{
|
||||
if(m->allowsTrade(EMarketMode::RESOURCE_SKILL))
|
||||
return 2000;
|
||||
}
|
||||
|
||||
switch(target->ID)
|
||||
{
|
||||
@ -305,8 +311,6 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn
|
||||
case Obj::SCHOOL_OF_MAGIC:
|
||||
case Obj::SCHOOL_OF_WAR:
|
||||
return 1000;
|
||||
case Obj::UNIVERSITY:
|
||||
return 2000;
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
case Obj::CREATURE_GENERATOR2:
|
||||
case Obj::CREATURE_GENERATOR3:
|
||||
|
@ -81,9 +81,9 @@ namespace Goals
|
||||
bool operator<(const TSubgoal & rhs) const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ITask> TTask;
|
||||
typedef std::vector<TTask> TTaskVec;
|
||||
typedef std::vector<TSubgoal> TGoalVec;
|
||||
using TTask = std::shared_ptr<ITask>;
|
||||
using TTaskVec = std::vector<TTask>;
|
||||
using TGoalVec = std::vector<TSubgoal>;
|
||||
|
||||
//method chaining + clone pattern
|
||||
#define SETTER(type, field) AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
|
||||
@ -107,8 +107,7 @@ namespace Goals
|
||||
const CGTownInstance *town; SETTER(CGTownInstance *, town)
|
||||
int bid; SETTER(int, bid)
|
||||
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID)
|
||||
: goalType(goal), hero()
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID): goalType(goal)
|
||||
{
|
||||
isAbstract = false;
|
||||
value = 0;
|
||||
|
@ -33,8 +33,6 @@ void RecruitHero::accept(AIGateway * ai)
|
||||
{
|
||||
auto t = town;
|
||||
|
||||
if(!t) t = ai->findTownWithTavern();
|
||||
|
||||
if(!t)
|
||||
{
|
||||
throw cannotFulfillGoalException("No town to recruit hero!");
|
||||
|
@ -1093,7 +1093,7 @@ void AINodeStorage::calculateTownPortal(
|
||||
if(nodeOptional)
|
||||
{
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||
logAi->trace("Adding town portal node at %s", targetTown->getObjectName());
|
||||
#endif
|
||||
output.push_back(nodeOptional.value());
|
||||
}
|
||||
|
@ -205,14 +205,14 @@ public:
|
||||
|
||||
inline void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater)
|
||||
{
|
||||
auto aiNode = static_cast<AIPathNode *>(node);
|
||||
auto * aiNode = static_cast<AIPathNode *>(node);
|
||||
|
||||
updater(aiNode);
|
||||
}
|
||||
|
||||
inline const CGHeroInstance * getHero(const CGPathNode * node) const
|
||||
{
|
||||
auto aiNode = getAINode(node);
|
||||
const auto * aiNode = getAINode(node);
|
||||
|
||||
return aiNode->actor->hero;
|
||||
}
|
||||
|
@ -32,9 +32,7 @@ public:
|
||||
virtual bool needsLastStack() const override;
|
||||
std::shared_ptr<SpecialAction> getActorAction() const;
|
||||
|
||||
HeroExchangeArmy() : CArmedInstance(true), armyCost(), requireBuyArmy(false)
|
||||
{
|
||||
}
|
||||
HeroExchangeArmy(): CArmedInstance(true), requireBuyArmy(false) {}
|
||||
};
|
||||
|
||||
struct ExchangeResult
|
||||
|
@ -53,15 +53,13 @@ namespace AIPathfinding
|
||||
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
{
|
||||
// do not allow ally shipyards because of bug
|
||||
if(t->hasBuilt(BuildingID::SHIPYARD) && t->getOwner() == ai->playerID)
|
||||
if(t->hasBuilt(BuildingID::SHIPYARD))
|
||||
shipyards.push_back(t);
|
||||
}
|
||||
|
||||
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
|
||||
{
|
||||
// do not allow ally shipyards because of bug
|
||||
if(obj->ID != Obj::TOWN && obj->getOwner() == ai->playerID) //towns were handled in the previous loop
|
||||
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
|
||||
{
|
||||
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
|
||||
shipyards.push_back(shipyard);
|
||||
|
@ -95,7 +95,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
ReachabilityInfo dists = cb->getReachability(stack);
|
||||
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
|
||||
|
||||
if(stack->type->getId() == CreatureID::CATAPULT)
|
||||
if(stack->creatureId() == CreatureID::CATAPULT)
|
||||
{
|
||||
BattleAction attack;
|
||||
static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
|
||||
@ -103,11 +103,11 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
attack.aimToHex(seletectedHex);
|
||||
attack.actionType = EActionType::CATAPULT;
|
||||
attack.side = side;
|
||||
attack.stackNumber = stack->ID;
|
||||
attack.stackNumber = stack->unitId();
|
||||
|
||||
return attack;
|
||||
}
|
||||
else if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
||||
else if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON))
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
@ -120,7 +120,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, true);
|
||||
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, false);
|
||||
|
||||
for (BattleHex hex : avHexes)
|
||||
{
|
||||
@ -270,7 +270,7 @@ BattleAction CStupidAI::goTowards(const CStack * stack, std::vector<BattleHex> h
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::FLYING))
|
||||
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.
|
||||
|
@ -257,7 +257,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
||||
auto art2 = a2->artType;
|
||||
|
||||
if(art1->price == art2->price)
|
||||
return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
|
||||
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
|
||||
else
|
||||
return art1->price > art2->price;
|
||||
}
|
||||
|
@ -23,9 +23,9 @@
|
||||
class CCallback;
|
||||
struct creInfo;
|
||||
|
||||
typedef const int3 & crint3;
|
||||
typedef const std::string & crstring;
|
||||
typedef std::pair<ui32, std::vector<CreatureID>> dwellingContent;
|
||||
using crint3 = const int3 &;
|
||||
using crstring = const std::string &;
|
||||
using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
|
||||
|
||||
const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
|
||||
const int ACTUAL_RESOURCE_COUNT = 7;
|
||||
|
@ -63,7 +63,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
|
||||
if(left.creature->getLevel() != right.creature->getLevel())
|
||||
return left.creature->getLevel() < right.creature->getLevel();
|
||||
|
||||
return left.creature->Speed() > right.creature->Speed();
|
||||
return left.creature->speed() > right.creature->speed();
|
||||
});
|
||||
|
||||
return weakest;
|
||||
|
@ -52,14 +52,14 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
||||
double shootersStrength = 0;
|
||||
ui32 maxSpeed = 0;
|
||||
|
||||
static const CSelector selectorSHOOTER = Selector::type()(Bonus::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER);
|
||||
static const CSelector selectorSHOOTER = Selector::type()(BonusType::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)BonusType::SHOOTER);
|
||||
|
||||
static const CSelector selectorFLYING = Selector::type()(Bonus::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING);
|
||||
static const CSelector selectorFLYING = Selector::type()(BonusType::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)BonusType::FLYING);
|
||||
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED);
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
|
||||
|
||||
for(auto s : army->Slots())
|
||||
{
|
||||
|
@ -76,7 +76,7 @@ namespace Goals
|
||||
//TODO: serialize?
|
||||
};
|
||||
|
||||
typedef std::vector<TSubgoal> TGoalVec;
|
||||
using TGoalVec = std::vector<TSubgoal>;
|
||||
|
||||
//method chaining + clone pattern
|
||||
#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
|
||||
@ -121,8 +121,7 @@ namespace Goals
|
||||
TSubgoal parent; VSETTER(TSubgoal, parent)
|
||||
EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
|
||||
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID)
|
||||
: goalType(goal), evaluationContext()
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID): goalType(goal)
|
||||
{
|
||||
priority = 0;
|
||||
isElementar = false;
|
||||
|
@ -131,13 +131,16 @@ TSubgoal CollectRes::whatToDoToTrade()
|
||||
|
||||
std::vector<const CGObjectInstance *> visObjs;
|
||||
ai->retrieveVisitableObjs(visObjs, true);
|
||||
for (const CGObjectInstance * obj : visObjs)
|
||||
for(const CGObjectInstance * obj : visObjs)
|
||||
{
|
||||
if (const IMarket * m = IMarket::castFrom(obj, false))
|
||||
if(const IMarket * m = IMarket::castFrom(obj, false); m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||
{
|
||||
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
|
||||
markets.push_back(m);
|
||||
else if (obj->ID == Obj::TRADING_POST)
|
||||
if(obj->ID == Obj::TOWN)
|
||||
{
|
||||
if(obj->tempOwner == ai->playerID)
|
||||
markets.push_back(m);
|
||||
}
|
||||
else
|
||||
markets.push_back(m);
|
||||
}
|
||||
}
|
||||
@ -149,9 +152,10 @@ TSubgoal CollectRes::whatToDoToTrade()
|
||||
|
||||
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
|
||||
{
|
||||
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
|
||||
auto * o = dynamic_cast<const CGObjectInstance *>(market);
|
||||
if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID))
|
||||
{
|
||||
if (!ai->isAccessible(market->o->visitablePos()))
|
||||
if(!ai->isAccessible(o->visitablePos()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -170,7 +174,7 @@ TSubgoal CollectRes::whatToDoToTrade()
|
||||
const IMarket * m = markets.back();
|
||||
//attempt trade at back (best prices)
|
||||
int howManyCanWeBuy = 0;
|
||||
for (auto i = EGameResID::WOOD; i <= EGameResID::GOLD; vstd::advance(i, 1))
|
||||
for (GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i)
|
||||
{
|
||||
if (GameResID(i) == resID)
|
||||
continue;
|
||||
@ -182,9 +186,10 @@ TSubgoal CollectRes::whatToDoToTrade()
|
||||
|
||||
if (howManyCanWeBuy >= value)
|
||||
{
|
||||
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
auto * o = dynamic_cast<const CGObjectInstance *>(m);
|
||||
auto backObj = cb->getTopObj(o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
|
||||
assert(backObj);
|
||||
auto objid = m->o->id.getNum();
|
||||
auto objid = o->id.getNum();
|
||||
if (backObj->tempOwner != ai->playerID) //top object not owned
|
||||
{
|
||||
return sptr(VisitObj(objid)); //just go there
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
class CCallback;
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb; //for templates
|
||||
|
||||
struct AIPathNode : public CGPathNode
|
||||
{
|
||||
uint32_t chainMask;
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
#include "../../lib/NetPacks.h"
|
||||
#include "../../lib/bonuses/CBonusSystemNode.h"
|
||||
#include "../../lib/bonuses/Limiters.h"
|
||||
#include "../../lib/bonuses/Updaters.h"
|
||||
#include "../../lib/bonuses/Propagators.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
@ -268,7 +272,7 @@ void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * to
|
||||
//moveCreaturesToHero(town);
|
||||
}
|
||||
|
||||
void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
void VCAI::tileHidden(const std::unordered_set<int3> & pos)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
@ -277,7 +281,7 @@ void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
clearPathsInfo();
|
||||
}
|
||||
|
||||
void VCAI::tileRevealed(const std::unordered_set<int3, ShashInt3> & pos)
|
||||
void VCAI::tileRevealed(const std::unordered_set<int3> & pos)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
@ -1605,10 +1609,10 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
|
||||
|
||||
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj)) //we may want to visit it with another hero
|
||||
{
|
||||
if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
|
||||
if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero
|
||||
return;
|
||||
|
||||
if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
|
||||
if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_BONUS) //or another time
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2130,7 +2134,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
//TODO trade only as much as needed
|
||||
if (toGive) //don't try to sell 0 resources
|
||||
{
|
||||
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
|
||||
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
|
||||
accquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
|
||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
|
||||
}
|
||||
@ -2746,7 +2750,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
|
||||
{
|
||||
//TODO: allow polling of remaining creatures in dwelling
|
||||
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
|
||||
return rewardable->getResetDuration() == 7;
|
||||
return rewardable->configuration.getResetDuration() == 7;
|
||||
|
||||
if(dynamic_cast<const CGDwelling *>(obj))
|
||||
return true;
|
||||
|
@ -160,7 +160,7 @@ public:
|
||||
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, ShashInt3> & pos) 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 * townOrTavern) override;
|
||||
@ -175,7 +175,7 @@ public:
|
||||
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, ShashInt3> & pos) override;
|
||||
void tileRevealed(const std::unordered_set<int3> & pos) override;
|
||||
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
|
||||
void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
|
||||
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
|
||||
|
@ -244,15 +244,15 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
|
||||
sendRequest(&pack);
|
||||
}
|
||||
|
||||
void CCallback::trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero)
|
||||
void CCallback::trade(const IMarket * market, EMarketMode::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 CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
|
||||
void CCallback::trade(const IMarket * market, EMarketMode::EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
|
||||
{
|
||||
TradeOnMarketplace pack;
|
||||
pack.marketId = market->id;
|
||||
pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
|
||||
pack.heroId = hero ? hero->id : ObjectInstanceID();
|
||||
pack.mode = mode;
|
||||
pack.r1 = id1;
|
||||
|
@ -33,6 +33,7 @@ class IBattleEventsReceiver;
|
||||
class IGameEventsReceiver;
|
||||
struct ArtifactLocation;
|
||||
class BattleStateInfoForRetreat;
|
||||
class IMarket;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -73,8 +74,8 @@ public:
|
||||
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 CGObjectInstance * market, EMarketMode::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 CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
|
||||
virtual void trade(const IMarket * market, EMarketMode::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::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(const JsonNode & reply, QueryID queryID) =0;
|
||||
@ -168,8 +169,8 @@ public:
|
||||
void endTurn() override;
|
||||
void swapGarrisonHero(const CGTownInstance *town) override;
|
||||
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
|
||||
void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const IMarket * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const IMarket * market, EMarketMode::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;
|
||||
|
BIN
CI/android/android-release.jks
Normal file
BIN
CI/android/android-release.jks
Normal file
Binary file not shown.
2
CI/android/releaseSigning.properties
Normal file
2
CI/android/releaseSigning.properties
Normal file
@ -0,0 +1,2 @@
|
||||
STORE_FILE=android-release.jks
|
||||
KEY_ALIAS=vcmi
|
@ -251,6 +251,7 @@ if(MINGW OR MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4250") # 4250: 'class1' : inherits 'class2::member' via dominance
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251") # 4251: class 'xxx' needs to have dll-interface to be used by clients of class 'yyy'
|
||||
|
@ -45,6 +45,15 @@
|
||||
"CMAKE_INSTALL_PREFIX" : "/usr/local"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-test",
|
||||
"inherits": "linux-release",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"ENABLE_TEST": "ON",
|
||||
"ENABLE_LUA": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"displayName": "Clang x86_64-pc-linux-gnu",
|
||||
@ -67,6 +76,28 @@
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-test",
|
||||
"displayName": "Clang x86_64-pc-linux-gnu with unit testing",
|
||||
"description": "VCMI Linux Clang",
|
||||
"inherits": "linux-test",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "/usr/bin/clang",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/clang++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-test",
|
||||
"displayName": "GCC x86_64-pc-linux-gnu with unit testing",
|
||||
"description": "VCMI Linux GCC",
|
||||
"inherits": "linux-test",
|
||||
"cacheVariables": {
|
||||
"ENABLE_LUA" : "OFF",
|
||||
"ENABLE_PCH" : "OFF",
|
||||
"CMAKE_C_COMPILER": "/usr/bin/gcc",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"displayName": "Windows x64 RelWithDebInfo",
|
||||
@ -213,6 +244,16 @@
|
||||
"configurePreset": "linux-clang-release",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-test",
|
||||
"configurePreset": "linux-clang-test",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-test",
|
||||
"configurePreset": "linux-gcc-test",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-release",
|
||||
"configurePreset": "linux-gcc-release",
|
||||
@ -295,6 +336,16 @@
|
||||
"configurePreset": "linux-gcc-release",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-test",
|
||||
"configurePreset": "linux-clang-test",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-test",
|
||||
"configurePreset": "linux-gcc-test",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "macos-xcode-release",
|
||||
"configurePreset": "macos-xcode-release",
|
||||
|
25
ChangeLog.md
25
ChangeLog.md
@ -1,6 +1,29 @@
|
||||
# 1.2.0 -> 1.3.0
|
||||
# 1.2.1 -> 1.3.0
|
||||
(unreleased)
|
||||
|
||||
# 1.2.0 -> 1.2.1
|
||||
|
||||
### GENERAL:
|
||||
* Implemented spell range overlay for Dimension Door and Scuttle Boat
|
||||
* Fixed movement cost penalty from terrain
|
||||
* Fixed empty Black Market on game start
|
||||
* Fixed bad morale happening after waiting
|
||||
* Fixed good morale happening after defeating last enemy unit
|
||||
* Fixed death animation of Efreeti killed by petrification attack
|
||||
* Fixed crash on leaving to main menu from battle in hotseat mode
|
||||
* Fixed music playback on switching between towns
|
||||
* Special months (double growth and plague) will now appear correctly
|
||||
* Adventure map spells are no longer visible on units in battle
|
||||
* Attempt to cast spell with no valid targets in hotseat will show appropriate error message
|
||||
* RMG settings will now show all existing in game templates and not just those suitable for current settings
|
||||
* RMG settings (map size and two-level maps) that are not compatible with current template will be blocked
|
||||
* Fixed centering of scenario information window
|
||||
* Fixed crash on empty save game list after filtering
|
||||
* Fixed blocked progress in Launcher on language detection failure
|
||||
* Launcher will now correctly handle selection of Ddata directory in H3 install
|
||||
* Map editor will now correctly save message property for events and pandoras
|
||||
* Fixed incorrect saving of heroes portraits in editor
|
||||
|
||||
# 1.1.1 -> 1.2.0
|
||||
|
||||
### GENERAL:
|
||||
|
6
Global.h
6
Global.h
@ -543,10 +543,12 @@ namespace vstd
|
||||
});
|
||||
}
|
||||
|
||||
/// Increments value by specific delta
|
||||
/// similar to std::next but works with other types, e.g. enum class
|
||||
template<typename T>
|
||||
void advance(T &obj, int change)
|
||||
T next(const T &obj, int change)
|
||||
{
|
||||
obj = (T)(((int)obj) + change);
|
||||
return static_cast<T>(static_cast<ptrdiff_t>(obj) + change);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 96 B |
BIN
Mods/vcmi/Data/debug/spellRange.png
Normal file
BIN
Mods/vcmi/Data/debug/spellRange.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 B |
@ -1,17 +1,17 @@
|
||||
{
|
||||
"vcmi.adventureMap.monsterThreat.title" : "\n\n 威胁等级: ",
|
||||
"vcmi.adventureMap.monsterThreat.levels.0" : "极低",
|
||||
"vcmi.adventureMap.monsterThreat.title" : "\n\n威胁度: ",
|
||||
"vcmi.adventureMap.monsterThreat.levels.0" : "不值一提",
|
||||
"vcmi.adventureMap.monsterThreat.levels.1" : "很低",
|
||||
"vcmi.adventureMap.monsterThreat.levels.2" : "低",
|
||||
"vcmi.adventureMap.monsterThreat.levels.3" : "较低",
|
||||
"vcmi.adventureMap.monsterThreat.levels.4" : "中等",
|
||||
"vcmi.adventureMap.monsterThreat.levels.4" : "势均力敌",
|
||||
"vcmi.adventureMap.monsterThreat.levels.5" : "较高",
|
||||
"vcmi.adventureMap.monsterThreat.levels.6" : "高",
|
||||
"vcmi.adventureMap.monsterThreat.levels.7" : "很高",
|
||||
"vcmi.adventureMap.monsterThreat.levels.8" : "挑战性的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.8" : "略有挑战",
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "压倒性的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "致命的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "自寻死路",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "天方夜谭",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "你想要重新开始游戏吗?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。",
|
||||
@ -21,100 +21,139 @@
|
||||
"vcmi.adventureMap.moveCostDetails" : "移动点数 - 花费: %TURNS 轮 + %POINTS 点移动力, 剩余移动力: %REMAINING",
|
||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "移动点数 - 花费: %POINTS 点移动力, 剩余移动力: %REMAINING",
|
||||
|
||||
"vcmi.server.errors.existingProcess" : "另一个VCMI进程在运行,请结束当前进程。",
|
||||
"vcmi.server.errors.modsIncompatibility" : "需要加载mod:",
|
||||
"vcmi.server.confirmReconnect" : "连接到上次吗?",
|
||||
"vcmi.capitalColors.0" : "红色",
|
||||
"vcmi.capitalColors.1" : "蓝色",
|
||||
"vcmi.capitalColors.2" : "青色",
|
||||
"vcmi.capitalColors.3" : "绿色",
|
||||
"vcmi.capitalColors.4" : "橙色",
|
||||
"vcmi.capitalColors.5" : "紫色",
|
||||
"vcmi.capitalColors.6" : "褐色",
|
||||
"vcmi.capitalColors.7" : "粉色",
|
||||
|
||||
"vcmi.settingsMainWindow.generalTab.hover" : "常规",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "切换到“系统选项”选项卡 - 这些设置与常规游戏客户端行为相关",
|
||||
"vcmi.settingsMainWindow.battleTab.hover" : "战斗",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "切换到“战斗选项”选项卡 - 这些设置允许配置战斗界面和相关内容",
|
||||
"vcmi.server.errors.existingProcess" : "一个VCMI进程已经在运行,启动新进程前请结束它。",
|
||||
"vcmi.server.errors.modsIncompatibility" : "需要加载的MOD列表:",
|
||||
"vcmi.server.confirmReconnect" : "您想要重连上一个会话么?",
|
||||
|
||||
"vcmi.settingsMainWindow.generalTab.hover" : "常规",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "切换到“常规”选项卡 - 设置游戏客户端呈现",
|
||||
"vcmi.settingsMainWindow.battleTab.hover" : "战斗",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "切换到“战斗”选项卡 - 这些设置允许配置战斗界面和相关内容",
|
||||
"vcmi.settingsMainWindow.adventureTab.hover" : "冒险地图",
|
||||
"vcmi.settingsMainWindow.adventureTab.help" : "切换到“冒险地图”选项卡 - 冒险地图允许你移动英雄",
|
||||
"vcmi.settingsMainWindow.otherTab.hover" : "其他设置",
|
||||
"vcmi.settingsMainWindow.otherTab.help" : "切换到“其他设置”选项卡 - 由于各种原因,这些选项不适合其他类别",
|
||||
"vcmi.settingsMainWindow.adventureTab.help" : "切换到“冒险地图”选项卡 - 冒险地图即玩家能操作英雄移动的界面",
|
||||
|
||||
"vcmi.systemOptions.videoGroup" : "视频设置",
|
||||
"vcmi.systemOptions.audioGroup" : "音频设置",
|
||||
"vcmi.systemOptions.otherGroup" : "其他设置", // unused right now
|
||||
"vcmi.systemOptions.townsGroup" : "城镇画面",
|
||||
|
||||
"vcmi.systemOptions.fullscreenButton.hover" : "全屏",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{全屏n}\n\n 当你选择全屏时,VCMI将会全屏运行,否则只会运行在指定框内",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{全屏}\n\n选中时,VCMI将以全屏运行,否则运行在窗口模式。",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "分辨率",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{选择分辨率}\n\n 改变游戏的分辨率,达到更加清晰的效果。需要重新启动才能完成更改。",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "选择分辨率",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "选择游戏的分辨率。",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{全屏}\n\n 选择切换到全屏失败!当前分辨率不支持全屏!",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "显示传输帧数",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{显示传输帧数}\n\n 打开/关闭在游戏窗口角落的传输帧数计数器。",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{分辨率选择}\n\n改变游戏内的分辨率,更改后需要重启游戏使其生效。",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "分辨率选择",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "修改游戏运行时的分辨率。",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{全屏}\n\n选择切换到全屏模式失败!当前显示器不支持该分辨率!",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "显示FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{显示FPS}\n\n打开/关闭在游戏窗口角落的FPS指示器。",
|
||||
|
||||
"vcmi.adventureOptions.infoBarPick.hover" : "在信息面板显示消息",
|
||||
"vcmi.adventureOptions.infoBarPick.help" : "{在信息面板显示消息}\n\n来自访问地图物件的信息将显示在信息面板,而不是弹出窗口。",
|
||||
"vcmi.adventureOptions.numericQuantities.hover" : "生物数量显示",
|
||||
"vcmi.adventureOptions.numericQuantities.help" : "{生物数量显示}\n\n 以数字 A-B 格式显示不准确的敌方生物数量。",
|
||||
"vcmi.adventureOptions.numericQuantities.help" : "{生物数量显示}\n\n以数字 A-B 格式显示不准确的敌方生物数量。",
|
||||
"vcmi.adventureOptions.forceMovementInfo.hover" : "在状态栏中显示移动力",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help" : "{在状态栏中显示移动力}\n\n 不需要按ALT就可以显示移动力。",
|
||||
"vcmi.adventureOptions.showGrid.hover" : "显示六角网格",
|
||||
"vcmi.adventureOptions.showGrid.help" : "{显示六角网格}\n\n 在战场上显示六角网格。",
|
||||
"vcmi.adventureOptions.mapScrollSpeed4.hover": "4",
|
||||
"vcmi.adventureOptions.mapScrollSpeed4.help": "设置动画速度为超快",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.hover": "5",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "设置动画速度为极速",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help" : "{在状态栏中显示移动力}\n\n不需要按ALT就可以显示移动力。",
|
||||
"vcmi.adventureOptions.showGrid.hover" : "显示网格",
|
||||
"vcmi.adventureOptions.showGrid.help" : "{显示网格}\n\n显示网格覆盖层,高亮冒险地图物件的边沿。",
|
||||
"vcmi.adventureOptions.mapSwipe.hover" : "地图拖动/镜头",
|
||||
"vcmi.adventureOptions.mapSwipe.help" : "{地图拖动/镜头}\n\n在触摸屏设备上,你可以用手指轻扫来移动地图。使用鼠标时,按住鼠标左键或中键移动地图。",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.help": "将地图卷动速度设置为非常慢",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "将地图卷动速度设置为非常快",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.help": "将地图卷动速度设置为即刻。",
|
||||
|
||||
"vcmi.battleOptions.showQueue.hover": "显示移动次序",
|
||||
"vcmi.battleOptions.showQueue.help": "{显示移动次序}\n\n 显示当前生物的移动次序。",
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "次序条尺寸 (设置后下一场战斗生效)",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.hover": "自动设置尺寸",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.help": "根据游戏分辨率设置尺寸 (像素小于700为小尺寸,根据实际调整)",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.hover": "小尺寸",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.help": "设置次序条为小尺寸",
|
||||
"vcmi.battleOptions.queueSizeBigButton.hover": "大尺寸",
|
||||
"vcmi.battleOptions.queueSizeBigButton.help": "设置次寻条为大尺寸(不能在像素小于700时生效)",
|
||||
"vcmi.battleOptions.animationsSpeed4.hover": "4",
|
||||
"vcmi.battleOptions.animationsSpeed4.help": "设置动画速度为超快",
|
||||
"vcmi.battleOptions.animationsSpeed5.hover": "5",
|
||||
"vcmi.battleOptions.animationsSpeed5.help": "设置动画速度为极速",
|
||||
"vcmi.battleOptions.animationsSpeed6.hover": "6",
|
||||
"vcmi.battleOptions.animationsSpeed6.help": "设置动画速度为最快",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "跳过开场音乐",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{跳过开场音乐}\n\n 战斗开始时跳过开场音乐,直接按Esc也可以跳过。",
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "回合顺序指示器",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.hover": "关闭",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.hover": "自动",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.hover": "小",
|
||||
"vcmi.battleOptions.queueSizeBigButton.hover": "大",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.help": "不显示回合顺序指示器",
|
||||
"vcmi.battleOptions.queueSizeAutoButton.help": "根据游戏的分辨率自动调整回合顺序指示器的大小(游戏处于高度低于700像素的分辨率时,使用小,否则使用大)",
|
||||
"vcmi.battleOptions.queueSizeSmallButton.help": "设置回合顺序指示器为小",
|
||||
"vcmi.battleOptions.queueSizeBigButton.help": "设置次寻条为大尺寸(无法在游戏高度像素低于700时生效)",
|
||||
"vcmi.battleOptions.animationsSpeed1.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed5.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed6.hover": "",
|
||||
"vcmi.battleOptions.animationsSpeed1.help": "设置动画速度为非常慢",
|
||||
"vcmi.battleOptions.animationsSpeed5.help": "设置动画速度为非常快",
|
||||
"vcmi.battleOptions.animationsSpeed6.help": "设置动画速度为即刻",
|
||||
"vcmi.battleOptions.touchscreenMode.hover": "触屏模式",
|
||||
"vcmi.battleOptions.touchscreenMode.help": "{触屏模式}\n\n当启用时,需要进行双击进行确认和执行动作。减少触屏设备误触。",
|
||||
"vcmi.battleOptions.movementHighlightOnHover.hover": "鼠标悬停高亮单位移动范围",
|
||||
"vcmi.battleOptions.movementHighlightOnHover.help": "{鼠标悬停高亮单位移动范围}\n\n当你的鼠标悬停在单位上时高亮他的行动范围。",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "跳过战斗开始音乐",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{跳过战斗开始音乐}\n\n战斗开始音乐播放期间,你也能够进行操作。",
|
||||
"vcmi.battleWindow.pressKeyToSkipIntro" : "按下任意键立即开始战斗",
|
||||
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "显示所有可以招募的城镇生物",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{显示所有可以招募的城镇生物}\n\n 显示当前所有可供招募的城镇生物 (左下角)。",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.hover": "缩小城镇生物信息",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.help": "{缩小城镇生物信息}\n\n 将城镇生物信息最小化.",
|
||||
"vcmi.battleWindow.damageEstimation.melee" : "近战攻击 %CREATURE (%DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.meleeKills" : "近战攻击 %CREATURE (%DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.ranged" : "射击 %CREATURE (%SHOTS, %DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.rangedKills" : "射击 %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.shots" : "%d 弹药剩余",
|
||||
"vcmi.battleWindow.damageEstimation.shots.1" : "%d 弹药剩余",
|
||||
"vcmi.battleWindow.damageEstimation.damage" : "%d 伤害",
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "%d 伤害",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d 将被消灭",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d 将被消灭",
|
||||
|
||||
"vcmi.townHall.missingBase" : "你必须先建造%s ",
|
||||
"vcmi.townHall.noCreaturesToRecruit" : "没有可供雇佣的生物。",
|
||||
"vcmi.townHall.greetingManaVortex" : "当你接近%s时,你的身体充满了新的能量。这使你的魔法值加倍。",
|
||||
"vcmi.townHall.greetingKnowledge" : "你学习了%s上的图形,并深入了解各种魔法的运作,这使你的知识点数+1。",
|
||||
"vcmi.townHall.greetingSpellPower" : "%s教你新的方法来集中你的魔法力量,这使你的力量点数+1。",
|
||||
"vcmi.townHall.greetingExperience" : "访问%s给你提供了更好的学习方法。这使你的经验值+1000。",
|
||||
"vcmi.townHall.greetingAttack" : "在%s参观后给你提供了更好的战斗技巧,这使你的攻击点数+1。",
|
||||
"vcmi.townHall.greetingDefence" : "在%s中度过一段时间后,经验丰富的勇士会教你额外的防御技能,这使你的防御点数+1。",
|
||||
"vcmi.battleResultsWindow.applyResultsLabel" : "接受战斗结果",
|
||||
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "显示可招募生物",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{显示可招募生物}\n\n在城镇摘要(城镇屏幕的左下角)中显示可招募的生物数量,而不是增长。",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "显示生物增长",
|
||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{显示生物增长}\n\n在城镇摘要(城镇屏幕的左下角)中显示生物的每周增长而不是可用数量。",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.hover": "紧凑生物信息",
|
||||
"vcmi.otherOptions.compactTownCreatureInfo.help": "{紧凑生物信息}\n\n城镇概要(城镇屏幕的左下角)中城镇生物信息紧凑显示。",
|
||||
|
||||
"vcmi.townHall.missingBase" : "必须先建造基础建筑 %s",
|
||||
"vcmi.townHall.noCreaturesToRecruit" : "没有可供招募的生物。",
|
||||
"vcmi.townHall.greetingManaVortex" : "接近%s时,你会全身充满活力,并且你的魔法值会加倍。",
|
||||
"vcmi.townHall.greetingKnowledge" : "你研究了%s的浮雕,洞察了魔法的秘密(知识+1)。",
|
||||
"vcmi.townHall.greetingSpellPower" : "%s教你如何运用魔法力量(力量+1)。",
|
||||
"vcmi.townHall.greetingExperience" : "参观%s可以让你学会许多新的技能(经验值+1000)。",
|
||||
"vcmi.townHall.greetingAttack" : "在%s中稍待片刻可以让你学会更有效的战斗技巧(攻击力+1)。",
|
||||
"vcmi.townHall.greetingDefence" : "在%s中稍待片刻,富有战斗经验的战士会教你防御技巧(防御力+1)。",
|
||||
"vcmi.townHall.hasNotProduced" : "本周%s并没有产生什么资源。",
|
||||
"vcmi.townHall.hasProduced" : "本周%s产生了%d个%s。",
|
||||
"vcmi.townHall.greetingCustomBonus" : "参观%s后,你的技巧有了提升。这使你受益匪浅。并且使你+%d %s%s",
|
||||
"vcmi.townHall.greetingCustomBonus" : "%s 赋予你 +%d %s%s",
|
||||
"vcmi.townHall.greetingCustomUntil" : "直到下一场战斗。",
|
||||
"vcmi.townHall.greetingInTownMagicWell" : "%s使你的魔法值恢复到最大值。",
|
||||
|
||||
"vcmi.logicalExpressions.anyOf" : "以下任何前提:",
|
||||
"vcmi.logicalExpressions.anyOf" : "以下任一前提:",
|
||||
"vcmi.logicalExpressions.allOf" : "以下所有前提:",
|
||||
"vcmi.logicalExpressions.noneOf" : "无前提:",
|
||||
|
||||
"vcmi.heroWindow.openCommander.hover" : "开启指挥官界面",
|
||||
"vcmi.heroWindow.openCommander.help" : "开启英雄的指挥官界面",
|
||||
"vcmi.heroWindow.openCommander.help" : "显示该英雄指挥官详细信息",
|
||||
|
||||
"vcmi.commanderWindow.artifactMessage" : "你要把这个宝物还给英雄吗?",
|
||||
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "属性界面",
|
||||
"vcmi.creatureWindow.showBonuses.help" : "显示指挥官的所有增强属性",
|
||||
"vcmi.creatureWindow.showSkills.hover" : "技能页面",
|
||||
"vcmi.creatureWindow.showSkills.help" : "显示指挥官的所有技能",
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "属性视图",
|
||||
"vcmi.creatureWindow.showBonuses.help" : "显示指挥官的所有属性增益",
|
||||
"vcmi.creatureWindow.showSkills.hover" : "技能视图",
|
||||
"vcmi.creatureWindow.showSkills.help" : "显示指挥官的所有学习的技能",
|
||||
"vcmi.creatureWindow.returnArtifact.hover" : "交换宝物",
|
||||
"vcmi.creatureWindow.returnArtifact.help" : "将宝物还到英雄的背包里",
|
||||
"vcmi.creatureWindow.returnArtifact.help" : "点击这个按钮将宝物反还到英雄的背包里",
|
||||
|
||||
"vcmi.questLog.hideComplete.hover" : "隐藏完成任务",
|
||||
"vcmi.questLog.hideComplete.help" : "隐藏所有完成的任务",
|
||||
|
||||
"vcmi.randomMapTab.widgets.defaultTemplate" : "默认",
|
||||
"vcmi.randomMapTab.widgets.templateLabel" : "格式",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "设置...",
|
||||
"vcmi.randomMapTab.widgets.templateLabel" : "模板",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "设定...",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "同盟关系",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "道路类型",
|
||||
|
||||
// few strings from WoG used by vcmi
|
||||
"vcmi.stackExperience.description" : "» 经 验 获 得 明 细 «\n\n生物类型 ................... : %s\n经验等级 ................. : %s (%i)\n经验点数 ............... : %i\n下一个等级所需经验 .. : %i\n每次战斗最大获得经验 ... : %i%% (%i)\n获得经验的生物数量 .... : %i\n最大招募数量\n不会丢失经验升级 .... : %i\n经验倍数 ........... : %.2f\n升级倍数 .............. : %.2f\n10级后经验值 ........ : %i\n最大招募数量下\n 升级到10级所需经验数量: %i",
|
||||
@ -130,154 +169,148 @@
|
||||
"vcmi.stackExperience.rank.9" : "中校 10级",
|
||||
"vcmi.stackExperience.rank.10" : "上校 11级",
|
||||
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "双击",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "可以攻击两次",
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "双重攻击",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "攻击两次",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name": "额外反击",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.description": "可以额外反击 ${val} 次",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.description": "每回合额外获得${val}次反击机会",
|
||||
"core.bonus.AIR_IMMUNITY.name": "气系免疫",
|
||||
"core.bonus.AIR_IMMUNITY.description": "免疫所有气系魔法",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.name": "环击",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.description": "攻击所有相邻部队",
|
||||
"core.bonus.ATTACKS_ALL_ADJACENT.description": "攻击所有相邻敌人",
|
||||
"core.bonus.BLOCKS_RETALIATION.name": "无反击",
|
||||
"core.bonus.BLOCKS_RETALIATION.description": "敌人无法反击",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "远程无反击",
|
||||
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "敌人无法对射击进行反击",
|
||||
"core.bonus.CATAPULT.name": "攻城",
|
||||
"core.bonus.CATAPULT.description": "可以攻击城墙",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "施法消耗 - (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "减少英雄的施法消耗",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "对方施法消耗 + (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "增加对方施法消耗",
|
||||
"core.bonus.CHARGE_IMMUNITY.name": "I免疫冲锋",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "施法消耗减少 (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "英雄施法消耗魔法值减少${val}点",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "施法阻碍 (${val})",
|
||||
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "敌方施法消耗的魔法值增加${val}点",
|
||||
"core.bonus.CHARGE_IMMUNITY.name": "免疫冲锋",
|
||||
"core.bonus.CHARGE_IMMUNITY.description": "对冲锋特技的额外伤害免疫",
|
||||
"core.bonus.DARKNESS.name": "黑暗天幕",
|
||||
"core.bonus.DARKNESS.description": "增加 ${val} 半径黑幕",
|
||||
"core.bonus.DARKNESS.name": "阴影庇体",
|
||||
"core.bonus.DARKNESS.description": "创建${val}半径黑幕",
|
||||
"core.bonus.DEATH_STARE.name": "死亡凝视 (${val}%)",
|
||||
"core.bonus.DEATH_STARE.description": "${val}% 几率直接杀死生物",
|
||||
"core.bonus.DEFENSIVE_STANCE.name": "防御奖励",
|
||||
"core.bonus.DEFENSIVE_STANCE.description": "当选择防御时+${val} 防御力",
|
||||
"core.bonus.DEATH_STARE.description": "${val}%几率直接杀死一单位生物",
|
||||
"core.bonus.DEFENSIVE_STANCE.name": "防御增效",
|
||||
"core.bonus.DEFENSIVE_STANCE.description": "当选择防御时+${val}防御力",
|
||||
"core.bonus.DESTRUCTION.name": "毁灭",
|
||||
"core.bonus.DESTRUCTION.description": "有${val}% 杀死额外数量的部队",
|
||||
"core.bonus.DESTRUCTION.description": "${val}%几率在攻击后追加消灭数量",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "致命一击",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% 几率造成双倍伤害",
|
||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}%几率造成双倍基础伤害",
|
||||
"core.bonus.DRAGON_NATURE.name": "龙",
|
||||
"core.bonus.DRAGON_NATURE.description": "生物属于龙类",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "魔法伤害免疫",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "对魔法伤害免疫",
|
||||
"core.bonus.DRAGON_NATURE.description": "生物拥有龙的特性",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "魔法直伤免疫",
|
||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "免疫直接造成伤害的魔法",
|
||||
"core.bonus.EARTH_IMMUNITY.name": "土系免疫",
|
||||
"core.bonus.EARTH_IMMUNITY.description": "免疫所有土系魔法",
|
||||
"core.bonus.ENCHANTER.name": "施法者",
|
||||
"core.bonus.ENCHANTER.description": "每回合群体施放 ${subtype.spell} ",
|
||||
"core.bonus.ENCHANTED.name": "魔法护身",
|
||||
"core.bonus.ENCHANTED.description": "自身被 ${subtype.spell} 魔法影响",
|
||||
"core.bonus.ENCHANTER.name": "强化师",
|
||||
"core.bonus.ENCHANTER.description": "每回合群体施放${subtype.spell}",
|
||||
"core.bonus.ENCHANTED.name": "魔法附身",
|
||||
"core.bonus.ENCHANTED.description": "永久处于${subtype.spell}影响",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "忽略防御 (${val}%)",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "攻击时忽略对方部分防御力",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "当攻击时,目标生物${val}%的防御力将被无视。",
|
||||
"core.bonus.FIRE_IMMUNITY.name": "火系免疫",
|
||||
"core.bonus.FIRE_IMMUNITY.description": "免疫所有火系魔法",
|
||||
"core.bonus.FIRE_SHIELD.name": "烈火神盾 (${val}%)",
|
||||
"core.bonus.FIRE_SHIELD.description": "拥有烈火神盾护身",
|
||||
"core.bonus.FIRE_SHIELD.description": "反弹部分受到的近战伤害",
|
||||
"core.bonus.FIRST_STRIKE.name": "抢先攻击",
|
||||
"core.bonus.FIRST_STRIKE.description": "在被反击前做出攻击",
|
||||
"core.bonus.FIRST_STRIKE.description": "该生物的反击将会在被攻击前进行",
|
||||
"core.bonus.FEAR.name": "恐惧",
|
||||
"core.bonus.FEAR.description": "引起恐惧",
|
||||
"core.bonus.FEAR.description": "使得敌方一只部队恐惧",
|
||||
"core.bonus.FEARLESS.name": "无惧",
|
||||
"core.bonus.FEARLESS.description": "免疫恐惧",
|
||||
"core.bonus.FLYING.name": "飞行兵种",
|
||||
"core.bonus.FLYING.description": "生物可以飞行",
|
||||
"core.bonus.FEARLESS.description": "免疫恐惧特质",
|
||||
"core.bonus.FLYING.name": "飞行",
|
||||
"core.bonus.FLYING.description": "以飞行的方式移动(无视障碍)",
|
||||
"core.bonus.FREE_SHOOTING.name": "近身射击",
|
||||
"core.bonus.FREE_SHOOTING.description": "靠近敌方也能射击",
|
||||
"core.bonus.FULL_HP_REGENERATION.name": "重生",
|
||||
"core.bonus.FULL_HP_REGENERATION.description": "可以自动恢复所有生命值",
|
||||
"core.bonus.GARGOYLE.name": "石像鬼属性",
|
||||
"core.bonus.FREE_SHOOTING.description": "能在近战范围内进行射击",
|
||||
"core.bonus.GARGOYLE.name": "石像鬼",
|
||||
"core.bonus.GARGOYLE.description": "不能被复活或治疗",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "减少伤害 (${val}%)",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "受攻击时减少受到的伤害",
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "减少从远程和近战中遭受的物理伤害",
|
||||
"core.bonus.HATE.name": "${subtype.creature}的死敌",
|
||||
"core.bonus.HATE.description": "对该部队造成 ${val}% 的额外伤害",
|
||||
"core.bonus.HEALER.name": "治疗",
|
||||
"core.bonus.HATE.description": "对${subtype.creature}造成额外${val}%伤害",
|
||||
"core.bonus.HEALER.name": "治疗者",
|
||||
"core.bonus.HEALER.description": "可以治疗友军单位",
|
||||
"core.bonus.HP_REGENERATION.name": "重生",
|
||||
"core.bonus.HP_REGENERATION.description": "每回合恢复 ${val} 点生命值",
|
||||
"core.bonus.JOUSTING.name": "冲锋",
|
||||
"core.bonus.JOUSTING.description": "每格行动增加+5%伤害",
|
||||
"core.bonus.KING1.name": "一般顶级怪物",
|
||||
"core.bonus.KING1.description": "被初级屠戮成性影响",
|
||||
"core.bonus.KING2.name": "智慧顶级怪物",
|
||||
"core.bonus.KING2.description": "被中级屠戮成性影响",
|
||||
"core.bonus.KING3.name": "精神顶级怪物",
|
||||
"core.bonus.KING3.description":"被高级屠戮成性影响",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "免疫 1-${val} 级魔法",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "免疫等级为 1-${val} 级的所有魔法",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "半程射击",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "超过 ${val} 格不能射击",
|
||||
"core.bonus.HP_REGENERATION.name": "再生",
|
||||
"core.bonus.HP_REGENERATION.description": "每回合恢复${SHval}点生命值",
|
||||
"core.bonus.JOUSTING.name": "英勇冲锋",
|
||||
"core.bonus.JOUSTING.description": "每移动一格 +${val}%伤害",
|
||||
"core.bonus.KING.name": "王牌",
|
||||
"core.bonus.KING.description": "受${val}级或更高级屠戮成性影响",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "免疫1-${val}级魔法",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "免疫1-${val}级的魔法",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "受限射击距离",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "无法以${val}格外的单位为射击目标",
|
||||
"core.bonus.LIFE_DRAIN.name": "吸取生命 (${val}%)",
|
||||
"core.bonus.LIFE_DRAIN.description": "吸取 ${val}% 伤害回复自身",
|
||||
"core.bonus.MANA_CHANNELING.name": "偷取魔法 ${val}%",
|
||||
"core.bonus.MANA_CHANNELING.description": "偷取部分敌人施法消耗",
|
||||
"core.bonus.LIFE_DRAIN.description": "吸取${val}%伤害回复自身",
|
||||
"core.bonus.MANA_CHANNELING.name": "魔法通道${val}%",
|
||||
"core.bonus.MANA_CHANNELING.description": "使你的英雄有${val}%几率获得敌人施法的魔法值",
|
||||
"core.bonus.MANA_DRAIN.name": "吸取魔力",
|
||||
"core.bonus.MANA_DRAIN.description": "每回合吸取 ${val} 魔法值",
|
||||
"core.bonus.MAGIC_MIRROR.name": "带有魔法神镜 (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "${val}% 几率反射魔法",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "(${MR}%) 魔法抵抗",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "${MR}% 几率抵抗敌人的魔法",
|
||||
"core.bonus.MANA_DRAIN.description": "每回合吸取${val}魔法值",
|
||||
"core.bonus.MAGIC_MIRROR.name": "魔法神镜 (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "${val}%几率将进攻性魔法导向一个敌人单位",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "魔法抵抗 (${val}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "${val}%几率抵抗敌人的魔法",
|
||||
"core.bonus.MIND_IMMUNITY.name": "免疫心智",
|
||||
"core.bonus.MIND_IMMUNITY.description": "不受心智魔法的影响",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "无障碍射击",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "射击不受距离影响",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "无视距离惩罚",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "任意距离均造成全额伤害",
|
||||
"core.bonus.NO_MELEE_PENALTY.name": "无近战惩罚",
|
||||
"core.bonus.NO_MELEE_PENALTY.description": "近战伤害不减",
|
||||
"core.bonus.NO_MELEE_PENALTY.description": "该生物没有近战伤害惩罚",
|
||||
"core.bonus.NO_MORALE.name": "无士气",
|
||||
"core.bonus.NO_MORALE.description": "生物不受士气影响",
|
||||
"core.bonus.NO_WALL_PENALTY.name": "无城墙影响",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "射击不受城墙的影响",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "攻城战中被城墙阻挡造成全额伤害",
|
||||
"core.bonus.NON_LIVING.name": "无生命",
|
||||
"core.bonus.NON_LIVING.description": "不受只对生命实体生物有效的魔法",
|
||||
"core.bonus.NON_LIVING.description": "免疫大多数的效果",
|
||||
"core.bonus.RANDOM_SPELLCASTER.name": "随机施法",
|
||||
"core.bonus.RANDOM_SPELLCASTER.description": "随机施放增益魔法",
|
||||
"core.bonus.RANDOM_SPELLCASTER.description": "可以施放随机魔法",
|
||||
"core.bonus.RANGED_RETALIATION.name": "远程反击",
|
||||
"core.bonus.RANGED_RETALIATION.description": "可以对远程攻击进行反击",
|
||||
"core.bonus.RECEPTIVE.name": "接受有益魔法",
|
||||
"core.bonus.RECEPTIVE.description": "不会免疫有益的魔法",
|
||||
"core.bonus.RECEPTIVE.name": "适应",
|
||||
"core.bonus.RECEPTIVE.description": "不会免疫有益魔法",
|
||||
"core.bonus.REBIRTH.name": "复生 (${val}%)",
|
||||
"core.bonus.REBIRTH.description": "{val}% 数量死亡后会复活",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "攻击并返回",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "攻击后回到初始位置",
|
||||
"core.bonus.SHOOTER.name": "射手",
|
||||
"core.bonus.SHOOTER.description": "生物可以设计",
|
||||
"core.bonus.REBIRTH.description": "当整支部队死亡后${val}%会复活",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "攻击后返回",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "近战攻击后回到初始位置",
|
||||
"core.bonus.SHOOTER.name": "远程攻击",
|
||||
"core.bonus.SHOOTER.description": "生物可以射击",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.name": "范围远程攻击",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "远程攻击可伤害范围内的多个目标",
|
||||
"core.bonus.SOUL_STEAL.name": "杀死敌人复生",
|
||||
"core.bonus.SOUL_STEAL.description": "当杀死敌人时获得 ${val} 数量",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "该生物的远程攻击将同时命中小范围内所有目标",
|
||||
"core.bonus.SOUL_STEAL.name": "灵魂窃取",
|
||||
"core.bonus.SOUL_STEAL.description": "每杀死一个敌人将获得${val}数量的生物",
|
||||
"core.bonus.SPELLCASTER.name": "施法者",
|
||||
"core.bonus.SPELLCASTER.description": "生物可以施放 ${subtype.spell}",
|
||||
"core.bonus.SPELLCASTER.description": "生物可以施放${subtype.spell}",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.name": "攻击后施法",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% 攻击后施放 ${subtype.spell}",
|
||||
"core.bonus.SPELL_AFTER_ATTACK.description": "攻击后${val}%几率施放${subtype.spell}魔法",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.name": "攻击前施法",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% 攻击前施放 ${subtype.spell}",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "魔法伤害抵抗",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "受魔法攻击时伤害减少 ${val}%",
|
||||
"core.bonus.SPELL_BEFORE_ATTACK.description": "攻击前${val}%几率施放${subtype.spell}魔法",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "魔法伤害减免",
|
||||
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "受魔法攻击时伤害减少${val}%",
|
||||
"core.bonus.SPELL_IMMUNITY.name": "特定魔法免疫",
|
||||
"core.bonus.SPELL_IMMUNITY.description": "免疫 ${subtype.spell}",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.name": "魔法攻击",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.description": "攻击时使用 ${subtype.spell}",
|
||||
"core.bonus.SPELL_IMMUNITY.description": "免疫${subtype.spell}魔法",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.name": "类施法攻击",
|
||||
"core.bonus.SPELL_LIKE_ATTACK.description": "攻击时使用${subtype.spell}",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.name": "抗魔光环",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.description": "邻近部队获得 ${val}% 魔法抵抗",
|
||||
"core.bonus.SPELL_RESISTANCE_AURA.description": "邻近部队获得${val}%几率抵抗魔法",
|
||||
"core.bonus.SUMMON_GUARDIANS.name": "召唤守卫",
|
||||
"core.bonus.SUMMON_GUARDIANS.description": "战斗前召唤 ${subtype.creature} (${val}%)",
|
||||
"core.bonus.SYNERGY_TARGET.name": "可协助攻击",
|
||||
"core.bonus.SUMMON_GUARDIANS.description": "战斗开始时召唤${subtype.creature}(${val}%)",
|
||||
"core.bonus.SYNERGY_TARGET.name": "协同攻击",
|
||||
"core.bonus.SYNERGY_TARGET.description": "生物受到协助攻击的影响",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "龙息",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "吐息攻击2个部队",
|
||||
"core.bonus.THREE_HEADED_ATTACK.name": "半环击",
|
||||
"core.bonus.THREE_HEADED_ATTACK.description": "攻击正前方多个敌人",
|
||||
"core.bonus.TRANSMUTATION.name": "变换",
|
||||
"core.bonus.TRANSMUTATION.description": "${val}% 机会将敌人变成其他生物",
|
||||
"core.bonus.UNDEAD.name": "不死生物",
|
||||
"core.bonus.UNDEAD.description": "生物有丧尸属性",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "吐息",
|
||||
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "吐息攻击(2格范围)",
|
||||
"core.bonus.THREE_HEADED_ATTACK.name": "三头攻击",
|
||||
"core.bonus.THREE_HEADED_ATTACK.description": "攻击三格邻接单位",
|
||||
"core.bonus.TRANSMUTATION.name": "变形术",
|
||||
"core.bonus.TRANSMUTATION.description": "${val}%机会将被攻击单位变成其他生物",
|
||||
"core.bonus.UNDEAD.name": "亡灵",
|
||||
"core.bonus.UNDEAD.description": "该生物属于亡灵",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.name": "无限反击",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.description": "每回合可以无限反击敌人",
|
||||
"core.bonus.WATER_IMMUNITY.name": "水系免疫",
|
||||
"core.bonus.WATER_IMMUNITY.description": "免疫水系魔法",
|
||||
"core.bonus.WIDE_BREATH.name": "弧形焰息",
|
||||
"core.bonus.WIDE_BREATH.description": "吐息攻击前方扇形6个部队"
|
||||
"core.bonus.WIDE_BREATH.description": "大范围喷吐攻击(目标左右以及后方共6格)"
|
||||
}
|
||||
|
@ -21,6 +21,15 @@
|
||||
"vcmi.adventureMap.moveCostDetails" : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
|
||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
|
||||
|
||||
"vcmi.capitalColors.0" : "Red",
|
||||
"vcmi.capitalColors.1" : "Blue",
|
||||
"vcmi.capitalColors.2" : "Tan",
|
||||
"vcmi.capitalColors.3" : "Green",
|
||||
"vcmi.capitalColors.4" : "Orange",
|
||||
"vcmi.capitalColors.5" : "Purple",
|
||||
"vcmi.capitalColors.6" : "Teal",
|
||||
"vcmi.capitalColors.7" : "Pink",
|
||||
|
||||
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
||||
"vcmi.server.errors.modsIncompatibility" : "The following mods are required to load the game:",
|
||||
"vcmi.server.confirmReconnect" : "Do you want to reconnect to the last session?",
|
||||
@ -225,7 +234,7 @@
|
||||
"core.bonus.HEALER.name": "Healer",
|
||||
"core.bonus.HEALER.description": "Heals allied units",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneration",
|
||||
"core.bonus.HP_REGENERATION.description": "Heals ${SHval} hit points every round",
|
||||
"core.bonus.HP_REGENERATION.description": "Heals ${val} hit points every round",
|
||||
"core.bonus.JOUSTING.name": "Champion charge",
|
||||
"core.bonus.JOUSTING.description": "+${val}% damage for each hex travelled",
|
||||
"core.bonus.KING.name": "King",
|
||||
|
@ -21,6 +21,15 @@
|
||||
"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.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
|
||||
"vcmi.server.errors.modsIncompatibility" : "Erforderliche Mods um das Spiel zu laden:",
|
||||
"vcmi.server.confirmReconnect" : "Mit der letzten Sitzung verbinden?",
|
||||
@ -220,7 +229,7 @@
|
||||
"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 ${SHval} Trefferpunkte jede Runde",
|
||||
"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",
|
||||
|
@ -208,7 +208,7 @@
|
||||
"core.bonus.HEALER.name": "Uzdrowiciel",
|
||||
"core.bonus.HEALER.description": "Leczy sprzymierzone jednostki",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneracja",
|
||||
"core.bonus.HP_REGENERATION.description": "Leczy ${SHval} punktów zdrowia każdej rundy",
|
||||
"core.bonus.HP_REGENERATION.description": "Leczy ${val} punktów zdrowia każdej rundy",
|
||||
"core.bonus.JOUSTING.name": "Szarża Czempiona",
|
||||
"core.bonus.JOUSTING.description": "+${val}% obrażeń na przebytego heksa",
|
||||
"core.bonus.KING.name": "Król",
|
||||
|
@ -112,9 +112,8 @@
|
||||
"matchTerrainToTown" : false,
|
||||
"treasure" :
|
||||
[
|
||||
{ "min" : 0, "max" : 0, "density" : 1 },
|
||||
{ "min" : 0, "max" : 0, "density" : 1 },
|
||||
{ "min" : 0, "max" : 0, "density" : 1 }
|
||||
{ "min" : 100, "max" : 1000, "density" : 10 },
|
||||
{ "min" : 1000, "max" : 3000, "density" : 1 }
|
||||
]
|
||||
},
|
||||
"12" :
|
||||
|
@ -232,7 +232,7 @@
|
||||
"core.bonus.HEALER.name": "Целитель",
|
||||
"core.bonus.HEALER.description": "Исцеляет дружественные юниты",
|
||||
"core.bonus.HP_REGENERATION.name": "Регенерация",
|
||||
"core.bonus.HP_REGENERATION.description": "Исцеляет ${SHval} очков здоровья каждый ход",
|
||||
"core.bonus.HP_REGENERATION.description": "Исцеляет ${val} очков здоровья каждый ход",
|
||||
"core.bonus.JOUSTING.name": "Разгон",
|
||||
"core.bonus.JOUSTING.description": "+${val}% урона за каждую пройденную клетку",
|
||||
"core.bonus.KING.name": "Король",
|
||||
|
@ -208,7 +208,7 @@
|
||||
"core.bonus.HEALER.name" : "Цілитель",
|
||||
"core.bonus.HEALER.description" : "Лікує союзників",
|
||||
"core.bonus.HP_REGENERATION.name" : "Регенерація",
|
||||
"core.bonus.HP_REGENERATION.description" : "Відновлює ${SHval} очок здоров'я кожного раунду",
|
||||
"core.bonus.HP_REGENERATION.description" : "Відновлює ${val} очок здоров'я кожного раунду",
|
||||
"core.bonus.JOUSTING.name" : "Турнірна перевага",
|
||||
"core.bonus.JOUSTING.description" : "+${val}% шкоди за кожен пройдений гекс",
|
||||
"core.bonus.KING.name" : "Король",
|
||||
|
@ -1,5 +1,6 @@
|
||||
[![GitHub](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg)](https://github.com/vcmi/vcmi/actions/workflows/github.yml)
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/vcmi/badge.svg)](https://scan.coverity.com/projects/vcmi)
|
||||
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.2.1/total)](https://github.com/vcmi/vcmi/releases/tag/1.2.1)
|
||||
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.2.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.2.0)
|
||||
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
|
||||
# VCMI Project
|
||||
|
@ -10,14 +10,16 @@ android {
|
||||
applicationId "is.xyz.vcmi"
|
||||
minSdk 19
|
||||
targetSdk 31
|
||||
versionCode 1103
|
||||
versionName "1.1"
|
||||
versionCode 1201
|
||||
versionName "1.2.1"
|
||||
setProperty("archivesBaseName", "vcmi")
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
releaseSigning
|
||||
LoadSigningConfig()
|
||||
dailySigning
|
||||
LoadSigningConfig("releaseSigning")
|
||||
LoadSigningConfig("dailySigning")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@ -46,6 +48,7 @@ android {
|
||||
daily {
|
||||
initWith release
|
||||
applicationIdSuffix '.daily'
|
||||
signingConfig signingConfigs.dailySigning
|
||||
manifestPlaceholders = [
|
||||
applicationLabel: 'VCMI daily',
|
||||
]
|
||||
@ -118,38 +121,48 @@ def ResolveGitInfo() {
|
||||
CommandOutput("git", ["describe", "--match=", "--always", "--abbrev=7"], ".")
|
||||
}
|
||||
|
||||
def SigningPropertiesPath(final basePath) {
|
||||
return file("${basePath}/signing.properties")
|
||||
def SigningPropertiesPath(final basePath, final signingConfigKey) {
|
||||
return file("${basePath}/${signingConfigKey}.properties")
|
||||
}
|
||||
|
||||
def SigningKeystorePath(final basePath, final keystoreFileName) {
|
||||
return file("${basePath}/${keystoreFileName}")
|
||||
}
|
||||
|
||||
def LoadSigningConfig() {
|
||||
def LoadSigningConfig(final signingConfigKey) {
|
||||
final def projectRoot = "${project.projectDir}/../../CI/android"
|
||||
final def props = new Properties()
|
||||
final def propFile = SigningPropertiesPath(projectRoot)
|
||||
final def propFile = SigningPropertiesPath(projectRoot, signingConfigKey)
|
||||
|
||||
def signingConfig = android.signingConfigs.getAt(signingConfigKey)
|
||||
|
||||
if (propFile.canRead()) {
|
||||
props.load(new FileInputStream(propFile))
|
||||
|
||||
if (props != null
|
||||
&& props.containsKey('STORE_FILE')
|
||||
&& props.containsKey('STORE_PASSWORD')
|
||||
&& props.containsKey('KEY_ALIAS')
|
||||
&& props.containsKey('KEY_PASSWORD')) {
|
||||
&& props.containsKey('KEY_ALIAS')) {
|
||||
|
||||
android.signingConfigs.releaseSigning.storeFile = SigningKeystorePath(projectRoot, props['STORE_FILE'])
|
||||
android.signingConfigs.releaseSigning.storePassword = props['STORE_PASSWORD']
|
||||
android.signingConfigs.releaseSigning.keyAlias = props['KEY_ALIAS']
|
||||
android.signingConfigs.releaseSigning.keyPassword = props['KEY_PASSWORD']
|
||||
signingConfig.storeFile = SigningKeystorePath(projectRoot, props['STORE_FILE'])
|
||||
signingConfig.storePassword = props['STORE_PASSWORD']
|
||||
signingConfig.keyAlias = props['KEY_ALIAS']
|
||||
|
||||
if(props.containsKey('STORE_PASSWORD'))
|
||||
signingConfig.storePassword = props['STORE_PASSWORD']
|
||||
else
|
||||
signingConfig.storePassword = System.getenv("ANDROID_STORE_PASSWORD")
|
||||
|
||||
if(props.containsKey('KEY_PASSWORD'))
|
||||
signingConfig.keyPassword = props['KEY_PASSWORD']
|
||||
else
|
||||
signingConfig.keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
|
||||
} else {
|
||||
println("Some props from signing file are missing")
|
||||
android.buildTypes.release.signingConfig = null
|
||||
android.signingConfigs.putAt(signingConfigKey, null)
|
||||
}
|
||||
} else {
|
||||
println("file with signing properties is missing")
|
||||
android.buildTypes.release.signingConfig = null
|
||||
android.signingConfigs.putAt(signingConfigKey, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ set(client_SRCS
|
||||
../CCallback.cpp
|
||||
|
||||
adventureMap/CAdvMapPanel.cpp
|
||||
adventureMap/CAdvMapInt.cpp
|
||||
adventureMap/CAdventureMapInterface.cpp
|
||||
adventureMap/CAdventureOptions.cpp
|
||||
adventureMap/CInGameConsole.cpp
|
||||
adventureMap/CInfoBar.cpp
|
||||
@ -31,6 +31,7 @@ set(client_SRCS
|
||||
gui/CursorHandler.cpp
|
||||
gui/InterfaceObjectConfigurable.cpp
|
||||
gui/NotificationHandler.cpp
|
||||
gui/ShortcutHandler.cpp
|
||||
|
||||
lobby/CBonusSelection.cpp
|
||||
lobby/CCampaignInfoScreen.cpp
|
||||
@ -85,6 +86,12 @@ set(client_SRCS
|
||||
widgets/MiscWidgets.cpp
|
||||
widgets/ObjectLists.cpp
|
||||
widgets/TextControls.cpp
|
||||
widgets/CArtifactsOfHeroBase.cpp
|
||||
widgets/CArtifactsOfHeroMain.cpp
|
||||
widgets/CArtifactsOfHeroKingdom.cpp
|
||||
widgets/CArtifactsOfHeroAltar.cpp
|
||||
widgets/CArtifactsOfHeroMarket.cpp
|
||||
widgets/CWindowWithArtifacts.cpp
|
||||
|
||||
windows/CCastleInterface.cpp
|
||||
windows/CCreatureWindow.cpp
|
||||
@ -110,6 +117,7 @@ set(client_SRCS
|
||||
CMT.cpp
|
||||
CMusicHandler.cpp
|
||||
CPlayerInterface.cpp
|
||||
PlayerLocalState.cpp
|
||||
CServerHandler.cpp
|
||||
CVideoHandler.cpp
|
||||
Client.cpp
|
||||
@ -122,7 +130,7 @@ set(client_HEADERS
|
||||
StdInc.h
|
||||
|
||||
adventureMap/CAdvMapPanel.h
|
||||
adventureMap/CAdvMapInt.h
|
||||
adventureMap/CAdventureMapInterface.h
|
||||
adventureMap/CAdventureOptions.h
|
||||
adventureMap/CInGameConsole.h
|
||||
adventureMap/CInfoBar.h
|
||||
@ -152,6 +160,8 @@ set(client_HEADERS
|
||||
gui/InterfaceObjectConfigurable.h
|
||||
gui/MouseButton.h
|
||||
gui/NotificationHandler.h
|
||||
gui/Shortcut.h
|
||||
gui/ShortcutHandler.h
|
||||
gui/TextAlignment.h
|
||||
|
||||
lobby/CBonusSelection.h
|
||||
@ -213,6 +223,12 @@ set(client_HEADERS
|
||||
widgets/MiscWidgets.h
|
||||
widgets/ObjectLists.h
|
||||
widgets/TextControls.h
|
||||
widgets/CArtifactsOfHeroBase.h
|
||||
widgets/CArtifactsOfHeroMain.h
|
||||
widgets/CArtifactsOfHeroKingdom.h
|
||||
widgets/CArtifactsOfHeroAltar.h
|
||||
widgets/CArtifactsOfHeroMarket.h
|
||||
widgets/CWindowWithArtifacts.h
|
||||
|
||||
windows/CCastleInterface.h
|
||||
windows/CCreatureWindow.h
|
||||
@ -238,6 +254,7 @@ set(client_HEADERS
|
||||
CMT.h
|
||||
CMusicHandler.h
|
||||
CPlayerInterface.h
|
||||
PlayerLocalState.h
|
||||
CServerHandler.h
|
||||
CVideoHandler.h
|
||||
Client.h
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
struct _Mix_Music;
|
||||
struct SDL_RWops;
|
||||
typedef struct _Mix_Music Mix_Music;
|
||||
using Mix_Music = struct _Mix_Music;
|
||||
struct Mix_Chunk;
|
||||
|
||||
class CAudioBase {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,16 +9,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../lib/FunctionList.h"
|
||||
#include "../lib/CGameInterface.h"
|
||||
#include "../lib/NetPacksBase.h"
|
||||
#include "gui/CIntObject.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define sprintf_s snprintf
|
||||
#endif
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class Artifact;
|
||||
@ -37,7 +31,7 @@ struct CPathsInfo;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CButton;
|
||||
class CAdvMapInt;
|
||||
class CAdventureMapInterface;
|
||||
class CCastleInterface;
|
||||
class BattleInterface;
|
||||
class CComponent;
|
||||
@ -51,103 +45,63 @@ class ClickableR;
|
||||
class Hoverable;
|
||||
class KeyInterested;
|
||||
class MotionInterested;
|
||||
class PlayerLocalState;
|
||||
class TimeInterested;
|
||||
class IShowable;
|
||||
|
||||
struct SDL_Surface;
|
||||
union SDL_Event;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
class mutex;
|
||||
class recursive_mutex;
|
||||
}
|
||||
|
||||
class CPlayerInterface;
|
||||
|
||||
class HeroPathStorage
|
||||
{
|
||||
CPlayerInterface & owner;
|
||||
|
||||
std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
|
||||
|
||||
public:
|
||||
explicit HeroPathStorage(CPlayerInterface &owner);
|
||||
|
||||
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);
|
||||
|
||||
template <typename Handler>
|
||||
void serialize( Handler &h, int version );
|
||||
};
|
||||
|
||||
/// Central class for managing user interface logic
|
||||
class CPlayerInterface : public CGameInterface, public IUpdateable
|
||||
{
|
||||
public:
|
||||
HeroPathStorage paths;
|
||||
bool duringMovement;
|
||||
bool ignoreEvents;
|
||||
size_t numOfMovedArts;
|
||||
|
||||
// -1 - just loaded game; 1 - just started game; 0 otherwise
|
||||
int firstCall;
|
||||
int autosaveCount;
|
||||
static const int SAVES_COUNT = 5;
|
||||
|
||||
std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
|
||||
bool allowBattleReplay = false;
|
||||
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
|
||||
const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
|
||||
|
||||
std::shared_ptr<Environment> env;
|
||||
ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
|
||||
int3 destinationTeleportPos;
|
||||
|
||||
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
|
||||
|
||||
static boost::recursive_mutex *pim;
|
||||
bool makingTurn; //if player is already making his turn
|
||||
int firstCall; // -1 - just loaded game; 1 - just started game; 0 otherwise
|
||||
int autosaveCount;
|
||||
static const int SAVES_COUNT = 5;
|
||||
|
||||
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
|
||||
const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
|
||||
|
||||
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
|
||||
|
||||
std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
|
||||
std::vector<const CGTownInstance *> towns; //our towns on the adventure map
|
||||
std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
|
||||
|
||||
//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.
|
||||
bool allowBattleReplay = false;
|
||||
std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
|
||||
|
||||
struct SpellbookLastSetting
|
||||
{
|
||||
int spellbookLastPageBattle, spellbokLastPageAdvmap; //on which page we left spellbook
|
||||
int spellbookLastTabBattle, spellbookLastTabAdvmap; //on which page we left spellbook
|
||||
|
||||
SpellbookLastSetting();
|
||||
template <typename Handler> void serialize( Handler &h, const int version )
|
||||
{
|
||||
h & spellbookLastPageBattle;
|
||||
h & spellbokLastPageAdvmap;
|
||||
h & spellbookLastTabBattle;
|
||||
h & spellbookLastTabAdvmap;
|
||||
}
|
||||
} spellbookSettings;
|
||||
protected: // Call-ins from server, should not be called directly, but only via GameInterface
|
||||
|
||||
void update() override;
|
||||
void initializeHeroTownList();
|
||||
int getLastIndex(std::string namePrefix);
|
||||
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
|
||||
|
||||
//overridden funcs from CGameInterface
|
||||
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;
|
||||
@ -172,22 +126,16 @@ public:
|
||||
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) override;
|
||||
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
|
||||
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(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 showPuzzleMap() override;
|
||||
void viewWorldMap() override;
|
||||
void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override;
|
||||
void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) override;
|
||||
void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
|
||||
void showTavernWindow(const CGObjectInstance *townOrTavern) override;
|
||||
void showThievesGuildWindow (const CGObjectInstance * obj) override;
|
||||
void showQuestLog() override;
|
||||
void advmapSpellCast(const CGHeroInstance * caster, int spellID) override; //called when a hero casts a spell
|
||||
void tileHidden(const std::unordered_set<int3, ShashInt3> &pos) override; //called when given tiles become hidden under fog of war
|
||||
void tileRevealed(const std::unordered_set<int3, ShashInt3> &pos) override; //called when fog of war disappears from given tiles
|
||||
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() override;
|
||||
@ -230,21 +178,23 @@ public:
|
||||
void yourTacticPhase(int distance) override;
|
||||
void forceEndTacticPhase() 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 *townOrTavern) 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<bool()> onYes);
|
||||
void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
|
||||
void garrisonChanged(const CGObjectInstance * obj);
|
||||
void heroKilled(const CGHeroInstance* hero);
|
||||
void waitWhileDialog(bool unlockPim = true);
|
||||
void waitForAllDialogs(bool unlockPim = true);
|
||||
void redrawHeroWin(const CGHeroInstance * hero);
|
||||
void openTownWindow(const CGTownInstance * town); //shows townscreen
|
||||
void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
|
||||
void updateInfo(const CGObjectInstance * specific);
|
||||
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
|
||||
void activateForSpectator(); // TODO: spectator probably need own player interface class
|
||||
|
||||
// show dialogs
|
||||
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);
|
||||
@ -253,10 +203,8 @@ public:
|
||||
void stopMovement();
|
||||
void moveHero(const CGHeroInstance *h, const CGPath& path);
|
||||
|
||||
void acceptTurn(); //used during hot seat after your turn message is close
|
||||
void tryDiggging(const CGHeroInstance *h);
|
||||
void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
|
||||
void requestReturningToMainMenu(bool won);
|
||||
void proposeLoadingGame();
|
||||
|
||||
///returns true if all events are processed internally
|
||||
@ -266,11 +214,6 @@ public:
|
||||
~CPlayerInterface();
|
||||
|
||||
private:
|
||||
|
||||
template <typename Handler> void serializeTempl(Handler &h, const int version);
|
||||
|
||||
private:
|
||||
|
||||
struct IgnoreEvents
|
||||
{
|
||||
CPlayerInterface & owner;
|
||||
@ -285,14 +228,18 @@ private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool duringMovement;
|
||||
bool ignoreEvents;
|
||||
size_t numOfMovedArts;
|
||||
|
||||
void heroKilled(const CGHeroInstance* hero);
|
||||
void garrisonsChanged(std::vector<const CGObjectInstance *> objs);
|
||||
void requestReturningToMainMenu(bool won);
|
||||
void acceptTurn(); //used during hot seat after your turn message is close
|
||||
void initializeHeroTownList();
|
||||
int getLastIndex(std::string namePrefix);
|
||||
void doMoveHero(const CGHeroInstance *h, CGPath path);
|
||||
void setMovementStatus(bool value);
|
||||
|
||||
/// Performs autosave, if needed according to settings
|
||||
void performAutosave();
|
||||
};
|
||||
|
||||
/// Provides global access to instance of interface of currently active player
|
||||
extern CPlayerInterface * LOCPLINT;
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "ClientNetPackVisitors.h"
|
||||
#include "adventureMap/CAdvMapInt.h"
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
@ -374,6 +374,7 @@ void CClient::endGame()
|
||||
//threads cleanup has to be after gs cleanup and before battleints cleanup to stop tacticThread
|
||||
cleanThreads();
|
||||
|
||||
CPlayerInterface::battleInt.reset();
|
||||
playerint.clear();
|
||||
battleints.clear();
|
||||
battleCallbacks.clear();
|
||||
|
@ -61,8 +61,8 @@ namespace boost { class thread; }
|
||||
template<typename T>
|
||||
class ThreadSafeVector
|
||||
{
|
||||
typedef std::vector<T> TVector;
|
||||
typedef boost::unique_lock<boost::mutex> TLock;
|
||||
using TVector = std::vector<T>;
|
||||
using TLock = boost::unique_lock<boost::mutex>;
|
||||
TVector items;
|
||||
boost::mutex mx;
|
||||
boost::condition_variable cond;
|
||||
@ -235,7 +235,7 @@ public:
|
||||
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, ShashInt3> & tiles, 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 {}
|
||||
|
||||
|
@ -8,13 +8,12 @@
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
#include "Client.h"
|
||||
#include "adventureMap/CInGameConsole.h"
|
||||
#include "adventureMap/CAdvMapInt.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "PlayerLocalState.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
@ -387,12 +386,12 @@ void ClientCommandManager::handleBonusesCommand(std::istringstream & singleWordB
|
||||
ss << b;
|
||||
return ss.str();
|
||||
};
|
||||
printCommandMessage("Bonuses of " + adventureInt->curArmy()->getObjectName() + "\n");
|
||||
printCommandMessage(format(adventureInt->curArmy()->getBonusList()) + "\n");
|
||||
printCommandMessage("Bonuses of " + LOCPLINT->localState->getCurrentArmy()->getObjectName() + "\n");
|
||||
printCommandMessage(format(LOCPLINT->localState->getCurrentArmy()->getBonusList()) + "\n");
|
||||
|
||||
printCommandMessage("\nInherited bonuses:\n");
|
||||
TCNodes parents;
|
||||
adventureInt->curArmy()->getParents(parents);
|
||||
LOCPLINT->localState->getCurrentArmy()->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
@ -416,7 +415,7 @@ void ClientCommandManager::handleTellCommand(std::istringstream& singleWordBuffe
|
||||
|
||||
void ClientCommandManager::handleMpCommand()
|
||||
{
|
||||
if(const CGHeroInstance* h = adventureInt->curHero())
|
||||
if(const CGHeroInstance* h = LOCPLINT->localState->getCurrentHero())
|
||||
printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
|
||||
}
|
||||
|
||||
@ -602,7 +601,7 @@ void ClientCommandManager::processCommand(const std::string & message, bool call
|
||||
else if(commandName == "tell")
|
||||
handleTellCommand(singleWordBuffer);
|
||||
|
||||
else if(commandName == "mp" && adventureInt)
|
||||
else if(commandName == "mp" && LOCPLINT)
|
||||
handleMpCommand();
|
||||
|
||||
else if (commandName == "set")
|
||||
|
@ -706,15 +706,15 @@ void ApplyClientNetPackVisitor::visitBattleSetActiveStack(BattleSetActiveStack &
|
||||
|
||||
const CStack *activated = gs.curB->battleGetStackByID(pack.stack);
|
||||
PlayerColor playerToCall; //pack.player that will move activated stack
|
||||
if (activated->hasBonusOfType(Bonus::HYPNOTIZED))
|
||||
if (activated->hasBonusOfType(BonusType::HYPNOTIZED))
|
||||
{
|
||||
playerToCall = (gs.curB->sides[0].color == activated->owner
|
||||
playerToCall = (gs.curB->sides[0].color == activated->unitOwner()
|
||||
? gs.curB->sides[1].color
|
||||
: gs.curB->sides[0].color);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerToCall = activated->owner;
|
||||
playerToCall = activated->unitOwner();
|
||||
}
|
||||
|
||||
cl.startPlayerBattleAction(playerToCall);
|
||||
|
268
client/PlayerLocalState.cpp
Normal file
268
client/PlayerLocalState.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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/CPathfinder.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/CGTownInstance.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "adventureMap/CAdventureMapInterface.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))
|
||||
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->movement == 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());
|
||||
}
|
111
client/PlayerLocalState.h
Normal file
111
client/PlayerLocalState.h
Normal file
@ -0,0 +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;
|
||||
}
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -11,9 +11,6 @@
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
#include "../../lib/int3.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGObjectInstance;
|
||||
@ -23,6 +20,8 @@ class CArmedInstance;
|
||||
class IShipyard;
|
||||
struct CGPathNode;
|
||||
struct ObjectPosInfo;
|
||||
struct Component;
|
||||
class int3;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -43,38 +42,28 @@ class MapAudioPlayer;
|
||||
|
||||
struct MapDrawingInfo;
|
||||
|
||||
enum class EAdvMapMode
|
||||
{
|
||||
NORMAL,
|
||||
WORLD_VIEW
|
||||
};
|
||||
|
||||
/// 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 CAdvMapInt : public CIntObject
|
||||
class CAdventureMapInterface : public CIntObject
|
||||
{
|
||||
//TODO: remove
|
||||
friend class CPlayerInterface;
|
||||
|
||||
private:
|
||||
enum EDirections {LEFT=1, RIGHT=2, UP=4, DOWN=8};
|
||||
enum EGameStates {NA, INGAME, WAITING};
|
||||
enum class EGameState
|
||||
{
|
||||
NOT_INITIALIZED,
|
||||
HOTSEAT_WAIT,
|
||||
MAKING_TURN,
|
||||
ENEMY_TURN,
|
||||
WORLD_VIEW
|
||||
};
|
||||
|
||||
EGameStates state;
|
||||
EAdvMapMode mode;
|
||||
|
||||
/// Currently selected object, can be town, hero or null
|
||||
const CArmedInstance *selection;
|
||||
EGameState state;
|
||||
|
||||
/// currently acting player
|
||||
PlayerColor player;
|
||||
|
||||
bool duringAITurn;
|
||||
PlayerColor currentPlayerID;
|
||||
|
||||
/// uses EDirections enum
|
||||
ui8 scrollingDir;
|
||||
bool scrollingState;
|
||||
bool scrollingCursorSet;
|
||||
|
||||
const CSpell *spellBeingCasted; //nullptr if none
|
||||
|
||||
@ -127,15 +116,15 @@ private:
|
||||
void fnextHero();
|
||||
void fendTurn();
|
||||
|
||||
void setScrollingCursor(ui8 direction) const;
|
||||
void selectionChanged();
|
||||
void hotkeyMoveHeroDirectional(Point direction);
|
||||
|
||||
bool isActive();
|
||||
void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
|
||||
|
||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
||||
//button updates
|
||||
void updateSleepWake(const CGHeroInstance *h);
|
||||
void updateSpellbook(const CGHeroInstance *h);
|
||||
|
||||
// update locked state of buttons
|
||||
void updateButtons();
|
||||
|
||||
void handleMapScrollingUpdate();
|
||||
|
||||
@ -143,11 +132,17 @@ private:
|
||||
|
||||
const CGObjectInstance *getActiveObject(const int3 &tile);
|
||||
|
||||
std::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
|
||||
std::optional<Point> keyToMoveDirection(EShortcut key);
|
||||
|
||||
public:
|
||||
CAdvMapInt();
|
||||
void endingTurn();
|
||||
|
||||
/// exits currently opened world view mode and returns to normal map
|
||||
void exitWorldView();
|
||||
void exitCastingMode();
|
||||
void leaveCastingMode(const int3 & castTarget);
|
||||
void abortCastingMode();
|
||||
|
||||
protected:
|
||||
// CIntObject interface implementation
|
||||
|
||||
void activate() override;
|
||||
@ -156,59 +151,70 @@ public:
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
|
||||
void keyPressed(const SDL_Keycode & key) override;
|
||||
void keyReleased(const SDL_Keycode & key) override;
|
||||
void mouseMoved (const Point & cursorPosition) override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
|
||||
// public interface
|
||||
public:
|
||||
CAdventureMapInterface();
|
||||
|
||||
/// called by MapView whenever currently visible area changes
|
||||
/// visibleArea describen now visible map section measured in tiles
|
||||
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
|
||||
/// Called by PlayerInterface when specified player is ready to start his turn
|
||||
void onHotseatWaitStarted(PlayerColor playerID);
|
||||
|
||||
/// Called when map audio should be paused, e.g. on combat or town scren access
|
||||
/// Called by PlayerInterface when AI or remote human player starts his turn
|
||||
void onEnemyTurnStarted(PlayerColor playerID);
|
||||
|
||||
/// 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();
|
||||
|
||||
void select(const CArmedInstance *sel, bool centerView = true);
|
||||
/// 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);
|
||||
|
||||
bool isHeroSleeping(const CGHeroInstance *hero);
|
||||
void setHeroSleeping(const CGHeroInstance *hero, bool sleep);
|
||||
int getNextHeroIndex(int startIndex); //for Next Hero button - cycles awake heroes with movement only
|
||||
|
||||
void setPlayer(PlayerColor Player);
|
||||
void startHotSeatWait(PlayerColor Player);
|
||||
void startTurn();
|
||||
void initializeNewTurn();
|
||||
void endingTurn();
|
||||
void aiTurnStarted();
|
||||
|
||||
void quickCombatLock(); //should be called when quick battle started
|
||||
void quickCombatUnlock();
|
||||
/// 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);
|
||||
void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1));
|
||||
const CGHeroInstance * curHero() const;
|
||||
const CGTownInstance * curTown() const;
|
||||
const CArmedInstance * curArmy() const;
|
||||
|
||||
void updateMoveHero(const CGHeroInstance *h, tribool hasPath = boost::logic::indeterminate);
|
||||
void updateNextHero(const CGHeroInstance *h);
|
||||
|
||||
/// returns area of screen covered by terrain (main game area)
|
||||
Rect terrainAreaPixels() const;
|
||||
|
||||
/// exits currently opened world view mode and returns to normal map
|
||||
void exitWorldView();
|
||||
|
||||
/// opens world view at default scale
|
||||
void openWorldView();
|
||||
|
||||
@ -219,4 +225,4 @@ public:
|
||||
void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
|
||||
};
|
||||
|
||||
extern std::shared_ptr<CAdvMapInt> adventureInt;
|
||||
extern std::shared_ptr<CAdventureMapInterface> adventureInt;
|
@ -11,13 +11,13 @@
|
||||
#include "StdInc.h"
|
||||
#include "CAdventureOptions.h"
|
||||
|
||||
#include "CAdvMapInt.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../lobby/CCampaignInfoScreen.h"
|
||||
#include "../lobby/CScenarioInfoScreen.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
@ -28,20 +28,19 @@ CAdventureOptions::CAdventureOptions()
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
|
||||
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_v);
|
||||
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
|
||||
viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
|
||||
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), SDLK_RETURN);
|
||||
exit->assignedKeys.insert(SDLK_ESCAPE);
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
|
||||
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_i);
|
||||
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
|
||||
scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
|
||||
|
||||
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_p);
|
||||
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE);
|
||||
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
|
||||
|
||||
dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
|
||||
if(const CGHeroInstance *h = adventureInt->curHero())
|
||||
dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_DIG_GRAIL);
|
||||
if(const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero())
|
||||
dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
|
||||
else
|
||||
dig->block(true);
|
||||
|
@ -14,9 +14,10 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../ClientCommandManager.h"
|
||||
#include "../adventureMap/CAdvMapInt.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../render/Colors.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
@ -108,30 +109,29 @@ void CInGameConsole::print(const std::string & txt)
|
||||
GH.totalRedraw(); // FIXME: ingame console has no parent widget set
|
||||
}
|
||||
|
||||
void CInGameConsole::keyPressed (const SDL_Keycode & key)
|
||||
void CInGameConsole::keyPressed (EShortcut key)
|
||||
{
|
||||
if (LOCPLINT->cingconsole != this)
|
||||
return;
|
||||
|
||||
if(!captureAllKeys && key != SDLK_TAB)
|
||||
if(!captureAllKeys && key != EShortcut::GAME_ACTIVATE_CONSOLE)
|
||||
return; //because user is not entering any text
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case SDLK_TAB:
|
||||
case SDLK_ESCAPE:
|
||||
{
|
||||
if(captureAllKeys)
|
||||
{
|
||||
endEnteringText(false);
|
||||
}
|
||||
else if(SDLK_TAB == key)
|
||||
{
|
||||
startEnteringText();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_RETURN: //enter key
|
||||
case EShortcut::GLOBAL_CANCEL:
|
||||
if(captureAllKeys)
|
||||
endEnteringText(false);
|
||||
break;
|
||||
|
||||
case EShortcut::GAME_ACTIVATE_CONSOLE:
|
||||
if(captureAllKeys)
|
||||
endEnteringText(false);
|
||||
else
|
||||
startEnteringText();
|
||||
break;
|
||||
|
||||
case EShortcut::GLOBAL_ACCEPT:
|
||||
{
|
||||
if(!enteredText.empty() && captureAllKeys)
|
||||
{
|
||||
@ -145,7 +145,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_BACKSPACE:
|
||||
case EShortcut::GLOBAL_BACKSPACE:
|
||||
{
|
||||
if(enteredText.size() > 1)
|
||||
{
|
||||
@ -155,7 +155,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_UP: //up arrow
|
||||
case EShortcut::MOVE_UP:
|
||||
{
|
||||
if(previouslyEntered.empty())
|
||||
break;
|
||||
@ -174,7 +174,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_DOWN: //down arrow
|
||||
case EShortcut::MOVE_DOWN:
|
||||
{
|
||||
if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
|
||||
{
|
||||
@ -190,10 +190,6 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +255,7 @@ void CInGameConsole::endEnteringText(bool processEnteredText)
|
||||
clientCommandThread.detach();
|
||||
}
|
||||
else
|
||||
LOCPLINT->cb->sendMessage(txt, adventureInt->curArmy());
|
||||
LOCPLINT->cb->sendMessage(txt, LOCPLINT->localState->getCurrentArmy());
|
||||
}
|
||||
enteredText.clear();
|
||||
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
void tick(uint32_t msPassed) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void keyPressed(const SDL_Keycode & key) override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
void textInputed(const std::string & enteredText) override;
|
||||
void textEdited(const std::string & enteredText) override;
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CInfoBar.h"
|
||||
|
||||
#include "CAdvMapInt.h"
|
||||
#include "CAdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -22,6 +22,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
@ -114,7 +115,7 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
//get amount of halls of each level
|
||||
std::vector<int> halls(4, 0);
|
||||
for(auto town : LOCPLINT->towns)
|
||||
for(auto town : LOCPLINT->localState->getOwnedTowns())
|
||||
{
|
||||
int hallLevel = town->hallLevel();
|
||||
//negative value means no village hall, unlikely but possible
|
||||
@ -237,15 +238,15 @@ void CInfoBar::reset()
|
||||
void CInfoBar::showSelection()
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
if(adventureInt->curHero())
|
||||
if(LOCPLINT->localState->getCurrentHero())
|
||||
{
|
||||
showHeroSelection(adventureInt->curHero());
|
||||
showHeroSelection(LOCPLINT->localState->getCurrentHero());
|
||||
return;
|
||||
}
|
||||
|
||||
if(adventureInt->curTown())
|
||||
if(LOCPLINT->localState->getCurrentTown())
|
||||
{
|
||||
showTownSelection(adventureInt->curTown());
|
||||
showTownSelection(LOCPLINT->localState->getCurrentTown());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -11,13 +11,14 @@
|
||||
#include "StdInc.h"
|
||||
#include "CList.h"
|
||||
|
||||
#include "CAdvMapInt.h"
|
||||
#include "CAdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
@ -203,8 +204,8 @@ std::shared_ptr<CIntObject> CHeroList::CHeroItem::genSelection()
|
||||
|
||||
void CHeroList::CHeroItem::select(bool on)
|
||||
{
|
||||
if(on && adventureInt->curHero() != hero)
|
||||
adventureInt->select(hero);
|
||||
if(on)
|
||||
LOCPLINT->localState->setSelection(hero);
|
||||
}
|
||||
|
||||
void CHeroList::CHeroItem::open()
|
||||
@ -224,19 +225,19 @@ std::string CHeroList::CHeroItem::getHoverText()
|
||||
|
||||
std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->wanderingHeroes.size() > index)
|
||||
return std::make_shared<CHeroItem>(this, LOCPLINT->wanderingHeroes[index]);
|
||||
if (LOCPLINT->localState->getWanderingHeroes().size() > index)
|
||||
return std::make_shared<CHeroItem>(this, LOCPLINT->localState->getWanderingHero(index));
|
||||
return std::make_shared<CEmptyHeroItem>();
|
||||
}
|
||||
|
||||
CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getWanderingHeroes().size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
|
||||
{
|
||||
}
|
||||
|
||||
void CHeroList::select(const CGHeroInstance * hero)
|
||||
{
|
||||
selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
|
||||
selectIndex(vstd::find_pos(LOCPLINT->localState->getWanderingHeroes(), hero));
|
||||
}
|
||||
|
||||
void CHeroList::update(const CGHeroInstance * hero)
|
||||
@ -245,7 +246,7 @@ void CHeroList::update(const CGHeroInstance * hero)
|
||||
for(auto & elem : listBox->getItems())
|
||||
{
|
||||
auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
|
||||
if(item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero))
|
||||
if(item && item->hero == hero && vstd::contains(LOCPLINT->localState->getWanderingHeroes(), hero))
|
||||
{
|
||||
item->update();
|
||||
return;
|
||||
@ -253,17 +254,17 @@ void CHeroList::update(const CGHeroInstance * hero)
|
||||
}
|
||||
//simplest solution for now: reset list and restore selection
|
||||
|
||||
listBox->resize(LOCPLINT->wanderingHeroes.size());
|
||||
if (adventureInt->curHero())
|
||||
select(adventureInt->curHero());
|
||||
listBox->resize(LOCPLINT->localState->getWanderingHeroes().size());
|
||||
if (LOCPLINT->localState->getCurrentHero())
|
||||
select(LOCPLINT->localState->getCurrentHero());
|
||||
|
||||
CList::update();
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->towns.size() > index)
|
||||
return std::make_shared<CTownItem>(this, LOCPLINT->towns[index]);
|
||||
if (LOCPLINT->localState->getOwnedTowns().size() > index)
|
||||
return std::make_shared<CTownItem>(this, LOCPLINT->localState->getOwnedTown(index));
|
||||
return std::make_shared<CAnimImage>("ITPA", 0);
|
||||
}
|
||||
|
||||
@ -292,8 +293,8 @@ void CTownList::CTownItem::update()
|
||||
|
||||
void CTownList::CTownItem::select(bool on)
|
||||
{
|
||||
if (on && adventureInt->curTown() != town)
|
||||
adventureInt->select(town);
|
||||
if(on)
|
||||
LOCPLINT->localState->setSelection(town);
|
||||
}
|
||||
|
||||
void CTownList::CTownItem::open()
|
||||
@ -312,22 +313,22 @@ std::string CTownList::CTownItem::getHoverText()
|
||||
}
|
||||
|
||||
CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getOwnedTowns().size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
|
||||
{
|
||||
}
|
||||
|
||||
void CTownList::select(const CGTownInstance * town)
|
||||
{
|
||||
selectIndex(vstd::find_pos(LOCPLINT->towns, town));
|
||||
selectIndex(vstd::find_pos(LOCPLINT->localState->getOwnedTowns(), town));
|
||||
}
|
||||
|
||||
void CTownList::update(const CGTownInstance *)
|
||||
{
|
||||
//simplest solution for now: reset list and restore selection
|
||||
|
||||
listBox->resize(LOCPLINT->towns.size());
|
||||
if (adventureInt->curTown())
|
||||
select(adventureInt->curTown());
|
||||
listBox->resize(LOCPLINT->localState->getOwnedTowns().size());
|
||||
if (LOCPLINT->localState->getCurrentTown())
|
||||
select(LOCPLINT->localState->getCurrentTown());
|
||||
|
||||
CList::update();
|
||||
}
|
||||
|
@ -11,14 +11,15 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMinimap.h"
|
||||
|
||||
#include "CAdvMapInt.h"
|
||||
#include "CAdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_PixelAccess.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
@ -27,6 +28,8 @@
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapping/CMapDefines.h"
|
||||
|
||||
#include <SDL_pixels.h>
|
||||
|
||||
ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
|
||||
{
|
||||
const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
|
||||
@ -225,10 +228,13 @@ void CMinimap::setAIRadar(bool on)
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CMinimap::updateTile(const int3 &pos)
|
||||
void CMinimap::updateTiles(std::unordered_set<int3> positions)
|
||||
{
|
||||
if(minimap)
|
||||
minimap->refreshTile(pos);
|
||||
{
|
||||
for (auto const & tile : positions)
|
||||
minimap->refreshTile(tile);
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../render/Canvas.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class ColorRGBA;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class Canvas;
|
||||
class CMinimap;
|
||||
|
||||
class CMinimapInstance : public CIntObject
|
||||
@ -68,6 +67,6 @@ public:
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
|
||||
void updateTile(const int3 &pos);
|
||||
void updateTiles(std::unordered_set<int3> positions);
|
||||
};
|
||||
|
||||
|
@ -80,7 +80,7 @@ std::string CResDataBar::buildDateString()
|
||||
void CResDataBar::draw(SDL_Surface * to)
|
||||
{
|
||||
//TODO: all this should be labels, but they require proper text update on change
|
||||
for (auto i=GameResID(EGameResID::WOOD); i <= GameResID(EGameResID::GOLD); vstd::advance(i, 1))
|
||||
for (GameResID i=EGameResID::WOOD; i <= GameResID(EGameResID::GOLD); ++i)
|
||||
{
|
||||
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(i));
|
||||
|
||||
|
@ -157,7 +157,7 @@ void MapAudioPlayer::updateAmbientSounds()
|
||||
};
|
||||
|
||||
int3 pos = currentSelection->getSightCenter();
|
||||
std::unordered_set<int3, ShashInt3> tiles;
|
||||
std::unordered_set<int3> tiles;
|
||||
LOCPLINT->cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV);
|
||||
for(int3 tile : tiles)
|
||||
{
|
||||
|
@ -140,7 +140,7 @@ bool BattleActionsController::isActiveStackSpellcaster() const
|
||||
if (!casterStack)
|
||||
return false;
|
||||
|
||||
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||
bool spellcaster = casterStack->hasBonusOfType(BonusType::SPELLCASTER);
|
||||
return (spellcaster && casterStack->canCast());
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
|
||||
case PossiblePlayerBattleAction::NO_LOCATION:
|
||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||
case PossiblePlayerBattleAction::OBSTACLE:
|
||||
if(!stack->hasBonusOfType(Bonus::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX)
|
||||
if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX)
|
||||
return 1;
|
||||
else
|
||||
return 100;//bottom priority
|
||||
@ -349,7 +349,7 @@ void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action,
|
||||
|
||||
case PossiblePlayerBattleAction::MOVE_TACTICS:
|
||||
case PossiblePlayerBattleAction::MOVE_STACK:
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(Bonus::FLYING))
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(BonusType::FLYING))
|
||||
CCS->curh->set(Cursor::Combat::FLY);
|
||||
else
|
||||
CCS->curh->set(Cursor::Combat::MOVE);
|
||||
@ -434,7 +434,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
||||
|
||||
case PossiblePlayerBattleAction::MOVE_TACTICS:
|
||||
case PossiblePlayerBattleAction::MOVE_STACK:
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(Bonus::FLYING))
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(BonusType::FLYING))
|
||||
return (boost::format(CGI->generaltexth->allTexts[295]) % owner.stacksController->getActiveStack()->getName()).str(); //Fly %s here
|
||||
else
|
||||
return (boost::format(CGI->generaltexth->allTexts[294]) % owner.stacksController->getActiveStack()->getName()).str(); //Move %s here
|
||||
@ -524,12 +524,12 @@ std::string BattleActionsController::actionGetStatusMessageBlocked(PossiblePlaye
|
||||
bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, BattleHex targetHex)
|
||||
{
|
||||
const CStack * targetStack = getStackForHex(targetHex);
|
||||
bool targetStackOwned = targetStack && targetStack->owner == owner.curInt->playerID;
|
||||
bool targetStackOwned = targetStack && targetStack->unitOwner() == owner.curInt->playerID;
|
||||
|
||||
switch (action.get())
|
||||
{
|
||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||
return (targetStack && targetStackOwned && targetStack->Speed() > 0);
|
||||
return (targetStack && targetStackOwned && targetStack->speed() > 0);
|
||||
|
||||
case PossiblePlayerBattleAction::CREATURE_INFO:
|
||||
return (targetStack && targetStackOwned && targetStack->alive());
|
||||
@ -620,7 +620,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
||||
{
|
||||
if(owner.stacksController->getActiveStack()->doubleWide())
|
||||
{
|
||||
std::vector<BattleHex> acc = owner.curInt->cb->battleGetAvailableHexes(owner.stacksController->getActiveStack(), true);
|
||||
std::vector<BattleHex> acc = owner.curInt->cb->battleGetAvailableHexes(owner.stacksController->getActiveStack(), false);
|
||||
BattleHex shiftedDest = targetHex.cloneInDirection(owner.stacksController->getActiveStack()->destShiftDir(), false);
|
||||
if(vstd::contains(acc, targetHex))
|
||||
owner.giveCommand(EActionType::WALK, targetHex);
|
||||
@ -863,7 +863,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
{
|
||||
creatureSpells.clear();
|
||||
|
||||
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||
bool spellcaster = casterStack->hasBonusOfType(BonusType::SPELLCASTER);
|
||||
if(casterStack->canCast() && spellcaster)
|
||||
{
|
||||
// faerie dragon can cast only one, randomly selected spell until their next move
|
||||
@ -874,7 +874,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
creatureSpells.push_back(spellToCast);
|
||||
}
|
||||
|
||||
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(Bonus::SPELLCASTER));
|
||||
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER));
|
||||
|
||||
for (auto const & bonus : *bl)
|
||||
{
|
||||
|
@ -71,17 +71,17 @@ std::vector<BattleAnimation *> & BattleAnimation::pendingAnimations()
|
||||
|
||||
std::shared_ptr<CreatureAnimation> BattleAnimation::stackAnimation(const CStack * stack) const
|
||||
{
|
||||
return owner.stacksController->stackAnimation[stack->ID];
|
||||
return owner.stacksController->stackAnimation[stack->unitId()];
|
||||
}
|
||||
|
||||
bool BattleAnimation::stackFacingRight(const CStack * stack)
|
||||
{
|
||||
return owner.stacksController->stackFacingRight[stack->ID];
|
||||
return owner.stacksController->stackFacingRight[stack->unitId()];
|
||||
}
|
||||
|
||||
void BattleAnimation::setStackFacingRight(const CStack * stack, bool facingRight)
|
||||
{
|
||||
owner.stacksController->stackFacingRight[stack->ID] = facingRight;
|
||||
owner.stacksController->stackFacingRight[stack->unitId()] = facingRight;
|
||||
}
|
||||
|
||||
BattleStackAnimation::BattleStackAnimation(BattleInterface & owner, const CStack * stack)
|
||||
@ -137,7 +137,7 @@ bool StackActionAnimation::init()
|
||||
|
||||
StackActionAnimation::~StackActionAnimation()
|
||||
{
|
||||
if (stack->isFrozen())
|
||||
if (stack->isFrozen() && currGroup != ECreatureAnimType::DEATH && currGroup != ECreatureAnimType::DEATH_RANGED)
|
||||
myAnim->setType(ECreatureAnimType::HOLDING);
|
||||
else
|
||||
myAnim->setType(nextGroup);
|
||||
@ -279,7 +279,7 @@ ECreatureAnimType MeleeAttackAnimation::selectGroup(bool multiAttack)
|
||||
getForwardGroup (multiAttack)
|
||||
};
|
||||
|
||||
int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1);
|
||||
int revShiftattacker = (attackingStack->unitSide() == BattleSide::ATTACKER ? -1 : 1);
|
||||
|
||||
int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
|
||||
if(mutPos == -1 && attackingStack->doubleWide())
|
||||
@ -370,7 +370,7 @@ bool MovementAnimation::init()
|
||||
distanceX = endPosition.x - begPosition.x;
|
||||
distanceY = endPosition.y - begPosition.y;
|
||||
|
||||
if (stack->hasBonus(Selector::type()(Bonus::FLYING)))
|
||||
if (stack->hasBonus(Selector::type()(BonusType::FLYING)))
|
||||
{
|
||||
float distance = static_cast<float>(sqrt(distanceX * distanceX + distanceY * distanceY));
|
||||
progressPerSecond = AnimationControls::getFlightDistance(stack->unitType()) / distance;
|
||||
|
@ -65,21 +65,21 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
|
||||
return;
|
||||
}
|
||||
//don't show animation when no HP is regenerated
|
||||
switch(bte.effect)
|
||||
switch(static_cast<BonusType>(bte.effect))
|
||||
{
|
||||
case Bonus::HP_REGENERATION:
|
||||
case BonusType::HP_REGENERATION:
|
||||
displayEffect(EBattleEffect::REGENERATION, "REGENER", stack->getPosition());
|
||||
break;
|
||||
case Bonus::MANA_DRAIN:
|
||||
case BonusType::MANA_DRAIN:
|
||||
displayEffect(EBattleEffect::MANA_DRAIN, "MANADRAI", stack->getPosition());
|
||||
break;
|
||||
case Bonus::POISON:
|
||||
case BonusType::POISON:
|
||||
displayEffect(EBattleEffect::POISON, "POISON", stack->getPosition());
|
||||
break;
|
||||
case Bonus::FEAR:
|
||||
case BonusType::FEAR:
|
||||
displayEffect(EBattleEffect::FEAR, "FEAR", stack->getPosition());
|
||||
break;
|
||||
case Bonus::MORALE:
|
||||
case BonusType::MORALE:
|
||||
{
|
||||
std::string hlp = CGI->generaltexth->allTexts[33];
|
||||
boost::algorithm::replace_first(hlp,"%s",(stack->getName()));
|
||||
|
@ -134,7 +134,7 @@ void BattleFieldController::renderBattlefield(Canvas & canvas)
|
||||
|
||||
void BattleFieldController::showBackground(Canvas & canvas)
|
||||
{
|
||||
if (owner.stacksController->getActiveStack() != nullptr ) //&& creAnims[stacksController->getActiveStack()->ID]->isIdle() //show everything with range
|
||||
if (owner.stacksController->getActiveStack() != nullptr ) //&& creAnims[stacksController->getActiveStack()->unitId()]->isIdle() //show everything with range
|
||||
showBackgroundImageWithHexes(canvas);
|
||||
else
|
||||
showBackgroundImage(canvas);
|
||||
@ -174,7 +174,7 @@ void BattleFieldController::redrawBackgroundWithHexes()
|
||||
const CStack *activeStack = owner.stacksController->getActiveStack();
|
||||
std::vector<BattleHex> attackableHexes;
|
||||
if(activeStack)
|
||||
occupiableHexes = owner.curInt->cb->battleGetAvailableHexes(activeStack, true, true, &attackableHexes);
|
||||
occupiableHexes = owner.curInt->cb->battleGetAvailableHexes(activeStack, false, true, &attackableHexes);
|
||||
|
||||
// prepare background graphic with hexes and shaded hexes
|
||||
backgroundWithHexes->draw(background, Point(0,0));
|
||||
@ -243,7 +243,7 @@ std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack()
|
||||
if (!owner.stacksController->getActiveStack())
|
||||
return result;
|
||||
|
||||
if (!settings["battle"]["movementHighlightOnHover"].Bool())
|
||||
if (!settings["battle"]["movementHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown())
|
||||
return result;
|
||||
|
||||
auto hoveredHex = getHoveredHex();
|
||||
@ -252,7 +252,7 @@ std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack()
|
||||
const CStack * const hoveredStack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true);
|
||||
if(hoveredStack)
|
||||
{
|
||||
std::vector<BattleHex> v = owner.curInt->cb->battleGetAvailableHexes(hoveredStack, false, true, nullptr);
|
||||
std::vector<BattleHex> v = owner.curInt->cb->battleGetAvailableHexes(hoveredStack, true, true, nullptr);
|
||||
for(BattleHex hex : v)
|
||||
result.insert(hex);
|
||||
}
|
||||
@ -289,7 +289,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange()
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<BattleHex> BattleFieldController::getHighlightedHexesMovementTarget()
|
||||
std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget()
|
||||
{
|
||||
const CStack * stack = owner.stacksController->getActiveStack();
|
||||
auto hoveredHex = getHoveredHex();
|
||||
@ -297,7 +297,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesMovementTarget()
|
||||
if(!stack)
|
||||
return {};
|
||||
|
||||
std::vector<BattleHex> availableHexes = owner.curInt->cb->battleGetAvailableHexes(stack, true, false, nullptr);
|
||||
std::vector<BattleHex> availableHexes = owner.curInt->cb->battleGetAvailableHexes(stack, false, false, nullptr);
|
||||
|
||||
auto hoveredStack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true);
|
||||
if(owner.curInt->cb->battleCanAttack(stack, hoveredStack, hoveredHex))
|
||||
@ -337,7 +337,7 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
||||
{
|
||||
std::set<BattleHex> hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
|
||||
std::set<BattleHex> hoveredSpellHexes = getHighlightedHexesForSpellRange();
|
||||
std::set<BattleHex> hoveredMoveHexes = getHighlightedHexesMovementTarget();
|
||||
std::set<BattleHex> hoveredMoveHexes = getHighlightedHexesForMovementTarget();
|
||||
|
||||
if(getHoveredHex() == BattleHex::INVALID)
|
||||
return;
|
||||
@ -540,7 +540,7 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget)
|
||||
case BattleHex::LEFT:
|
||||
case BattleHex::BOTTOM_LEFT:
|
||||
{
|
||||
if ( attacker->side == BattleSide::ATTACKER )
|
||||
if ( attacker->unitSide() == BattleSide::ATTACKER )
|
||||
return attackTarget.cloneInDirection(direction);
|
||||
else
|
||||
return attackTarget.cloneInDirection(direction).cloneInDirection(BattleHex::LEFT);
|
||||
@ -550,7 +550,7 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget)
|
||||
case BattleHex::RIGHT:
|
||||
case BattleHex::BOTTOM_RIGHT:
|
||||
{
|
||||
if ( attacker->side == BattleSide::ATTACKER )
|
||||
if ( attacker->unitSide() == BattleSide::ATTACKER )
|
||||
return attackTarget.cloneInDirection(direction).cloneInDirection(BattleHex::RIGHT);
|
||||
else
|
||||
return attackTarget.cloneInDirection(direction);
|
||||
@ -558,7 +558,7 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget)
|
||||
|
||||
case BattleHex::TOP:
|
||||
{
|
||||
if ( attacker->side == BattleSide::ATTACKER )
|
||||
if ( attacker->unitSide() == BattleSide::ATTACKER )
|
||||
return attackTarget.cloneInDirection(BattleHex::TOP_RIGHT);
|
||||
else
|
||||
return attackTarget.cloneInDirection(BattleHex::TOP_LEFT);
|
||||
@ -566,7 +566,7 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget)
|
||||
|
||||
case BattleHex::BOTTOM:
|
||||
{
|
||||
if ( attacker->side == BattleSide::ATTACKER )
|
||||
if ( attacker->unitSide() == BattleSide::ATTACKER )
|
||||
return attackTarget.cloneInDirection(BattleHex::BOTTOM_RIGHT);
|
||||
else
|
||||
return attackTarget.cloneInDirection(BattleHex::BOTTOM_LEFT);
|
||||
|
@ -53,7 +53,7 @@ class BattleFieldController : public CIntObject
|
||||
std::set<BattleHex> getHighlightedHexesForActiveStack();
|
||||
std::set<BattleHex> getMovementRangeForHoveredStack();
|
||||
std::set<BattleHex> getHighlightedHexesForSpellRange();
|
||||
std::set<BattleHex> getHighlightedHexesMovementTarget();
|
||||
std::set<BattleHex> getHighlightedHexesForMovementTarget();
|
||||
|
||||
void showBackground(Canvas & canvas);
|
||||
void showBackgroundImage(Canvas & canvas);
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../adventureMap/CAdvMapInt.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CStack.h"
|
||||
@ -208,7 +208,7 @@ void BattleInterface::stacksAreAttacked(std::vector<StackAttackedInfo> attackedI
|
||||
|
||||
for(const StackAttackedInfo & attackedInfo : attackedInfos)
|
||||
{
|
||||
ui8 side = attackedInfo.defender->side;
|
||||
ui8 side = attackedInfo.defender->unitSide();
|
||||
killedBySide.at(side) += attackedInfo.amountKilled;
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ const CGHeroInstance * BattleInterface::getActiveHero()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(attacker->side == BattleSide::ATTACKER)
|
||||
if(attacker->unitSide() == BattleSide::ATTACKER)
|
||||
{
|
||||
return attackingHeroInstance;
|
||||
}
|
||||
@ -636,7 +636,7 @@ void BattleInterface::tacticPhaseEnd()
|
||||
|
||||
static bool immobile(const CStack *s)
|
||||
{
|
||||
return !s->Speed(0, true); //should bound stacks be immobile?
|
||||
return !s->speed(0, true); //should bound stacks be immobile?
|
||||
}
|
||||
|
||||
void BattleInterface::tacticNextStack(const CStack * current)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
@ -406,12 +407,12 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
|
||||
background->colorize(owner.playerID);
|
||||
pos = center(background->pos);
|
||||
|
||||
exit = std::make_shared<CButton>(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN);
|
||||
exit = std::make_shared<CButton>(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_ACCEPT);
|
||||
exit->setBorderColor(Colors::METALLIC_GOLD);
|
||||
|
||||
if(allowReplay)
|
||||
{
|
||||
repeat = std::make_shared<CButton>(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, SDLK_ESCAPE);
|
||||
repeat = std::make_shared<CButton>(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, EShortcut::GLOBAL_CANCEL);
|
||||
repeat->setBorderColor(Colors::METALLIC_GOLD);
|
||||
labels.push_back(std::make_shared<CLabel>(232, 520, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.battleResultsWindow.applyResultsLabel")));
|
||||
}
|
||||
@ -455,18 +456,18 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
|
||||
auto stacks = owner.cb->battleGetAllStacks();
|
||||
vstd::erase_if(stacks, [i](const CStack * stack) //erase stack of other side and not coming from garrison
|
||||
{
|
||||
return stack->side != i || !stack->base;
|
||||
return stack->unitSide() != i || !stack->base;
|
||||
});
|
||||
|
||||
auto best = vstd::maxElementByFun(stacks, [](const CStack * stack)
|
||||
{
|
||||
return stack->type->getAIValue();
|
||||
return stack->unitType()->getAIValue();
|
||||
});
|
||||
|
||||
if(best != stacks.end()) //should be always but to be safe...
|
||||
{
|
||||
icons.push_back(std::make_shared<CAnimImage>("TWCRPORT", (*best)->type->getIconIndex(), 0, xs[i], 38));
|
||||
sideNames[i] = (*best)->type->getNamePluralTranslated();
|
||||
icons.push_back(std::make_shared<CAnimImage>("TWCRPORT", (*best)->unitType()->getIconIndex(), 0, xs[i], 38));
|
||||
sideNames[i] = (*best)->unitType()->getNamePluralTranslated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ std::shared_ptr<CAnimation> BattleProjectileController::getProjectileImage(const
|
||||
|
||||
void BattleProjectileController::emitStackProjectile(const CStack * stack)
|
||||
{
|
||||
int stackID = stack ? stack->ID : -1;
|
||||
int stackID = stack ? stack->unitId() : -1;
|
||||
|
||||
for (auto projectile : projectiles)
|
||||
{
|
||||
@ -232,7 +232,7 @@ void BattleProjectileController::showProjectiles(Canvas & canvas)
|
||||
|
||||
bool BattleProjectileController::hasActiveProjectile(const CStack * stack, bool emittedOnly) const
|
||||
{
|
||||
int stackID = stack ? stack->ID : -1;
|
||||
int stackID = stack ? stack->unitId() : -1;
|
||||
|
||||
for(auto const & instance : projectiles)
|
||||
{
|
||||
@ -294,7 +294,7 @@ void BattleProjectileController::createCatapultProjectile(const CStack * shooter
|
||||
catapultProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getCatapultSpeed());
|
||||
catapultProjectile->from = from;
|
||||
catapultProjectile->dest = dest;
|
||||
catapultProjectile->shooterID = shooter->ID;
|
||||
catapultProjectile->shooterID = shooter->unitId();
|
||||
catapultProjectile->playing = false;
|
||||
catapultProjectile->frameProgress = 0.f;
|
||||
|
||||
@ -333,7 +333,7 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
|
||||
|
||||
projectile->from = from;
|
||||
projectile->dest = dest;
|
||||
projectile->shooterID = shooter->ID;
|
||||
projectile->shooterID = shooter->unitId();
|
||||
projectile->progress = 0;
|
||||
projectile->playing = false;
|
||||
|
||||
@ -357,7 +357,7 @@ void BattleProjectileController::createSpellProjectile(const CStack * shooter, P
|
||||
projectile->reverse = from.x > dest.x;
|
||||
projectile->from = from;
|
||||
projectile->dest = dest;
|
||||
projectile->shooterID = shooter ? shooter->ID : -1;
|
||||
projectile->shooterID = shooter ? shooter->unitId() : -1;
|
||||
projectile->progress = 0;
|
||||
projectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
|
||||
projectile->playing = false;
|
||||
|
@ -104,10 +104,10 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
|
||||
|
||||
BattleHex BattleStacksController::getStackCurrentPosition(const CStack * stack) const
|
||||
{
|
||||
if ( !stackAnimation.at(stack->ID)->isMoving())
|
||||
if ( !stackAnimation.at(stack->unitId())->isMoving())
|
||||
return stack->getPosition();
|
||||
|
||||
if (stack->hasBonusOfType(Bonus::FLYING) && stackAnimation.at(stack->ID)->getType() == ECreatureAnimType::MOVING )
|
||||
if (stack->hasBonusOfType(BonusType::FLYING) && stackAnimation.at(stack->unitId())->getType() == ECreatureAnimType::MOVING )
|
||||
return BattleHex::HEX_AFTER_ALL;
|
||||
|
||||
for (auto & anim : currentAnimations)
|
||||
@ -131,14 +131,14 @@ void BattleStacksController::collectRenderableObjects(BattleRenderer & renderer)
|
||||
|
||||
for (auto stack : stacks)
|
||||
{
|
||||
if (stackAnimation.find(stack->ID) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
|
||||
if (stackAnimation.find(stack->unitId()) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
|
||||
continue;
|
||||
|
||||
//FIXME: hack to ignore ghost stacks
|
||||
if ((stackAnimation[stack->ID]->getType() == ECreatureAnimType::DEAD || stackAnimation[stack->ID]->getType() == ECreatureAnimType::HOLDING) && stack->isGhost())
|
||||
if ((stackAnimation[stack->unitId()]->getType() == ECreatureAnimType::DEAD || stackAnimation[stack->unitId()]->getType() == ECreatureAnimType::HOLDING) && stack->isGhost())
|
||||
continue;
|
||||
|
||||
auto layer = stackAnimation[stack->ID]->isDead() ? EBattleFieldLayer::CORPSES : EBattleFieldLayer::STACKS;
|
||||
auto layer = stackAnimation[stack->unitId()]->isDead() ? EBattleFieldLayer::CORPSES : EBattleFieldLayer::STACKS;
|
||||
auto location = getStackCurrentPosition(stack);
|
||||
|
||||
renderer.insert(layer, location, [this, stack]( BattleRenderer::RendererRef renderer ){
|
||||
@ -159,13 +159,13 @@ void BattleStacksController::stackReset(const CStack * stack)
|
||||
owner.checkForAnimations();
|
||||
|
||||
//reset orientation?
|
||||
//stackFacingRight[stack->ID] = stack->side == BattleSide::ATTACKER;
|
||||
//stackFacingRight[stack->unitId()] = stack->unitSide() == BattleSide::ATTACKER;
|
||||
|
||||
auto iter = stackAnimation.find(stack->ID);
|
||||
auto iter = stackAnimation.find(stack->unitId());
|
||||
|
||||
if(iter == stackAnimation.end())
|
||||
{
|
||||
logGlobal->error("Unit %d have no animation", stack->ID);
|
||||
logGlobal->error("Unit %d have no animation", stack->unitId());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
|
||||
// Tower shooters have only their upper half visible
|
||||
static const int turretCreatureAnimationHeight = 232;
|
||||
|
||||
stackFacingRight[stack->ID] = stack->side == BattleSide::ATTACKER; // must be set before getting stack position
|
||||
stackFacingRight[stack->unitId()] = stack->unitSide() == BattleSide::ATTACKER; // must be set before getting stack position
|
||||
|
||||
Point coords = getStackPositionAtHex(stack->getPosition(), stack);
|
||||
|
||||
@ -195,26 +195,26 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
|
||||
|
||||
const CCreature *turretCreature = owner.siegeController->getTurretCreature();
|
||||
|
||||
stackAnimation[stack->ID] = AnimationControls::getAnimation(turretCreature);
|
||||
stackAnimation[stack->ID]->pos.h = turretCreatureAnimationHeight;
|
||||
stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
|
||||
stackAnimation[stack->unitId()] = AnimationControls::getAnimation(turretCreature);
|
||||
stackAnimation[stack->unitId()]->pos.h = turretCreatureAnimationHeight;
|
||||
stackAnimation[stack->unitId()]->pos.w = stackAnimation[stack->unitId()]->getWidth();
|
||||
|
||||
// FIXME: workaround for visible animation of Medusa tails (animation disabled in H3)
|
||||
if (turretCreature->getId() == CreatureID::MEDUSA )
|
||||
stackAnimation[stack->ID]->pos.w = 250;
|
||||
stackAnimation[stack->unitId()]->pos.w = 250;
|
||||
|
||||
coords = owner.siegeController->getTurretCreaturePosition(stack->initialPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
stackAnimation[stack->ID] = AnimationControls::getAnimation(stack->unitType());
|
||||
stackAnimation[stack->ID]->onAnimationReset += std::bind(&onAnimationFinished, stack, stackAnimation[stack->ID]);
|
||||
stackAnimation[stack->ID]->pos.h = stackAnimation[stack->ID]->getHeight();
|
||||
stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
|
||||
stackAnimation[stack->unitId()] = AnimationControls::getAnimation(stack->unitType());
|
||||
stackAnimation[stack->unitId()]->onAnimationReset += std::bind(&onAnimationFinished, stack, stackAnimation[stack->unitId()]);
|
||||
stackAnimation[stack->unitId()]->pos.h = stackAnimation[stack->unitId()]->getHeight();
|
||||
stackAnimation[stack->unitId()]->pos.w = stackAnimation[stack->unitId()]->getWidth();
|
||||
}
|
||||
stackAnimation[stack->ID]->pos.x = coords.x;
|
||||
stackAnimation[stack->ID]->pos.y = coords.y;
|
||||
stackAnimation[stack->ID]->setType(ECreatureAnimType::HOLDING);
|
||||
stackAnimation[stack->unitId()]->pos.x = coords.x;
|
||||
stackAnimation[stack->unitId()]->pos.y = coords.y;
|
||||
stackAnimation[stack->unitId()]->setType(ECreatureAnimType::HOLDING);
|
||||
|
||||
if (!instant)
|
||||
{
|
||||
@ -234,28 +234,20 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
|
||||
void BattleStacksController::setActiveStack(const CStack *stack)
|
||||
{
|
||||
if (activeStack) // update UI
|
||||
stackAnimation[activeStack->ID]->setBorderColor(AnimationControls::getNoBorder());
|
||||
stackAnimation[activeStack->unitId()]->setBorderColor(AnimationControls::getNoBorder());
|
||||
|
||||
activeStack = stack;
|
||||
|
||||
if (activeStack) // update UI
|
||||
stackAnimation[activeStack->ID]->setBorderColor(AnimationControls::getGoldBorder());
|
||||
stackAnimation[activeStack->unitId()]->setBorderColor(AnimationControls::getGoldBorder());
|
||||
|
||||
owner.windowObject->blockUI(activeStack == nullptr);
|
||||
}
|
||||
|
||||
bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
|
||||
{
|
||||
BattleHex currentActionTarget;
|
||||
if(owner.curInt->curAction)
|
||||
{
|
||||
auto target = owner.curInt->curAction->getTarget(owner.curInt->cb.get());
|
||||
if(!target.empty())
|
||||
currentActionTarget = target.at(0).hexValue;
|
||||
}
|
||||
|
||||
//do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1)
|
||||
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->getCount() == 1)
|
||||
return false;
|
||||
|
||||
if(!stack->alive())
|
||||
@ -266,7 +258,7 @@ bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
|
||||
return false;
|
||||
|
||||
// if stack has any ongoing animation - hide the box
|
||||
if (stackAmountBoxHidden.count(stack->ID))
|
||||
if (stackAmountBoxHidden.count(stack->unitId()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -299,7 +291,7 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack *
|
||||
|
||||
bool doubleWide = stack->doubleWide();
|
||||
bool turnedRight = facingRight(stack);
|
||||
bool attacker = stack->side == BattleSide::ATTACKER;
|
||||
bool attacker = stack->unitSide() == BattleSide::ATTACKER;
|
||||
|
||||
BattleHex stackPos = stack->getPosition();
|
||||
|
||||
@ -342,8 +334,8 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
|
||||
fullFilter = ColorFilter::genCombined(fullFilter, filter.effect);
|
||||
}
|
||||
|
||||
stackAnimation[stack->ID]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
|
||||
stackAnimation[stack->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
|
||||
stackAnimation[stack->unitId()]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
|
||||
}
|
||||
|
||||
void BattleStacksController::update()
|
||||
@ -398,17 +390,17 @@ void BattleStacksController::addNewAnim(BattleAnimation *anim)
|
||||
|
||||
auto stackAnimation = dynamic_cast<BattleStackAnimation*>(anim);
|
||||
if(stackAnimation)
|
||||
stackAmountBoxHidden.insert(stackAnimation->stack->ID);
|
||||
stackAmountBoxHidden.insert(stackAnimation->stack->unitId());
|
||||
}
|
||||
|
||||
void BattleStacksController::stackRemoved(uint32_t stackID)
|
||||
{
|
||||
if (getActiveStack() && getActiveStack()->ID == stackID)
|
||||
if (getActiveStack() && getActiveStack()->unitId() == stackID)
|
||||
{
|
||||
BattleAction *action = new BattleAction();
|
||||
action->side = owner.defendingHeroInstance ? (owner.curInt->playerID == owner.defendingHeroInstance->tempOwner) : false;
|
||||
action->actionType = EActionType::CANCEL;
|
||||
action->stackNumber = getActiveStack()->ID;
|
||||
action->stackNumber = getActiveStack()->unitId();
|
||||
owner.givenCommand.setn(action);
|
||||
setActiveStack(nullptr);
|
||||
}
|
||||
@ -439,7 +431,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
|
||||
|
||||
// FIXME: this check is better, however not usable since stacksAreAttacked is called after net pack is applyed - petrification is already removed
|
||||
// if (needsReverse && !attackedInfo.defender->isFrozen())
|
||||
if (needsReverse && stackAnimation[attackedInfo.defender->ID]->getType() != ECreatureAnimType::FROZEN)
|
||||
if (needsReverse && stackAnimation[attackedInfo.defender->unitId()]->getType() != ECreatureAnimType::FROZEN)
|
||||
{
|
||||
owner.addToAnimationStage(EAnimationEvents::MOVEMENT, [=]()
|
||||
{
|
||||
@ -493,7 +485,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
|
||||
{
|
||||
owner.addToAnimationStage(EAnimationEvents::AFTER_HIT, [=](){
|
||||
addNewAnim(new ColorTransformAnimation(owner, attackedInfo.defender, "summonFadeOut", nullptr));
|
||||
stackRemoved(attackedInfo.defender->ID);
|
||||
stackRemoved(attackedInfo.defender->unitId());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -511,7 +503,7 @@ void BattleStacksController::stackTeleported(const CStack *stack, std::vector<Ba
|
||||
});
|
||||
|
||||
owner.addToAnimationStage(EAnimationEvents::AFTER_HIT, [=](){
|
||||
stackAnimation[stack->ID]->pos.moveTo(getStackPositionAtHex(destHex.back(), stack));
|
||||
stackAnimation[stack->unitId()]->pos.moveTo(getStackPositionAtHex(destHex.back(), stack));
|
||||
addNewAnim( new ColorTransformAnimation(owner, stack, "teleportFadeIn", nullptr) );
|
||||
});
|
||||
|
||||
@ -536,7 +528,7 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH
|
||||
addNewAnim(new MovementStartAnimation(owner, stack));
|
||||
});
|
||||
|
||||
if (!stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1)))
|
||||
if (!stack->hasBonus(Selector::typeSubtype(BonusType::FLYING, 1)))
|
||||
{
|
||||
owner.addToAnimationStage(EAnimationEvents::MOVEMENT, [&]()
|
||||
{
|
||||
@ -559,7 +551,7 @@ bool BattleStacksController::shouldAttackFacingRight(const CStack * attacker, co
|
||||
attacker,
|
||||
defender);
|
||||
|
||||
if (attacker->side == BattleSide::ATTACKER)
|
||||
if (attacker->unitSide() == BattleSide::ATTACKER)
|
||||
return !mustReverse;
|
||||
else
|
||||
return mustReverse;
|
||||
@ -677,9 +669,9 @@ void BattleStacksController::endAction(const BattleAction* action)
|
||||
|
||||
for (const CStack *s : stacks)
|
||||
{
|
||||
bool shouldFaceRight = s && s->side == BattleSide::ATTACKER;
|
||||
bool shouldFaceRight = s && s->unitSide() == BattleSide::ATTACKER;
|
||||
|
||||
if (s && facingRight(s) != shouldFaceRight && s->alive() && stackAnimation[s->ID]->isIdle())
|
||||
if (s && facingRight(s) != shouldFaceRight && s->alive() && stackAnimation[s->unitId()]->isIdle())
|
||||
{
|
||||
addNewAnim(new ReverseAnimation(owner, s, s->getPosition()));
|
||||
}
|
||||
@ -723,7 +715,7 @@ void BattleStacksController::activateStack()
|
||||
if ( !stackToActivate)
|
||||
return;
|
||||
|
||||
owner.trySetActivePlayer(stackToActivate->owner);
|
||||
owner.trySetActivePlayer(stackToActivate->unitOwner());
|
||||
|
||||
setActiveStack(stackToActivate);
|
||||
stackToActivate = nullptr;
|
||||
@ -740,7 +732,7 @@ const CStack* BattleStacksController::getActiveStack() const
|
||||
|
||||
bool BattleStacksController::facingRight(const CStack * stack) const
|
||||
{
|
||||
return stackFacingRight.at(stack->ID);
|
||||
return stackFacingRight.at(stack->unitId());
|
||||
}
|
||||
|
||||
Point BattleStacksController::getStackPositionAtHex(BattleHex hexNum, const CStack * stack) const
|
||||
@ -765,7 +757,7 @@ Point BattleStacksController::getStackPositionAtHex(BattleHex hexNum, const CSta
|
||||
//shifting position for double - hex creatures
|
||||
if(stack->doubleWide())
|
||||
{
|
||||
if(stack->side == BattleSide::ATTACKER)
|
||||
if(stack->unitSide() == BattleSide::ATTACKER)
|
||||
{
|
||||
if(facingRight(stack))
|
||||
ret.x -= 44;
|
||||
@ -801,7 +793,7 @@ void BattleStacksController::removeExpiredColorFilters()
|
||||
{
|
||||
if (!filter.persistent)
|
||||
{
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(Bonus::SPELL_EFFECT, filter.source->id), Selector::all))
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, filter.source->id), Selector::all))
|
||||
return true;
|
||||
if (filter.effect == ColorFilter::genEmptyShifter())
|
||||
return true;
|
||||
@ -820,9 +812,9 @@ void BattleStacksController::updateHoveredStacks()
|
||||
continue;
|
||||
|
||||
if (stack == activeStack)
|
||||
stackAnimation[stack->ID]->setBorderColor(AnimationControls::getGoldBorder());
|
||||
stackAnimation[stack->unitId()]->setBorderColor(AnimationControls::getGoldBorder());
|
||||
else
|
||||
stackAnimation[stack->ID]->setBorderColor(AnimationControls::getNoBorder());
|
||||
stackAnimation[stack->unitId()]->setBorderColor(AnimationControls::getNoBorder());
|
||||
}
|
||||
|
||||
for(const auto * stack : newStacks)
|
||||
@ -830,9 +822,9 @@ void BattleStacksController::updateHoveredStacks()
|
||||
if (vstd::contains(mouseHoveredStacks, stack))
|
||||
continue;
|
||||
|
||||
stackAnimation[stack->ID]->setBorderColor(AnimationControls::getBlueBorder());
|
||||
if (stackAnimation[stack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0 && stack->alive() && !stack->isFrozen())
|
||||
stackAnimation[stack->ID]->playOnce(ECreatureAnimType::MOUSEON);
|
||||
stackAnimation[stack->unitId()]->setBorderColor(AnimationControls::getBlueBorder());
|
||||
if (stackAnimation[stack->unitId()]->framesInGroup(ECreatureAnimType::MOUSEON) > 0 && stack->alive() && !stack->isFrozen())
|
||||
stackAnimation[stack->unitId()]->playOnce(ECreatureAnimType::MOUSEON);
|
||||
}
|
||||
|
||||
mouseHoveredStacks = newStacks;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../windows/CSpellWindow.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -50,19 +51,23 @@ BattleWindow::BattleWindow(BattleInterface & owner):
|
||||
|
||||
const JsonNode config(ResourceID("config/widgets/BattleWindow.json"));
|
||||
|
||||
addCallback("options", std::bind(&BattleWindow::bOptionsf, this));
|
||||
addCallback("surrender", std::bind(&BattleWindow::bSurrenderf, this));
|
||||
addCallback("flee", std::bind(&BattleWindow::bFleef, this));
|
||||
addCallback("autofight", std::bind(&BattleWindow::bAutofightf, this));
|
||||
addCallback("spellbook", std::bind(&BattleWindow::bSpellf, this));
|
||||
addCallback("wait", std::bind(&BattleWindow::bWaitf, this));
|
||||
addCallback("defence", std::bind(&BattleWindow::bDefencef, this));
|
||||
addCallback("consoleUp", std::bind(&BattleWindow::bConsoleUpf, this));
|
||||
addCallback("consoleDown", std::bind(&BattleWindow::bConsoleDownf, this));
|
||||
addCallback("tacticNext", std::bind(&BattleWindow::bTacticNextStack, this));
|
||||
addCallback("tacticEnd", std::bind(&BattleWindow::bTacticPhaseEnd, this));
|
||||
addCallback("alternativeAction", std::bind(&BattleWindow::bSwitchActionf, this));
|
||||
|
||||
addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this));
|
||||
addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this));
|
||||
addShortcut(EShortcut::BATTLE_RETREAT, std::bind(&BattleWindow::bFleef, this));
|
||||
addShortcut(EShortcut::BATTLE_AUTOCOMBAT, std::bind(&BattleWindow::bAutofightf, this));
|
||||
addShortcut(EShortcut::BATTLE_CAST_SPELL, std::bind(&BattleWindow::bSpellf, this));
|
||||
addShortcut(EShortcut::BATTLE_WAIT, std::bind(&BattleWindow::bWaitf, this));
|
||||
addShortcut(EShortcut::BATTLE_DEFEND, std::bind(&BattleWindow::bDefencef, this));
|
||||
addShortcut(EShortcut::BATTLE_CONSOLE_UP, std::bind(&BattleWindow::bConsoleUpf, this));
|
||||
addShortcut(EShortcut::BATTLE_CONSOLE_DOWN, std::bind(&BattleWindow::bConsoleDownf, this));
|
||||
addShortcut(EShortcut::BATTLE_TACTICS_NEXT, std::bind(&BattleWindow::bTacticNextStack, this));
|
||||
addShortcut(EShortcut::BATTLE_TACTICS_END, std::bind(&BattleWindow::bTacticPhaseEnd, this));
|
||||
addShortcut(EShortcut::BATTLE_SELECT_ACTION, std::bind(&BattleWindow::bSwitchActionf, this));
|
||||
|
||||
addShortcut(EShortcut::BATTLE_TOGGLE_QUEUE, [this](){ this->toggleQueueVisibility();});
|
||||
addShortcut(EShortcut::BATTLE_USE_CREATURE_SPELL, [this](){ this->owner.actionsController->enterCreatureCastingMode(); });
|
||||
addShortcut(EShortcut::GLOBAL_CANCEL, [this](){ this->owner.actionsController->endCastingSpell(); });
|
||||
|
||||
build(config);
|
||||
|
||||
console = widget<BattleConsole>("console");
|
||||
@ -182,43 +187,14 @@ void BattleWindow::deactivate()
|
||||
LOCPLINT->cingconsole->deactivate();
|
||||
}
|
||||
|
||||
void BattleWindow::keyPressed(const SDL_Keycode & key)
|
||||
void BattleWindow::keyPressed(EShortcut key)
|
||||
{
|
||||
if (owner.openingPlaying())
|
||||
{
|
||||
owner.openingEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
if(key == SDLK_q)
|
||||
{
|
||||
toggleQueueVisibility();
|
||||
}
|
||||
else if(key == SDLK_f)
|
||||
{
|
||||
owner.actionsController->enterCreatureCastingMode();
|
||||
}
|
||||
else if(key == SDLK_ESCAPE)
|
||||
{
|
||||
owner.actionsController->endCastingSpell();
|
||||
}
|
||||
else if(GH.isKeyboardShiftDown())
|
||||
{
|
||||
// save and activate setting
|
||||
Settings movementHighlightOnHover = settings.write["battle"]["movementHighlightOnHover"];
|
||||
movementHighlightOnHoverCache = movementHighlightOnHover->Bool();
|
||||
movementHighlightOnHover->Bool() = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BattleWindow::keyReleased(const SDL_Keycode & key)
|
||||
{
|
||||
if(!GH.isKeyboardShiftDown())
|
||||
{
|
||||
// set back to initial state
|
||||
Settings movementHighlightOnHover = settings.write["battle"]["movementHighlightOnHover"];
|
||||
movementHighlightOnHover->Bool() = movementHighlightOnHoverCache;
|
||||
}
|
||||
InterfaceObjectConfigurable::keyPressed(key);
|
||||
}
|
||||
|
||||
void BattleWindow::clickRight(tribool down, bool previousState)
|
||||
@ -450,11 +426,11 @@ void BattleWindow::bSpellf()
|
||||
{
|
||||
//TODO: move to spell mechanics, add more information to spell cast problem
|
||||
//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
|
||||
auto blockingBonus = owner.currentHero()->getBonusLocalFirst(Selector::type()(Bonus::BLOCK_ALL_MAGIC));
|
||||
auto blockingBonus = owner.currentHero()->getBonusLocalFirst(Selector::type()(BonusType::BLOCK_ALL_MAGIC));
|
||||
if (!blockingBonus)
|
||||
return;
|
||||
|
||||
if (blockingBonus->source == Bonus::ARTIFACT)
|
||||
if (blockingBonus->source == BonusSource::ARTIFACT)
|
||||
{
|
||||
const auto artID = ArtifactID(blockingBonus->sid);
|
||||
//If we have artifact, put name of our hero. Otherwise assume it's the enemy.
|
||||
@ -554,40 +530,18 @@ void BattleWindow::blockUI(bool on)
|
||||
|
||||
bool canWait = owner.stacksController->getActiveStack() ? !owner.stacksController->getActiveStack()->waitedThisTurn : false;
|
||||
|
||||
if(auto w = widget<CButton>("options"))
|
||||
w->block(on);
|
||||
if(auto w = widget<CButton>("flee"))
|
||||
w->block(on || !owner.curInt->cb->battleCanFlee());
|
||||
if(auto w = widget<CButton>("surrender"))
|
||||
w->block(on || owner.curInt->cb->battleGetSurrenderCost() < 0);
|
||||
if(auto w = widget<CButton>("cast"))
|
||||
w->block(on || owner.tacticsMode || !canCastSpells);
|
||||
if(auto w = widget<CButton>("wait"))
|
||||
w->block(on || owner.tacticsMode || !canWait);
|
||||
if(auto w = widget<CButton>("defence"))
|
||||
w->block(on || owner.tacticsMode);
|
||||
if(auto w = widget<CButton>("alternativeAction"))
|
||||
w->block(on || owner.tacticsMode);
|
||||
if(auto w = widget<CButton>("autofight"))
|
||||
w->block(owner.actionsController->spellcastingModeActive());
|
||||
|
||||
auto btactEnd = widget<CButton>("tacticEnd");
|
||||
auto btactNext = widget<CButton>("tacticNext");
|
||||
if(owner.tacticsMode && btactEnd && btactNext)
|
||||
{
|
||||
btactNext->block(on);
|
||||
btactEnd->block(on);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bConsoleUp = widget<CButton>("consoleUp");
|
||||
auto bConsoleDown = widget<CButton>("consoleDown");
|
||||
if(bConsoleUp && bConsoleDown)
|
||||
{
|
||||
bConsoleUp->block(on);
|
||||
bConsoleDown->block(on);
|
||||
}
|
||||
}
|
||||
setShortcutBlocked(EShortcut::GLOBAL_OPTIONS, on);
|
||||
setShortcutBlocked(EShortcut::BATTLE_RETREAT, on || !owner.curInt->cb->battleCanFlee());
|
||||
setShortcutBlocked(EShortcut::BATTLE_SURRENDER, on || owner.curInt->cb->battleGetSurrenderCost() < 0);
|
||||
setShortcutBlocked(EShortcut::BATTLE_CAST_SPELL, on || owner.tacticsMode || !canCastSpells);
|
||||
setShortcutBlocked(EShortcut::BATTLE_WAIT, on || owner.tacticsMode || !canWait);
|
||||
setShortcutBlocked(EShortcut::BATTLE_DEFEND, on || owner.tacticsMode);
|
||||
setShortcutBlocked(EShortcut::BATTLE_SELECT_ACTION, on || owner.tacticsMode);
|
||||
setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, owner.actionsController->spellcastingModeActive());
|
||||
setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on && owner.tacticsMode);
|
||||
setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on && owner.tacticsMode);
|
||||
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode);
|
||||
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode);
|
||||
}
|
||||
|
||||
std::optional<uint32_t> BattleWindow::getQueueHoveredUnitId()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user