1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge branch 'develop' into battle-queue-improvement

This commit is contained in:
Dydzio 2023-02-02 18:26:31 +01:00
commit 2ef33d54ab
267 changed files with 12186 additions and 10608 deletions

View File

@ -99,20 +99,21 @@ jobs:
extension: ipa
preset: ios-release-conan
conan_profile: ios-arm64
- platform: mxe
os: ubuntu-20.04
mxe: i686-w64-mingw32.shared
test: 0
pack: 1
cpack_args: -D CPACK_NSIS_EXECUTABLE=`which makensis`
extension: exe
cmake_args: -G Ninja
- platform: msvc
os: windows-latest
test: 0
pack: 1
extension: exe
preset: windows-msvc-release
- platform: mingw-ubuntu
os: ubuntu-22.04
test: 0
pack: 1
extension: exe
cpack_args: -D CPACK_NSIS_EXECUTABLE=`which makensis`
cmake_args: -G Ninja
preset: windows-mingw-conan-linux
conan_profile: mingw64-linux.jinja
runs-on: ${{ matrix.os }}
defaults:
run:
@ -126,7 +127,6 @@ jobs:
- name: Dependencies
run: source '${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh'
env:
MXE_TARGET: ${{ matrix.mxe }}
VCMI_BUILD_PLATFORM: x64
- uses: actions/setup-python@v4
@ -164,40 +164,16 @@ jobs:
env:
PULL_REQUEST: ${{ github.event.pull_request.number }}
- name: Configure CMake
if: "${{ matrix.preset == '' }}"
run: |
mkdir -p '${{github.workspace}}/out/build/${{matrix.preset}}'
cd '${{github.workspace}}/out/build/${{matrix.preset}}'
cmake \
../.. -GNinja \
${{matrix.cmake_args}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DENABLE_TEST=${{matrix.test}} \
-DENABLE_STRICT_COMPILATION=ON \
-DPACKAGE_NAME_SUFFIX:STRING="$VCMI_PACKAGE_NAME_SUFFIX" \
-DPACKAGE_FILE_NAME:STRING="$VCMI_PACKAGE_FILE_NAME" \
-DENABLE_GITVERSION="$VCMI_PACKAGE_GITVERSION"
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
- name: CMake Preset
if: "${{ matrix.preset != '' }}"
run: |
cmake --preset ${{ matrix.preset }}
- name: Build
if: "${{ matrix.preset == '' }}"
run: |
cmake --build '${{github.workspace}}/out/build/${{matrix.preset}}'
- name: Build Preset
if: "${{ matrix.preset != '' }}"
run: |
cmake --build --preset ${{matrix.preset}}
- name: Test
if: ${{ matrix.test == 1 && matrix.preset != ''}}
if: ${{ matrix.test == 1 }}
run: |
ctest --preset ${{matrix.preset}}
@ -213,7 +189,7 @@ jobs:
rm -rf _CPack_Packages
- name: Additional logs
if: ${{ failure() && steps.cpack.outcome == 'failure' && matrix.platform == 'mxe' }}
if: ${{ failure() && steps.cpack.outcome == 'failure' && matrix.platform == 'msvc' }}
run: |
cat '${{github.workspace}}/out/build/${{matrix.preset}}/_CPack_Packages/win32/NSIS/project.nsi'
cat '${{github.workspace}}/out/build/${{matrix.preset}}/_CPack_Packages/win32/NSIS/NSISOutput.log'
@ -227,7 +203,7 @@ jobs:
${{github.workspace}}/**/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
- name: Upload build
if: ${{ matrix.pack == 1 && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' }}
if: ${{ matrix.pack == 1 && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform == 'msvc' }}
run: |
cd '${{github.workspace}}/out/build/${{matrix.preset}}'
source '${{github.workspace}}/CI/upload_package.sh'
@ -245,7 +221,7 @@ jobs:
- name: Trigger Android
uses: peter-evans/repository-dispatch@v1
if: ${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master') && matrix.platform == 'mxe' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master') && matrix.platform == 'msvc' }}
with:
token: ${{ secrets.VCMI_ANDROID_ACCESS_TOKEN }}
repository: vcmi/vcmi-android

View File

@ -12,6 +12,10 @@ if(TBB_FOUND AND MSVC)
install_vcpkg_imported_tgt(TBB::tbb)
endif()
#FuzzyLite uses MSVC pragmas in headers, so, we need to disable -Wunknown-pragmas
if(MINGW)
add_compile_options(-Wno-unknown-pragmas)
endif()
if(NOT FORCE_BUNDLED_FL)
find_package(fuzzylite)
@ -27,6 +31,10 @@ if(NOT fuzzylite_FOUND)
set(FL_BUILD_BINARY OFF CACHE BOOL "")
set(FL_BUILD_SHARED OFF CACHE BOOL "")
set(FL_BUILD_TESTS OFF CACHE BOOL "")
#It is for compiling FuzzyLite, it will not compile without it on GCC
if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
add_compile_options(-Wno-error=deprecated-declarations)
endif()
add_subdirectory(FuzzyLite/fuzzylite EXCLUDE_FROM_ALL)
add_library(fuzzylite::fuzzylite ALIAS fl-static)
target_include_directories(fl-static PUBLIC ${CMAKE_HOME_DIRECTORY}/AI/FuzzyLite/fuzzylite)

View File

@ -314,8 +314,9 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
return false;
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods

View File

@ -70,12 +70,15 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj)
return;
// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
return;
if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
return;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
{
if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
return;
if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
return;
}
if(obj->ID == Obj::MONSTER)
return;

View File

@ -32,10 +32,6 @@ MapObjectsEvaluator::MapObjectsEvaluator()
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = handler->getAiValue().get();
}
else if(VLC->objtypeh->getObjGroupAiValue(primaryID) != boost::none) //if value is not initialized - fallback to default value for this object family if it exists
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = VLC->objtypeh->getObjGroupAiValue(primaryID).get();
}
else //some default handling when aiValue not found, objects that require advanced properties (unavailable from handler) get their value calculated in getObjectValue
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0;

View File

@ -29,12 +29,6 @@
extern FuzzyHelper * fh;
VCMI_LIB_NAMESPACE_BEGIN
class CGVisitableOPW;
VCMI_LIB_NAMESPACE_END
const double SAFE_ATTACK_CONSTANT = 1.5;
//one thread may be turn of AI and another will be handling a side effect for AI2
@ -1606,12 +1600,19 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
{
if(!obj)
return;
if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
return;
if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
return;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj)) //we may want to visit it with another hero
{
if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
return;
if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
return;
}
if(obj->ID == Obj::MONSTER)
return;
alreadyVisited.insert(obj);
}
@ -2742,8 +2743,9 @@ bool AIStatus::channelProbing()
bool isWeeklyRevisitable(const CGObjectInstance * obj)
{
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods

View File

@ -11,7 +11,6 @@
#include "CCallback.h"
#include "lib/CCreatureHandler.h"
#include "client/CGameInfo.h"
#include "lib/CGameState.h"
#include "client/CPlayerInterface.h"
#include "client/Client.h"
@ -21,7 +20,6 @@
#include "lib/CGeneralTextHandler.h"
#include "lib/CHeroHandler.h"
#include "lib/NetPacks.h"
#include "client/mapHandler.h"
#include "lib/CArtHandler.h"
#include "lib/GameConstants.h"
#include "lib/CPlayerState.h"

View File

@ -0,0 +1,20 @@
{% macro generate_env(target_host) -%}
CONAN_CROSS_COMPILE={{ target_host }}-
CHOST={{ target_host }}
AR={{ target_host }}-ar
AS={{ target_host }}-as
CC={{ target_host }}-gcc
CXX={{ target_host }}-g++
RANLIB={{ target_host }}-ranlib
STRIP={{ target_host }}-strip
{%- endmacro -%}
{% macro generate_env_win32(target_host) -%}
CONAN_SYSTEM_LIBRARY_LOCATION=/usr/lib/gcc/{{ target_host }}/10-posix/
RC={{ target_host }}-windres
{%- endmacro -%}
{% macro generate_conf(target_host) -%}
tools.build:compiler_executables = {"c": "{{ target_host }}-gcc", "cpp": "{{ target_host }}-g++"}
tools.build:sysroot = /usr/{{ target_host }}
{%- endmacro -%}

View File

@ -0,0 +1,10 @@
[settings]
os=Windows
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=10
compiler.cppstd=11
build_type=Release
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@ -0,0 +1,15 @@
{% import 'base/cross-macro.j2' as cross -%}
include(base/cross-windows)
{% set target_host="i686-w64-mingw32" %}
[settings]
arch=x86
[conf]
{{ cross.generate_conf(target_host)}}
tools.build:cflags = ["-msse2"]
tools.build:cxxflags = ["-msse2"]
[env]
{{ cross.generate_env(target_host) }}
{{ cross.generate_env_win32(target_host) }}

View File

@ -0,0 +1,13 @@
{% import 'base/cross-macro.j2' as cross -%}
include(base/cross-windows)
{% set target_host="x86_64-w64-mingw32" %}
[settings]
arch=x86_64
[conf]
{{ cross.generate_conf(target_host)}}
[env]
{{ cross.generate_env(target_host) }}
{{ cross.generate_env_win32(target_host) }}

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
"$1/ios/zip2ipa.sh" "$2"

View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
sudo apt-get update
sudo apt-get install ninja-build mingw-w64 nsis
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
# Workaround for getting new MinGW headers on Ubuntu 22.04.
# Remove it once MinGW headers version in repository will be 10.0 at least
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-common_10.0.0-2_all.deb \
&& sudo dpkg -i mingw-w64-common_10.0.0-2_all.deb;
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-x86-64-dev_10.0.0-2_all.deb \
&& sudo dpkg -i mingw-w64-x86-64-dev_10.0.0-2_all.deb;
mkdir ~/.conan ; cd ~/.conan
curl -L "https://github.com/vcmi/vcmi-deps-windows-conan/releases/download/1.0/vcmi-deps-windows-conan-w64.tgz" \
| tar -xzf -

View File

@ -1,53 +0,0 @@
#!/bin/sh
# steps to upgrade MXE dependencies:
# 1) Use Debian/Ubuntu system or install one (virtual machines will work too)
# 2) update following script to include any new dependencies
# You can also run it to upgrade existing ones, but don't expect much
# MXE repository only provides ancient versions for the sake of "stability"
# https://github.com/vcmi/vcmi-deps-mxe/blob/master/mirror-mxe.sh
# 3) make release in vcmi-deps-mxe repository using resulting tar archive
# 4) update paths to tar archive in this script
# Install nsis for installer creation
sudo add-apt-repository 'deb http://security.ubuntu.com/ubuntu bionic-security main'
sudo apt-get install -qq nsis ninja-build libssl1.0.0
# MXE repository was too slow for Travis far too often
wget -nv https://github.com/vcmi/vcmi-deps-mxe/releases/download/2021-02-20/mxe-i686-w64-mingw32.shared-2021-01-22.tar
tar -xvf mxe-i686-w64-mingw32.shared-2021-01-22.tar
sudo dpkg -i mxe-*.deb
sudo apt-get install -f --yes
if false; then
# Add MXE repository and key
echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" \
| sudo tee /etc/apt/sources.list.d/mxeapt.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB
# Install needed packages
sudo apt-get update -qq
sudo apt-get install -q --yes \
mxe-$MXE_TARGET-gcc \
mxe-$MXE_TARGET-boost \
mxe-$MXE_TARGET-zlib \
mxe-$MXE_TARGET-sdl2 \
mxe-$MXE_TARGET-sdl2-gfx \
mxe-$MXE_TARGET-sdl2-image \
mxe-$MXE_TARGET-sdl2-mixer \
mxe-$MXE_TARGET-sdl2-ttf \
mxe-$MXE_TARGET-ffmpeg \
mxe-$MXE_TARGET-qt \
mxe-$MXE_TARGET-qtbase \
mxe-$MXE_TARGET-intel-tbb \
mxe-i686-w64-mingw32.static-luajit
fi # Disable
# alias for CMake
CMAKE_LOCATION=$(which cmake)
sudo mv $CMAKE_LOCATION $CMAKE_LOCATION.orig
sudo ln -s /usr/lib/mxe/usr/bin/$MXE_TARGET-cmake $CMAKE_LOCATION

View File

@ -1,7 +1,7 @@
# Minimum required version greatly affect CMake behavior
# So cmake_minimum_required must be called before the project()
# 3.10.0 is used since it's minimal in MXE dependencies for now
cmake_minimum_required(VERSION 3.10.0)
# 3.16.0 is used since it's used by our currently oldest suppored system: Ubuntu-20.04
cmake_minimum_required(VERSION 3.16.0)
project(VCMI)
# TODO
@ -10,9 +10,6 @@ project(VCMI)
# 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.
#
# MXE:
# - Try to implement MXE support into BundleUtilities so we can deploy deps automatically
#
# Vckpg:
# - Improve install code once there is better way to deploy DLLs and Qt plugins
#
@ -57,9 +54,8 @@ if(APPLE_IOS)
else()
option(ENABLE_TEST "Enable compilation of unit tests" OFF)
endif()
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0")
option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
endif(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0")
option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON)
option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON)
option(ENABLE_STRICT_COMPILATION "Treat all compiler warnings as errors" OFF)
@ -90,11 +86,6 @@ if(APPLE_IOS AND COPY_CONFIG_ON_BUILD)
set(COPY_CONFIG_ON_BUILD OFF)
endif()
# No QT Linguist on MXE
if((MINGW) AND (${CMAKE_CROSSCOMPILING}))
set(ENABLE_TRANSLATIONS OFF)
endif()
############################################
# Miscellaneous options #
############################################
@ -138,10 +129,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_
set(ENABLE_PCH OFF) # broken
endif()
if( ${CMAKE_VERSION} VERSION_LESS "3.16.0")
set(ENABLE_PCH OFF) #not supported
endif()
if(ENABLE_PCH)
macro(enable_pch name)
target_precompile_headers(${name} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:<StdInc.h$<ANGLE-R>>)
@ -168,12 +155,12 @@ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
#Global fallback mapping
# RelWithDebInfo falls back to Release, then MinSizeRel
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel "")
# MinSizeRel falls back to Release, then RelWithDebInfo
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 "")
# RelWithDebInfo falls back to Release, then MinSizeRel, and then to None (tbb in 22.04 requires it)
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None "")
# MinSizeRel falls back to Release, then RelWithDebInfo, and then to None (tbb in 22.04 requires it)
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel Release RelWithDebInfo None "")
# Release falls back to RelWithDebInfo, then MinSizeRel, and then to None (tbb in 22.04 requires it)
set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None "")
set(CMAKE_XCODE_ATTRIBUTE_APP_DISPLAY_NAME ${APP_DISPLAY_NAME})
set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
@ -296,12 +283,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") # low chance of any significant issues
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-varargs") # emitted in fuzzylite headers, disabled
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # emitted only by ancient gcc 5.5 in MXE build, remove after upgrade
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") # emitted only by ancient gcc 5.5 in MXE build, remove after upgrade
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") # emitted only by ancient gcc 5.5 in MXE build, remove after upgrade
endif()
if(ENABLE_STRICT_COMPILATION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=array-bounds") # false positives in boost::multiarray during release build, keep as warning-only
@ -328,6 +309,27 @@ if(ENABLE_LUA)
add_definitions(-DSCRIPTING_ENABLED=1)
endif()
if(USING_CONAN AND (MINGW AND CMAKE_HOST_UNIX))
# Hack for workaround https://github.com/conan-io/conan-center-index/issues/15405
# Remove once it will be fixed
execute_process(COMMAND
bash -c "grep -rl Mf ${CONAN_INSTALL_FOLDER} | xargs sed -i 's/Mf/mf/g'"
)
# Hack for workaround ffmpeg broken linking (conan ffmpeg forgots to link to ws2_32)
# Remove once it will be fixed
execute_process(COMMAND
bash -c "grep -rl secur32 ${CONAN_INSTALL_FOLDER} | xargs sed -i 's/secur32)/secur32 ws2_32)/g'"
)
execute_process(COMMAND
bash -c "grep -rl secur32 ${CONAN_INSTALL_FOLDER} | xargs sed -i 's/secur32 mfplat/secur32 ws2_32 mfplat/g'"
)
# Fixup tbb for cross-compiling on Conan
# Remove once it will be fixed
execute_process(COMMAND
bash -c "grep -rl tbb12 ${CONAN_INSTALL_FOLDER} | xargs sed -i 's/tbb tbb12/tbb12/g'"
)
endif()
############################################
# Finding packages #
############################################
@ -385,14 +387,7 @@ endif()
if(ENABLE_LUA)
find_package(luajit)
# MXE paths hardcoded for current dependencies pack - tried and could not make it work another way
if((MINGW) AND (${CMAKE_CROSSCOMPILING}) AND (DEFINED MSYS) AND (NOT TARGET luajit::luajit))
add_library(luajit::luajit STATIC IMPORTED)
set_target_properties(luajit::luajit PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "/usr/lib/mxe/usr/i686-w64-mingw32.static/include/luajit-2.0")
set_target_properties(luajit::luajit PROPERTIES
IMPORTED_LOCATION "/usr/lib/mxe/usr/i686-w64-mingw32.static/lib/libluajit-5.1.a")
endif()
if(TARGET luajit::luajit)
message(STATUS "Using LuaJIT provided by system")
else()
@ -544,13 +539,21 @@ endif()
if(WIN32)
file(GLOB dep_files
${dep_files}
"${CMAKE_FIND_ROOT_PATH}/bin/*.dll")
if(USING_CONAN)
#Conan imports enabled
vcmi_install_conan_deps("\${CMAKE_INSTALL_PREFIX}")
file(GLOB dep_files
${dep_files}
"${CMAKE_SYSROOT}/bin/*.dll"
"${CMAKE_SYSROOT}/lib/*.dll"
"${CONAN_SYSTEM_LIBRARY_LOCATION}/*.dll")
else()
file(GLOB dep_files
${dep_files}
"${CMAKE_FIND_ROOT_PATH}/bin/*.dll")
endif()
if((${CMAKE_CROSSCOMPILING}) AND (DEFINED MSYS))
message(STATUS "Detected MXE build")
elseif(CMAKE_BUILD_TYPE MATCHES Debug)
if(CMAKE_BUILD_TYPE MATCHES Debug)
# Copy debug versions of libraries if build type is debug
set(debug_postfix d)
endif()
@ -617,7 +620,11 @@ if(WIN32)
else()
set(CPACK_NSIS_PACKAGE_NAME "VCMI ${CPACK_PACKAGE_VERSION} ${PACKAGE_NAME_SUFFIX} ")
endif()
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
if(CMAKE_SYSTEM_PROCESSOR MATCHES ".*64")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
else()
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
endif()
if(ENABLE_LAUNCHER)
set(CPACK_PACKAGE_EXECUTABLES "VCMI_launcher;VCMI")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " CreateShortCut \\\"$DESKTOP\\\\VCMI.lnk\\\" \\\"$INSTDIR\\\\VCMI_launcher.exe\\\"")
@ -627,6 +634,12 @@ if(WIN32)
endif()
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " Delete \\\"$DESKTOP\\\\VCMI.lnk\\\" ")
# Strip MinGW CPack target if build configuration without debug info
if(MINGW)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
set(CPACK_STRIP_FILES ON)
endif()
endif()
# set the install/unistall icon used for the installer itself
# There is a bug in NSI that does not handle full unix paths properly.
set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/client\\\\vcmi.ico")
@ -643,7 +656,7 @@ if(WIN32)
set(CPACK_NSIS_URL_INFO_ABOUT "http://vcmi.eu/")
set(CPACK_NSIS_CONTACT @CPACK_PACKAGE_CONTACT@)
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
# Use BundleUtilities to fix build when Vcpkg is used and disable it for MXE
# Use BundleUtilities to fix build when Vcpkg is used and disable it for mingw
if(NOT (${CMAKE_CROSSCOMPILING}))
add_subdirectory(win)
endif()
@ -687,6 +700,7 @@ elseif(APPLE_MACOS AND NOT ENABLE_MONOLITHIC_INSTALL)
add_subdirectory(osx)
elseif(APPLE_IOS)
set(CPACK_GENERATOR ZIP)
set(CPACK_ARCHIVE_FILE_EXTENSION ipa)
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_CURRENT_BINARY_DIR};${CMAKE_PROJECT_NAME};app;/")
else()

View File

@ -81,6 +81,19 @@
"FORCE_BUNDLED_MINIZIP": "ON"
}
},
{
"name": "windows-mingw-conan-linux",
"displayName": "Ninja+Conan release",
"description": "VCMI Windows Ninja using Conan on Linux",
"inherits": [
"build-with-conan",
"default-release"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"FORCE_BUNDLED_FL": "ON"
}
},
{
"name": "macos-ninja-release",
"displayName": "Ninja release",
@ -222,6 +235,12 @@
"configurePreset": "windows-msvc-release",
"inherits": "default-release"
},
{
"name": "windows-mingw-conan-linux",
"configurePreset": "windows-mingw-conan-linux",
"inherits": "default-release",
"configuration": "Release"
},
{
"name": "ios-release-conan",
"configurePreset": "ios-release-conan",
@ -271,6 +290,11 @@
"name": "windows-msvc-release",
"configurePreset": "windows-msvc-release",
"inherits": "default-release"
},
{
"name": "windows-mingw-conan-linux",
"configurePreset": "windows-mingw-conan-linux",
"inherits": "default-release"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,243 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Threat: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Effortless",
"vcmi.adventureMap.monsterThreat.levels.1" : "Very Weak",
"vcmi.adventureMap.monsterThreat.levels.2" : "Weak",
"vcmi.adventureMap.monsterThreat.levels.3" : "A bit weaker",
"vcmi.adventureMap.monsterThreat.levels.4" : "Equal",
"vcmi.adventureMap.monsterThreat.levels.5" : "A bit stronger",
"vcmi.adventureMap.monsterThreat.levels.6" : "Strong",
"vcmi.adventureMap.monsterThreat.levels.7" : "Very Strong",
"vcmi.adventureMap.monsterThreat.levels.8" : "Challenging",
"vcmi.adventureMap.monsterThreat.levels.9" : "Overpowering",
"vcmi.adventureMap.monsterThreat.levels.10" : "Deadly",
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossible",
"vcmi.adventureMap.confirmRestartGame" : "Are you sure you want to restart game?",
"vcmi.adventureMap.noTownWithMarket" : "No available marketplace!",
"vcmi.adventureMap.noTownWithTavern" : "No available town with tavern!",
"vcmi.adventureMap.spellUnknownProblem" : "Unknown problem with this spell, no more information available.",
"vcmi.adventureMap.playerAttacked" : "Player has been attacked: %s",
"vcmi.adventureMap.moveCostDetails" : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
"vcmi.server.errors.existingProcess" : "Another vcmiserver process is running, please terminate it first",
"vcmi.server.errors.modsIncompatibility" : "Required mods to load game:",
"vcmi.server.confirmReconnect" : "Connect to the last session?",
"vcmi.systemOptions.fullscreenButton.hover" : "Fullscreen",
"vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\n If selected, VCMI will run in fullscreen mode, othervice VCMI will run in window",
"vcmi.systemOptions.resolutionButton.hover" : "Resolution",
"vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Change in-game screen resolution. Game restart required to apply new resolution.",
"vcmi.systemOptions.resolutionMenu.hover" : "Select resolution",
"vcmi.systemOptions.resolutionMenu.help" : "Change in-game screen resolution.",
"vcmi.townHall.missingBase" : "Base building %s must be built first",
"vcmi.townHall.noCreaturesToRecruit" : "There are no creatures to recruit!",
"vcmi.townHall.greetingManaVortex" : "As you near the %s your body is filled with new energy. You have doubled your normal spell points.",
"vcmi.townHall.greetingKnowledge" : "You study the glyphs on the %s and gain insight into the workings of various magics (+1 Knowledge).",
"vcmi.townHall.greetingSpellPower" : "The %s teaches you new ways to focus your magical powers (+1 Power).",
"vcmi.townHall.greetingExperience" : "A visit to the %s teaches you many new skills (+1000 Experience).",
"vcmi.townHall.greetingAttack" : "Some time spent at the %s allows you to learn more effective combat skills (+1 Attack Skill).",
"vcmi.townHall.greetingDefence" : "Spending time in the %s, the experienced warriors therein teach you additional defensive skills (+1 Defense).",
"vcmi.townHall.hasNotProduced" : "The %s has not produced anything yet.",
"vcmi.townHall.hasProduced" : "The %s produced %d %s this week.",
"vcmi.townHall.greetingCustomBonus" : "%s gives you +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " until next battle.",
"vcmi.townHall.greetingInTownMagicWell" : "%s has restored your spell points to maximum.",
"vcmi.logicalExpressions.anyOf" : "Any of the following:",
"vcmi.logicalExpressions.allOf" : "All of the following:",
"vcmi.logicalExpressions.noneOf" : "None of the following:",
"vcmi.heroWindow.openCommander.hover" : "Open commander window",
"vcmi.heroWindow.openCommander.help" : "Displays information about commander of this hero",
"vcmi.commanderWindow.artifactMessage" : "Do you want to give this artifact back to hero?",
"vcmi.creatureWindow.showBonuses.hover" : "Switch to bonuses view",
"vcmi.creatureWindow.showBonuses.help" : "Displays all active bonuses of the commander",
"vcmi.creatureWindow.showSkills.hover" : "Switch to skills view",
"vcmi.creatureWindow.showSkills.help" : "Displays all learned skills of the commander",
"vcmi.creatureWindow.returnArtifact.hover" : "Give back artifact",
"vcmi.creatureWindow.returnArtifact.help" : "Use this button to return stack artifact back into hero backpack",
"vcmi.questLog.hideComplete.hover" : "Hide complete quests",
"vcmi.questLog.hideComplete.help" : "Hide all quests that already completed",
"vcmi.randomMapTab.widgets.defaultTemplate" : "default",
"vcmi.randomMapTab.widgets.templateLabel" : "Template",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Setup...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team alignments",
// few strings from WoG used by vcmi
"vcmi.stackExperience.description" : "» S t a c k E x p e r i e n c e D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
"vcmi.stackExperience.rank.1" : "Basic",
"vcmi.stackExperience.rank.2" : "Novice",
"vcmi.stackExperience.rank.3" : "Trained",
"vcmi.stackExperience.rank.4" : "Skilled",
"vcmi.stackExperience.rank.5" : "Proven",
"vcmi.stackExperience.rank.6" : "Veteran",
"vcmi.stackExperience.rank.7" : "Adept",
"vcmi.stackExperience.rank.8" : "Expert",
"vcmi.stackExperience.rank.9" : "Elite",
"vcmi.stackExperience.rank.10" : "Master",
"vcmi.stackExperience.rank.11" : "Ace",
"core.bonus.ADDITIONAL_ATTACK.name": "Double Strike",
"core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice",
"core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations",
"core.bonus.ADDITIONAL_RETALIATION.description": "May Retaliate ${val} extra times",
"core.bonus.AIR_IMMUNITY.name": "Air immunity",
"core.bonus.AIR_IMMUNITY.description": "Immune to all Air school spells",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Attack all around",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Attacks all adjacent enemies",
"core.bonus.BLOCKS_RETALIATION.name": "No retaliation",
"core.bonus.BLOCKS_RETALIATION.description": "Enemy cannot Retaliate",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "No ranged retaliation",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Enemy cannot Retaliate by shooting",
"core.bonus.CATAPULT.name": "Catapult",
"core.bonus.CATAPULT.description": "Attacks siege walls",
"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Additional siege attacks",
"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Can hit siege walls ${val} extra times per attack",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduce Casting Cost (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduces spell cost for hero",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Magic Damper (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Increases Cost of enemy spells",
"core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge",
"core.bonus.CHARGE_IMMUNITY.description": "Immune to Champion charge",
"core.bonus.DARKNESS.name": "Darkness cover",
"core.bonus.DARKNESS.description": "Adds ${val} darkness radius",
"core.bonus.DEATH_STARE.name": "Death Stare (${val}%)",
"core.bonus.DEATH_STARE.description": "${val}% chance to kill single creature",
"core.bonus.DEFENSIVE_STANCE.name": "Defense Bonus",
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Defense when defending",
"core.bonus.DESTRUCTION.name": "Destruction",
"core.bonus.DESTRUCTION.description": "Has ${val}% chance to kill extra units after attack",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Death Blow",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% chance for double damage",
"core.bonus.DRAGON_NATURE.name": "Dragon",
"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direct Damage Immunity",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immune to direct damage spells",
"core.bonus.EARTH_IMMUNITY.name": "Earth immunity",
"core.bonus.EARTH_IMMUNITY.description": "Immune to all Earth school spells",
"core.bonus.ENCHANTER.name": "Enchanter",
"core.bonus.ENCHANTER.description": "Can cast mass ${subtype.spell} every turn",
"core.bonus.ENCHANTED.name": "Enchanted",
"core.bonus.ENCHANTED.description": "Affected by permanent ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignore Defense (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignores part of Defence for the attack",
"core.bonus.FIRE_IMMUNITY.name": "Fire immunity",
"core.bonus.FIRE_IMMUNITY.description": "Immune to all Fire school spells",
"core.bonus.FIRE_SHIELD.name": "Fire Shield (${val}%)",
"core.bonus.FIRE_SHIELD.description": "Reflects part of melee damage",
"core.bonus.FIRST_STRIKE.name": "First Strike",
"core.bonus.FIRST_STRIKE.description": "This creature attacks first instead of retaliating",
"core.bonus.FEAR.name": "Fear",
"core.bonus.FEAR.description": "Causes Fear on an enemy stack",
"core.bonus.FEARLESS.name": "Fearless",
"core.bonus.FEARLESS.description": "Immune to Fear ability",
"core.bonus.FLYING.name": "Fly",
"core.bonus.FLYING.description": "Can Fly (ignores obstacles)",
"core.bonus.FREE_SHOOTING.name": "Shoot Close",
"core.bonus.FREE_SHOOTING.description": "Can shoot in Close Combat",
"core.bonus.FULL_HP_REGENERATION.name": "Regeneration",
"core.bonus.FULL_HP_REGENERATION.description": "May Regenerate to full Health",
"core.bonus.GARGOYLE.name": "Gargoyle",
"core.bonus.GARGOYLE.description": "Cannot be rised or healed",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reduce Damage (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduces physical damage from ranged or melee",
"core.bonus.HATE.name": "Hates ${subtype.creature}",
"core.bonus.HATE.description": "Does ${val}% more damage",
"core.bonus.HEALER.name": "Healer",
"core.bonus.HEALER.description": "Heals allied units",
"core.bonus.HP_REGENERATION.name": "Regeneration",
"core.bonus.HP_REGENERATION.description": "Heals ${val} hit points every round",
"core.bonus.JOUSTING.name": "Champion Charge",
"core.bonus.JOUSTING.description": "+5% damage per hex travelled",
"core.bonus.KING1.name": "King 1",
"core.bonus.KING1.description": "Vulnerable to basic SLAYER",
"core.bonus.KING2.name": "King 2",
"core.bonus.KING2.description": "Vulnerable to advanced SLAYER",
"core.bonus.KING3.name": "King 3",
"core.bonus.KING3.description":"Vulnerable to expert SLAYER",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Spell immunity 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immune to spells of levels 1-${val}",
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Limited shooting range",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "",
"core.bonus.LIFE_DRAIN.name": "Drain life (${val}%)",
"core.bonus.LIFE_DRAIN.description": "Drains ${val}% of damage dealt",
"core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%",
"core.bonus.MANA_CHANNELING.description": "Gives your hero mana spent by enemy",
"core.bonus.MANA_DRAIN.name": "Mana Drain",
"core.bonus.MANA_DRAIN.description": "Drains ${val} mana every turn",
"core.bonus.MAGIC_MIRROR.name": "Magic Mirror (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "${val}% chance to redirects an offensive spell to enemy",
"core.bonus.MAGIC_RESISTANCE.name": "Magic Resistance(${MR}%)",
"core.bonus.MAGIC_RESISTANCE.description": "${MR}% chance to resist enemy spell",
"core.bonus.MIND_IMMUNITY.name": "Mind Spell Immunity",
"core.bonus.MIND_IMMUNITY.description": "Immune to Mind-type spells",
"core.bonus.NO_DISTANCE_PENALTY.name": "No distance penalty",
"core.bonus.NO_DISTANCE_PENALTY.description": "Full damage from any distance",
"core.bonus.NO_MELEE_PENALTY.name": "No melee penalty",
"core.bonus.NO_MELEE_PENALTY.description": "Creature has no Melee Penalty",
"core.bonus.NO_MORALE.name": "Neutral Morale",
"core.bonus.NO_MORALE.description": "Creature is immune to morale effects",
"core.bonus.NO_WALL_PENALTY.name": "No wall penalty",
"core.bonus.NO_WALL_PENALTY.description": "Full damage during siege",
"core.bonus.NON_LIVING.name": "Non living",
"core.bonus.NON_LIVING.description": "Immunity to many effects",
"core.bonus.RANDOM_SPELLCASTER.name": "Random spellcaster",
"core.bonus.RANDOM_SPELLCASTER.description": "Can cast random spell",
"core.bonus.RANGED_RETALIATION.name": "Ranged retaliation",
"core.bonus.RANGED_RETALIATION.description": "Can perform ranged counterattack",
"core.bonus.RECEPTIVE.name": "Receptive",
"core.bonus.RECEPTIVE.description": "No Immunity to Friendly Spells",
"core.bonus.REBIRTH.name": "Rebirth (${val}%)",
"core.bonus.REBIRTH.description": "${val}% of stack will rise after death",
"core.bonus.RETURN_AFTER_STRIKE.name": "Attack and Return",
"core.bonus.RETURN_AFTER_STRIKE.description": "Returns after melee attack",
"core.bonus.SELF_LUCK.name": "Positive luck",
"core.bonus.SELF_LUCK.description": "Always has Positive Luck",
"core.bonus.SELF_MORALE.name": "Positive morale",
"core.bonus.SELF_MORALE.description": "Always has Positive Morale",
"core.bonus.SHOOTER.name": "Ranged",
"core.bonus.SHOOTER.description": "Creature can shoot",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Shoot all around",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "This creature's ranged attacks strike all targets in a small area",
"core.bonus.SOUL_STEAL.name": "Soul Steal",
"core.bonus.SOUL_STEAL.description": "Gains ${val} new creatures for each enemy killed",
"core.bonus.SPELLCASTER.name": "Spellcaster",
"core.bonus.SPELLCASTER.description": "Can cast ${subtype.spell}",
"core.bonus.SPELL_AFTER_ATTACK.name": "Cast After Attack",
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% to cast ${subtype.spell} after attack",
"core.bonus.SPELL_BEFORE_ATTACK.name": "Cast Before Attack",
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% to cast ${subtype.spell} before attack",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Spell Resistance",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Damage from spells reduced ${val}%.",
"core.bonus.SPELL_IMMUNITY.name": "Spell immunity",
"core.bonus.SPELL_IMMUNITY.description": "Immune to ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "Spell-like attack",
"core.bonus.SPELL_LIKE_ATTACK.description": "Attacks with ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura of Resistance",
"core.bonus.SPELL_RESISTANCE_AURA.description": "Nearby stacks get ${val}% resistance",
"core.bonus.SUMMON_GUARDIANS.name": "Summon guardians",
"core.bonus.SUMMON_GUARDIANS.description": "At battle start summons ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "Synergizable",
"core.bonus.SYNERGY_TARGET.description": "This creature is vulnerable to synergy effect",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Breath Attack (2-hex range)",
"core.bonus.THREE_HEADED_ATTACK.name": "Three-headed attack",
"core.bonus.THREE_HEADED_ATTACK.description": "Attacks three adjacent units",
"core.bonus.TRANSMUTATION.name": "Transmutation",
"core.bonus.TRANSMUTATION.description": "${val}% chance to transform attacked unit to other type",
"core.bonus.UNDEAD.name": "Undead",
"core.bonus.UNDEAD.description": "Creature is Undead",
"core.bonus.UNLIMITED_RETALIATIONS.name": "Unlimited retaliations",
"core.bonus.UNLIMITED_RETALIATIONS.description": "Retaliates any number of attacks",
"core.bonus.WATER_IMMUNITY.name": "Water immunity",
"core.bonus.WATER_IMMUNITY.description": "Immune to all Water school spells",
"core.bonus.WIDE_BREATH.name": "Wide breath",
"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)"
}

View File

@ -0,0 +1,222 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Bedrohung: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Mühelos",
"vcmi.adventureMap.monsterThreat.levels.1" : "Sehr schwach",
"vcmi.adventureMap.monsterThreat.levels.2" : "Schwach",
"vcmi.adventureMap.monsterThreat.levels.3" : "Ein bisschen schwächer",
"vcmi.adventureMap.monsterThreat.levels.4" : "Gleichauf",
"vcmi.adventureMap.monsterThreat.levels.5" : "Ein bisschen stärker",
"vcmi.adventureMap.monsterThreat.levels.6" : "Stark",
"vcmi.adventureMap.monsterThreat.levels.7" : "Sehr Stark",
"vcmi.adventureMap.monsterThreat.levels.8" : "Herausfordernd",
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
"vcmi.adventureMap.monsterThreat.levels.10" : "Tötlich",
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich",
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
"vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!",
"vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.",
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
"vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
"vcmi.server.errors.modsIncompatibility" : "Erforderliche Mods um das Spiel zu laden:",
"vcmi.systemOptions.fullscreenButton.hover" : "Vollbild",
"vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\n Wenn ausgewählt wird VCMI im Vollbildmodus laufen, ansonsten im Fenstermodus",
"vcmi.systemOptions.resolutionButton.hover" : "Auflösung",
"vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Ändert die Spielauflösung. Spielneustart ist erforderlich um neue Auflösung zu übernehmen.",
"vcmi.systemOptions.resolutionMenu.hover" : "Wähle Auflösung",
"vcmi.systemOptions.resolutionMenu.help" : "Ändere die Spielauflösung.",
"vcmi.townHall.missingBase" : "Basis Gebäude %s muss als erstes gebaut werden",
"vcmi.townHall.noCreaturesToRecruit" : "Es gibt keine Kreaturen zu rekrutieren!",
"vcmi.townHall.greetingManaVortex" : "Wenn Ihr Euch den %s nähert, wird Euer Körper mit neuer Energie gefüllt. Ihr habt Eure normalen Zauberpunkte verdoppelt.",
"vcmi.townHall.greetingKnowledge" : "Ihr studiert die Glyphen auf dem %s und erhaltet Einblick in die Funktionsweise verschiedener Magie (+1 Wissen).",
"vcmi.townHall.greetingSpellPower" : "Der %s lehrt Euch neue Wege, Eure magischen Kräfte zu bündeln (+1 Kraft).",
"vcmi.townHall.greetingExperience" : "Ein Besuch bei den %s bringt Euch viele neue Fähigkeiten bei (+1000 Erfahrung).",
"vcmi.townHall.greetingAttack" : "Nach einiger Zeit im %s könnt Ihr effizientere Kampffertigkeiten erlernen (+1 Angriffsfertigkeit).",
"vcmi.townHall.greetingDefence" : "Wenn Ihr Zeit im %s verbringt, bringen Euch die erfahrenen Krieger dort zusätzliche Verteidigungsfähigkeiten bei (+1 Verteidigung).",
"vcmi.townHall.hasNotProduced" : "Die %s hat noch nichts produziert.",
"vcmi.townHall.hasProduced" : "Die %s hat diese Woche %d %s produziert.",
"vcmi.townHall.greetingCustomBonus" : "%s gibt Ihnen +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " bis zur nächsten Schlacht.",
"vcmi.townHall.greetingInTownMagicWell" : "%s hat Eure Zauberpunkte wieder auf das Maximum erhöht.",
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
"vcmi.heroWindow.openCommander.hover" : "Öffne Kommandanten-Fenster",
"vcmi.heroWindow.openCommander.help" : "Zeige Informationen über Kommandanten dieses Helden",
"vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?",
"vcmi.creatureWindow.showBonuses.hover" : "Wechsle zur Bonus-Ansicht",
"vcmi.creatureWindow.showBonuses.help" : "Zeige alle aktiven Boni des Kommandanten",
"vcmi.creatureWindow.showSkills.hover" : "Wechsle zur Fertigkeits-Ansicht",
"vcmi.creatureWindow.showSkills.help" : "Zeige alle erlernten Fertigkeiten des Kommandanten",
"vcmi.creatureWindow.returnArtifact.hover" : "Artefekt zurückgeben",
"vcmi.creatureWindow.returnArtifact.help" : "Nutze diese Schaltfläche, um Stapel-Artefakt in den Rucksack des Helden zurückzugeben",
"vcmi.questLog.hideComplete.hover" : "Verstecke abgeschlossene Quests",
"vcmi.questLog.hideComplete.help" : "Verstecke alle Quests die bereits abgeschlossen sind",
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
"core.bonus.ADDITIONAL_RETALIATION.description": "Kann ${val} zusätzliche Male vergelten",
"core.bonus.AIR_IMMUNITY.name": "Luftimmunität",
"core.bonus.AIR_IMMUNITY.description": "Immun gegen alle Luftschulzauber",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Rundum angreifen",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Greift alle benachbarten Gegner an",
"core.bonus.BLOCKS_RETALIATION.name": "Keine Vergeltung",
"core.bonus.BLOCKS_RETALIATION.description": "Feind kann nicht vergelten",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "Keine Reichweitenverschiebung",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Feind kann nicht durch Schießen vergelten",
"core.bonus.CATAPULT.name": "Katapult",
"core.bonus.CATAPULT.description": "Greift Belagerungsmauern an",
"core.bonus.CATAPULT_EXTRA_SHOTS.name": "Zusätzliche Belagerungsangriffe",
"core.bonus.CATAPULT_EXTRA_SHOTS.description": "Kann Belagerungsmauern ${val} zusätzliche Male pro Angriff treffen",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduziere Zauberkosten (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduziert die Zauberkosten für den Helden",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Zauberdämpfer (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Erhöht die Kosten von gegnerischen Zaubern",
"core.bonus.CHARGE_IMMUNITY.name": "Immun gegen Aufladung",
"core.bonus.CHARGE_IMMUNITY.description": "Immune to Champion charge",
"core.bonus.DAEMON_SUMMONING.name": "Beschwörer (${subtype.creature})",
"core.bonus.DAEMON_SUMMONING.description": "Kann Kreaturen aus Leichen auferstehen lassen",
"core.bonus.DARKNESS.name": "Darkness cover",
"core.bonus.DARKNESS.description": "Fügt ${val} Dunkelheitsradius hinzu",
"core.bonus.DEATH_STARE.name": "Todesstarren (${val}%)",
"core.bonus.DEATH_STARE.description": "${val}% Chance, eine einzelne Kreatur zu töten",
"core.bonus.DEFENSIVE_STANCE.name": "Verteidigungsbonus",
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Verteidigung beim Verteidigen",
"core.bonus.DESTRUCTION.name": "Zerstörung",
"core.bonus.DESTRUCTION.description": "Hat ${val}% Chance, zusätzliche Einheiten nach dem Angriff zu töten",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Todesstoß",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% Chance auf doppelten Schaden",
"core.bonus.DRAGON_NATURE.name": "Drache",
"core.bonus.DRAGON_NATURE.description": "Kreatur hat eine Drachennatur",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direkte Schadensimmunität",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immun gegen Direktschadenszauber",
"core.bonus.EARTH_IMMUNITY.name": "Erdimmunität",
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
"core.bonus.ENCHANTER.name": "Verzauberer",
"core.bonus.ENCHANTER.description": "Kann jede Runde eine Masse von ${subtype.spell} zaubern",
"core.bonus.ENCHANTED.name": "Verzaubert",
"core.bonus.ENCHANTED.description": "Beeinflusst von permanentem ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignoriere Verteidigung (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignoriert einen Teil der Verteidigung für den Angriff",
"core.bonus.FIRE_IMMUNITY.name": "Feuerimmunität",
"core.bonus.FIRE_IMMUNITY.description": "Immun gegen alle Zauber der Schule des Feuers",
"core.bonus.FIRE_SHIELD.name": "Feuerschild (${val}%)",
"core.bonus.FIRE_SHIELD.description": "Reflektiert einen Teil des Nahkampfschadens",
"core.bonus.FIRST_STRIKE.name": "Erstschlag",
"core.bonus.FIRST_STRIKE.description": "Diese Kreatur greift zuerst an, anstatt zu vergelten",
"core.bonus.FEAR.name": "Furcht",
"core.bonus.FEAR.description": "Verursacht Furcht bei einem gegnerischen Stapel",
"core.bonus.FEARLESS.name": "Furchtlos",
"core.bonus.FEARLESS.description": "immun gegen die Fähigkeit Furcht",
"core.bonus.FLYING.name": "Fliegen",
"core.bonus.FLYING.description": "Kann fliegen (ignoriert Hindernisse)",
"core.bonus.FREE_SHOOTING.name": "Nah schießen",
"core.bonus.FREE_SHOOTING.description": "Kann im Nahkampf schießen",
"core.bonus.FULL_HP_REGENERATION.name": "Regeneration",
"core.bonus.FULL_HP_REGENERATION.description": "Kann auf volle Lebenspunkte regenerieren",
"core.bonus.GARGOYLE.name": "Gargoyle",
"core.bonus.GARGOYLE.description": "Kann nicht aufgerichtet oder geheilt werden",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Schaden vermindern (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduziert physischen Schaden aus dem Fern- oder Nahkampf",
"core.bonus.HATE.name": "Hasst ${subtype.creature}",
"core.bonus.HATE.description": "Macht ${val}% mehr Schaden",
"core.bonus.HEALER.name": "Heiler",
"core.bonus.HEALER.description": "Heilt verbündete Einheiten",
"core.bonus.HP_REGENERATION.name": "Regeneration",
"core.bonus.HP_REGENERATION.description": "Heilt ${val} Trefferpunkte jede Runde",
"core.bonus.JOUSTING.name": "Champion Charge",
"core.bonus.JOUSTING.description": "+5% Schaden pro zurückgelegtem Feld",
"core.bonus.KING1.name": "König 1",
"core.bonus.KING1.description": "Anfällig für grundlegende SLAYER",
"core.bonus.KING2.name": "König 2",
"core.bonus.KING2.description": "Anfällig für erweiterte SLAYER",
"core.bonus.KING3.name": "König3",
"core.bonus.KING3.description": "Anfällig für Experten-SLAYER",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Zauberimmunität 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immun gegen Zaubersprüche der Stufen 1-${val}",
"core.bonus.LIFE_DRAIN.name": "Leben entziehen (${val}%)",
"core.bonus.LIFE_DRAIN.description": "Drainiert ${val}% des zugefügten Schadens",
"core.bonus.MANA_CHANNELING.name": "Magiekanal ${val}%",
"core.bonus.MANA_CHANNELING.description": "Gibt Ihrem Helden Mana, das vom Gegner ausgegeben wird",
"core.bonus.MANA_DRAIN.name": "Mana-Entzug",
"core.bonus.MANA_DRAIN.description": "Entzieht ${val} Mana jede Runde",
"core.bonus.MAGIC_MIRROR.name": "Zauberspiegel (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "${val}% Chance, einen Angriffszauber auf den Gegner umzulenken",
"core.bonus.MAGIC_RESISTANCE.name": "Magie-Widerstand(${MR}%)",
"core.bonus.MAGIC_RESISTANCE.description": "${MR}% Chance, gegnerischem Zauber zu widerstehen",
"core.bonus.MIND_IMMUNITY.name": "Geist-Zauber-Immunität",
"core.bonus.MIND_IMMUNITY.description": "Immun gegen Zauber vom Typ Geist",
"core.bonus.NO_DISTANCE_PENALTY.name": "Keine Entfernungsstrafe",
"core.bonus.NO_DISTANCE_PENALTY.description": "Voller Schaden aus beliebiger Entfernung",
"core.bonus.NO_MELEE_PENALTY.name": "Keine Nahkampf-Strafe",
"core.bonus.NO_MELEE_PENALTY.description": "Kreatur hat keinen Nahkampf-Malus",
"core.bonus.NO_MORALE.name": "Neutrale Moral",
"core.bonus.NO_MORALE.description": "Kreatur ist immun gegen Moral-Effekte",
"core.bonus.NO_WALL_PENALTY.name": "Keine Wand-Strafe",
"core.bonus.NO_WALL_PENALTY.description": "Voller Schaden bei Belagerung",
"core.bonus.NON_LIVING.name": "Nicht lebend",
"core.bonus.NON_LIVING.description": "Immunität gegen viele Effekte",
"core.bonus.RANDOM_SPELLCASTER.name": "Zufälliger Zauberwirker",
"core.bonus.RANDOM_SPELLCASTER.description": "Kann einen zufälligen Zauberspruch wirken",
"core.bonus.RANGED_RETALIATION.name": "Fernkampf-Vergeltung",
"core.bonus.RANGED_RETALIATION.description": "Kann einen Fernkampf-Gegenangriff durchführen",
"core.bonus.RECEPTIVE.name": "Empfänglich",
"core.bonus.RECEPTIVE.description": "Keine Immunität gegen Freundschaftszauber",
"core.bonus.REBIRTH.name": "Wiedergeburt (${val}%)",
"core.bonus.REBIRTH.description": "${val}% des Stacks wird nach dem Tod auferstehen",
"core.bonus.RETURN_AFTER_STRIKE.name": "Angriff und Rückkehr",
"core.bonus.RETURN_AFTER_STRIKE.description": "Kehrt nach Nahkampfangriff zurück",
"core.bonus.SELF_LUCK.name": "Positives Glück",
"core.bonus.SELF_LUCK.description": "Hat immer positives Glück",
"core.bonus.SELF_MORALE.name": "Positive Moral",
"core.bonus.SELF_MORALE.description": "Hat immer positive Moral",
"core.bonus.SHOOTER.name": "Fernkämpfer",
"core.bonus.SHOOTER.description": "Kreatur kann schießen",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Schießt rundherum",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Die Fernkampfangriffe dieser Kreatur treffen alle Ziele in einem kleinen Bereich",
"core.bonus.SOUL_STEAL.name": "Seelenraub",
"core.bonus.SOUL_STEAL.description": "Gewinnt ${val} neue Kreaturen für jeden getöteten Gegner",
"core.bonus.SPELLCASTER.name": "Zauberer",
"core.bonus.SPELLCASTER.description": "Kann ${subtype.spell} zaubern",
"core.bonus.SPELL_AFTER_ATTACK.name": "Nach Angriff zaubern",
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}%, um ${subtype.spell} nach dem Angriff zu wirken",
"core.bonus.SPELL_BEFORE_ATTACK.name": "Zauber vor Angriff",
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% um ${subtype.spell} vor dem Angriff zu wirken",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Schaden von Zaubern reduziert ${val}%.",
"core.bonus.SPELL_IMMUNITY.name": "Zauberimmunität",
"core.bonus.SPELL_IMMUNITY.description": "Immun gegen ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "zauberähnlicher Angriff",
"core.bonus.SPELL_LIKE_ATTACK.description": "Angriffe mit ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura des Widerstands",
"core.bonus.SPELL_RESISTANCE_AURA.description": "Stapel in der Nähe erhalten ${val}% Widerstand",
"core.bonus.SUMMON_GUARDIANS.name": "Wächter beschwören",
"core.bonus.SUMMON_GUARDIANS.description": "Beschwört bei Kampfbeginn ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "Synergierbar",
"core.bonus.SYNERGY_TARGET.description": "Diese Kreatur ist anfällig für Synergieeffekte",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Atem-Angriff (2-Hex-Bereich)",
"core.bonus.THREE_HEADED_ATTACK.name": "Dreiköpfiger Angriff",
"core.bonus.THREE_HEADED_ATTACK.description": "Greift drei benachbarte Einheiten an",
"core.bonus.TRANSMUTATION.name": "Transmutation",
"core.bonus.TRANSMUTATION.description": "${val}% Chance, angegriffene Einheit in einen anderen Typ zu verwandeln",
"core.bonus.UNDEAD.name": "Untot",
"core.bonus.UNDEAD.description": "Kreatur ist untot",
"core.bonus.UNLIMITED_RETALIATIONS.name": "Unbegrenzte Vergeltungsmaßnahmen",
"core.bonus.UNLIMITED_RETALIATIONS.description": "Vergeltungen für eine beliebige Anzahl von Angriffen",
"core.bonus.WATER_IMMUNITY.name": "Wasser-Immunität",
"core.bonus.WATER_IMMUNITY.description": "Immun gegen alle Zauber der Wasserschule",
"core.bonus.WIDE_BREATH.name": "Breiter Atem",
"core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)"
}

View File

@ -0,0 +1,70 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Poziom zagrożenia: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Zerowy",
"vcmi.adventureMap.monsterThreat.levels.1" : "Bardzo słaby",
"vcmi.adventureMap.monsterThreat.levels.2" : "Słaby",
"vcmi.adventureMap.monsterThreat.levels.3" : "Nieco słabszy",
"vcmi.adventureMap.monsterThreat.levels.4" : "Równie silny",
"vcmi.adventureMap.monsterThreat.levels.5" : "Nieco silniejszy",
"vcmi.adventureMap.monsterThreat.levels.6" : "Silny",
"vcmi.adventureMap.monsterThreat.levels.7" : "Bardzo silny",
"vcmi.adventureMap.monsterThreat.levels.8" : "Wyzwanie",
"vcmi.adventureMap.monsterThreat.levels.9" : "Przytłaczający",
"vcmi.adventureMap.monsterThreat.levels.10" : "Śmiertelny",
"vcmi.adventureMap.monsterThreat.levels.11" : "Nie do pokonania",
"vcmi.adventureMap.confirmRestartGame" : "Czy na pewno chcesz zrestartować grę?",
"vcmi.adventureMap.noTownWithMarket" : "Brak dostępnego targowiska!",
"vcmi.adventureMap.noTownWithTavern" : "Brak dostępnego miasta z karczmą!",
"vcmi.adventureMap.spellUnknownProblem" : "Nieznany problem z zaklęciem, brak dodatkowych informacji.",
"vcmi.adventureMap.playerAttacked" : "Gracz został zaatakowany: %s",
"vcmi.server.errors.existingProcess" : "Inny proces vcmiserver został już uruchomiony, zakończ go nim przejdziesz dalej",
"vcmi.server.errors.modsIncompatibility" : "Mody wymagane do wczytania gry:",
"vcmi.server.confirmReconnect" : "Połączyć z ostatnią sesją?",
"vcmi.systemOptions.fullscreenButton.hover" : "Pełny ekran",
"vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\n Po wybraniu VCMI uruchomi się w trybie pełnoekranowym, w przeciwnym wypadku uruchomi się w oknie",
"vcmi.systemOptions.resolutionButton.hover" : "Rozdzielczość",
"vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Zmień rozdzielczość ekranu w grze. Restart gry jest wymagany, by zmiany zostały uwzględnione.",
"vcmi.systemOptions.resolutionMenu.hover" : "Wybierz rozdzielczość",
"vcmi.systemOptions.resolutionMenu.help" : "Zmień rozdzielczość ekranu w grze.",
"vcmi.townHall.missingBase" : "Podstawowy budynek %s musi zostać najpierw wybudowany",
"vcmi.townHall.noCreaturesToRecruit" : "Brak stworzeń do rekrutacji!",
"vcmi.townHall.greetingManaVortex" : "Zbliżając się do %s czujesz jak twoje ciało wypełnia energia. Ilość pkt. magii, które posiadasz, zwiększa się dwukrotnie.",
"vcmi.townHall.greetingKnowledge" : "Studiując napisy na %s odkrywasz nowe aspekty stosowania magii (wiedza +1).",
"vcmi.townHall.greetingSpellPower" : "Odwiedzając %s dowiadujesz się, jak zwiększyć potęgę swojej mocy magicznej (moc +1).",
"vcmi.townHall.greetingExperience" : "Wizyta w %s zwiększa twoje doświadczenie (doświadczenie +1000).",
"vcmi.townHall.greetingAttack" : "Krótka wizyka w %s umożliwia ci polepszenie technik walki (atak +1).",
"vcmi.townHall.greetingDefence" : "Odwiedzasz %s. Doświadczeni żołnierze, którzy tam przebywają, uczą cię sztuki skutecznej obrony (obrona +1).",
"vcmi.townHall.hasNotProduced" : "%s nic jeszcze nie wyprodukował.",
"vcmi.townHall.hasProduced" : "%s wyprodukował w tym tygodniu: %d %s.",
"vcmi.townHall.greetingCustomBonus" : "%s daje tobie +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " do następnej bitwy.",
"vcmi.townHall.greetingInTownMagicWell" : "%s przywraca ci wszystkie punkty magii.",
"vcmi.logicalExpressions.anyOf" : "Dowolne spośród:",
"vcmi.logicalExpressions.allOf" : "Wszystkie spośród:",
"vcmi.logicalExpressions.noneOf" : "Żadne spośród:",
"vcmi.heroWindow.openCommander.hover" : "Otwórz okno dowódcy",
"vcmi.heroWindow.openCommander.help" : "Wyświetla informacje o dowódcy przynależącym do tego bohatera",
"vcmi.commanderWindow.artifactMessage" : "Czy chcesz zwrócić ten artefakt bohaterowi?",
"vcmi.creatureWindow.showBonuses.hover" : "Przełącz do widoku bonusów",
"vcmi.creatureWindow.showBonuses.help" : "Wyświetla wszystkie aktywne bonusy dowódcy",
"vcmi.creatureWindow.showSkills.hover" : "Przełącz do widoku umiejętności",
"vcmi.creatureWindow.showSkills.help" : "Wyświetla wszystkie nauczone umiejętności dowódcy",
"vcmi.creatureWindow.returnArtifact.hover" : "Zwróć artefakt",
"vcmi.creatureWindow.returnArtifact.help" : "Użyj tego przycisku by zwrócić artefakt do sakwy bohatera",
"vcmi.questLog.hideComplete.hover" : "Ukryj ukończone misje",
"vcmi.questLog.hideComplete.help" : "Ukrywa wszystkie misje, które zostały zakończone",
"vcmi.randomMapTab.widgets.defaultTemplate" : "domyślny",
"vcmi.randomMapTab.widgets.templateLabel" : "Szablon",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Ustaw...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Sojusze"
}

View File

@ -0,0 +1,225 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n Сила загону: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Безсилий",
"vcmi.adventureMap.monsterThreat.levels.1" : "Дуже слабкий",
"vcmi.adventureMap.monsterThreat.levels.2" : "Слабкий",
"vcmi.adventureMap.monsterThreat.levels.3" : "Трохи слабша",
"vcmi.adventureMap.monsterThreat.levels.4" : "Відповідна",
"vcmi.adventureMap.monsterThreat.levels.5" : "Трохи сильніша",
"vcmi.adventureMap.monsterThreat.levels.6" : "Сильніша",
"vcmi.adventureMap.monsterThreat.levels.7" : "Дуже сильна",
"vcmi.adventureMap.monsterThreat.levels.8" : "Кидає виклик",
"vcmi.adventureMap.monsterThreat.levels.9" : "Нездоланна",
"vcmi.adventureMap.monsterThreat.levels.10" : "Смертельна",
"vcmi.adventureMap.monsterThreat.levels.11" : "Неможлива",
"vcmi.adventureMap.confirmRestartGame" : "Ви впевнені, що хочете перезапустити гру?",
"vcmi.adventureMap.noTownWithMarket" : "Немає доступних ринків!",
"vcmi.adventureMap.noTownWithTavern" : "Немає доступного міста з таверною!",
"vcmi.adventureMap.spellUnknownProblem" : "Невідома проблема з цим заклинанням, більше інформації немає.",
"vcmi.adventureMap.playerAttacked" : "Гравця атаковано: %s",
"vcmi.server.errors.existingProcess" : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його",
"vcmi.server.errors.modsIncompatibility" : "Потрібні модифікації для завантаження гри:",
"vcmi.server.confirmReconnect" : "Підключитися до минулої сесії?",
"vcmi.systemOptions.fullscreenButton.hover" : "Режим на весь екран",
"vcmi.systemOptions.fullscreenButton.help" : "{Режим на весь екран}\n\n Якщо обрано, VCMI буде запускатися в режимі на весь екран, інакше — віконний режим",
"vcmi.systemOptions.resolutionButton.hover" : "Розширення екрану",
"vcmi.systemOptions.resolutionButton.help" : "{Розширення екрану}\n\n Зміна розширення екрану в грі. Аби зміни набули чинності необхідно перезавантажити гру.",
"vcmi.systemOptions.resolutionMenu.hover" : "Обрати розширення екрану",
"vcmi.systemOptions.resolutionMenu.help" : "Змінити розширення екрану в грі.",
"vcmi.townHall.missingBase" : "Спочатку необхідно звести початкову будівлю: %s",
"vcmi.townHall.noCreaturesToRecruit" : "Немає істот, яких можна завербувати!",
"vcmi.townHall.greetingManaVortex" : "Неподалік %s ваше тіло наповнюється новою силою. Ваша звична магічна енергія сьогодні подвоєна.",
"vcmi.townHall.greetingKnowledge" : "Ви вивчили знаки на %s, і на вас зійшло прозріння у справах магії. (+1 Knowledge).",
"vcmi.townHall.greetingSpellPower" : "В %s вас навчили новим методам концентрації магічної сили. (+1 Power).",
"vcmi.townHall.greetingExperience" : "Відвідавши %s, ви дізналися багато нового. (+1000 Experience).",
"vcmi.townHall.greetingAttack" : "Перебування у %s дозволило вам краще використовувати бойові навички (+1 Attack Skill).",
"vcmi.townHall.greetingDefence" : "У %s досвідчені воїни виклали вам свої захисні вміння. (+1 Defense).",
"vcmi.townHall.hasNotProduced" : "Поки що %s нічого не створило.",
"vcmi.townHall.hasProduced" : "Цього тижня %s створило %d одиниць, цього разу це %s.",
"vcmi.townHall.greetingCustomBonus" : "%s дає вам +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " до наступної битви.",
"vcmi.townHall.greetingInTownMagicWell" : "%s повністю відновлює ваш запас очків магії.",
"vcmi.logicalExpressions.anyOf" : "Будь-що з перерахованого:",
"vcmi.logicalExpressions.allOf" : "Все з перерахованого:",
"vcmi.logicalExpressions.noneOf" : "Нічого з перерахованого:",
"vcmi.heroWindow.openCommander.hover" : "Відкрити вікно командира",
"vcmi.heroWindow.openCommander.help" : "Показує інформацію про командира героя",
"vcmi.commanderWindow.artifactMessage" : "Бажаєте передати цей артефакт герою?",
"vcmi.creatureWindow.showBonuses.hover" : "Перейти до перегляду бонусів",
"vcmi.creatureWindow.showBonuses.help" : "Відображає всі активні бонуси командира",
"vcmi.creatureWindow.showSkills.hover" : "Перейдіть до перегляду вмінь",
"vcmi.creatureWindow.showSkills.help" : "Відображає всі вивчені командиром вміння",
"vcmi.creatureWindow.returnArtifact.hover" : "Повернути артефакт",
"vcmi.creatureWindow.returnArtifact.help" : "Використовуйте цю кнопку, щоб повернути артефакт загону назад у рюкзак героя",
"vcmi.questLog.hideComplete.hover" : "Приховати завершені квести",
"vcmi.questLog.hideComplete.help" : "Приховує всі квести, які вже мають стан виконаних",
"vcmi.randomMapTab.widgets.defaultTemplate" : "за замовчуванням",
"vcmi.randomMapTab.widgets.templateLabel" : "Шаблон",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Налаштувати...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Розподіл команд",
"core.bonus.ADDITIONAL_ATTACK.name" : "Подвійний удар",
"core.bonus.ADDITIONAL_ATTACK.description" : "Атакує двічі",
"core.bonus.ADDITIONAL_RETALIATION.name" : "Додаткові відплати",
"core.bonus.ADDITIONAL_RETALIATION.description" : "Може нанести ${val} додаткових ударів у відповідь",
"core.bonus.AIR_IMMUNITY.name" : "Імунітет до повітря",
"core.bonus.AIR_IMMUNITY.description" : "Імунітет до всіх заклять школи повітря",
"core.bonus.ATTACKS_ALL_ADJACENT.name" : "Атакує всіх навколо",
"core.bonus.ATTACKS_ALL_ADJACENT.description" : "Атакує всіх сусідніх ворогів",
"core.bonus.BLOCKS_RETALIATION.name" : "Ворог не відповідає",
"core.bonus.BLOCKS_RETALIATION.description" : "Ворог не може атакувати у відповідь",
"core.bonus.BLOCKS_RANGED_RETALIATION.name" : "Немає дальнього удару у відповідь",
"core.bonus.BLOCKS_RANGED_RETALIATION.description" : "Ворог не може відповісти пострілом",
"core.bonus.CATAPULT.name" : "Катапульта",
"core.bonus.CATAPULT.description" : "Атакує стіни фортеці",
"core.bonus.CATAPULT_EXTRA_SHOTS.name" : "Додаткові атаки стін",
"core.bonus.CATAPULT_EXTRA_SHOTS.description" : "Може вражати стіни фортеці ${val} додаткових разів за атаку",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name" : "Зменшує вартість закляття (${value})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description" : "Зменшує вартість закляття для героя",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name" : "Демпфер магії (${value})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description" : "Збільшує вартість ворожих заклять",
"core.bonus.CHARGE_IMMUNITY.name" : "Імунітет до атак з розгону",
"core.bonus.CHARGE_IMMUNITY.description" : "Імунітет до турнірної переваги",
"core.bonus.DARKNESS.name" : "Покриття темряви",
"core.bonus.DARKNESS.description" : "Додає ${val} радіусу темряви",
"core.bonus.DEATH_STARE.name" : "Погляд смерті (${val}%)",
"core.bonus.DEATH_STARE.description" : "${val}% шанс вбити одну істоту",
"core.bonus.DEFENSIVE_STANCE.name" : "Бонус до захисту",
"core.bonus.DEFENSIVE_STANCE.description" : "+${val} Захист при обороні",
"core.bonus.DESTRUCTION.name" : "Знищення",
"core.bonus.DESTRUCTION.description" : "Має ${val}% шанс вбити додаткових юнітів після атаки",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name" : "Смертельний удар",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "${val}% шанс нанести подвійний шкоди",
"core.bonus.DRAGON_NATURE.name" : "Дракон",
"core.bonus.DRAGON_NATURE.description" : "Істота має драконячу природу",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name" : "Імунітет до прямої шкоди",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description" : "Імунітет до заклять, що завдають прямої шкоди",
"core.bonus.EARTH_IMMUNITY.name" : "Імунітет Землі",
"core.bonus.EARTH_IMMUNITY.description" : "Імунітет до всіх заклять школи Землі",
"core.bonus.ENCHANTER.name" : "Чарівник",
"core.bonus.ENCHANTER.description" : "Може використовувати масове закляття ${subtype.spell} кожного ходу",
"core.bonus.ENCHANTED.name" : "Зачарований",
"core.bonus.ENCHANTED.description" : "Піддається впливу постійних закляття ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name" : "Ігнорує ${val}% захисту",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "Ігнорує частину захисту для атаки",
"core.bonus.FIRE_IMMUNITY.name" : "Імунітет до вогню",
"core.bonus.FIRE_IMMUNITY.description" : "Імунітет до всіх заклять школи вогню",
"core.bonus.FIRE_SHIELD.name" : "Вогняний щит (${value}%)",
"core.bonus.FIRE_SHIELD.description" : "Повертає частину шкоди ближнього бою тому, хто їх завдав",
"core.bonus.FIRST_STRIKE.name" : "Перший удар",
"core.bonus.FIRST_STRIKE.description" : "Цей загін атакує першим замість того, щоб відповідати",
"core.bonus.FEAR.name" : "Страх",
"core.bonus.FEAR.description" : "Спричиняє страх у загоні ворога",
"core.bonus.FEARLESS.name" : "Безстрашний",
"core.bonus.FEARLESS.description" : "Імунітет до страху",
"core.bonus.FLYING.name" : "Літає",
"core.bonus.FLYING.description" : "Може літати (ігнорує перешкоди)",
"core.bonus.FREE_SHOOTING.name" : "Стріляє впритул",
"core.bonus.FREE_SHOOTING.description" : "Може стріляти в ближньому бою",
"core.bonus.FULL_HP_REGENERATION.name" : "Регенерація",
"core.bonus.FULL_HP_REGENERATION.description" : "Може регенерувати до повного здоров'я",
"core.bonus.GARGOYLE.name" : "Горгулья",
"core.bonus.GARGOYLE.description" : "Не може бути відроджена або зцілена",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name" : "Зменшує шкоду (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description" : "Зменшує фізичний урон від ударів з дальньої та ближньої дистанції",
"core.bonus.HATE.name" : "Ненавидить ${subtype.creature}",
"core.bonus.HATE.description" : "Завдає на ${val}% більше шкоди",
"core.bonus.HEALER.name" : "Цілитель",
"core.bonus.HEALER.description" : "Лікує союзників",
"core.bonus.HP_REGENERATION.name" : "Регенерація",
"core.bonus.HP_REGENERATION.description" : "Відновлює ${val} очок здоров'я кожного раунду",
"core.bonus.JOUSTING.name" : "Турнірна перевага",
"core.bonus.JOUSTING.description" : "+5% шкоди за кожен пройдений гекс",
"core.bonus.KING1.name" : "Король 1",
"core.bonus.KING1.description" : "Вразливий до 1-го рівня закляття Вбивця",
"core.bonus.KING2.name" : "Король 2",
"core.bonus.KING2.description" : "Вразливий до 2-го рівня закляття Вбивця",
"core.bonus.KING3.name" : "Король 3",
"core.bonus.KING3.description" : "Вразливий до 3-го рівня закляття Вбивця",
"core.bonus.LEVEL_SPELL_IMMUNITY.name" : "Імунітет до заклять 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description" : "Імунітет до заклять рівнів 1-${val}",
"core.bonus.LIFE_DRAIN.name" : "Висмоктує життя (${val}%)",
"core.bonus.LIFE_DRAIN.description" : "Висмоктує ${val}% від завданої шкоди",
"core.bonus.MANA_CHANNELING.name" : "Магічний канал ${val}%",
"core.bonus.MANA_CHANNELING.description" : "Повертає вашому герою ману, витрачену ворогом",
"core.bonus.MANA_DRAIN.name" : "Викрадання мани",
"core.bonus.MANA_DRAIN.description" : "Викрадає ${val} мани кожного ходу",
"core.bonus.MAGIC_MIRROR.name" : "Магічне дзеркало (${val}%)",
"core.bonus.MAGIC_MIRROR.description" : "Відбиває ворожі заклинання до випадкової істоти ворога з силою в ${val}%",
"core.bonus.MAGIC_RESISTANCE.name" : "Опір магії (${MR}%)",
"core.bonus.MAGIC_RESISTANCE.description" : "${MR}% шанс протистояти ворожим закляттям",
"core.bonus.MIND_IMMUNITY.name" : "Імунітет до заклять розуму",
"core.bonus.MIND_IMMUNITY.description" : "Імунітет до заклять типу ",
"core.bonus.NO_DISTANCE_PENALTY.name" : "Немає штрафу за відстань",
"core.bonus.NO_DISTANCE_PENALTY.description" : "Повна шкода з будь-якої відстані",
"core.bonus.NO_MELEE_PENALTY.name" : "Немає штрафу за ближній бій",
"core.bonus.NO_MELEE_PENALTY.description" : "Загін не має штрафу за ближній бій",
"core.bonus.NO_MORALE.name" : "Нейтральний бойовий дух",
"core.bonus.NO_MORALE.description" : "Загін має імунітет до ефектів моралі",
"core.bonus.NO_WALL_PENALTY.name" : "Немає штрафу за перешкоди",
"core.bonus.NO_WALL_PENALTY.description" : "Повна шкода при пострілах через стіни",
"core.bonus.NON_LIVING.name" : "Не жива",
"core.bonus.NON_LIVING.description" : "Імунітет до багатьох ефектів",
"core.bonus.RANDOM_SPELLCASTER.name" : "Випадковий заклинатель",
"core.bonus.RANDOM_SPELLCASTER.description" : "Може накласти випадкове закляття",
"core.bonus.RANGED_RETALIATION.name" : "Дистанційне відплата",
"core.bonus.RANGED_RETALIATION.description" : "Може наносити контратаку пострілом",
"core.bonus.RECEPTIVE.name" : "Сприйнятливий",
"core.bonus.RECEPTIVE.description" : "Не має імунітету до дружніх заклять",
"core.bonus.REBIRTH.name" : "Відродження (${val}%)",
"core.bonus.REBIRTH.description" : "${val}% загону відродиться після смерті",
"core.bonus.RETURN_AFTER_STRIKE.name" : "Атакує і повертається",
"core.bonus.RETURN_AFTER_STRIKE.description" : "Повертається після атаки ближнього бою",
"core.bonus.SELF_LUCK.name" : "Позитивна удача",
"core.bonus.SELF_LUCK.description" : "Завжди має позитивну удачу",
"core.bonus.SELF_MORALE.name" : "Позитивний бойовий дух",
"core.bonus.SELF_MORALE.description" : "Завжди має позитивний бойовий дух",
"core.bonus.SHOOTER.name" : "Стрілок",
"core.bonus.SHOOTER.description" : "Істота може стріляти",
"core.bonus.SHOOTS_ALL_ADJACENT.name" : "Стріляйте по площі",
"core.bonus.SHOOTS_ALL_ADJACENT.description" : "Дистанційні атаки цієї істоти вражають всі цілі на невеликій площі",
"core.bonus.SOUL_STEAL.name" : "Викрадення душ",
"core.bonus.SOUL_STEAL.description" : "Отримує ${val} нових істот за кожного вбитого ворога",
"core.bonus.SPELLCASTER.name" : "Заклинатель",
"core.bonus.SPELLCASTER.description" : "Може використовувати закляття ${subtype.spell}",
"core.bonus.SPELL_AFTER_ATTACK.name" : "Після атаки",
"core.bonus.SPELL_AFTER_ATTACK.description" : "${val}%, щоб застосувати ${subtype.spell} після атаки",
"core.bonus.SPELL_BEFORE_ATTACK.name" : "закляття перед атакою",
"core.bonus.SPELL_BEFORE_ATTACK.description" : "Застосовує ${subtype.spell} з вірогідністю ${value}% перед атакою",
"core.bonus.SPELL_DAMAGE_REDUCTION.name" : "Стійкість до заклять",
"core.bonus.SPELL_DAMAGE_REDUCTION.description" : "Шкода від заклять зменшується на ${val}%.",
"core.bonus.SPELL_IMMUNITY.name" : "Імунітет до заклять",
"core.bonus.SPELL_IMMUNITY.description" : "Імунітет до ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name" : "Атака, схожа на закляття",
"core.bonus.SPELL_LIKE_ATTACK.description" : "Атакує за допомогою ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name" : "Аура стійкості",
"core.bonus.SPELL_RESISTANCE_AURA.description" : "Поруч розташовані стеки отримують ${val}% опору",
"core.bonus.SUMMON_GUARDIANS.name" : "Закликати охоронців",
"core.bonus.SUMMON_GUARDIANS.description" : "На початку бою викликає ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name" : "Синергізм",
"core.bonus.SYNERGY_TARGET.description" : "Ця істота вразлива до ефекту синергії",
"core.bonus.TWO_HEX_ATTACK_BREATH.name" : "Подих",
"core.bonus.TWO_HEX_ATTACK_BREATH.description" : "Атакує додаткову ціль позаду",
"core.bonus.THREE_HEADED_ATTACK.name" : "Триголова атака",
"core.bonus.THREE_HEADED_ATTACK.description" : "Атакує до трьох сусідніх загонів",
"core.bonus.TRANSMUTATION.name" : "Трансмутація",
"core.bonus.TRANSMUTATION.description" : "${val}% шанс перетворити атакованого юніта в інший тип",
"core.bonus.UNDEAD.name" : "Нежить",
"core.bonus.UNDEAD.description" : "Істота є нежить",
"core.bonus.UNLIMITED_RETALIATIONS.name" : "Необмежена кількість ударів у відповідь",
"core.bonus.UNLIMITED_RETALIATIONS.description" : "Відбиває будь-яку кількість атак",
"core.bonus.WATER_IMMUNITY.name" : "Імунітет до води",
"core.bonus.WATER_IMMUNITY.description" : "Імунітет до всіх заклять школи Води",
"core.bonus.WIDE_BREATH.name" : "Широкий подих",
"core.bonus.WIDE_BREATH.description" : "Атака широким подихом",
}

View File

@ -7,6 +7,21 @@
"description" : "Grundlegende Dateien, die für die korrekte Ausführung von VCMI erforderlich sind",
"author" : "VCMI-Team",
"modType" : "Grafik",
"translations" : [
"config/vcmi/german.json"
]
},
"polish" : {
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"author" : "VCMI Team",
"modType" : "Graphical",
"translations" : [
"config/vcmi/polish.json"
]
},
"ukrainian" : {
@ -14,15 +29,23 @@
"description" : "Ключові файли необхідні для повноцінної роботи VCMI",
"author" : "Команда VCMI",
"modType" : "Графіка",
"translations" : [
"config/vcmi/ukrainian.json"
]
},
"version" : "1.1",
"version" : "1.1.1",
"author" : "VCMI Team",
"contact" : "http://forum.vcmi.eu/index.php",
"modType" : "Graphical",
"factions" : [ "config/vcmi/towerFactions" ],
"creatures" : [ "config/vcmi/towerCreature" ],
"translations" : [
"config/vcmi/english.json"
],
"filesystem":
{

View File

@ -30,7 +30,7 @@ Platform support is constantly tested by continuous integration and CMake config
* (optional) All platforms: [using Conan package manager to obtain prebuilt dependencies](docs/conan.md)
* [On Linux](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux))
* [On Linux for Windows with MXE](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux/Cmake/MXE))
* [On Linux for Windows with Conan and mingw](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux/Cmake/Conan))
* [On macOS](https://wiki.vcmi.eu/How_to_build_VCMI_(macOS))
* [On Windows using MSVC and Vcpkg](https://wiki.vcmi.eu/How_to_build_VCMI_(Windows/Vcpkg))
* [iOS on macOS](https://wiki.vcmi.eu/How_to_build_VCMI_(iOS))
@ -40,4 +40,4 @@ Platform support is constantly tested by continuous integration and CMake config
VCMI Project source code is licensed under GPL version 2 or later.
VCMI Project assets are licensed under CC-BY-SA 4.0. Assets sources and information about contributors are available under following link: [https://github.com/vcmi/vcmi-assets]
Copyright (C) 2007-2022 VCMI Team (check AUTHORS file for the contributors list)
Copyright (C) 2007-2023 VCMI Team (check AUTHORS file for the contributors list)

View File

@ -11,48 +11,43 @@
//
#include "StdInc.h"
#include <boost/program_options.hpp>
#include "gui/SDL_Extensions.h"
#include "CGameInfo.h"
#include "mapHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileStream.h"
#include "mainmenu/CMainMenu.h"
#include "lobby/CSelectionBase.h"
#include "windows/CCastleInterface.h"
#include "../lib/CConsoleHandler.h"
#include "gui/CursorHandler.h"
#include "../lib/CGameState.h"
#include "../CCallback.h"
#include "CPlayerInterface.h"
#include "windows/CAdvmapInterface.h"
#include "../lib/CBuildingHandler.h"
#include "CVideoHandler.h"
#include "CMusicHandler.h"
#include "Client.h"
#include "gui/CGuiHandler.h"
#include "CServerHandler.h"
#include "gui/NotificationHandler.h"
#include "ClientCommandManager.h"
#include "windows/CMessage.h"
#include "renderSDL/SDL_Extensions.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileStream.h"
#include "../lib/CConsoleHandler.h"
#include "../lib/CGameState.h"
#include "../lib/CBuildingHandler.h"
#include "../CCallback.h"
#include "../lib/CHeroHandler.h"
#include "../lib/spells/CSpellHandler.h"
#include "CMusicHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "Graphics.h"
#include "Client.h"
#include "../lib/serializer/BinaryDeserializer.h"
#include "../lib/serializer/BinarySerializer.h"
#include "../lib/VCMIDirs.h"
#include "../lib/NetPacks.h"
#include "CMessage.h"
#include "../lib/CModHandler.h"
#include "../lib/CTownHandler.h"
#include "gui/CGuiHandler.h"
#include "../lib/logging/CBasicLogConfigurator.h"
#include "../lib/CPlayerState.h"
#include "gui/CAnimation.h"
#include "../lib/serializer/Connection.h"
#include "CServerHandler.h"
#include "gui/NotificationHandler.h"
#include "ClientCommandManager.h"
#include <boost/asio.hpp>
#include <boost/program_options.hpp>
#include "mainmenu/CPrologEpilogVideo.h"
#include <vstd/StringUtils.h>
@ -376,7 +371,8 @@ int main(int argc, char * argv[])
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
#endif // VCMI_ANDROID
GH.mainFPSmng->init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management
//(!)init here AFTER SDL_Init() while using SDL for FPS management
GH.init();
SDL_LogSetOutputFunction(&SDLLogCallback, nullptr);
@ -432,11 +428,20 @@ int main(int argc, char * argv[])
CCS->musich->setVolume((ui32)settings["general"]["music"].Float());
logGlobal->info("Initializing screen and sound handling: %d ms", pomtime.getDiff());
}
#ifdef VCMI_MAC
// Ctrl+click should be treated as a right click on Mac OS X
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
#endif
#ifdef SDL_HINT_MOUSE_TOUCH_EVENTS
if(GH.isPointerRelativeMode)
{
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
}
#endif
#ifndef VCMI_NO_THREADED_LOAD
//we can properly play intro only in the main thread, so we have to move loading to the separate thread
boost::thread loading(init);
@ -703,7 +708,7 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
if(nullptr == mainWindow)
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
auto createWindow = [displayIndex](Uint32 extraFlags) -> bool {
auto createWindow = [displayIndex](uint32_t extraFlags) -> bool {
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN | extraFlags);
return mainWindow != nullptr;
};
@ -713,7 +718,7 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
SDL_SetHint(SDL_HINT_RETURN_KEY_HIDES_IME, "1");
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
Uint32 windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI;
uint32_t windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI;
if(!createWindow(windowFlags | SDL_WINDOW_METAL))
{
logGlobal->warn("Metal unavailable, using OpenGLES");

View File

@ -1,174 +1,215 @@
set(client_SRCS
StdInc.cpp
../CCallback.cpp
StdInc.cpp
../CCallback.cpp
battle/BattleActionsController.cpp
battle/BattleAnimationClasses.cpp
battle/BattleEffectsController.cpp
battle/BattleFieldController.cpp
battle/BattleInterfaceClasses.cpp
battle/BattleInterface.cpp
battle/BattleObstacleController.cpp
battle/BattleProjectileController.cpp
battle/BattleRenderer.cpp
battle/BattleSiegeController.cpp
battle/BattleStacksController.cpp
battle/BattleWindow.cpp
battle/CreatureAnimation.cpp
adventureMap/CAdvMapPanel.cpp
adventureMap/CAdvMapInt.cpp
adventureMap/CAdventureOptions.cpp
adventureMap/CInGameConsole.cpp
adventureMap/CInfoBar.cpp
adventureMap/CList.cpp
adventureMap/CMinimap.cpp
adventureMap/CResDataBar.cpp
adventureMap/CTerrainRect.cpp
adventureMap/mapHandler.cpp
gui/CAnimation.cpp
gui/Canvas.cpp
gui/CursorHandler.cpp
gui/CGuiHandler.cpp
gui/CIntObject.cpp
gui/ColorFilter.cpp
gui/Fonts.cpp
gui/SDL_Extensions.cpp
gui/NotificationHandler.cpp
gui/InterfaceObjectConfigurable.cpp
battle/BattleActionsController.cpp
battle/BattleAnimationClasses.cpp
battle/BattleEffectsController.cpp
battle/BattleFieldController.cpp
battle/BattleInterface.cpp
battle/BattleInterfaceClasses.cpp
battle/BattleObstacleController.cpp
battle/BattleProjectileController.cpp
battle/BattleRenderer.cpp
battle/BattleSiegeController.cpp
battle/BattleStacksController.cpp
battle/BattleWindow.cpp
battle/CreatureAnimation.cpp
widgets/AdventureMapClasses.cpp
widgets/Buttons.cpp
widgets/CArtifactHolder.cpp
widgets/CComponent.cpp
widgets/CGarrisonInt.cpp
widgets/Images.cpp
widgets/MiscWidgets.cpp
widgets/ObjectLists.cpp
widgets/TextControls.cpp
gui/CGuiHandler.cpp
gui/CIntObject.cpp
gui/CursorHandler.cpp
gui/InterfaceObjectConfigurable.cpp
gui/NotificationHandler.cpp
windows/CAdvmapInterface.cpp
windows/CCastleInterface.cpp
windows/CCreatureWindow.cpp
windows/CreaturePurchaseCard.cpp
windows/CHeroWindow.cpp
windows/CKingdomInterface.cpp
windows/CQuestLog.cpp
windows/CSpellWindow.cpp
windows/CTradeWindow.cpp
windows/CWindowObject.cpp
windows/GUIClasses.cpp
windows/InfoWindows.cpp
windows/QuickRecruitmentWindow.cpp
lobby/CBonusSelection.cpp
lobby/CCampaignInfoScreen.cpp
lobby/CLobbyScreen.cpp
lobby/CSavingScreen.cpp
lobby/CScenarioInfoScreen.cpp
lobby/CSelectionBase.cpp
lobby/OptionsTab.cpp
lobby/RandomMapTab.cpp
lobby/SelectionTab.cpp
mainmenu/CMainMenu.cpp
mainmenu/CCampaignScreen.cpp
mainmenu/CreditsScreen.cpp
mainmenu/CPrologEpilogVideo.cpp
mainmenu/CCampaignScreen.cpp
mainmenu/CMainMenu.cpp
mainmenu/CPrologEpilogVideo.cpp
mainmenu/CreditsScreen.cpp
lobby/CBonusSelection.cpp
lobby/CSelectionBase.cpp
lobby/CLobbyScreen.cpp
lobby/CSavingScreen.cpp
lobby/CScenarioInfoScreen.cpp
lobby/CCampaignInfoScreen.cpp
lobby/OptionsTab.cpp
lobby/RandomMapTab.cpp
lobby/SelectionTab.cpp
render/CAnimation.cpp
render/CBitmapHandler.cpp
render/CDefFile.cpp
render/CFadeAnimation.cpp
render/Canvas.cpp
render/ColorFilter.cpp
render/Graphics.cpp
render/IFont.cpp
CBitmapHandler.cpp
CreatureCostBox.cpp
CGameInfo.cpp
Client.cpp
CMessage.cpp
CMT.cpp
CMusicHandler.cpp
CPlayerInterface.cpp
CVideoHandler.cpp
CServerHandler.cpp
Graphics.cpp
mapHandler.cpp
NetPacksClient.cpp
NetPacksLobbyClient.cpp
SDLRWwrapper.cpp
ClientCommandManager.cpp
renderSDL/CBitmapFont.cpp
renderSDL/CBitmapHanFont.cpp
renderSDL/CTrueTypeFont.cpp
renderSDL/CursorHardware.cpp
renderSDL/CursorSoftware.cpp
renderSDL/SDLImage.cpp
renderSDL/SDLImageLoader.cpp
renderSDL/SDLRWwrapper.cpp
renderSDL/SDL_Extensions.cpp
widgets/Buttons.cpp
widgets/CArtifactHolder.cpp
widgets/CComponent.cpp
widgets/CGarrisonInt.cpp
widgets/CreatureCostBox.cpp
widgets/Images.cpp
widgets/MiscWidgets.cpp
widgets/ObjectLists.cpp
widgets/TextControls.cpp
windows/CCastleInterface.cpp
windows/CCreatureWindow.cpp
windows/CHeroWindow.cpp
windows/CKingdomInterface.cpp
windows/CMessage.cpp
windows/CQuestLog.cpp
windows/CSpellWindow.cpp
windows/CTradeWindow.cpp
windows/CWindowObject.cpp
windows/CreaturePurchaseCard.cpp
windows/GUIClasses.cpp
windows/InfoWindows.cpp
windows/QuickRecruitmentWindow.cpp
CGameInfo.cpp
CMT.cpp
CMusicHandler.cpp
CPlayerInterface.cpp
CServerHandler.cpp
CVideoHandler.cpp
Client.cpp
ClientCommandManager.cpp
NetPacksClient.cpp
NetPacksLobbyClient.cpp
)
set(client_HEADERS
StdInc.h
StdInc.h
battle/BattleActionsController.h
battle/BattleAnimationClasses.h
battle/BattleEffectsController.h
battle/BattleFieldController.h
battle/BattleInterfaceClasses.h
battle/BattleInterface.h
battle/BattleObstacleController.h
battle/BattleProjectileController.h
battle/BattleRenderer.h
battle/BattleSiegeController.h
battle/BattleStacksController.h
battle/BattleWindow.h
battle/CreatureAnimation.h
battle/BattleConstants.h
adventureMap/CAdvMapPanel.h
adventureMap/CAdvMapInt.h
adventureMap/CAdventureOptions.h
adventureMap/CInGameConsole.h
adventureMap/CInfoBar.h
adventureMap/CList.h
adventureMap/CMinimap.h
adventureMap/CResDataBar.h
adventureMap/CTerrainRect.h
adventureMap/mapHandler.h
gui/CAnimation.h
gui/Canvas.h
gui/CursorHandler.h
gui/CGuiHandler.h
gui/ColorFilter.h
gui/CIntObject.h
gui/Fonts.h
gui/TextAlignment.h
gui/SDL_Compat.h
gui/SDL_Extensions.h
gui/SDL_Pixels.h
gui/NotificationHandler.h
gui/InterfaceObjectConfigurable.h
battle/BattleActionsController.h
battle/BattleAnimationClasses.h
battle/BattleConstants.h
battle/BattleEffectsController.h
battle/BattleFieldController.h
battle/BattleInterface.h
battle/BattleInterfaceClasses.h
battle/BattleObstacleController.h
battle/BattleProjectileController.h
battle/BattleRenderer.h
battle/BattleSiegeController.h
battle/BattleStacksController.h
battle/BattleWindow.h
battle/CreatureAnimation.h
widgets/AdventureMapClasses.h
widgets/Buttons.h
widgets/CArtifactHolder.h
widgets/CComponent.h
widgets/CGarrisonInt.h
widgets/Images.h
widgets/MiscWidgets.h
widgets/ObjectLists.h
widgets/TextControls.h
windows/CAdvmapInterface.h
windows/CCastleInterface.h
windows/CCreatureWindow.h
windows/CreaturePurchaseCard.h
windows/CHeroWindow.h
windows/CKingdomInterface.h
windows/CQuestLog.h
windows/CSpellWindow.h
windows/CTradeWindow.h
windows/CWindowObject.h
windows/GUIClasses.h
windows/InfoWindows.h
windows/QuickRecruitmentWindow.h
gui/CGuiHandler.h
gui/CIntObject.h
gui/CursorHandler.h
gui/InterfaceObjectConfigurable.h
gui/NotificationHandler.h
gui/TextAlignment.h
mainmenu/CMainMenu.h
mainmenu/CCampaignScreen.h
mainmenu/CreditsScreen.h
mainmenu/CPrologEpilogVideo.h
lobby/CBonusSelection.h
lobby/CCampaignInfoScreen.h
lobby/CLobbyScreen.h
lobby/CSavingScreen.h
lobby/CScenarioInfoScreen.h
lobby/CSelectionBase.h
lobby/OptionsTab.h
lobby/RandomMapTab.h
lobby/SelectionTab.h
lobby/CBonusSelection.h
lobby/CSelectionBase.h
lobby/CLobbyScreen.h
lobby/CSavingScreen.h
lobby/CScenarioInfoScreen.h
lobby/CCampaignInfoScreen.h
lobby/OptionsTab.h
lobby/RandomMapTab.h
lobby/SelectionTab.h
mainmenu/CCampaignScreen.h
mainmenu/CMainMenu.h
mainmenu/CPrologEpilogVideo.h
mainmenu/CreditsScreen.h
CBitmapHandler.h
CreatureCostBox.h
CGameInfo.h
Client.h
CMessage.h
CMT.h
CMusicHandler.h
CPlayerInterface.h
CVideoHandler.h
CServerHandler.h
Graphics.h
mapHandler.h
resource.h
SDLRWwrapper.h
ClientCommandManager.h
render/CAnimation.h
render/CBitmapHandler.h
render/CDefFile.h
render/CFadeAnimation.h
render/Canvas.h
render/ColorFilter.h
render/Graphics.h
render/ICursor.h
render/IFont.h
render/IImage.h
render/IImageLoader.h
renderSDL/CBitmapFont.h
renderSDL/CBitmapHanFont.h
renderSDL/CTrueTypeFont.h
renderSDL/CursorHardware.h
renderSDL/CursorSoftware.h
renderSDL/SDLImage.h
renderSDL/SDLImageLoader.h
renderSDL/SDLRWwrapper.h
renderSDL/SDL_Extensions.h
renderSDL/SDL_PixelAccess.h
widgets/Buttons.h
widgets/CArtifactHolder.h
widgets/CComponent.h
widgets/CGarrisonInt.h
widgets/CreatureCostBox.h
widgets/Images.h
widgets/MiscWidgets.h
widgets/ObjectLists.h
widgets/TextControls.h
windows/CCastleInterface.h
windows/CCreatureWindow.h
windows/CHeroWindow.h
windows/CKingdomInterface.h
windows/CMessage.h
windows/CQuestLog.h
windows/CSpellWindow.h
windows/CTradeWindow.h
windows/CWindowObject.h
windows/CreaturePurchaseCard.h
windows/GUIClasses.h
windows/InfoWindows.h
windows/QuickRecruitmentWindow.h
CGameInfo.h
CMT.h
CMusicHandler.h
CPlayerInterface.h
CServerHandler.h
CVideoHandler.h
Client.h
ClientCommandManager.h
resource.h
)
if(APPLE_IOS)
@ -263,8 +304,7 @@ elseif(APPLE_IOS)
set_source_files_properties(${XCODE_RESOURCE_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
# workaround to prevent CMAKE_SKIP_PRECOMPILE_HEADERS being added as compile flag
# add max version condition when https://gitlab.kitware.com/cmake/cmake/-/merge_requests/7562 is merged
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.22.0")
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.22.0" AND CMAKE_VERSION VERSION_LESS "3.25.0")
set_source_files_properties(${XCODE_RESOURCE_PATH} PROPERTIES LANGUAGE CXX)
endif()
endforeach()
@ -290,8 +330,15 @@ else()
target_compile_definitions(vcmiclient PRIVATE DISABLE_VIDEO)
endif()
target_include_directories(vcmiclient
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(vcmiclient PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
if (ffmpeg_INCLUDE_DIRS)
target_include_directories(vcmiclient PRIVATE
${ffmpeg_INCLUDE_DIRS}
)
endif()
vcmi_set_output_dir(vcmiclient "")
enable_pch(vcmiclient)

View File

@ -13,7 +13,8 @@
#include "CMusicHandler.h"
#include "CGameInfo.h"
#include "SDLRWwrapper.h"
#include "renderSDL/SDLRWwrapper.h"
#include "../lib/JsonNode.h"
#include "../lib/GameConstants.h"
#include "../lib/filesystem/Filesystem.h"
@ -22,6 +23,7 @@
#include "../lib/VCMIDirs.h"
#include "../lib/TerrainHandler.h"
#define VCMI_SOUND_NAME(x)
#define VCMI_SOUND_FILE(y) #y,

View File

@ -11,7 +11,8 @@
#include <vcmi/Artifact.h>
#include "windows/CAdvmapInterface.h"
#include "adventureMap/CAdvMapInt.h"
#include "adventureMap/mapHandler.h"
#include "battle/BattleInterface.h"
#include "battle/BattleEffectsController.h"
#include "battle/BattleFieldController.h"
@ -25,15 +26,15 @@
#include "windows/CHeroWindow.h"
#include "windows/CCreatureWindow.h"
#include "windows/CQuestLog.h"
#include "CMessage.h"
#include "CPlayerInterface.h"
#include "gui/SDL_Extensions.h"
#include "widgets/CComponent.h"
#include "widgets/Buttons.h"
#include "windows/CTradeWindow.h"
#include "windows/CSpellWindow.h"
#include "../lib/CConfigHandler.h"
#include "Graphics.h"
#include "windows/GUIClasses.h"
#include "render/CAnimation.h"
#include "render/IImage.h"
#include "../lib/CArtHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
@ -51,24 +52,23 @@
#include "../lib/NetPacks.h"//todo: remove
#include "../lib/mapping/CMap.h"
#include "../lib/VCMIDirs.h"
#include "mapHandler.h"
#include "../lib/CStopWatch.h"
#include "../lib/StartInfo.h"
#include "../lib/CPlayerState.h"
#include "../lib/GameConstants.h"
#include "gui/CGuiHandler.h"
#include "gui/CAnimation.h"
#include "windows/InfoWindows.h"
#include "../lib/UnlockGuard.h"
#include "../lib/CPathfinder.h"
#include "../lib/RoadHandler.h"
#include "../lib/TerrainHandler.h"
#include <SDL_timer.h>
#include "CServerHandler.h"
// FIXME: only needed for CGameState::mutex
#include "../lib/CGameState.h"
#include "gui/NotificationHandler.h"
#include "adventureMap/CInGameConsole.h"
#include <SDL_events.h>
// The macro below is used to mark functions that are called by client when game state changes.
// They all assume that CPlayerInterface::pim mutex is locked.
@ -342,7 +342,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
auto unlockPim = vstd::makeUnlockGuard(*pim);
while(frameNumber == GH.mainFPSmng->getFrameNumber())
SDL_Delay(5);
boost::this_thread::sleep(boost::posix_time::milliseconds(5));
};
//first initializing done
@ -1514,7 +1514,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
{
auto unlockPim = vstd::makeUnlockGuard(*pim);
IgnoreEvents ignore(*this);
SDL_Delay(focusTime);
boost::this_thread::sleep(boost::posix_time::milliseconds(focusTime));
}
}
CCS->curh->show();
@ -2276,7 +2276,7 @@ void CPlayerInterface::waitForAllDialogs(bool unlockPim)
while(!dialogs.empty())
{
auto unlock = vstd::makeUnlockGuardIf(*pim, unlockPim);
SDL_Delay(5);
boost::this_thread::sleep(boost::posix_time::milliseconds(5));
}
waitWhileDialog(unlockPim);
}

View File

@ -60,6 +60,8 @@
#include <windows.h>
#endif
#include <SDL_events.h>
template<typename T> class CApplyOnLobby;
const std::string CServerHandler::localhostAddress{"127.0.0.1"};

View File

@ -11,13 +11,24 @@
#include "CVideoHandler.h"
#include "gui/CGuiHandler.h"
#include "gui/SDL_Extensions.h"
#include "renderSDL/SDL_Extensions.h"
#include "CPlayerInterface.h"
#include "../lib/filesystem/Filesystem.h"
#include <SDL_render.h>
#include <SDL_events.h>
extern CGuiHandler GH; //global gui handler
#ifndef DISABLE_VIDEO
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
//reads events and returns true on key down
static bool keyDown()
{
@ -56,22 +67,20 @@ static si64 lodSeek(void * opaque, si64 pos, int whence)
}
CVideoPlayer::CVideoPlayer()
{
stream = -1;
format = nullptr;
codecContext = nullptr;
codec = nullptr;
frame = nullptr;
sws = nullptr;
context = nullptr;
texture = nullptr;
dest = nullptr;
destRect = CSDL_Ext::genRect(0,0,0,0);
pos = CSDL_Ext::genRect(0,0,0,0);
refreshWait = 0;
refreshCount = 0;
doLoop = false;
}
: stream(-1)
, format (nullptr)
, codecContext(nullptr)
, codec(nullptr)
, frame(nullptr)
, sws(nullptr)
, context(nullptr)
, texture(nullptr)
, dest(nullptr)
, destRect(0,0,0,0)
, pos(0,0,0,0)
, frameTime(0)
, doLoop(false)
{}
bool CVideoPlayer::open(std::string fname, bool scale)
{
@ -85,9 +94,8 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay, bool scal
close();
this->fname = fname;
refreshWait = 3;
refreshCount = -1;
doLoop = loop;
frameTime = 0;
ResourceID resource(std::string("Video/") + fname, EResType::VIDEO);
@ -254,6 +262,7 @@ bool CVideoPlayer::nextFrame()
if (doLoop && !gotError)
{
// Rewind
frameTime = 0;
if (av_seek_frame(format, stream, 0, AVSEEK_FLAG_BYTE) < 0)
break;
gotError = true;
@ -354,9 +363,11 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
if (sws == nullptr)
return;
if (refreshCount <= 0)
double frameEndTime = (frame->pts + frame->pkt_duration) * av_q2d(format->streams[stream]->time_base);
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.0;
if (frameTime >= frameEndTime )
{
refreshCount = refreshWait;
if (nextFrame())
show(x,y,dst,update);
else
@ -374,8 +385,6 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
{
redraw(x, y, dst, update);
}
refreshCount --;
}
void CVideoPlayer::close()

View File

@ -56,39 +56,11 @@ public:
#include "../lib/filesystem/CInputStream.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
//compatibility for libav 9.18 in ubuntu 14.04, 52.66.100 is ffmpeg 2.2.3
#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 66, 100))
inline AVFrame * av_frame_alloc()
{
return avcodec_alloc_frame();
}
inline void av_frame_free(AVFrame ** frame)
{
av_free(*frame);
*frame = nullptr;
}
#endif // VCMI_USE_OLD_AVUTIL
//fix for travis-ci
#if (LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 0, 0))
#define AVPixelFormat PixelFormat
#define AV_PIX_FMT_NONE PIX_FMT_NONE
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
#define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
#endif
struct AVFormatContext;
struct AVCodecContext;
struct AVCodec;
struct AVFrame;
struct AVIOContext;
class CVideoPlayer : public IMainVideoPlayer
{
@ -108,8 +80,8 @@ class CVideoPlayer : public IMainVideoPlayer
Rect destRect; // valid when dest is used
Rect pos; // destination on screen
int refreshWait; // Wait several refresh before updating the image
int refreshCount;
/// video playback currnet progress, in seconds
double frameTime;
bool doLoop; // loop through video
bool playVideo(int x, int y, bool stopOnKey);
@ -141,4 +113,3 @@ public:
};
#endif

View File

@ -13,6 +13,8 @@
#include "CMusicHandler.h"
#include "../lib/mapping/CCampaignHandler.h"
#include "../CCallback.h"
#include "adventureMap/CAdvMapInt.h"
#include "adventureMap/mapHandler.h"
#include "../lib/CConsoleHandler.h"
#include "CGameInfo.h"
#include "../lib/CGameState.h"
@ -35,7 +37,6 @@
#include "../lib/mapping/CMap.h"
#include "../lib/mapping/CMapService.h"
#include "../lib/JsonNode.h"
#include "mapHandler.h"
#include "../lib/CConfigHandler.h"
#include "mainmenu/CMainMenu.h"
#include "mainmenu/CCampaignScreen.h"
@ -46,7 +47,6 @@
#include "gui/CGuiHandler.h"
#include "CServerHandler.h"
#include "../lib/ScriptHandler.h"
#include "windows/CAdvmapInterface.h"
#include <vcmi/events/EventBus.h>
#ifdef VCMI_ANDROID
@ -775,14 +775,14 @@ void CClient::removeGUI()
}
#ifdef VCMI_ANDROID
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jobject cls)
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jclass cls)
{
logNetwork->info("Received clientSetupJNI");
CAndroidVMHelper::cacheVM(env);
}
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jobject cls)
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jclass cls)
{
logNetwork->info("Received server closed signal");
if (CSH) {
@ -790,13 +790,13 @@ extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerCl
}
}
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jobject cls)
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jclass cls)
{
logNetwork->info("Received server ready signal");
androidTestServerReadyFlag.store(true);
}
extern "C" JNIEXPORT bool JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jobject cls)
extern "C" JNIEXPORT jboolean JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jclass cls)
{
logGlobal->info("Received emergency save game request");
if(!LOCPLINT || !LOCPLINT->cb)

View File

@ -12,6 +12,8 @@
#include "ClientCommandManager.h"
#include "Client.h"
#include "adventureMap/CInGameConsole.h"
#include "adventureMap/CAdvMapInt.h"
#include "CPlayerInterface.h"
#include "CServerHandler.h"
#include "gui/CGuiHandler.h"
@ -20,19 +22,21 @@
#include "../lib/CGameState.h"
#include "../lib/CPlayerState.h"
#include "../lib/StringConstants.h"
#include "gui/CAnimation.h"
#include "windows/CAdvmapInterface.h"
#include "windows/CCastleInterface.h"
#include "render/CAnimation.h"
#include "../CCallback.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CModHandler.h"
#include "../lib/VCMIDirs.h"
#include "CMT.h"
#ifdef SCRIPTING_ENABLED
#include "../lib/ScriptHandler.h"
#endif
#include <SDL_surface.h>
void ClientCommandManager::handleGoSolo()
{
Settings session = settings.write["session"];

View File

@ -10,12 +10,21 @@
#include "StdInc.h"
#include "../lib/NetPacks.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileInfo.h"
#include "../CCallback.h"
#include "Client.h"
#include "CPlayerInterface.h"
#include "CGameInfo.h"
#include "windows/GUIClasses.h"
#include "adventureMap/mapHandler.h"
#include "adventureMap/CInGameConsole.h"
#include "battle/BattleInterface.h"
#include "gui/CGuiHandler.h"
#include "widgets/MiscWidgets.h"
#include "CMT.h"
#include "CServerHandler.h"
#include "../CCallback.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileInfo.h"
#include "../lib/serializer/Connection.h"
#include "../lib/serializer/BinarySerializer.h"
#include "../lib/CGeneralTextHandler.h"
@ -26,22 +35,13 @@
#include "../lib/spells/CSpellHandler.h"
#include "../lib/CSoundBase.h"
#include "../lib/StartInfo.h"
#include "mapHandler.h"
#include "windows/GUIClasses.h"
#include "../lib/CConfigHandler.h"
#include "gui/SDL_Extensions.h"
#include "battle/BattleInterface.h"
#include "../lib/mapping/CCampaignHandler.h"
#include "../lib/CGameState.h"
#include "../lib/CStack.h"
#include "../lib/battle/BattleInfo.h"
#include "../lib/GameConstants.h"
#include "../lib/CPlayerState.h"
#include "gui/CGuiHandler.h"
#include "widgets/MiscWidgets.h"
#include "widgets/AdventureMapClasses.h"
#include "CMT.h"
#include "CServerHandler.h"
// TODO: as Tow suggested these template should all be part of CClient
// This will require rework spectator interface properly though

View File

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

View File

@ -1,5 +1,5 @@
/*
* CAdvmapInterface.cpp, part of VCMI engine
* CAdvMapInt.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -8,55 +8,44 @@
*
*/
#include "StdInc.h"
#include "CAdvmapInterface.h"
#include "CAdvMapInt.h"
#include "CCastleInterface.h"
#include "CHeroWindow.h"
#include "CKingdomInterface.h"
#include "CSpellWindow.h"
#include "CTradeWindow.h"
#include "GUIClasses.h"
#include "InfoWindows.h"
#include "CAdvMapPanel.h"
#include "CAdventureOptions.h"
#include "CInGameConsole.h"
#include "mapHandler.h"
#include "../CBitmapHandler.h"
#include "../windows/CKingdomInterface.h"
#include "../windows/CSpellWindow.h"
#include "../windows/CTradeWindow.h"
#include "../windows/GUIClasses.h"
#include "../windows/InfoWindows.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../mainmenu/CMainMenu.h"
#include "../lobby/CSelectionBase.h"
#include "../lobby/CCampaignInfoScreen.h"
#include "../lobby/CSavingScreen.h"
#include "../lobby/CScenarioInfoScreen.h"
#include "../Graphics.h"
#include "../mapHandler.h"
#include "../gui/CAnimation.h"
#include "../render/CAnimation.h"
#include "../gui/CursorHandler.h"
#include "../render/IImage.h"
#include "../gui/CGuiHandler.h"
#include "../gui/SDL_Extensions.h"
#include "../widgets/MiscWidgets.h"
#include "../widgets/TextControls.h"
#include "../widgets/Buttons.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGameState.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/CSoundBase.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/CTownHandler.h"
#include "../../lib/JsonNode.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/CPathfinder.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/StartInfo.h"
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/TerrainHandler.h"
#include <SDL_surface.h>
#include <SDL_events.h>
#define ADVOPT (conf.go()->ac)
using namespace CSDL_Ext;
std::shared_ptr<CAdvMapInt> adventureInt;
@ -86,470 +75,6 @@ static void setScrollingCursor(ui8 direction)
CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
}
CTerrainRect::CTerrainRect()
: fadeSurface(nullptr),
lastRedrawStatus(EMapAnimRedrawStatus::OK),
fadeAnim(std::make_shared<CFadeAnimation>()),
curHoveredTile(-1,-1,-1),
currentPath(nullptr)
{
tilesw=(ADVOPT.advmapW+31)/32;
tilesh=(ADVOPT.advmapH+31)/32;
pos.x=ADVOPT.advmapX;
pos.y=ADVOPT.advmapY;
pos.w=ADVOPT.advmapW;
pos.h=ADVOPT.advmapH;
moveX = moveY = 0;
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
}
CTerrainRect::~CTerrainRect()
{
if(fadeSurface)
SDL_FreeSurface(fadeSurface);
}
void CTerrainRect::deactivate()
{
CIntObject::deactivate();
curHoveredTile = int3(-1,-1,-1); //we lost info about hovered tile when disabling
}
void CTerrainRect::clickLeft(tribool down, bool previousState)
{
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if(indeterminate(down))
return;
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(adventureInt->swipeEnabled)
{
if(handleSwipeStateChange((bool)down == true))
{
return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
}
}
else
{
#endif
if(down == false)
return;
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
}
#endif
int3 mp = whichTileIsIt();
if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
return;
adventureInt->tileLClicked(mp);
}
void CTerrainRect::clickRight(tribool down, bool previousState)
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(adventureInt->swipeEnabled && isSwiping)
return;
#endif
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
int3 mp = whichTileIsIt();
if(CGI->mh->map->isInTheMap(mp) && down)
adventureInt->tileRClicked(mp);
}
void CTerrainRect::clickMiddle(tribool down, bool previousState)
{
handleSwipeStateChange((bool)down == true);
}
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
{
handleHover(sEvent);
if(!adventureInt->swipeEnabled)
return;
handleSwipeMove(sEvent);
}
void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(sEvent.state == 0) // any "button" is enough on mobile
#else
if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
#endif
{
return;
}
if(!isSwiping)
{
// try to distinguish if this touch was meant to be a swipe or just fat-fingering press
if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop ||
abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop)
{
isSwiping = true;
}
}
if(isSwiping)
{
adventureInt->swipeTargetPosition.x =
swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32;
adventureInt->swipeTargetPosition.y =
swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32;
adventureInt->swipeMovementRequested = true;
}
}
bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
{
if(btnPressed)
{
swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
swipeInitialMapPos = int3(adventureInt->position);
return true;
}
else if(isSwiping) // only accept this touch if it wasn't a swipe
{
isSwiping = false;
return true;
}
return false;
}
void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
{
int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y);
int3 pom = adventureInt->verifyPos(tHovered);
if(tHovered != pom) //tile outside the map
{
CCS->curh->set(Cursor::Map::POINTER);
return;
}
if (pom != curHoveredTile)
{
curHoveredTile = pom;
adventureInt->tileHovered(pom);
}
}
void CTerrainRect::hover(bool on)
{
if (!on)
{
adventureInt->statusbar->clear();
CCS->curh->set(Cursor::Map::POINTER);
}
//Hoverable::hover(on);
}
void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
{
const static int pns[9][9] = {
{16, 17, 18, 7, -1, 19, 6, 5, -1},
{ 8, 9, 18, 7, -1, 19, 6, -1, 20},
{ 8, 1, 10, 7, -1, 19, -1, 21, 20},
{24, 17, 18, 15, -1, -1, 6, 5, 4},
{-1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 8, 1, 2, -1, -1, 11, 22, 21, 20},
{24, 17, -1, 23, -1, 3, 14, 5, 4},
{24, -1, 2, 23, -1, 3, 22, 13, 4},
{-1, 1, 2, 23, -1, 3, 22, 21, 12}
}; //table of magic values TODO meaning, change variable name
for (int i = 0; i < -1 + (int)currentPath->nodes.size(); ++i)
{
const int3 &curPos = currentPath->nodes[i].coord, &nextPos = currentPath->nodes[i+1].coord;
if(curPos.z != adventureInt->position.z)
continue;
int pn=-1;//number of picture
if (i==0) //last tile
{
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
if (x<0 || y<0 || x>pos.w || y>pos.h)
continue;
pn=0;
}
else
{
const int3 &prevPos = currentPath->nodes[i-1].coord;
std::vector<CGPathNode> & cv = currentPath->nodes;
/* Vector directions
* 0 1 2
* \ | /
* 3 - 4 - 5
* / | \
* 6 7 8
*For example:
* |
* |__\
* /
* is id1=7, id2=5 (pns[7][5])
*/
bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
{
int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1); //Direction of entering vector
int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector
pn=pns[id1][id2];
}
else //path discontinuity or sea/land transition (eg. when moving through Subterranean Gate or Boat)
{
pn = 0;
}
}
if (currentPath->nodes[i].turns)
pn+=25;
if (pn>=0)
{
const auto arrow = graphics->heroMoveArrows->getImage(pn);
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
if (x< -32 || y< -32 || x>pos.w || y>pos.h)
continue;
int hvx = (x + arrow->width()) - (pos.x + pos.w),
hvy = (y + arrow->height()) - (pos.y + pos.h);
Rect prevClip;
CSDL_Ext::getClipRect(to, prevClip);
CSDL_Ext::setClipRect(to, extRect); //preventing blitting outside of that rect
if(ADVOPT.smoothMove) //version for smooth hero move, with pos shifts
{
if (hvx<0 && hvy<0)
{
arrow->draw(to, x + moveX, y + moveY);
}
else if(hvx<0)
{
Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
else if (hvy<0)
{
Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
else
{
Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
}
else //standard version
{
if (hvx<0 && hvy<0)
{
arrow->draw(to, x, y);
}
else if(hvx<0)
{
Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
arrow->draw(to, x, y, &srcRect);
}
else if (hvy<0)
{
Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
arrow->draw(to, x, y, &srcRect);
}
else
{
Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
arrow->draw(to, x, y, &srcRect);
}
}
CSDL_Ext::setClipRect(to, prevClip);
}
} //for (int i=0;i<currentPath->nodes.size()-1;i++)
}
void CTerrainRect::show(SDL_Surface * to)
{
if (adventureInt->mode == EAdvMapMode::NORMAL)
{
MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos);
info.otherheroAnim = true;
info.anim = adventureInt->anim;
info.heroAnim = adventureInt->heroAnim;
if (ADVOPT.smoothMove)
info.movement = int3(moveX, moveY, 0);
lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
if (fadeAnim->isFading())
{
Rect r(pos);
fadeAnim->update();
fadeAnim->draw(to, r.topLeft());
}
if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
{
showPath(pos, to);
}
}
}
void CTerrainRect::showAll(SDL_Surface * to)
{
// world view map is static and doesn't need redraw every frame
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
{
MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos, adventureInt->worldViewIcons);
info.scaled = true;
info.scale = adventureInt->worldViewScale;
adventureInt->worldViewOptions.adjustDrawingInfo(info);
CGI->mh->drawTerrainRectNew(to, &info);
}
}
void CTerrainRect::showAnim(SDL_Surface * to)
{
if (fadeAnim->isFading())
show(to);
else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
}
int3 CTerrainRect::whichTileIsIt(const int x, const int y)
{
int3 ret;
ret.x = adventureInt->position.x + ((x-CGI->mh->offsetX-pos.x)/32);
ret.y = adventureInt->position.y + ((y-CGI->mh->offsetY-pos.y)/32);
ret.z = adventureInt->position.z;
return ret;
}
int3 CTerrainRect::whichTileIsIt()
{
if(GH.current)
return whichTileIsIt(GH.current->motion.x,GH.current->motion.y);
else
return int3(-1);
}
int3 CTerrainRect::tileCountOnScreen()
{
switch (adventureInt->mode)
{
default:
logGlobal->error("Unknown map mode %d", (int)adventureInt->mode);
return int3();
case EAdvMapMode::NORMAL:
return int3(tilesw, tilesh, 1);
case EAdvMapMode::WORLD_VIEW:
return int3((si32)(tilesw / adventureInt->worldViewScale), (si32)(tilesh / adventureInt->worldViewScale), 1);
}
}
void CTerrainRect::fadeFromCurrentView()
{
if (!ADVOPT.screenFading)
return;
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if (!fadeSurface)
fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
CSDL_Ext::blitSurface(screen, fadeSurface, Point(0,0));
fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
}
bool CTerrainRect::needsAnimUpdate()
{
return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
}
void CResDataBar::clickRight(tribool down, bool previousState)
{
}
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
{
pos.x += x;
pos.y += y;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(defname, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->bg->w;
pos.h = background->bg->h;
txtpos.resize(8);
for (int i = 0; i < 8 ; i++)
{
txtpos[i].first = pos.x + offx + resdist*i;
txtpos[i].second = pos.y + offy;
}
txtpos[7].first = txtpos[6].first + datedist;
datetext = CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+ ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
addUsedEvents(RCLICK);
}
CResDataBar::CResDataBar()
{
pos.x += ADVOPT.resdatabarX;
pos.y += ADVOPT.resdatabarY;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(ADVOPT.resdatabarG, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->bg->w;
pos.h = background->bg->h;
txtpos.resize(8);
for (int i = 0; i < 8 ; i++)
{
txtpos[i].first = pos.x + ADVOPT.resOffsetX + ADVOPT.resDist*i;
txtpos[i].second = pos.y + ADVOPT.resOffsetY;
}
txtpos[7].first = txtpos[6].first + ADVOPT.resDateDist;
datetext = CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+ ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
}
CResDataBar::~CResDataBar() = default;
void CResDataBar::draw(SDL_Surface * to)
{
//TODO: all this should be labels, but they require proper text update on change
for (auto i=Res::WOOD; i<=Res::GOLD; vstd::advance(i, 1))
{
std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first,txtpos[i].second));
}
std::vector<std::string> temp;
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::MONTH)));
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK)));
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)));
graphics->fonts[FONT_SMALL]->renderTextLeft(to, processStr(datetext,temp), Colors::WHITE, Point(txtpos[7].first,txtpos[7].second));
}
void CResDataBar::show(SDL_Surface * to)
{
}
void CResDataBar::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
draw(to);
}
CAdvMapInt::CAdvMapInt():
mode(EAdvMapMode::NORMAL),
worldViewScale(0.0f), //actual init later in changeMode
@ -569,10 +94,10 @@ CAdvMapInt::CAdvMapInt():
pos.h = screen->h;
strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
townList.onSelect = std::bind(&CAdvMapInt::selectionChanged,this);
bg = BitmapHandler::loadBitmap(ADVOPT.mainGraphic);
bg = IImage::createFromFile(ADVOPT.mainGraphic);
if(!ADVOPT.worldViewGraphic.empty())
{
bgWorldView = BitmapHandler::loadBitmap(ADVOPT.worldViewGraphic);
bgWorldView = IImage::createFromFile(ADVOPT.worldViewGraphic);
}
else
{
@ -582,7 +107,7 @@ CAdvMapInt::CAdvMapInt():
if (!bgWorldView)
{
logGlobal->warn("bgWorldView not defined in resolution config; fallback to VWorld.bmp");
bgWorldView = BitmapHandler::loadBitmap("VWorld.bmp");
bgWorldView = IImage::createFromFile("VWorld.bmp");
}
worldViewIcons = std::make_shared<CAnimation>("VwSymbol");//todo: customize with ADVOPT
@ -715,11 +240,6 @@ CAdvMapInt::CAdvMapInt():
addUsedEvents(MOVE);
}
CAdvMapInt::~CAdvMapInt()
{
SDL_FreeSurface(bg);
}
void CAdvMapInt::fshowOverview()
{
GH.pushIntT<CKingdomInterface>();
@ -979,7 +499,7 @@ void CAdvMapInt::deactivate()
void CAdvMapInt::showAll(SDL_Surface * to)
{
blitAt(bg,0,0,to);
bg->draw(to, 0, 0);
if(state != INGAME)
return;
@ -1096,7 +616,7 @@ void CAdvMapInt::handleMapScrollingUpdate()
int scrollSpeed = static_cast<int>(settings["adventure"]["scrollSpeed"].Float());
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
if((animValHitCount % (4 / scrollSpeed)) == 0
&& ((GH.topInt().get() == this) || isCtrlKeyDown()))
&& ((GH.topInt().get() == this) || CSDL_Ext::isCtrlKeyDown()))
{
if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
position.x--;
@ -1460,7 +980,7 @@ void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
// adventure map scrolling with mouse
// currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed
// don't scroll if there is no window in focus - these events don't seem to correspond to the actual mouse movement
if(!isCtrlKeyDown() && isActive() && sEvent.windowID != 0 && mode == EAdvMapMode::NORMAL)
if(!CSDL_Ext::isCtrlKeyDown() && isActive() && sEvent.windowID != 0 && mode == EAdvMapMode::NORMAL)
{
if(sEvent.x<15)
{
@ -1510,7 +1030,7 @@ void CAdvMapInt::startHotSeatWait(PlayerColor Player)
void CAdvMapInt::setPlayer(PlayerColor Player)
{
player = Player;
graphics->blueToPlayersAdv(bg,player);
bg->playerColored(player);
panelMain->setPlayerColor(player);
panelWorldView->setPlayerColor(player);
@ -1587,6 +1107,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
bool isHero = false;
if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
{
assert(!terrain.currentPath); //path can be active only when hero is selected
@ -1594,10 +1115,11 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
else if(canSelect)
select(static_cast<const CArmedInstance*>(topBlocking), false);
return;
}
else if(const CGHeroInstance * currentHero = curHero()) //hero is selected
{
isHero = true;
const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
if(currentHero == topBlocking) //clicked selected hero
{
@ -1639,7 +1161,8 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
throw std::runtime_error("Nothing is selected...");
}
if(const IShipyard *shipyard = ourInaccessibleShipyard(topBlocking))
const auto shipyard = ourInaccessibleShipyard(topBlocking);
if(isHero && shipyard != nullptr)
{
LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
}
@ -1709,7 +1232,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
else
CCS->curh->set(Cursor::Map::POINTER);
}
else if(const CGHeroInstance * h = curHero())
else if(const CGHeroInstance * hero = curHero())
{
std::array<Cursor::Map, 4> cursorMove = { Cursor::Map::T1_MOVE, Cursor::Map::T2_MOVE, Cursor::Map::T3_MOVE, Cursor::Map::T4_MOVE, };
std::array<Cursor::Map, 4> cursorAttack = { Cursor::Map::T1_ATTACK, Cursor::Map::T2_ATTACK, Cursor::Map::T3_ATTACK, Cursor::Map::T4_ATTACK, };
@ -1719,16 +1242,21 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, };
std::array<Cursor::Map, 4> cursorSailVisit = { Cursor::Map::T1_SAIL_VISIT, Cursor::Map::T2_SAIL_VISIT, Cursor::Map::T3_SAIL_VISIT, Cursor::Map::T4_SAIL_VISIT, };
const CGPathNode * pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPos);
assert(pnode);
const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
assert(pathNode);
int turns = pnode->turns;
if(LOCPLINT->altPressed() && pathNode->reachable()) //overwrite status bar text with movement info
{
ShowMoveDetailsInStatusbar(*hero, *pathNode);
}
int turns = pathNode->turns;
vstd::amin(turns, 3);
switch(pnode->action)
switch(pathNode->action)
{
case CGPathNode::NORMAL:
case CGPathNode::TELEPORT_NORMAL:
if(pnode->layer == EPathfindingLayer::LAND)
if(pathNode->layer == EPathfindingLayer::LAND)
CCS->curh->set(cursorMove[turns]);
else
CCS->curh->set(cursorSailVisit[turns]);
@ -1744,7 +1272,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
else
CCS->curh->set(cursorExchange[turns]);
}
else if(pnode->layer == EPathfindingLayer::LAND)
else if(pathNode->layer == EPathfindingLayer::LAND)
CCS->curh->set(cursorVisit[turns]);
else
CCS->curh->set(cursorSailVisit[turns]);
@ -1785,6 +1313,21 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
}
}
void CAdvMapInt::ShowMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
{
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
statusbar->write(result);
}
void CAdvMapInt::tileRClicked(const int3 &mapPos)
{
if(mode != EAdvMapMode::NORMAL)
@ -1814,7 +1357,7 @@ void CAdvMapInt::tileRClicked(const int3 &mapPos)
return;
}
CRClickPopup::createAndPush(obj, CSDL_Ext::fromSDL(GH.current->motion), ETextAlignment::CENTER);
CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
}
void CAdvMapInt::enterCastingMode(const CSpell * sp)
@ -1947,42 +1490,6 @@ void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale)
}
}
CAdventureOptions::CAdventureOptions()
: CWindowObject(PLAYER_COLORED, "ADVOPTS")
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_v);
viewWorld->addCallback(std::bind(&CPlayerInterface::viewWorldMap, LOCPLINT));
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), SDLK_RETURN);
exit->assignedKeys.insert(SDLK_ESCAPE);
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_i);
scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_p);
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
if(const CGHeroInstance *h = adventureInt->curHero())
dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
else
dig->block(true);
}
void CAdventureOptions::showScenarioInfo()
{
if(LOCPLINT->cb->getStartInfo()->campState)
{
GH.pushIntT<CCampaignInfoScreen>();
}
else
{
GH.pushIntT<CScenarioInfoScreen>();
}
}
CAdvMapInt::WorldViewOptions::WorldViewOptions()
{
clear();
@ -2001,3 +1508,4 @@ void CAdvMapInt::WorldViewOptions::adjustDrawingInfo(MapDrawingInfo& info)
info.additionalIcons = &iconPositions;
}

View File

@ -1,5 +1,5 @@
/*
* CAdvmapInterface.h, part of VCMI engine
* CAdvMapInt.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -9,118 +9,45 @@
*/
#pragma once
#include "../widgets/AdventureMapClasses.h"
#include "CWindowObject.h"
#include "../gui/CIntObject.h"
#include "../widgets/TextControls.h"
#include "../widgets/Buttons.h"
#include "../../lib/int3.h"
#include "../../lib/GameConstants.h"
#include "../../lib/spells/ViewSpellInt.h"
#include "CTerrainRect.h"
#include "CResDataBar.h"
#include "CList.h"
#include "CInfoBar.h"
#include "CMinimap.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CGPath;
class CGObjectInstance;
class CGHeroInstance;
class CGTownInstance;
class CSpell;
class CArmedInstance;
class IShipyard;
struct CGPathNode;
struct ObjectPosInfo;
VCMI_LIB_NAMESPACE_END
class CCallback;
class CAdvMapInt;
class CHeroWindow;
enum class EMapAnimRedrawStatus;
class CFadeAnimation;
class CButton;
class IImage;
class CAnimImage;
class CGStatusBar;
class CAdvMapPanel;
class CAdvMapWorldViewPanel;
class CAnimation;
struct MapDrawingInfo;
/*****************************/
enum class EAdvMapMode
{
NORMAL,
WORLD_VIEW
};
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
class CAdventureOptions : public CWindowObject
{
public:
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> viewWorld;
std::shared_ptr<CButton> puzzle;
std::shared_ptr<CButton> dig;
std::shared_ptr<CButton> scenInfo;
/*std::shared_ptr<CButton> replay*/
CAdventureOptions();
static void showScenarioInfo();
};
/// Holds information about which tiles of the terrain are shown/not shown at the screen
class CTerrainRect : public CIntObject
{
SDL_Surface * fadeSurface;
EMapAnimRedrawStatus lastRedrawStatus;
std::shared_ptr<CFadeAnimation> fadeAnim;
int3 swipeInitialMapPos;
int3 swipeInitialRealPos;
bool isSwiping;
static constexpr float SwipeTouchSlop = 16.0f;
void handleHover(const SDL_MouseMotionEvent & sEvent);
void handleSwipeMove(const SDL_MouseMotionEvent & sEvent);
/// handles start/finish of swipe (press/release of corresponding button); returns true if state change was handled
bool handleSwipeStateChange(bool btnPressed);
public:
int tilesw, tilesh; //width and height of terrain to blit in tiles
int3 curHoveredTile;
int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
CGPath * currentPath;
CTerrainRect();
virtual ~CTerrainRect();
void deactivate() override;
void clickLeft(tribool down, bool previousState) override;
void clickRight(tribool down, bool previousState) override;
void clickMiddle(tribool down, bool previousState) override;
void hover(bool on) override;
void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
void showAnim(SDL_Surface * to);
void showPath(const Rect &extRect, SDL_Surface * to);
int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
int3 whichTileIsIt(); //uses current cursor pos
/// @returns number of visible tiles on screen respecting current map scaling
int3 tileCountOnScreen();
/// animates view by caching current surface and crossfading it with normal screen
void fadeFromCurrentView();
bool needsAnimUpdate();
};
/// Resources bar which shows information about how many gold, crystals,... you have
/// Current date is displayed too
class CResDataBar : public CIntObject
{
public:
std::shared_ptr<CPicture> background;
std::vector<std::pair<int,int> > txtpos;
std::string datetext;
void clickRight(tribool down, bool previousState) override;
CResDataBar();
CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
~CResDataBar();
void draw(SDL_Surface * to);
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
};
/// That's a huge class which handles general adventure map actions and
/// shows the right menu(questlog, spellbook, end turn,..) from where you
/// can get to the towns and heroes.
@ -131,7 +58,6 @@ class CAdvMapInt : public CIntObject
public:
CAdvMapInt();
~CAdvMapInt();
int3 position; //top left corner of visible map part
PlayerColor player;
@ -169,8 +95,8 @@ public:
WorldViewOptions worldViewOptions;
SDL_Surface * bg;
SDL_Surface * bgWorldView;
std::shared_ptr<IImage> bg;
std::shared_ptr<IImage> bgWorldView;
std::vector<std::shared_ptr<CAnimImage>> gems;
CMinimap minimap;
std::shared_ptr<CGStatusBar> statusbar;
@ -269,6 +195,8 @@ public:
void handleMapScrollingUpdate();
void handleSwipeUpdate();
private:
void ShowMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
};
extern std::shared_ptr<CAdvMapInt> adventureInt;

View File

@ -0,0 +1,96 @@
/*
* CAdvMapPanel.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CAdvMapPanel.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include "../gui/CGuiHandler.h"
CAdvMapPanel::CAdvMapPanel(std::shared_ptr<IImage> bg, Point position)
: CIntObject()
, background(bg)
{
defActions = 255;
recActions = 255;
pos.x += position.x;
pos.y += position.y;
if (bg)
{
pos.w = bg->width();
pos.h = bg->height();
}
}
void CAdvMapPanel::addChildColorableButton(std::shared_ptr<CButton> button)
{
colorableButtons.push_back(button);
addChildToPanel(button, ACTIVATE | DEACTIVATE);
}
void CAdvMapPanel::setPlayerColor(const PlayerColor & clr)
{
for(auto & button : colorableButtons)
{
button->setPlayerColor(clr);
}
}
void CAdvMapPanel::showAll(SDL_Surface * to)
{
if(background)
background->draw(to, pos.x, pos.y);
CIntObject::showAll(to);
}
void CAdvMapPanel::addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions)
{
otherObjects.push_back(obj);
obj->recActions |= actions | SHOWALL;
obj->recActions &= ~DISPOSE;
addChild(obj.get(), false);
}
CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color)
: CAdvMapPanel(bg, position), icons(_icons)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
int fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
if(fillerHeight > 0)
{
backgroundFiller = std::make_shared<CFilledTexture>("DIBOXBCK", Rect(0, pos.h, pos.w, fillerHeight));
}
}
CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel() = default;
void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset)
{
assert(iconsData.size() == currentIcons.size());
for(size_t idx = 0; idx < iconsData.size(); idx++)
{
const auto & data = iconsData.at(idx);
currentIcons[idx]->setFrame(data.first + indexOffset);
}
}
void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOffset)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
iconsData.push_back(data);
currentIcons.push_back(std::make_shared<CAnimImage>(icons, data.first + indexOffset, 0, data.second.x, data.second.y));
}

View File

@ -0,0 +1,60 @@
/*
* CAdvMapPanel.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class CAnimation;
class CAnimImage;
class CFilledTexture;
class CButton;
class IImage;
/// simple panel that contains other displayable elements; used to separate groups of controls
class CAdvMapPanel : public CIntObject
{
std::vector<std::shared_ptr<CButton>> colorableButtons;
std::vector<std::shared_ptr<CIntObject>> otherObjects;
/// the surface passed to this obj will be freed in dtor
std::shared_ptr<IImage> background;
public:
CAdvMapPanel(std::shared_ptr<IImage> bg, Point position);
void addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions = 0);
void addChildColorableButton(std::shared_ptr<CButton> button);
/// recolors all buttons to given player color
void setPlayerColor(const PlayerColor & clr);
void showAll(SDL_Surface * to) override;
};
/// specialized version of CAdvMapPanel that handles recolorable def-based pictures for world view info panel
class CAdvMapWorldViewPanel : public CAdvMapPanel
{
/// data that allows reconstruction of panel info icons
std::vector<std::pair<int, Point>> iconsData;
/// ptrs to child-pictures constructed from iconsData
std::vector<std::shared_ptr<CAnimImage>> currentIcons;
/// surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
std::shared_ptr<CFilledTexture> backgroundFiller;
std::shared_ptr<CAnimation> icons;
public:
CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color);
virtual ~CAdvMapWorldViewPanel();
void addChildIcon(std::pair<int, Point> data, int indexOffset);
/// recreates all pictures from given def to recolor them according to current player color
void recolorIcons(const PlayerColor & color, int indexOffset);
};

View File

@ -0,0 +1,61 @@
/*
* CAdventureOptions.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CAdventureOptions.h"
#include "CAdvMapInt.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../lobby/CCampaignInfoScreen.h"
#include "../lobby/CScenarioInfoScreen.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/Buttons.h"
#include "../../CCallback.h"
#include "../../lib/StartInfo.h"
CAdventureOptions::CAdventureOptions()
: CWindowObject(PLAYER_COLORED, "ADVOPTS")
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_v);
viewWorld->addCallback(std::bind(&CPlayerInterface::viewWorldMap, LOCPLINT));
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), SDLK_RETURN);
exit->assignedKeys.insert(SDLK_ESCAPE);
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_i);
scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_p);
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
dig = std::make_shared<CButton>(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
if(const CGHeroInstance *h = adventureInt->curHero())
dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
else
dig->block(true);
}
void CAdventureOptions::showScenarioInfo()
{
if(LOCPLINT->cb->getStartInfo()->campState)
{
GH.pushIntT<CCampaignInfoScreen>();
}
else
{
GH.pushIntT<CScenarioInfoScreen>();
}
}

View File

@ -0,0 +1,30 @@
/*
* CAdventureOptions.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../windows/CWindowObject.h"
class CButton;
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
class CAdventureOptions : public CWindowObject
{
public:
std::shared_ptr<CButton> exit;
std::shared_ptr<CButton> viewWorld;
std::shared_ptr<CButton> puzzle;
std::shared_ptr<CButton> dig;
std::shared_ptr<CButton> scenInfo;
/*std::shared_ptr<CButton> replay*/
CAdventureOptions();
static void showScenarioInfo();
};

View File

@ -0,0 +1,261 @@
/*
* CInGameConsole.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 "CInGameConsole.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../ClientCommandManager.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/mapObjects/CArmedInstance.h"
#include <SDL_timer.h>
#include <SDL_events.h>
CInGameConsole::CInGameConsole()
: CIntObject(KEYBOARD | TEXTINPUT),
prevEntDisp(-1),
defaultTimeout(10000),
maxDisplayedTexts(10)
{
}
void CInGameConsole::show(SDL_Surface * to)
{
int number = 0;
std::vector<std::list< std::pair< std::string, uint32_t > >::iterator> toDel;
boost::unique_lock<boost::mutex> lock(texts_mx);
for(auto it = texts.begin(); it != texts.end(); ++it, ++number)
{
Point leftBottomCorner(0, pos.h);
graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN,
Point(leftBottomCorner.x + 50, leftBottomCorner.y - (int)texts.size() * 20 - 80 + number*20));
if((int)(SDL_GetTicks() - it->second) > defaultTimeout)
{
toDel.push_back(it);
}
}
for(auto & elem : toDel)
{
texts.erase(elem);
}
}
void CInGameConsole::print(const std::string &txt)
{
boost::unique_lock<boost::mutex> lock(texts_mx);
int lineLen = conf.go()->ac.outputLineLength;
if(txt.size() < lineLen)
{
texts.push_back(std::make_pair(txt, SDL_GetTicks()));
if(texts.size() > maxDisplayedTexts)
{
texts.pop_front();
}
}
else
{
assert(lineLen);
for(int g=0; g<txt.size() / lineLen + 1; ++g)
{
std::string part = txt.substr(g * lineLen, lineLen);
if(part.size() == 0)
break;
texts.push_back(std::make_pair(part, SDL_GetTicks()));
if(texts.size() > maxDisplayedTexts)
{
texts.pop_front();
}
}
}
}
void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
{
if(key.type != SDL_KEYDOWN) return;
if(!captureAllKeys && key.keysym.sym != SDLK_TAB) return; //because user is not entering any text
switch(key.keysym.sym)
{
case SDLK_TAB:
case SDLK_ESCAPE:
{
if(captureAllKeys)
{
endEnteringText(false);
}
else if(SDLK_TAB == key.keysym.sym)
{
startEnteringText();
}
break;
}
case SDLK_RETURN: //enter key
{
if(!enteredText.empty() && captureAllKeys)
{
bool anyTextExceptCaret = enteredText.size() > 1;
endEnteringText(anyTextExceptCaret);
if(anyTextExceptCaret)
{
CCS->soundh->playSound("CHAT");
}
}
break;
}
case SDLK_BACKSPACE:
{
if(enteredText.size() > 1)
{
Unicode::trimRight(enteredText,2);
enteredText += '_';
refreshEnteredText();
}
break;
}
case SDLK_UP: //up arrow
{
if(previouslyEntered.size() == 0)
break;
if(prevEntDisp == -1)
{
prevEntDisp = static_cast<int>(previouslyEntered.size() - 1);
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
else if( prevEntDisp > 0)
{
--prevEntDisp;
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
break;
}
case SDLK_DOWN: //down arrow
{
if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
{
++prevEntDisp;
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature
{
prevEntDisp = -1;
enteredText = "_";
refreshEnteredText();
}
break;
}
default:
{
break;
}
}
}
void CInGameConsole::textInputed(const SDL_TextInputEvent & event)
{
if(!captureAllKeys || enteredText.size() == 0)
return;
enteredText.resize(enteredText.size()-1);
enteredText += event.text;
enteredText += "_";
refreshEnteredText();
}
void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
{
//do nothing here
}
void CInGameConsole::startEnteringText()
{
if (!active)
return;
if (captureAllKeys)
return;
assert(GH.statusbar);
assert(currentStatusBar.expired());//effectively, nullptr check
currentStatusBar = GH.statusbar;
captureAllKeys = true;
enteredText = "_";
GH.statusbar->setEnteringMode(true);
GH.statusbar->setEnteredText(enteredText);
}
void CInGameConsole::endEnteringText(bool processEnteredText)
{
captureAllKeys = false;
prevEntDisp = -1;
if(processEnteredText)
{
std::string txt = enteredText.substr(0, enteredText.size()-1);
previouslyEntered.push_back(txt);
if(txt.at(0) == '/')
{
//some commands like gosolo don't work when executed from GUI thread
auto threadFunction = [=]()
{
ClientCommandManager commandController;
commandController.processCommand(txt.substr(1), true);
};
boost::thread clientCommandThread(threadFunction);
clientCommandThread.detach();
}
else
LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
}
enteredText.clear();
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteringMode(false);
currentStatusBar.reset();
}
void CInGameConsole::refreshEnteredText()
{
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteredText(enteredText);
}

View File

@ -0,0 +1,39 @@
/*
* CInGameConsole.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
class CInGameConsole : public CIntObject
{
private:
std::list< std::pair< std::string, uint32_t > > texts; //list<text to show, time of add>
boost::mutex texts_mx; // protects texts
std::vector< std::string > previouslyEntered; //previously entered texts, for up/down arrows to work
int prevEntDisp; //displayed entry from previouslyEntered - if none it's -1
int defaultTimeout; //timeout for new texts (in ms)
int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
std::weak_ptr<IStatusBar> currentStatusBar;
public:
std::string enteredText;
void show(SDL_Surface * to) override;
void print(const std::string &txt);
void keyPressed (const SDL_KeyboardEvent & key) override; //call-in
void textInputed(const SDL_TextInputEvent & event) override;
void textEdited(const SDL_TextEditingEvent & event) override;
void startEnteringText();
void endEnteringText(bool processEnteredText);
void refreshEnteredText();
CInGameConsole();
};

View File

@ -0,0 +1,323 @@
/*
* CInfoBar.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 "CInfoBar.h"
#include "CAdvMapInt.h"
#include "../widgets/CComponent.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
#include "../widgets/MiscWidgets.h"
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../../CCallback.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
CInfoBar::CVisibleInfo::CVisibleInfo()
: CIntObject(0, Point(8, 12))
{
}
void CInfoBar::CVisibleInfo::show(SDL_Surface * to)
{
CIntObject::show(to);
for(auto object : forceRefresh)
object->showAll(to);
}
CInfoBar::EmptyVisibleInfo::EmptyVisibleInfo()
{
}
CInfoBar::VisibleHeroInfo::VisibleHeroInfo(const CGHeroInstance * hero)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATHR");
heroTooltip = std::make_shared<CHeroTooltip>(Point(0,0), hero);
}
CInfoBar::VisibleTownInfo::VisibleTownInfo(const CGTownInstance * town)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATCS");
townTooltip = std::make_shared<CTownTooltip>(Point(0,0), town);
}
CInfoBar::VisibleDateInfo::VisibleDateInfo()
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
animation = std::make_shared<CShowableAnim>(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE, 180);// H3 uses around 175-180 ms per frame
std::string labelText;
if(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) == 1 && LOCPLINT->cb->getDate(Date::DAY) != 1) // monday of any week but first - show new week info
labelText = CGI->generaltexth->allTexts[63] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK));
else
labelText = CGI->generaltexth->allTexts[64] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK));
label = std::make_shared<CLabel>(95, 31, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, labelText);
forceRefresh.push_back(label);
}
std::string CInfoBar::VisibleDateInfo::getNewDayName()
{
if(LOCPLINT->cb->getDate(Date::DAY) == 1)
return "NEWDAY";
if(LOCPLINT->cb->getDate(Date::DAY) != 1)
return "NEWDAY";
switch(LOCPLINT->cb->getDate(Date::WEEK))
{
case 1:
return "NEWWEEK1";
case 2:
return "NEWWEEK2";
case 3:
return "NEWWEEK3";
case 4:
return "NEWWEEK4";
default:
return "";
}
}
CInfoBar::VisibleEnemyTurnInfo::VisibleEnemyTurnInfo(PlayerColor player)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATNX");
banner = std::make_shared<CAnimImage>("CREST58", player.getNum(), 0, 20, 51);
sand = std::make_shared<CShowableAnim>(99, 51, "HOURSAND", 0, 100); // H3 uses around 100 ms per frame
glass = std::make_shared<CShowableAnim>(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 1000); // H3 scales this nicely for AI turn duration, don't have anything like that in vcmi
}
CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo()
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
//get amount of halls of each level
std::vector<int> halls(4, 0);
for(auto town : LOCPLINT->towns)
{
int hallLevel = town->hallLevel();
//negative value means no village hall, unlikely but possible
if(hallLevel >= 0)
halls.at(hallLevel)++;
}
std::vector<PlayerColor> allies, enemies;
//generate list of allies and enemies
for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
{
if(LOCPLINT->cb->getPlayerStatus(PlayerColor(i), false) == EPlayerStatus::INGAME)
{
if(LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, PlayerColor(i)) != PlayerRelations::ENEMIES)
allies.push_back(PlayerColor(i));
else
enemies.push_back(PlayerColor(i));
}
}
//generate widgets
background = std::make_shared<CPicture>("ADSTATIN");
allyLabel = std::make_shared<CLabel>(10, 106, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[390] + ":");
enemyLabel = std::make_shared<CLabel>(10, 136, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[391] + ":");
int posx = allyLabel->pos.w + allyLabel->pos.x - pos.x + 4;
for(PlayerColor & player : allies)
{
auto image = std::make_shared<CAnimImage>("ITGFLAGS", player.getNum(), 0, posx, 102);
posx += image->pos.w;
flags.push_back(image);
}
posx = enemyLabel->pos.w + enemyLabel->pos.x - pos.x + 4;
for(PlayerColor & player : enemies)
{
auto image = std::make_shared<CAnimImage>("ITGFLAGS", player.getNum(), 0, posx, 132);
posx += image->pos.w;
flags.push_back(image);
}
for(size_t i=0; i<halls.size(); i++)
{
hallIcons.push_back(std::make_shared<CAnimImage>("itmtl", i, 0, 6 + 42 * (int)i , 11));
if(halls[i])
hallLabels.push_back(std::make_shared<CLabel>( 26 + 42 * (int)i, 64, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, boost::lexical_cast<std::string>(halls[i])));
}
}
CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const Component & compToDisplay, std::string message)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>("ADSTATOT", 1, 0);
comp = std::make_shared<CComponent>(compToDisplay);
comp->moveTo(Point(pos.x+47, pos.y+50));
text = std::make_shared<CTextBox>(message, Rect(10, 4, 160, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
}
void CInfoBar::playNewDaySound()
{
if(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) != 1) // not first day of the week
CCS->soundh->playSound(soundBase::newDay);
else if(LOCPLINT->cb->getDate(Date::WEEK) != 1) // not first week in month
CCS->soundh->playSound(soundBase::newWeek);
else if(LOCPLINT->cb->getDate(Date::MONTH) != 1) // not first month
CCS->soundh->playSound(soundBase::newMonth);
else
CCS->soundh->playSound(soundBase::newDay);
}
void CInfoBar::reset()
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
state = EMPTY;
visibleInfo = std::make_shared<EmptyVisibleInfo>();
}
void CInfoBar::showSelection()
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
if(adventureInt->selection)
{
if(auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
{
showHeroSelection(hero);
return;
}
else if(auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection))
{
showTownSelection(town);
return;
}
}
showGameStatus();//FIXME: may be incorrect but shouldn't happen in general
}
void CInfoBar::tick()
{
removeUsedEvents(TIME);
if(GH.topInt() == adventureInt)
showSelection();
}
void CInfoBar::clickLeft(tribool down, bool previousState)
{
if(down)
{
if(state == HERO || state == TOWN)
showGameStatus();
else if(state == GAME)
showDate();
else
showSelection();
}
}
void CInfoBar::clickRight(tribool down, bool previousState)
{
adventureInt->handleRightClick(CGI->generaltexth->allTexts[109], down);
}
void CInfoBar::hover(bool on)
{
if(on)
GH.statusbar->write(CGI->generaltexth->zelp[292].first);
else
GH.statusbar->clear();
}
CInfoBar::CInfoBar(const Rect & position)
: CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()),
state(EMPTY)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
pos.w = position.w;
pos.h = position.h;
reset();
}
void CInfoBar::showDate()
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
playNewDaySound();
state = DATE;
visibleInfo = std::make_shared<VisibleDateInfo>();
setTimer(3000); // confirmed to match H3
redraw();
}
void CInfoBar::showComponent(const Component & comp, std::string message)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
state = COMPONENT;
visibleInfo = std::make_shared<VisibleComponentInfo>(comp, message);
setTimer(3000);
redraw();
}
void CInfoBar::startEnemyTurn(PlayerColor color)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
state = AITURN;
visibleInfo = std::make_shared<VisibleEnemyTurnInfo>(color);
redraw();
}
void CInfoBar::showHeroSelection(const CGHeroInstance * hero)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
if(!hero)
{
reset();
}
else
{
state = HERO;
visibleInfo = std::make_shared<VisibleHeroInfo>(hero);
}
redraw();
}
void CInfoBar::showTownSelection(const CGTownInstance * town)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
if(!town)
{
reset();
}
else
{
state = TOWN;
visibleInfo = std::make_shared<VisibleTownInfo>(town);
}
redraw();
}
void CInfoBar::showGameStatus()
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
state = GAME;
visibleInfo = std::make_shared<VisibleGameStatusInfo>();
setTimer(3000);
redraw();
}

View File

@ -0,0 +1,146 @@
/*
* CInfoBar.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CGTownInstance;
struct Component;
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class CAnimImage;
class CShowableAnim;
class CComponent;
class CHeroTooltip;
class CTownTooltip;
class CLabel;
class CTextBox;
/// Info box which shows next week/day information, hold the current date
class CInfoBar : public CIntObject
{
//all visible information located in one object - for ease of replacing
class CVisibleInfo : public CIntObject
{
public:
void show(SDL_Surface * to) override;
protected:
std::shared_ptr<CPicture> background;
std::list<std::shared_ptr<CIntObject>> forceRefresh;
CVisibleInfo();
};
class EmptyVisibleInfo : public CVisibleInfo
{
public:
EmptyVisibleInfo();
};
class VisibleHeroInfo : public CVisibleInfo
{
std::shared_ptr<CHeroTooltip> heroTooltip;
public:
VisibleHeroInfo(const CGHeroInstance * hero);
};
class VisibleTownInfo : public CVisibleInfo
{
std::shared_ptr<CTownTooltip> townTooltip;
public:
VisibleTownInfo(const CGTownInstance * town);
};
class VisibleDateInfo : public CVisibleInfo
{
std::shared_ptr<CShowableAnim> animation;
std::shared_ptr<CLabel> label;
std::string getNewDayName();
public:
VisibleDateInfo();
};
class VisibleEnemyTurnInfo : public CVisibleInfo
{
std::shared_ptr<CAnimImage> banner;
std::shared_ptr<CShowableAnim> glass;
std::shared_ptr<CShowableAnim> sand;
public:
VisibleEnemyTurnInfo(PlayerColor player);
};
class VisibleGameStatusInfo : public CVisibleInfo
{
std::shared_ptr<CLabel> allyLabel;
std::shared_ptr<CLabel> enemyLabel;
std::vector<std::shared_ptr<CAnimImage>> flags;
std::vector<std::shared_ptr<CAnimImage>> hallIcons;
std::vector<std::shared_ptr<CLabel>> hallLabels;
public:
VisibleGameStatusInfo();
};
class VisibleComponentInfo : public CVisibleInfo
{
std::shared_ptr<CComponent> comp;
std::shared_ptr<CTextBox> text;
public:
VisibleComponentInfo(const Component & compToDisplay, std::string message);
};
enum EState
{
EMPTY, HERO, TOWN, DATE, GAME, AITURN, COMPONENT
};
std::shared_ptr<CVisibleInfo> visibleInfo;
EState state;
//removes all information about current state, deactivates timer (if any)
void reset();
void tick() override;
void clickLeft(tribool down, bool previousState) override;
void clickRight(tribool down, bool previousState) override;
void hover(bool on) override;
void playNewDaySound();
public:
CInfoBar(const Rect & pos);
/// show new day/week animation
void showDate();
/// show component for 3 seconds. Used to display picked up resources
void showComponent(const Component & comp, std::string message);
/// print enemy turn progress
void startEnemyTurn(PlayerColor color);
/// reset to default view - selected object
void showSelection();
/// show hero\town information
void showHeroSelection(const CGHeroInstance * hero);
void showTownSelection(const CGTownInstance * town);
/// for 3 seconds shows amount of town halls and players status
void showGameStatus();
};

View File

@ -0,0 +1,339 @@
/*
* CList.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 "CList.h"
#include "CAdvMapInt.h"
#include "../widgets/Images.h"
#include "../widgets/Buttons.h"
#include "../windows/InfoWindows.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/CModHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
CList::CListItem::CListItem(CList * Parent)
: CIntObject(LCLICK | RCLICK | HOVER),
parent(Parent),
selection()
{
defActions = 255-DISPOSE;
}
CList::CListItem::~CListItem()
{
}
void CList::CListItem::clickRight(tribool down, bool previousState)
{
if (down == true)
showTooltip();
}
void CList::CListItem::clickLeft(tribool down, bool previousState)
{
if(down == true)
{
//second click on already selected item
if(parent->selected == this->shared_from_this())
{
open();
}
else
{
//first click - switch selection
parent->select(this->shared_from_this());
}
}
}
void CList::CListItem::hover(bool on)
{
if (on)
GH.statusbar->write(getHoverText());
else
GH.statusbar->clear();
}
void CList::CListItem::onSelect(bool on)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
selection.reset();
if(on)
selection = genSelection();
select(on);
GH.totalRedraw();
}
CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create)
: CIntObject(0, position),
size(Size),
selected(nullptr)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
scrollUp = std::make_shared<CButton>(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]);
scrollDown = std::make_shared<CButton>(Point(0, scrollUp->pos.h + 32*(int)size), btnDown, CGI->generaltexth->zelp[helpDown]);
listBox = std::make_shared<CListBox>(create, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
//assign callback only after list was created
scrollUp->addCallback(std::bind(&CListBox::moveToPrev, listBox));
scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox));
scrollUp->addCallback(std::bind(&CList::update, this));
scrollDown->addCallback(std::bind(&CList::update, this));
update();
}
void CList::update()
{
bool onTop = listBox->getPos() == 0;
bool onBottom = listBox->getPos() + size >= listBox->size();
scrollUp->block(onTop);
scrollDown->block(onBottom);
}
void CList::select(std::shared_ptr<CListItem> which)
{
if(selected == which)
return;
if(selected)
selected->onSelect(false);
selected = which;
if(which)
{
which->onSelect(true);
onSelect();
}
}
int CList::getSelectedIndex()
{
return static_cast<int>(listBox->getIndexOf(selected));
}
void CList::selectIndex(int which)
{
if(which < 0)
{
if(selected)
select(nullptr);
}
else
{
listBox->scrollTo(which);
update();
select(std::dynamic_pointer_cast<CListItem>(listBox->getItem(which)));
}
}
void CList::selectNext()
{
int index = getSelectedIndex() + 1;
if(index >= listBox->size())
index = 0;
selectIndex(index);
}
void CList::selectPrev()
{
int index = getSelectedIndex();
if(index <= 0)
selectIndex(0);
else
selectIndex(index-1);
}
CHeroList::CEmptyHeroItem::CEmptyHeroItem()
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
movement = std::make_shared<CAnimImage>("IMOBIL", 0, 0, 0, 1);
portrait = std::make_shared<CPicture>("HPSXXX", movement->pos.w + 1, 0);
mana = std::make_shared<CAnimImage>("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1 );
pos.w = mana->pos.w + mana->pos.x - pos.x;
pos.h = std::max(std::max<int>(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h);
}
CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
: CListItem(parent),
hero(Hero)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
movement = std::make_shared<CAnimImage>("IMOBIL", 0, 0, 0, 1);
portrait = std::make_shared<CAnimImage>("PortraitsSmall", hero->portrait, 0, movement->pos.w + 1);
mana = std::make_shared<CAnimImage>("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1);
pos.w = mana->pos.w + mana->pos.x - pos.x;
pos.h = std::max(std::max<int>(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h);
update();
}
void CHeroList::CHeroItem::update()
{
movement->setFrame(std::min<size_t>(movement->size()-1, hero->movement / 100));
mana->setFrame(std::min<size_t>(mana->size()-1, hero->mana / 5));
redraw();
}
std::shared_ptr<CIntObject> CHeroList::CHeroItem::genSelection()
{
return std::make_shared<CPicture>("HPSYYY", movement->pos.w + 1, 0);
}
void CHeroList::CHeroItem::select(bool on)
{
if(on && adventureInt->selection != hero)
adventureInt->select(hero);
}
void CHeroList::CHeroItem::open()
{
LOCPLINT->openHeroWindow(hero);
}
void CHeroList::CHeroItem::showTooltip()
{
CRClickPopup::createAndPush(hero, GH.getCursorPosition());
}
std::string CHeroList::CHeroItem::getHoverText()
{
return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->type->heroClass->getNameTranslated());
}
std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
{
if (LOCPLINT->wanderingHeroes.size() > index)
return std::make_shared<CHeroItem>(this, LOCPLINT->wanderingHeroes[index]);
return std::make_shared<CEmptyHeroItem>();
}
CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
{
}
void CHeroList::select(const CGHeroInstance * hero)
{
selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
}
void CHeroList::update(const CGHeroInstance * hero)
{
//this hero is already present, update its status
for(auto & elem : listBox->getItems())
{
auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
if(item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero))
{
item->update();
return;
}
}
//simplest solution for now: reset list and restore selection
listBox->resize(LOCPLINT->wanderingHeroes.size());
if (adventureInt->selection)
{
auto selectedHero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
if (selectedHero)
select(selectedHero);
}
CList::update();
}
std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
{
if (LOCPLINT->towns.size() > index)
return std::make_shared<CTownItem>(this, LOCPLINT->towns[index]);
return std::make_shared<CAnimImage>("ITPA", 0);
}
CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
CListItem(parent),
town(Town)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
picture = std::make_shared<CAnimImage>("ITPA", 0);
pos = picture->pos;
update();
}
std::shared_ptr<CIntObject> CTownList::CTownItem::genSelection()
{
return std::make_shared<CAnimImage>("ITPA", 1);
}
void CTownList::CTownItem::update()
{
size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
picture->setFrame(iconIndex + 2);
redraw();
}
void CTownList::CTownItem::select(bool on)
{
if (on && adventureInt->selection != town)
adventureInt->select(town);
}
void CTownList::CTownItem::open()
{
LOCPLINT->openTownWindow(town);
}
void CTownList::CTownItem::showTooltip()
{
CRClickPopup::createAndPush(town, GH.getCursorPosition());
}
std::string CTownList::CTownItem::getHoverText()
{
return town->getObjectName();
}
CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
{
}
void CTownList::select(const CGTownInstance * town)
{
selectIndex(vstd::find_pos(LOCPLINT->towns, town));
}
void CTownList::update(const CGTownInstance *)
{
//simplest solution for now: reset list and restore selection
listBox->resize(LOCPLINT->towns.size());
if (adventureInt->selection)
{
auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
if (town)
select(town);
}
CList::update();
}

176
client/adventureMap/CList.h Normal file
View File

@ -0,0 +1,176 @@
/*
* CList.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
#include "../widgets/ObjectLists.h"
#include "../../lib/FunctionList.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CGTownInstance;
VCMI_LIB_NAMESPACE_END
class CButton;
/// Base UI Element for hero\town lists
class CList : public CIntObject
{
protected:
class CListItem : public CIntObject, public std::enable_shared_from_this<CListItem>
{
CList * parent;
std::shared_ptr<CIntObject> selection;
public:
CListItem(CList * parent);
~CListItem();
void clickRight(tribool down, bool previousState) override;
void clickLeft(tribool down, bool previousState) override;
void hover(bool on) override;
void onSelect(bool on);
/// create object with selection rectangle
virtual std::shared_ptr<CIntObject> genSelection()=0;
/// reaction on item selection (e.g. enable selection border)
/// NOTE: item may be deleted in selected state
virtual void select(bool on)=0;
/// open item (town or hero screen)
virtual void open()=0;
/// show right-click tooltip
virtual void showTooltip()=0;
/// get hover text for status bar
virtual std::string getHoverText()=0;
};
std::shared_ptr<CListBox> listBox;
const size_t size;
/**
* @brief CList - protected constructor
* @param size - maximal amount of visible at once items
* @param position - cordinates
* @param btnUp - path to image to use as top button
* @param btnDown - path to image to use as bottom button
* @param listAmount - amount of items in the list
* @param helpUp - index in zelp.txt for button help tooltip
* @param helpDown - index in zelp.txt for button help tooltip
* @param create - function for creating items in listbox
* @param destroy - function for deleting items in listbox
*/
CList(int size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create);
//for selection\deselection
std::shared_ptr<CListItem> selected;
void select(std::shared_ptr<CListItem> which);
friend class CListItem;
std::shared_ptr<CButton> scrollUp;
std::shared_ptr<CButton> scrollDown;
/// should be called when list is invalidated
void update();
public:
/// functions that will be called when selection changes
CFunctionList<void()> onSelect;
/// return index of currently selected element
int getSelectedIndex();
/// set of methods to switch selection
void selectIndex(int which);
void selectNext();
void selectPrev();
};
/// List of heroes which is shown at the right of the adventure map screen
class CHeroList : public CList
{
/// Empty hero item used as placeholder for unused entries in list
class CEmptyHeroItem : public CIntObject
{
std::shared_ptr<CAnimImage> movement;
std::shared_ptr<CAnimImage> mana;
std::shared_ptr<CPicture> portrait;
public:
CEmptyHeroItem();
};
class CHeroItem : public CListItem
{
std::shared_ptr<CAnimImage> movement;
std::shared_ptr<CAnimImage> mana;
std::shared_ptr<CAnimImage> portrait;
public:
const CGHeroInstance * const hero;
CHeroItem(CHeroList * parent, const CGHeroInstance * hero);
std::shared_ptr<CIntObject> genSelection() override;
void update();
void select(bool on) override;
void open() override;
void showTooltip() override;
std::string getHoverText() override;
};
std::shared_ptr<CIntObject> createHeroItem(size_t index);
public:
/**
* @brief CHeroList
* @param size, position, btnUp, btnDown @see CList::CList
*/
CHeroList(int size, Point position, std::string btnUp, std::string btnDown);
/// Select specific hero and scroll if needed
void select(const CGHeroInstance * hero = nullptr);
/// Update hero. Will add or remove it from the list if needed
void update(const CGHeroInstance * hero = nullptr);
};
/// List of towns which is shown at the right of the adventure map screen or in the town screen
class CTownList : public CList
{
class CTownItem : public CListItem
{
std::shared_ptr<CAnimImage> picture;
public:
const CGTownInstance * const town;
CTownItem(CTownList *parent, const CGTownInstance * town);
std::shared_ptr<CIntObject> genSelection() override;
void update();
void select(bool on) override;
void open() override;
void showTooltip() override;
std::string getHoverText() override;
};
std::shared_ptr<CIntObject> createTownItem(size_t index);
public:
/**
* @brief CTownList
* @param size, position, btnUp, btnDown @see CList::CList
*/
CTownList(int size, Point position, std::string btnUp, std::string btnDown);
/// Select specific town and scroll if needed
void select(const CGTownInstance * town = nullptr);
/// Update town. Will add or remove it from the list if needed
void update(const CGTownInstance * town = nullptr);
};

View File

@ -0,0 +1,317 @@
/*
* CMinimap.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 "CMinimap.h"
#include "CAdvMapInt.h"
#include "../widgets/Images.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../renderSDL/SDL_PixelAccess.h"
#include "../../CCallback.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/TerrainHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMapDefines.h"
#include <SDL_surface.h>
const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
{
const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
// if tile is not visible it will be black on minimap
if(!tile)
return Colors::BLACK;
// if object at tile is owned - it will be colored as its owner
for (const CGObjectInstance *obj : tile->blockingObjects)
{
//heroes will be blitted later
switch (obj->ID)
{
case Obj::HERO:
case Obj::PRISON:
continue;
}
PlayerColor player = obj->getOwner();
if(player == PlayerColor::NEUTRAL)
return *graphics->neutralColor;
else
if (player < PlayerColor::PLAYER_LIMIT)
return graphics->playerColors[player.getNum()];
}
// else - use terrain color (blocked version or normal)
const auto & colorPair = parent->colors.find(tile->terType->getId())->second;
if (tile->blocked && (!tile->visitable))
return colorPair.second;
else
return colorPair.first;
}
void CMinimapInstance::tileToPixels (const int3 &tile, int &x, int &y, int toX, int toY)
{
int3 mapSizes = LOCPLINT->cb->getMapSize();
double stepX = double(pos.w) / mapSizes.x;
double stepY = double(pos.h) / mapSizes.y;
x = static_cast<int>(toX + stepX * tile.x);
y = static_cast<int>(toY + stepY * tile.y);
}
void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY)
{
//coordinates of rectangle on minimap representing this tile
// begin - first to blit, end - first NOT to blit
int xBegin, yBegin, xEnd, yEnd;
tileToPixels (tile, xBegin, yBegin, toX, toY);
tileToPixels (int3 (tile.x + 1, tile.y + 1, tile.z), xEnd, yEnd, toX, toY);
for (int y=yBegin; y<yEnd; y++)
{
uint8_t *ptr = (uint8_t*)to->pixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel;
for (int x=xBegin; x<xEnd; x++)
ColorPutter<4, 1>::PutColor(ptr, color);
}
}
void CMinimapInstance::refreshTile(const int3 &tile)
{
blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0);
}
void CMinimapInstance::drawScaled(int level)
{
int3 mapSizes = LOCPLINT->cb->getMapSize();
//size of one map tile on our minimap
double stepX = double(pos.w) / mapSizes.x;
double stepY = double(pos.h) / mapSizes.y;
double currY = 0;
for (int y=0; y<mapSizes.y; y++, currY += stepY)
{
double currX = 0;
for (int x=0; x<mapSizes.x; x++, currX += stepX)
{
const SDL_Color &color = getTileColor(int3(x,y,level));
//coordinates of rectangle on minimap representing this tile
// begin - first to blit, end - first NOT to blit
int xBegin = static_cast<int>(currX);
int yBegin = static_cast<int>(currY);
int xEnd = static_cast<int>(currX + stepX);
int yEnd = static_cast<int>(currY + stepY);
for (int y=yBegin; y<yEnd; y++)
{
uint8_t *ptr = (uint8_t*)minimap->pixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel;
for (int x=xBegin; x<xEnd; x++)
ColorPutter<4, 1>::PutColor(ptr, color);
}
}
}
}
CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level):
parent(Parent),
minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)),
level(Level)
{
pos.w = parent->pos.w;
pos.h = parent->pos.h;
drawScaled(level);
}
CMinimapInstance::~CMinimapInstance()
{
SDL_FreeSurface(minimap);
}
void CMinimapInstance::showAll(SDL_Surface * to)
{
blitAtLoc(minimap, 0, 0, to);
//draw heroes
std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes?
for(auto & hero : heroes)
{
int3 position = hero->visitablePos();
if(position.z == level)
{
const SDL_Color & color = graphics->playerColors[hero->getOwner().getNum()];
blitTileWithColor(color, position, to, pos.x, pos.y);
}
}
}
std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors()
{
std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > ret;
for(const auto & terrain : CGI->terrainTypeHandler->objects)
{
SDL_Color normal = CSDL_Ext::toSDL(terrain->minimapUnblocked);
SDL_Color blocked = CSDL_Ext::toSDL(terrain->minimapBlocked);
ret[terrain->getId()] = std::make_pair(normal, blocked);
}
return ret;
}
CMinimap::CMinimap(const Rect & position)
: CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()),
level(0),
colors(loadColors())
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
pos.w = position.w;
pos.h = position.h;
aiShield = std::make_shared<CPicture>("AIShield");
aiShield->disable();
}
int3 CMinimap::translateMousePosition()
{
// 0 = top-left corner, 1 = bottom-right corner
double dx = double(GH.getCursorPosition().x - pos.x) / pos.w;
double dy = double(GH.getCursorPosition().y - pos.y) / pos.h;
int3 mapSizes = LOCPLINT->cb->getMapSize();
int3 tile ((si32)(mapSizes.x * dx), (si32)(mapSizes.y * dy), level);
return tile;
}
void CMinimap::moveAdvMapSelection()
{
int3 newLocation = translateMousePosition();
adventureInt->centerOn(newLocation);
if (!(adventureInt->active & GENERAL))
GH.totalRedraw(); //redraw this as well as inactive adventure map
else
redraw();//redraw only this
}
void CMinimap::clickLeft(tribool down, bool previousState)
{
if(down)
moveAdvMapSelection();
}
void CMinimap::clickRight(tribool down, bool previousState)
{
adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down);
}
void CMinimap::hover(bool on)
{
if(on)
GH.statusbar->write(CGI->generaltexth->zelp[291].first);
else
GH.statusbar->clear();
}
void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent)
{
if(mouseState(EIntObjMouseBtnType::LEFT))
moveAdvMapSelection();
}
void CMinimap::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
if(minimap)
{
int3 mapSizes = LOCPLINT->cb->getMapSize();
int3 tileCountOnScreen = adventureInt->terrain.tileCountOnScreen();
//draw radar
Rect oldClip;
Rect radar =
{
si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x),
si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y),
ui16(tileCountOnScreen.x * pos.w / mapSizes.x),
ui16(tileCountOnScreen.y * pos.h / mapSizes.y)
};
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
{
// adjusts radar so that it doesn't go out of map in world view mode (since there's no frame)
radar.x = std::min<int>(std::max(pos.x, radar.x), pos.x + pos.w - radar.w);
radar.y = std::min<int>(std::max(pos.y, radar.y), pos.y + pos.h - radar.h);
if(radar.x < pos.x && radar.y < pos.y)
return; // whole map is visible at once, no point in redrawing border
}
CSDL_Ext::getClipRect(to, oldClip);
CSDL_Ext::setClipRect(to, pos);
CSDL_Ext::drawDashedBorder(to, radar, Colors::PURPLE);
CSDL_Ext::setClipRect(to, oldClip);
}
}
void CMinimap::update()
{
if(aiShield->recActions & UPDATE) //AI turn is going on. There is no need to update minimap
return;
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
minimap = std::make_shared<CMinimapInstance>(this, level);
redraw();
}
void CMinimap::setLevel(int newLevel)
{
level = newLevel;
update();
}
void CMinimap::setAIRadar(bool on)
{
if(on)
{
aiShield->enable();
minimap.reset();
}
else
{
aiShield->disable();
update();
}
// this my happen during AI turn when this interface is inactive
// force redraw in order to properly update interface
GH.totalRedraw();
}
void CMinimap::hideTile(const int3 &pos)
{
if(minimap)
minimap->refreshTile(pos);
}
void CMinimap::showTile(const int3 &pos)
{
if(minimap)
minimap->refreshTile(pos);
}

View File

@ -0,0 +1,77 @@
/*
* CMinimap.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
#include "../../lib/GameConstants.h"
struct SDL_Color;
class CMinimap;
class CMinimapInstance : public CIntObject
{
CMinimap * parent;
SDL_Surface * minimap;
int level;
//get color of selected tile on minimap
const SDL_Color & getTileColor(const int3 & pos);
void blitTileWithColor(const SDL_Color & color, const int3 & pos, SDL_Surface * to, int x, int y);
//draw minimap already scaled.
//result is not antialiased. Will result in "missing" pixels on huge maps (>144)
void drawScaled(int level);
public:
CMinimapInstance(CMinimap * parent, int level);
~CMinimapInstance();
void showAll(SDL_Surface * to) override;
void tileToPixels (const int3 & tile, int & x, int & y, int toX = 0, int toY = 0);
void refreshTile(const int3 & pos);
};
/// Minimap which is displayed at the right upper corner of adventure map
class CMinimap : public CIntObject
{
protected:
std::shared_ptr<CPicture> aiShield; //the graphic displayed during AI turn
std::shared_ptr<CMinimapInstance> minimap;
int level;
//to initialize colors
std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > loadColors();
void clickLeft(tribool down, bool previousState) override;
void clickRight(tribool down, bool previousState) override;
void hover (bool on) override;
void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
void moveAdvMapSelection();
public:
// terrainID -> (normal color, blocked color)
const std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > colors;
CMinimap(const Rect & position);
//should be called to invalidate whole map - different player or level
int3 translateMousePosition();
void update();
void setLevel(int level);
void setAIRadar(bool on);
void showAll(SDL_Surface * to) override;
void hideTile(const int3 &pos); //puts FoW
void showTile(const int3 &pos); //removes FoW
};

View File

@ -0,0 +1,105 @@
/*
* CResDataBar.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CResDataBar.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/Images.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#define ADVOPT (conf.go()->ac)
void CResDataBar::clickRight(tribool down, bool previousState)
{
}
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
{
pos.x += x;
pos.y += y;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(defname, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->pos.w;
pos.h = background->pos.h;
txtpos.resize(8);
for (int i = 0; i < 8 ; i++)
{
txtpos[i].first = pos.x + offx + resdist*i;
txtpos[i].second = pos.y + offy;
}
txtpos[7].first = txtpos[6].first + datedist;
datetext = CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+ ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
addUsedEvents(RCLICK);
}
CResDataBar::CResDataBar()
{
pos.x += ADVOPT.resdatabarX;
pos.y += ADVOPT.resdatabarY;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
background = std::make_shared<CPicture>(ADVOPT.resdatabarG, 0, 0);
background->colorize(LOCPLINT->playerID);
pos.w = background->pos.w;
pos.h = background->pos.h;
txtpos.resize(8);
for (int i = 0; i < 8 ; i++)
{
txtpos[i].first = pos.x + ADVOPT.resOffsetX + ADVOPT.resDist*i;
txtpos[i].second = pos.y + ADVOPT.resOffsetY;
}
txtpos[7].first = txtpos[6].first + ADVOPT.resDateDist;
datetext = CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+ ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
}
CResDataBar::~CResDataBar() = default;
void CResDataBar::draw(SDL_Surface * to)
{
//TODO: all this should be labels, but they require proper text update on change
for (auto i=Res::WOOD; i<=Res::GOLD; vstd::advance(i, 1))
{
std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first,txtpos[i].second));
}
std::vector<std::string> temp;
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::MONTH)));
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK)));
temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)));
graphics->fonts[FONT_SMALL]->renderTextLeft(to, CSDL_Ext::processStr(datetext,temp), Colors::WHITE, Point(txtpos[7].first,txtpos[7].second));
}
void CResDataBar::show(SDL_Surface * to)
{
}
void CResDataBar::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
draw(to);
}

View File

@ -0,0 +1,33 @@
/*
* CResDataBar.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
/// Resources bar which shows information about how many gold, crystals,... you have
/// Current date is displayed too
class CResDataBar : public CIntObject
{
public:
std::shared_ptr<CPicture> background;
std::vector<std::pair<int,int> > txtpos;
std::string datetext;
void clickRight(tribool down, bool previousState) override;
CResDataBar();
CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
~CResDataBar();
void draw(SDL_Surface * to);
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
};

View File

@ -0,0 +1,414 @@
/*
* CTerrainRect.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 "CTerrainRect.h"
#include "mapHandler.h"
#include "CAdvMapInt.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../render/CAnimation.h"
#include "../render/CFadeAnimation.h"
#include "../render/IImage.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../widgets/TextControls.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/CPathfinder.h"
#include <SDL_events.h>
#define ADVOPT (conf.go()->ac)
CTerrainRect::CTerrainRect()
: fadeSurface(nullptr),
lastRedrawStatus(EMapAnimRedrawStatus::OK),
fadeAnim(std::make_shared<CFadeAnimation>()),
curHoveredTile(-1,-1,-1),
currentPath(nullptr)
{
tilesw=(ADVOPT.advmapW+31)/32;
tilesh=(ADVOPT.advmapH+31)/32;
pos.x=ADVOPT.advmapX;
pos.y=ADVOPT.advmapY;
pos.w=ADVOPT.advmapW;
pos.h=ADVOPT.advmapH;
moveX = moveY = 0;
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
}
CTerrainRect::~CTerrainRect()
{
if(fadeSurface)
SDL_FreeSurface(fadeSurface);
}
void CTerrainRect::deactivate()
{
CIntObject::deactivate();
curHoveredTile = int3(-1,-1,-1); //we lost info about hovered tile when disabling
}
void CTerrainRect::clickLeft(tribool down, bool previousState)
{
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if(indeterminate(down))
return;
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(adventureInt->swipeEnabled)
{
if(handleSwipeStateChange((bool)down == true))
{
return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
}
}
else
{
#endif
if(down == false)
return;
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
}
#endif
int3 mp = whichTileIsIt();
if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
return;
adventureInt->tileLClicked(mp);
}
void CTerrainRect::clickRight(tribool down, bool previousState)
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(adventureInt->swipeEnabled && isSwiping)
return;
#endif
if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
int3 mp = whichTileIsIt();
if(CGI->mh->map->isInTheMap(mp) && down)
adventureInt->tileRClicked(mp);
}
void CTerrainRect::clickMiddle(tribool down, bool previousState)
{
handleSwipeStateChange((bool)down == true);
}
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
{
handleHover(sEvent);
if(!adventureInt->swipeEnabled)
return;
handleSwipeMove(sEvent);
}
void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
if(sEvent.state == 0 || GH.multifinger) // any "button" is enough on mobile
#else
if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
#endif
{
return;
}
if(!isSwiping)
{
// try to distinguish if this touch was meant to be a swipe or just fat-fingering press
if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop ||
abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop)
{
isSwiping = true;
}
}
if(isSwiping)
{
adventureInt->swipeTargetPosition.x =
swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32;
adventureInt->swipeTargetPosition.y =
swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32;
adventureInt->swipeMovementRequested = true;
}
}
bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
{
if(btnPressed)
{
swipeInitialRealPos = int3(GH.getCursorPosition().x, GH.getCursorPosition().y, 0);
swipeInitialMapPos = int3(adventureInt->position);
return true;
}
else if(isSwiping) // only accept this touch if it wasn't a swipe
{
isSwiping = false;
return true;
}
return false;
}
void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
{
int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y);
int3 pom = adventureInt->verifyPos(tHovered);
if(tHovered != pom) //tile outside the map
{
CCS->curh->set(Cursor::Map::POINTER);
return;
}
if (pom != curHoveredTile)
{
curHoveredTile = pom;
adventureInt->tileHovered(pom);
}
}
void CTerrainRect::hover(bool on)
{
if (!on)
{
adventureInt->statusbar->clear();
CCS->curh->set(Cursor::Map::POINTER);
}
//Hoverable::hover(on);
}
void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
{
const static int pns[9][9] = {
{16, 17, 18, 7, -1, 19, 6, 5, -1},
{ 8, 9, 18, 7, -1, 19, 6, -1, 20},
{ 8, 1, 10, 7, -1, 19, -1, 21, 20},
{24, 17, 18, 15, -1, -1, 6, 5, 4},
{-1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 8, 1, 2, -1, -1, 11, 22, 21, 20},
{24, 17, -1, 23, -1, 3, 14, 5, 4},
{24, -1, 2, 23, -1, 3, 22, 13, 4},
{-1, 1, 2, 23, -1, 3, 22, 21, 12}
}; //table of magic values TODO meaning, change variable name
for (int i = 0; i < -1 + (int)currentPath->nodes.size(); ++i)
{
const int3 &curPos = currentPath->nodes[i].coord, &nextPos = currentPath->nodes[i+1].coord;
if(curPos.z != adventureInt->position.z)
continue;
int pn=-1;//number of picture
if (i==0) //last tile
{
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
if (x<0 || y<0 || x>pos.w || y>pos.h)
continue;
pn=0;
}
else
{
const int3 &prevPos = currentPath->nodes[i-1].coord;
std::vector<CGPathNode> & cv = currentPath->nodes;
/* Vector directions
* 0 1 2
* \ | /
* 3 - 4 - 5
* / | \
* 6 7 8
*For example:
* |
* |__\
* /
* is id1=7, id2=5 (pns[7][5])
*/
bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
{
int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1); //Direction of entering vector
int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector
pn=pns[id1][id2];
}
else //path discontinuity or sea/land transition (eg. when moving through Subterranean Gate or Boat)
{
pn = 0;
}
}
if (currentPath->nodes[i].turns)
pn+=25;
if (pn>=0)
{
const auto arrow = graphics->heroMoveArrows->getImage(pn);
int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
if (x< -32 || y< -32 || x>pos.w || y>pos.h)
continue;
int hvx = (x + arrow->width()) - (pos.x + pos.w),
hvy = (y + arrow->height()) - (pos.y + pos.h);
Rect prevClip;
CSDL_Ext::getClipRect(to, prevClip);
CSDL_Ext::setClipRect(to, extRect); //preventing blitting outside of that rect
if(ADVOPT.smoothMove) //version for smooth hero move, with pos shifts
{
if (hvx<0 && hvy<0)
{
arrow->draw(to, x + moveX, y + moveY);
}
else if(hvx<0)
{
Rect srcRect = CSDL_Ext::genRect(arrow->height() - hvy, arrow->width(), 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
else if (hvy<0)
{
Rect srcRect = CSDL_Ext::genRect(arrow->height(), arrow->width() - hvx, 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
else
{
Rect srcRect = CSDL_Ext::genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
arrow->draw(to, x + moveX, y + moveY, &srcRect);
}
}
else //standard version
{
if (hvx<0 && hvy<0)
{
arrow->draw(to, x, y);
}
else if(hvx<0)
{
Rect srcRect = CSDL_Ext::genRect(arrow->height() - hvy, arrow->width(), 0, 0);
arrow->draw(to, x, y, &srcRect);
}
else if (hvy<0)
{
Rect srcRect = CSDL_Ext::genRect(arrow->height(), arrow->width() - hvx, 0, 0);
arrow->draw(to, x, y, &srcRect);
}
else
{
Rect srcRect = CSDL_Ext::genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
arrow->draw(to, x, y, &srcRect);
}
}
CSDL_Ext::setClipRect(to, prevClip);
}
} //for (int i=0;i<currentPath->nodes.size()-1;i++)
}
void CTerrainRect::show(SDL_Surface * to)
{
if (adventureInt->mode == EAdvMapMode::NORMAL)
{
MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos);
info.otherheroAnim = true;
info.anim = adventureInt->anim;
info.heroAnim = adventureInt->heroAnim;
if (ADVOPT.smoothMove)
info.movement = int3(moveX, moveY, 0);
lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
if (fadeAnim->isFading())
{
Rect r(pos);
fadeAnim->update();
fadeAnim->draw(to, r.topLeft());
}
if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
{
showPath(pos, to);
}
}
}
void CTerrainRect::showAll(SDL_Surface * to)
{
// world view map is static and doesn't need redraw every frame
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
{
MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos, adventureInt->worldViewIcons);
info.scaled = true;
info.scale = adventureInt->worldViewScale;
adventureInt->worldViewOptions.adjustDrawingInfo(info);
CGI->mh->drawTerrainRectNew(to, &info);
}
}
void CTerrainRect::showAnim(SDL_Surface * to)
{
if (fadeAnim->isFading())
show(to);
else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
}
int3 CTerrainRect::whichTileIsIt(const int x, const int y)
{
int3 ret;
ret.x = adventureInt->position.x + ((x-CGI->mh->offsetX-pos.x)/32);
ret.y = adventureInt->position.y + ((y-CGI->mh->offsetY-pos.y)/32);
ret.z = adventureInt->position.z;
return ret;
}
int3 CTerrainRect::whichTileIsIt()
{
return whichTileIsIt(GH.getCursorPosition().x, GH.getCursorPosition().y);
}
int3 CTerrainRect::tileCountOnScreen()
{
switch (adventureInt->mode)
{
default:
logGlobal->error("Unknown map mode %d", (int)adventureInt->mode);
return int3();
case EAdvMapMode::NORMAL:
return int3(tilesw, tilesh, 1);
case EAdvMapMode::WORLD_VIEW:
return int3((si32)(tilesw / adventureInt->worldViewScale), (si32)(tilesh / adventureInt->worldViewScale), 1);
}
}
void CTerrainRect::fadeFromCurrentView()
{
if (!ADVOPT.screenFading)
return;
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if (!fadeSurface)
fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
CSDL_Ext::blitSurface(screen, fadeSurface, Point(0,0));
fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
}
bool CTerrainRect::needsAnimUpdate()
{
return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
}

View File

@ -0,0 +1,64 @@
/*
* CTerrainRect.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../gui/CIntObject.h"
#include "../../lib/int3.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CGPath;
VCMI_LIB_NAMESPACE_END
enum class EMapAnimRedrawStatus;
class CFadeAnimation;
/// Holds information about which tiles of the terrain are shown/not shown at the screen
class CTerrainRect : public CIntObject
{
SDL_Surface * fadeSurface;
EMapAnimRedrawStatus lastRedrawStatus;
std::shared_ptr<CFadeAnimation> fadeAnim;
int3 swipeInitialMapPos;
int3 swipeInitialRealPos;
bool isSwiping;
static constexpr float SwipeTouchSlop = 16.0f;
void handleHover(const SDL_MouseMotionEvent & sEvent);
void handleSwipeMove(const SDL_MouseMotionEvent & sEvent);
/// handles start/finish of swipe (press/release of corresponding button); returns true if state change was handled
bool handleSwipeStateChange(bool btnPressed);
public:
int tilesw, tilesh; //width and height of terrain to blit in tiles
int3 curHoveredTile;
int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
CGPath * currentPath;
CTerrainRect();
virtual ~CTerrainRect();
void deactivate() override;
void clickLeft(tribool down, bool previousState) override;
void clickRight(tribool down, bool previousState) override;
void clickMiddle(tribool down, bool previousState) override;
void hover(bool on) override;
void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
void showAnim(SDL_Surface * to);
void showPath(const Rect &extRect, SDL_Surface * to);
int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
int3 whichTileIsIt(); //uses current cursor pos
/// @returns number of visible tiles on screen respecting current map scaling
int3 tileCountOnScreen();
/// animates view by caching current surface and crossfading it with normal screen
void fadeFromCurrentView();
bool needsAnimUpdate();
};

View File

@ -11,30 +11,24 @@
#include "StdInc.h"
#include "mapHandler.h"
#include "CBitmapHandler.h"
#include "gui/CAnimation.h"
#include "gui/SDL_Extensions.h"
#include "CGameInfo.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/mapObjects/CObjectClassesHandler.h"
#include "../lib/CGameState.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CTownHandler.h"
#include "../lib/CModHandler.h"
#include "Graphics.h"
#include "../lib/mapping/CMap.h"
#include "../lib/CConfigHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/GameConstants.h"
#include "../lib/CStopWatch.h"
#include "CMT.h"
#include "CMusicHandler.h"
#include "../lib/CRandomGenerator.h"
#include "../lib/RoadHandler.h"
#include "../lib/RiverHandler.h"
#include "../lib/TerrainHandler.h"
#include "../lib/filesystem/ResourceID.h"
#include "../lib/JsonDetail.h"
#include "../render/CAnimation.h"
#include "../render/CFadeAnimation.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../CGameInfo.h"
#include "../render/Graphics.h"
#include "../render/IImage.h"
#include "../CMusicHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CObjectClassesHandler.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/CStopWatch.h"
#include "../../lib/CRandomGenerator.h"
#include "../../lib/RoadHandler.h"
#include "../../lib/RiverHandler.h"
#include "../../lib/TerrainHandler.h"
#define ADVOPT (conf.go()->ac)
@ -899,22 +893,22 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
{
if(parent->map->getTile(int3(pos.x, pos.y, pos.z)).blocked) //temporary hiding blocked positions
{
static SDL_Surface * block = nullptr;
static std::shared_ptr<IImage> block;
if (!block)
block = BitmapHandler::loadBitmap("blocked");
block = IImage::createFromFile("blocked");
CSDL_Ext::blitSurface(block, targetSurf, realTileRect.topLeft());
block->draw(targetSurf, realTileRect.x, realTileRect.y);
}
}
if (settings["session"]["showVisit"].Bool())
{
if(parent->map->getTile(int3(pos.x, pos.y, pos.z)).visitable) //temporary hiding visitable positions
{
static SDL_Surface * visit = nullptr;
static std::shared_ptr<IImage> visit;
if (!visit)
visit = BitmapHandler::loadBitmap("visitable");
visit = IImage::createFromFile("visitable");
CSDL_Ext::blitSurface(visit, targetSurf, realTileRect.topLeft());
visit->draw(targetSurf, realTileRect.x, realTileRect.y);
}
}
}
@ -1371,7 +1365,7 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
if(t.hasFavorableWinds())
{
out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS, 0);
return;
}
const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y];
@ -1434,7 +1428,7 @@ std::shared_ptr<IImage> CMapHandler::CMapCache::requestWorldViewCacheOrCreate(CM
auto iter = cache.find(key);
if(iter == cache.end())
{
auto scaled = fullSurface->scaleFast(worldViewCachedScale);
auto scaled = fullSurface->scaleFast(fullSurface->dimensions() * worldViewCachedScale);
cache[key] = scaled;
return scaled;
}

View File

@ -10,9 +10,9 @@
#pragma once
#include "../lib/int3.h"
#include "../lib/spells/ViewSpellInt.h"
#include "../lib/Rect.h"
#include "../../lib/int3.h"
#include "../../lib/spells/ViewSpellInt.h"
#include "../../lib/Rect.h"
#ifdef IN
#undef IN

View File

@ -364,7 +364,7 @@ bool MovementAnimation::init()
Point begPosition = owner.stacksController->getStackPositionAtHex(prevHex, stack);
Point endPosition = owner.stacksController->getStackPositionAtHex(nextHex, stack);
timeToMove = AnimationControls::getMovementDuration(stack->getCreature());
progressPerSecond = AnimationControls::getMovementDistance(stack->getCreature());
begX = begPosition.x;
begY = begPosition.y;
@ -375,8 +375,7 @@ bool MovementAnimation::init()
if (stack->hasBonus(Selector::type()(Bonus::FLYING)))
{
float distance = static_cast<float>(sqrt(distanceX * distanceX + distanceY * distanceY));
timeToMove *= AnimationControls::getFlightDistance(stack->getCreature()) / distance;
progressPerSecond = AnimationControls::getFlightDistance(stack->getCreature()) / distance;
}
return true;
@ -384,7 +383,7 @@ bool MovementAnimation::init()
void MovementAnimation::nextFrame()
{
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * timeToMove;
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * progressPerSecond;
//moving instructions
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
@ -432,7 +431,7 @@ MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stac
curentMoveIndex(0),
begX(0), begY(0),
distanceX(0), distanceY(0),
timeToMove(0.0),
progressPerSecond(0.0),
progress(0.0)
{
logAnim->debug("Created MovementAnimation for %s", stack->getName());

View File

@ -147,8 +147,11 @@ private:
double begX, begY; // starting position
double distanceX, distanceY; // full movement distance, may be negative if creture moves topleft
double timeToMove; // full length of movement animation
double progress; // range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
/// progress gain per second
double progressPerSecond;
/// range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
double progress;
public:
bool init() override;

View File

@ -79,7 +79,7 @@ enum class ECreatureAnimType
DEAD = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
DEAD_RANGED = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copiend here
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copied here
FROZEN = 25, // new group, used when stack animation is paused (e.g. petrified). If empty - consist of first frame from HOLDING animation
CAST_UP = 30,

View File

@ -21,8 +21,8 @@
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../render/Canvas.h"
#include "../render/CAnimation.h"
#include "../../CCallback.h"
#include "../../lib/battle/BattleAction.h"

View File

@ -22,11 +22,11 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../widgets/AdventureMapClasses.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CursorHandler.h"
#include "../adventureMap/CInGameConsole.h"
#include "../../CCallback.h"
#include "../../lib/BattleFieldHandler.h"

View File

@ -24,13 +24,12 @@
#include "BattleRenderer.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/Canvas.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../windows/CAdvmapInterface.h"
#include "../render/Canvas.h"
#include "../adventureMap/CAdvMapInt.h"
#include "../../CCallback.h"
#include "../../lib/CStack.h"
@ -207,11 +206,8 @@ void BattleInterface::stacksAreAttacked(std::vector<StackAttackedInfo> attackedI
std::array<int, 2> killedBySide = {0, 0};
int targets = 0;
for(const StackAttackedInfo & attackedInfo : attackedInfos)
{
++targets;
ui8 side = attackedInfo.defender->side;
killedBySide.at(side) += attackedInfo.amountKilled;
}
@ -523,16 +519,16 @@ void BattleInterface::displaySpellHit(const CSpell * spell, BattleHex destinatio
void BattleInterface::setAnimSpeed(int set)
{
Settings speed = settings.write["battle"]["animationSpeed"];
speed->Float() = float(set) / 100;
Settings speed = settings.write["battle"]["speedFactor"];
speed->Float() = float(set);
}
int BattleInterface::getAnimSpeed() const
{
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
return static_cast<int>(vstd::round(settings["session"]["spectate-battle-speed"].Float() *100));
return static_cast<int>(vstd::round(settings["session"]["spectate-battle-speed"].Float()));
return static_cast<int>(vstd::round(settings["battle"]["animationSpeed"].Float() *100));
return static_cast<int>(vstd::round(settings["battle"]["speedFactor"].Float()));
}
CPlayerInterface *BattleInterface::getCurrentPlayerInterface() const

View File

@ -19,21 +19,21 @@
#include "BattleWindow.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../CVideoHandler.h"
#include "../Graphics.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/AdventureMapClasses.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
#include "../windows/CMessage.h"
#include "../windows/CCreatureWindow.h"
#include "../windows/CSpellWindow.h"
#include "../render/CAnimation.h"
#include "../adventureMap/CInGameConsole.h"
#include "../../CCallback.h"
#include "../../lib/CStack.h"
@ -48,6 +48,9 @@
#include "../../lib/CondSh.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include <SDL_surface.h>
#include <SDL_events.h>
void BattleConsole::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
@ -218,8 +221,10 @@ void BattleHero::render(Canvas & canvas)
canvas.draw(flagFrame, flagPosition);
canvas.draw(heroFrame, heroPosition);
flagCurrentFrame += currentSpeed;
currentFrame += currentSpeed;
float timePassed = float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
@ -238,8 +243,8 @@ void BattleHero::pause()
void BattleHero::play()
{
//FIXME: un-hardcode speed
currentSpeed = 0.25f;
//H3 speed: 10 fps ( 100 ms per frame)
currentSpeed = 10.f;
}
float BattleHero::getFrame() const
@ -438,13 +443,13 @@ BattleOptionsWindow::BattleOptionsWindow(BattleInterface & owner):
std::shared_ptr<CToggleButton> toggle;
toggle = std::make_shared<CToggleButton>(Point( 28, 225), "sysopb9.def", CGI->generaltexth->zelp[422]);
animSpeeds->addToggle(40, toggle);
animSpeeds->addToggle(1, toggle);
toggle = std::make_shared<CToggleButton>(Point( 92, 225), "sysob10.def", CGI->generaltexth->zelp[423]);
animSpeeds->addToggle(63, toggle);
animSpeeds->addToggle(2, toggle);
toggle = std::make_shared<CToggleButton>(Point(156, 225), "sysob11.def", CGI->generaltexth->zelp[424]);
animSpeeds->addToggle(100, toggle);
animSpeeds->addToggle(3, toggle);
animSpeeds->setSelected(owner.getAnimSpeed());
@ -872,7 +877,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn)
if (unit->unitType()->idNumber == CreatureID::ARROW_TOWERS)
icon->setFrame(owner->getSiegeShooterIconID(), 1);
amount->setText(CSDL_Ext::makeNumberShort(unit->getCount()));
amount->setText(CSDL_Ext::makeNumberShort(unit->getCount(), 4));
if(stateIcon)
{

View File

@ -20,9 +20,8 @@
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../gui/CGuiHandler.h"
#include "../render/Canvas.h"
#include "../../CCallback.h"
#include "../../lib/battle/CObstacleInstance.h"

View File

@ -15,8 +15,7 @@
#include "BattleStacksController.h"
#include "CreatureAnimation.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../render/Canvas.h"
#include "../gui/CGuiHandler.h"
#include "../CGameInfo.h"
@ -77,7 +76,11 @@ void ProjectileAnimatedMissile::show(Canvas & canvas)
void ProjectileCatapult::show(Canvas & canvas)
{
auto image = animation->getImage(frameNum, 0, true);
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0);
auto image = animation->getImage(frameIndex, 0, true);
if(image)
{
@ -86,8 +89,6 @@ void ProjectileCatapult::show(Canvas & canvas)
Point pos(posX, posY);
canvas.draw(image, pos);
frameNum = (frameNum + 1) % animation->size(0);
}
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
@ -116,10 +117,7 @@ void ProjectileRay::show(Canvas & canvas)
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
SDL_Color beginColor{ ray.r1, ray.g1, ray.b1, ray.a1};
SDL_Color endColor { ray.r2, ray.g2, ray.b2, ray.a2};
canvas.drawLine(Point(x1, y1 + i), Point(x2, y2+i), beginColor, endColor);
canvas.drawLine(Point(x1, y1 + i), Point(x2, y2+i), ray.start, ray.end);
}
}
else // draw in vertical axis
@ -133,10 +131,8 @@ void ProjectileRay::show(Canvas & canvas)
for (size_t i = 0; i < rayConfig.size(); ++i)
{
auto ray = rayConfig[i];
SDL_Color beginColor{ ray.r1, ray.g1, ray.b1, ray.a1};
SDL_Color endColor { ray.r2, ray.g2, ray.b2, ray.a2};
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), beginColor, endColor);
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
}
}
@ -294,13 +290,13 @@ void BattleProjectileController::createCatapultProjectile(const CStack * shooter
auto catapultProjectile = new ProjectileCatapult();
catapultProjectile->animation = getProjectileImage(shooter);
catapultProjectile->frameNum = 0;
catapultProjectile->progress = 0;
catapultProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getCatapultSpeed());
catapultProjectile->from = from;
catapultProjectile->dest = dest;
catapultProjectile->shooterID = shooter->ID;
catapultProjectile->playing = false;
catapultProjectile->frameProgress = 0.f;
projectiles.push_back(std::shared_ptr<ProjectileBase>(catapultProjectile));
}
@ -321,6 +317,7 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
projectile.reset(rayProjectile);
rayProjectile->rayConfig = shooterInfo.animation.projectileRay;
rayProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getRayProjectileSpeed());
}
else if (stackUsesMissileProjectile(shooter))
{
@ -328,11 +325,12 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
projectile.reset(missileProjectile);
missileProjectile->animation = getProjectileImage(shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->reverse = !owner.stacksController->facingRight(shooter);
missileProjectile->frameNum = computeProjectileFrameID(from, dest, shooter);
missileProjectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
}
projectile->speed = computeProjectileFlightTime(from, dest, AnimationControls::getProjectileSpeed());
projectile->from = from;
projectile->dest = dest;
projectile->shooterID = shooter->ID;

View File

@ -61,7 +61,7 @@ struct ProjectileCatapult : ProjectileBase
void show(Canvas & canvas) override;
std::shared_ptr<CAnimation> animation;
int frameNum; // frame to display from projectile animation
float frameProgress;
};
/// Projectile for mages/evil eye - render ray expanding from origin position to destination

View File

@ -20,8 +20,8 @@
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../../CCallback.h"
#include "../../lib/NetPacks.h"

View File

@ -25,13 +25,12 @@
#include "../CPlayerInterface.h"
#include "../CMusicHandler.h"
#include "../CGameInfo.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../gui/Canvas.h"
#include "../gui/SDL_Extensions.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../render/Canvas.h"
#include "../../CCallback.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/battle/BattleHex.h"
#include "../../lib/CGameState.h"
#include "../../lib/CStack.h"
@ -89,10 +88,10 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f );
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f );
amountNormal->adjustPalette(shifterNormal);
amountPositive->adjustPalette(shifterPositive);
amountNegative->adjustPalette(shifterNegative);
amountEffNeutral->adjustPalette(shifterNeutral);
amountNormal->adjustPalette(shifterNormal, 0);
amountPositive->adjustPalette(shifterPositive, 0);
amountNegative->adjustPalette(shifterNegative, 0);
amountEffNeutral->adjustPalette(shifterNeutral, 0);
//Restore border color {255, 231, 132, 255} to its original state
amountNormal->resetPalette(26);
@ -318,7 +317,7 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack *
//blitting amount
Point textPos = stackAnimation[stack->ID]->pos.topLeft() + amountBG->dimensions()/2 + Point(xAdd, yAdd);
canvas.drawText(textPos, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, CSDL_Ext::makeNumberShort(stack->getCount()));
canvas.drawText(textPos, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, CSDL_Ext::makeNumberShort(stack->getCount(), 4));
}
void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "../gui/ColorFilter.h"
#include "../render/ColorFilter.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -17,17 +17,17 @@
#include "BattleActionsController.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CPlayerInterface.h"
#include "../CMusicHandler.h"
#include "../gui/Canvas.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CAnimation.h"
#include "../windows/CSpellWindow.h"
#include "../widgets/AdventureMapClasses.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../windows/CMessage.h"
#include "../render/CAnimation.h"
#include "../render/Canvas.h"
#include "../adventureMap/CInGameConsole.h"
#include "../../CCallback.h"
#include "../../lib/CGeneralTextHandler.h"
@ -36,6 +36,9 @@
#include "../../lib/CConfigHandler.h"
#include "../../lib/filesystem/ResourceID.h"
#include <SDL_surface.h>
#include <SDL_events.h>
BattleWindow::BattleWindow(BattleInterface & owner):
owner(owner)
{

View File

@ -13,9 +13,9 @@
#include "../../lib/CConfigHandler.h"
#include "../../lib/CCreatureHandler.h"
#include "../gui/Canvas.h"
#include "../gui/ColorFilter.h"
#include "../gui/SDL_Extensions.h"
#include "../render/Canvas.h"
#include "../render/ColorFilter.h"
#include "../renderSDL/SDL_Extensions.h"
static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };
@ -47,6 +47,15 @@ std::shared_ptr<CreatureAnimation> AnimationControls::getAnimation(const CCreatu
return std::make_shared<CreatureAnimation>(creature->animDefName, func);
}
float AnimationControls::getAnimationSpeedFactor()
{
// according to testing, H3 ratios between slow/medium/fast might actually be 36/60/100 (x1.666)
// exact value is hard to tell due to large rounding errors
// however we will assume them to be 33/66/100 since these values are better for standard 60 fps displays:
// with these numbers, base frame display duration will be 100/66/33 ms - exactly 6/4/2 frames
return settings["battle"]["speedFactor"].Float();
}
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType type)
{
assert(creature->animation.walkAnimationTime != 0);
@ -56,20 +65,23 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
// possible new fields for creature format:
//split "Attack time" into "Shoot Time" and "Cast Time"
// a lot of arbitrary multipliers, mostly to make animation speed closer to H3
const float baseSpeed = 0.1f;
const float speedMult = static_cast<float>(settings["battle"]["animationSpeed"].Float());
const float speed = baseSpeed / speedMult;
// base speed for all H3 animations on slow speed is 10 frames per second (or 100ms per frame)
const float baseSpeed = 10.f;
const float speed = baseSpeed * getAnimationSpeedFactor();
switch (type)
{
case ECreatureAnimType::MOVING:
return static_cast<float>(speed * 2 * creature->animation.walkAnimationTime / anim->framesInGroup(type));
return speed / creature->animation.walkAnimationTime;
case ECreatureAnimType::MOUSEON:
return baseSpeed;
case ECreatureAnimType::HOLDING:
return static_cast<float>(baseSpeed * creature->animation.idleAnimationTime / anim->framesInGroup(type));
if ( creature->animation.idleAnimationTime > 0.01)
return speed / creature->animation.idleAnimationTime;
else
return 0.f; // this animation is disabled for current creature
case ECreatureAnimType::SHOOT_UP:
case ECreatureAnimType::SHOOT_FRONT:
@ -80,7 +92,7 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::CAST_DOWN:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::CAST_UP:
return static_cast<float>(speed * 4 * creature->animation.attackAnimationTime / anim->framesInGroup(type));
return speed / creature->animation.attackAnimationTime;
// as strange as it looks like "attackAnimationTime" does not affects melee attacks
// necessary because length of these animations must be same for all creatures for synchronization
@ -95,15 +107,15 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::GROUP_ATTACK_DOWN:
case ECreatureAnimType::GROUP_ATTACK_FRONT:
case ECreatureAnimType::GROUP_ATTACK_UP:
return speed * 3 / anim->framesInGroup(type);
return speed;
case ECreatureAnimType::TURN_L:
case ECreatureAnimType::TURN_R:
return speed / 3;
return speed;
case ECreatureAnimType::MOVE_START:
case ECreatureAnimType::MOVE_END:
return speed / 3;
return speed;
case ECreatureAnimType::DEAD:
case ECreatureAnimType::DEAD_RANGED:
@ -116,37 +128,51 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
float AnimationControls::getProjectileSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 4000);
// H3 speed: 1250/2500/3750 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 1250);
}
float AnimationControls::getRayProjectileSpeed()
{
// H3 speed: 4000/8000/12000 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 4000);
}
float AnimationControls::getCatapultSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 1000);
// H3 speed: 200/400/600 pixels per second
return static_cast<float>(getAnimationSpeedFactor() * 200);
}
float AnimationControls::getSpellEffectSpeed()
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 30);
// H3 speed: 10/20/30 frames per second
return static_cast<float>(getAnimationSpeedFactor() * 10);
}
float AnimationControls::getMovementDuration(const CCreature * creature)
float AnimationControls::getMovementDistance(const CCreature * creature)
{
return static_cast<float>(settings["battle"]["animationSpeed"].Float() * 4 / creature->animation.walkAnimationTime);
// H3 speed: 2/4/6 tiles per second
return static_cast<float>( 2.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFlightDistance(const CCreature * creature)
{
return static_cast<float>(creature->animation.flightAnimationDistance * 200);
// Note: for whatever reason, H3 uses "Walk Animation Time" here, even though "Flight Animation Distance" also exists
// H3 speed: 250/500/750 pixels per second
return static_cast<float>( 250.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);
}
float AnimationControls::getFadeInDuration()
{
return 1.0f / settings["battle"]["animationSpeed"].Float();
// H3 speed: 500/250/166 ms
return 0.5f / getAnimationSpeedFactor();
}
float AnimationControls::getObstaclesSpeed()
{
return 10.0;// does not seems to be affected by animaiton speed settings
// H3 speed: 20 frames per second, irregardless of speed setting.
return 20.f;
}
ECreatureAnimType CreatureAnimation::getType() const
@ -345,7 +371,7 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
genSpecialPalette(SpecialPalette);
image->setSpecialPallete(SpecialPalette);
image->adjustPalette(shifter);
image->adjustPalette(shifter, 8);
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
@ -407,7 +433,5 @@ void CreatureAnimation::pause()
void CreatureAnimation::play()
{
//logAnim->trace("Play %s group %d at %d:%d", name, static_cast<int>(getType()), pos.x, pos.y);
speed = 0;
if(speedController(this, type) != 0)
speed = 1 / speedController(this, type);
speed = speedController(this, type);
}

View File

@ -11,7 +11,10 @@
#include "../../lib/FunctionList.h"
#include "../widgets/Images.h"
#include "../gui/CAnimation.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include <SDL_pixels.h>
class CIntObject;
class CreatureAnimation;
@ -25,25 +28,32 @@ namespace AnimationControls
SDL_Color getGoldBorder();
SDL_Color getNoBorder();
/// returns animation speed factor according to game settings,
/// slow speed is considered to be "base speed" and will return 1.0
float getAnimationSpeedFactor();
/// creates animation object with preset speed control
std::shared_ptr<CreatureAnimation> getAnimation(const CCreature * creature);
/// returns animation speed of specific group, taking in mind game setting (in frames per second)
float getCreatureAnimationSpeed(const CCreature * creature, const CreatureAnimation * anim, ECreatureAnimType groupID);
/// returns how far projectile should move per second
/// returns how far projectile should move per second, in pixels per second
float getProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second (horizontal axis only)
/// returns how far projectile should move per second, in pixels per second
float getRayProjectileSpeed();
/// returns speed of catapult projectile, in pixels per second, on a straight line, without parabola correction
float getCatapultSpeed();
/// returns speed of any spell effects, including any special effects like morale (in frames per second)
float getSpellEffectSpeed();
/// returns duration of full movement animation, in seconds. Needed to move animation on screen
float getMovementDuration(const CCreature * creature);
/// returns speed of movement animation across the screen, in tiles per second
float getMovementDistance(const CCreature * creature);
/// Returns distance on which flying creatures should during one animation loop
/// returns speed of movement animation across the screen, in pixels per seconds
float getFlightDistance(const CCreature * creature);
/// Returns total time for full fade-in effect on newly summoned creatures, in seconds

File diff suppressed because it is too large Load Diff

View File

@ -11,19 +11,22 @@
#include "CGuiHandler.h"
#include "../lib/CondSh.h"
#include <SDL_timer.h>
#include "CIntObject.h"
#include "CursorHandler.h"
#include "SDL_Extensions.h"
#include "../CGameInfo.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/CConfigHandler.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../CMT.h"
#include "../CPlayerInterface.h"
#include "../battle/BattleInterface.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h>
#include <SDL_timer.h>
#include <SDL_events.h>
extern std::queue<SDL_Event> SDLEventsQueue;
extern boost::mutex eventsM;
@ -79,6 +82,13 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
processList(CIntObject::TEXTINPUT,activityFlag,&textInterested,cb);
}
void CGuiHandler::init()
{
mainFPSmng->init();
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
}
void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
{
processLists(activityFlag,[&](std::list<CIntObject*> * lst){
@ -192,27 +202,99 @@ void CGuiHandler::handleEvents()
while(!SDLEventsQueue.empty())
{
continueEventHandling = true;
SDL_Event ev = SDLEventsQueue.front();
current = &ev;
SDL_Event currentEvent = SDLEventsQueue.front();
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
SDLEventsQueue.pop();
// In a sequence of mouse motion events, skip all but the last one.
// This prevents freezes when every motion event takes longer to handle than interval at which
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
// so that the events would start piling up faster than they can be processed.
if ((ev.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
if ((currentEvent.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
continue;
handleCurrentEvent();
handleCurrentEvent(currentEvent);
}
}
void CGuiHandler::handleCurrentEvent()
void CGuiHandler::convertTouchToMouse(SDL_Event * current)
{
if(current->type == SDL_KEYDOWN || current->type == SDL_KEYUP)
int rLogicalWidth, rLogicalHeight;
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
int adjustedMouseY = (int)(current->tfinger.y * rLogicalHeight);
int adjustedMouseX = (int)(current->tfinger.x * rLogicalWidth);
current->button.x = adjustedMouseX;
current->motion.x = adjustedMouseX;
current->button.y = adjustedMouseY;
current->motion.y = adjustedMouseY;
}
void CGuiHandler::fakeMoveCursor(float dx, float dy)
{
int x, y, w, h;
SDL_Event event;
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
sme.state = SDL_GetMouseState(&x, &y);
SDL_GetWindowSize(mainWindow, &w, &h);
sme.x = CCS->curh->position().x + (int)(GH.pointerSpeedMultiplier * w * dx);
sme.y = CCS->curh->position().y + (int)(GH.pointerSpeedMultiplier * h * dy);
vstd::abetween(sme.x, 0, w);
vstd::abetween(sme.y, 0, h);
event.motion = sme;
SDL_PushEvent(&event);
}
void CGuiHandler::fakeMouseMove()
{
fakeMoveCursor(0, 0);
}
void CGuiHandler::fakeMouseButtonEventRelativeMode(bool down, bool right)
{
SDL_Event event;
SDL_MouseButtonEvent sme = {SDL_MOUSEBUTTONDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if(!down)
{
SDL_KeyboardEvent key = current->key;
if(current->type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
sme.type = SDL_MOUSEBUTTONUP;
}
sme.button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
sme.x = CCS->curh->position().x;
sme.y = CCS->curh->position().y;
float xScale, yScale;
int w, h, rLogicalWidth, rLogicalHeight;
SDL_GetWindowSize(mainWindow, &w, &h);
SDL_RenderGetLogicalSize(mainRenderer, &rLogicalWidth, &rLogicalHeight);
SDL_RenderGetScale(mainRenderer, &xScale, &yScale);
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
CSDL_Ext::warpMouse(
(int)(sme.x * xScale) + (w - rLogicalWidth * xScale) / 2,
(int)(sme.y * yScale + (h - rLogicalHeight * yScale) / 2));
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
event.button = sme;
SDL_PushEvent(&event);
}
void CGuiHandler::handleCurrentEvent( SDL_Event & current )
{
if(current.type == SDL_KEYDOWN || current.type == SDL_KEYUP)
{
SDL_KeyboardEvent key = current.key;
if(current.type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
{
//TODO: we need some central place for all interface-independent hotkeys
Settings s = settings.write["session"];
@ -275,34 +357,39 @@ void CGuiHandler::handleCurrentEvent()
if(vstd::contains(keyinterested,*i) && (!keysCaptured || (*i)->captureThisEvent(key)))
(**i).keyPressed(key);
}
else if(current->type == SDL_MOUSEMOTION)
else if(current.type == SDL_MOUSEMOTION)
{
handleMouseMotion();
handleMouseMotion(current);
}
else if(current->type == SDL_MOUSEBUTTONDOWN)
else if(current.type == SDL_MOUSEBUTTONDOWN)
{
switch(current->button.button)
switch(current.button.button)
{
case SDL_BUTTON_LEFT:
if(lastClick == current->motion && (SDL_GetTicks() - lastClickTime) < 300)
{
auto doubleClicked = false;
if(lastClick == getCursorPosition() && (SDL_GetTicks() - lastClickTime) < 300)
{
std::list<CIntObject*> hlp = doubleClickInterested;
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
{
if(!vstd::contains(doubleClickInterested, *i)) continue;
if((*i)->pos.isInside(current->motion.x, current->motion.y))
if((*i)->pos.isInside(current.motion.x, current.motion.y))
{
(*i)->onDoubleClick();
doubleClicked = true;
}
}
}
lastClick = current->motion;
lastClick = current.motion;
lastClickTime = SDL_GetTicks();
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, true);
if(!doubleClicked)
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, true);
break;
}
case SDL_BUTTON_RIGHT:
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, true);
break;
@ -313,7 +400,7 @@ void CGuiHandler::handleCurrentEvent()
break;
}
}
else if(current->type == SDL_MOUSEWHEEL)
else if(current.type == SDL_MOUSEWHEEL)
{
std::list<CIntObject*> hlp = wheelInterested;
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
@ -322,40 +409,97 @@ void CGuiHandler::handleCurrentEvent()
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
int x = 0, y = 0;
SDL_GetMouseState(&x, &y);
(*i)->wheelScrolled(current->wheel.y < 0, (*i)->pos.isInside(x, y));
(*i)->wheelScrolled(current.wheel.y < 0, (*i)->pos.isInside(x, y));
}
}
else if(current->type == SDL_TEXTINPUT)
else if(current.type == SDL_TEXTINPUT)
{
for(auto it : textInterested)
{
it->textInputed(current->text);
it->textInputed(current.text);
}
}
else if(current->type == SDL_TEXTEDITING)
else if(current.type == SDL_TEXTEDITING)
{
for(auto it : textInterested)
{
it->textEdited(current->edit);
it->textEdited(current.edit);
}
}
//todo: muiltitouch
else if(current->type == SDL_MOUSEBUTTONUP)
else if(current.type == SDL_MOUSEBUTTONUP)
{
switch(current->button.button)
if(!multifinger)
{
case SDL_BUTTON_LEFT:
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, false);
break;
case SDL_BUTTON_RIGHT:
switch(current.button.button)
{
case SDL_BUTTON_LEFT:
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, false);
break;
case SDL_BUTTON_RIGHT:
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, false);
break;
case SDL_BUTTON_MIDDLE:
handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, false);
break;
}
}
}
else if(current.type == SDL_FINGERMOTION)
{
if(isPointerRelativeMode)
{
fakeMoveCursor(current.tfinger.dx, current.tfinger.dy);
}
}
else if(current.type == SDL_FINGERDOWN)
{
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
multifinger = fingerCount > 1;
if(isPointerRelativeMode)
{
if(current.tfinger.x > 0.5)
{
bool isRightClick = current.tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(true, isRightClick);
}
}
#ifndef VCMI_IOS
else if(fingerCount == 2)
{
convertTouchToMouse(&current);
handleMouseMotion(current);
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, true);
}
#endif //VCMI_IOS
}
else if(current.type == SDL_FINGERUP)
{
#ifndef VCMI_IOS
auto fingerCount = SDL_GetNumTouchFingers(current.tfinger.touchId);
#endif //VCMI_IOS
if(isPointerRelativeMode)
{
if(current.tfinger.x > 0.5)
{
bool isRightClick = current.tfinger.y < 0.5;
fakeMouseButtonEventRelativeMode(false, isRightClick);
}
}
#ifndef VCMI_IOS
else if(multifinger)
{
convertTouchToMouse(&current);
handleMouseMotion(current);
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, false);
break;
case SDL_BUTTON_MIDDLE:
handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, false);
break;
multifinger = fingerCount != 0;
}
#endif //VCMI_IOS
}
current = nullptr;
} //event end
void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed)
@ -368,7 +512,7 @@ void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntOb
auto prev = (*i)->mouseState(btn);
if(!isPressed)
(*i)->updateMouseState(btn, isPressed);
if((*i)->pos.isInside(current->motion.x, current->motion.y))
if((*i)->pos.isInside(getCursorPosition()))
{
if(isPressed)
(*i)->updateMouseState(btn, isPressed);
@ -379,13 +523,13 @@ void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntOb
}
}
void CGuiHandler::handleMouseMotion()
void CGuiHandler::handleMouseMotion(const SDL_Event & current)
{
//sending active, hovered hoverable objects hover() call
std::vector<CIntObject*> hlp;
for(auto & elem : hoverable)
{
if(elem->pos.isInside(current->motion.x, current->motion.y))
if(elem->pos.isInside(getCursorPosition()))
{
if (!(elem)->hovered)
hlp.push_back((elem));
@ -402,7 +546,7 @@ void CGuiHandler::handleMouseMotion()
elem->hovered = true;
}
handleMoveInterested(current->motion);
handleMoveInterested(current.motion);
}
void CGuiHandler::simpleRedraw()
@ -427,20 +571,6 @@ void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
}
}
void CGuiHandler::fakeMouseMove()
{
SDL_Event event;
SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0, 0, 0};
int x, y;
sme.state = SDL_GetMouseState(&x, &y);
sme.x = x;
sme.y = y;
event.motion = sme;
SDL_PushEvent(&event);
}
void CGuiHandler::renderFrame()
{
@ -482,11 +612,11 @@ void CGuiHandler::renderFrame()
CGuiHandler::CGuiHandler()
: lastClick(-500, -500),lastClickTime(0), defActionsDef(0), captureChildren(false)
: lastClick(-500, -500),lastClickTime(0), defActionsDef(0), captureChildren(false),
multifinger(false)
{
continueEventHandling = true;
curInt = nullptr;
current = nullptr;
statusbar = nullptr;
// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
@ -507,14 +637,18 @@ void CGuiHandler::breakEventHandling()
continueEventHandling = false;
}
const Point & CGuiHandler::getCursorPosition() const
{
return cursorPosition;
}
void CGuiHandler::drawFPSCounter()
{
const static SDL_Color yellow = {255, 255, 0, 0};
static SDL_Rect overlay = { 0, 0, 64, 32};
Uint32 black = SDL_MapRGB(screen->format, 10, 10, 10);
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black);
std::string fps = boost::lexical_cast<std::string>(mainFPSmng->fps);
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, yellow, Point(10, 10));
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
}
SDL_Keycode CGuiHandler::arrowToNum(SDL_Keycode key)
@ -619,7 +753,8 @@ void CFramerateManager::framerateDelay()
// FPS is higher than it should be, then wait some time
if(timeElapsed < rateticks)
{
SDL_Delay((Uint32)ceil(this->rateticks) - timeElapsed);
int timeToSleep = (uint32_t)ceil(this->rateticks) - timeElapsed;
boost::this_thread::sleep(boost::posix_time::milliseconds(timeToSleep));
}
currentTicks = SDL_GetTicks();

View File

@ -10,8 +10,7 @@
#pragma once
#include "../../lib/Point.h"
#include <SDL_events.h>
#include "SDL_keycode.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -20,6 +19,7 @@ template <typename T> struct CondSh;
VCMI_LIB_NAMESPACE_END
union SDL_Event;
struct SDL_MouseMotionEvent;
class CFramerateManager;
class IStatusBar;
@ -70,26 +70,35 @@ public:
std::shared_ptr<IStatusBar> statusbar;
private:
Point cursorPosition;
std::vector<std::shared_ptr<IShowActivatable>> disposed;
std::atomic<bool> continueEventHandling;
typedef std::list<CIntObject*> CIntObjectList;
//active GUI elements (listening for events
CIntObjectList lclickable,
rclickable,
mclickable,
hoverable,
keyinterested,
motioninterested,
timeinterested,
wheelInterested,
doubleClickInterested,
textInterested;
CIntObjectList lclickable;
CIntObjectList rclickable;
CIntObjectList mclickable;
CIntObjectList hoverable;
CIntObjectList keyinterested;
CIntObjectList motioninterested;
CIntObjectList timeinterested;
CIntObjectList wheelInterested;
CIntObjectList doubleClickInterested;
CIntObjectList textInterested;
void handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed);
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
void handleCurrentEvent(SDL_Event &current);
void handleMouseMotion(const SDL_Event & current);
void handleMoveInterested( const SDL_MouseMotionEvent & motion );
void convertTouchToMouse(SDL_Event * current);
void fakeMoveCursor(float dx, float dy);
void fakeMouseButtonEventRelativeMode(bool down, bool right);
public:
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
void handleElementDeActivate(CIntObject * elem, ui16 activityFlag);
@ -98,11 +107,15 @@ public:
//objs to blit
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
SDL_Event * current; //current event - can be set to nullptr to stop handling event
const Point & getCursorPosition() const;
IUpdateable *curInt;
Point lastClick;
unsigned lastClickTime;
bool multifinger;
bool isPointerRelativeMode;
float pointerSpeedMultiplier;
ui8 defActionsDef; //default auto actions
bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
@ -111,6 +124,7 @@ public:
CGuiHandler();
~CGuiHandler();
void init();
void renderFrame();
void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering
@ -132,9 +146,6 @@ public:
void updateTime(); //handles timeInterested
void handleEvents(); //takes events from queue and calls interested objects
void handleCurrentEvent();
void handleMouseMotion();
void handleMoveInterested( const SDL_MouseMotionEvent & motion );
void fakeMouseMove();
void breakEventHandling(); //current event won't be propagated anymore
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen

View File

@ -11,10 +11,12 @@
#include "CIntObject.h"
#include "CGuiHandler.h"
#include "SDL_Extensions.h"
#include "../CMessage.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../windows/CMessage.h"
#include <SDL_pixels.h>
#include <SDL_surface.h>
#include <SDL_events.h>
IShowActivatable::IShowActivatable()
{

View File

@ -10,7 +10,7 @@
#pragma once
#include "../../lib/Rect.h"
#include "../Graphics.h"
#include "../render/Graphics.h"
struct SDL_Surface;
class CGuiHandler;

View File

@ -11,11 +11,23 @@
#include "StdInc.h"
#include "CursorHandler.h"
#include "SDL_Extensions.h"
#include "CGuiHandler.h"
#include "CAnimation.h"
#include "../renderSDL/CursorSoftware.h"
#include "../renderSDL/CursorHardware.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../CMT.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h>
#include <SDL_events.h>
#ifdef VCMI_APPLE
#include <dispatch/dispatch.h>
#endif
std::unique_ptr<ICursor> CursorHandler::createCursor()
{
if (settings["video"]["cursor"].String() == "auto")
@ -256,7 +268,7 @@ void CursorHandler::centerCursor()
void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f;
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
@ -304,136 +316,3 @@ void CursorHandler::show()
cursor->setVisible(true);
}
void CursorSoftware::render()
{
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
if (needUpdate)
updateTexture();
Point renderPos = pos - pivot;
SDL_Rect destRect;
destRect.x = renderPos.x;
destRect.y = renderPos.y;
destRect.w = 40;
destRect.h = 40;
SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
}
void CursorSoftware::createTexture(const Point & dimensions)
{
if(cursorTexture)
SDL_DestroyTexture(cursorTexture);
if (cursorSurface)
SDL_FreeSurface(cursorSurface);
cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
}
void CursorSoftware::updateTexture()
{
Point dimensions(-1, -1);
if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
createTexture(cursorImage->dimensions());
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
cursorImage->draw(cursorSurface);
SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
needUpdate = false;
}
void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
{
assert(image != nullptr);
cursorImage = image;
pivot = pivotOffset;
needUpdate = true;
}
void CursorSoftware::setCursorPosition( const Point & newPos )
{
pos = newPos;
}
void CursorSoftware::setVisible(bool on)
{
visible = on;
}
CursorSoftware::CursorSoftware():
cursorTexture(nullptr),
cursorSurface(nullptr),
needUpdate(false),
visible(false),
pivot(0,0)
{
SDL_ShowCursor(SDL_DISABLE);
}
CursorSoftware::~CursorSoftware()
{
if(cursorTexture)
SDL_DestroyTexture(cursorTexture);
if (cursorSurface)
SDL_FreeSurface(cursorSurface);
}
CursorHardware::CursorHardware():
cursor(nullptr)
{
SDL_ShowCursor(SDL_DISABLE);
}
CursorHardware::~CursorHardware()
{
if(cursor)
SDL_FreeCursor(cursor);
}
void CursorHardware::setVisible(bool on)
{
if (on)
SDL_ShowCursor(SDL_ENABLE);
else
SDL_ShowCursor(SDL_DISABLE);
}
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
{
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y);
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
image->draw(cursorSurface);
auto oldCursor = cursor;
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
if (!cursor)
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
SDL_FreeSurface(cursorSurface);
SDL_SetCursor(cursor);
if (oldCursor)
SDL_FreeCursor(oldCursor);
}
void CursorHardware::setCursorPosition( const Point & newPos )
{
//no-op
}
void CursorHardware::render()
{
//no-op
}

View File

@ -9,14 +9,12 @@
*/
#pragma once
class CAnimation;
class IImage;
struct SDL_Surface;
struct SDL_Texture;
struct SDL_Cursor;
#include "../../lib/Point.h"
class ICursor;
class IImage;
class CAnimation;
namespace Cursor
{
enum class Type {
@ -112,57 +110,6 @@ namespace Cursor
};
}
class ICursor
{
public:
virtual ~ICursor() = default;
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
virtual void setCursorPosition( const Point & newPos ) = 0;
virtual void render() = 0;
virtual void setVisible( bool on) = 0;
};
class CursorHardware : public ICursor
{
std::shared_ptr<IImage> cursorImage;
SDL_Cursor * cursor;
public:
CursorHardware();
~CursorHardware();
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void setVisible( bool on) override;
};
class CursorSoftware : public ICursor
{
std::shared_ptr<IImage> cursorImage;
SDL_Texture * cursorTexture;
SDL_Surface * cursorSurface;
Point pos;
Point pivot;
bool needUpdate;
bool visible;
void createTexture(const Point & dimensions);
void updateTexture();
public:
CursorSoftware();
~CursorSoftware();
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void setVisible( bool on) override;
};
/// handles mouse cursor
class CursorHandler final
{

View File

@ -1,395 +0,0 @@
/*
* Fonts.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 "Fonts.h"
#include <SDL_ttf.h>
#include "SDL_Pixels.h"
#include "../../lib/JsonNode.h"
#include "../../lib/vcmi_endian.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/CGeneralTextHandler.h"
size_t IFont::getStringWidth(const std::string & data) const
{
size_t width = 0;
for(size_t i=0; i<data.size(); i += Unicode::getCharacterSize(data[i]))
{
width += getGlyphWidth(data.data() + i);
}
return width;
}
void IFont::renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
renderText(surface, data, color, pos);
}
void IFont::renderTextRight(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
Point size((int)getStringWidth(data), (int)getLineHeight());
renderText(surface, data, color, pos - size);
}
void IFont::renderTextCenter(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
Point size((int)getStringWidth(data), (int)getLineHeight());
renderText(surface, data, color, pos - size / 2);
}
void IFont::renderTextLinesLeft(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
{
Point currPos = pos;
for(const std::string & line : data)
{
renderTextLeft(surface, line, color, currPos);
currPos.y += (int)getLineHeight();
}
}
void IFont::renderTextLinesRight(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
{
Point currPos = pos;
currPos.y -= (int)data.size() * (int)getLineHeight();
for(const std::string & line : data)
{
renderTextRight(surface, line, color, currPos);
currPos.y += (int)getLineHeight();
}
}
void IFont::renderTextLinesCenter(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
{
Point currPos = pos;
currPos.y -= (int)data.size() * (int)getLineHeight() / 2;
for(const std::string & line : data)
{
renderTextCenter(surface, line, color, currPos);
currPos.y += (int)getLineHeight();
}
}
std::array<CBitmapFont::BitmapChar, CBitmapFont::totalChars> CBitmapFont::loadChars() const
{
std::array<BitmapChar, totalChars> ret;
size_t offset = 32;
for (auto & elem : ret)
{
elem.leftOffset = read_le_u32(data.first.get() + offset); offset+=4;
elem.width = read_le_u32(data.first.get() + offset); offset+=4;
elem.rightOffset = read_le_u32(data.first.get() + offset); offset+=4;
}
for (auto & elem : ret)
{
int pixelOffset = read_le_u32(data.first.get() + offset); offset+=4;
elem.pixels = data.first.get() + 4128 + pixelOffset;
assert(pixelOffset + 4128 < data.second);
}
return ret;
}
CBitmapFont::CBitmapFont(const std::string & filename):
data(CResourceHandler::get()->load(ResourceID("data/" + filename, EResType::BMP_FONT))->readAll()),
chars(loadChars()),
height(data.first.get()[5])
{}
size_t CBitmapFont::getLineHeight() const
{
return height;
}
size_t CBitmapFont::getGlyphWidth(const char * data) const
{
std::string localChar = Unicode::fromUnicode(std::string(data, Unicode::getCharacterSize(data[0])));
if (localChar.size() == 1)
{
const BitmapChar & ch = chars[ui8(localChar[0])];
return ch.leftOffset + ch.width + ch.rightOffset;
}
return 0;
}
void CBitmapFont::renderCharacter(SDL_Surface * surface, const BitmapChar & character, const SDL_Color & color, int &posX, int &posY) const
{
Rect clipRect;
CSDL_Ext::getClipRect(surface, clipRect);
posX += character.leftOffset;
CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
// start of line, may differ from 0 due to end of surface or clipped surface
int lineBegin = std::max<int>(0, clipRect.y - posY);
int lineEnd = std::min<int>(height, clipRect.y + clipRect.h - posY - 1);
// start end end of each row, may differ from 0
int rowBegin = std::max<int>(0, clipRect.x - posX);
int rowEnd = std::min<int>(character.width, clipRect.x + clipRect.w - posX - 1);
//for each line in symbol
for(int dy = lineBegin; dy <lineEnd; dy++)
{
Uint8 *dstLine = (Uint8*)surface->pixels;
Uint8 *srcLine = character.pixels;
// shift source\destination pixels to current position
dstLine += (posY+dy) * surface->pitch + posX * bpp;
srcLine += dy * character.width;
//for each column in line
for(int dx = rowBegin; dx < rowEnd; dx++)
{
Uint8* dstPixel = dstLine + dx*bpp;
switch(srcLine[dx])
{
case 1: //black "shadow"
colorPutter(dstPixel, 0, 0, 0);
break;
case 255: //text colour
colorPutter(dstPixel, color.r, color.g, color.b);
break;
default :
break; //transparency
}
}
}
posX += character.width;
posX += character.rightOffset;
}
void CBitmapFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
if (data.empty())
return;
assert(surface);
int posX = pos.x;
int posY = pos.y;
// Should be used to detect incorrect text parsing. Disabled right now due to some old UI code (mostly pregame and battles)
//assert(data[0] != '{');
//assert(data[data.size()-1] != '}');
SDL_LockSurface(surface);
for(size_t i=0; i<data.size(); i += Unicode::getCharacterSize(data[i]))
{
std::string localChar = Unicode::fromUnicode(data.substr(i, Unicode::getCharacterSize(data[i])));
if (localChar.size() == 1)
renderCharacter(surface, chars[ui8(localChar[0])], color, posX, posY);
}
SDL_UnlockSurface(surface);
}
std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config)
{
std::string filename = "Data/" + config["file"].String();
return CResourceHandler::get()->load(ResourceID(filename, EResType::TTF_FONT))->readAll();
}
TTF_Font * CTrueTypeFont::loadFont(const JsonNode &config)
{
int pointSize = static_cast<int>(config["size"].Float());
if(!TTF_WasInit() && TTF_Init()==-1)
throw std::runtime_error(std::string("Failed to initialize true type support: ") + TTF_GetError() + "\n");
return TTF_OpenFontRW(SDL_RWFromConstMem(data.first.get(), (int)data.second), 1, pointSize);
}
int CTrueTypeFont::getFontStyle(const JsonNode &config)
{
const JsonVector & names = config["style"].Vector();
int ret = 0;
for(const JsonNode & node : names)
{
if (node.String() == "bold")
ret |= TTF_STYLE_BOLD;
else if (node.String() == "italic")
ret |= TTF_STYLE_ITALIC;
}
return ret;
}
CTrueTypeFont::CTrueTypeFont(const JsonNode & fontConfig):
data(loadData(fontConfig)),
font(loadFont(fontConfig), TTF_CloseFont),
blended(fontConfig["blend"].Bool())
{
assert(font);
TTF_SetFontStyle(font.get(), getFontStyle(fontConfig));
}
size_t CTrueTypeFont::getLineHeight() const
{
return TTF_FontHeight(font.get());
}
size_t CTrueTypeFont::getGlyphWidth(const char *data) const
{
return getStringWidth(std::string(data, Unicode::getCharacterSize(*data)));
/*
int advance;
TTF_GlyphMetrics(font.get(), *data, nullptr, nullptr, nullptr, nullptr, &advance);
return advance;
*/
}
size_t CTrueTypeFont::getStringWidth(const std::string & data) const
{
int width;
TTF_SizeUTF8(font.get(), data.c_str(), &width, nullptr);
return width;
}
void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
if (color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow
{
SDL_Color black = { 0, 0, 0, SDL_ALPHA_OPAQUE};
renderText(surface, data, black, pos + Point(1,1));
}
if (!data.empty())
{
SDL_Surface * rendered;
if (blended)
rendered = TTF_RenderUTF8_Blended(font.get(), data.c_str(), color);
else
rendered = TTF_RenderUTF8_Solid(font.get(), data.c_str(), color);
assert(rendered);
CSDL_Ext::blitSurface(rendered, surface, pos);
SDL_FreeSurface(rendered);
}
}
size_t CBitmapHanFont::getCharacterDataOffset(size_t index) const
{
size_t rowSize = (size + 7) / 8; // 1 bit per pixel, rounded up
size_t charSize = rowSize * size; // glyph contains "size" rows
return index * charSize;
}
size_t CBitmapHanFont::getCharacterIndex(ui8 first, ui8 second) const
{
if (second > 0x7f )
second--;
return (first - 0x81) * (12*16 - 2) + (second - 0x40);
}
void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const
{
//TODO: somewhat duplicated with CBitmapFont::renderCharacter();
Rect clipRect;
CSDL_Ext::getClipRect(surface, clipRect);
CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
// start of line, may differ from 0 due to end of surface or clipped surface
int lineBegin = std::max<int>(0, clipRect.y - posY);
int lineEnd = std::min((int)size, clipRect.y + clipRect.h - posY);
// start end end of each row, may differ from 0
int rowBegin = std::max<int>(0, clipRect.x - posX);
int rowEnd = std::min<int>((int)size, clipRect.x + clipRect.w - posX);
//for each line in symbol
for(int dy = lineBegin; dy <lineEnd; dy++)
{
Uint8 *dstLine = (Uint8*)surface->pixels;
Uint8 *source = data.first.get() + getCharacterDataOffset(characterIndex);
// shift source\destination pixels to current position
dstLine += (posY+dy) * surface->pitch + posX * bpp;
source += ((size + 7) / 8) * dy;
//for each column in line
for(int dx = rowBegin; dx < rowEnd; dx++)
{
// select current bit in bitmap
int bit = (source[dx / 8] << (dx % 8)) & 0x80;
Uint8* dstPixel = dstLine + dx*bpp;
if (bit != 0)
colorPutter(dstPixel, color.r, color.g, color.b);
}
}
posX += (int)size + 1;
}
void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
int posX = pos.x;
int posY = pos.y;
SDL_LockSurface(surface);
for(size_t i=0; i<data.size(); i += Unicode::getCharacterSize(data[i]))
{
std::string localChar = Unicode::fromUnicode(data.substr(i, Unicode::getCharacterSize(data[i])));
if (localChar.size() == 1)
fallback->renderCharacter(surface, fallback->chars[ui8(localChar[0])], color, posX, posY);
if (localChar.size() == 2)
renderCharacter(surface, (int)getCharacterIndex(localChar[0], localChar[1]), color, posX, posY);
}
SDL_UnlockSurface(surface);
}
CBitmapHanFont::CBitmapHanFont(const JsonNode &config):
fallback(new CBitmapFont(config["fallback"].String())),
data(CResourceHandler::get()->load(ResourceID("data/" + config["name"].String(), EResType::OTHER))->readAll()),
size((size_t)config["size"].Float())
{
// basic tests to make sure that fonts are OK
// 1) fonts must contain 190 "sections", 126 symbols each.
assert(getCharacterIndex(0xfe, 0xff) == 190*126);
// 2) ensure that font size is correct - enough to fit all possible symbols
assert(getCharacterDataOffset(getCharacterIndex(0xfe, 0xff)) == data.second);
}
size_t CBitmapHanFont::getLineHeight() const
{
return std::max(size + 1, fallback->getLineHeight());
}
size_t CBitmapHanFont::getGlyphWidth(const char * data) const
{
std::string localChar = Unicode::fromUnicode(std::string(data, Unicode::getCharacterSize(data[0])));
if (localChar.size() == 1)
return fallback->getGlyphWidth(data);
if (localChar.size() == 2)
return size + 1;
return 0;
}

View File

@ -1,136 +0,0 @@
/*
* Fonts.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Surface;
struct SDL_Color;
typedef struct _TTF_Font TTF_Font;
class CBitmapFont;
class CBitmapHanFont;
class IFont
{
protected:
/// Internal function to render font, see renderTextLeft
virtual void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const = 0;
public:
virtual ~IFont()
{}
/// Returns height of font
virtual size_t getLineHeight() const = 0;
/// Returns width, in pixels of a character glyph. Pointer must contain at least characterSize valid bytes
virtual size_t getGlyphWidth(const char * data) const = 0;
/// Return width of the string
virtual size_t getStringWidth(const std::string & data) const;
/**
* @param surface - destination to print text on
* @param data - string to print
* @param color - font color
* @param pos - position of rendered font
*/
/// pos = topleft corner of the text
void renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
/// pos = center of the text
void renderTextRight(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
/// pos = bottomright corner of the text
void renderTextCenter(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
/// pos = topleft corner of the text
void renderTextLinesLeft(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
/// pos = center of the text
void renderTextLinesRight(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
/// pos = bottomright corner of the text
void renderTextLinesCenter(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
};
class CBitmapFont : public IFont
{
static const size_t totalChars = 256;
struct BitmapChar
{
si32 leftOffset;
ui32 width;
si32 rightOffset;
ui8 *pixels; // pixels of this character, part of BitmapFont::data
};
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
const std::array<BitmapChar, totalChars> chars;
const ui8 height;
std::array<BitmapChar, totalChars> loadChars() const;
void renderCharacter(SDL_Surface * surface, const BitmapChar & character, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CBitmapFont(const std::string & filename);
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
friend class CBitmapHanFont;
};
/// supports multi-byte characters for such languages like Chinese
class CBitmapHanFont : public IFont
{
std::unique_ptr<CBitmapFont> fallback;
// data, directly copied from file
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
// size of the font. Not available in file but needed for proper rendering
const size_t size;
size_t getCharacterDataOffset(size_t index) const;
size_t getCharacterIndex(ui8 first, ui8 second) const;
void renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CBitmapHanFont(const JsonNode & config);
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
};
class CTrueTypeFont : public IFont
{
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
const std::unique_ptr<TTF_Font, void (*)(TTF_Font*)> font;
const bool blended;
std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const JsonNode & config);
TTF_Font * loadFont(const JsonNode & config);
int getFontStyle(const JsonNode & config);
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CTrueTypeFont(const JsonNode & fontConfig);
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
size_t getStringWidth(const std::string & data) const override;
};

View File

@ -14,7 +14,6 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"

View File

@ -1,25 +0,0 @@
/*
* SDL_Compat.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 <SDL_version.h>
#if (SDL_MAJOR_VERSION == 2)
#include <SDL_keycode.h>
typedef int SDLX_Coord;
typedef int SDLX_Size;
#else
#error "unknown or unsupported SDL version"
#endif

View File

@ -10,11 +10,13 @@
#import <UIKit/UIKit.h>
#include <SDL_events.h>
NS_ASSUME_NONNULL_BEGIN
@interface GameChatKeyboardHandler : NSObject
- (void)triggerInput;
+ (void)sendKeyEventWithKeyCode:(SDL_KeyCode)keyCode;
@end

View File

@ -10,20 +10,8 @@
#import "GameChatKeyboardHandler.h"
#include <SDL_events.h>
static int watchReturnKey(void * userdata, SDL_Event * event);
static void sendKeyEvent(SDL_KeyCode keyCode)
{
SDL_Event keyEvent;
keyEvent.key = (SDL_KeyboardEvent){
.type = SDL_KEYDOWN,
.keysym.sym = keyCode,
};
SDL_PushEvent(&keyEvent);
}
@interface GameChatKeyboardHandler ()
@property (nonatomic) BOOL wasChatMessageSent;
@ -31,28 +19,40 @@ static void sendKeyEvent(SDL_KeyCode keyCode)
@implementation GameChatKeyboardHandler
- (void)triggerInput {
+ (void)sendKeyEventWithKeyCode:(SDL_KeyCode)keyCode
{
SDL_Event keyEvent;
keyEvent.key = (SDL_KeyboardEvent){
.type = SDL_KEYDOWN,
.state = SDL_PRESSED,
.keysym.sym = keyCode,
};
SDL_PushEvent(&keyEvent);
}
- (instancetype)init {
self = [super init];
__auto_type notificationCenter = NSNotificationCenter.defaultCenter;
[notificationCenter addObserver:self selector:@selector(textDidBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[notificationCenter addObserver:self selector:@selector(textDidEndEditing:) name:UITextFieldTextDidEndEditingNotification object:nil];
self.wasChatMessageSent = NO;
sendKeyEvent(SDLK_TAB);
return self;
}
#pragma mark - Notifications
- (void)textDidBeginEditing:(NSNotification *)n {
self.wasChatMessageSent = NO;
// watch for pressing Return to ignore sending Escape key after keyboard is closed
SDL_AddEventWatch(watchReturnKey, (__bridge void *)self);
}
- (void)textDidEndEditing:(NSNotification *)n {
[NSNotificationCenter.defaultCenter removeObserver:self];
// discard chat message
if(!self.wasChatMessageSent)
sendKeyEvent(SDLK_ESCAPE);
[[self class] sendKeyEventWithKeyCode:SDLK_ESCAPE];
}
@end

View File

@ -95,7 +95,7 @@
- (void)handlePinch:(UIGestureRecognizer *)gesture {
if(gesture.state != UIGestureRecognizerStateBegan || CSH->state != EClientState::GAMEPLAY)
return;
[self.gameChatHandler triggerInput];
[GameChatKeyboardHandler sendKeyEventWithKeyCode:SDLK_SPACE];
}
#pragma mark - UIGestureRecognizerDelegate

View File

@ -17,13 +17,10 @@
#include "CSelectionBase.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CVideoHandler.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../mainmenu/CMainMenu.h"
#include "../mainmenu/CPrologEpilogVideo.h"
#include "../widgets/CComponent.h"
@ -33,6 +30,9 @@
#include "../widgets/TextControls.h"
#include "../windows/GUIClasses.h"
#include "../windows/InfoWindows.h"
#include "../render/IImage.h"
#include "../render/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/CGeneralTextHandler.h"
@ -119,7 +119,6 @@ CBonusSelection::CBonusSelection()
void CBonusSelection::loadPositionsOfGraphics()
{
const JsonNode config(ResourceID("config/campaign_regions.json"));
int idx = 0;
for(const JsonNode & campaign : config["campaign_regions"].Vector())
{
@ -140,7 +139,6 @@ void CBonusSelection::loadPositionsOfGraphics()
campDescriptions.push_back(sc);
idx++;
}
}
@ -494,8 +492,8 @@ CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, cons
graphicsSelected->disable();
graphicsStriped = std::make_shared<CPicture>(prefix + "Co" + suffix + ".BMP");
graphicsStriped->disable();
pos.w = graphicsNotSelected->bg->w;
pos.h = graphicsNotSelected->bg->h;
pos.w = graphicsNotSelected->pos.w;
pos.h = graphicsNotSelected->pos.h;
}
@ -527,7 +525,7 @@ void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState)
if(indeterminate(down))
return;
if(!down && selectable && !CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.current->motion.x - pos.x, GH.current->motion.y - pos.y))
if(!down && selectable && !graphicsNotSelected->getSurface()->isTransparent(GH.getCursorPosition() - pos.topLeft()))
{
CSH->setCampaignMap(idOfMapAndRegion);
}
@ -537,7 +535,7 @@ void CBonusSelection::CRegion::clickRight(tribool down, bool previousState)
{
// FIXME: For some reason "down" is only ever contain indeterminate_value
auto text = CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionText;
if(!CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.current->motion.x - pos.x, GH.current->motion.y - pos.y) && text.size())
if(!graphicsNotSelected->getSurface()->isTransparent(GH.getCursorPosition() - pos.topLeft()) && text.size())
{
CRClickPopup::createAndPush(text);
}

View File

@ -15,10 +15,8 @@
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/StartInfo.h"
#include "../../lib/mapping/CMapInfo.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CPlayerInterface.h"
CCampaignInfoScreen::CCampaignInfoScreen()

View File

@ -20,12 +20,10 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CVideoHandler.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../mainmenu/CMainMenu.h"
#include "../widgets/CComponent.h"
@ -35,6 +33,7 @@
#include "../widgets/TextControls.h"
#include "../windows/GUIClasses.h"
#include "../windows/InfoWindows.h"
#include "../render/CAnimation.h"
#include "../../lib/NetPacksLobby.h"
#include "../../lib/CGeneralTextHandler.h"
@ -44,6 +43,8 @@
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/serializer/Connection.h"
#include <SDL_events.h>
ISelectionScreenInfo::ISelectionScreenInfo(ESelectionScreen ScreenType)
: screenType(ScreenType)
{
@ -312,7 +313,7 @@ CChatBox::CChatBox(const Rect & rect)
type |= REDRAW_PARENT;
const int height = static_cast<int>(graphics->fonts[FONT_SMALL]->getLineHeight());
inputBox = std::make_shared<CTextInput>(Rect(0, rect.h - height, rect.w, height));
inputBox = std::make_shared<CTextInput>(Rect(0, rect.h - height, rect.w, height), EFonts::FONT_SMALL, 0);
inputBox->removeUsedEvents(KEYBOARD);
chatHistory = std::make_shared<CTextBox>("", Rect(0, 0, rect.w, rect.h - height), 1);

View File

@ -14,7 +14,6 @@
#include "../CGameInfo.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"

View File

@ -14,7 +14,6 @@
#include "../CGameInfo.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"

View File

@ -14,10 +14,8 @@
#include "CLobbyScreen.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"
@ -26,6 +24,7 @@
#include "../widgets/TextControls.h"
#include "../windows/GUIClasses.h"
#include "../windows/InfoWindows.h"
#include "../render/CAnimation.h"
#include "../../CCallback.h"
@ -36,6 +35,7 @@
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/serializer/Connection.h"
#include <SDL_events.h>
bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::shared_ptr<CMapInfo> bbb)
{
@ -273,9 +273,9 @@ void SelectionTab::clickLeft(tribool down, bool previousState)
select(line);
}
#ifdef VCMI_IOS
// focus input field if clicked inside it
else if(inputName && inputName->active && inputNameRect.isInside(GH.current->button.x, GH.current->button.y))
inputName->giveFocus();
// focus input field if clicked inside it
else if(inputName && inputName->active && inputNameRect.isInside(GH.getCursorPosition()))
inputName->giveFocus();
#endif
}
}
@ -454,8 +454,7 @@ void SelectionTab::updateListItems()
int SelectionTab::getLine()
{
int line = -1;
Point clickPos(GH.current->button.x, GH.current->button.y);
clickPos = clickPos - pos.topLeft();
Point clickPos = GH.getCursorPosition() - pos.topLeft();
// Ignore clicks on save name area
int maxPosY;

View File

@ -14,12 +14,10 @@
#include "CMainMenu.h"
#include "../CGameInfo.h"
#include "../CMessage.h"
#include "../CMusicHandler.h"
#include "../CVideoHandler.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"

View File

@ -20,7 +20,6 @@
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/filesystem/CCompressedStream.h"
#include "../gui/SDL_Extensions.h"
#include "../gui/CursorHandler.h"
#include "../CGameInfo.h"
@ -28,7 +27,6 @@
#include "../../lib/JsonNode.h"
#include "../CMusicHandler.h"
#include "../CVideoHandler.h"
#include "../Graphics.h"
#include "../../lib/serializer/Connection.h"
#include "../../lib/serializer/CTypeList.h"
#include "../../lib/VCMIDirs.h"
@ -36,10 +34,8 @@
#include "../windows/GUIClasses.h"
#include "../CPlayerInterface.h"
#include "../../CCallback.h"
#include "../CMessage.h"
#include "../Client.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CAnimation.h"
#include "../widgets/CComponent.h"
#include "../widgets/Buttons.h"
#include "../widgets/MiscWidgets.h"
@ -56,6 +52,7 @@
#include "../../lib/CondSh.h"
#include "../../lib/mapping/CCampaignHandler.h"
#include <SDL_events.h>
namespace fs = boost::filesystem;
@ -76,11 +73,7 @@ CMenuScreen::CMenuScreen(const JsonNode & configNode)
background = std::make_shared<CPicture>(config["background"].String());
if(config["scalable"].Bool())
{
if(background->bg->format->palette)
background->convertToScreenBPP();
background->scaleTo(Point(screen->w, screen->h));
}
pos = background->center();
@ -439,11 +432,6 @@ CMultiPlayers::CMultiPlayers(const std::string & firstPlayer, ESelectionScreen S
void CMultiPlayers::onChange(std::string newText)
{
size_t namesCount = 0;
for(auto & elem : inputNames)
if(!elem->getText().empty())
namesCount++;
}
void CMultiPlayers::enterSelectionScreen()

View File

@ -0,0 +1,377 @@
/*
* CAnimation.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 "CAnimation.h"
#include "CDefFile.h"
#include "Graphics.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/JsonNode.h"
#include "../renderSDL/SDLImage.h"
std::shared_ptr<IImage> CAnimation::getFromExtraDef(std::string filename)
{
size_t pos = filename.find(':');
if (pos == -1)
return nullptr;
CAnimation anim(filename.substr(0, pos));
pos++;
size_t frame = atoi(filename.c_str()+pos);
size_t group = 0;
pos = filename.find(':', pos);
if (pos != -1)
{
pos++;
group = frame;
frame = atoi(filename.c_str()+pos);
}
anim.load(frame ,group);
auto ret = anim.images[group][frame];
anim.images.clear();
return ret;
}
bool CAnimation::loadFrame(size_t frame, size_t group)
{
if(size(group) <= frame)
{
printError(frame, group, "LoadFrame");
return false;
}
auto image = getImage(frame, group, false);
if(image)
{
return true;
}
//try to get image from def
if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL)
{
if(defFile)
{
auto frameList = defFile->getEntries();
if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
{
images[group][frame] = std::make_shared<SDLImage>(defFile.get(), frame, group);
return true;
}
}
// still here? image is missing
printError(frame, group, "LoadFrame");
images[group][frame] = std::make_shared<SDLImage>("DEFAULT");
}
else //load from separate file
{
auto img = getFromExtraDef(source[group][frame]["file"].String());
if(!img)
img = std::make_shared<SDLImage>(source[group][frame]);
images[group][frame] = img;
return true;
}
return false;
}
bool CAnimation::unloadFrame(size_t frame, size_t group)
{
auto image = getImage(frame, group, false);
if(image)
{
images[group].erase(frame);
if(images[group].empty())
images.erase(group);
return true;
}
return false;
}
void CAnimation::initFromJson(const JsonNode & config)
{
std::string basepath;
basepath = config["basepath"].String();
JsonNode base(JsonNode::JsonType::DATA_STRUCT);
base["margins"] = config["margins"];
base["width"] = config["width"];
base["height"] = config["height"];
for(const JsonNode & group : config["sequences"].Vector())
{
size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING)
source[groupID].clear();
for(const JsonNode & frame : group["frames"].Vector())
{
JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
JsonUtils::inherit(toAdd, base);
toAdd["file"].String() = basepath + frame.String();
source[groupID].push_back(toAdd);
}
}
for(const JsonNode & node : config["images"].Vector())
{
size_t group = node["group"].Integer();
size_t frame = node["frame"].Integer();
if (source[group].size() <= frame)
source[group].resize(frame+1);
JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
JsonUtils::inherit(toAdd, base);
toAdd["file"].String() = basepath + node["file"].String();
source[group][frame] = toAdd;
}
}
void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
{
if(images.empty())
{
logGlobal->error("Nothing to export, animation is empty");
return;
}
boost::filesystem::path actualPath = path / "SPRITES" / name;
boost::filesystem::create_directories(actualPath);
size_t counter = 0;
for(const auto & groupPair : images)
{
size_t group = groupPair.first;
for(const auto & imagePair : groupPair.second)
{
size_t frame = imagePair.first;
const auto img = imagePair.second;
boost::format fmt("%d_%d.bmp");
fmt % group % frame;
img->exportBitmap(actualPath / fmt.str());
counter++;
}
}
logGlobal->info("Exported %d frames to %s", counter, actualPath.string());
}
void CAnimation::init()
{
if(defFile)
{
const std::map<size_t, size_t> defEntries = defFile->getEntries();
for (auto & defEntry : defEntries)
source[defEntry.first].resize(defEntry.second);
}
ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
if (vstd::contains(graphics->imageLists, resID.getName()))
initFromJson(graphics->imageLists[resID.getName()]);
auto configList = CResourceHandler::get()->getResourcesWithName(resID);
for(auto & loader : configList)
{
auto stream = loader->load(resID);
std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
stream->read(textData.get(), stream->getSize());
const JsonNode config((char*)textData.get(), stream->getSize());
initFromJson(config);
}
}
void CAnimation::printError(size_t frame, size_t group, std::string type) const
{
logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame);
}
CAnimation::CAnimation(std::string Name):
name(Name),
preloaded(false),
defFile()
{
size_t dotPos = name.find_last_of('.');
if ( dotPos!=-1 )
name.erase(dotPos);
std::transform(name.begin(), name.end(), name.begin(), toupper);
ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION);
if(CResourceHandler::get()->existsResource(resource))
defFile = std::make_shared<CDefFile>(name);
init();
if(source.empty())
logAnim->error("Animation %s failed to load", Name);
}
CAnimation::CAnimation():
name(""),
preloaded(false),
defFile()
{
init();
}
CAnimation::~CAnimation() = default;
void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup)
{
if(!source.count(sourceGroup))
{
logAnim->error("Group %d missing in %s", sourceGroup, name);
return;
}
if(source[sourceGroup].size() <= sourceFrame)
{
logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name);
return;
}
//todo: clone actual loaded Image object
JsonNode clone(source[sourceGroup][sourceFrame]);
if(clone.getType() == JsonNode::JsonType::DATA_NULL)
{
std::string temp = name+":"+boost::lexical_cast<std::string>(sourceGroup)+":"+boost::lexical_cast<std::string>(sourceFrame);
clone["file"].String() = temp;
}
source[targetGroup].push_back(clone);
size_t index = source[targetGroup].size() - 1;
if(preloaded)
load(index, targetGroup);
}
void CAnimation::setCustom(std::string filename, size_t frame, size_t group)
{
if (source[group].size() <= frame)
source[group].resize(frame+1);
source[group][frame]["file"].String() = filename;
//FIXME: update image if already loaded
}
std::shared_ptr<IImage> CAnimation::getImage(size_t frame, size_t group, bool verbose) const
{
auto groupIter = images.find(group);
if (groupIter != images.end())
{
auto imageIter = groupIter->second.find(frame);
if (imageIter != groupIter->second.end())
return imageIter->second;
}
if (verbose)
printError(frame, group, "GetImage");
return nullptr;
}
void CAnimation::load()
{
for (auto & elem : source)
for (size_t image=0; image < elem.second.size(); image++)
loadFrame(image, elem.first);
}
void CAnimation::unload()
{
for (auto & elem : source)
for (size_t image=0; image < elem.second.size(); image++)
unloadFrame(image, elem.first);
}
void CAnimation::preload()
{
if(!preloaded)
{
preloaded = true;
load();
}
}
void CAnimation::loadGroup(size_t group)
{
if (vstd::contains(source, group))
for (size_t image=0; image < source[group].size(); image++)
loadFrame(image, group);
}
void CAnimation::unloadGroup(size_t group)
{
if (vstd::contains(source, group))
for (size_t image=0; image < source[group].size(); image++)
unloadFrame(image, group);
}
void CAnimation::load(size_t frame, size_t group)
{
loadFrame(frame, group);
}
void CAnimation::unload(size_t frame, size_t group)
{
unloadFrame(frame, group);
}
size_t CAnimation::size(size_t group) const
{
auto iter = source.find(group);
if (iter != source.end())
return iter->second.size();
return 0;
}
void CAnimation::horizontalFlip()
{
for(auto & group : images)
for(auto & image : group.second)
image.second->horizontalFlip();
}
void CAnimation::verticalFlip()
{
for(auto & group : images)
for(auto & image : group.second)
image.second->verticalFlip();
}
void CAnimation::playerColored(PlayerColor player)
{
for(auto & group : images)
for(auto & image : group.second)
image.second->playerColored(player);
}
void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
{
for(size_t frame = 0; frame < size(sourceGroup); ++frame)
{
duplicateImage(sourceGroup, frame, targetGroup);
auto image = getImage(frame, targetGroup);
image->verticalFlip();
}
}

View File

@ -9,77 +9,14 @@
*/
#pragma once
#include "../../lib/vcmi_endian.h"
#include "../../lib/GameConstants.h"
#ifdef IN
#undef IN
#endif
#ifdef OUT
#undef OUT
#endif
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
class Rect;
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Surface;
struct SDL_Color;
class CDefFile;
class ColorFilter;
/*
* Base class for images, can be used for non-animation pictures as well
*/
class IImage
{
public:
using SpecialPalette = std::array<SDL_Color, 7>;
//draws image on surface "where" at position
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0;
virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0;
virtual std::shared_ptr<IImage> scaleFast(float scale) const = 0;
virtual void exportBitmap(const boost::filesystem::path & path) const = 0;
//Change palette to specific player
virtual void playerColored(PlayerColor player)=0;
//set special color for flag
virtual void setFlagColor(PlayerColor player)=0;
//test transparency of specific pixel
virtual bool isTransparent(const Point & coords) const = 0;
virtual Point dimensions() const = 0;
int width() const;
int height() const;
//only indexed bitmaps, 16 colors maximum
virtual void shiftPalette(int from, int howMany) = 0;
virtual void adjustPalette(const ColorFilter & shifter) = 0;
virtual void resetPalette(int colorID) = 0;
virtual void resetPalette() = 0;
//only indexed bitmaps with 7 special colors
virtual void setSpecialPallete(const SpecialPalette & SpecialPalette) = 0;
virtual void horizontalFlip() = 0;
virtual void verticalFlip() = 0;
IImage();
virtual ~IImage();
/// loads image from specified file. Returns 0-sized images on failure
static std::shared_ptr<IImage> createFromFile( const std::string & path );
};
class IImage;
/// Class for handling animation
class CAnimation
@ -154,31 +91,3 @@ public:
void createFlippedGroup(const size_t sourceGroup, const size_t targetGroup);
};
const float DEFAULT_DELTA = 0.05f;
class CFadeAnimation
{
public:
enum class EMode
{
NONE, IN, OUT
};
private:
float delta;
SDL_Surface * fadingSurface;
bool fading;
float fadingCounter;
bool shouldFreeSurface;
float initialCounter() const;
bool isFinished() const;
public:
EMode fadingMode;
CFadeAnimation();
~CFadeAnimation();
void init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd = false, float animDelta = DEFAULT_DELTA);
void update();
void draw(SDL_Surface * targetSurface, const Point & targetPoint);
bool isFading() const { return fading; }
};

View File

@ -8,13 +8,15 @@
*
*/
#include "StdInc.h"
#include "CBitmapHandler.h"
#include "../renderSDL/SDL_Extensions.h"
#include "../lib/filesystem/Filesystem.h"
#include <SDL_image.h>
#include "CBitmapHandler.h"
#include "gui/SDL_Extensions.h"
#include "../lib/vcmi_endian.h"
#include <SDL_image.h>
namespace BitmapHandler
{
SDL_Surface * loadH3PCX(ui8 * data, size_t size);

358
client/render/CDefFile.cpp Normal file
View File

@ -0,0 +1,358 @@
/*
* CDefFile.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 "CDefFile.h"
#include "IImageLoader.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/Point.h"
#include <SDL_pixels.h>
// Extremely simple file cache. TODO: smarter, more general solution
class CFileCache
{
static const int cacheSize = 50; //Max number of cached files
struct FileData
{
ResourceID name;
size_t size;
std::unique_ptr<ui8[]> data;
std::unique_ptr<ui8[]> getCopy()
{
auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
std::copy(data.get(), data.get() + size, ret.get());
return ret;
}
FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
name{std::move(name_)},
size{size_},
data{std::move(data_)}
{}
};
std::deque<FileData> cache;
public:
std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
{
for(auto & file : cache)
{
if (file.name == rid)
return file.getCopy();
}
// Still here? Cache miss
if (cache.size() > cacheSize)
cache.pop_front();
auto data = CResourceHandler::get()->load(rid)->readAll();
cache.emplace_back(std::move(rid), data.second, std::move(data.first));
return cache.back().getCopy();
}
};
enum class DefType : uint32_t
{
SPELL = 0x40,
SPRITE = 0x41,
CREATURE = 0x42,
MAP = 0x43,
MAP_HERO = 0x44,
TERRAIN = 0x45,
CURSOR = 0x46,
INTERFACE = 0x47,
SPRITE_FRAME = 0x48,
BATTLE_HERO = 0x49
};
static CFileCache animationCache;
/*************************************************************************
* DefFile, class used for def loading *
*************************************************************************/
bool operator== (const SDL_Color & lhs, const SDL_Color & rhs)
{
return (lhs.a == rhs.a) && (lhs.b == rhs.b) &&(lhs.g == rhs.g) &&(lhs.r == rhs.r);
}
CDefFile::CDefFile(std::string Name):
data(nullptr),
palette(nullptr)
{
//First 8 colors in def palette used for transparency
static SDL_Color H3Palette[8] =
{
{ 0, 0, 0, 0},// transparency ( used in most images )
{ 0, 0, 0, 64},// shadow border ( used in battle, adventure map def's )
{ 0, 0, 0, 64},// shadow border ( used in fog-of-war def's )
{ 0, 0, 0, 128},// shadow body ( used in fog-of-war def's )
{ 0, 0, 0, 128},// shadow body ( used in battle, adventure map def's )
{ 0, 0, 0, 0},// selection ( used in battle def's )
{ 0, 0, 0, 128},// shadow body below selection ( used in battle def's )
{ 0, 0, 0, 64} // shadow border below selection ( used in battle def's )
};
data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]);
int it = 0;
ui32 type = read_le_u32(data.get() + it);
it+=4;
//int width = read_le_u32(data + it); it+=4;//not used
//int height = read_le_u32(data + it); it+=4;
it+=8;
ui32 totalBlocks = read_le_u32(data.get() + it);
it+=4;
for (ui32 i= 0; i<256; i++)
{
palette[i].r = data[it++];
palette[i].g = data[it++];
palette[i].b = data[it++];
palette[i].a = SDL_ALPHA_OPAQUE;
}
switch(static_cast<DefType>(type))
{
case DefType::SPELL:
palette[0] = H3Palette[0];
break;
case DefType::SPRITE:
case DefType::SPRITE_FRAME:
for(ui32 i= 0; i<8; i++)
palette[i] = H3Palette[i];
break;
case DefType::CREATURE:
palette[0] = H3Palette[0];
palette[1] = H3Palette[1];
palette[4] = H3Palette[4];
palette[5] = H3Palette[5];
palette[6] = H3Palette[6];
palette[7] = H3Palette[7];
break;
case DefType::MAP:
case DefType::MAP_HERO:
palette[0] = H3Palette[0];
palette[1] = H3Palette[1];
palette[4] = H3Palette[4];
//5 = owner flag, handled separately
break;
case DefType::TERRAIN:
palette[0] = H3Palette[0];
palette[1] = H3Palette[1];
palette[2] = H3Palette[2];
palette[3] = H3Palette[3];
palette[4] = H3Palette[4];
break;
case DefType::CURSOR:
palette[0] = H3Palette[0];
break;
case DefType::INTERFACE:
palette[0] = H3Palette[0];
palette[1] = H3Palette[1];
palette[4] = H3Palette[4];
//player colors handled separately
//TODO: disallow colorizing other def types
break;
case DefType::BATTLE_HERO:
palette[0] = H3Palette[0];
palette[1] = H3Palette[1];
palette[4] = H3Palette[4];
break;
default:
logAnim->error("Unknown def type %d in %s", type, Name);
break;
}
for (ui32 i=0; i<totalBlocks; i++)
{
size_t blockID = read_le_u32(data.get() + it);
it+=4;
size_t totalEntries = read_le_u32(data.get() + it);
it+=12;
//8 unknown bytes - skipping
//13 bytes for name of every frame in this block - not used, skipping
it+= 13 * (int)totalEntries;
for (ui32 j=0; j<totalEntries; j++)
{
size_t currOffset = read_le_u32(data.get() + it);
offset[blockID].push_back(currOffset);
it += 4;
}
}
}
void CDefFile::loadFrame(size_t frame, size_t group, IImageLoader &loader) const
{
std::map<size_t, std::vector <size_t> >::const_iterator it;
it = offset.find(group);
assert (it != offset.end());
const ui8 * FDef = data.get()+it->second[frame];
const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
SSpriteDef sprite;
sprite.format = read_le_u32(&sd.format);
sprite.fullWidth = read_le_u32(&sd.fullWidth);
sprite.fullHeight = read_le_u32(&sd.fullHeight);
sprite.width = read_le_u32(&sd.width);
sprite.height = read_le_u32(&sd.height);
sprite.leftMargin = read_le_u32(&sd.leftMargin);
sprite.topMargin = read_le_u32(&sd.topMargin);
ui32 currentOffset = sizeof(SSpriteDef);
//special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
if(sprite.format == 1 && sprite.width > sprite.fullWidth && sprite.height > sprite.fullHeight)
{
sprite.leftMargin = 0;
sprite.topMargin = 0;
sprite.width = sprite.fullWidth;
sprite.height = sprite.fullHeight;
currentOffset -= 16;
}
const ui32 BaseOffset = currentOffset;
loader.init(Point(sprite.width, sprite.height),
Point(sprite.leftMargin, sprite.topMargin),
Point(sprite.fullWidth, sprite.fullHeight), palette.get());
switch(sprite.format)
{
case 0:
{
//pixel data is not compressed, copy data to surface
for(ui32 i=0; i<sprite.height; i++)
{
loader.load(sprite.width, FDef + currentOffset);
currentOffset += sprite.width;
loader.endLine();
}
break;
}
case 1:
{
//for each line we have offset of pixel data
const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+currentOffset);
currentOffset += sizeof(ui32) * sprite.height;
for(ui32 i=0; i<sprite.height; i++)
{
//get position of the line
currentOffset=BaseOffset + read_le_u32(RWEntriesLoc + i);
ui32 TotalRowLength = 0;
while(TotalRowLength<sprite.width)
{
ui8 segmentType = FDef[currentOffset++];
ui32 length = FDef[currentOffset++] + 1;
if(segmentType==0xFF)//Raw data
{
loader.load(length, FDef + currentOffset);
currentOffset+=length;
}
else// RLE
{
loader.load(length, segmentType);
}
TotalRowLength += length;
}
loader.endLine();
}
break;
}
case 2:
{
currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset);
for(ui32 i=0; i<sprite.height; i++)
{
ui32 TotalRowLength=0;
while(TotalRowLength<sprite.width)
{
ui8 segment=FDef[currentOffset++];
ui8 code = segment / 32;
ui8 length = (segment & 31) + 1;
if(code==7)//Raw data
{
loader.load(length, FDef + currentOffset);
currentOffset += length;
}
else//RLE
{
loader.load(length, code);
}
TotalRowLength+=length;
}
loader.endLine();
}
break;
}
case 3:
{
for(ui32 i=0; i<sprite.height; i++)
{
currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset+i*2*(sprite.width/32));
ui32 TotalRowLength=0;
while(TotalRowLength<sprite.width)
{
ui8 segment = FDef[currentOffset++];
ui8 code = segment / 32;
ui8 length = (segment & 31) + 1;
if(code==7)//Raw data
{
loader.load(length, FDef + currentOffset);
currentOffset += length;
}
else//RLE
{
loader.load(length, code);
}
TotalRowLength += length;
}
loader.endLine();
}
break;
}
default:
logGlobal->error("Error: unsupported format of def file: %d", sprite.format);
break;
}
}
CDefFile::~CDefFile() = default;
const std::map<size_t, size_t > CDefFile::getEntries() const
{
std::map<size_t, size_t > ret;
for (auto & elem : offset)
ret[elem.first] = elem.second.size();
return ret;
}

51
client/render/CDefFile.h Normal file
View File

@ -0,0 +1,51 @@
/*
* CDefFile.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../../lib/vcmi_endian.h"
class IImageLoader;
struct SDL_Color;
/// Class for def loading
/// After loading will store general info (palette and frame offsets) and pointer to file itself
class CDefFile
{
private:
PACKED_STRUCT_BEGIN
struct SSpriteDef
{
ui32 size;
ui32 format; /// format in which pixel data is stored
ui32 fullWidth; /// full width and height of frame, including borders
ui32 fullHeight;
ui32 width; /// width and height of pixel data, borders excluded
ui32 height;
si32 leftMargin;
si32 topMargin;
} PACKED_STRUCT_END;
//offset[group][frame] - offset of frame data in file
std::map<size_t, std::vector <size_t> > offset;
std::unique_ptr<ui8[]> data;
std::unique_ptr<SDL_Color[]> palette;
public:
CDefFile(std::string Name);
~CDefFile();
//load frame as SDL_Surface
void loadFrame(size_t frame, size_t group, IImageLoader &loader) const;
const std::map<size_t, size_t> getEntries() const;
};

View File

@ -0,0 +1,101 @@
/*
* CFadeAnimation.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 "CFadeAnimation.h"
#include "../renderSDL/SDL_Extensions.h"
#include <SDL_surface.h>
float CFadeAnimation::initialCounter() const
{
if (fadingMode == EMode::OUT)
return 1.0f;
return 0.0f;
}
void CFadeAnimation::update()
{
if (!fading)
return;
if (fadingMode == EMode::OUT)
fadingCounter -= delta;
else
fadingCounter += delta;
if (isFinished())
{
fading = false;
if (shouldFreeSurface)
{
SDL_FreeSurface(fadingSurface);
fadingSurface = nullptr;
}
}
}
bool CFadeAnimation::isFinished() const
{
if (fadingMode == EMode::OUT)
return fadingCounter <= 0.0f;
return fadingCounter >= 1.0f;
}
CFadeAnimation::CFadeAnimation()
: delta(0), fadingSurface(nullptr), fading(false), fadingCounter(0), shouldFreeSurface(false),
fadingMode(EMode::NONE)
{
}
CFadeAnimation::~CFadeAnimation()
{
if (fadingSurface && shouldFreeSurface)
SDL_FreeSurface(fadingSurface);
}
void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd, float animDelta)
{
if (fading)
{
// in that case, immediately finish the previous fade
// (alternatively, we could just return here to ignore the new fade request until this one finished (but we'd need to free the passed bitmap to avoid leaks))
logGlobal->warn("Tried to init fading animation that is already running.");
if (fadingSurface && shouldFreeSurface)
SDL_FreeSurface(fadingSurface);
}
if (animDelta <= 0.0f)
{
logGlobal->warn("Fade anim: delta should be positive; %f given.", animDelta);
animDelta = DEFAULT_DELTA;
}
if (sourceSurface)
fadingSurface = sourceSurface;
delta = animDelta;
fadingMode = mode;
fadingCounter = initialCounter();
fading = true;
shouldFreeSurface = freeSurfaceAtEnd;
}
void CFadeAnimation::draw(SDL_Surface * targetSurface, const Point &targetPoint)
{
if (!fading || !fadingSurface || fadingMode == EMode::NONE)
{
fading = false;
return;
}
CSDL_Ext::setAlpha(fadingSurface, (int)(fadingCounter * 255));
CSDL_Ext::blitSurface(fadingSurface, targetSurface, targetPoint); //FIXME
CSDL_Ext::setAlpha(fadingSurface, 255);
}

View File

@ -0,0 +1,53 @@
/*
* CFadeAnimation.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
#ifdef IN
#undef IN
#endif
#ifdef OUT
#undef OUT
#endif
VCMI_LIB_NAMESPACE_BEGIN
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Surface;
const float DEFAULT_DELTA = 0.05f;
class CFadeAnimation
{
public:
enum class EMode
{
NONE, IN, OUT
};
private:
float delta;
SDL_Surface * fadingSurface;
bool fading;
float fadingCounter;
bool shouldFreeSurface;
float initialCounter() const;
bool isFinished() const;
public:
EMode fadingMode;
CFadeAnimation();
~CFadeAnimation();
void init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd = false, float animDelta = DEFAULT_DELTA);
void update();
void draw(SDL_Surface * targetSurface, const Point & targetPoint);
bool isFading() const { return fading; }
};

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