mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-30 04:30:42 +02:00
Merge remote-tracking branch 'upstream/develop' into vlc-obstacles
# Conflicts: # .github/workflows/github.yml # CI/mac/before_install.sh # client/CGameInfo.cpp # client/CGameInfo.h # client/battle/CBattleInterface.cpp # config/gameConfig.json # config/obstacles.json # config/randomMap.json # config/schemas/battlefield.json # config/terrains.json # include/vcmi/Services.h # lib/BattleFieldHandler.cpp # lib/BattleFieldHandler.h # lib/CHeroHandler.cpp # lib/CHeroHandler.h # lib/CModHandler.cpp # lib/GameConstants.cpp # lib/GameConstants.h # lib/IGameCallback.cpp # lib/Terrain.cpp # lib/Terrain.h # lib/VCMI_Lib.cpp # lib/VCMI_Lib.h # lib/battle/BattleInfo.cpp # lib/mapObjects/CObjectClassesHandler.cpp # lib/mapObjects/CObjectClassesHandler.h # lib/mapObjects/ObjectTemplate.cpp # lib/mapObjects/ObjectTemplate.h # lib/mapping/CCampaignHandler.h # lib/rmg/CMapGenerator.cpp # lib/rmg/CMapGenerator.h # lib/rmg/ConnectionsPlacer.cpp # lib/rmg/ObjectManager.cpp # lib/rmg/ObjectManager.h # lib/rmg/ObstaclePlacer.cpp # lib/rmg/RiverPlacer.cpp # lib/rmg/RmgObject.cpp # lib/rmg/RoadPlacer.cpp # lib/rmg/RoadPlacer.h # lib/rmg/TownPlacer.cpp # lib/rmg/TreasurePlacer.cpp # lib/rmg/TreasurePlacer.h # lib/rmg/WaterProxy.cpp # lib/spells/CSpellHandler.cpp # test/mock/mock_Services.h
This commit is contained in:
commit
e5e5e78fca
31
.github/workflows/github.yml
vendored
31
.github/workflows/github.yml
vendored
@ -23,12 +23,22 @@ jobs:
|
||||
os: ubuntu-20.04
|
||||
test: 0
|
||||
preset: linux-gcc-release
|
||||
- platform: mac
|
||||
os: macos-10.15
|
||||
- platform: mac-intel
|
||||
os: macos-12
|
||||
test: 0
|
||||
pack: 1
|
||||
extension: dmg
|
||||
preset: macos-ninja-release
|
||||
preset: macos-conan-ninja-release
|
||||
conan_profile: macos-intel
|
||||
artifact_platform: intel
|
||||
- platform: mac-arm
|
||||
os: macos-12
|
||||
test: 0
|
||||
pack: 1
|
||||
extension: dmg
|
||||
preset: macos-arm-conan-ninja-release
|
||||
conan_profile: macos-arm
|
||||
artifact_platform: arm
|
||||
- platform: mxe
|
||||
os: ubuntu-20.04
|
||||
mxe: i686-w64-mingw32.shared
|
||||
@ -59,6 +69,18 @@ jobs:
|
||||
MXE_TARGET: ${{ matrix.mxe }}
|
||||
VCMI_BUILD_PLATFORM: x64
|
||||
|
||||
- name: Conan setup
|
||||
if: "${{ matrix.conan_profile != '' }}"
|
||||
run: |
|
||||
pip3 install conan
|
||||
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 }}
|
||||
|
||||
- name: Git branch name
|
||||
id: git-branch-name
|
||||
uses: EthanSK/git-branch-name-action@v1
|
||||
@ -66,6 +88,9 @@ jobs:
|
||||
- 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
|
||||
env:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/server/vcmiserver
|
||||
/launcher/vcmilauncher
|
||||
/launcher/vcmilauncher_automoc.cpp
|
||||
/conan-generated
|
||||
|
||||
build/
|
||||
.cache/*
|
||||
|
@ -92,6 +92,8 @@ int32_t estimateTownIncome(CCallback * cb, const CGObjectInstance * target, cons
|
||||
|
||||
TResources getCreatureBankResources(const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
{
|
||||
//Fixme: unused variable hero
|
||||
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance);
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
auto resources = bankInfo->getPossibleResourcesReward();
|
||||
@ -114,11 +116,33 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
|
||||
auto creatures = bankInfo->getPossibleCreaturesReward();
|
||||
uint64_t result = 0;
|
||||
|
||||
for(auto c : creatures)
|
||||
const auto& slots = hero->Slots();
|
||||
ui64 weakestStackPower = 0;
|
||||
if (slots.size() >= GameConstants::ARMY_SIZE)
|
||||
{
|
||||
result += c.data.type->AIValue * c.data.count * c.chance / 100;
|
||||
//No free slot, we might discard our weakest stack
|
||||
weakestStackPower = std::numeric_limits<ui64>().max();
|
||||
for (const auto stack : slots)
|
||||
{
|
||||
vstd::amin(weakestStackPower, stack.second->getPower());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c : creatures)
|
||||
{
|
||||
//Only if hero has slot for this creature in the army
|
||||
if (hero->getSlotFor(c.data.type).validSlot())
|
||||
{
|
||||
result += (c.data.type->AIValue * c.data.count) * c.chance;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we will need to discard the weakest stack
|
||||
result += (c.data.type->AIValue * c.data.count - weakestStackPower) * c.chance;
|
||||
}
|
||||
}
|
||||
result /= 100; //divide by total chance
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -168,13 +192,13 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
|
||||
return 1500;
|
||||
|
||||
auto statsValue =
|
||||
4 * art->valOfBonuses(Bonus::LAND_MOVEMENT)
|
||||
10 * art->valOfBonuses(Bonus::LAND_MOVEMENT)
|
||||
+ 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)
|
||||
+ 700 * art->getDefense(false)
|
||||
+ 500 * art->valOfBonuses(Bonus::LUCK);
|
||||
|
||||
auto classValue = 0;
|
||||
@ -234,6 +258,8 @@ uint64_t RewardEvaluator::getArmyReward(
|
||||
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
? enemyArmyEliminationRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->getArmyStrength()
|
||||
: 0;
|
||||
case Obj::PANDORAS_BOX:
|
||||
return 5000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -273,7 +299,14 @@ float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy
|
||||
vstd::amax(objectValue, getStrategicalValue(obj));
|
||||
}
|
||||
|
||||
return objectValue / 2.0f + enemy->level / 15.0f;
|
||||
/*
|
||||
1. If an enemy hero can attack nearby object, it's not useful to capture the object on our own.
|
||||
Killing the hero is almost as important (0.9) as capturing the object itself.
|
||||
|
||||
2. The formula quickly approaches 1.0 as hero level increases,
|
||||
but higher level always means higher value and the minimal value for level 1 hero is 0.5
|
||||
*/
|
||||
return std::min(1.0f, objectValue * 0.9f + (1.0f - (1.0f / (1 + enemy->level))));
|
||||
}
|
||||
|
||||
float RewardEvaluator::getResourceRequirementStrength(int resType) const
|
||||
@ -322,13 +355,28 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
case Obj::RESOURCE:
|
||||
return target->subID == Res::GOLD ? 0 : 0.1f * getResourceRequirementStrength(target->subID);
|
||||
|
||||
case Obj::CREATURE_BANK:
|
||||
{
|
||||
auto resourceReward = getCreatureBankResources(target, nullptr);
|
||||
float sum = 0.0f;
|
||||
for (TResources::nziterator it (resourceReward); it.valid(); it++)
|
||||
{
|
||||
//Evaluate resources used for construction. Gold is evaluated separately.
|
||||
if (it->resType != Res::GOLD)
|
||||
{
|
||||
sum += 0.1f * getResourceRequirementStrength(it->resType);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
case Obj::TOWN:
|
||||
if(ai->buildAnalyzer->getDevelopmentInfo().empty())
|
||||
return 1;
|
||||
|
||||
return dynamic_cast<const CGTownInstance *>(target)->hasFort()
|
||||
? (target->tempOwner == PlayerColor::NEUTRAL ? 0.8f : 1.0f)
|
||||
: 0.5f;
|
||||
: 0.7f;
|
||||
|
||||
case Obj::HERO:
|
||||
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
@ -385,6 +433,9 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
|
||||
return 8;
|
||||
case Obj::WITCH_HUT:
|
||||
return evaluateWitchHutSkillScore(dynamic_cast<const CGWitchHut *>(target), hero, role);
|
||||
case Obj::PANDORAS_BOX:
|
||||
//Can contains experience, spells, or skills (only on custom maps)
|
||||
return 2.5f;
|
||||
case Obj::HERO:
|
||||
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
? enemyHeroEliminationSkillRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->level
|
||||
@ -464,6 +515,11 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
|
||||
return 10000;
|
||||
case Obj::SEA_CHEST:
|
||||
return 1500;
|
||||
case Obj::PANDORAS_BOX:
|
||||
return 5000;
|
||||
case Obj::PRISON:
|
||||
//Objectively saves us 2500 to hire hero
|
||||
return GameConstants::HERO_GOLD_COST;
|
||||
case Obj::HERO:
|
||||
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
? heroEliminationBonus + enemyArmyEliminationGoldRewardRatio * getArmyCost(dynamic_cast<const CGHeroInstance *>(target))
|
||||
|
2
AUTHORS
2
AUTHORS
@ -80,4 +80,4 @@ Dmitry Orlov, <shubus.corporation@gmail.com>
|
||||
* special buildings support in fan towns, new features and bug fixes
|
||||
|
||||
Andrey Cherkas aka nordsoft, <nordsoft@yahoo.com>
|
||||
* random map generator features and bug fixes
|
||||
* new terrain support, random map generator features and various bug fixes
|
11
CI/conan/macos-arm
Normal file
11
CI/conan/macos-arm
Normal file
@ -0,0 +1,11 @@
|
||||
[settings]
|
||||
os=Macos
|
||||
os.version=11.0
|
||||
arch=armv8
|
||||
compiler=apple-clang
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
11
CI/conan/macos-intel
Normal file
11
CI/conan/macos-intel
Normal file
@ -0,0 +1,11 @@
|
||||
[settings]
|
||||
os=Macos
|
||||
os.version=10.13
|
||||
arch=x86_64
|
||||
compiler=apple-clang
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
4
CI/mac-arm/before_install.sh
Executable file
4
CI/mac-arm/before_install.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DEPS_FILENAME=intel-cross-arm
|
||||
. CI/mac/before_install.sh
|
4
CI/mac-intel/before_install.sh
Executable file
4
CI/mac-intel/before_install.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DEPS_FILENAME=intel
|
||||
. CI/mac/before_install.sh
|
13
CI/mac/before_install.sh
Normal file → Executable file
13
CI/mac/before_install.sh
Normal file → Executable file
@ -1,8 +1,9 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
brew update
|
||||
#brew pin python@3.9
|
||||
brew install smpeg2 libpng freetype qt5 ffmpeg ninja boost tbb luajit
|
||||
brew install sdl2 sdl2_ttf sdl2_image sdl2_mixer
|
||||
echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV
|
||||
|
||||
echo CMAKE_PREFIX_PATH="$(brew --prefix)/opt/qt5:$CMAKE_PREFIX_PATH" >> $GITHUB_ENV
|
||||
brew install ninja
|
||||
|
||||
mkdir ~/.conan ; cd ~/.conan
|
||||
curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/latest/download/$DEPS_FILENAME.txz" \
|
||||
| tar -xf -
|
||||
|
@ -9,9 +9,6 @@ project(VCMI)
|
||||
# - There is problem with running fixup_bundle in main project after subdirectories.
|
||||
# Cmake put them after all install code of main CMakelists in cmake_install.cmake
|
||||
# Currently I just added extra add_subdirectory and CMakeLists.txt in osx directory to bypass that.
|
||||
# - Try to fix build with RPATH.
|
||||
# Currently if CMAKE_MACOSX_RPATH=1 then AI libs unable to find @rpath/libvcmi.dylib
|
||||
# I tried to set few different INSTALL_RPATH for all targets in AI directory, but nothing worked.
|
||||
#
|
||||
# MXE:
|
||||
# - Try to implement MXE support into BundleUtilities so we can deploy deps automatically
|
||||
@ -40,8 +37,8 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
set(VCMI_VERSION_MAJOR 0)
|
||||
set(VCMI_VERSION_MINOR 99)
|
||||
set(VCMI_VERSION_MAJOR 1)
|
||||
set(VCMI_VERSION_MINOR 0)
|
||||
set(VCMI_VERSION_PATCH 0)
|
||||
|
||||
option(ENABLE_ERM "Enable compilation of ERM scripting module" ON)
|
||||
@ -90,6 +87,7 @@ define_property(
|
||||
# Generate Version.cpp
|
||||
if(ENABLE_GITVERSION)
|
||||
add_custom_target(update_version ALL
|
||||
BYPRODUCTS "Version.cpp"
|
||||
COMMAND ${CMAKE_COMMAND} -DGIT_SHA1="${GIT_SHA1}" -P "${PROJECT_SOURCE_DIR}/cmake_modules/Version.cmake"
|
||||
)
|
||||
else()
|
||||
@ -130,10 +128,6 @@ set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel Release RelWithDebInfo "")
|
||||
# Release falls back to RelWithDebInfo, then MinSizeRel
|
||||
set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel "")
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH 0)
|
||||
endif(APPLE)
|
||||
|
||||
if(MINGW OR MSVC)
|
||||
# Windows Vista or newer for FuzzyLite 6 to compile
|
||||
add_definitions(-D_WIN32_WINNT=0x0600)
|
||||
@ -225,7 +219,13 @@ endif()
|
||||
############################################
|
||||
|
||||
find_package(Boost 1.48.0 REQUIRED COMPONENTS date_time filesystem locale program_options system thread)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
# Conan compatibility
|
||||
if(TARGET zlib::zlib)
|
||||
add_library(ZLIB::ZLIB ALIAS zlib::zlib)
|
||||
endif()
|
||||
|
||||
find_package(ffmpeg REQUIRED COMPONENTS avutil swscale avformat avcodec)
|
||||
option(FORCE_BUNDLED_MINIZIP "Force bundled Minizip library" OFF)
|
||||
if(NOT FORCE_BUNDLED_MINIZIP)
|
||||
@ -290,6 +290,7 @@ elseif(APPLE)
|
||||
# includes lib path which determines where to install shared libraries (either /lib or /lib64)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks")
|
||||
if(ENABLE_MONOLITHIC_INSTALL)
|
||||
set(BIN_DIR "." CACHE STRING "Where to install binaries")
|
||||
set(LIB_DIR "." CACHE STRING "Where to install main library")
|
||||
@ -301,7 +302,7 @@ elseif(APPLE)
|
||||
set(APP_BUNDLE_RESOURCES_DIR "${APP_BUNDLE_CONTENTS_DIR}/Resources")
|
||||
|
||||
set(BIN_DIR "${APP_BUNDLE_BINARY_DIR}" CACHE STRING "Where to install binaries")
|
||||
set(LIB_DIR "${APP_BUNDLE_BINARY_DIR}" CACHE STRING "Where to install main library")
|
||||
set(LIB_DIR "${APP_BUNDLE_CONTENTS_DIR}/Frameworks" CACHE STRING "Where to install main library")
|
||||
set(DATA_DIR "${APP_BUNDLE_RESOURCES_DIR}/Data" CACHE STRING "Where to install data files")
|
||||
endif()
|
||||
else()
|
||||
|
@ -11,8 +11,8 @@
|
||||
"PACKAGE_FILE_NAME" : "$env{VCMI_PACKAGE_FILE_NAME}",
|
||||
"PACKAGE_NAME_SUFFIX" : "$env{VCMI_PACKAGE_NAME_SUFFIX}",
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"FORCE_BUNDLED_FL" : "0",
|
||||
"ENABLE_TEST": "0"
|
||||
"FORCE_BUNDLED_FL": "OFF",
|
||||
"ENABLE_TEST": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -65,6 +65,26 @@
|
||||
"description": "VCMI MacOS Ninja",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "macos-conan-ninja-release",
|
||||
"displayName": "Ninja+Conan release",
|
||||
"description": "VCMI MacOS Ninja using Conan",
|
||||
"inherits": "default-release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/conan-generated/conan_toolchain.cmake",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-arm-conan-ninja-release",
|
||||
"displayName": "Ninja+Conan arm64 release",
|
||||
"description": "VCMI MacOS-arm64 Ninja using Conan",
|
||||
"inherits": "macos-conan-ninja-release",
|
||||
"cacheVariables": {
|
||||
"ENABLE_ERM": "OFF",
|
||||
"ENABLE_LUA": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-xcode-release",
|
||||
"displayName": "XCode release",
|
||||
@ -99,6 +119,16 @@
|
||||
"configurePreset": "macos-ninja-release",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "macos-conan-ninja-release",
|
||||
"configurePreset": "macos-conan-ninja-release",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "macos-arm-conan-ninja-release",
|
||||
"configurePreset": "macos-arm-conan-ninja-release",
|
||||
"inherits": "default-release"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"configurePreset": "windows-msvc-release",
|
||||
|
55
ChangeLog
55
ChangeLog
@ -1,9 +1,12 @@
|
||||
0.99 -> 1.00
|
||||
0.99 -> 1.0
|
||||
|
||||
GENERAL:
|
||||
* Spectator mode was implemented through command-line options
|
||||
* Some main menu settings get saved after returning to main menu - last selected map, save etc.
|
||||
* Restart scenario button should work correctly now
|
||||
* Skyship Grail works now immediately after capturing without battle
|
||||
* Lodestar Grail implemented
|
||||
* Fixed Gargoyles immunity
|
||||
* New bonuses:
|
||||
- SOUL_STEAL - "WoG ghost" ability, should work somewhat same as in H3
|
||||
- TRANSMUTATION - "WoG werewolf"-like ability
|
||||
@ -35,17 +38,53 @@ MODS:
|
||||
* Added bonus updaters for hero specialties
|
||||
* Added allOf, anyOf and noneOf qualifiers for bonus limiters
|
||||
* Added bonus limiters: alignment, faction and terrain
|
||||
* Supported new terrains, new battlefields, custom water and rock terrains
|
||||
* Following special buildings becomes available in the fan towns:
|
||||
- attackVisitingBonus
|
||||
- defenceVisitingBonus
|
||||
- spellPowerVisitingBonus
|
||||
- knowledgeVisitingBonus
|
||||
- experienceVisitingBonus
|
||||
- lighthouse
|
||||
- treasury
|
||||
|
||||
SOUND:
|
||||
* Fixed many mising or wrong pickup and visit sounds for map objects
|
||||
* All map objects now have ambient sounds identical to OH3
|
||||
|
||||
RANDOM MAP GENERATOR:
|
||||
* Random map generator supports water modes (normal, islands)
|
||||
* Added config randomMap.json with settings for map generator
|
||||
* Added parameter for template allowedWaterContent
|
||||
* Extra resource packs appear nearby mines
|
||||
* All map objects now have ambient sounds identical to OH3
|
||||
|
||||
RANDOM MAP GENERATOR:
|
||||
* Random map generator supports water modes (normal, islands)
|
||||
* Added config randomMap.json with settings for map generator
|
||||
* Added parameter for template allowedWaterContent
|
||||
* Extra resource packs appear nearby mines
|
||||
* Underground can be player starting place for factions allowed to be placed underground
|
||||
* Improved obstacles placement aesthetics
|
||||
* Rivers are generated on the random maps
|
||||
* RMG works more stable, various crashes have been fixed
|
||||
* Treasures requiring guards are guaranteed to be protected
|
||||
|
||||
VCAI:
|
||||
* Reworked goal decomposition engine, fixing many loopholes. AI will now pick correct goals faster.
|
||||
* AI will now use universal pathfinding globally
|
||||
* AI can use Summon Boat and Town Portal
|
||||
* AI can gather and save resources on purpose
|
||||
* AI will only buy army on demand instead of every turn
|
||||
* AI can distinguish the value of all map objects
|
||||
* General speed optimizations
|
||||
|
||||
BATTLES:
|
||||
* Towers should block ranged retaliation
|
||||
* AI can bypass broken wall with moat instead of standing and waiting until gate is destroyed
|
||||
* Towers do not attack war machines automatically
|
||||
* Draw is possible now as battle outcome in case the battle ends with only summoned creatures (both sides loose)
|
||||
|
||||
ADVENTURE MAP:
|
||||
* Added buttons and keyboard shortcuts to quickly exchange army and artifacts between heroes
|
||||
* Fix: Captured town should not be duplicated on the UI
|
||||
|
||||
LAUNCHER:
|
||||
* Implemented notifications about updates
|
||||
* Supported redirection links for downloading mods
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
|
BIN
Mods/vcmi/Data/QuickRecruitmentWindow/CreaturePurchaseCard.png
Executable file
BIN
Mods/vcmi/Data/QuickRecruitmentWindow/CreaturePurchaseCard.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
40
Mods/vcmi/Data/s/std.verm
Executable file
40
Mods/vcmi/Data/s/std.verm
Executable file
@ -0,0 +1,40 @@
|
||||
VERM
|
||||
; standard verm file, global engine things should be put here
|
||||
|
||||
!?PI;
|
||||
; example 1 --- Hello World
|
||||
![print ^Hello world!^]
|
||||
|
||||
; example 2 --- simple arithmetics
|
||||
![defun add [x y] [+ x y]]
|
||||
![print [add 2 3]]
|
||||
|
||||
; example 3 --- semantic macros
|
||||
![defmacro do-n-times [times body]
|
||||
`[progn
|
||||
[setq do-counter 0]
|
||||
[setq do-max ,times]
|
||||
[do [< do-counter do-max]
|
||||
[progn
|
||||
[setq do-counter [+ do-counter 1]]
|
||||
,body
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
![do-n-times 4 [print ^tekst\n^]]
|
||||
|
||||
|
||||
; example 4 --- conditional expression
|
||||
![if [> 2 1] [print ^Wieksze^] [print ^Mniejsze^]]
|
||||
|
||||
; example 5 --- lambda expressions
|
||||
![[lambda [x y] [if [> x y] [print ^wieksze^] [print ^mniejsze^]]] 2 3]
|
||||
|
||||
; example 6 --- resursion
|
||||
![defun factorial [n]
|
||||
[if [= n 0] 1
|
||||
[* n [factorial [- n 1]]]
|
||||
]
|
||||
]
|
||||
![print [factorial 8]]
|
14
Mods/vcmi/Data/s/testy.erm
Executable file
14
Mods/vcmi/Data/s/testy.erm
Executable file
@ -0,0 +1,14 @@
|
||||
ZVSE
|
||||
!?PI;
|
||||
!!VRv2777:S4;
|
||||
!!DO1/0/5/1&v2777<>1:P0;
|
||||
|
||||
!?FU1;
|
||||
!!VRv2778:Sx16%2;
|
||||
!!IF&x16>3:M^Hello world number %X16! To duza liczba^;
|
||||
!!IF&v2778==0&x16<=3:M^Hello world number %X16! To mala parzysta liczba^;
|
||||
!!IF&v2778==1&x16<=3:M^Hello world number %X16! To mala nieparzysta liczba^;
|
||||
|
||||
!?PI;
|
||||
!!VRz10:S^Composed hello ^;
|
||||
!!IF:M^%Z10%%world%%, v2777=%V2777, v2778=%V2778!^;
|
BIN
Mods/vcmi/Maps/VCMI_Tests_2011b.h3m
Executable file
BIN
Mods/vcmi/Maps/VCMI_Tests_2011b.h3m
Executable file
Binary file not shown.
@ -10,7 +10,6 @@
|
||||
// CMT.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
#include "StdInc.h"
|
||||
#include <SDL_mixer.h>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
@ -160,7 +159,7 @@ static void SDLLogCallback(void* userdata,
|
||||
|
||||
#if defined(VCMI_WINDOWS) && !defined(__GNUC__) && defined(VCMI_WITH_DEBUG_CONSOLE)
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
#elif defined(VCMI_APPLE) || defined(VCMI_ANDROID)
|
||||
#elif defined(VCMI_ANDROID)
|
||||
int SDL_main(int argc, char *argv[])
|
||||
#else
|
||||
int main(int argc, char * argv[])
|
||||
|
@ -142,7 +142,6 @@ set(client_HEADERS
|
||||
Graphics.h
|
||||
mapHandler.h
|
||||
resource.h
|
||||
SDLMain.h
|
||||
SDLRWwrapper.h
|
||||
)
|
||||
|
||||
@ -152,9 +151,7 @@ if(ANDROID) # android needs client/server to be libraries, not executables, so w
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(client_SRCS ${client_SRCS} SDLMain.m)
|
||||
elseif(WIN32)
|
||||
if(WIN32)
|
||||
set(client_ICON "VCMI_client.rc")
|
||||
endif()
|
||||
|
||||
@ -175,11 +172,12 @@ if(WIN32)
|
||||
if(NOT ENABLE_DEBUG_CONSOLE)
|
||||
target_link_libraries(vcmiclient SDL2::SDL2main)
|
||||
endif()
|
||||
target_compile_definitions(vcmiclient PRIVATE WINDOWS_IGNORE_PACKING_MISMATCH)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmiclient PRIVATE
|
||||
vcmi SDL2::SDL2 SDL2::Image SDL2::Mixer SDL2::TTF
|
||||
ffmpeg::swscale ffmpeg::avutil ffmpeg::avcodec ffmpeg::avformat TBB::tbb
|
||||
ffmpeg::swscale ffmpeg::avutil ffmpeg::avcodec ffmpeg::avformat
|
||||
)
|
||||
|
||||
target_include_directories(vcmiclient
|
||||
|
@ -272,13 +272,14 @@ void CSoundHandler::soundFinishedCallback(int channel)
|
||||
{
|
||||
std::map<int, std::function<void()> >::iterator iter;
|
||||
iter = callbacks.find(channel);
|
||||
if (iter == callbacks.end())
|
||||
return;
|
||||
|
||||
assert(iter != callbacks.end());
|
||||
|
||||
if (iter->second)
|
||||
iter->second();
|
||||
|
||||
auto callback = std::move(iter->second);
|
||||
callbacks.erase(iter);
|
||||
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
int CSoundHandler::ambientGetRange() const
|
||||
|
@ -47,6 +47,10 @@
|
||||
|
||||
#include <vcmi/events/EventBus.h>
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
template<typename T> class CApplyOnLobby;
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
@ -694,7 +698,23 @@ void CServerHandler::threadRunServer()
|
||||
}
|
||||
comm += " > \"" + logName + '\"';
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
int result = -1;
|
||||
const auto bufSize = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), nullptr, 0);
|
||||
if(bufSize > 0)
|
||||
{
|
||||
std::wstring wComm(bufSize, {});
|
||||
const auto convertResult = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), &wComm[0], bufSize);
|
||||
if(convertResult > 0)
|
||||
result = ::_wsystem(wComm.c_str());
|
||||
else
|
||||
logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to convert server launch command to wide string: " + comm);
|
||||
}
|
||||
else
|
||||
logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to obtain buffer length to convert server launch command to wide string : " + comm);
|
||||
#else
|
||||
int result = std::system(comm.c_str());
|
||||
#endif
|
||||
if (result == 0)
|
||||
{
|
||||
logNetwork->info("Server closed correctly");
|
||||
|
@ -173,8 +173,8 @@ void Graphics::loadHeroAnimations()
|
||||
{
|
||||
for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates())
|
||||
{
|
||||
if (!heroAnimations.count(templ.animationFile))
|
||||
heroAnimations[templ.animationFile] = loadHeroAnimation(templ.animationFile);
|
||||
if (!heroAnimations.count(templ->animationFile))
|
||||
heroAnimations[templ->animationFile] = loadHeroAnimation(templ->animationFile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,21 +381,21 @@ std::shared_ptr<CAnimation> Graphics::getAnimation(const CGObjectInstance* obj)
|
||||
return getAnimation(obj->appearance);
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
|
||||
std::shared_ptr<CAnimation> Graphics::getAnimation(std::shared_ptr<const ObjectTemplate> info)
|
||||
{
|
||||
//the only(?) invisible object
|
||||
if(info.id == Obj::EVENT)
|
||||
if(info->id == Obj::EVENT)
|
||||
{
|
||||
return std::shared_ptr<CAnimation>();
|
||||
}
|
||||
|
||||
if(info.animationFile.empty())
|
||||
if(info->animationFile.empty())
|
||||
{
|
||||
logGlobal->warn("Def name for obj (%d,%d) is empty!", info.id, info.subid);
|
||||
logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
|
||||
return std::shared_ptr<CAnimation>();
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> ret = mapObjectAnimations[info.animationFile];
|
||||
std::shared_ptr<CAnimation> ret = mapObjectAnimations[info->animationFile];
|
||||
|
||||
//already loaded
|
||||
if(ret)
|
||||
@ -404,8 +404,8 @@ std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = std::make_shared<CAnimation>(info.animationFile);
|
||||
mapObjectAnimations[info.animationFile] = ret;
|
||||
ret = std::make_shared<CAnimation>(info->animationFile);
|
||||
mapObjectAnimations[info->animationFile] = ret;
|
||||
|
||||
ret->preload();
|
||||
return ret;
|
||||
|
@ -98,7 +98,7 @@ public:
|
||||
void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
|
||||
|
||||
std::shared_ptr<CAnimation> getAnimation(const CGObjectInstance * obj);
|
||||
std::shared_ptr<CAnimation> getAnimation(const ObjectTemplate & info);
|
||||
std::shared_ptr<CAnimation> getAnimation(std::shared_ptr<const ObjectTemplate> info);
|
||||
};
|
||||
|
||||
extern Graphics * graphics;
|
||||
|
@ -1,16 +0,0 @@
|
||||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#ifndef _SDLMain_h_
|
||||
#define _SDLMain_h_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SDLMain : NSObject
|
||||
@end
|
||||
|
||||
#endif /* _SDLMain_h_ */
|
383
client/SDLMain.m
383
client/SDLMain.m
@ -1,383 +0,0 @@
|
||||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDLMain.h"
|
||||
#include <sys/param.h> /* for MAXPATHLEN */
|
||||
#include <unistd.h>
|
||||
|
||||
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
|
||||
but the method still is there and works. To avoid warnings, we declare
|
||||
it ourselves here. */
|
||||
@interface NSApplication(SDL_Missing_Methods)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
/* Use this flag to determine whether we use SDLMain.nib or not */
|
||||
#define SDL_USE_NIB_FILE 0
|
||||
|
||||
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||
#define SDL_USE_CPS 1
|
||||
#ifdef SDL_USE_CPS
|
||||
/* Portions of CPS.h */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static BOOL gFinderLaunch;
|
||||
static BOOL gCalledAppMainline = FALSE;
|
||||
|
||||
static NSString *getApplicationName(void)
|
||||
{
|
||||
const NSDictionary *dict;
|
||||
NSString *appName = 0;
|
||||
|
||||
/* Determine the application name */
|
||||
dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||
if (dict)
|
||||
appName = [dict objectForKey: @"CFBundleName"];
|
||||
|
||||
if (![appName length])
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* A helper category for NSString */
|
||||
@interface NSString (ReplaceSubString)
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface SDLApplication : NSApplication
|
||||
@end
|
||||
|
||||
@implementation SDLApplication
|
||||
/* Invoked from the Quit menu item */
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
/* Post a SDL_QUIT event */
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
@end
|
||||
|
||||
/* The main class of the application, the application's delegate */
|
||||
@implementation SDLMain
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||
{
|
||||
if (shouldChdir)
|
||||
{
|
||||
char parentdir[MAXPATHLEN];
|
||||
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
|
||||
if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
|
||||
chdir(parentdir); /* chdir to the binary app's parent */
|
||||
}
|
||||
CFRelease(url);
|
||||
CFRelease(url2);
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
|
||||
/* Fix menu to contain the real app name instead of "SDL App" */
|
||||
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
||||
{
|
||||
NSRange aRange;
|
||||
NSEnumerator *enumerator;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
||||
|
||||
enumerator = [[aMenu itemArray] objectEnumerator];
|
||||
while ((menuItem = [enumerator nextObject]))
|
||||
{
|
||||
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
||||
if ([menuItem hasSubmenu])
|
||||
[self fixMenu:[menuItem submenu] withAppName:appName];
|
||||
}
|
||||
[ aMenu sizeToFit ];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = getApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
/* Replacement for NSApplicationMain */
|
||||
static void CustomApplicationMain (int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
SDLMain *sdlMain;
|
||||
|
||||
/* Ensure the application object is initialised */
|
||||
[SDLApplication sharedApplication];
|
||||
|
||||
#ifdef SDL_USE_CPS
|
||||
{
|
||||
CPSProcessSerNum PSN;
|
||||
/* Tell the dock about us */
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[SDLApplication sharedApplication];
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
sdlMain = [[SDLMain alloc] init];
|
||||
[NSApp setDelegate:sdlMain];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[sdlMain release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Catch document open requests...this lets us notice files when the app
|
||||
* was launched by double-clicking a document, or when a document was
|
||||
* dragged/dropped on the app's icon. You need to have a
|
||||
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||
* apparently.
|
||||
*
|
||||
* Files are added to gArgv, so to the app, they'll look like command line
|
||||
* arguments. Previously, apps launched from the finder had nothing but
|
||||
* an argv[0].
|
||||
*
|
||||
* This message may be received multiple times to open several docs on launch.
|
||||
*
|
||||
* This message is ignored once the app's mainline has been called.
|
||||
*/
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
const char *temparg;
|
||||
size_t arglen;
|
||||
char *arg;
|
||||
char **newargv;
|
||||
|
||||
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||
return FALSE;
|
||||
|
||||
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||
return FALSE;
|
||||
|
||||
temparg = [filename UTF8String];
|
||||
arglen = SDL_strlen(temparg) + 1;
|
||||
arg = (char *) SDL_malloc(arglen);
|
||||
if (arg == NULL)
|
||||
return FALSE;
|
||||
|
||||
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||
if (newargv == NULL)
|
||||
{
|
||||
SDL_free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
gArgv = newargv;
|
||||
|
||||
SDL_strlcpy(arg, temparg, arglen);
|
||||
gArgv[gArgc++] = arg;
|
||||
gArgv[gArgc] = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
[self setupWorkingDirectory:gFinderLaunch];
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* Set the main menu to contain the real app name instead of "SDL App" */
|
||||
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
||||
#endif
|
||||
|
||||
/* Hand off to main application code */
|
||||
gCalledAppMainline = TRUE;
|
||||
status = SDL_main (gArgc, gArgv);
|
||||
|
||||
/* We're done, thank you for playing */
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSString (ReplaceSubString)
|
||||
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||
{
|
||||
unsigned int bufferSize;
|
||||
unsigned int selfLen = [self length];
|
||||
unsigned int aStringLen = [aString length];
|
||||
unichar *buffer;
|
||||
NSRange localRange;
|
||||
NSString *result;
|
||||
|
||||
bufferSize = selfLen + aStringLen - aRange.length;
|
||||
buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||
|
||||
/* Get first part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aRange.location;
|
||||
[self getCharacters:buffer range:localRange];
|
||||
|
||||
/* Get middle part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aStringLen;
|
||||
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||
|
||||
/* Get last part into buffer */
|
||||
localRange.location = aRange.location + aRange.length;
|
||||
localRange.length = selfLen - localRange.location;
|
||||
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||
|
||||
/* Build output string */
|
||||
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||
|
||||
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#ifdef main
|
||||
# undef main
|
||||
#endif
|
||||
|
||||
|
||||
/* Main entry point to executable - should *not* be SDL_main! */
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
/* Copy the arguments into a global variable */
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||
gArgv[0] = argv[0];
|
||||
gArgv[1] = NULL;
|
||||
gArgc = 1;
|
||||
gFinderLaunch = YES;
|
||||
} else {
|
||||
int i;
|
||||
gArgc = argc;
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||
for (i = 0; i <= argc; i++)
|
||||
gArgv[i] = argv[i];
|
||||
gFinderLaunch = NO;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
[SDLApplication poseAsClass:[NSApplication class]];
|
||||
NSApplicationMain (argc, argv);
|
||||
#else
|
||||
CustomApplicationMain (argc, argv);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -420,22 +420,6 @@ void CMeleeAttackAnimation::endAnim()
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CMovementAnimation::shouldRotate()
|
||||
{
|
||||
Point begPosition = CClickableHex::getXYUnitAnim(oldPos, stack, owner);
|
||||
Point endPosition = CClickableHex::getXYUnitAnim(nextHex, stack, owner);
|
||||
|
||||
if((begPosition.x > endPosition.x) && owner->creDir[stack->ID] == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((begPosition.x < endPosition.x) && owner->creDir[stack->ID] == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMovementAnimation::init()
|
||||
{
|
||||
if( !isEarliest(false) )
|
||||
@ -456,7 +440,7 @@ bool CMovementAnimation::init()
|
||||
}
|
||||
|
||||
//reverse unit if necessary
|
||||
if(shouldRotate())
|
||||
if(owner->shouldRotate(stack, oldPos, nextHex))
|
||||
{
|
||||
// it seems that H3 does NOT plays full rotation animation here in most situations
|
||||
// Logical since it takes quite a lot of time
|
||||
|
@ -118,8 +118,6 @@ public:
|
||||
class CMovementAnimation : public CBattleStackAnimation
|
||||
{
|
||||
private:
|
||||
bool shouldRotate();
|
||||
|
||||
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
|
||||
ui32 curentMoveIndex; // index of nextHex in destTiles
|
||||
|
||||
|
@ -503,13 +503,14 @@ void CBattleInterface::activate()
|
||||
bWait->activate();
|
||||
bDefence->activate();
|
||||
|
||||
for (auto hex : bfield)
|
||||
hex->activate();
|
||||
|
||||
if (attackingHero)
|
||||
attackingHero->activate();
|
||||
if (defendingHero)
|
||||
defendingHero->activate();
|
||||
|
||||
for (auto hex : bfield)
|
||||
hex->activate();
|
||||
|
||||
if (settings["battle"]["showQueue"].Bool())
|
||||
queue->activate();
|
||||
|
||||
@ -1561,6 +1562,19 @@ CPlayerInterface *CBattleInterface::getCurrentPlayerInterface() const
|
||||
return curInt.get();
|
||||
}
|
||||
|
||||
bool CBattleInterface::shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex)
|
||||
{
|
||||
Point begPosition = CClickableHex::getXYUnitAnim(oldPos,stack, this);
|
||||
Point endPosition = CClickableHex::getXYUnitAnim(nextHex, stack, this);
|
||||
|
||||
if((begPosition.x > endPosition.x) && creDir[stack->ID])
|
||||
return true;
|
||||
else if((begPosition.x < endPosition.x) && !creDir[stack->ID])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CBattleInterface::setActiveStack(const CStack *stack)
|
||||
{
|
||||
if (activeStack) // update UI
|
||||
@ -1928,6 +1942,9 @@ void CBattleInterface::startAction(const BattleAction* action)
|
||||
{
|
||||
pendingAnims.push_back(std::make_pair(new CMovementStartAnimation(this, stack), false));
|
||||
}
|
||||
|
||||
if(shouldRotate(stack, stack->getPosition(), actionTarget.at(0).hexValue))
|
||||
pendingAnims.push_back(std::make_pair(new CReverseAnimation(this, stack, stack->getPosition(), true), false));
|
||||
}
|
||||
|
||||
redraw(); // redraw after deactivation, including proper handling of hovered hexes
|
||||
|
@ -294,6 +294,7 @@ public:
|
||||
void setAnimSpeed(int set); //speed of animation; range 1..100
|
||||
int getAnimSpeed() const; //speed of animation; range 1..100
|
||||
CPlayerInterface *getCurrentPlayerInterface() const;
|
||||
bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex);
|
||||
|
||||
std::vector<std::shared_ptr<CClickableHex>> bfield; //11 lines, 17 hexes on each
|
||||
SDL_Surface *cellBorder, *cellShade;
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "CAnimation.h"
|
||||
|
||||
#include <SDL_image.h>
|
||||
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
|
@ -16,6 +16,10 @@
|
||||
#include "../Graphics.h"
|
||||
#include "../CMT.h"
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
const SDL_Color Colors::YELLOW = { 229, 215, 123, 0 };
|
||||
const SDL_Color Colors::WHITE = { 255, 243, 222, 0 };
|
||||
const SDL_Color Colors::METALLIC_GOLD = { 173, 142, 66, 0 };
|
||||
@ -786,19 +790,35 @@ SDL_Color CSDL_Ext::makeColor(ui8 r, ui8 g, ui8 b, ui8 a)
|
||||
|
||||
void CSDL_Ext::startTextInput(SDL_Rect * where)
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_FALSE)
|
||||
{
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
SDL_SetTextInputRect(where);
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSDL_Ext::stopTextInput()
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||
{
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
STRONG_INLINE static uint32_t mapColor(SDL_Surface * surface, SDL_Color color)
|
||||
|
@ -360,8 +360,10 @@ void CBonusSelection::updateAfterStateChange()
|
||||
buttonStart->disable();
|
||||
buttonRestart->enable();
|
||||
buttonBack->block(false);
|
||||
buttonDifficultyLeft->disable();
|
||||
buttonDifficultyRight->disable();
|
||||
if(buttonDifficultyLeft)
|
||||
buttonDifficultyLeft->disable();
|
||||
if(buttonDifficultyRight)
|
||||
buttonDifficultyRight->disable();
|
||||
}
|
||||
if(CSH->campaignBonus == -1)
|
||||
{
|
||||
|
@ -131,25 +131,19 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
|
||||
CSH->sendStartGame(allowOnlyAI);
|
||||
buttonStart->block(true);
|
||||
}
|
||||
catch(ExceptionMapMissing & e)
|
||||
catch(std::exception & e)
|
||||
{
|
||||
(void)e; // unused
|
||||
}
|
||||
catch(ExceptionNoHuman & e)
|
||||
{
|
||||
(void)e; // unused
|
||||
// You must position yourself prior to starting the game.
|
||||
CInfoWindow::showInfoDialog(std::ref(CGI->generaltexth->allTexts[530]), CInfoWindow::TCompsInfo(), PlayerColor(1));
|
||||
}
|
||||
catch(ExceptionNoTemplate & e)
|
||||
{
|
||||
(void)e; // unused
|
||||
// Could not create a random map that fits current choices.
|
||||
CInfoWindow::showInfoDialog(std::ref(CGI->generaltexth->allTexts[751]), CInfoWindow::TCompsInfo(), PlayerColor(1));
|
||||
logGlobal->error("Exception during startScenario: %s", e.what());
|
||||
|
||||
if(std::string(e.what()) == "ExceptionNoHuman")
|
||||
CInfoWindow::showInfoDialog(std::ref(CGI->generaltexth->allTexts[530]), CInfoWindow::TCompsInfo(), PlayerColor(1));
|
||||
|
||||
if(std::string(e.what()) == "ExceptionNoTemplate")
|
||||
CInfoWindow::showInfoDialog(std::ref(CGI->generaltexth->allTexts[751]), CInfoWindow::TCompsInfo(), PlayerColor(1));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
||||
logGlobal->error("Unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -979,7 +979,7 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGH
|
||||
if (hero->boat)
|
||||
animation = graphics->boatAnimations[hero->boat->subID];
|
||||
else
|
||||
animation = graphics->heroAnimations[hero->appearance.animationFile];
|
||||
animation = graphics->heroAnimations[hero->appearance->animationFile];
|
||||
|
||||
bool moving = !hero->isStanding;
|
||||
int group = getHeroFrameGroup(hero->moveDir, moving);
|
||||
@ -1486,8 +1486,8 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
|
||||
return true;
|
||||
if (!b)
|
||||
return false;
|
||||
if (a->appearance.printPriority != b->appearance.printPriority)
|
||||
return a->appearance.printPriority > b->appearance.printPriority;
|
||||
if (a->appearance->printPriority != b->appearance->printPriority)
|
||||
return a->appearance->printPriority > b->appearance->printPriority;
|
||||
|
||||
if(a->pos.y != b->pos.y)
|
||||
return a->pos.y < b->pos.y;
|
||||
|
@ -324,6 +324,11 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
|
||||
|
||||
upgradeBtn->addOverlay(std::make_shared<CAnimImage>("CPRSMALL", VLC->creh->objects[upgradeInfo.info.newID[buttonIndex]]->iconIndex));
|
||||
|
||||
if(buttonsToCreate == 1) // single upgrade avaialbe
|
||||
{
|
||||
upgradeBtn->assignedKeys.insert(SDLK_u);
|
||||
}
|
||||
|
||||
upgrade[buttonIndex] = upgradeBtn;
|
||||
}
|
||||
}
|
||||
|
@ -29,20 +29,20 @@ void QuickRecruitmentWindow::setButtons()
|
||||
|
||||
void QuickRecruitmentWindow::setCancelButton()
|
||||
{
|
||||
cancelButton = std::make_shared<CButton>(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_RETURN);
|
||||
cancelButton = std::make_shared<CButton>(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_ESCAPE);
|
||||
cancelButton->setImageOrder(0, 1, 2, 3);
|
||||
}
|
||||
|
||||
void QuickRecruitmentWindow::setBuyButton()
|
||||
{
|
||||
buyButton = std::make_shared<CButton>(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); });
|
||||
buyButton = std::make_shared<CButton>(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); }, SDLK_RETURN);
|
||||
cancelButton->assignedKeys.insert(SDLK_ESCAPE);
|
||||
buyButton->setImageOrder(0, 1, 2, 3);
|
||||
}
|
||||
|
||||
void QuickRecruitmentWindow::setMaxButton()
|
||||
{
|
||||
maxButton = std::make_shared<CButton>(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); });
|
||||
maxButton = std::make_shared<CButton>(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }, SDLK_m);
|
||||
maxButton->setImageOrder(0, 1, 2, 3);
|
||||
}
|
||||
|
||||
|
197
conanfile.py
Normal file
197
conanfile.py
Normal file
@ -0,0 +1,197 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.apple import is_apple_os
|
||||
from conan.tools.cmake import CMakeDeps
|
||||
from conans import tools
|
||||
|
||||
import os
|
||||
|
||||
class VCMI(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "CMakeToolchain"
|
||||
requires = [
|
||||
"boost/1.79.0",
|
||||
"ffmpeg/4.4",
|
||||
"minizip/1.2.12",
|
||||
"onetbb/2021.3.0", # Nullkiller AI
|
||||
"qt/5.15.5", # launcher
|
||||
"sdl/2.0.20",
|
||||
"sdl_image/2.0.5",
|
||||
"sdl_mixer/2.0.4",
|
||||
"sdl_ttf/2.0.18",
|
||||
]
|
||||
|
||||
def _disableQtOptions(disableFlag, options):
|
||||
return " ".join([f"-{disableFlag}-{tool}" for tool in options])
|
||||
|
||||
_qtOptions = [
|
||||
_disableQtOptions("no", [
|
||||
"gif",
|
||||
"ico",
|
||||
]),
|
||||
_disableQtOptions("no-feature", [
|
||||
# xpm format is required for Drag'n'Drop support
|
||||
"imageformat_bmp",
|
||||
"imageformat_jpeg",
|
||||
"imageformat_ppm",
|
||||
"imageformat_xbm",
|
||||
|
||||
# we need only macdeployqt
|
||||
# TODO: disabling these doesn't disable generation of CMake targets
|
||||
# TODO: in Qt 6.3 it's a part of qtbase
|
||||
# "assistant",
|
||||
# "designer",
|
||||
# "distancefieldgenerator",
|
||||
# "kmap2qmap",
|
||||
# "linguist",
|
||||
# "makeqpf",
|
||||
# "pixeltool",
|
||||
# "qdbus",
|
||||
# "qev",
|
||||
# "qtattributionsscanner",
|
||||
# "qtdiag",
|
||||
# "qtpaths",
|
||||
# "qtplugininfo",
|
||||
]),
|
||||
]
|
||||
|
||||
default_options = {
|
||||
# shared libs
|
||||
"boost/*:shared": True,
|
||||
"libpng/*:shared": True, # SDL_image and Qt depend on it
|
||||
"minizip/*:shared": True,
|
||||
"onetbb/*:shared": True,
|
||||
"qt/*:shared": True,
|
||||
|
||||
# we need only the following Boost parts:
|
||||
# date_time filesystem locale program_options system thread
|
||||
# some other parts are also enabled because they're dependents
|
||||
# see e.g. conan-center-index/recipes/boost/all/dependencies
|
||||
"boost/*:without_context": True,
|
||||
"boost/*:without_contract": True,
|
||||
"boost/*:without_coroutine": True,
|
||||
"boost/*:without_fiber": True,
|
||||
"boost/*:without_graph": True,
|
||||
"boost/*:without_graph_parallel": True,
|
||||
"boost/*:without_iostreams": True,
|
||||
"boost/*:without_json": True,
|
||||
"boost/*:without_log": True,
|
||||
"boost/*:without_math": True,
|
||||
"boost/*:without_mpi": True,
|
||||
"boost/*:without_nowide": True,
|
||||
"boost/*:without_python": True,
|
||||
"boost/*:without_random": True,
|
||||
"boost/*:without_regex": True,
|
||||
"boost/*:without_serialization": True,
|
||||
"boost/*:without_stacktrace": True,
|
||||
"boost/*:without_test": True,
|
||||
"boost/*:without_timer": True,
|
||||
"boost/*:without_type_erasure": True,
|
||||
"boost/*:without_wave": True,
|
||||
|
||||
"ffmpeg/*:avdevice": False,
|
||||
"ffmpeg/*:avfilter": False,
|
||||
"ffmpeg/*:postproc": False,
|
||||
"ffmpeg/*:swresample": False,
|
||||
"ffmpeg/*:with_freetype": False,
|
||||
"ffmpeg/*:with_libfdk_aac": False,
|
||||
"ffmpeg/*:with_libmp3lame": False,
|
||||
"ffmpeg/*:with_libvpx": False,
|
||||
"ffmpeg/*:with_libwebp": False,
|
||||
"ffmpeg/*:with_libx264": False,
|
||||
"ffmpeg/*:with_libx265": False,
|
||||
"ffmpeg/*:with_openh264": False,
|
||||
"ffmpeg/*:with_openjpeg": False,
|
||||
"ffmpeg/*:with_opus": False,
|
||||
"ffmpeg/*:with_programs": False,
|
||||
"ffmpeg/*:with_ssl": False,
|
||||
"ffmpeg/*:with_vorbis": False,
|
||||
|
||||
"sdl/*:vulkan": False,
|
||||
|
||||
"sdl_image/*:imageio": True,
|
||||
"sdl_image/*:lbm": False,
|
||||
"sdl_image/*:pnm": False,
|
||||
"sdl_image/*:svg": False,
|
||||
"sdl_image/*:tga": False,
|
||||
"sdl_image/*:with_libjpeg": False,
|
||||
"sdl_image/*:with_libtiff": False,
|
||||
"sdl_image/*:with_libwebp": False,
|
||||
"sdl_image/*:xcf": False,
|
||||
"sdl_image/*:xpm": False,
|
||||
"sdl_image/*:xv": False,
|
||||
|
||||
"sdl_mixer/*:flac": False,
|
||||
"sdl_mixer/*:mad": False,
|
||||
"sdl_mixer/*:mikmod": False,
|
||||
"sdl_mixer/*:modplug": False,
|
||||
"sdl_mixer/*:nativemidi": False,
|
||||
"sdl_mixer/*:opus": False,
|
||||
"sdl_mixer/*:wav": False,
|
||||
|
||||
"qt/*:config": " ".join(_qtOptions),
|
||||
"qt/*:openssl": False,
|
||||
"qt/*:qttools": True,
|
||||
"qt/*:with_freetype": False,
|
||||
"qt/*:with_libjpeg": False,
|
||||
"qt/*:with_md4c": False,
|
||||
"qt/*:with_mysql": False,
|
||||
"qt/*:with_odbc": False,
|
||||
"qt/*:with_openal": False,
|
||||
"qt/*:with_pq": False,
|
||||
|
||||
# transitive deps
|
||||
"pcre2/*:build_pcre2grep": False, # doesn't link to overridden bzip2 & zlib, the tool isn't needed anyway
|
||||
}
|
||||
|
||||
def configure(self):
|
||||
# workaround: macOS deployment target isn't passed to linker when building Boost
|
||||
# TODO: remove when https://github.com/conan-io/conan-center-index/pull/12468 is merged
|
||||
if is_apple_os(self):
|
||||
osVersion = self.settings.get_safe("os.version")
|
||||
if osVersion:
|
||||
deploymentTargetFlag = tools.apple_deployment_target_flag(
|
||||
self.settings.os,
|
||||
osVersion,
|
||||
self.settings.get_safe("os.sdk"),
|
||||
self.settings.get_safe("os.subsystem"),
|
||||
self.settings.get_safe("arch")
|
||||
)
|
||||
self.options["boost"].extra_b2_flags = f"linkflags={deploymentTargetFlag}"
|
||||
|
||||
def requirements(self):
|
||||
# use Apple system libraries instead of external ones
|
||||
if is_apple_os(self):
|
||||
systemLibsOverrides = [
|
||||
"bzip2/1.0.8",
|
||||
"libiconv/1.17",
|
||||
"sqlite3/3.39.2",
|
||||
"zlib/1.2.12",
|
||||
]
|
||||
for lib in systemLibsOverrides:
|
||||
self.requires(f"{lib}@kambala/apple", override=True)
|
||||
|
||||
# TODO: the latest official release of LuaJIT (which is quite old) can't be built for arm Mac
|
||||
if self.settings.os != "Macos" or self.settings.arch != "armv8":
|
||||
self.requires("luajit/2.0.5")
|
||||
|
||||
def generate(self):
|
||||
deps = CMakeDeps(self)
|
||||
if os.getenv("USE_CONAN_WITH_ALL_CONFIGS", "0") == "0":
|
||||
deps.generate()
|
||||
return
|
||||
|
||||
# allow using prebuilt deps with all configs
|
||||
# credits to https://github.com/conan-io/conan/issues/11607#issuecomment-1188500937 for the workaround
|
||||
configs = [
|
||||
"Debug",
|
||||
"MinSizeRel",
|
||||
"Release",
|
||||
"RelWithDebInfo",
|
||||
]
|
||||
for config in configs:
|
||||
print(f"generating CMakeDeps for {config}")
|
||||
deps.configuration = config
|
||||
deps.generate()
|
||||
|
||||
def imports(self):
|
||||
self.copy("*.dylib", "Frameworks", "lib")
|
12
config/creatures/conflux.json
Normal file → Executable file
12
config/creatures/conflux.json
Normal file → Executable file
@ -31,6 +31,12 @@
|
||||
"type" : "MORE_DAMAGE_FROM_SPELL",
|
||||
"subtype" : "spell.chainLightning",
|
||||
"val" : 100
|
||||
},
|
||||
"armageddonVulnerablity" :
|
||||
{
|
||||
"type" : "MORE_DAMAGE_FROM_SPELL",
|
||||
"subtype" : "spell.armageddon",
|
||||
"val" : 100
|
||||
}
|
||||
},
|
||||
"upgrades": ["stormElemental"],
|
||||
@ -523,6 +529,12 @@
|
||||
"type" : "MORE_DAMAGE_FROM_SPELL",
|
||||
"subtype" : "spell.chainLightning",
|
||||
"val" : 100
|
||||
},
|
||||
"armageddonVulnerablity" :
|
||||
{
|
||||
"type" : "MORE_DAMAGE_FROM_SPELL",
|
||||
"subtype" : "spell.armageddon",
|
||||
"val" : 100
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
2
config/creatures/inferno.json
Normal file → Executable file
2
config/creatures/inferno.json
Normal file → Executable file
@ -315,7 +315,7 @@
|
||||
"fireShield" :
|
||||
{
|
||||
"type" : "FIRE_SHIELD",
|
||||
"val" : 30
|
||||
"val" : 20
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
7
config/heroes/conflux.json
Normal file → Executable file
7
config/heroes/conflux.json
Normal file → Executable file
@ -46,8 +46,9 @@
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"health" : {
|
||||
"type" : "STACK_HEALTH",
|
||||
"damage" : {
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"subtype" : 0,
|
||||
"val" : 5
|
||||
},
|
||||
"attack" : {
|
||||
@ -173,7 +174,7 @@
|
||||
]
|
||||
},
|
||||
"bonuses" : {
|
||||
"health" : {
|
||||
"damage" : {
|
||||
"type" : "CREATURE_DAMAGE",
|
||||
"subtype" : 0,
|
||||
"val" : 5
|
||||
|
@ -478,6 +478,9 @@
|
||||
"object" : {
|
||||
"index" : 0,
|
||||
"aiValue" : 500,
|
||||
"sounds" : {
|
||||
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
|
||||
},
|
||||
"templates" : {
|
||||
"normal" : {
|
||||
"visitableFrom" : [ "+++", "+-+", "+++" ],
|
||||
|
@ -22,6 +22,7 @@
|
||||
},
|
||||
"minGuardStrength" : 2000,
|
||||
"defaultRoadType" : "pc", //pd - dirt, pg - gravel, pc - cobblestone
|
||||
"secondaryRoadType": "pd",
|
||||
"treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit
|
||||
"prisons" :
|
||||
{
|
||||
|
@ -363,12 +363,12 @@
|
||||
"type" : "object",
|
||||
"default": {},
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "repositoryURL", "enableInstalledMods", "autoCheckRepositories" ],
|
||||
"required" : [ "repositoryURL", "enableInstalledMods", "autoCheckRepositories", "updateOnStartup", "updateConfigUrl" ],
|
||||
"properties" : {
|
||||
"repositoryURL" : {
|
||||
"type" : "array",
|
||||
"default" : [
|
||||
"http://download.vcmi.eu/mods/repository/repository.json"
|
||||
"https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/github.json"
|
||||
],
|
||||
"items" : {
|
||||
"type" : "string"
|
||||
@ -381,6 +381,14 @@
|
||||
"autoCheckRepositories" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"updateOnStartup" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"updateConfigUrl" : {
|
||||
"type" : "string",
|
||||
"default" : "https://raw.githubusercontent.com/vcmi/vcmi-updates/master/vcmi-updates.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
"code" : "dt",
|
||||
"river" : "rm",
|
||||
"battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"],
|
||||
"terrainViewPatterns" : "dirt"
|
||||
"terrainViewPatterns" : "dirt",
|
||||
"horseSoundId" : 0
|
||||
},
|
||||
"sand" :
|
||||
{
|
||||
@ -22,7 +23,8 @@
|
||||
"river" : "rm",
|
||||
"battleFields" : ["sand_mesas"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "sand"
|
||||
"terrainViewPatterns" : "sand",
|
||||
"horseSoundId" : 1
|
||||
},
|
||||
"grass" :
|
||||
{
|
||||
@ -33,7 +35,8 @@
|
||||
"tiles" : "GRASTL",
|
||||
"code" : "gr",
|
||||
"river" : "rw",
|
||||
"battleFields" : ["grass_hills", "grass_pines"]
|
||||
"battleFields" : ["grass_hills", "grass_pines"],
|
||||
"horseSoundId" : 2
|
||||
},
|
||||
"snow" :
|
||||
{
|
||||
@ -44,7 +47,8 @@
|
||||
"tiles" : "SNOWTL",
|
||||
"code" : "sn",
|
||||
"river" : "ri",
|
||||
"battleFields" : ["snow_mountains", "snow_trees"]
|
||||
"battleFields" : ["snow_mountains", "snow_trees"],
|
||||
"horseSoundId" : 3
|
||||
},
|
||||
"swamp" :
|
||||
{
|
||||
@ -55,7 +59,8 @@
|
||||
"tiles" : "SWMPTL",
|
||||
"code" : "sw",
|
||||
"river" : "rw",
|
||||
"battleFields" : ["swamp_trees"]
|
||||
"battleFields" : ["swamp_trees"],
|
||||
"horseSoundId" : 4
|
||||
},
|
||||
"rough" :
|
||||
{
|
||||
@ -66,7 +71,8 @@
|
||||
"tiles" : "ROUGTL",
|
||||
"code" : "rg",
|
||||
"river" : "rm",
|
||||
"battleFields" : ["rough"]
|
||||
"battleFields" : ["rough"],
|
||||
"horseSoundId" : 5
|
||||
},
|
||||
"subterra" :
|
||||
{
|
||||
@ -79,7 +85,8 @@
|
||||
"code" : "sb",
|
||||
"river" : "rw",
|
||||
"battleFields" : ["subterranean"],
|
||||
"rockTerrain" : "rock"
|
||||
"rockTerrain" : "rock",
|
||||
"horseSoundId" : 6
|
||||
},
|
||||
"lava" :
|
||||
{
|
||||
@ -91,7 +98,8 @@
|
||||
"code" : "lv",
|
||||
"river" : "rl",
|
||||
"battleFields" : ["lava"],
|
||||
"rockTerrain" : "rock"
|
||||
"rockTerrain" : "rock",
|
||||
"horseSoundId" : 7
|
||||
},
|
||||
"water" :
|
||||
{
|
||||
@ -104,7 +112,11 @@
|
||||
"code" : "wt",
|
||||
"battleFields" : ["ship"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "water"
|
||||
"terrainViewPatterns" : "water",
|
||||
"horseSoundId" : 8,
|
||||
"sounds": {
|
||||
"ambient": ["LOOPOCEA"]
|
||||
}
|
||||
},
|
||||
"rock" :
|
||||
{
|
||||
@ -117,6 +129,7 @@
|
||||
"code" : "rc",
|
||||
"battleFields" : ["rocklands"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "rock"
|
||||
"terrainViewPatterns" : "rock",
|
||||
"horseSoundId" : 9
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ set(launcher_SRCS
|
||||
mainwindow_moc.cpp
|
||||
launcherdirs.cpp
|
||||
jsonutils.cpp
|
||||
updatedialog_moc.cpp
|
||||
)
|
||||
|
||||
set(launcher_HEADERS
|
||||
@ -41,6 +42,7 @@ set(launcher_HEADERS
|
||||
mainwindow_moc.h
|
||||
launcherdirs.h
|
||||
jsonutils.h
|
||||
updatedialog_moc.h
|
||||
)
|
||||
|
||||
set(launcher_FORMS
|
||||
@ -48,6 +50,7 @@ set(launcher_FORMS
|
||||
modManager/imageviewer_moc.ui
|
||||
settingsView/csettingsview_moc.ui
|
||||
mainwindow_moc.ui
|
||||
updatedialog_moc.ui
|
||||
)
|
||||
|
||||
assign_source_group(${launcher_SRCS} ${launcher_HEADERS} VCMI_launcher.rc)
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
|
||||
#include "updatedialog_moc.h"
|
||||
|
||||
void MainWindow::load()
|
||||
{
|
||||
// Set current working dir to executable folder.
|
||||
@ -80,6 +82,9 @@ MainWindow::MainWindow(QWidget * parent)
|
||||
|
||||
connect(ui->tabSelectList, SIGNAL(currentRowChanged(int)),
|
||||
ui->tabListWidget, SLOT(setCurrentIndex(int)));
|
||||
|
||||
if(settings["launcher"]["updateOnStartup"].Bool())
|
||||
UpdateDialog::showUpdateDialog(false);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
@ -20,6 +20,7 @@ CDownloadManager::CDownloadManager()
|
||||
|
||||
void CDownloadManager::downloadFile(const QUrl & url, const QString & file)
|
||||
{
|
||||
filename = file;
|
||||
QNetworkRequest request(url);
|
||||
FileEntry entry;
|
||||
entry.file.reset(new QFile(CLauncherDirs::get().downloadsPath() + '/' + file));
|
||||
@ -61,6 +62,23 @@ CDownloadManager::FileEntry & CDownloadManager::getEntry(QNetworkReply * reply)
|
||||
void CDownloadManager::downloadFinished(QNetworkReply * reply)
|
||||
{
|
||||
FileEntry & file = getEntry(reply);
|
||||
|
||||
QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
QUrl qurl = possibleRedirectUrl.toUrl();
|
||||
|
||||
if(possibleRedirectUrl.isValid())
|
||||
{
|
||||
for(int i = 0; i< currentDownloads.size(); ++i)
|
||||
{
|
||||
if(currentDownloads[i].file == file.file)
|
||||
{
|
||||
currentDownloads.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
downloadFile(qurl, filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if(file.reply->error())
|
||||
{
|
||||
|
@ -35,6 +35,8 @@ class CDownloadManager : public QObject
|
||||
};
|
||||
|
||||
QStringList encounteredErrors;
|
||||
|
||||
QString filename;
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
|
@ -92,6 +92,11 @@ bool CModEntry::isUpdateable() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CModEntry::isEssential() const
|
||||
{
|
||||
return getValue("storedLocaly").toBool();
|
||||
}
|
||||
|
||||
bool CModEntry::isInstalled() const
|
||||
{
|
||||
return !localData.isEmpty();
|
||||
@ -152,6 +157,10 @@ QVariantMap CModList::copyField(QVariantMap data, QString from, QString to)
|
||||
return renamed;
|
||||
}
|
||||
|
||||
void CModList::reloadRepositories()
|
||||
{
|
||||
}
|
||||
|
||||
void CModList::resetRepositories()
|
||||
{
|
||||
repositories.clear();
|
||||
@ -176,7 +185,7 @@ void CModList::modChanged(QString modID)
|
||||
{
|
||||
}
|
||||
|
||||
static QVariant getValue(QVariantMap input, QString path)
|
||||
static QVariant getValue(QVariant input, QString path)
|
||||
{
|
||||
if(path.size() > 1)
|
||||
{
|
||||
@ -184,7 +193,7 @@ static QVariant getValue(QVariantMap input, QString path)
|
||||
QString remainder = "/" + path.section('/', 2, -1);
|
||||
|
||||
entryName.remove(0, 1);
|
||||
return getValue(input.value(entryName).toMap(), remainder);
|
||||
return getValue(input.toMap().value(entryName), remainder);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -208,11 +217,29 @@ CModEntry CModList::getMod(QString modname) const
|
||||
}
|
||||
else
|
||||
{
|
||||
if(conf.canConvert<QVariantMap>())
|
||||
if(!conf.toMap().isEmpty())
|
||||
{
|
||||
settings = conf.toMap();
|
||||
if(settings.value("active").isNull())
|
||||
settings["active"] = true; // default
|
||||
}
|
||||
else
|
||||
settings.insert("active", conf);
|
||||
}
|
||||
|
||||
if(settings["active"].toBool())
|
||||
{
|
||||
QString rootPath = path.section('/', 0, 1);
|
||||
if(path != rootPath)
|
||||
{
|
||||
conf = getValue(modSettings, rootPath);
|
||||
const auto confMap = conf.toMap();
|
||||
if(!conf.isNull() && !confMap["active"].isNull() && !confMap["active"].toBool())
|
||||
{
|
||||
settings = confMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto entry : repositories)
|
||||
{
|
||||
|
@ -49,6 +49,8 @@ public:
|
||||
bool isUpdateable() const;
|
||||
// installed
|
||||
bool isInstalled() const;
|
||||
// vcmi essential files
|
||||
bool isEssential() const;
|
||||
|
||||
// see ModStatus enum
|
||||
int getModStatus() const;
|
||||
@ -74,6 +76,7 @@ class CModList
|
||||
|
||||
public:
|
||||
virtual void resetRepositories();
|
||||
virtual void reloadRepositories();
|
||||
virtual void addRepository(QVariantMap data);
|
||||
virtual void setLocalModList(QVariantMap data);
|
||||
virtual void setModSettings(QVariant data);
|
||||
|
@ -160,6 +160,12 @@ QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void CModListModel::reloadRepositories()
|
||||
{
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void CModListModel::resetRepositories()
|
||||
{
|
||||
beginResetModel();
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
|
||||
/// CModListContainer overrides
|
||||
void resetRepositories() override;
|
||||
void reloadRepositories() override;
|
||||
void addRepository(QVariantMap data) override;
|
||||
void modChanged(QString modID) override;
|
||||
|
||||
|
@ -310,10 +310,10 @@ void CModListView::selectMod(const QModelIndex & index)
|
||||
// Block buttons if action is not allowed at this time
|
||||
// TODO: automate handling of some of these cases instead of forcing player
|
||||
// to resolve all conflicts manually.
|
||||
ui->disableButton->setEnabled(!hasDependentMods);
|
||||
ui->disableButton->setEnabled(!hasDependentMods && !mod.isEssential());
|
||||
ui->enableButton->setEnabled(!hasBlockingMods && !hasInvalidDeps);
|
||||
ui->installButton->setEnabled(!hasInvalidDeps);
|
||||
ui->uninstallButton->setEnabled(!hasDependentMods);
|
||||
ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isEssential());
|
||||
ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods);
|
||||
|
||||
loadScreenshots();
|
||||
@ -522,6 +522,9 @@ void CModListView::downloadFile(QString file, QString url, QString description)
|
||||
|
||||
connect(dlManager, SIGNAL(finished(QStringList,QStringList,QStringList)),
|
||||
this, SLOT(downloadFinished(QStringList,QStringList,QStringList)));
|
||||
|
||||
|
||||
connect(modModel, &CModListModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged);
|
||||
|
||||
|
||||
QString progressBarFormat = "Downloading %s%. %p% (%v KB out of %m KB) finished";
|
||||
@ -658,7 +661,7 @@ void CModListView::installMods(QStringList archives)
|
||||
auto mod = modModel->getMod(modName);
|
||||
if(mod.isInstalled() && !mod.getValue("keepDisabled").toBool())
|
||||
{
|
||||
if(manager->enableMod(modName))
|
||||
if(mod.isDisabled() && manager->enableMod(modName))
|
||||
{
|
||||
for(QString child : modModel->getChildren(modName))
|
||||
enableMod(child);
|
||||
|
@ -24,21 +24,21 @@ static QString detectModArchive(QString path, QString modName)
|
||||
|
||||
QString modDirName;
|
||||
|
||||
for(auto file : files)
|
||||
for(int folderLevel : {0, 1}) //search in subfolder if there is no mod.json in the root
|
||||
{
|
||||
QString filename = QString::fromUtf8(file.c_str());
|
||||
if(filename.toLower().startsWith(modName))
|
||||
for(auto file : files)
|
||||
{
|
||||
// archive must contain mod.json file
|
||||
if(filename.toLower() == modName + "/mod.json")
|
||||
modDirName = filename.section('/', 0, 0);
|
||||
}
|
||||
else // all files must be in <modname> directory
|
||||
{
|
||||
return "";
|
||||
QString filename = QString::fromUtf8(file.c_str());
|
||||
modDirName = filename.section('/', 0, folderLevel);
|
||||
|
||||
if(filename == modDirName + "/mod.json")
|
||||
{
|
||||
return modDirName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modDirName;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
CModManager::CModManager(CModList * modList)
|
||||
@ -74,6 +74,7 @@ void CModManager::loadMods()
|
||||
CModHandler handler;
|
||||
handler.loadMods();
|
||||
auto installedMods = handler.getAllMods();
|
||||
localMods.clear();
|
||||
|
||||
for(auto modname : installedMods)
|
||||
{
|
||||
@ -82,6 +83,13 @@ void CModManager::loadMods()
|
||||
{
|
||||
boost::filesystem::path name = *CResourceHandler::get()->getResourceName(resID);
|
||||
auto mod = JsonUtils::JsonFromFile(pathToQString(name));
|
||||
if(!name.is_absolute())
|
||||
{
|
||||
auto json = JsonUtils::toJson(mod);
|
||||
json["storedLocaly"].Bool() = true;
|
||||
mod = JsonUtils::toVariant(json);
|
||||
}
|
||||
|
||||
localMods.insert(QString::fromUtf8(modname.c_str()).toLower(), mod);
|
||||
}
|
||||
}
|
||||
@ -259,11 +267,20 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
|
||||
return addError(modname, "Failed to extract mod data");
|
||||
}
|
||||
|
||||
QVariantMap json = JsonUtils::JsonFromFile(destDir + modDirName + "/mod.json").toMap();
|
||||
|
||||
localMods.insert(modname, json);
|
||||
modList->setLocalModList(localMods);
|
||||
modList->modChanged(modname);
|
||||
//rename folder and fix the path
|
||||
QDir extractedDir(destDir + modDirName);
|
||||
auto rc = QFile::rename(destDir + modDirName, destDir + modname);
|
||||
if (rc)
|
||||
extractedDir.setPath(destDir + modname);
|
||||
|
||||
//there are possible excessive files - remove them
|
||||
QString upperLevel = modDirName.section('/', 0, 0);
|
||||
if(upperLevel != modDirName)
|
||||
removeModDir(destDir + upperLevel);
|
||||
|
||||
CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &) { return true; });
|
||||
loadMods();
|
||||
modList->reloadRepositories();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -277,15 +294,13 @@ bool CModManager::doUninstallMod(QString modname)
|
||||
if(!QDir(modDir).exists())
|
||||
return addError(modname, "Data with this mod was not found");
|
||||
|
||||
if(!localMods.contains(modname))
|
||||
return addError(modname, "Data with this mod was not found");
|
||||
|
||||
QDir modFullDir(modDir);
|
||||
if(!removeModDir(modDir))
|
||||
return addError(modname, "Failed to delete mod data");
|
||||
return addError(modname, "Mod is located in protected directory, plase remove it manually:\n" + modFullDir.absolutePath());
|
||||
|
||||
localMods.remove(modname);
|
||||
modList->setLocalModList(localMods);
|
||||
modList->modChanged(modname);
|
||||
CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &){ return true; });
|
||||
loadMods();
|
||||
modList->reloadRepositories();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "csettingsview_moc.h"
|
||||
#include "ui_csettingsview_moc.h"
|
||||
|
||||
#include "../updatedialog_moc.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QGuiApplication>
|
||||
|
||||
@ -232,3 +234,9 @@ void CSettingsView::on_comboBoxAutoSave_currentIndexChanged(int index)
|
||||
Settings node = settings.write["general"]["saveFrequency"];
|
||||
node->Integer() = index;
|
||||
}
|
||||
|
||||
void CSettingsView::on_updatesButton_clicked()
|
||||
{
|
||||
UpdateDialog::showUpdateDialog(true);
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,8 @@ private slots:
|
||||
|
||||
void on_comboBoxAutoSave_currentIndexChanged(int index);
|
||||
|
||||
void on_updatesButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::CSettingsView * ui;
|
||||
};
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>738</width>
|
||||
<height>471</height>
|
||||
<width>779</width>
|
||||
<height>619</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -26,40 +26,15 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="3" column="6">
|
||||
<widget class="QLabel" name="labelTempDir">
|
||||
<property name="text">
|
||||
<string>Log files directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="6">
|
||||
<widget class="QComboBox" name="comboBoxAutoCheck">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>On</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6" colspan="4">
|
||||
<widget class="QLabel" name="labelDataDirs">
|
||||
<item row="8" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelAIMovingOnTheMap">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Data Directories</string>
|
||||
<string>AI on the map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -67,7 +42,6 @@
|
||||
<widget class="QLabel" name="labelVideo">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
@ -76,68 +50,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="labelResolution">
|
||||
<property name="text">
|
||||
<string>Resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="9">
|
||||
<widget class="QPushButton" name="openTempDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="4">
|
||||
<widget class="QComboBox" name="comboBoxNeutralAI">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StupidAI</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6">
|
||||
<widget class="QLabel" name="labelUserDataDir">
|
||||
<property name="text">
|
||||
<string>User data directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QLabel" name="labelEnemyAI">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enemy AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelAutoCheck">
|
||||
<property name="text">
|
||||
<string>Check repositories on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="labelDisplayIndex">
|
||||
<property name="text">
|
||||
<string>Display index</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="labelShowIntro">
|
||||
<property name="text">
|
||||
@ -145,91 +57,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="6" colspan="4">
|
||||
<widget class="QLabel" name="labelGeneral">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>General</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="4">
|
||||
<widget class="QComboBox" name="comboBoxDisplayIndex">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="8">
|
||||
<widget class="QPushButton" name="changeGameDataDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="4">
|
||||
<widget class="QComboBox" name="comboBoxPlayerAI">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VCAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Nullkiller</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="7" colspan="2">
|
||||
<widget class="QLineEdit" name="lineEditTempDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>/home/user/.vcmi</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelRepositories">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Repositories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="9">
|
||||
<widget class="QPushButton" name="openGameDataDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="7" colspan="3">
|
||||
<widget class="QSpinBox" name="spinBoxNetworkPort">
|
||||
<property name="minimum">
|
||||
@ -243,86 +70,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1" colspan="9">
|
||||
<widget class="QPlainTextEdit" name="plainTextEditRepos">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string>http://downloads.vcmi.eu/Mods/repository.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="9">
|
||||
<widget class="QPushButton" name="openUserDataDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QLabel" name="labelFriendlyAI">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Friendly AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLabel" name="labelPlayerAI">
|
||||
<property name="text">
|
||||
<string>Player AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="4">
|
||||
<widget class="QComboBox" name="comboBoxEnemyAI">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StupidAI</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="labelFullScreen">
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="4">
|
||||
<spacer name="spacerSections">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>56</width>
|
||||
<height>8</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<widget class="QLineEdit" name="lineEditGameDir">
|
||||
<property name="minimumSize">
|
||||
@ -336,10 +83,31 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="QLabel" name="labelGameDir">
|
||||
<item row="10" column="6">
|
||||
<widget class="QLabel" name="labelEncoding">
|
||||
<property name="text">
|
||||
<string>Extra data directory</string>
|
||||
<string>Heroes III character set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="checkBoxFullScreen">
|
||||
<property name="text">
|
||||
<string>Real fullscreen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="labelResolution">
|
||||
<property name="text">
|
||||
<string>Resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="labelFullScreen">
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -379,16 +147,72 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelAIMovingOnTheMap">
|
||||
<item row="8" column="6" colspan="4">
|
||||
<widget class="QLabel" name="labelGeneral">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AI on the map</string>
|
||||
<string>General</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1" colspan="9">
|
||||
<widget class="QPlainTextEdit" name="plainTextEditRepos">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string>http://downloads.vcmi.eu/Mods/repository.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="9">
|
||||
<widget class="QPushButton" name="openGameDataDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLabel" name="labelPlayerAI">
|
||||
<property name="text">
|
||||
<string>Player AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelRepositories">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Repositories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="4">
|
||||
<widget class="QComboBox" name="comboBoxPlayerAI">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VCAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Nullkiller</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="labelDisplayIndex">
|
||||
<property name="text">
|
||||
<string>Display index</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -409,11 +233,67 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="4">
|
||||
<widget class="QComboBox" name="comboBoxNeutralAI">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StupidAI</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="4">
|
||||
<widget class="QComboBox" name="comboBoxFriendlyAI">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BattleAI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StupidAI</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="5">
|
||||
<spacer name="spacerColumns">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>8</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="9">
|
||||
<widget class="QPushButton" name="openTempDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1" colspan="4">
|
||||
<widget class="QLabel" name="LauncherSettings">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
@ -422,10 +302,33 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="6">
|
||||
<widget class="QLabel" name="labelNetworkPort">
|
||||
<item row="1" column="6">
|
||||
<widget class="QLabel" name="labelGameDir">
|
||||
<property name="text">
|
||||
<string>Network port</string>
|
||||
<string>Extra data directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QLabel" name="labelEnemyAI">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enemy AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="8">
|
||||
<widget class="QPushButton" name="changeGameDataDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -449,7 +352,7 @@
|
||||
<string>1024x768</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1181x664</string>
|
||||
</property>
|
||||
@ -506,8 +409,24 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1" colspan="4">
|
||||
<spacer name="spacerRepos">
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="labelNeutralAI">
|
||||
<property name="text">
|
||||
<string>Neutral AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="4">
|
||||
<widget class="QComboBox" name="comboBoxDisplayIndex">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="4">
|
||||
<spacer name="spacerSections">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
@ -516,14 +435,67 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<width>56</width>
|
||||
<height>8</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="13" column="4">
|
||||
<widget class="QComboBox" name="comboBoxFriendlyAI">
|
||||
<item row="3" column="7" colspan="2">
|
||||
<widget class="QLineEdit" name="lineEditTempDir">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>/home/user/.vcmi</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="6">
|
||||
<widget class="QComboBox" name="comboBoxAutoCheck">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>On</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="7">
|
||||
<widget class="QComboBox" name="comboBoxAutoSave">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>On</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="4">
|
||||
<widget class="QComboBox" name="comboBoxEnemyAI">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -542,6 +514,96 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1" colspan="4">
|
||||
<spacer name="spacerRepos">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>8</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLabel" name="labelAIInTheBattlefield">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AI in the battlefield</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1" colspan="4">
|
||||
<widget class="QLabel" name="labelAutoCheck">
|
||||
<property name="text">
|
||||
<string>Check repositories on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QLabel" name="labelFriendlyAI">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Friendly AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="6">
|
||||
<widget class="QLabel" name="labelTempDir">
|
||||
<property name="text">
|
||||
<string>Log files directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6" colspan="4">
|
||||
<widget class="QLabel" name="labelDataDirs">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Data Directories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6">
|
||||
<widget class="QLabel" name="labelUserDataDir">
|
||||
<property name="text">
|
||||
<string>User data directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="9">
|
||||
<widget class="QPushButton" name="openUserDataDir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="6">
|
||||
<widget class="QLabel" name="labelNetworkPort">
|
||||
<property name="text">
|
||||
<string>Network port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="7" colspan="3">
|
||||
<widget class="QComboBox" name="comboBoxEncoding">
|
||||
<item>
|
||||
@ -576,58 +638,6 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLabel" name="labelAIInTheBattlefield">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AI in the battlefield</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="5">
|
||||
<spacer name="spacerColumns">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>8</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="10" column="6">
|
||||
<widget class="QLabel" name="labelEncoding">
|
||||
<property name="text">
|
||||
<string>Heroes III character set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="labelNeutralAI">
|
||||
<property name="text">
|
||||
<string>Neutral AI</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="checkBoxFullScreen">
|
||||
<property name="text">
|
||||
<string>Real fullscreen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="6">
|
||||
<widget class="QLabel" name="labelAutoSave">
|
||||
<property name="text">
|
||||
@ -635,21 +645,11 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="7">
|
||||
<widget class="QComboBox" name="comboBoxAutoSave">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<item row="17" column="7">
|
||||
<widget class="QPushButton" name="updatesButton">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>On</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
147
launcher/updatedialog_moc.cpp
Normal file
147
launcher/updatedialog_moc.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* updatedialog_moc.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 "updatedialog_moc.h"
|
||||
#include "ui_updatedialog_moc.h"
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
UpdateDialog::UpdateDialog(bool calledManually, QWidget *parent):
|
||||
QDialog(parent),
|
||||
ui(new Ui::UpdateDialog),
|
||||
calledManually(calledManually)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
if(calledManually)
|
||||
{
|
||||
setWindowModality(Qt::ApplicationModal);
|
||||
show();
|
||||
}
|
||||
|
||||
connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close()));
|
||||
|
||||
if(settings["launcher"]["updateOnStartup"].Bool())
|
||||
ui->checkOnStartup->setCheckState(Qt::CheckState::Checked);
|
||||
|
||||
currentVersion = GameConstants::VCMI_VERSION;
|
||||
|
||||
setWindowTitle(QString::fromStdString(currentVersion));
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
platformParameter = "windows";
|
||||
#elif defined(VCMI_MAC)
|
||||
platformParameter = "macos";
|
||||
#elif defined(VCMI_IOS)
|
||||
platformParameter = "ios";
|
||||
#elif defined(VCMI_ANDROID)
|
||||
platformParameter = "android";
|
||||
#elif defined(VCMI_UNIX)
|
||||
platformParameter = "linux";
|
||||
#endif
|
||||
|
||||
QString url = QString::fromStdString(settings["launcher"]["updateConfigUrl"].String());
|
||||
|
||||
QNetworkReply *response = networkManager.get(QNetworkRequest(QUrl(url)));
|
||||
|
||||
connect(response, &QNetworkReply::finished, [&, response]{
|
||||
response->deleteLater();
|
||||
|
||||
if(response->error() != QNetworkReply::NoError)
|
||||
{
|
||||
ui->versionLabel->setStyleSheet("QLabel { background-color : red; color : black; }");
|
||||
ui->versionLabel->setText("Network error");
|
||||
ui->plainTextEdit->setPlainText(response->errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
auto byteArray = response->readAll();
|
||||
JsonNode node(byteArray.constData(), byteArray.size());
|
||||
loadFromJson(node);
|
||||
});
|
||||
}
|
||||
|
||||
UpdateDialog::~UpdateDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void UpdateDialog::showUpdateDialog(bool isManually)
|
||||
{
|
||||
UpdateDialog * dialog = new UpdateDialog(isManually);
|
||||
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
void UpdateDialog::on_checkOnStartup_stateChanged(int state)
|
||||
{
|
||||
Settings node = settings.write["launcher"]["updateOnStartup"];
|
||||
node->Bool() = ui->checkOnStartup->isChecked();
|
||||
}
|
||||
|
||||
void UpdateDialog::loadFromJson(const JsonNode & node)
|
||||
{
|
||||
if(node.getType() != JsonNode::JsonType::DATA_STRUCT ||
|
||||
node["updateType"].getType() != JsonNode::JsonType::DATA_STRING ||
|
||||
node["version"].getType() != JsonNode::JsonType::DATA_STRING ||
|
||||
node["changeLog"].getType() != JsonNode::JsonType::DATA_STRING ||
|
||||
node.getType() != JsonNode::JsonType::DATA_STRUCT) //we need at least one link - other are optional
|
||||
{
|
||||
ui->plainTextEdit->setPlainText("Cannot read JSON from url or incorrect JSON data");
|
||||
return;
|
||||
}
|
||||
|
||||
//check whether update is needed
|
||||
bool isFutureVersion = true;
|
||||
std::string newVersion = node["version"].String();
|
||||
for(auto & prevVersion : node["history"].Vector())
|
||||
{
|
||||
if(prevVersion.String() == currentVersion)
|
||||
isFutureVersion = false;
|
||||
}
|
||||
|
||||
if(isFutureVersion || currentVersion == newVersion)
|
||||
{
|
||||
if(!calledManually)
|
||||
close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!calledManually)
|
||||
{
|
||||
setWindowModality(Qt::ApplicationModal);
|
||||
show();
|
||||
}
|
||||
|
||||
const auto updateType = node["updateType"].String();
|
||||
|
||||
QString bgColor;
|
||||
if(updateType == "minor")
|
||||
bgColor = "gray";
|
||||
else if(updateType == "major")
|
||||
bgColor = "orange";
|
||||
else if(updateType == "critical")
|
||||
bgColor = "red";
|
||||
|
||||
ui->versionLabel->setStyleSheet(QString("QLabel { background-color : %1; color : black; }").arg(bgColor));
|
||||
ui->versionLabel->setText(QString::fromStdString(newVersion));
|
||||
ui->plainTextEdit->setPlainText(QString::fromStdString(node["changeLog"].String()));
|
||||
|
||||
QString downloadLink = QString::fromStdString(node["downloadLinks"]["other"].String());
|
||||
if(node["downloadLinks"][platformParameter].getType() == JsonNode::JsonType::DATA_STRING)
|
||||
downloadLink = QString::fromStdString(node["downloadLinks"][platformParameter].String());
|
||||
|
||||
ui->downloadLink->setText(QString{"<a href=\"%1\">Download page</a>"}.arg(downloadLink));
|
||||
}
|
44
launcher/updatedialog_moc.h
Normal file
44
launcher/updatedialog_moc.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* updatedialog_moc.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <QDialog>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
class JsonNode;
|
||||
|
||||
namespace Ui {
|
||||
class UpdateDialog;
|
||||
}
|
||||
|
||||
class UpdateDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UpdateDialog(bool calledManually, QWidget *parent = nullptr);
|
||||
~UpdateDialog();
|
||||
|
||||
static void showUpdateDialog(bool isManually);
|
||||
|
||||
private slots:
|
||||
void on_checkOnStartup_stateChanged(int state);
|
||||
|
||||
private:
|
||||
Ui::UpdateDialog *ui;
|
||||
|
||||
std::string currentVersion;
|
||||
std::string platformParameter = "other";
|
||||
|
||||
QNetworkAccessManager networkManager;
|
||||
|
||||
bool calledManually;
|
||||
|
||||
void loadFromJson(const JsonNode & node);
|
||||
};
|
128
launcher/updatedialog_moc.ui
Normal file
128
launcher/updatedialog_moc.ui
Normal file
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UpdateDialog</class>
|
||||
<widget class="QDialog" name="UpdateDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>371</width>
|
||||
<height>247</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>371</width>
|
||||
<height>247</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="5">
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>18</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel {background-color: green; color : black} </string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>You have latest version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="5">
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="checkOnStartup">
|
||||
<property name="text">
|
||||
<string>Check updates on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="downloadLink">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -105,4 +105,4 @@ void BattleFieldInfo::registerIcons(const IconRegistar & cb) const
|
||||
BattleField BattleFieldInfo::getId() const
|
||||
{
|
||||
return battlefield;
|
||||
}
|
||||
}
|
@ -611,9 +611,6 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
|
||||
if(!node["shots"].isNull())
|
||||
cre->addBonus(node["shots"].Integer(), Bonus::SHOTS);
|
||||
|
||||
if(!node["spellPoints"].isNull())
|
||||
cre->addBonus(node["spellPoints"].Integer(), Bonus::CASTS);
|
||||
|
||||
loadStackExperience(cre, node["stackExperience"]);
|
||||
loadJsonAnimation(cre, node["graphics"]);
|
||||
loadCreatureJson(cre, node);
|
||||
|
@ -438,7 +438,7 @@ assign_source_group(${lib_SRCS} ${lib_HEADERS})
|
||||
add_library(vcmi SHARED ${lib_SRCS} ${lib_HEADERS})
|
||||
set_target_properties(vcmi PROPERTIES COMPILE_DEFINITIONS "VCMI_DLL=1")
|
||||
target_link_libraries(vcmi PUBLIC
|
||||
minizip::minizip SDL2::SDL2 ZLIB::ZLIB
|
||||
minizip::minizip ZLIB::ZLIB
|
||||
${SYSTEM_LIBS} Boost::boost Boost::thread Boost::filesystem Boost::program_options Boost::locale Boost::date_time
|
||||
)
|
||||
|
||||
@ -446,6 +446,7 @@ target_include_directories(vcmi
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PUBLIC ${CMAKE_HOME_DIRECTORY}
|
||||
PUBLIC ${CMAKE_HOME_DIRECTORY}/include
|
||||
PRIVATE ${SDL2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
@ -620,10 +620,11 @@ void LayerTransitionRule::process(
|
||||
destination.blocked = true;
|
||||
}
|
||||
}
|
||||
else if(source.node->accessible != CGPathNode::ACCESSIBLE && destination.node->accessible != CGPathNode::ACCESSIBLE)
|
||||
else if(destination.node->accessible != CGPathNode::ACCESSIBLE)
|
||||
{
|
||||
/// Hero that fly can only land on accessible tiles
|
||||
destination.blocked = true;
|
||||
if(!destination.isGuardianTile && destination.nodeObject)
|
||||
destination.blocked = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -52,9 +52,9 @@ const TeamID TeamID::NO_TEAM = TeamID(255);
|
||||
namespace GameConstants
|
||||
{
|
||||
#ifdef VCMI_NO_EXTRA_VERSION
|
||||
const std::string VCMI_VERSION = std::string("VCMI 0.99");
|
||||
const std::string VCMI_VERSION = std::string("VCMI 1.0.0");
|
||||
#else
|
||||
const std::string VCMI_VERSION = std::string("VCMI 0.99 ") + GIT_SHA1;
|
||||
const std::string VCMI_VERSION = std::string("VCMI 1.0.0.") + GIT_SHA1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -948,7 +948,7 @@ void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
|
||||
getAllParents(lparents);
|
||||
|
||||
for(auto parent : lparents)
|
||||
parent->bonuses.getAllBonuses(beforeUpdate);
|
||||
parent->getAllBonusesRec(beforeUpdate);
|
||||
|
||||
bonuses.getAllBonuses(beforeUpdate);
|
||||
|
||||
@ -957,7 +957,10 @@ void CBonusSystemNode::getAllBonusesRec(BonusList &out) const
|
||||
auto updated = b->updater
|
||||
? getUpdatedBonus(b, b->updater)
|
||||
: b;
|
||||
out.push_back(updated);
|
||||
|
||||
//do not add bonus with same pointer
|
||||
if(!vstd::contains(out, updated))
|
||||
out.push_back(updated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ std::string StartInfo::getCampaignName() const
|
||||
void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
|
||||
{
|
||||
if(!mi)
|
||||
throw ExceptionMapMissing();
|
||||
throw std::domain_error("ExceptionMapMissing");
|
||||
|
||||
//there must be at least one human player before game can be started
|
||||
std::map<PlayerColor, PlayerSettings>::const_iterator i;
|
||||
@ -73,12 +73,12 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
|
||||
break;
|
||||
|
||||
if(i == si->playerInfos.cend() && !ignoreNoHuman)
|
||||
throw ExceptionNoHuman();
|
||||
throw std::domain_error("ExceptionNoHuman");
|
||||
|
||||
if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME)
|
||||
{
|
||||
if(!si->mapGenOptions->checkOptions())
|
||||
throw ExceptionNoTemplate();
|
||||
throw std::domain_error("ExceptionNoTemplate");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,3 @@ struct DLL_LINKAGE LobbyInfo : public LobbyState
|
||||
TeamID getPlayerTeamId(PlayerColor color);
|
||||
};
|
||||
|
||||
class ExceptionMapMissing : public std::exception {};
|
||||
class ExceptionNoHuman : public std::exception {};
|
||||
class ExceptionNoTemplate : public std::exception {};
|
||||
|
@ -154,6 +154,11 @@ Terrain::Manager::Manager()
|
||||
}
|
||||
|
||||
terrainInfo[terr.first] = info;
|
||||
if(!terrainId.count(terr.first))
|
||||
{
|
||||
terrainId[terr.first] = terrainVault.size();
|
||||
terrainVault.push_back(terr.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,12 +169,18 @@ Terrain::Manager & Terrain::Manager::get()
|
||||
return manager;
|
||||
}
|
||||
|
||||
std::vector<Terrain> Terrain::Manager::terrains()
|
||||
const std::vector<Terrain> & Terrain::Manager::terrains()
|
||||
{
|
||||
std::set<Terrain> _terrains; //have to use std::set to have ordered container. Othervise de-sync is possible
|
||||
for(const auto & info : Terrain::Manager::get().terrainInfo)
|
||||
_terrains.insert(info.first);
|
||||
return std::vector<Terrain>(_terrains.begin(), _terrains.end());
|
||||
return Terrain::Manager::get().terrainVault;
|
||||
}
|
||||
|
||||
int Terrain::Manager::id(const Terrain & terrain)
|
||||
{
|
||||
if(terrain.name == "ANY") return -3;
|
||||
if(terrain.name == "WRONG") return -2;
|
||||
if(terrain.name == "BORDER") return -1;
|
||||
|
||||
return Terrain::Manager::get().terrainId.at(terrain);
|
||||
}
|
||||
|
||||
const Terrain::Info & Terrain::Manager::getInfo(const Terrain & terrain)
|
||||
@ -219,13 +230,7 @@ bool operator<(const Terrain & l, const Terrain & r)
|
||||
|
||||
int Terrain::id() const
|
||||
{
|
||||
if(name == "ANY") return -3;
|
||||
if(name == "WRONG") return -2;
|
||||
if(name == "BORDER") return -1;
|
||||
|
||||
auto _terrains = Terrain::Manager::terrains();
|
||||
auto iter = std::find(_terrains.begin(), _terrains.end(), *this);
|
||||
return iter - _terrains.begin();
|
||||
return Terrain::Manager::id(*this);
|
||||
}
|
||||
|
||||
bool Terrain::isLand() const
|
||||
|
@ -48,14 +48,17 @@ public:
|
||||
class DLL_LINKAGE Manager
|
||||
{
|
||||
public:
|
||||
static std::vector<Terrain> terrains();
|
||||
static const std::vector<Terrain> & terrains();
|
||||
static const Info & getInfo(const Terrain &);
|
||||
static int id(const Terrain &);
|
||||
|
||||
private:
|
||||
static Manager & get();
|
||||
Manager();
|
||||
|
||||
std::unordered_map<std::string, Info> terrainInfo;
|
||||
std::vector<Terrain> terrainVault;
|
||||
std::map<Terrain, int> terrainId;
|
||||
};
|
||||
|
||||
/*enum EETerrainType
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CBinaryReader.h"
|
||||
|
||||
//FIXME:library file depends on SDL - make cause troubles
|
||||
#include <SDL_endian.h>
|
||||
#include "CInputStream.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
|
@ -16,9 +16,10 @@
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDirectory, size_t depth, bool initial):
|
||||
baseDirectory(std::move(baseDirectory)),
|
||||
mountPoint(std::move(_mountPoint)),
|
||||
fileList(listFiles(mountPoint, depth, initial))
|
||||
baseDirectory(std::move(baseDirectory)),
|
||||
mountPoint(std::move(_mountPoint)),
|
||||
fileList(listFiles(mountPoint, depth, initial)),
|
||||
recursiveDepth(depth)
|
||||
{
|
||||
logGlobal->trace("File system loaded, %d files found", fileList.size());
|
||||
}
|
||||
@ -52,7 +53,7 @@ void CFilesystemLoader::updateFilteredFiles(std::function<bool(const std::string
|
||||
{
|
||||
if (filter(mountPoint))
|
||||
{
|
||||
fileList = listFiles(mountPoint, 1, false);
|
||||
fileList = listFiles(mountPoint, recursiveDepth, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ private:
|
||||
boost::filesystem::path baseDirectory;
|
||||
|
||||
std::string mountPoint;
|
||||
|
||||
size_t recursiveDepth;
|
||||
|
||||
/** A list of files in the directory
|
||||
* key = ResourceID for resource loader
|
||||
|
@ -450,9 +450,8 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
int txt_id;
|
||||
|
||||
if (cb->getHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//GameConstants::MAX_HEROES_PER_PLAYER) //free hero slot
|
||||
if (cb->getHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//free hero slot
|
||||
{
|
||||
cb->changeObjPos(id,pos+int3(1,0,0),0);
|
||||
//update hero parameters
|
||||
SetMovePoints smp;
|
||||
smp.hid = id;
|
||||
@ -534,7 +533,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(cb->gameState()->getTile(visitablePos())->terType, this);
|
||||
if (customApp)
|
||||
appearance = customApp.get();
|
||||
appearance = customApp;
|
||||
}
|
||||
|
||||
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||
@ -1413,7 +1412,9 @@ void CGHeroInstance::setHeroTypeName(const std::string & identifier)
|
||||
if(rawId)
|
||||
subID = rawId.get();
|
||||
else
|
||||
subID = 0; //fallback to Orrin, throw error instead?
|
||||
{
|
||||
throw std::runtime_error("Couldn't resolve hero identifier " + identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1133,7 +1133,7 @@ void CGTownInstance::updateAppearance()
|
||||
//FIXME: not the best way to do this
|
||||
auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this);
|
||||
if (app)
|
||||
appearance = app.get();
|
||||
appearance = app;
|
||||
}
|
||||
|
||||
std::string CGTownInstance::nodeName() const
|
||||
|
@ -117,11 +117,11 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
|
||||
|
||||
for (size_t i=0; i<totalNumber; i++)
|
||||
{
|
||||
ObjectTemplate templ;
|
||||
templ.readTxt(parser);
|
||||
auto templ = new ObjectTemplate;
|
||||
templ->readTxt(parser);
|
||||
parser.endLine();
|
||||
std::pair<si32, si32> key(templ.id.num, templ.subid);
|
||||
legacyTemplates.insert(std::make_pair(key, templ));
|
||||
std::pair<si32, si32> key(templ->id.num, templ->subid);
|
||||
legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(templ)));
|
||||
}
|
||||
|
||||
std::vector<JsonNode> ret(dataSize);// create storage for 256 objects
|
||||
@ -448,7 +448,6 @@ AObjectTypeHandler::AObjectTypeHandler():
|
||||
|
||||
AObjectTypeHandler::~AObjectTypeHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::setType(si32 type, si32 subtype)
|
||||
@ -488,12 +487,12 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
|
||||
entry.second.setType(JsonNode::JsonType::DATA_STRUCT);
|
||||
JsonUtils::inherit(entry.second, base);
|
||||
|
||||
ObjectTemplate tmpl;
|
||||
tmpl.id = Obj(type);
|
||||
tmpl.subid = subtype;
|
||||
tmpl.stringID = entry.first; // FIXME: create "fullID" - type.object.template?
|
||||
tmpl.readJson(entry.second);
|
||||
templates.push_back(tmpl);
|
||||
auto tmpl = new ObjectTemplate;
|
||||
tmpl->id = Obj(type);
|
||||
tmpl->subid = subtype;
|
||||
tmpl->stringID = entry.first; // FIXME: create "fullID" - type.object.template?
|
||||
tmpl->readJson(entry.second);
|
||||
templates.push_back(std::shared_ptr<const ObjectTemplate>(tmpl));
|
||||
}
|
||||
|
||||
if (input["name"].isNull())
|
||||
@ -523,7 +522,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
|
||||
initTypeData(input);
|
||||
}
|
||||
|
||||
bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const
|
||||
bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const
|
||||
{
|
||||
return false; // by default there are no overrides
|
||||
}
|
||||
@ -551,26 +550,28 @@ SObjectSounds AObjectTypeHandler::getSounds() const
|
||||
return sounds;
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::addTemplate(const ObjectTemplate & templ)
|
||||
void AObjectTypeHandler::addTemplate(std::shared_ptr<const ObjectTemplate> templ)
|
||||
{
|
||||
//Otherwise the template remains constant
|
||||
auto ptr = const_cast<ObjectTemplate*>(templ.get());
|
||||
ptr->id = Obj(type);
|
||||
ptr->subid = subtype;
|
||||
templates.push_back(templ);
|
||||
templates.back().id = Obj(type);
|
||||
templates.back().subid = subtype;
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::addTemplate(JsonNode config)
|
||||
{
|
||||
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null
|
||||
JsonUtils::inherit(config, base);
|
||||
ObjectTemplate tmpl;
|
||||
tmpl.id = Obj(type);
|
||||
tmpl.subid = subtype;
|
||||
tmpl.stringID = ""; // TODO?
|
||||
tmpl.readJson(config);
|
||||
templates.push_back(tmpl);
|
||||
auto tmpl = new ObjectTemplate;
|
||||
tmpl->id = Obj(type);
|
||||
tmpl->subid = subtype;
|
||||
tmpl->stringID = ""; // TODO?
|
||||
tmpl->readJson(config);
|
||||
templates.emplace_back(tmpl);
|
||||
}
|
||||
|
||||
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates() const
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> AObjectTypeHandler::getTemplates() const
|
||||
{
|
||||
return templates;
|
||||
}
|
||||
@ -580,14 +581,14 @@ BattleField AObjectTypeHandler::getBattlefield() const
|
||||
return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE;
|
||||
}
|
||||
|
||||
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>>AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
|
||||
{
|
||||
std::vector<ObjectTemplate> templates = getTemplates();
|
||||
std::vector<ObjectTemplate> filtered;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> templates = getTemplates();
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> filtered;
|
||||
|
||||
std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj)
|
||||
std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](std::shared_ptr<const ObjectTemplate> obj)
|
||||
{
|
||||
return obj.canBePlacedAt(terrainType);
|
||||
return obj->canBePlacedAt(terrainType);
|
||||
});
|
||||
// H3 defines allowed terrains in a weird way - artifacts, monsters and resources have faulty masks here
|
||||
// Perhaps we should re-define faulty templates and remove this workaround (already done for resources)
|
||||
@ -597,15 +598,15 @@ std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & ter
|
||||
return filtered;
|
||||
}
|
||||
|
||||
boost::optional<ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
|
||||
std::shared_ptr<const ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
|
||||
{
|
||||
std::vector<ObjectTemplate> ret = getTemplates(terrainType);
|
||||
for (auto & tmpl : ret)
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> ret = getTemplates(terrainType);
|
||||
for (const auto & tmpl: ret)
|
||||
{
|
||||
if (objectFilter(object, tmpl))
|
||||
return tmpl;
|
||||
}
|
||||
return boost::optional<ObjectTemplate>();
|
||||
return std::shared_ptr<const ObjectTemplate>(); //empty
|
||||
}
|
||||
|
||||
const RandomMapInfo & AObjectTypeHandler::getRMGInfo()
|
||||
|
@ -144,7 +144,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
|
||||
|
||||
JsonNode base; /// describes base template
|
||||
|
||||
std::vector<ObjectTemplate> templates;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
|
||||
|
||||
SObjectSounds sounds;
|
||||
|
||||
@ -154,7 +154,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
|
||||
|
||||
protected:
|
||||
void preInitObject(CGObjectInstance * obj) const;
|
||||
virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
|
||||
virtual bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const;
|
||||
|
||||
/// initialization for classes that inherit this one
|
||||
virtual void initTypeData(const JsonNode & input);
|
||||
@ -177,17 +177,21 @@ public:
|
||||
boost::optional<std::string> getCustomName() const;
|
||||
SObjectSounds getSounds() const;
|
||||
|
||||
void addTemplate(const ObjectTemplate & templ);
|
||||
void addTemplate(std::shared_ptr<const ObjectTemplate> templ);
|
||||
void addTemplate(JsonNode config);
|
||||
|
||||
/// returns all templates matching parameters
|
||||
std::vector<ObjectTemplate> getTemplates() const;
|
||||
std::vector<ObjectTemplate> getTemplates(const Terrain & terrainType) const;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates() const;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates(const Terrain & terrainType) const;
|
||||
|
||||
/// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle)
|
||||
/// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server)
|
||||
std::shared_ptr<const ObjectTemplate> getOverride(const Terrain & terrainType, const CGObjectInstance * object) const;
|
||||
|
||||
BattleField getBattlefield() const;
|
||||
|
||||
/// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle)
|
||||
/// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server)
|
||||
boost::optional<ObjectTemplate> getOverride(const Terrain & terrainType, const CGObjectInstance * object) const;
|
||||
|
||||
const RandomMapInfo & getRMGInfo();
|
||||
|
||||
@ -199,14 +203,14 @@ public:
|
||||
|
||||
/// Creates object and set up core properties (like ID/subID). Object is NOT initialized
|
||||
/// to allow creating objects before game start (e.g. map loading)
|
||||
virtual CGObjectInstance * create(const ObjectTemplate & tmpl) const = 0;
|
||||
virtual CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const = 0;
|
||||
|
||||
/// Configures object properties. Should be re-entrable, resetting state of the object if necessarily
|
||||
/// This should set remaining properties, including randomized or depending on map
|
||||
virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0;
|
||||
|
||||
/// Returns object configuration, if available. Otherwise returns NULL
|
||||
virtual std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const = 0;
|
||||
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const = 0;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -264,7 +268,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
|
||||
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
|
||||
|
||||
/// container with H3 templates, used only during loading, no need to serialize it
|
||||
typedef std::multimap<std::pair<si32, si32>, ObjectTemplate> TTemplatesContainer;
|
||||
typedef std::multimap<std::pair<si32, si32>, std::shared_ptr<const ObjectTemplate>> TTemplatesContainer;
|
||||
TTemplatesContainer legacyTemplates;
|
||||
|
||||
/// contains list of custom names for H3 objects (e.g. Dwellings), used to load H3 data
|
||||
|
@ -152,24 +152,24 @@ void CGObjectInstance::setOwner(PlayerColor ow)
|
||||
}
|
||||
int CGObjectInstance::getWidth() const//returns width of object graphic in tiles
|
||||
{
|
||||
return appearance.getWidth();
|
||||
return appearance->getWidth();
|
||||
}
|
||||
int CGObjectInstance::getHeight() const //returns height of object graphic in tiles
|
||||
{
|
||||
return appearance.getHeight();
|
||||
return appearance->getHeight();
|
||||
}
|
||||
bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
|
||||
{
|
||||
return appearance.isVisitableAt(pos.x - x, pos.y - y);
|
||||
return appearance->isVisitableAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
bool CGObjectInstance::blockingAt(int x, int y) const
|
||||
{
|
||||
return appearance.isBlockedAt(pos.x - x, pos.y - y);
|
||||
return appearance->isBlockedAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
|
||||
bool CGObjectInstance::coveringAt(int x, int y) const
|
||||
{
|
||||
return appearance.isVisibleAt(pos.x - x, pos.y - y);
|
||||
return appearance->isVisibleAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
|
||||
std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
@ -179,7 +179,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
{
|
||||
for(int h=0; h<getHeight(); ++h)
|
||||
{
|
||||
if(appearance.isBlockedAt(w, h))
|
||||
if(appearance->isBlockedAt(w, h))
|
||||
ret.insert(int3(pos.x - w, pos.y - h, pos.z));
|
||||
}
|
||||
}
|
||||
@ -188,7 +188,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
|
||||
std::set<int3> CGObjectInstance::getBlockedOffsets() const
|
||||
{
|
||||
return appearance.getBlockedOffsets();
|
||||
return appearance->getBlockedOffsets();
|
||||
}
|
||||
|
||||
void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
@ -210,6 +210,11 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
appearance = handler->getTemplates(tile.terType)[0];
|
||||
else
|
||||
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
|
||||
if (ID == Obj::HERO)
|
||||
{
|
||||
//adjust for the prison offset
|
||||
pos = visitablePos();
|
||||
}
|
||||
cb->gameState()->map->addBlockVisTiles(this);
|
||||
}
|
||||
|
||||
@ -259,7 +264,7 @@ int CGObjectInstance::getSightRadius() const
|
||||
|
||||
int3 CGObjectInstance::getVisitableOffset() const
|
||||
{
|
||||
return appearance.getVisitableOffset();
|
||||
return appearance->getVisitableOffset();
|
||||
}
|
||||
|
||||
void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const
|
||||
@ -345,7 +350,7 @@ int3 CGObjectInstance::visitablePos() const
|
||||
|
||||
bool CGObjectInstance::isVisitable() const
|
||||
{
|
||||
return appearance.isVisitable();
|
||||
return appearance->isVisitable();
|
||||
}
|
||||
|
||||
bool CGObjectInstance::passableFor(PlayerColor color) const
|
||||
@ -370,7 +375,7 @@ void CGObjectInstance::serializeJson(JsonSerializeFormat & handler)
|
||||
handler.serializeInt("y", pos.y);
|
||||
handler.serializeInt("l", pos.z);
|
||||
JsonNode app;
|
||||
appearance.writeJson(app, false);
|
||||
appearance->writeJson(app, false);
|
||||
handler.serializeRaw("template",app, boost::none);
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ public:
|
||||
/// Index of object in map's list of objects
|
||||
ObjectInstanceID id;
|
||||
/// Defines appearance of object on map (animation, blocked tiles, blit order, etc)
|
||||
ObjectTemplate appearance;
|
||||
std::shared_ptr<const ObjectTemplate> appearance;
|
||||
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
|
||||
bool blockVisit;
|
||||
|
||||
|
@ -183,7 +183,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
|
||||
objectInfo.init(config);
|
||||
}
|
||||
|
||||
CGObjectInstance * CRewardableConstructor::create(const ObjectTemplate & tmpl) const
|
||||
CGObjectInstance * CRewardableConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
auto ret = new CRewardableObject();
|
||||
preInitObject(ret);
|
||||
@ -196,7 +196,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG
|
||||
objectInfo.configureObject(dynamic_cast<CRewardableObject*>(object), rng);
|
||||
}
|
||||
|
||||
std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
|
||||
std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
return std::unique_ptr<IObjectInfo>(new CRandomRewardObjectInfo(objectInfo));
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
|
||||
public:
|
||||
CRewardableConstructor();
|
||||
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||
|
||||
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
|
||||
|
||||
std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
|
||||
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||
};
|
||||
|
@ -1122,7 +1122,7 @@ std::vector<int3> CGMagicSpring::getVisitableOffsets() const
|
||||
|
||||
for(int y = 0; y < 6; y++)
|
||||
for (int x = 0; x < 8; x++) //starting from left
|
||||
if (appearance.isVisitableAt(x, y))
|
||||
if (appearance->isVisitableAt(x, y))
|
||||
visitableTiles.push_back (int3(x, y , 0));
|
||||
|
||||
return visitableTiles;
|
||||
|
@ -56,7 +56,7 @@ void CTownInstanceConstructor::afterLoadFinalization()
|
||||
}
|
||||
}
|
||||
|
||||
bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const
|
||||
bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr<const ObjectTemplate> templ) const
|
||||
{
|
||||
auto town = dynamic_cast<const CGTownInstance *>(object);
|
||||
|
||||
@ -65,10 +65,10 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, con
|
||||
return town->hasBuilt(id);
|
||||
};
|
||||
|
||||
return filters.count(templ.stringID) != 0 && filters.at(templ.stringID).test(buildTest);
|
||||
return filters.count(templ->stringID) != 0 && filters.at(templ->stringID).test(buildTest);
|
||||
}
|
||||
|
||||
CGObjectInstance * CTownInstanceConstructor::create(const ObjectTemplate & tmpl) const
|
||||
CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
CGTownInstance * obj = createTyped(tmpl);
|
||||
obj->town = faction->town;
|
||||
@ -80,7 +80,7 @@ void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRando
|
||||
{
|
||||
auto templ = getOverride(object->cb->getTile(object->pos)->terType, object);
|
||||
if(templ)
|
||||
object->appearance = templ.get();
|
||||
object->appearance = templ;
|
||||
}
|
||||
|
||||
CHeroInstanceConstructor::CHeroInstanceConstructor()
|
||||
@ -110,7 +110,7 @@ void CHeroInstanceConstructor::afterLoadFinalization()
|
||||
}
|
||||
}
|
||||
|
||||
bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const
|
||||
bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr<const ObjectTemplate> templ) const
|
||||
{
|
||||
auto hero = dynamic_cast<const CGHeroInstance *>(object);
|
||||
|
||||
@ -119,14 +119,14 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, con
|
||||
return hero->type->ID == id;
|
||||
};
|
||||
|
||||
if(filters.count(templ.stringID))
|
||||
if(filters.count(templ->stringID))
|
||||
{
|
||||
return filters.at(templ.stringID).test(heroTest);
|
||||
return filters.at(templ->stringID).test(heroTest);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CGObjectInstance * CHeroInstanceConstructor::create(const ObjectTemplate & tmpl) const
|
||||
CGObjectInstance * CHeroInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
CGHeroInstance * obj = createTyped(tmpl);
|
||||
obj->type = nullptr; //FIXME: set to valid value. somehow.
|
||||
@ -167,12 +167,12 @@ void CDwellingInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
guards = input["guards"];
|
||||
}
|
||||
|
||||
bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const
|
||||
bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CGObjectInstance * CDwellingInstanceConstructor::create(const ObjectTemplate & tmpl) const
|
||||
CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
CGDwelling * obj = createTyped(tmpl);
|
||||
|
||||
@ -272,7 +272,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
|
||||
}
|
||||
|
||||
CGObjectInstance *CBankInstanceConstructor::create(const ObjectTemplate & tmpl) const
|
||||
CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
return createTyped(tmpl);
|
||||
}
|
||||
@ -494,7 +494,7 @@ bool CBankInfo::givesSpells() const
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
|
||||
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
|
||||
{
|
||||
return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
|
||||
}
|
||||
|
@ -26,17 +26,31 @@ template<class ObjectType>
|
||||
class CDefaultObjectTypeHandler : public AObjectTypeHandler
|
||||
{
|
||||
protected:
|
||||
ObjectType * createTyped(const ObjectTemplate & tmpl) const
|
||||
ObjectType * createTyped(std::shared_ptr<const ObjectTemplate> tmpl /* = nullptr */) const
|
||||
{
|
||||
auto obj = new ObjectType();
|
||||
preInitObject(obj);
|
||||
obj->appearance = tmpl;
|
||||
|
||||
if (tmpl)
|
||||
{
|
||||
obj->appearance = tmpl;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto templates = getTemplates();
|
||||
if (templates.empty())
|
||||
{
|
||||
throw std::runtime_error("No handler for created object");
|
||||
}
|
||||
obj->appearance = templates.front(); //just any template for now, will be initialized later
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
public:
|
||||
CDefaultObjectTypeHandler() {}
|
||||
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override
|
||||
{
|
||||
return createTyped(tmpl);
|
||||
}
|
||||
@ -45,7 +59,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override
|
||||
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@ -62,7 +76,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance
|
||||
{
|
||||
JsonNode filtersJson;
|
||||
protected:
|
||||
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
|
||||
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
|
||||
void initTypeData(const JsonNode & input) override;
|
||||
|
||||
public:
|
||||
@ -70,7 +84,7 @@ public:
|
||||
std::map<std::string, LogicalExpression<BuildingID>> filters;
|
||||
|
||||
CTownInstanceConstructor();
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
|
||||
void afterLoadFinalization() override;
|
||||
|
||||
@ -87,7 +101,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
|
||||
{
|
||||
JsonNode filtersJson;
|
||||
protected:
|
||||
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
|
||||
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
|
||||
void initTypeData(const JsonNode & input) override;
|
||||
|
||||
public:
|
||||
@ -95,7 +109,7 @@ public:
|
||||
std::map<std::string, LogicalExpression<HeroTypeID>> filters;
|
||||
|
||||
CHeroInstanceConstructor();
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
|
||||
void afterLoadFinalization() override;
|
||||
|
||||
@ -115,13 +129,13 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling
|
||||
JsonNode guards;
|
||||
|
||||
protected:
|
||||
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
|
||||
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||
void initTypeData(const JsonNode & input) override;
|
||||
|
||||
public:
|
||||
|
||||
CDwellingInstanceConstructor();
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
|
||||
|
||||
bool producesCreature(const CCreature * crea) const;
|
||||
@ -207,10 +221,10 @@ public:
|
||||
|
||||
CBankInstanceConstructor();
|
||||
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
|
||||
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
|
||||
|
||||
std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
|
||||
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -53,7 +53,9 @@ ObjectTemplate::ObjectTemplate():
|
||||
id(Obj::NO_OBJ),
|
||||
subid(0),
|
||||
printPriority(0),
|
||||
stringID("")
|
||||
width(0),
|
||||
height(0),
|
||||
visitable(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -65,7 +67,13 @@ ObjectTemplate::ObjectTemplate(const ObjectTemplate& other):
|
||||
printPriority(other.printPriority),
|
||||
animationFile(other.animationFile),
|
||||
editorAnimationFile(other.editorAnimationFile),
|
||||
stringID(other.stringID)
|
||||
stringID(other.stringID),
|
||||
width(other.width),
|
||||
height(other.height),
|
||||
visitable(other.visitable),
|
||||
blockedOffsets(other.blockedOffsets),
|
||||
blockMapOffset(other.blockMapOffset),
|
||||
visitableOffset(other.visitableOffset)
|
||||
{
|
||||
//default copy constructor is failing with usedTiles this for unknown reason
|
||||
|
||||
@ -84,11 +92,18 @@ ObjectTemplate & ObjectTemplate::operator=(const ObjectTemplate & rhs)
|
||||
animationFile = rhs.animationFile;
|
||||
editorAnimationFile = rhs.editorAnimationFile;
|
||||
stringID = rhs.stringID;
|
||||
width = rhs.width;
|
||||
height = rhs.height;
|
||||
visitable = rhs.visitable;
|
||||
blockedOffsets = rhs.blockedOffsets;
|
||||
blockMapOffset = rhs.blockMapOffset;
|
||||
visitableOffset = rhs.visitableOffset;
|
||||
|
||||
usedTiles.clear();
|
||||
usedTiles.resize(rhs.usedTiles.size());
|
||||
for(size_t i = 0; i < usedTiles.size(); i++)
|
||||
std::copy(rhs.usedTiles[i].begin(), rhs.usedTiles[i].end(), std::back_inserter(usedTiles[i]));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -121,9 +136,9 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
||||
assert(visitStr.size() == 6*8);
|
||||
|
||||
setSize(8, 6);
|
||||
for (size_t i=0; i<6; i++) // 6 rows
|
||||
for(size_t i = 0; i < 6; i++) // 6 rows
|
||||
{
|
||||
for (size_t j=0; j<8; j++) // 8 columns
|
||||
for(size_t j = 0; j < 8; j++) // 8 columns
|
||||
{
|
||||
auto & tile = usedTiles[i][j];
|
||||
tile |= VISIBLE; // assume that all tiles are visible
|
||||
@ -141,7 +156,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
||||
std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain
|
||||
|
||||
assert(terrStr.size() == 9); // all terrains but rock
|
||||
for (size_t i=0; i<9; i++)
|
||||
for(size_t i = 0; i < 9; i++)
|
||||
{
|
||||
if (terrStr[8-i] == '1')
|
||||
allowedTerrains.insert(Terrain::createTerrainTypeH3M(i));
|
||||
@ -168,6 +183,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
||||
visitDir = (8|16|32|64|128);
|
||||
|
||||
readMsk();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void ObjectTemplate::readMsk()
|
||||
@ -197,9 +213,9 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
|
||||
for(auto & byte : visitMask)
|
||||
byte = reader.readUInt8();
|
||||
|
||||
for (size_t i=0; i<6; i++) // 6 rows
|
||||
for(size_t i = 0; i < 6; i++) // 6 rows
|
||||
{
|
||||
for (size_t j=0; j<8; j++) // 8 columns
|
||||
for(size_t j = 0; j < 8; j++) // 8 columns
|
||||
{
|
||||
auto & tile = usedTiles[5 - i][7 - j];
|
||||
tile |= VISIBLE; // assume that all tiles are visible
|
||||
@ -213,7 +229,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
|
||||
|
||||
reader.readUInt16();
|
||||
ui16 terrMask = reader.readUInt16();
|
||||
for (size_t i=0; i<9; i++)
|
||||
for(size_t i = 0; i < 9; i++)
|
||||
{
|
||||
if (((terrMask >> i) & 1 ) != 0)
|
||||
allowedTerrains.insert(Terrain::createTerrainTypeH3M(i));
|
||||
@ -243,6 +259,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
|
||||
readMsk();
|
||||
|
||||
afterLoadFixup();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
|
||||
@ -288,16 +305,16 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case ' ' : return 0;
|
||||
case '0' : return 0;
|
||||
case 'V' : return VISIBLE;
|
||||
case 'B' : return VISIBLE | BLOCKED;
|
||||
case 'H' : return BLOCKED;
|
||||
case 'A' : return VISIBLE | BLOCKED | VISITABLE;
|
||||
case 'T' : return BLOCKED | VISITABLE;
|
||||
default:
|
||||
logGlobal->error("Unrecognized char %s in template mask", ch);
|
||||
return 0;
|
||||
case ' ' : return 0;
|
||||
case '0' : return 0;
|
||||
case 'V' : return VISIBLE;
|
||||
case 'B' : return VISIBLE | BLOCKED;
|
||||
case 'H' : return BLOCKED;
|
||||
case 'A' : return VISIBLE | BLOCKED | VISITABLE;
|
||||
case 'T' : return BLOCKED | VISITABLE;
|
||||
default:
|
||||
logGlobal->error("Unrecognized char %s in template mask", ch);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -320,6 +337,7 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
|
||||
printPriority = static_cast<si32>(node["zIndex"].Float());
|
||||
|
||||
afterLoadFixup();
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
|
||||
@ -413,38 +431,42 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
|
||||
node["zIndex"].Float() = printPriority;
|
||||
}
|
||||
|
||||
ui32 ObjectTemplate::getWidth() const
|
||||
void ObjectTemplate::calculateWidth()
|
||||
{
|
||||
//TODO: Use 2D array
|
||||
//TODO: better precalculate and store constant value
|
||||
ui32 ret = 0;
|
||||
for (const auto &row : usedTiles) //copy is expensive
|
||||
for(const auto& row : usedTiles) //copy is expensive
|
||||
{
|
||||
ret = std::max<ui32>(ret, (ui32)row.size());
|
||||
width = std::max<ui32>(width, (ui32)row.size());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 ObjectTemplate::getHeight() const
|
||||
void ObjectTemplate::calculateHeight()
|
||||
{
|
||||
//TODO: Use 2D array
|
||||
return static_cast<ui32>(usedTiles.size());
|
||||
height = static_cast<ui32>(usedTiles.size());
|
||||
}
|
||||
|
||||
void ObjectTemplate::setSize(ui32 width, ui32 height)
|
||||
{
|
||||
usedTiles.resize(height);
|
||||
for (auto & line : usedTiles)
|
||||
for(auto & line : usedTiles)
|
||||
line.resize(width, 0);
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitable() const
|
||||
void ObjectTemplate::calculateVsitable()
|
||||
{
|
||||
for (auto & line : usedTiles)
|
||||
for (auto & tile : line)
|
||||
for(auto& line : usedTiles)
|
||||
{
|
||||
for(auto& tile : line)
|
||||
{
|
||||
if (tile & VISITABLE)
|
||||
return true;
|
||||
return false;
|
||||
{
|
||||
visitable = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
visitable = false;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isWithin(si32 X, si32 Y) const
|
||||
@ -469,31 +491,33 @@ bool ObjectTemplate::isBlockedAt(si32 X, si32 Y) const
|
||||
return isWithin(X, Y) && usedTiles[Y][X] & BLOCKED;
|
||||
}
|
||||
|
||||
std::set<int3> ObjectTemplate::getBlockedOffsets() const
|
||||
void ObjectTemplate::calculateBlockedOffsets()
|
||||
{
|
||||
std::set<int3> ret;
|
||||
blockedOffsets.clear();
|
||||
for(int w = 0; w < (int)getWidth(); ++w)
|
||||
{
|
||||
for(int h = 0; h < (int)getHeight(); ++h)
|
||||
{
|
||||
if (isBlockedAt(w, h))
|
||||
ret.insert(int3(-w, -h, 0));
|
||||
blockedOffsets.insert(int3(-w, -h, 0));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int3 ObjectTemplate::getBlockMapOffset() const
|
||||
void ObjectTemplate::calculateBlockMapOffset()
|
||||
{
|
||||
for(int w = 0; w < (int)getWidth(); ++w)
|
||||
{
|
||||
for(int h = 0; h < (int)getHeight(); ++h)
|
||||
{
|
||||
if (isBlockedAt(w, h))
|
||||
return int3(w, h, 0);
|
||||
{
|
||||
blockMapOffset = int3(w, h, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return int3(0,0,0);
|
||||
blockMapOffset = int3(0, 0, 0);
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
|
||||
@ -502,6 +526,7 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
|
||||
// 1 2 3
|
||||
// 8 4
|
||||
// 7 6 5
|
||||
//TODO: static? cached?
|
||||
int dirMap[3][3] =
|
||||
{
|
||||
{ visitDir & 1, visitDir & 2, visitDir & 4 },
|
||||
@ -515,22 +540,20 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
|
||||
return dirMap[dy][dx] != 0;
|
||||
}
|
||||
|
||||
int3 ObjectTemplate::getVisitableOffset() const
|
||||
void ObjectTemplate::calculateVisitableOffset()
|
||||
{
|
||||
for(int y = 0; y < (int)getHeight(); y++)
|
||||
for (int x = 0; x < (int)getWidth(); x++)
|
||||
{
|
||||
for(int x = 0; x < (int)getWidth(); x++)
|
||||
{
|
||||
if (isVisitableAt(x, y))
|
||||
return int3(x,y,0);
|
||||
|
||||
//logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!");
|
||||
return int3(0,0,0);
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitableFromTop() const
|
||||
{
|
||||
return visitDir & 2;
|
||||
//for some reason the line below is never called :?
|
||||
//return isVisitableFrom (0, 1);
|
||||
{
|
||||
visitableOffset = int3(x, y, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
visitableOffset = int3(0, 0, 0);
|
||||
}
|
||||
|
||||
bool ObjectTemplate::canBePlacedAt(Terrain terrain) const
|
||||
@ -538,3 +561,14 @@ bool ObjectTemplate::canBePlacedAt(Terrain terrain) const
|
||||
return allowedTerrains.count(terrain) != 0;
|
||||
}
|
||||
|
||||
void ObjectTemplate::recalculate()
|
||||
{
|
||||
calculateWidth();
|
||||
calculateHeight();
|
||||
calculateVsitable();
|
||||
//The lines below use width and height
|
||||
calculateBlockedOffsets();
|
||||
calculateBlockMapOffset();
|
||||
calculateVisitableOffset();
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../GameConstants.h"
|
||||
#include "../int3.h"
|
||||
|
||||
class CBinaryReader;
|
||||
class CLegacyConfigParser;
|
||||
@ -50,11 +51,22 @@ public:
|
||||
/// string ID, equals to def base name for h3m files (lower case, no extension) or specified in mod data
|
||||
std::string stringID;
|
||||
|
||||
ui32 getWidth() const;
|
||||
ui32 getHeight() const;
|
||||
inline ui32 getWidth() const
|
||||
{
|
||||
return width;
|
||||
};
|
||||
|
||||
inline ui32 getHeight() const
|
||||
{
|
||||
return height;
|
||||
};
|
||||
|
||||
void setSize(ui32 width, ui32 height);
|
||||
|
||||
bool isVisitable() const;
|
||||
inline bool isVisitable() const
|
||||
{
|
||||
return visitable;
|
||||
};
|
||||
|
||||
// Checks object used tiles
|
||||
// Position is relative to bottom-right corner of the object, can not be negative
|
||||
@ -62,13 +74,29 @@ public:
|
||||
bool isVisitableAt(si32 X, si32 Y) const;
|
||||
bool isVisibleAt(si32 X, si32 Y) const;
|
||||
bool isBlockedAt(si32 X, si32 Y) const;
|
||||
std::set<int3> getBlockedOffsets() const;
|
||||
int3 getBlockMapOffset() const; //bottom-right corner when firts blocked tile is
|
||||
|
||||
inline std::set<int3> getBlockedOffsets() const
|
||||
{
|
||||
return blockedOffsets;
|
||||
};
|
||||
|
||||
inline int3 getBlockMapOffset() const
|
||||
{
|
||||
return blockMapOffset;
|
||||
};
|
||||
|
||||
// Checks if object is visitable from certain direction. X and Y must be between -1..+1
|
||||
bool isVisitableFrom(si8 X, si8 Y) const;
|
||||
int3 getVisitableOffset() const;
|
||||
bool isVisitableFromTop() const;
|
||||
inline int3 getVisitableOffset() const
|
||||
{
|
||||
//logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!");
|
||||
return visitableOffset;
|
||||
};
|
||||
|
||||
inline bool isVisitableFromTop() const
|
||||
{
|
||||
return visitDir & 2;
|
||||
};
|
||||
|
||||
// Checks if object can be placed on specific terrain
|
||||
bool canBePlacedAt(Terrain terrain) const;
|
||||
@ -87,6 +115,25 @@ public:
|
||||
|
||||
bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); }
|
||||
|
||||
private:
|
||||
ui32 width;
|
||||
ui32 height;
|
||||
bool visitable;
|
||||
|
||||
std::set<int3> blockedOffsets;
|
||||
int3 blockMapOffset;
|
||||
int3 visitableOffset;
|
||||
|
||||
void recalculate();
|
||||
|
||||
void calculateWidth();
|
||||
void calculateHeight();
|
||||
void calculateVsitable();
|
||||
void calculateBlockedOffsets();
|
||||
void calculateBlockMapOffset();
|
||||
void calculateVisitableOffset();
|
||||
|
||||
public:
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & usedTiles;
|
||||
@ -98,6 +145,11 @@ public:
|
||||
h & printPriority;
|
||||
h & visitDir;
|
||||
h & editorAnimationFile;
|
||||
|
||||
if (!h.saving)
|
||||
{
|
||||
recalculate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -146,7 +146,7 @@ public:
|
||||
// FIXME: due to usage of JsonNode I can't make these methods const
|
||||
const CGHeroInstance * strongestHero(PlayerColor owner);
|
||||
std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
|
||||
|
||||
|
||||
CCampaignScenario();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int formatVersion)
|
||||
|
@ -417,7 +417,7 @@ bool CMap::checkForVisitableDir(const int3 & src, const TerrainTile *pom, const
|
||||
if(!vstd::contains(pom->blockingObjects, obj)) //this visitable object is not blocking, ignore
|
||||
continue;
|
||||
|
||||
if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y))
|
||||
if (!obj->appearance->isVisitableFrom(src.x - dst.x, src.y - dst.y))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -958,9 +958,9 @@ void CMapLoaderH3M::readDefInfo()
|
||||
// Read custom defs
|
||||
for(int idd = 0; idd < defAmount; ++idd)
|
||||
{
|
||||
ObjectTemplate tmpl;
|
||||
tmpl.readMap(reader);
|
||||
templates.push_back(tmpl);
|
||||
auto tmpl = new ObjectTemplate;
|
||||
tmpl->readMap(reader);
|
||||
templates.push_back(std::shared_ptr<const ObjectTemplate>(tmpl));
|
||||
}
|
||||
}
|
||||
|
||||
@ -977,10 +977,10 @@ void CMapLoaderH3M::readObjects()
|
||||
int defnum = reader.readUInt32();
|
||||
ObjectInstanceID idToBeGiven = ObjectInstanceID((si32)map->objects.size());
|
||||
|
||||
ObjectTemplate & objTempl = templates.at(defnum);
|
||||
std::shared_ptr<const ObjectTemplate> objTempl = templates.at(defnum);
|
||||
reader.skip(5);
|
||||
|
||||
switch(objTempl.id)
|
||||
switch(objTempl->id)
|
||||
{
|
||||
case Obj::EVENT:
|
||||
{
|
||||
@ -1212,15 +1212,15 @@ void CMapLoaderH3M::readObjects()
|
||||
|
||||
readMessageAndGuards(art->message, art);
|
||||
|
||||
if(objTempl.id == Obj::SPELL_SCROLL)
|
||||
if(objTempl->id == Obj::SPELL_SCROLL)
|
||||
{
|
||||
spellID = reader.readUInt32();
|
||||
artID = ArtifactID::SPELL_SCROLL;
|
||||
}
|
||||
else if(objTempl.id == Obj::ARTIFACT)
|
||||
else if(objTempl->id == Obj::ARTIFACT)
|
||||
{
|
||||
//specific artifact
|
||||
artID = objTempl.subid;
|
||||
artID = objTempl->subid;
|
||||
}
|
||||
|
||||
art->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID);
|
||||
@ -1235,7 +1235,7 @@ void CMapLoaderH3M::readObjects()
|
||||
readMessageAndGuards(res->message, res);
|
||||
|
||||
res->amount = reader.readUInt32();
|
||||
if(objTempl.subid == Res::GOLD)
|
||||
if(objTempl->subid == Res::GOLD)
|
||||
{
|
||||
// Gold is multiplied by 100.
|
||||
res->amount *= 100;
|
||||
@ -1246,7 +1246,7 @@ void CMapLoaderH3M::readObjects()
|
||||
case Obj::RANDOM_TOWN:
|
||||
case Obj::TOWN:
|
||||
{
|
||||
nobj = readTown(objTempl.subid);
|
||||
nobj = readTown(objTempl->subid);
|
||||
break;
|
||||
}
|
||||
case Obj::MINE:
|
||||
@ -1347,7 +1347,7 @@ void CMapLoaderH3M::readObjects()
|
||||
auto dwelling = new CGDwelling();
|
||||
nobj = dwelling;
|
||||
CSpecObjInfo * spec = nullptr;
|
||||
switch(objTempl.id)
|
||||
switch(objTempl->id)
|
||||
{
|
||||
case Obj::RANDOM_DWELLING:
|
||||
spec = new CCreGenLeveledCastleInfo();
|
||||
@ -1450,7 +1450,7 @@ void CMapLoaderH3M::readObjects()
|
||||
}
|
||||
case Obj::PYRAMID: //Pyramid of WoG object
|
||||
{
|
||||
if(objTempl.subid == 0)
|
||||
if(objTempl->subid == 0)
|
||||
{
|
||||
nobj = new CBank();
|
||||
}
|
||||
@ -1470,13 +1470,13 @@ void CMapLoaderH3M::readObjects()
|
||||
}
|
||||
default: //any other object
|
||||
{
|
||||
if (VLC->objtypeh->knownSubObjects(objTempl.id).count(objTempl.subid))
|
||||
if (VLC->objtypeh->knownSubObjects(objTempl->id).count(objTempl->subid))
|
||||
{
|
||||
nobj = VLC->objtypeh->getHandlerFor(objTempl.id, objTempl.subid)->create(objTempl);
|
||||
nobj = VLC->objtypeh->getHandlerFor(objTempl->id, objTempl->subid)->create(objTempl);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl.id.toEnum(), objTempl.subid, objPos.toString(), map->name);
|
||||
logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl->id.toEnum(), objTempl->subid, objPos.toString(), map->name);
|
||||
nobj = new CGObjectInstance();
|
||||
}
|
||||
break;
|
||||
@ -1484,11 +1484,11 @@ void CMapLoaderH3M::readObjects()
|
||||
}
|
||||
|
||||
nobj->pos = objPos;
|
||||
nobj->ID = objTempl.id;
|
||||
nobj->ID = objTempl->id;
|
||||
nobj->id = idToBeGiven;
|
||||
if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON)
|
||||
{
|
||||
nobj->subID = objTempl.subid;
|
||||
nobj->subID = objTempl->subid;
|
||||
}
|
||||
nobj->appearance = objTempl;
|
||||
assert(idToBeGiven == ObjectInstanceID((si32)map->objects.size()));
|
||||
|
@ -245,7 +245,7 @@ private:
|
||||
|
||||
/** List of templates loaded from the map, used on later stage to create
|
||||
* objects but not needed for fully functional CMap */
|
||||
std::vector<ObjectTemplate> templates;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
|
||||
|
||||
/** ptr to the map object which gets filled by data from the buffer */
|
||||
CMap * map;
|
||||
|
@ -1110,13 +1110,14 @@ void CMapLoaderJson::MapObjectLoader::construct()
|
||||
|
||||
auto handler = VLC->objtypeh->getHandlerFor(typeName, subtypeName);
|
||||
|
||||
ObjectTemplate appearance;
|
||||
auto appearance = new ObjectTemplate;
|
||||
|
||||
appearance.id = Obj(handler->type);
|
||||
appearance.subid = handler->subtype;
|
||||
appearance.readJson(configuration["template"], false);
|
||||
appearance->id = Obj(handler->type);
|
||||
appearance->subid = handler->subtype;
|
||||
appearance->readJson(configuration["template"], false);
|
||||
|
||||
instance = handler->create(appearance);
|
||||
// Will be destroyed soon and replaced with shared template
|
||||
instance = handler->create(std::shared_ptr<const ObjectTemplate>(appearance));
|
||||
|
||||
instance->id = ObjectInstanceID((si32)owner->map->objects.size());
|
||||
instance->instanceName = jsonKey;
|
||||
|
@ -64,6 +64,7 @@ void CMapGenerator::loadConfig()
|
||||
config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer();
|
||||
config.minGuardStrength = randomMapJson["minGuardStrength"].Integer();
|
||||
config.defaultRoadType = randomMapJson["defaultRoadType"].String();
|
||||
config.secondaryRoadType = randomMapJson["secondaryRoadType"].String();
|
||||
config.treasureValueLimit = randomMapJson["treasureValueLimit"].Integer();
|
||||
for(auto & i : randomMapJson["prisons"]["experience"].Vector())
|
||||
config.prisonExperience.push_back(i.Integer());
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
int mineExtraResources;
|
||||
int minGuardStrength;
|
||||
std::string defaultRoadType;
|
||||
std::string secondaryRoadType;
|
||||
int treasureValueLimit;
|
||||
std::vector<int> prisonExperience, prisonValues;
|
||||
std::vector<int> scrollValues;
|
||||
|
@ -204,8 +204,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
||||
auto & managerOther = *otherZone->getModificator<ObjectManager>();
|
||||
|
||||
auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
|
||||
auto gate1 = factory->create(ObjectTemplate());
|
||||
auto gate2 = factory->create(ObjectTemplate());
|
||||
auto gate1 = factory->create();
|
||||
auto gate2 = factory->create();
|
||||
rmg::Object rmgGate1(*gate1), rmgGate2(*gate2);
|
||||
rmgGate1.setTemplate(zone.getTerrainType());
|
||||
rmgGate2.setTemplate(otherZone->getTerrainType());
|
||||
@ -249,8 +249,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
||||
if(!success)
|
||||
{
|
||||
auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex());
|
||||
auto teleport1 = factory->create(ObjectTemplate());
|
||||
auto teleport2 = factory->create(ObjectTemplate());
|
||||
auto teleport1 = factory->create();
|
||||
auto teleport2 = factory->create();
|
||||
|
||||
zone.getModificator<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength());
|
||||
otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength());
|
||||
|
@ -27,7 +27,7 @@
|
||||
void ObjectManager::process()
|
||||
{
|
||||
zone.fractalize();
|
||||
createRequiredObjects();
|
||||
createRequiredObjects();
|
||||
}
|
||||
|
||||
void ObjectManager::init()
|
||||
@ -77,6 +77,21 @@ const rmg::Area & ObjectManager::getVisitableArea() const
|
||||
return objectsVisitableArea;
|
||||
}
|
||||
|
||||
std::vector<CGObjectInstance*> ObjectManager::getMines() const
|
||||
{
|
||||
std::vector<CGObjectInstance*> mines;
|
||||
|
||||
for (auto object : objects)
|
||||
{
|
||||
if (object->ID == Obj::MINE)
|
||||
{
|
||||
mines.push_back(object);
|
||||
}
|
||||
}
|
||||
|
||||
return mines;
|
||||
}
|
||||
|
||||
int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const
|
||||
{
|
||||
float bestWeight = 0.f;
|
||||
@ -220,6 +235,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
{
|
||||
logGlobal->trace("Creating required objects");
|
||||
|
||||
RandomGeneratorUtil::randomShuffle(requiredObjects, generator.rand);
|
||||
for(const auto & object : requiredObjects)
|
||||
{
|
||||
auto * obj = object.first;
|
||||
@ -351,7 +367,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
||||
objects.push_back(&instance->object());
|
||||
if(auto * m = zone.getModificator<RoadPlacer>())
|
||||
{
|
||||
if(instance->object().appearance.isVisitableFromTop())
|
||||
if(instance->object().appearance->isVisitableFromTop())
|
||||
m->areaForRoads().add(instance->getVisitablePosition());
|
||||
else
|
||||
{
|
||||
@ -433,7 +449,7 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
|
||||
|
||||
auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
|
||||
|
||||
auto guard = (CGCreature *) guardFactory->create(ObjectTemplate());
|
||||
auto guard = (CGCreature *) guardFactory->create();
|
||||
guard->character = CGCreature::HOSTILE;
|
||||
auto hlp = new CStackInstance(creId, amount);
|
||||
//will be set during initialization
|
||||
|
@ -63,6 +63,8 @@ public:
|
||||
void createDistancesPriorityQueue();
|
||||
|
||||
const rmg::Area & getVisitableArea() const;
|
||||
|
||||
std::vector<CGObjectInstance*> getMines() const;
|
||||
|
||||
protected:
|
||||
//content info
|
||||
|
@ -31,7 +31,7 @@ void ObstaclePlacer::process()
|
||||
|
||||
auto * riverManager = zone.getModificator<RiverPlacer>();
|
||||
|
||||
typedef std::vector<ObjectTemplate> ObstacleVector;
|
||||
typedef std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleVector;
|
||||
//obstacleVector possibleObstacles;
|
||||
|
||||
std::map<int, ObstacleVector> obstaclesBySize;
|
||||
@ -48,8 +48,8 @@ void ObstaclePlacer::process()
|
||||
{
|
||||
for(auto temp : handler->getTemplates())
|
||||
{
|
||||
if(temp.canBePlacedAt(zone.getTerrainType()) && temp.getBlockMapOffset().valid())
|
||||
obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp);
|
||||
if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid())
|
||||
obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ void ObstaclePlacer::process()
|
||||
|
||||
for(auto & temp : shuffledObstacles)
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(temp.id, temp.subid);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
|
||||
auto obj = handler->create(temp);
|
||||
allObjects.emplace_back(*obj);
|
||||
rmg::Object * rmgObject = &allObjects.back();
|
||||
|
@ -374,10 +374,10 @@ void RiverPlacer::connectRiver(const int3 & tile)
|
||||
auto handler = VLC->objtypeh->getHandlerFor(RIVER_DELTA_ID, RIVER_DELTA_SUBTYPE);
|
||||
assert(handler->isStaticObject());
|
||||
|
||||
std::vector<ObjectTemplate> tmplates;
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> tmplates;
|
||||
for(auto & temp : handler->getTemplates())
|
||||
{
|
||||
if(temp.canBePlacedAt(zone.getTerrainType()))
|
||||
if(temp->canBePlacedAt(zone.getTerrainType()))
|
||||
tmplates.push_back(temp);
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
|
||||
std::string targetTemplateName = RIVER_DELTA_TEMPLATE_NAME.at(river) + std::to_string(deltaOrientations[pos]) + ".def";
|
||||
for(auto & templ : tmplates)
|
||||
{
|
||||
if(templ.animationFile == targetTemplateName)
|
||||
if(templ->animationFile == targetTemplateName)
|
||||
{
|
||||
auto obj = handler->create(templ);
|
||||
rmg::Object deltaObj(*obj, deltaPositions[pos]);
|
||||
|
@ -105,7 +105,7 @@ void Object::Instance::setPositionRaw(const int3 & position)
|
||||
|
||||
void Object::Instance::setTemplate(const Terrain & terrain)
|
||||
{
|
||||
if(dObject.appearance.id == Obj::NO_OBJ)
|
||||
if(dObject.appearance->id == Obj::NO_OBJ)
|
||||
{
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
|
||||
if(templates.empty())
|
||||
@ -130,7 +130,7 @@ void Object::Instance::clear()
|
||||
bool Object::Instance::isVisitableFrom(const int3 & position) const
|
||||
{
|
||||
auto relPosition = position - getPosition(true);
|
||||
return dObject.appearance.isVisitableFrom(relPosition.x, relPosition.y);
|
||||
return dObject.appearance->isVisitableFrom(relPosition.x, relPosition.y);
|
||||
}
|
||||
|
||||
CGObjectInstance & Object::Instance::object()
|
||||
@ -286,7 +286,7 @@ void Object::Instance::finalize(RmgMap & map)
|
||||
throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString()));
|
||||
}
|
||||
|
||||
if (dObject.appearance.id == Obj::NO_OBJ)
|
||||
if (dObject.appearance->id == Obj::NO_OBJ)
|
||||
{
|
||||
auto terrainType = map.map().getTile(getPosition(true)).terType;
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType);
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "RoadPlacer.h"
|
||||
#include "ObjectManager.h"
|
||||
#include "Functions.h"
|
||||
#include "CMapGenerator.h"
|
||||
#include "RmgMap.h"
|
||||
@ -63,12 +64,13 @@ bool RoadPlacer::createRoad(const int3 & dst)
|
||||
|
||||
}
|
||||
|
||||
void RoadPlacer::drawRoads()
|
||||
void RoadPlacer::drawRoads(bool secondary)
|
||||
{
|
||||
zone.areaPossible().subtract(roads);
|
||||
zone.freePaths().unite(roads);
|
||||
map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
|
||||
map.getEditManager()->drawRoad(generator.getConfig().defaultRoadType, &generator.rand);
|
||||
std::string roadType = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
|
||||
map.getEditManager()->drawRoad(roadType, &generator.rand);
|
||||
}
|
||||
|
||||
void RoadPlacer::addRoadNode(const int3& node)
|
||||
@ -78,7 +80,22 @@ void RoadPlacer::addRoadNode(const int3& node)
|
||||
|
||||
void RoadPlacer::connectRoads()
|
||||
{
|
||||
if(roadNodes.empty())
|
||||
bool noRoadNodes = false;
|
||||
//Assumes objects are already placed
|
||||
if (roadNodes.size() < 2)
|
||||
{
|
||||
//If there are no nodes, draw roads to mines
|
||||
noRoadNodes = true;
|
||||
if (auto* m = zone.getModificator<ObjectManager>())
|
||||
{
|
||||
for (auto object : m->getMines())
|
||||
{
|
||||
addRoadNode(object->visitablePos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(roadNodes.size() < 2)
|
||||
return;
|
||||
|
||||
//take any tile from road nodes as destination zone for all other road nodes
|
||||
@ -90,7 +107,8 @@ void RoadPlacer::connectRoads()
|
||||
createRoad(node);
|
||||
}
|
||||
|
||||
drawRoads();
|
||||
//Draw dirt roads if there are only mines
|
||||
drawRoads(noRoadNodes);
|
||||
}
|
||||
|
||||
char RoadPlacer::dump(const int3 & t)
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
|
||||
protected:
|
||||
bool createRoad(const int3 & dst);
|
||||
void drawRoads(); //actually updates tiles
|
||||
void drawRoads(bool secondary = false); //actually updates tiles
|
||||
|
||||
protected:
|
||||
rmg::Tileset roadNodes; //tiles to be connected with roads
|
||||
|
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