Merge branch 'master' into android-support
4
.gitattributes
vendored
|
@ -2,8 +2,8 @@
|
|||
/attic export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
RELEASE_NOTES export-ignore
|
||||
/.github export-ignore
|
||||
/changelog_entries export-ignore
|
||||
|
||||
# help github's language detection and syntax highlighting
|
||||
*.cfg linguist-language=INI
|
||||
|
|
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -1,5 +1,5 @@
|
|||
name: Bug Report
|
||||
description: Create a report to help us improve the game,
|
||||
description: Create a bug report to help us improve the game.
|
||||
labels: ['Bug']
|
||||
|
||||
body:
|
||||
|
@ -8,17 +8,11 @@ body:
|
|||
attributes:
|
||||
label: Game and System Information
|
||||
description: |
|
||||
- What version of the game are you running?
|
||||
- Where did you download it from? (Steam, Mac App Store, our website, built from source, etc.)
|
||||
- If you're using a custom build, what compiler did you use, and what commit did you build at?
|
||||
- What version of the game are you running? (If you built wesnoth yourself, mention the exact commit)
|
||||
- What OS are you running?
|
||||
value: |
|
||||
- **Version:**
|
||||
- **Downloaded from:**
|
||||
- **Build info:**
|
||||
- **OS:**
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
|
|
314
.github/labeler.yml
vendored
|
@ -1,138 +1,284 @@
|
|||
Achievements:
|
||||
- src/achievements.hpp
|
||||
- src/achievements.cpp
|
||||
- src/gui/dialogs/achievements_dialog.hpp
|
||||
- src/gui/dialogs/achievements_dialog.cpp
|
||||
- data/achievements.cfg
|
||||
- data/campaigns/*/achievements.cfg
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/achievements.hpp
|
||||
- src/achievements.cpp
|
||||
- src/gui/dialogs/achievements_dialog.hpp
|
||||
- src/gui/dialogs/achievements_dialog.cpp
|
||||
- data/achievements.cfg
|
||||
- data/campaigns/*/achievements.cfg
|
||||
|
||||
Add-ons:
|
||||
- src/server/campaignd/**
|
||||
- src/server/common/**
|
||||
- src/addon/**
|
||||
- src/gui/dialogs/addon/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/server/campaignd/**
|
||||
- src/server/common/**
|
||||
- src/addon/**
|
||||
- src/gui/dialogs/addon/**
|
||||
|
||||
AI:
|
||||
- data/ai/**/*
|
||||
- data/campaigns/*/ai/**
|
||||
- src/ai/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/ai/**/*
|
||||
- data/campaigns/*/ai/**
|
||||
- src/ai/**/*
|
||||
|
||||
Audio:
|
||||
- data/campaigns/*/sounds/**
|
||||
- data/core/music/*
|
||||
- data/core/sounds/**/*
|
||||
- sounds/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/*/sounds/**
|
||||
- data/core/music/*
|
||||
- data/core/sounds/**/*
|
||||
- sounds/*
|
||||
|
||||
Building:
|
||||
- projectfiles/**/*
|
||||
- source_lists/*
|
||||
- CMakeLists.txt
|
||||
- cmake/*
|
||||
- src/CMakeLists.txt
|
||||
- SConstruct
|
||||
- scons/*
|
||||
- src/SConscript
|
||||
- src/SConstruct
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- projectfiles/**/*
|
||||
- source_lists/*
|
||||
- CMakeLists.txt
|
||||
- cmake/*
|
||||
- src/CMakeLists.txt
|
||||
- SConstruct
|
||||
- scons/*
|
||||
- src/SConscript
|
||||
- src/SConstruct
|
||||
|
||||
Campaign:
|
||||
- data/campaigns/**/*
|
||||
Campaign AToTB:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Two_Brothers/**
|
||||
|
||||
Campaign DW:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Dead_Water/**
|
||||
|
||||
Campaign DM:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Delfadors_Memoirs/**
|
||||
|
||||
Campaign DiD:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Descent_Into_Darkness/**
|
||||
|
||||
Campaign EI:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Eastern_Invasion/**
|
||||
|
||||
Campaign HttT:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Heir_To_The_Throne/**
|
||||
|
||||
Campaign LoW:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Legend_of_Wesmere/**
|
||||
|
||||
Campaign L:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Liberty/**
|
||||
|
||||
Campaign NR:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Northern_Rebirth/**
|
||||
|
||||
Campaign SoF:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Sceptre_of_Fire/**
|
||||
|
||||
Campaign SotA:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Secrets_of_the_Ancients/**
|
||||
|
||||
Campaign SotBE:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Son_Of_The_Black_Eye/**
|
||||
|
||||
Campaign THoT:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/The_Hammer_of_Thursagan/**
|
||||
|
||||
Campaign TRoW:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/The_Rise_Of_Wesnoth/**
|
||||
|
||||
Campaign TSG:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/The_South_Guard/**
|
||||
|
||||
Campaign Tutorial:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/tutorial/**
|
||||
|
||||
Campaign UtBS:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Under_the_Burning_Suns/**
|
||||
|
||||
Campaign WoF:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/Winds_of_Fate/**
|
||||
|
||||
Campaign WC:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/campaigns/World_Conquest/**
|
||||
|
||||
CMake:
|
||||
- CMakeLists.txt
|
||||
- cmake/*
|
||||
- src/CMakeLists.txt
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- CMakeLists.txt
|
||||
- cmake/*
|
||||
- src/CMakeLists.txt
|
||||
|
||||
Docker:
|
||||
- .dockerignore
|
||||
- utils/dockerbuilds/travis/*
|
||||
- utils/travis/docker_run.sh
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- .dockerignore
|
||||
- utils/dockerbuilds/travis/*
|
||||
- utils/travis/docker_run.sh
|
||||
|
||||
Docs:
|
||||
- doc/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- doc/**/*
|
||||
|
||||
Editor:
|
||||
- data/core/editor/*
|
||||
- src/editor/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/core/editor/*
|
||||
- src/editor/**/*
|
||||
|
||||
Graphics:
|
||||
- images/**/*
|
||||
- data/campaigns/*/images/**
|
||||
- data/core/images/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- images/**/*
|
||||
- data/campaigns/*/images/**
|
||||
- data/core/images/**/*
|
||||
|
||||
Help:
|
||||
- data/tips.cfg
|
||||
- data/core/help.cfg
|
||||
- src/help/*
|
||||
- src/gui/dialogs/help_browser.*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/tips.cfg
|
||||
- data/core/help.cfg
|
||||
- src/help/*
|
||||
- src/gui/dialogs/help_browser.*
|
||||
|
||||
Input:
|
||||
- data/core/hotkeys.cfg
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/core/hotkeys.cfg
|
||||
|
||||
Lua API:
|
||||
- src/scripting/**/*
|
||||
- data/lua/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/scripting/**/*
|
||||
- data/lua/**/*
|
||||
|
||||
macOS:
|
||||
- projectfiles/Xcode/**/*
|
||||
- src/macosx/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- projectfiles/Xcode/**/*
|
||||
- src/macosx/*
|
||||
|
||||
MP:
|
||||
- data/multiplayer/**/*
|
||||
- src/server/wesnothd/**
|
||||
- src/server/common/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/multiplayer/**/*
|
||||
- src/server/wesnothd/**
|
||||
- src/server/common/**
|
||||
|
||||
Packaging:
|
||||
- packaging/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packaging/**/*
|
||||
|
||||
Schema:
|
||||
- utils/travis/schema_validation.sh
|
||||
- data/schema/**/*
|
||||
- src/serialization/schema/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- utils/travis/schema_validation.sh
|
||||
- data/schema/**/*
|
||||
- src/serialization/schema/*
|
||||
|
||||
SCons:
|
||||
- SConstruct
|
||||
- scons/*
|
||||
- src/SConscript
|
||||
- src/SConstruct
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- SConstruct
|
||||
- scons/*
|
||||
- src/SConscript
|
||||
- src/SConstruct
|
||||
|
||||
Services:
|
||||
- utils/mp-server/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- utils/mp-server/**
|
||||
|
||||
Terrain:
|
||||
- src/terrain/**/*
|
||||
- data/campaigns/*/images/terrain/**
|
||||
- data/campaigns/**/terrain.cfg
|
||||
- data/campaigns/**/terrain_graphics.cfg
|
||||
- data/core/images/terrain/**
|
||||
- data/core/terrain.cfg
|
||||
- data/core/terrain-graphics.cfg
|
||||
- data/core/terrain-graphics/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/terrain/**/*
|
||||
- data/campaigns/*/images/terrain/**
|
||||
- data/campaigns/**/terrain.cfg
|
||||
- data/campaigns/**/terrain_graphics.cfg
|
||||
- data/core/images/terrain/**
|
||||
- data/core/terrain.cfg
|
||||
- data/core/terrain-graphics.cfg
|
||||
- data/core/terrain-graphics/**
|
||||
|
||||
Translations:
|
||||
- po/**/*
|
||||
- data/languages/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- po/**/*
|
||||
- data/languages/*
|
||||
|
||||
Travis:
|
||||
- utils/travis/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- utils/travis/**/*
|
||||
|
||||
UI:
|
||||
- src/gui/**/*
|
||||
- data/gui/**/*
|
||||
- data/schema/gui.cfg
|
||||
- data/themes/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/gui/**/*
|
||||
- data/gui/**/*
|
||||
- data/schema/gui.cfg
|
||||
- data/themes/*
|
||||
|
||||
Unit Tests:
|
||||
- data/test/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/test/**/*
|
||||
|
||||
Units:
|
||||
- src/units/**/*
|
||||
- data/campaigns/*/units/**
|
||||
- data/core/units/**/*
|
||||
- data/core/images/units/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/units/**/*
|
||||
- data/campaigns/*/units/**
|
||||
- data/core/units/**/*
|
||||
- data/core/images/units/**/*
|
||||
|
||||
WML Tools:
|
||||
- data/tools/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- data/tools/**/*
|
||||
|
||||
WFL:
|
||||
- src/formula/**
|
||||
- src/ai/formula/**
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/formula/**
|
||||
- src/ai/formula/**
|
||||
|
|
206
.github/workflows/ci-main.yml
vendored
|
@ -17,31 +17,42 @@ jobs:
|
|||
CLICOLOR_FORCE: 1
|
||||
|
||||
steps:
|
||||
- { uses: actions/checkout@v3, with: { submodules: "recursive" } }
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set git safe directory
|
||||
if: success() || failure()
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
- name: Check for invalid characters
|
||||
if: success() || failure()
|
||||
run: |
|
||||
./utils/CI/check_utf8.sh
|
||||
./utils/CI/utf8_bom_dog.sh
|
||||
- name: Whitespace and WML indentation check
|
||||
if: success() || failure()
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
./utils/CI/fix_whitespace.sh; git status; [ "$(git status --short | wc -l)" = 0 ]
|
||||
run: ./utils/CI/fix_whitespace.sh; git status; git diff --exit-code
|
||||
- name: Run luacheck
|
||||
if: success() || failure()
|
||||
run: luacheck .
|
||||
- name: Doxygen check
|
||||
if: success() || failure()
|
||||
run: doxygen Doxyfile
|
||||
run: doxygen doc/doxygen/Doxyfile
|
||||
|
||||
copyright: # check takes a bit longer and does not need to run in docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- { uses: actions/checkout@v4, with: { fetch-depth: 50 } }
|
||||
|
||||
- name: Copyright check
|
||||
run: ./update_copyrights --output=
|
||||
|
||||
ubuntu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { tool: scons, cc: gcc, cxx: g++, cfg: release, lto: true }
|
||||
- { tool: cmake, cc: clang, cxx: clang++, cfg: debug, lto: false }
|
||||
- { tool: scons, cc: gcc, cxx: g++, cfg: debug, lto: false, sys_lua: false }
|
||||
- { tool: cmake, cc: clang, cxx: clang++, cfg: release, lto: true, sys_lua: true }
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: wesnoth/wesnoth:2204-master
|
||||
|
@ -53,84 +64,87 @@ jobs:
|
|||
CXX: ${{ matrix.cxx }}
|
||||
CXX_STD: 17
|
||||
LTO: ${{ matrix.lto }}
|
||||
SYS_LUA: ${{ matrix.sys_lua }}
|
||||
CLICOLOR_FORCE: 1
|
||||
DISPLAY: :99.0
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
SDL_VIDEODRIVER: dummy
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
- { uses: actions/checkout@v4, with: { submodules: "recursive" } }
|
||||
|
||||
# wesnothd and campaignd should be buildable without SDL2 being present
|
||||
# 1) wesnothd and campaignd should be buildable without SDL2 being present
|
||||
# 2) boost is installed via apt as well as built and installed into /usr/local (for _GLIBCXX_DEBUG support in boost program options)
|
||||
# 3) mariadbpp currently has a deprecation warning that causes strict builds to fail
|
||||
# scons doesn't build it for the wesnoth client and the boost tests, but cmake apparently does
|
||||
- name: Build wesnoth, wesnothd, campaignd and unit tests
|
||||
id: build # needed to check step outcome
|
||||
run: |
|
||||
case $TOOL in
|
||||
scons)
|
||||
build() {
|
||||
ldconfig
|
||||
scons "$@" build="$CFG" ctool="$CC" cxxtool="$CXX" cxx_std="$CXX_STD" \
|
||||
extra_flags_config="-pipe" strict=true forum_user_handler=true \
|
||||
nls=false enable_lto="$LTO" force_color=true jobs=2 --debug=time
|
||||
extra_flags_config="-pipe" forum_user_handler=true \
|
||||
nls=false enable_lto="$LTO" system_lua="$SYS_LUA" force_color=true \
|
||||
jobs=2 --debug=time glibcxx_debug=true glibcxx_assertions=true
|
||||
}
|
||||
build wesnoth boost_unit_tests
|
||||
build strict=true wesnoth boost_unit_tests
|
||||
|
||||
apt remove -y -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev
|
||||
|
||||
build wesnothd campaignd
|
||||
build strict=false wesnothd campaignd
|
||||
;;
|
||||
cmake)
|
||||
build() {
|
||||
ldconfig
|
||||
cmake "$@" -DCMAKE_BUILD_TYPE="$CFG" -DCXX_STD="$CXX_STD" \
|
||||
-DEXTRA_FLAGS_CONFIG="-pipe" -DENABLE_STRICT_COMPILATION=true -DENABLE_MYSQL=true \
|
||||
-DENABLE_NLS=false -DENABLE_LTO="$LTO" -DFORCE_COLOR_OUTPUT=true -DLTO_JOBS=2 .
|
||||
-DEXTRA_FLAGS_CONFIG="-pipe" -DENABLE_MYSQL=true \
|
||||
-DENABLE_NLS=false -DENABLE_LTO="$LTO" -DFORCE_COLOR_OUTPUT=true -DLTO_JOBS=2 \
|
||||
-DENABLE_SYSTEM_LUA="$SYS_LUA" .
|
||||
}
|
||||
build -DENABLE_GAME=true -DENABLE_SERVER=false -DENABLE_CAMPAIGN_SERVER=false -DENABLE_TESTS=true
|
||||
rm -R /usr/local/lib/cmake
|
||||
rm /usr/local/lib/libboost*
|
||||
rm -R /usr/local/include/boost
|
||||
build -DENABLE_GAME=true -DENABLE_SERVER=false -DENABLE_CAMPAIGN_SERVER=false -DENABLE_TESTS=true -DENABLE_STRICT_COMPILATION=false
|
||||
make conftests
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
apt remove -y -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev
|
||||
|
||||
build -DENABLE_GAME=false -DENABLE_SERVER=true -DENABLE_CAMPAIGN_SERVER=true -DENABLE_TESTS=false
|
||||
build -DENABLE_GAME=false -DENABLE_SERVER=true -DENABLE_CAMPAIGN_SERVER=true -DENABLE_TESTS=false -DENABLE_STRICT_COMPILATION=false
|
||||
make VERBOSE=1 -j2
|
||||
;;
|
||||
esac
|
||||
- name: Start Xvfb
|
||||
if: steps.build.outcome == 'success'
|
||||
run: start-stop-daemon --start --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1024x768x24
|
||||
- name: Check path options
|
||||
if: steps.build.outcome == 'success'
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: |
|
||||
./wesnoth --version
|
||||
for opt in config data userconfig userdata
|
||||
do
|
||||
output=$(./wesnoth --nobanner --"$opt"-path)
|
||||
if [ "$output" = "" ]; then printf 'option --%s-path prints nothing to stdout!' "$opt" >&2; exit 1; fi
|
||||
if [ "$output" = "" ]; then printf 'option --%s-path prints nothing to stdout!\n' "$opt" >&2; exit 1; fi
|
||||
printf '%s-path: %s\n' "$opt" "$output"
|
||||
done
|
||||
- name: WML validation
|
||||
if: steps.build.outcome == 'success'
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: ./utils/CI/schema_validation.sh
|
||||
- name: Run WML tests
|
||||
if: steps.build.outcome == 'success'
|
||||
run: ./run_wml_tests -g -c -t 20
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: ./run_wml_tests -g -c -t 20 -bt 1000
|
||||
- name: Run play tests
|
||||
if: steps.build.outcome == 'success'
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: ./utils/CI/play_test_executor.sh
|
||||
- name: Run MP tests
|
||||
if: steps.build.outcome == 'success'
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: ./utils/CI/mp_test_executor.sh
|
||||
- name: Run unit tests
|
||||
if: steps.build.outcome == 'success'
|
||||
if: success() || steps.build.outcome == 'success'
|
||||
run: ./run_boost_tests
|
||||
|
||||
steam-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- { uses: actions/checkout@v3, with: { submodules: "recursive" } }
|
||||
- { uses: actions/checkout@v4, with: { submodules: "recursive" } }
|
||||
|
||||
- name: Steam Runtime
|
||||
run: |
|
||||
|
@ -139,7 +153,7 @@ jobs:
|
|||
tar -cf "steambuild-$version.tar" steambuild
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Steam-Linux
|
||||
path: utils/dockerbuilds/steambuild-*.tar
|
||||
|
@ -148,7 +162,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- { uses: actions/checkout@v3, with: { submodules: "recursive" } }
|
||||
- { uses: actions/checkout@v4, with: { submodules: "recursive" } }
|
||||
|
||||
- name: MinGW Crosscompile
|
||||
run: |
|
||||
|
@ -161,13 +175,13 @@ jobs:
|
|||
mv mingwbuild/wesnoth*-win64.exe "wesnoth-$version-win64.exe"
|
||||
|
||||
- name: Upload Source
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Source
|
||||
path: wesnoth-*.tar.bz2
|
||||
|
||||
- name: Upload Windows-Installer
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows-Installer
|
||||
path: utils/dockerbuilds/wesnoth-*-win64.exe
|
||||
|
@ -179,20 +193,20 @@ jobs:
|
|||
options: --tty --cap-add=ALL --privileged # docker create options
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
- { uses: actions/checkout@v4, with: { submodules: "recursive", fetch-depth: 500, fetch-tags: true } }
|
||||
|
||||
- name: Flatpak
|
||||
run: |
|
||||
# write to an ignored filename so the build is not marked as modified
|
||||
jq '.modules |= map(select(.name == "wesnoth").sources[0]={"type":"dir","path":"."}) | ."build-options".env.FLATPAK_BUILDER_N_JOBS="2"' packaging/flatpak/org.wesnoth.Wesnoth.json > wesnoth-manifest.json
|
||||
flatpak-builder --force-clean --default-branch=ci --disable-rofiles-fuse wesnoth-app wesnoth-manifest.json
|
||||
flatpak build-export export wesnoth-app ci
|
||||
flatpak build-bundle export wesnoth.flatpak org.wesnoth.Wesnoth ci --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
jq '.modules |= map(select(.name == "wesnoth").sources[0]={type:"dir",path:"."})' packaging/flatpak/org.wesnoth.Wesnoth.json > wesnoth-manifest.json
|
||||
git config --global --add safe.directory "$PWD"
|
||||
branch=ci-$(git describe)
|
||||
flatpak-builder --force-clean --default-branch="$branch" --disable-rofiles-fuse --jobs=2 wesnoth-app wesnoth-manifest.json
|
||||
flatpak build-export export wesnoth-app "$branch"
|
||||
flatpak build-bundle export wesnoth.flatpak org.wesnoth.Wesnoth "$branch" --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
- name: Upload flatpak bundle
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Flatpak-Bundle
|
||||
path: wesnoth.flatpak
|
||||
|
@ -208,7 +222,7 @@ jobs:
|
|||
CLICOLOR_FORCE: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
|
@ -236,7 +250,7 @@ jobs:
|
|||
CFG: ${{ matrix.cfg }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
|
@ -248,29 +262,79 @@ jobs:
|
|||
run: scons translations build=release --debug=time nls=true jobs=2
|
||||
- name: Build wesnoth and unit tests
|
||||
working-directory: projectfiles/Xcode
|
||||
run: xcodebuild ARCHS=x86_64 -project "The Battle for Wesnoth.xcodeproj" -target "The Battle for Wesnoth" -target "unit_tests" -configuration "$CFG"
|
||||
# - name: Create disk image
|
||||
# working-directory: projectfiles/Xcode
|
||||
# run: hdiutil create -volname "Wesnoth_$CFG" -fs 'HFS+' -srcfolder "build/$CFG" -ov -format UDBZ "Wesnoth_${CFG}.dmg"
|
||||
run: |
|
||||
xcodebuild ARCHS=x86_64 -project "The Battle for Wesnoth.xcodeproj" -target "The Battle for Wesnoth" -target "unit_tests" -configuration "$CFG"
|
||||
# xcodebuild or maybe clang do not appear to fail properly when input files cannot be found; double check executables were produced
|
||||
[ -x "build/$CFG/The Battle for Wesnoth.app" ] || { printf 'Failed to build wesnoth executable!\n' >&2; exit 1; }
|
||||
[ -x "build/$CFG/unit_tests" ] || { printf 'Failed to build unit test executable!\n' >&2; exit 1; }
|
||||
- name: Create disk image
|
||||
working-directory: projectfiles/Xcode
|
||||
run: hdiutil create -volname "Wesnoth_$CFG" -fs 'HFS+' -srcfolder "build/$CFG" -ov -format UDBZ "Wesnoth_${CFG}.dmg"
|
||||
- name: Upload disk image
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS ${{ matrix.cfg }} disk image
|
||||
path: projectfiles/Xcode/Wesnoth_${{ matrix.cfg }}.dmg
|
||||
- name: Run WML tests
|
||||
if: matrix.cfg == 'Release'
|
||||
run: ./run_wml_tests -g -c -t 30 -p "projectfiles/Xcode/build/$CFG/The Battle for Wesnoth.app/Contents/MacOS/The Battle for Wesnoth"
|
||||
- name: Run unit tests
|
||||
run: ./run_boost_tests --path=projectfiles/Xcode/build/"$CFG" --executable=unit_tests
|
||||
|
||||
# run after all other jobs have completed to check overall build status
|
||||
discord-notification:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: Discord Notification
|
||||
|
||||
needs: [checks, ubuntu, steam-runtime, mingw, flatpak, translations, macos-intel]
|
||||
if: always()
|
||||
windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cfg: [Debug, Release]
|
||||
env:
|
||||
CFG: ${{ matrix.cfg }}
|
||||
VCPKG_FEATURE_FLAGS: dependencygraph
|
||||
permissions:
|
||||
contents: write # for dependency graph
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: technote-space/workflow-conclusion-action@v3
|
||||
- uses: rjstone/discord-webhook-notify@v1
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure' && github.event_name == 'push'
|
||||
- { uses: actions/checkout@v4, with: { submodules: "recursive" } }
|
||||
|
||||
- name: Make version of the runner image (https://github.com/actions/runner-images/releases) accessible to expression
|
||||
run: echo IMAGE_VERSION=%ImageVersion%>> %GITHUB_ENV%
|
||||
|
||||
- name: Cache object files
|
||||
id: windows-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vcpkg_installed # vcpkg generates this dir next to vcpkg.json
|
||||
# the final key part needs to be changed if anything in the build process changes that is not already included here
|
||||
key: win-cache-master-${{ matrix.cfg }}-${{ env.IMAGE_VERSION }}-${{ hashFiles('vcpkg.json') }}-0001
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Run cmake
|
||||
run: cmake -DCMAKE_BUILD_TYPE=%CFG% -DENABLE_GAME=true -DENABLE_SERVER=true -DENABLE_CAMPAIGN_SERVER=true
|
||||
-DENABLE_TESTS=true -DENABLE_MYSQL=false -DENABLE_NLS=false
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_INSTALL_OPTIONS=--debug
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_GENERATOR_PLATFORM=x64 -G "Visual Studio 16 2019" .
|
||||
|
||||
- name: Build wesnoth, wesnothd, campaignd and unit tests
|
||||
run: MSBuild.exe wesnoth.sln -p:Configuration=%CFG%
|
||||
|
||||
- name: Run WML unit tests
|
||||
if: matrix.cfg == 'Release'
|
||||
run: python run_wml_tests -v -g -c -t 20 -p %CFG%/wesnoth.exe
|
||||
|
||||
# run after all other jobs have completed to check overall build status
|
||||
notification:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [checks, copyright, ubuntu, steam-runtime, mingw, flatpak, translations, macos-intel, windows]
|
||||
if: failure() && github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: Discord Notification
|
||||
uses: rjstone/discord-webhook-notify@v1
|
||||
with:
|
||||
severity: error
|
||||
webhookUrl: ${{ secrets.DISCORD_CI_WEBHOOK }}
|
||||
|
@ -278,3 +342,17 @@ jobs:
|
|||
pusher: ${{ github.actor }}
|
||||
commit: ${{ github.event.head_commit.message }}
|
||||
commit url: ${{ github.event.head_commit.url }}
|
||||
- name: Prepare message
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
MSG: ${{ github.event.head_commit.message }}
|
||||
run: |
|
||||
printf COMMIT_SUBJECT=%s "${MSG}" | head -n 1 >> "$GITHUB_ENV"
|
||||
- name: IRC Notification
|
||||
uses: rectalogic/notify-irc@v1
|
||||
with:
|
||||
channel: ${{ vars.IRC_CHANNEL }}
|
||||
server: ${{ vars.IRC_SERVER }}
|
||||
nickname: ${{ vars.IRC_NICK }} # is also used for sasl username
|
||||
sasl_password: ${{ secrets.IRC_SASL_PASSWORD }}
|
||||
message: "❌ ${{ github.workflow }} workflow run ${{ github.run_number }} failed on \x0306${{ github.ref_name }}\x0F: ${{ env.COMMIT_SUBJECT }} by \x0315${{ github.actor }}\x0F: \x0302${{ github.event.head_commit.url }}\x0F"
|
||||
|
|
65
.github/workflows/codeql.yml
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners
|
||||
# Consider using larger runners for possible analysis time improvements.
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp', 'python', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- { name: Checkout repository, uses: actions/checkout@v4, with: { submodules: "recursive" } }
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Install dependencies
|
||||
if: matrix.language == 'cpp'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install scons libboost-system1.74-dev libboost-filesystem1.74-dev libboost-iostreams1.74-dev \
|
||||
libboost-serialization1.74-dev libboost-locale1.74-dev libboost-regex1.74-dev libboost-random1.74-dev \
|
||||
libboost-program-options1.74-dev libboost-thread1.74-dev libboost-context1.74-dev libboost-test-dev \
|
||||
libboost-coroutine1.74-dev libboost-graph1.74-dev libasio-dev libsdl2-dev libsdl2-image-dev \
|
||||
libsdl2-mixer-dev libvorbis-dev libpango1.0-dev libssl-dev libcurl4-openssl-dev liblua5.4-dev
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
45
.github/workflows/irc-notify.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: IRC Notification
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
types: [ opened, closed, reopened ]
|
||||
issues:
|
||||
types: [ opened, closed, reopened ]
|
||||
# create: # creation also gets mentioned when pushing
|
||||
delete:
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
if: vars.IRC_CHANNEL != '' # skip if not set up (like in a fork for example)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare message
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
MSG: ${{ github.event.head_commit.message }}
|
||||
run: |
|
||||
printf COMMIT_SUBJECT=%s "${MSG}" | head -n 1 >> "$GITHUB_ENV"
|
||||
- uses: rectalogic/notify-irc@v1
|
||||
env: { COLOR: "\x03", BLUE: "02", PURPLE: "06", ACTOR: "\x0315${{ github.actor }}\x03" }
|
||||
with:
|
||||
channel: ${{ vars.IRC_CHANNEL }}
|
||||
server: ${{ vars.IRC_SERVER }}
|
||||
nickname: ${{ vars.IRC_NICK }} # is also used for sasl username
|
||||
sasl_password: ${{ secrets.IRC_SASL_PASSWORD }}
|
||||
# See https://docs.github.com/en/actions/learn-github-actions/expressions
|
||||
# github context: https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
|
||||
# github.event data: https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads
|
||||
message: >-
|
||||
${{ (github.event_name == 'push' &&
|
||||
format('{1} {2} {0}{3}{4}{0}: {5}: {0}{6}{7}{0}', env.COLOR, env.ACTOR,
|
||||
(github.event.created && (github.event.tag && 'tagged' || 'created')) || (github.event.deleted && 'deleted') || (github.event.forced && 'force-pushed to') || 'pushed to',
|
||||
env.PURPLE, github.ref_name, env.COMMIT_SUBJECT, env.BLUE, github.event.compare)
|
||||
) || (github.event_name == 'pull_request_target' &&
|
||||
format('{1} {2} PR #{3} for {0}{4}{5}{0}: {6}: {0}{7}{8}{0}', env.COLOR, env.ACTOR, github.event.action, github.event.number, env.PURPLE, github.base_ref, github.event.pull_request.title, env.BLUE, github.event.pull_request.html_url)
|
||||
) || (github.event_name == 'issues' &&
|
||||
format('{1} {2} issue {0}{3}#{4}{0}: {5}: {0}{6}{7}{0}', env.COLOR, env.ACTOR, github.event.action, env.PURPLE, github.event.issue.number, github.event.issue.title, env.BLUE, github.event.issue.html_url)
|
||||
) || ((github.event_name == 'create' || github.event_name == 'delete') &&
|
||||
format('{1} {2}d {3} {0}{4}{5}{0}', env.COLOR, env.ACTOR, github.event_name, github.event.ref_type, env.PURPLE, github.event.ref)
|
||||
)
|
||||
}}
|
||||
_debug: ${{ toJSON(github) }}
|
6
.github/workflows/labeler.yml
vendored
|
@ -4,9 +4,9 @@ on:
|
|||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@main
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: "false"
|
||||
|
|
48
.github/workflows/map-diff.yml
vendored
|
@ -7,14 +7,16 @@ on:
|
|||
|
||||
jobs:
|
||||
comment-map-diff:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: utils/wesnoth-map-diff
|
||||
steps:
|
||||
- uses: actions/checkout@v3.0.0
|
||||
- uses: actions/setup-node@v3.0.0
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Package install
|
||||
|
@ -26,40 +28,41 @@ jobs:
|
|||
env:
|
||||
SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
sudo apt-get install -y pngquant
|
||||
sudo apt-get -y -q install pngquant
|
||||
comment_body=""
|
||||
log() { printf '\e[1m%s\e[m\n' "$*"; } # write log message in bold
|
||||
|
||||
## Get maps changed
|
||||
## Get changed maps
|
||||
git fetch --depth=1 origin "$SHA"
|
||||
mapfile -t map_paths < <(git diff --name-only HEAD "$SHA" | grep '\.map$')
|
||||
|
||||
for map_path in "${map_paths[@]}"
|
||||
do
|
||||
echo "** Working on $map_path **"
|
||||
|
||||
## Get new map version
|
||||
log "Check out $map_path from $SHA..."
|
||||
new_map=${map_path##*/}
|
||||
git show "$SHA":"$map_path" > "$new_map"
|
||||
|
||||
## Run map diff
|
||||
diff_image=${new_map%.map}.png
|
||||
node build/index.js "../../$map_path" "$new_map" "$diff_image"
|
||||
log "Generate map diff image for $map_path..."
|
||||
node build/index.js "../../$map_path" "$new_map" diff_image.png
|
||||
identify diff_image.png
|
||||
|
||||
## Compress image
|
||||
pngquant --force --output "$diff_image" "$diff_image"
|
||||
log 'Compress image...'
|
||||
pngquant --force --output diff_image.png diff_image.png
|
||||
identify diff_image.png
|
||||
|
||||
## Write comment body
|
||||
curl_result=$(curl -F "image=@\"$diff_image\"" "https://api.imgur.com/3/upload")
|
||||
log 'Uploading diff_image.png...'
|
||||
json=$(curl -s -F image=@diff_image.png https://api.imgur.com/3/upload | tee /dev/stderr) || continue
|
||||
|
||||
echo "Imgur result:"
|
||||
echo "$curl_result"
|
||||
|
||||
image_link=$(jq -r ".data.link" <<< "$curl_result")
|
||||
|
||||
comment_body="$comment_body<h3>$map_path</h3><img src=\"$image_link\" /><br />"
|
||||
log 'Generating HTML comment...'
|
||||
html=$(jq -r --arg path "$map_path" 'if .data.link != null
|
||||
then @html "<h3>\($path)</h3><img src=\"\(.data.link)\" /> <br />"
|
||||
elif has("errors") then .errors[].status | halt_error
|
||||
else "Unexpected JSON structure!\n" | halt_error end' <<< "$json") || continue
|
||||
comment_body+=$html
|
||||
done
|
||||
|
||||
echo "::set-output name=COMMENT_BODY::$comment_body"
|
||||
printf 'COMMENT_BODY=%s\n' "$comment_body" >> "$GITHUB_OUTPUT"
|
||||
- name: Find comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: fc
|
||||
|
@ -67,10 +70,9 @@ jobs:
|
|||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
- name: Add comment
|
||||
uses: peter-evans/create-or-update-comment@v1.4.5
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body: |
|
||||
${{ steps.get-maps-diff.outputs.COMMENT_BODY }}
|
||||
body: ${{ steps.get-maps-diff.outputs.COMMENT_BODY }}
|
||||
|
|
3
.gitignore
vendored
|
@ -219,3 +219,6 @@ config.h
|
|||
callgrind.out.*
|
||||
data/dist
|
||||
clean.sh
|
||||
|
||||
# clangd cache
|
||||
.cache/clangd
|
||||
|
|
|
@ -29,12 +29,6 @@ endfunction()
|
|||
# Options
|
||||
#
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/src/modules/lua/.git")
|
||||
message(FATAL_ERROR "Lua submodule does not exist. You must run 'git submodule update --init --recursive' to initialize it.")
|
||||
else()
|
||||
message("-- Lua submodule found.")
|
||||
endif()
|
||||
|
||||
# Adhere to GNU filesystem layout conventions
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
@ -58,7 +52,27 @@ option(ENABLE_MYSQL "Enable building MP/add-ons servers with mysql support" OFF)
|
|||
option(ENABLE_TESTS "Build unit tests")
|
||||
option(ENABLE_NLS "Enable building of translations" ${ENABLE_GAME})
|
||||
|
||||
set(BOOST_VERSION "1.66")
|
||||
set(BOOST_VERSION "1.67")
|
||||
|
||||
if(NOT WIN32)
|
||||
set(Lua_FIND_VERSION_MAJOR 5)
|
||||
set(Lua_FIND_VERSION_MINOR 4)
|
||||
option(ENABLE_SYSTEM_LUA "Enable use of system Lua ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR} (compiled as C++)" OFF)
|
||||
endif()
|
||||
if(ENABLE_SYSTEM_LUA)
|
||||
set(Lua_FIND_VERSION_EXACT ON)
|
||||
set(Lua_FIND_VERSION_COUNT 2)
|
||||
include(FindLua)
|
||||
if(NOT LUA_FOUND)
|
||||
message(FATAL_ERROR "Lua ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR} C++ not found. Try setting 'LUA_DIR'.")
|
||||
endif()
|
||||
else()
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/src/modules/lua/.git")
|
||||
message(FATAL_ERROR "Lua submodule does not exist. You must run 'git submodule update --init --recursive' to initialize it.")
|
||||
else()
|
||||
message("-- Lua submodule found.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# set what std version to use
|
||||
if(NOT CXX_STD)
|
||||
|
@ -79,15 +93,16 @@ if(APPLE)
|
|||
find_library(SECURITY_LIBRARY Security REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS iostreams program_options regex system thread random coroutine locale filesystem)
|
||||
find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS iostreams program_options regex system thread random coroutine locale filesystem graph)
|
||||
find_package(ICU REQUIRED COMPONENTS data i18n uc)
|
||||
|
||||
# no, gettext executables are not required when NLS is deactivated
|
||||
find_package(Gettext)
|
||||
find_package(Python)
|
||||
|
||||
find_package(X11)
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32 AND NOT ENABLE_SYSTEM_LUA)
|
||||
# Use the safer `mkstemp' instead of `tmpnam' on POSIX systems.
|
||||
add_definitions(-DLUA_USE_POSIX)
|
||||
endif()
|
||||
|
@ -190,6 +205,18 @@ if(NOT MSVC)
|
|||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Qunused-arguments -Wno-unknown-warning-option -Wmismatched-tags -Wno-conditional-uninitialized -Wno-unused-lambda-capture")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14)
|
||||
# GCC-13 added this new warning, and included it in -Wextra,
|
||||
# however in GCC-13 it has a lot of false positives.
|
||||
#
|
||||
# It's likely to generate false postives with GCC-14 too, but
|
||||
# I'm using a narrow version check as GCC-14 is still in dev.
|
||||
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110075
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-dangling-reference")
|
||||
endif()
|
||||
|
||||
### Set strict compiler flags.
|
||||
|
||||
if(ENABLE_STRICT_COMPILATION)
|
||||
|
@ -447,7 +474,7 @@ if(NOT MSVC)
|
|||
else()
|
||||
set(CMAKE_CXX_FLAGS "/W3 /WX /wd4503 /wd4351 /wd4250 /wd4244 /wd4267 /we4239 /wd4275 /EHsc /utf-8 /Zc:__cplusplus" CACHE STRING "Global flags used by the CXX compiler during all builds." FORCE)
|
||||
set(CMAKE_C_FLAGS "/WX" CACHE STRING "Global flags used by the C compiler during all builds." FORCE)
|
||||
add_definitions(-D_WIN32_WINNT=_WIN32_WINNT_WIN7 -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -DNOMINMAX)
|
||||
add_definitions(-D_WIN32_WINNT=0x0601 -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -DNOMINMAX)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG_LUA")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
|
||||
|
@ -557,7 +584,7 @@ endif()
|
|||
|
||||
add_subdirectory(doc)
|
||||
|
||||
if(GETTEXT_FOUND AND ENABLE_NLS)
|
||||
if(GETTEXT_FOUND AND Python_FOUND AND ENABLE_NLS)
|
||||
add_subdirectory(po)
|
||||
endif()
|
||||
|
||||
|
|
2
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
The Battle for Wesnoth's Code of Conduct may be found in the following forum post:
|
||||
https://forums.wesnoth.org/viewtopic.php?t=24277
|
|
@ -18,7 +18,7 @@ Wesnoth's engine conforms to the C++17 standard. We encourage the use of standar
|
|||
|
||||
### Code Formatting
|
||||
|
||||
All C++, WML and Lua files are in UTF-8, as we use Gettext-style translations, and translatable strings use some punctuation that's outside of the ASCII subset. More details are in the [Typography Style Guide](https://wiki.wesnoth.org/GettextForWesnothDevelopers).
|
||||
All C++, WML and Lua files are in UTF-8, as we use Gettext-style translations, and translatable strings use some punctuation that's outside of the ASCII subset. More details are in the [Typography Style Guide](https://wiki.wesnoth.org/Typography_Style_Guide) and the guide to [using Gettext strings](https://wiki.wesnoth.org/GettextForWesnothDevelopers).
|
||||
|
||||
If your pull request touches the engine's C++ source code, we recommend (but don't require) you run `clang-format` on your changes before submission (Visual Studio Code gives you a handy context menu option to do so). This ensures that your code remains formatted according to our conventions. Make a local commit before running `clang-format`, in case more code than expected gets changed.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Prerequisites
|
||||
|
||||
Wesnoth requires a compiler with sufficient C++17 support such as GCC 7 and
|
||||
Wesnoth requires a compiler with sufficient C++17 support such as GCC 8 and
|
||||
later, or a version of Clang with equivalent support.
|
||||
|
||||
You'll need to have these libraries and their development headers installed in
|
||||
|
@ -18,6 +18,7 @@ order to build Wesnoth:
|
|||
* Program Options
|
||||
* System
|
||||
* Coroutine
|
||||
* Graph
|
||||
* SDL2 libraries:
|
||||
* SDL2 >= 2.0.10 (macOS: 2.0.22 due to needing https://github.com/libsdl-org/SDL/commit/3bebdaccb7bff8c40438856081d404a7ce3def30)
|
||||
* SDL2_image >= 2.0.2 (with PNG, JPEG, and WEBP support)
|
||||
|
@ -75,9 +76,7 @@ See [here](https://github.com/wesnoth/wesnoth/blob/master/projectfiles/Xcode/REA
|
|||
### Windows
|
||||
Wesnoth uses CMake for project configuration and vcpkg for installing dependencies. See [here](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio) for information on using Visual Studio with cmake. The first time it's run, vcpkg will build all the required dependencies which may take over an hour, however it will only need to be done once.
|
||||
|
||||
NOTE 1: You will need a Windows implementation of pkg-config present in your PATH, such as [pkg-config-lite](https://sourceforge.net/projects/pkgconfiglite/).
|
||||
|
||||
NOTE 2: You will need to run `vcpkg integrate install` on the command line to make Visual Studio aware of vcpkg. If Visual Studio is open when this is executed, then you will need to close and re-open Visual Studio.
|
||||
NOTE 1: You will need to run `vcpkg integrate install` on the command line to make Visual Studio aware of vcpkg. If Visual Studio is open when this is executed, then you will need to close and re-open Visual Studio.
|
||||
|
||||
## SCons Build
|
||||
|
||||
|
|
47
SConstruct
|
@ -9,6 +9,8 @@
|
|||
|
||||
EnsureSConsVersion(0,98,3)
|
||||
|
||||
lua_ver = "5.4"
|
||||
|
||||
import os, sys, shutil, re, subprocess
|
||||
from glob import glob
|
||||
from subprocess import Popen, PIPE, call, check_output
|
||||
|
@ -101,6 +103,7 @@ opts.AddVariables(
|
|||
('boost_suffix', 'Suffix of boost libraries.'),
|
||||
PathVariable('gettextdir', 'Root directory of Gettext\'s installation.', "", OptionalPath),
|
||||
PathVariable('gtkdir', 'Directory where GTK SDK is installed.', "", OptionalPath),
|
||||
BoolVariable('system_lua', 'Enable use of system Lua ' + lua_ver + ' (compiled as C++, only for non-Windows systems).', False),
|
||||
PathVariable('luadir', 'Directory where Lua binary package is unpacked.', "", OptionalPath),
|
||||
('host', 'Cross-compile host.', ''),
|
||||
PathVariable('ndkdir', 'Root directory of android NDK to use', "", OptionalPath),
|
||||
|
@ -189,7 +192,7 @@ if env['distcc']:
|
|||
|
||||
if env['ccache']: env.Tool('ccache')
|
||||
|
||||
boost_version = "1.66"
|
||||
boost_version = "1.67"
|
||||
|
||||
def SortHelpText(a, b):
|
||||
return (a > b) - (a < b)
|
||||
|
@ -205,9 +208,7 @@ Important switches include:
|
|||
in build/release and copy resulting binaries
|
||||
into distribution/working copy root.
|
||||
build=debug same for debug build variant
|
||||
binaries will be copied with -debug suffix
|
||||
build=profile build with instrumentation for a supported profiler
|
||||
binaries will be copied with -profile suffix
|
||||
|
||||
With no arguments, the recipe builds wesnoth and wesnothd. Available
|
||||
build targets include the individual binaries:
|
||||
|
@ -317,7 +318,7 @@ def Warning(message):
|
|||
|
||||
from metasconf import init_metasconf
|
||||
configure_args = dict(
|
||||
custom_tests = init_metasconf(env, ["cplusplus", "sdl", "boost", "cairo", "pango", "pkgconfig", "gettext_tool"]),
|
||||
custom_tests = init_metasconf(env, ["cplusplus", "sdl", "boost", "cairo", "pango", "pkgconfig", "gettext_tool", "lua"]),
|
||||
config_h = "$build_dir/config.h",
|
||||
log_file="$build_dir/config.log", conf_dir="$build_dir/sconf_temp")
|
||||
|
||||
|
@ -335,6 +336,15 @@ if "gcc" in env["TOOLS"]:
|
|||
env.AppendUnique(CCFLAGS = Split("-Wall -Wextra"))
|
||||
env.AppendUnique(CXXFLAGS = Split("-Werror=non-virtual-dtor -std=c++" + env["cxx_std"]))
|
||||
|
||||
# GCC-13 added this new warning, and included it in -Wextra,
|
||||
# however in GCC-13 it has a lot of false positives.
|
||||
#
|
||||
# It's likely to generate false postives with GCC-14 too, but
|
||||
# I'm using a narrow version check as GCC-14 is still in dev.
|
||||
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110075
|
||||
if "CXXVERSION" in env and env["CXXVERSION"].startswith("13."):
|
||||
env.AppendUnique(CXXFLAGS = "-Wno-dangling-reference")
|
||||
|
||||
if env["prereqs"]:
|
||||
conf = env.Configure(**configure_args)
|
||||
|
||||
|
@ -361,7 +371,7 @@ if env["prereqs"]:
|
|||
env["PKG_CONFIG_FLAGS"] = "--dont-define-prefix"
|
||||
|
||||
have_server_prereqs = (\
|
||||
conf.CheckCPlusPlus(gcc_version = "7") & \
|
||||
conf.CheckCPlusPlus(gcc_version = "8") & \
|
||||
conf.CheckBoost("iostreams", require_version = boost_version) & \
|
||||
conf.CheckBoostIostreamsGZip() & \
|
||||
conf.CheckBoostIostreamsBZip2() & \
|
||||
|
@ -399,12 +409,19 @@ if env["prereqs"]:
|
|||
have_client_prereqs = have_client_prereqs & conf.CheckPKG("fontconfig")
|
||||
have_client_prereqs = have_client_prereqs & conf.CheckBoost("regex")
|
||||
have_client_prereqs = have_client_prereqs & conf.CheckLib("curl")
|
||||
have_client_prereqs = have_client_prereqs & conf.CheckBoost("graph")
|
||||
|
||||
if not File("#/src/modules/lua/.git").rfile().exists():
|
||||
have_client_prereqs = False
|
||||
Warning("Lua submodule does not exist. You must run 'git submodule update --init --recursive' to initialize it.")
|
||||
if env["system_lua"]:
|
||||
if env["PLATFORM"] == 'win32':
|
||||
Warning("System Lua cannot be used on Windows.")
|
||||
if not conf.CheckLua(lua_ver):
|
||||
have_client_prereqs = False
|
||||
else:
|
||||
print("Lua submodule found.")
|
||||
if not File("#/src/modules/lua/.git").rfile().exists():
|
||||
have_client_prereqs = False
|
||||
Warning("Lua submodule does not exist. You must run 'git submodule update --init --recursive' to initialize it.")
|
||||
else:
|
||||
print("Lua submodule found.")
|
||||
|
||||
if not have_client_prereqs:
|
||||
Warning("Client prerequisites are not met. wesnoth cannot be built.")
|
||||
|
@ -483,6 +500,8 @@ for env in [test_env, client_env, env]:
|
|||
if os.path.isabs(env["build_dir"]):
|
||||
build_root = ""
|
||||
env.Prepend(CPPPATH = [build_root + "$build_dir", "#/src"])
|
||||
if env["system_lua"]:
|
||||
env.Append(CPPDEFINES = ["HAVE_SYSTEM_LUA"])
|
||||
|
||||
env.Append(CPPDEFINES = ["HAVE_CONFIG_H"])
|
||||
|
||||
|
@ -688,9 +707,6 @@ if env["use_srcdir"] == True:
|
|||
else:
|
||||
build_dir = os.path.join("$build_dir", build)
|
||||
|
||||
if build == "release" : build_suffix = ""
|
||||
else : build_suffix = "-" + build
|
||||
Export("build_suffix")
|
||||
env.SConscript("src/SConscript", variant_dir = build_dir, duplicate = False)
|
||||
Import(binaries + ["sources"])
|
||||
binary_nodes = [eval(binary) for binary in binaries]
|
||||
|
@ -719,7 +735,7 @@ env.Clean(all, 'TAGS')
|
|||
# Unix installation productions
|
||||
#
|
||||
# These will not be portable to Windows or Mac. They assume a Unix-like
|
||||
# directory structure and FreeDesktop standard locations foicon, app,
|
||||
# directory structure and FreeDesktop standard locations for icon, app,
|
||||
# and doc files.
|
||||
#
|
||||
|
||||
|
@ -786,7 +802,8 @@ if not access(fifodir, F_OK):
|
|||
env.Alias("install-wesnothd", fifodir)
|
||||
if env["systemd"]:
|
||||
env.InstallData("prefix", "wesnothd", "#packaging/systemd/wesnothd.service", "lib/systemd/system")
|
||||
env.InstallData("prefix", "wesnothd", "#packaging/systemd/wesnothd.conf", "lib/tmpfiles.d")
|
||||
env.InstallData("prefix", "wesnothd", "#packaging/systemd/wesnothd.tmpfiles.conf", "lib/tmpfiles.d")
|
||||
env.InstallData("prefix", "wesnothd", "#packaging/systemd/wesnothd.sysusers.conf", "lib/sysusers.d")
|
||||
|
||||
# Wesnoth campaign server
|
||||
env.InstallBinary(campaignd)
|
||||
|
@ -795,7 +812,7 @@ env.InstallBinary(campaignd)
|
|||
install = env.Alias('install', [])
|
||||
for installable in ('wesnoth',
|
||||
'wesnothd', 'campaignd'):
|
||||
if os.path.exists(installable + build_suffix) or installable in COMMAND_LINE_TARGETS or "all" in COMMAND_LINE_TARGETS:
|
||||
if os.path.exists(installable) or installable in COMMAND_LINE_TARGETS or "all" in COMMAND_LINE_TARGETS:
|
||||
env.Alias('install', env.Alias('install-'+installable))
|
||||
|
||||
#
|
||||
|
|
0
about.wiki
Normal file
185
add_source_file
|
@ -1,10 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
# A script for adding source files to the Battle for Wesnoth project.
|
||||
|
||||
# known issues:
|
||||
# xcode - if a file already exists in 'wesnoth' target, then it incorrectly thinks it also exists in the 'tests' target even though the tests build will fail
|
||||
|
||||
"""
|
||||
Add files to the specified build targets, supporting
|
||||
CMake, SCons, Xcode and the Code::Blocks projects.
|
||||
|
||||
Valid build targets are:
|
||||
* "wesnoth" - the main game (default if no target is specified)
|
||||
* "wesnothd" - the wesnoth server
|
||||
* "campaignd"
|
||||
* "lua"
|
||||
* "tests" - boost unit tests
|
||||
|
||||
The files will be added to:
|
||||
* the lists used by CMake and SCons in "source_lists"
|
||||
* the Xcode project
|
||||
* The Code::Blocks project
|
||||
|
||||
This only supports files inside the "src" directory.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import inspect
|
||||
import pathlib
|
||||
|
@ -15,30 +34,10 @@ except:
|
|||
print('\n'.join((
|
||||
'This script requires the "pbxproj" module.',
|
||||
'Install it using "pip install pbxproj"',
|
||||
'optionally setting up a python3-venv first.',
|
||||
)))
|
||||
exit(1)
|
||||
|
||||
USAGE = f"""USAGE: {sys.argv[0]} <file> [target ...]
|
||||
|
||||
Adds <file> to the specified build targets.
|
||||
|
||||
Valid build targets are:
|
||||
* "wesnoth" - the main game
|
||||
* "wesnothd" - the wesnoth server
|
||||
* "campaignd"
|
||||
* "lua"
|
||||
* "tests" - boost unit tests
|
||||
|
||||
If no build target is specified, "wesnoth" is assumed.
|
||||
|
||||
The file will be added to:
|
||||
* the lists used by CMake and SCons in "source_lists"
|
||||
* the Xcode project
|
||||
* The Code::Blocks project
|
||||
|
||||
This only supports files inside the "src" directory.
|
||||
"""
|
||||
|
||||
#=========#
|
||||
# Globals #
|
||||
#=========#
|
||||
|
@ -91,18 +90,18 @@ def add_to_xcode(filename, targets):
|
|||
"The Battle for Wesnoth.xcodeproj",
|
||||
"project.pbxproj",
|
||||
)
|
||||
|
||||
|
||||
project = pbxproj.XcodeProject.load(projectfile)
|
||||
|
||||
|
||||
translated_targets = [item for t in targets for item in xcode_target_translations[t]]
|
||||
translated_targets = list(set(translated_targets))
|
||||
print(" xcode targets:", translated_targets)
|
||||
|
||||
|
||||
for tname in translated_targets:
|
||||
if not project.get_target_by_name(tname):
|
||||
raise Exception(
|
||||
f"Could not find target '{tname}' in Xcode project file")
|
||||
|
||||
|
||||
# groups are organized by directory structure under "src"
|
||||
src_groups = project.get_groups_by_name("src")
|
||||
if len(src_groups) != 1:
|
||||
|
@ -115,7 +114,7 @@ def add_to_xcode(filename, targets):
|
|||
groupname = parent_group.get_name()
|
||||
raise Exception(f"problem finding '{d}' group in '{groupname}'")
|
||||
parent_group = found_groups[0]
|
||||
|
||||
|
||||
# if the group already has an entry with the same filename, loudly skip.
|
||||
# note: this doesn't allow adding to targets one at a time.
|
||||
# a new file should be added to all targets at once...
|
||||
|
@ -124,7 +123,7 @@ def add_to_xcode(filename, targets):
|
|||
if project.get_files_by_name(filename.name, parent=parent_group):
|
||||
print(" '"+filename.name+"' already found in Xcode project '"+",".join(translated_targets)+"', skipping")
|
||||
return
|
||||
|
||||
|
||||
# force is True here because otherwise a duplicate filename in
|
||||
# a different place will block addition of the new file.
|
||||
# the rest is just to match existing project file structure.
|
||||
|
@ -134,7 +133,7 @@ def add_to_xcode(filename, targets):
|
|||
parent=parent_group,
|
||||
target_name=translated_targets,
|
||||
)
|
||||
|
||||
|
||||
# that's done, save the file
|
||||
project.save()
|
||||
return
|
||||
|
@ -147,7 +146,7 @@ def add_to_source_list(filename, source_list):
|
|||
source_list_file = rootdir.joinpath("source_lists", source_list)
|
||||
sl_lines = open(source_list_file).readlines()
|
||||
file_line = filename.as_posix() + '\n'
|
||||
|
||||
|
||||
# we only need source files in the source_lists, not header files
|
||||
if filename.suffix != ".cpp":
|
||||
return
|
||||
|
@ -156,12 +155,12 @@ def add_to_source_list(filename, source_list):
|
|||
if file_line in sl_lines:
|
||||
print(f" '{filename}' already found in '{source_list}', skipping")
|
||||
return
|
||||
|
||||
|
||||
sl_lines.append(file_line)
|
||||
sl_lines.sort()
|
||||
open(source_list_file, 'w').writelines(sl_lines)
|
||||
|
||||
def add_to_source_lists(filename, target):
|
||||
def add_to_source_lists(filename, targets):
|
||||
translated_targets = [source_list_target_translations[t] for t in targets]
|
||||
print(" source_list targets:", translated_targets)
|
||||
for t in translated_targets:
|
||||
|
@ -178,18 +177,18 @@ def add_to_code_blocks_target(filename, target):
|
|||
f"{target}.cbp",
|
||||
)
|
||||
cbp_lines = open(cbp_file).readlines()
|
||||
|
||||
|
||||
filename_for_cbp = pathlib.PurePath(
|
||||
"..", "..", "src", filename
|
||||
).as_posix()
|
||||
|
||||
|
||||
elem = f"\t\t<Unit filename=\"{filename_for_cbp}\" />\n"
|
||||
|
||||
|
||||
# if the target already has an entry with the same filename, loudly skip
|
||||
if elem in cbp_lines:
|
||||
print(f" '{filename}' already found in '{target}.cbp', skipping")
|
||||
return
|
||||
|
||||
|
||||
# find an appropriate line to add before/after
|
||||
index = 0
|
||||
for line in cbp_lines:
|
||||
|
@ -201,7 +200,7 @@ def add_to_code_blocks_target(filename, target):
|
|||
break
|
||||
index += 1
|
||||
cbp_lines.insert(index, elem)
|
||||
|
||||
|
||||
open(cbp_file, 'w').writelines(cbp_lines)
|
||||
|
||||
def add_to_code_blocks(filename, targets):
|
||||
|
@ -209,38 +208,94 @@ def add_to_code_blocks(filename, targets):
|
|||
print(" code::blocks targets:", translated_targets)
|
||||
for t in translated_targets:
|
||||
add_to_code_blocks_target(filename, t)
|
||||
# if there's also an .hpp file, add that too
|
||||
hpp = filename.with_suffix(".hpp")
|
||||
if rootdir.joinpath("src", hpp).exists():
|
||||
add_to_code_blocks_target(hpp, t)
|
||||
|
||||
def sanity_check_existing_cpp_hpp(filenames):
|
||||
"""
|
||||
If we're adding a .cpp file, check whether a .hpp should be added too, etc.
|
||||
Only the files named on the command line are added, this exits if the check fails.
|
||||
"""
|
||||
any_check_failed = False
|
||||
for filename in filenames:
|
||||
if filenames.count(filename) > 1:
|
||||
print(f"ERROR: File '{filename}' given multiple times")
|
||||
any_check_failed = True
|
||||
|
||||
if not rootdir.joinpath("src", filename).exists():
|
||||
print(f"WARN: File '{filename}' does not exist")
|
||||
any_check_failed = True
|
||||
|
||||
spouse = None
|
||||
if filename.suffix == ".cpp":
|
||||
spouse = filename.with_suffix(".hpp")
|
||||
elif filename.suffix == ".hpp":
|
||||
spouse = filename.with_suffix(".cpp")
|
||||
|
||||
if rootdir.joinpath("src", spouse).exists() and not filenames.count(spouse):
|
||||
print(f"WARN: Requested to add '{filename}', should '{spouse}' be added too?")
|
||||
any_check_failed = True
|
||||
|
||||
if any_check_failed:
|
||||
break
|
||||
|
||||
if any_check_failed:
|
||||
print("ERROR: Not making changes, as checks failed and --no-checks option was not used.")
|
||||
exit(1)
|
||||
|
||||
def canonicalise_filenames(original_filenames):
|
||||
"""
|
||||
The script supports giving the filenames with or without the "src/" prefix.
|
||||
|
||||
Strip the "src/" if present, functions that need it will add it again later.
|
||||
"""
|
||||
filenames = []
|
||||
|
||||
# If src/src/ exists, the filenames become ambiguous. No need to support that.
|
||||
if rootdir.joinpath("src", "src").exists():
|
||||
print("Please don't add a file or directory called src/src.")
|
||||
exit(1)
|
||||
|
||||
for filename in options.filename:
|
||||
filename = pathlib.PurePath(filename)
|
||||
parts = filename.parts
|
||||
if parts[0] == "src":
|
||||
filename = pathlib.PurePath(*parts[1:])
|
||||
else:
|
||||
filename = pathlib.PurePath(*parts)
|
||||
filenames.append(filename)
|
||||
|
||||
return filenames
|
||||
|
||||
#======#
|
||||
# main #
|
||||
#======#
|
||||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
# a file argument is mandatory
|
||||
if len(sys.argv) < 2:
|
||||
print(USAGE)
|
||||
exit(1)
|
||||
|
||||
filename = pathlib.PurePath(sys.argv[1])
|
||||
targets = sys.argv[2:]
|
||||
|
||||
# the default target is "wesnoth"
|
||||
if not targets: targets = ["wesnoth"]
|
||||
|
||||
# this only works on files in "src/".
|
||||
# if it started with "src/", remove it.
|
||||
parts = filename.parts
|
||||
if parts[0] == "src":
|
||||
filename = pathlib.PurePath(*parts[1:])
|
||||
|
||||
# note: this does not currently check whether or not the file exists,
|
||||
# and does not currently work with file paths relative to elsewhere.
|
||||
# it could be made to do that.
|
||||
|
||||
print(f"adding '{filename}' to targets: {targets}")
|
||||
add_to_xcode(filename, targets)
|
||||
add_to_source_lists(filename, targets)
|
||||
add_to_code_blocks(filename, targets)
|
||||
ap.add_argument("filename", action="store", nargs="+",
|
||||
help="the .cpp and .hpp files to add")
|
||||
ap.add_argument("--target", action="store", nargs=1,
|
||||
default=["wesnoth"],
|
||||
help="which build targets to add the file to")
|
||||
ap.add_argument("--no-checks", action="store_true",
|
||||
help="do not check whether the files exist, etc")
|
||||
# By default, recognise --help too
|
||||
options = ap.parse_args()
|
||||
|
||||
# Bail out if someone uses the old syntax of "add_source_file src/foo.cpp campaignd"
|
||||
if not options.no_checks:
|
||||
if len(options.filename) == 2 and not options.filename[1].count('.'):
|
||||
print("The usage has changed, targets now need to be given using --target name")
|
||||
exit(1)
|
||||
|
||||
# Convert the names to pathlib.PurePath objects without leading "src/"
|
||||
filenames = canonicalise_filenames(options.filename)
|
||||
|
||||
if not options.no_checks:
|
||||
sanity_check_existing_cpp_hpp(filenames)
|
||||
|
||||
for filename in filenames:
|
||||
print(f"adding '{filename}' to targets: {options.target}")
|
||||
add_to_xcode(filename, options.target)
|
||||
add_to_source_lists(filename, options.target)
|
||||
add_to_code_blocks(filename, options.target)
|
||||
|
|
|
@ -6,6 +6,7 @@ test_gui2/modal_dialog_test_addon_connect
|
|||
test_gui2/modal_dialog_test_addon_license_prompt
|
||||
test_gui2/modal_dialog_test_campaign_difficulty
|
||||
test_gui2/modal_dialog_test_chat_log
|
||||
test_gui2/modal_dialog_test_editor_choose_addon
|
||||
test_gui2/modal_dialog_test_core_selection
|
||||
test_gui2/modal_dialog_test_custom_tod
|
||||
test_gui2/modal_dialog_test_depcheck_confirm_change
|
||||
|
@ -13,6 +14,8 @@ test_gui2/modal_dialog_test_depcheck_select_new
|
|||
test_gui2/modal_dialog_test_edit_label
|
||||
test_gui2/modal_dialog_test_edit_text
|
||||
test_gui2/modal_dialog_test_editor_edit_label
|
||||
test_gui2/modal_dialog_test_editor_edit_pbl
|
||||
test_gui2/modal_dialog_test_editor_edit_pbl_translation
|
||||
test_gui2/modal_dialog_test_editor_edit_side
|
||||
test_gui2/modal_dialog_test_editor_edit_scenario
|
||||
test_gui2/modal_dialog_test_editor_generate_map
|
||||
|
@ -43,12 +46,14 @@ test_gui2/modal_dialog_test_mp_connect
|
|||
test_gui2/modal_dialog_test_mp_join_game_password_prompt
|
||||
test_gui2/modal_dialog_test_mp_login
|
||||
test_gui2/modal_dialog_test_mp_method_selection
|
||||
test_gui2/modal_dialog_test_prompt
|
||||
test_gui2/modal_dialog_test_simple_item_selector
|
||||
test_gui2/modal_dialog_test_screenshot_notification
|
||||
test_gui2/modal_dialog_test_select_orb_colors
|
||||
test_gui2/modal_dialog_test_statistics_dialog
|
||||
test_gui2/modal_dialog_test_surrender_quit
|
||||
test_gui2/modal_dialog_test_theme_list
|
||||
test_gui2/modal_dialog_test_tod_new_schedule
|
||||
test_gui2/modal_dialog_test_transient_message
|
||||
test_gui2/modal_dialog_test_unit_create
|
||||
test_gui2/modal_dialog_test_wml_error
|
||||
|
@ -67,6 +72,7 @@ cmdline_opts/test_empty_options
|
|||
cmdline_opts/test_default_options
|
||||
cmdline_opts/test_full_options
|
||||
cmdline_opts/test_positional_options
|
||||
cmdline_opts/test_log_domain_severity_override_order
|
||||
test_config/test_config_attribute_value
|
||||
test_config/test_variable_info
|
||||
test_config/add_child_EmptyThis_newKey_AppendAndReturnNewEmptyChild
|
||||
|
@ -79,6 +85,9 @@ config_cache/test_macrosubstitution
|
|||
config_cache/test_transaction
|
||||
config_cache/test_define_loading
|
||||
config_cache/test_lead_spaces_loading
|
||||
config_filters/test_int_add_sub_filter
|
||||
config_filters/test_int_positive_filter
|
||||
config_filters/test_int_signed_filter
|
||||
filesystem/test_fs_game_path_reverse_engineering
|
||||
filesystem/test_fs_base
|
||||
filesystem/test_fs_enum
|
||||
|
@ -226,6 +235,10 @@ rng/test_mt_rng_reproducibility5
|
|||
rng/test_mt_rng_reproducibility_coverage
|
||||
rng/validate_get_random_int
|
||||
rng/validate_get_random_int2
|
||||
sdl/test_scale_sharp_nullptr
|
||||
sdl/test_scale_sharp_zero
|
||||
sdl/test_scale_sharp_round
|
||||
sdl/test_scale_sharp_fractional
|
||||
test_serialization_utils_and_unicode/utils_join_test
|
||||
test_serialization_utils_and_unicode/utils_split_test
|
||||
test_serialization_utils_and_unicode/utils_quoted_split_test
|
||||
|
@ -248,3 +261,14 @@ whiteboard_side_actions_container/test_insertion
|
|||
whiteboard_side_actions_container/test_removal
|
||||
feature_test_WML_macro_define/macro_define_noArgument_ParseAsExpected
|
||||
feature_test_WML_macro_define/macro_define_1Argument_ParseAsExpected
|
||||
test_schema_self_validator/test_schema_super_self_reference
|
||||
test_schema_self_validator/test_schema_super_cycle
|
||||
test_schema_validator/test_super_cycle
|
||||
test_schema_validator/test_super_cycle_only_if_used
|
||||
test_schema_validator/test_super_cycle_crashes_on_unknown_key
|
||||
test_schema_validator/test_super_missing
|
||||
test_schema_validator/test_super_missing_only_if_used
|
||||
test_schema_validator/test_super_mandatory
|
||||
test_schema_validator/test_super_mandatory_missing
|
||||
test_schema_validator/test_super_cycle_mandatory
|
||||
test_schema_validator/test_schema_link_cycle
|
||||
|
|
590
changelog.md
|
@ -1,4 +1,4 @@
|
|||
## Version 1.17.17+dev
|
||||
## Version 1.19.0-dev
|
||||
### Add-ons client
|
||||
### Add-ons server
|
||||
### Campaigns
|
||||
|
@ -7,15 +7,493 @@
|
|||
### Lua API
|
||||
### Packaging
|
||||
### Terrain
|
||||
* Added ruined walls terrain variations: Xur (damaged Xu); Xor (damaged Xos); and Exos (ruined Xos, is Flat/Cave alias)
|
||||
### Translations
|
||||
* Updated translations: British English, Finnish, French, Spanish
|
||||
* Updated translations: Bengali, British English, Chinese (Traditional), Czech, Dutch, Finnish, French, German, Italian, Japanese, Polish, Spanish
|
||||
* Added new font "Lohit-Bengali.ttf" to support Bengali translation
|
||||
### Units
|
||||
### User interface
|
||||
### WML Engine
|
||||
### Miscellaneous and Bug Fixes
|
||||
|
||||
## Version 1.17.26
|
||||
### Campaigns
|
||||
* Eastern Invasion
|
||||
* Many minor bugfixes
|
||||
* Heir to the Throne
|
||||
* Fix S17 to ensure the starting castle always has 6 hexes for recruiting (PR #8314)
|
||||
* Clarify S11's objectives by editing the map (no string changes) (PR #8326)
|
||||
* Add decorations and map embellishments to S16, S19a and S22 (PR #8243)
|
||||
* Use swamp terrain instead of sand for S19b (PR #8243)
|
||||
* Under the Burning Suns
|
||||
* Improve continuity between events that can be triggered in various orders.
|
||||
* Adjust Fighter unit line for easier early leveling and more late game staying power.
|
||||
* World Conquest
|
||||
* Fix the Bezoar artifact (issue #6513)
|
||||
### Multiplayer
|
||||
* Assume that all players need a copy of an add-on, by defaulting `require_modification`/`require_campaign` to `yes` (PR #8135)
|
||||
### Lua API
|
||||
* Added documentation for linting and IDE support
|
||||
### Packaging
|
||||
* Add CMake and SCons options to use an already installed copy of Lua 5.4 (PR #8234)
|
||||
* The system copy of Lua must be compiled as C++ rather than C, as Wesnoth uses C++ exceptions.
|
||||
* Windows requires a compile-time change to Lua, so must use the in-tree Lua submodule.
|
||||
### Translations
|
||||
* New translation: Bengali
|
||||
* Updated translations: Arabic, British English, Czech, Finnish, French
|
||||
### Units
|
||||
* War Harbinger: +3 HP (29 -> 32), XP to 100, cost +2g (41 -> 43), removed forest and village dodge modifiers
|
||||
* Dark Omen: removed forest and village dodge modifiers
|
||||
* Raven: removed forest and village dodge modifiers
|
||||
### User interface
|
||||
* New multiline textbox and numerical spinner widgets (PR #8199)
|
||||
### WML Engine
|
||||
* Added a composite hero/leader ellipse, in case an author forgets to `UNMAKE_HERO` when converting to a leader (PR #8375)
|
||||
* `[event]name=unit_placed` is now non-undoable by default (issue #7780)
|
||||
* `[scroll]` now includes a delay, this was needed for visual effects such as the `QUAKE` macro
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix some logs not being included in the logfile (issue #8108)
|
||||
* Multiple fixes to handling of Lua errors, some of which could cause a crash (PR #8234)
|
||||
* When WML specifies a unit or unit type’s gender, automatically include that in the .po files as a translation hint
|
||||
* Make sure `transform_unit` doesn't heal the unit when removing objects (PR #8147)
|
||||
* Fixed bugs in the rushers recruit AI (issue #8297)
|
||||
* Savefiles now include the ID of the core in use
|
||||
|
||||
## Version 1.17.25
|
||||
### Campaigns
|
||||
* Eastern Invasion
|
||||
* Fix S04b’s time limit, which extends by 10 turns if a bonus objective is completed.
|
||||
* Fix macros in translatable strings, because they prevent translation. (issue #8225)
|
||||
* Winds of Fate
|
||||
* Made the epilogue more vague about events on the Green Isle
|
||||
* Removed part of epilogue suggesting a Drake-led war around 120YW
|
||||
### Translations
|
||||
* Updated translations: Arabic, British English, Czech, Finnish, French, German, Italian, Slovak
|
||||
### Units
|
||||
* New fish and bug zombie variations
|
||||
### User interface
|
||||
* Added hotkeys for various menu items and ThemeWML buttons in Map Editor
|
||||
* Added shortcut Ctrl+G for 'I'm Ready' button in mp_staging dialog (Addresses issue #8250)
|
||||
* Fix the left pane of the help browser’s layout for right-to-left languages (Arabic and Hebrew) (issue #8205)
|
||||
### Miscellaneous and Bug Fixes
|
||||
* wmllint now warns about `[kill]` tags containing `[filter]` tags
|
||||
* Fix a regression from 1.13.11 that inverted horizontal scrolling with SDL versions 2.0.18+ on X11 and versions 2.0.20+ on Wayland (issues #3362 and #7404, PR #8137)
|
||||
* Run wesnothd server as `_wesnoth:_wesnoth` instead of `nobody:users`, improving safety and fixing a warning message in systemd 246+
|
||||
|
||||
## Version 1.17.24
|
||||
### Campaigns
|
||||
* Winds of Fate
|
||||
* Fix macro argument in translatable string
|
||||
* Eastern Invasion
|
||||
* Change S04b’s `scenario_id` to `en_US` spelling (issue #7994)
|
||||
* S04b start-of-scenario saves since 1.17.21 will be broken
|
||||
* S03 mid-scenario saves since 1.17.21 will show a warning, and players will only be able to go west
|
||||
* Saves from earlier versions are already broken by the campaign rewrite
|
||||
* Fix macro argument in translatable string
|
||||
### Editor
|
||||
* Time Schedule Editor now saves custom time schedules to `utils/schedule.cfg` under the Add-on directory. (Cache needs to be manually reloaded with `F5` after exiting Map Editor.)
|
||||
* Changing the color sliders and pressing the preview button shows a preview of the tint on the map.
|
||||
* New dialog for entering custom schedule ID and name. The user is required to enter a non-empty schedule id and name. ID will be autogenerated if the user doesn't specify it or already exists.
|
||||
* Textbox to enter Add-on ID after selecting `New Scenario > New Add-on`.
|
||||
### Multiplayer
|
||||
* Fix Add-on version check error #4055
|
||||
### Lua API
|
||||
* units.remove_modifications can now remove multiple types of modifications.
|
||||
### Translations
|
||||
* Updated translations: British English, Bulgarian, Finnish, French, German, Polish, Slovak
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix various assertion failures on invalid input
|
||||
* Fix end credits not showing when a scenario has no [side]s #8090
|
||||
* The command line `--preprocess` utility now accepts any filename, not just those ending .cfg.
|
||||
|
||||
## Version 1.17.23
|
||||
### Campaigns
|
||||
* Under the Burning Suns
|
||||
* All sand terrian now correctly applies the dehydrated effect
|
||||
### Translations
|
||||
* Updated translations: British English, Czech, French, Italian, Polish, Slovak, Welsh
|
||||
### Units
|
||||
* Drakes:
|
||||
* Saurian Seer - modify the unit to fix reductions in power level between level 2 and level 3.
|
||||
### User interface
|
||||
* If wesnoth is installed with most campaigns missing, an error message is shown in the campaign selection dialog
|
||||
* The inspect dialog again is able to correctly paginate large amounts of text
|
||||
* The credits now actually show up instead of crashing the game
|
||||
* Significantly improve game history query performance, preventing the query from timing out
|
||||
* The help browser, when looking at a unit type which uses the same image for male and female units, now only shows one image instead of showing a duplicate.
|
||||
### WML Engine
|
||||
* [filter_ability], [filter_ability_active], and [filter_specials] are renamed to [experimental_filter_ability], [experimental_filter_ability_active], and [experimental_filter_specials]
|
||||
* Add a [damage_type]'replacement_type' and 'alternative_type' attribute weapon special to change the type of attack under specific conditions (terrain, time of day, leadership etc...)
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Added basic support for the `ttkthemes` Python library to GUI.pyw on Linux
|
||||
* Fixed the screen not redrawing on Windows after unlocking the computer
|
||||
* Fixed occasional bad rendering for blurred/translucent areas
|
||||
* Significantly reduced CPU usage when a dialog is open
|
||||
* Avoid an assert in `replay::add_start_if_not_there_yet` with corrupt files (issue #7154)
|
||||
* Fix Leaders sometimes being removed in controller=null sides in mp campaigns (#7886)
|
||||
* fix [store_reachable_locations] when the player has shroud #7891
|
||||
|
||||
|
||||
## Version 1.17.22
|
||||
### Campaigns
|
||||
* Under the Burning Suns
|
||||
* More AMLAs for Kaleh and Nym.
|
||||
* Balance changes.
|
||||
### Multiplayer server
|
||||
* Record and replay chat messages when joining a game that has not started yet
|
||||
### Lua API
|
||||
* New experimental Lua undo API
|
||||
* New Lua mouse click callback
|
||||
### Terrain
|
||||
* New embellishment for adding windows to impassible walls.
|
||||
### Translations
|
||||
* Updated translations: Welsh
|
||||
* Updated translations: British English, Chinese (Simplified), Czech, French, Slovak
|
||||
### Units
|
||||
* Update graphics and stats of Roc
|
||||
* New monster unit line - Ravens
|
||||
* New Jumping Spider monster.
|
||||
* Drakes:
|
||||
* Saurian Oracle – xp changed from 66 to 74
|
||||
* Saurian Soothsayer – xp changed from 100 to 66
|
||||
* Saurian Prophet is added as an upgrade of the Saurian Oracle
|
||||
* Saurian Seer is added as an upgrade of the Saurian Soothsayer
|
||||
### User interface
|
||||
* Village ownership is shown with flag and side number in terrain info (issue #4334)
|
||||
### WML API
|
||||
* New ability attributes `halo_image=` and `halo_image_self=`, to illustrate the effects of the illumination ability, HttT's initiative, etc.
|
||||
* New ability attributes `overlay_image=` and `overlay_image_self=`.
|
||||
* New ability tag and attribute `[overwrite]priority=`.
|
||||
* Two new tags, `[filter_ability]` and `[filter_ability_active]`, which have a temporary exception from the API freeze.
|
||||
### WML Engine
|
||||
* The implementation of `[allow_undo]` has changed, the behavior should be the same.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Asset copyright tracking is now handled in a .csv file, and sanity checked during CI builds.
|
||||
* Various fixes to World Conquest multiplayer campaign.
|
||||
|
||||
## Version 1.17.21
|
||||
### Campaigns
|
||||
* Eastern Invasion
|
||||
* Revise entire campaign (Dalas version)
|
||||
* Descent into Darkness
|
||||
* Buff soul rend
|
||||
* Liberty
|
||||
* Make Harper a girl and revise some dialogue
|
||||
* Legend of Wesmere
|
||||
* Replace lingering instances of ‘old’ style gold carry over for S9, S11 and S14 (issue #7862)
|
||||
### Translations
|
||||
* Updated translations: Arabic, British English, Czech, German, French
|
||||
### Units
|
||||
* Updates to Troll sprites and animations (#7844)
|
||||
* Giant Ant Queen, Fire Ant Queen, Ant Eggs, and Ant Zombies
|
||||
* Rebels:
|
||||
* Elvish Enchanterss – xp changed from 180 to 198.
|
||||
* Elvish Sylph – cost changed from 148 to 161.
|
||||
* Loyalists:
|
||||
* Young Ogre – cost changed from 15 to 13, xp changed from 32 to 34, mountain defense changed from 60% to 50%, arcane resistance changed from 20% to 10%.
|
||||
* Ogre – cost changed from 30 to 27, mountain defense changed from 60% to 50%, arcane resistance changed from 20% to 10%.
|
||||
* Miscellaneous:
|
||||
* Elder Falcon - cost changed from 18 to 20.
|
||||
* Giant Ant – xp changed from 26 to 16.
|
||||
* Soldier ant – cost changed from 16 to 12, hp changed from 35 to 36.
|
||||
* Fire Ant - xp changed from 50 to 22.
|
||||
* Firebomb Ant - hp changed from 38 to 45, cost changed from 14 to 19.
|
||||
* Yeti - cost changed from 80 to 151.
|
||||
* Piglet – xp changed from 26 to 20.
|
||||
* Woodland Boar – cost changed from 15 to 16, hp changed from 35 to 34.
|
||||
* Fire guardian – xp changed from 29 to 24, hp changed from 23 to 25.
|
||||
* Fire Wraith – gold cost changed from 22 to 17.
|
||||
* Bay Horse and Dark Horse – xp changed from 44 to 24.
|
||||
* Mermaid Initiate – cost changed from 19 to 16, xp changed from 50 to 42.
|
||||
* Mermaind Priestess – cost changed from 38 to 31, xp changed from 132 to 61.
|
||||
* Mermaid Diviner – cost changed from 57 to 49.
|
||||
* Mermaid Enchanterss – cost changed from 33 to 32, xp changed from 90 to 46.
|
||||
* Mermaid Siren – cost changed from 52 to 42.
|
||||
* Merman Brawler - cost changed from 19 to 17.
|
||||
* Naga Guardian – xp changed from 35 to 36.
|
||||
* Naga Warden – xp changed from 70 to 54.
|
||||
* Troll Hero – cost changed from 30 to 42, xp changed from 52 to 90.
|
||||
* Great Troll – cost changed from 48 to 78.
|
||||
* Troll Shaman – cost changed from 32 to 35.
|
||||
* Dwarvish Scout – xp changed from 30 to 33.
|
||||
* Dwarvish Pathfinder – cost changed from 30 to 24, xp changed from 60 to 65.
|
||||
* Dwarvish Explorer –cost changed from 45 to 51.
|
||||
* Dwarvish Runesmith – cost changed from 30 to 27, xp changed from 86 to 71.
|
||||
* Dwarvish Runemaster – cost changed from 50 to 55.
|
||||
* Dwarvish Arcanister – cost changed form 80 to 98.
|
||||
* Dwarvish Miner – cost changed from 19 to 5.
|
||||
* Orcish Leader – cost changed from 20 to 21, xp changed from 60 to 54.
|
||||
* Orcish Ruler – cost changed from 35 to 45, xp changed from 120 to 102.
|
||||
* Orcish Sovereign – cost changed from 50 to 80.
|
||||
* Skeleton Rider – xp changed from 44 to 33.
|
||||
* Death Squire – xp changed from 144 to 91.
|
||||
* Death Knight –cost changed from 45 to 80.
|
||||
* Royal Warrior - cost changed from 40 to 47.
|
||||
* Elvish Lord – cost changed from 36 to 42, xp changed from 120 to 97.
|
||||
* Elvish Highlord - cost changed from 56 to 77.
|
||||
* Nibbler – cost changed from 10 to 15, xp changed from 32 to 26.
|
||||
* Caribe – cost changed from 16 to 25, xp changed from 40 to 43.
|
||||
* Hunter Caribe – cost changed from 25 to 40.
|
||||
* Dragonfly Naiad – cost changed from 8 to 11, xp changed from 23 to 16.
|
||||
* Dragonfly – cost changed from 13 to 16, xp changed from 23 to 25.
|
||||
* Grand Dragonfly – hp changed from 34 to 39, cost changed from 22 to 30.
|
||||
* Ruffian - cost changed from 6 to 7.
|
||||
* Thug - xp changed from 42 to 32.
|
||||
* Bandit - cost changed from 23 to 22, xp changed from 70 to 61.
|
||||
* Peasant and Woodsman - xp changed from 23 to 19.
|
||||
* Elder Mage - cost changed from 65 to 90.
|
||||
|
||||
## Version 1.17.20
|
||||
### Campaigns
|
||||
* A Tale of Two Brothers
|
||||
* Remove the magic communication amulets from the story.
|
||||
* Under the Burning Suns
|
||||
* S3: Ensure all of Garak’s attacks are changed to fire type (#7774).
|
||||
### Editor
|
||||
* Added the ability to create a basic add-on - the _server.pbl, folder structure, and saving scenario files in a format which works with add-ons.
|
||||
### Multiplayer
|
||||
* New game mechanics for 4p King of the Hill:
|
||||
* A side with a leader holding the hill keep gets bonus gold each turn.
|
||||
* A treasure chest which only a leader can unlock further rewards the first player to take the hill.
|
||||
* Each side gets two leaders. If one dies then another can be "recalled" (recruited) from the recall list at its full unit cost.
|
||||
* Computer players will try to take the hill with one of their two leaders.
|
||||
* Revised map.
|
||||
### Translations
|
||||
* Updated translations: British English, Czech, Italian, Japanese
|
||||
### Units
|
||||
* Rebels:
|
||||
* Elvish Outrider - movement changed from 10 to 11
|
||||
* Drakes:
|
||||
* Saurian Soothsayer - movement changed from 6 to 7
|
||||
* Movement type changes:
|
||||
* Smallfoot, armoredfoot, elusivefoot, mounted, fly, smallfly, lightfly, deepsea, mountainfoot, gurefoot, rodentfoot, lizard, dunefoot, duneelusivefoot, dunearmoredfoot, dunehorse, dunearmoredhorse - arcane resistance changed from 20% to 10%.
|
||||
* Treefolk - arcane resistance changed from -30% to -20%.
|
||||
* Undeadfoot - arcane resistance changed from -50% to -20%.
|
||||
* Undeadfly - arcane resistance changed from -40% to -20%.
|
||||
* Drakefly, drakeglide, drakeglide2, drakefoot - arcane resistance changed from -30% to -10%.
|
||||
* Loyalists:
|
||||
* Paladin - arcane resistance changed from 60% to 30%.
|
||||
* White mage - arcane resistance changed from 40% to 30%.
|
||||
* Mage of Light - arcane resistance changed from 60% to 50%.
|
||||
* Undead:
|
||||
* Lich - arcane resistance changed from -40% to -20%.
|
||||
### WML Engine
|
||||
* Add support for filters to match negative values
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix a crash in the `:inspect` window when pagination is used (issue #7851).
|
||||
|
||||
## Version 1.17.19
|
||||
### Campaigns
|
||||
* Descent into Darkness
|
||||
* S3: Resolved Darken Volk speaking from the darkness at the end of the scenario (#7758).
|
||||
* Liberty
|
||||
* S4: Resolved nameless enemies gaining names at day-break (#7748).
|
||||
* S5: Make the loss of Hans a lose-condition (#7750).
|
||||
### Translations
|
||||
* Updated translations: British English, Czech, Finnish, French, Portuguese (Brazil)
|
||||
### Units
|
||||
* Rebels:
|
||||
* Elvish Fighter - xp changed from 40 to 37, and new updated sprite and animations
|
||||
* Merman Hunter - xp changed from 35 to 33.
|
||||
* Elvish Scout - xp changed from 32 to 42.
|
||||
* Elvish Hero - xp changed from 90 to 80.
|
||||
* Elvish Captain - xp changed from 90 to 85.
|
||||
* Elvish Marksman - cost changed from 31 to 34.
|
||||
* Elvish Ranger - xp changed from 90 to 85.
|
||||
* Elvish Rider - ranged damage changed from 9 to 11, hp changed from 46 to 49, cost changed from 28 to 35, xp changed from 53 to 75.
|
||||
* Elvish Druid - cost changed from 27 to 25, xp changed from 80 to 85.
|
||||
* Elvish Sorceress - cost changed from 32 to 34, xp changed from 100 to 90.
|
||||
* Elder Wose - cost changed from 27 to 28, xp changed from 100 to 80.
|
||||
* Merman Netcaster - cost changed from 27 to 26, xp changed from 85 to 54.
|
||||
* Merman Spearman - cost changed from 27 to 22, xp changed from 85 to 54.
|
||||
* Champion - melee damage changed from 9 to 8. added 10 melee accuracy, hp changed from 70 to 72, cost changed from 48 to 61.
|
||||
* Elvish Marshal - hp changed from 62 to 68, cost changed from 54 to 67.
|
||||
* Elvish Sharpshooter - ranged damage changed from 10 to 12. cost changed from 51 to 62.
|
||||
* Elvish Avenger - ranged damage changed from 10 to 11, hp changed from 55 to 59, cost changed from 53 to 66.
|
||||
* Elvish Outrider - ranged damage changed from 8 to 11, melee damage changed from 7 to 8, hp changed from 57 to 60, cost changed from 43 to 58.
|
||||
* Elvish Shyde - hp changed from 46 to 51, melee damage changed from 6 to 7, ranged slow (entangle) damage changed from 6 to 8, ranged magical (thorns) damage changed from 8 to 14, cost changed from 52 to 69.
|
||||
* Elvish Enchantress - cost changed from 55 to 70, ranged slow damage changed from 5 to 7, ranged magical damage changed from 9 to 13.
|
||||
* Ancient Wose - cost changed from 48 to 50.
|
||||
* Merman Entangler - cost changed from 46 to 42.
|
||||
* Merman Javelineer - cost changed from 48 to 55.
|
||||
* Elvish Sylph - hp changed from 60 to 68, ranged slow damage changed from 6 to 7, ranged magical damage changed from 10 to 16, cost changed from 67 to 148.
|
||||
* Undead:
|
||||
* Skeleton - xp changed from 35 to 39.
|
||||
* Ghoul - xp changed from 35 to 30.
|
||||
* Blood Bat - cost changed from 22 to 23, xp changed from 70 to 37.
|
||||
* Revenant - cost changed from 31 to 28, xp changed from 85 to 78.
|
||||
* Dark Sorcerer - cost changed from 33 to 34, xp changed from 90 to 110.
|
||||
* Shadow - cost changed from 38 to 44, xp changed from 100 to 77.
|
||||
* Wraith - xp changed from 100 to 90.
|
||||
* Necrophage - melee damage changed from 7 to 9, cost changed from 27 to 23, xp changed from 120 to 61.
|
||||
* Bone Shooter - cost changed from 26 to 24, xp changed from 80 to 60.
|
||||
* Dread Bat - cost changed from 34 to 32.
|
||||
* Draug - cost changed from 47 to 70.
|
||||
* Lich - cost changed from 50 to 90.
|
||||
* Necromancer - ranged cold damage changed from 17 to 19, ranged arcane damage changed from 12 to 16, hp changed from 70 to 76, impact resistance changed from 0% to 10%, cost changed from 50 to 90.
|
||||
* Nightgaunt - cost changed from 52 to 71.
|
||||
* Specter - cost changed from 52 to 78.
|
||||
* Ghast - melee damage changed from 10 to 12, cost changed from 43 to 50.
|
||||
* Banebow - cost changed from 41 to 52.
|
||||
* Ancient lich cost changed from 100 to 214.
|
||||
* Drakes:
|
||||
* Drake Fighter - xp changed from 42 to 41.
|
||||
* Drake Clasher - xp changed from 43 to 41.
|
||||
* Drake Glider - xp changed from 35 to 39.
|
||||
* Drake Warrior - xp changed from 70 to 81, cost changed from 32 to 31.
|
||||
* Drake Arbiter - hp changed from 62 to 63, xp changed from 105 to 81.
|
||||
* Drake Thrasher - xp changed from 95 to 77.
|
||||
* Drake Flare - xp changed from 80 to 86.
|
||||
* Fire Drake - xp changed from 80 to 84.
|
||||
* Sky Drake - xp changed from 80 to 76, cost changed from 28 to 32.
|
||||
* Saurian Ambusher - hp changed from 36 to 38, cost changed from 24 to 22.
|
||||
* Saurian Soothsayer - melee attack strikes changed from 2 to 3, cost changed from 25 to 28.
|
||||
* Saurian Spearthrower - melee damage changed from 6 to 5.
|
||||
* Drake Blademaster - cost changed from 47 to 62.
|
||||
* Drake Warden - cost changed from 46 to 62.
|
||||
* Drake Enforcer - cost changed from 44 to 58.
|
||||
* Drake Flameheart - cost changed from 51 to 66.
|
||||
* Inferno Drake - cost changed from 51 to 64.
|
||||
* Hurricane Drake - cost changed from 43 to 59.
|
||||
* Saurian Flanker - hp changed from 47 to 52, cost changed from 46 to 42.
|
||||
* Saurian Javelineer - ranged attack strikes changed from 5 to 4, cost changed from 52 to 42.
|
||||
* Armageddon Drake - cost changed from 85 to 118.
|
||||
* Dunefolk:
|
||||
* Dune Burner - hp changed from 34 to 35, xp changed from 40 to 37.
|
||||
* Dune Herbalist - cost changed from 14 to 13, xp changed from 39 to 36.
|
||||
* Dune Rider - hp changed from 34 to 33, xp changed from 47 to 49.
|
||||
* Dune Rover - hp changed from 32 to 33, xp changed from 43 to 40.
|
||||
* Dune Soldier - cost changed from 18 to 17, xp changed from 43 to 39.
|
||||
* Dune Skirmisher - xp changed from 34 to 39.
|
||||
* Dune Explorer - xp changed from 80 to 73.
|
||||
* Dune Swordsman - cost changed from 30 to 28, xp changed from 75 to 68.
|
||||
* Dune Captain - cost changed from 30 to 29, xp changed from 75 to 74.
|
||||
* Dune Spearguard - cost changed from 30 to 29, xp changed from 75 to 74.
|
||||
* Dune Scorcher - ranged damage changed from 9 to 10, cost changed from 25 to 23, xp changed from 70 to 60.
|
||||
* Dune Raider - cost changed from 34 to 35, xp changed from 70 to 77.
|
||||
* Dune Swiftrider - xp changed from 65 to 62.
|
||||
* Dune Sunderer - xp changed from 76 to 85.
|
||||
* Dune Apothecary - cost changed from 27 to 23, xp changed from 65 to 50.
|
||||
* Dune Falconer - hp changed from 38 to 39, cost changed from 23 to 29, xp changed from 78 to 70.
|
||||
* Dune Strider - hp changed from 39 to 42, cost changed from 23 to 29, xp changed from 74 to 64.
|
||||
* Naga Ophidian - hp changed from 43 to 44, cost changed from 22 to 24, xp changed from 76 to 62.
|
||||
* Naga Ringcaster - hp changed from 40 to 41, cost changed from 22 to 24, ranged damage changed from 7 to 8, xp changed from 70 to 62.
|
||||
* Dune Blademaster - hp changed from 69 to 76.
|
||||
* Dune Spearmaster - hp changed from 67 to 68.
|
||||
* Dune Cataphract - mace melee attack damage changed from 14 to 15, lance damage changed from 13 to 14, cost changed from 62 to 69.
|
||||
* Dune Harrier - cost changed from 47 to 50.
|
||||
* Dune Sky Hunter - cost changed from 50 to 54.
|
||||
* Knalgan Alliance:
|
||||
* Dwarvish Fighter - xp changed from 41 to 45.
|
||||
* Dwarvish Thunderer - xp changed from 40 to 35.
|
||||
* Dwarvish Guardsman - ranged damage increased from 5 to 6, xp changed from 47 to 40.
|
||||
* Footpad - xp changed from 36 to 35.
|
||||
* Thief - xp changed from 28 to 29.
|
||||
* Poacher - xp changed from 29 to 28.
|
||||
* Gryphon Rider - cost changed from 24 to 23, xp changed from 38 to 46.
|
||||
* Dwarvish Steelclad - xp changed from 74 to 88.
|
||||
* Dwarvish Thunderguard - cost changed from 27 to 24, xp changed from 95 to 65.
|
||||
* Dwarvish Stalwart - hp changed from 54 to 59, ranged damage changed from 8 to 9, xp changed from 85 to 78.
|
||||
* Outlaw - cost changed from 26 to 24, hp changed from 42 to 47, xp changed from 77 to 71.
|
||||
* Rogue - xp changed from 70 to 80, cost changed from 24 to 25.
|
||||
* Trapper - hp changed from 45 to 49, melee damage changed from 4 to 5, xp changed from 65 to 73.
|
||||
* Gryphon Master - cost changed from 38 to 40.
|
||||
* Dwarvish Lord - cost changed from 50 to 69.
|
||||
* Dwarvish Sentinel - cost changed from 44 to 63.
|
||||
* Dwarvish Dragonguard - cost changed from 46 to 61.
|
||||
* Fugitive - hp changed from 62 to 68, cost changed from 53 to 55.
|
||||
* Huntsman - cost changed from 50 to 43.
|
||||
* Ranger - cost changed from 52 to 43.
|
||||
* Assassin - cost changed from 44 to 46.
|
||||
* Northerners:
|
||||
* Orcish Archer - xp changed from 30 to 32.
|
||||
* Naga Fighter - xp changed from 32 to 33.
|
||||
* Wolf Rider - xp changed from 30 to 34.
|
||||
* Orcish Warrior - cost changed from 26 to 23, xp changed from 60 to 67.
|
||||
* Goblin Pillager - cost changed from 28 to 31.
|
||||
* Troll - cost changed from 27 to 25, xp changed from 66 to 58.
|
||||
* Troll Rocklobber - ranged damage changed from 17 to 19, hp changed from 51 to 53.
|
||||
* Naga Warrior - cost changed from 24 to 22, xp changed from 66 to 56.
|
||||
* Orcish Crossbowman - melee damage changed from 4 to 6, ranged pierce damage changed from 8 to 9, hp changed from 43 to 46, cost changed from 21 to 22, xp changed from 80 to 43.
|
||||
* Orcish Slayer - cost changed from 26 to 21, xp changed from 64 to 62.
|
||||
* Orcish Warlord - cost changed from 48 to 52.
|
||||
* Direwolf Rider - cost changed from 44 to 52.
|
||||
* Troll Warrior - cost changed from 44 to 49.
|
||||
* Naga Myrmidon - cost changed from 48 to 47.
|
||||
* Orcish Nightblade - cost changed from 43 to 53.
|
||||
* Orcish Slurbow - cost changed from 43 to 37.
|
||||
* Loyalists:
|
||||
* Cavalryman - blade resistance changed from 30% to 20%, xp changed from 40 to 44.
|
||||
* Bowman - xp changed from 39 to 35, melee damage changed from 4 to 5.
|
||||
* Horseman - cost changed from 23 to 24, xp changed from 44 to 54.
|
||||
* Merman Fighter - xp changed from 36 to 34.
|
||||
* Mage - xp changed from 54 to 48.
|
||||
* Javelineer - ranged damage increased from 11 to 13, cost changed from 25 to 24.
|
||||
* Pikeman - cost changed from 25 to 24, xp changed from 65 to 69.
|
||||
* Swordsman - xp changed from 60 to 80.
|
||||
* Knight - cost changed from 38 to 50, xp changed from 120 to 100.
|
||||
* Lancer - cost changed from 44 to 47.
|
||||
* Shock Trooper - cost changed from 35 to 28, xp changed from 110 to 65.
|
||||
* Red Mage - cost changed from 38 to 35, xp changed from 100 to 83.
|
||||
* White Mage - cost changed from 38 to 31, xp changed from 136 to 75.
|
||||
* Dragoon - cost changed from 28 to 29, xp changed from 95 to 80.
|
||||
* Longbowman - cost changed from 26 to 23, xp changed from 68 to 65.
|
||||
* Duelist - cost changed from 29 to 31, xp changed from 90 to 80.
|
||||
* Merman Warrior - cost changed from 26 to 24, xp changed from 80 to 60.
|
||||
* Lieutenant - cost changed from 34 to 31, xp changed from 80 to 85.
|
||||
* Halberdier - cost changed from 44 to 52.
|
||||
* Royal Guard - cost changed from 44 to 61.
|
||||
* Grand Knight - hp changed from 78 to 84, lance damage changed from 17 to 19, cost changed from 58 to 86.
|
||||
* Paladin - hp changed from 65 to 77, lance damage changed from 15 to 16, melee damage changed from 8 to 9, cost changed from 58 to 82.
|
||||
* Iron Mauler - cost changed from 50 to 49.
|
||||
* Arch Mage - cost changed from 59 to 63, xp changed from 220 to 150.
|
||||
* Mage of Light - cost changed from 60 to 59.
|
||||
* Cavalier - cost changed from 52 to 62.
|
||||
* Master Bowman - cost changed from 46 to 50.
|
||||
* Master at Arms - melee damage increased from 7 to 8, cost changed from 44 to 65.
|
||||
* Merman Hoplite - cost changed from 43 to 45.
|
||||
* Merman Triton - cost changed from 43 to 46.
|
||||
* General - hp changed from 50 to 55, cost changed from 54 to 66, xp changed from 180 to 105.
|
||||
* Great Mage - ranged damage changed from 16 to 17, cost changed from 72 to 110.
|
||||
* Grand Marshal - hp changed from 60 to 68 cost changed from 66 to 101.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Prevent duplicate advancements from being added to unit type. (issue #7009)
|
||||
* When `[unit]` recalls a unit, remove it from the recall list before firing `[event]name=unit_placed`. (issue #7769)
|
||||
|
||||
## Version 1.17.18
|
||||
### Campaigns
|
||||
* Heir to the Throne
|
||||
* S19c and S20b: Improve rewards by giving a Lieutenant instead of a Sergeant.
|
||||
* Northern Rebirth
|
||||
* Large grammar correction to the English text. (PRs #7643, #7663, #7676, #7680, #7682, #7683)
|
||||
* UtBS
|
||||
* Make the name of the dark assassin translatable again. (PR #7696)
|
||||
* Added po hints (translation hints).
|
||||
* World Conquest
|
||||
* Fixed a bug that the epic trait raised XP requirements for advancements that cost less than 60 XP.
|
||||
### Multiplayer
|
||||
* Add some search terms to the match history dialog. (PR #7571)
|
||||
### Terrain
|
||||
* Added ruined walls terrain variations: Xur (damaged Xu); Xor (damaged Xos); and Exos (ruined Xos, is Flat/Cave alias). (PR #7629)
|
||||
### Translations
|
||||
* Updated translations: British English, Czech, Finnish, French, Spanish
|
||||
* Python files are now searched for translatable strings.
|
||||
* Added the `wesnoth-tools` textdomain for WML-maintenance tools.
|
||||
### User interface
|
||||
* Fix file dialogs (e.g. Save As dialog in the Map Editor) not listing /run/media/USER
|
||||
as a possible file location on modern Linux distributions.
|
||||
### WML Engine
|
||||
* Update the preferences window’s Display resolution list if the game window is resized.
|
||||
* Better error handling if the data directory is not found. (issue #6883)
|
||||
* The Log File button is now available in the game version info dialog on non-Windows platforms.
|
||||
* Rearranged some elements in Display preferences.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Move multiplayer macros to proper folder. (PR #7647)
|
||||
* Fixed updating POT files with version 0.68-1 of the po4a toolsuite. (issue #7149)
|
||||
* On platforms where logging used to go to stdout/stderr by default, it’s now copied there even when logging to a file. (PR #7672)
|
||||
* `GUI.pyw` now has the i18n mechanics to be translatable, but please wait until the English has been revised.
|
||||
* Refactored hotkey handling, so it no longer has two different codepaths for WML menu items.
|
||||
* The old `utils/change_textdomain` script has been removed. (PR #7688)
|
||||
* wmlxgettext now supports extracting all textdomains at once. (PR #7624)
|
||||
* Fixed a crash in the autocorrect handling on the Wesnoth console. (issue #7412)
|
||||
|
||||
## Version 1.17.17
|
||||
### Translations
|
||||
|
@ -464,6 +942,100 @@
|
|||
* wmllint, wmlscope and wmlindent now support the command line `--version` flag, which reports the current version of Wesnoth (issue #6346).
|
||||
* wmllint is now capable of handling unit levels and types when checking recruitment patterns.
|
||||
|
||||
## Version 1.16.12
|
||||
### Security Fixes
|
||||
* Run wesnothd server as `_wesnoth:_wesnoth` instead of `nobody:users`, improving safety and fixing a warning message in systemd 246+
|
||||
### Translations
|
||||
* Updated translations: Czech, German, Italian
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix a regression from 1.13.11 that inverted horizontal scrolling with SDL versions 2.0.18+ on X11 and versions 2.0.20+ on Wayland (issues #3362 and #7404, PR #8158)
|
||||
|
||||
## Version 1.16.11
|
||||
### Translations
|
||||
* Updated translations: Arabic, British English, Bulgarian, Chinese (Simplified), Czech, French, Japanese, Polish, Slovak, Welsh
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix a crash in the `:inspect` window when pagination is used (issue #7851).
|
||||
* Fix "Something is wrong with the addon version check database supporting the multiplayer lobby".
|
||||
Note that It also required the host of the game to use wesnoth 1.16.11
|
||||
|
||||
## Version 1.16.10
|
||||
### Campaigns
|
||||
* Under the Burning Suns
|
||||
* S03: Ensure all of Garak’s attacks are changed to fire type (issue #7774).
|
||||
* Make the name of the dark assassin translatable again (PR #7675).
|
||||
* Added po hints (translation hints).
|
||||
* World Conquest
|
||||
* Fix lua error at victory after promoting a replacement commander (issue #7823).
|
||||
### Translations
|
||||
* Updated translations: Arabic, Czech, French, Italian, Spanish, Portuguese (Brazil)
|
||||
### User interface
|
||||
* Fix file dialogs (e.g. Save As dialog in the Map Editor) not listing /run/media/USER
|
||||
as a possible file location on modern Linux distributions.
|
||||
* Update the preferences window’s Display resolution list if the game window is resized.
|
||||
* Enable Whiteboard if the "enable planning mode on start" option is set (issue #7820).
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fix compilation with Clang 16 (PR #7586).
|
||||
* Fix crash in the Flatpak version when recruiting in Wayland (issue #7104).
|
||||
* Fix the version dialog’s display on MacOS.
|
||||
* Fixed updating POT files with version 0.68-1 of the po4a toolsuite (issue #7149).
|
||||
|
||||
## Version 1.16.9
|
||||
### Campaigns
|
||||
* Heir to the Throne
|
||||
* S03: Rewrite intro text, including a hint about training troops (PR #7291)
|
||||
* World Conquest
|
||||
* Fixed rare lua error when a unit is killed.
|
||||
* Fixed Bezoar Item.
|
||||
* Fixed some items losing their effects in later scenarios.
|
||||
* Fixed units losing hp when picking up an artifact
|
||||
* Partially fixed "Promote to commander" (but required a further fix in 1.16.10)
|
||||
### Editor
|
||||
* Scrolling the map north or south can move the text that overlays the map, so it doesn't always obscure the southmost hexes of the map. (issue #6422)
|
||||
### Multiplayer
|
||||
* Allied units’ orbs no longer look like the player’s own units’ orbs (issue #7108).
|
||||
* By default, they are now two-color during the ally’s turn.
|
||||
* Added an advanced setting to always show them as single-color (the ally color).
|
||||
* "Back to Turn" menu no longer crashes the game when used
|
||||
After the game has ended. #4236
|
||||
### Translations
|
||||
* Updated translations: British English, Bulgarian, Chinese (Simplified), Czech, Dutch, French, German, Italian, Polish, Portuguese (Brazil), Spanish, Turkish
|
||||
### User interface
|
||||
* Improved the layout of the orb colors dialog, and added tooltips.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fixed Possible Crash when using planning mode #3768
|
||||
* Fixed Possible OOS when using planning mode #7465
|
||||
* Fixed game hanging when an enemy attacks a unit
|
||||
with planned moves #5302
|
||||
* Fixed custom effects implemented via wesnoth.effects
|
||||
losing their effects in later scenarios.
|
||||
* Fix Crash undo+redo error after reloading #7253
|
||||
* Fix Crash when loading old replays #7253
|
||||
* Fix Crash when a unit has bad animations replays #5032
|
||||
* Postponed the removal of the `SPECIAL_NOTES` macro, which means there will be fewer log messages.
|
||||
* `wesnoth_addon_manager` now supports SSL/TLS connection (using the `--secure` flag)
|
||||
* `wesnoth_addon_manager` now supports IPv6 connection (using the `--ipv6` flag)
|
||||
* Added new command line flag `--terms` to `wesnoth_addon_manager` (retrieves and prints the add-ons server upload terms)
|
||||
* w`esnoth_addon_manager` now supports the `--version` flag (reports the current Wesnoth version)
|
||||
* Removed unused `--color` switch from `wesnoth_addon_manager`
|
||||
|
||||
## Version 1.16.8
|
||||
### Editor
|
||||
* Fixed: the unit tool crashes when placing a unit (issue #7296).
|
||||
### Translations
|
||||
* Updated translations: Finnish, Spanish
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Make the log messages about "Skipping duplicate unit variation ID" say which `[unit_type]` is causing the error.
|
||||
|
||||
## Version 1.16.7
|
||||
### Translations
|
||||
* Updated translations: Arabic, British English, Czech, Finnish, French, Italian, Japanese, Polish, Portuguese (Brazil), Turkish
|
||||
### Miscellaneous and Bug Fixes
|
||||
* wmllint now validates `rank=` values in `[campaign]` (issue #7224)
|
||||
* Add disconnect check to alert users when they lose connection to the multiplayer server (issue #1336)
|
||||
* Fixed a crash when checking if abilities are active during game initialisation after loading a saved game. (issues #5643, #7238)
|
||||
* Fix a crash when an out-of-bounds side number is used in Lua’s `sync.evaluate_multiple` (PR #7222)
|
||||
* Fixed special notes being duplicated when storing units (issue #7153).
|
||||
|
||||
## Version 1.16.6
|
||||
### Translations
|
||||
* Updated translations: Arabic, British English, Bulgarian, Chinese (Simplified), Chinese (Traditional), Czech, French, Italian, Japanese, Portuguese (Brazil), Russian, Swedish, Turkish
|
||||
|
@ -822,9 +1394,9 @@
|
|||
* Moved Naga guardian line from UtBS to core, and made some revisions
|
||||
* Dune Apothecary experience needed to level up changed from 100 to 65
|
||||
* Dune Captain experience needed to level up changed from 86 to 75
|
||||
* Dune Herbalist cost changed form 15 to 14
|
||||
* Dune Horse Archer experience needed to level up changed form 70 to 65
|
||||
* Naga Sicarius hp changed form 55 to 53 and melee damage changed form 9 to 6, new special "deflect" added to melee attack
|
||||
* Dune Herbalist cost changed from 15 to 14
|
||||
* Dune Horse Archer experience needed to level up changed from 70 to 65
|
||||
* Naga Sicarius hp changed from 55 to 53 and melee damage changed from 9 to 6, new special "deflect" added to melee attack
|
||||
* Fire Guardian can now level into Fire Wraith, experience needed to level up for Fire Guardian changed from 50 to 29
|
||||
* Revised statistics of all animal horses, Bay Horse can now level into Great Horse
|
||||
### Wesnoth Formula Language
|
||||
|
@ -2327,7 +2899,7 @@
|
|||
the lich brothers and Krash got more gold.
|
||||
* Sceptre of Fire:
|
||||
* S4: a cave entrance has been added.
|
||||
* S5: Gryphon Riders are now available starting form S5 (formerly S3).
|
||||
* S5: Gryphon Riders are now available starting from S5 (formerly S3).
|
||||
* S7: it's now impossible to kill all pursuers.
|
||||
* Secrets of the Ancients:
|
||||
* Fix dialog said by wrong unit & revert previous fix (issue #3294).
|
||||
|
@ -3215,7 +3787,7 @@
|
|||
### Editor
|
||||
* Fixed a crash when placing units.
|
||||
* Restored the ability to preview different ToDs. Note this still does not
|
||||
work when invoked form the Custom ToD window.
|
||||
work when invoked from the Custom ToD window.
|
||||
* Fixed editor sides not having the correct side number.
|
||||
* Redesigned Windows, Areas, and Sides menus.
|
||||
* The Windows menu will now display maps starting from 1 rather than 0.
|
||||
|
|
3
changelog_entries/addon_server_id_ui.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
### Add-ons client
|
||||
* The add-ons server identifier (e.g. 1.18) is now displayed on the bottom left after the
|
||||
server address. If debug mode is enabled the server software version is also shown.
|
3
changelog_entries/recruit_recall_filtering.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
### User interface
|
||||
* Added unit type level as a filter criterion in the Recruit Unit dialog.
|
||||
* Added unit race and alignment as additional filter criteria in the Recall Unit dialog.
|
3
changelog_entries/sota_crelanu.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
### Campaigns
|
||||
* Secrets of the Ancients
|
||||
* S16: Keep Crelanu within his ring of protective holy waters (issue #8361)
|
3
changelog_entries/ui-outro-text-fading.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
### User interface
|
||||
* Fixed timing issue with the outro screen's text fading effect that made it so fast as
|
||||
to be unnoticeable on many hardware configurations.
|
3
changelog_entries/wc-double-scrollbar-bugfix.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
### Campaigns
|
||||
* World Conquest
|
||||
Fix World Conquest's Help dialog showing double scrollbars due to the left tree having it's vertical scrollbar disabled. Changed both vertical and horizontal scrollbar modes to automatic. (issue #8576)
|
266
cmake/FindLua.cmake
Normal file
|
@ -0,0 +1,266 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindLua
|
||||
-------
|
||||
|
||||
Locate Lua library compiled as C++ code.
|
||||
|
||||
This module defines:
|
||||
|
||||
``LUA_FOUND``
|
||||
if false, do not try to link to Lua
|
||||
``LUA_LIBRARIES``
|
||||
both lua and lualib
|
||||
``LUA_INCLUDE_DIR``
|
||||
where to find lua.h
|
||||
``LUA_VERSION_STRING``
|
||||
the version of Lua found
|
||||
``LUA_VERSION_MAJOR``
|
||||
the major version of Lua
|
||||
``LUA_VERSION_MINOR``
|
||||
the minor version of Lua
|
||||
``LUA_VERSION_PATCH``
|
||||
the patch version of Lua
|
||||
|
||||
Note that the expected include convention for Wesnoth source files is
|
||||
|
||||
::
|
||||
|
||||
#include "lua/wrapper_lua.h"
|
||||
#include "lua/wrapper_lualib.h"
|
||||
#include "lua/wrapper_lauxlib.h"
|
||||
|
||||
and not
|
||||
|
||||
::
|
||||
|
||||
#include <lua/lua.h>
|
||||
#include <lua/lualib.h>
|
||||
#include <lua/lauxlib.h>
|
||||
|
||||
nor
|
||||
|
||||
::
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
This is because Wesnoth can be configured to use either its Lua submodule or a
|
||||
system copy of Lua, so wrapper headers exist to include the correct headers.
|
||||
|
||||
When compiled as C++, Lua_ handles errors with exceptions. Wesnoth_ requires
|
||||
this.
|
||||
|
||||
.. _Lua: https://github.com/lua/lua/blob/9be74cc/ldo.c#L47
|
||||
.. _Wesnoth: https://github.com/wesnoth/wesnoth/commit/e4d0ae0
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_policy(PUSH) # Policies apply to functions at definition-time
|
||||
cmake_policy(SET CMP0012 NEW) # For while(TRUE)
|
||||
|
||||
unset(_lua_include_subdirs)
|
||||
unset(_lua_library_names)
|
||||
unset(_lua_append_versions)
|
||||
|
||||
# this is a function only to have all the variables inside go away automatically
|
||||
function(_lua_get_versions)
|
||||
set(LUA_VERSIONS5 5.4 5.3 5.2 5.1 5.0)
|
||||
|
||||
if (Lua_FIND_VERSION_EXACT)
|
||||
if (Lua_FIND_VERSION_COUNT GREATER 1)
|
||||
set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR})
|
||||
endif ()
|
||||
elseif (Lua_FIND_VERSION)
|
||||
# once there is a different major version supported this should become a loop
|
||||
if (NOT Lua_FIND_VERSION_MAJOR GREATER 5)
|
||||
if (Lua_FIND_VERSION_COUNT EQUAL 1)
|
||||
set(_lua_append_versions ${LUA_VERSIONS5})
|
||||
else ()
|
||||
foreach (subver IN LISTS LUA_VERSIONS5)
|
||||
if (NOT subver VERSION_LESS ${Lua_FIND_VERSION})
|
||||
list(APPEND _lua_append_versions ${subver})
|
||||
endif ()
|
||||
endforeach ()
|
||||
# New version -> Search for it (heuristic only! Defines in include might have changed)
|
||||
if (NOT _lua_append_versions)
|
||||
set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR})
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
else ()
|
||||
# once there is a different major version supported this should become a loop
|
||||
set(_lua_append_versions ${LUA_VERSIONS5})
|
||||
endif ()
|
||||
|
||||
if (LUA_Debug)
|
||||
message(STATUS "Considering following Lua versions: ${_lua_append_versions}")
|
||||
endif()
|
||||
|
||||
set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_lua_set_version_vars)
|
||||
set(_lua_include_subdirs_raw "lua")
|
||||
|
||||
foreach (ver IN LISTS _lua_append_versions)
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}")
|
||||
list(APPEND _lua_include_subdirs_raw
|
||||
lua${CMAKE_MATCH_1}${CMAKE_MATCH_2}
|
||||
lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
|
||||
lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
|
||||
)
|
||||
endforeach ()
|
||||
|
||||
# Prepend "include/" to each path directly after the path
|
||||
set(_lua_include_subdirs "include")
|
||||
foreach (dir IN LISTS _lua_include_subdirs_raw)
|
||||
list(APPEND _lua_include_subdirs "${dir}" "include/${dir}")
|
||||
endforeach ()
|
||||
|
||||
set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE)
|
||||
endfunction(_lua_set_version_vars)
|
||||
|
||||
function(_lua_get_header_version)
|
||||
unset(LUA_VERSION_STRING PARENT_SCOPE)
|
||||
set(_hdr_file "${LUA_INCLUDE_DIR}/lua.h")
|
||||
|
||||
if (NOT EXISTS "${_hdr_file}")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
# At least 5.[012] have different ways to express the version
|
||||
# so all of them need to be tested. Lua 5.2 defines LUA_VERSION
|
||||
# and LUA_RELEASE as joined by the C preprocessor, so avoid those.
|
||||
file(STRINGS "${_hdr_file}" lua_version_strings
|
||||
REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*")
|
||||
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};")
|
||||
if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};")
|
||||
set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
|
||||
else ()
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
|
||||
if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
|
||||
endif ()
|
||||
string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}")
|
||||
endif ()
|
||||
foreach (ver IN LISTS _lua_append_versions)
|
||||
if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
|
||||
set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE)
|
||||
set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE)
|
||||
set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE)
|
||||
set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE)
|
||||
return()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endfunction(_lua_get_header_version)
|
||||
|
||||
function(_lua_find_header)
|
||||
_lua_set_version_vars()
|
||||
|
||||
# Initialize as local variable
|
||||
set(CMAKE_IGNORE_PATH ${CMAKE_IGNORE_PATH})
|
||||
while (TRUE)
|
||||
# Find the next header to test. Check each possible subdir in order
|
||||
# This prefers e.g. higher versions as they are earlier in the list
|
||||
# It is also consistent with previous versions of FindLua
|
||||
foreach (subdir IN LISTS _lua_include_subdirs)
|
||||
find_path(LUA_INCLUDE_DIR lua.h
|
||||
HINTS ENV LUA_DIR
|
||||
PATH_SUFFIXES ${subdir}
|
||||
)
|
||||
if (LUA_INCLUDE_DIR)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
# Did not found header -> Fail
|
||||
if (NOT LUA_INCLUDE_DIR)
|
||||
return()
|
||||
endif()
|
||||
_lua_get_header_version()
|
||||
# Found accepted version -> Ok
|
||||
if (LUA_VERSION_STRING)
|
||||
if (LUA_Debug)
|
||||
message(STATUS "Found suitable version ${LUA_VERSION_STRING} in ${LUA_INCLUDE_DIR}/lua.h")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
# Found wrong version -> Ignore this path and retry
|
||||
if (LUA_Debug)
|
||||
message(STATUS "Ignoring unsuitable version in ${LUA_INCLUDE_DIR}")
|
||||
endif()
|
||||
list(APPEND CMAKE_IGNORE_PATH "${LUA_INCLUDE_DIR}")
|
||||
unset(LUA_INCLUDE_DIR CACHE)
|
||||
unset(LUA_INCLUDE_DIR)
|
||||
unset(LUA_INCLUDE_DIR PARENT_SCOPE)
|
||||
endwhile ()
|
||||
endfunction()
|
||||
|
||||
_lua_get_versions()
|
||||
_lua_find_header()
|
||||
_lua_get_header_version()
|
||||
unset(_lua_append_versions)
|
||||
|
||||
# Add new names here when found in distributions.
|
||||
# Debian since lua5.2 5.2.3-2 uses luaX.Y-c++:
|
||||
# https://salsa.debian.org/lua-team/lua5.2/-/commit/fa2dc77c
|
||||
# Arch since lua 5.4.4-4 uses lua++-X.Y:
|
||||
# https://gitlab.archlinux.org/archlinux/packaging/packages/lua/-/commit/4e97e19d
|
||||
if (LUA_VERSION_STRING)
|
||||
set(_lua_library_names
|
||||
lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}-c++
|
||||
lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}-c++
|
||||
lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}-c++
|
||||
lua.${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}-c++
|
||||
lua++${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}
|
||||
lua++${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
lua++-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
lua++.${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_library(LUA_LIBRARY
|
||||
NAMES ${_lua_library_names} lua
|
||||
NAMES_PER_DIR
|
||||
HINTS
|
||||
ENV LUA_DIR
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
unset(_lua_library_names)
|
||||
|
||||
if (LUA_LIBRARY)
|
||||
# include the math library for Unix
|
||||
if (UNIX AND NOT APPLE AND NOT BEOS)
|
||||
find_library(LUA_MATH_LIBRARY m)
|
||||
mark_as_advanced(LUA_MATH_LIBRARY)
|
||||
set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}")
|
||||
|
||||
# include dl library for statically-linked Lua library
|
||||
get_filename_component(LUA_LIB_EXT ${LUA_LIBRARY} EXT)
|
||||
if(LUA_LIB_EXT STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
|
||||
list(APPEND LUA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
# For Windows and Mac, don't need to explicitly include the math library
|
||||
else ()
|
||||
set(LUA_LIBRARIES "${LUA_LIBRARY}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua
|
||||
REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR
|
||||
VERSION_VAR LUA_VERSION_STRING)
|
||||
|
||||
mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY)
|
||||
|
||||
cmake_policy(POP)
|
611
cmake/FindPackageHandleStandardArgs.cmake
Normal file
|
@ -0,0 +1,611 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindPackageHandleStandardArgs
|
||||
-----------------------------
|
||||
|
||||
This module provides functions intended to be used in :ref:`Find Modules`
|
||||
implementing :command:`find_package(<PackageName>)` calls.
|
||||
|
||||
.. command:: find_package_handle_standard_args
|
||||
|
||||
This command handles the ``REQUIRED``, ``QUIET`` and version-related
|
||||
arguments of :command:`find_package`. It also sets the
|
||||
``<PackageName>_FOUND`` variable. The package is considered found if all
|
||||
variables listed contain valid results, e.g. valid filepaths.
|
||||
|
||||
There are two signatures:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_handle_standard_args(<PackageName>
|
||||
(DEFAULT_MSG|<custom-failure-message>)
|
||||
<required-var>...
|
||||
)
|
||||
|
||||
find_package_handle_standard_args(<PackageName>
|
||||
[FOUND_VAR <result-var>]
|
||||
[REQUIRED_VARS <required-var>...]
|
||||
[VERSION_VAR <version-var>]
|
||||
[HANDLE_VERSION_RANGE]
|
||||
[HANDLE_COMPONENTS]
|
||||
[CONFIG_MODE]
|
||||
[NAME_MISMATCHED]
|
||||
[REASON_FAILURE_MESSAGE <reason-failure-message>]
|
||||
[FAIL_MESSAGE <custom-failure-message>]
|
||||
)
|
||||
|
||||
The ``<PackageName>_FOUND`` variable will be set to ``TRUE`` if all
|
||||
the variables ``<required-var>...`` are valid and any optional
|
||||
constraints are satisfied, and ``FALSE`` otherwise. A success or
|
||||
failure message may be displayed based on the results and on
|
||||
whether the ``REQUIRED`` and/or ``QUIET`` option was given to
|
||||
the :command:`find_package` call.
|
||||
|
||||
The options are:
|
||||
|
||||
``(DEFAULT_MSG|<custom-failure-message>)``
|
||||
In the simple signature this specifies the failure message.
|
||||
Use ``DEFAULT_MSG`` to ask for a default message to be computed
|
||||
(recommended). Not valid in the full signature.
|
||||
|
||||
``FOUND_VAR <result-var>``
|
||||
.. deprecated:: 3.3
|
||||
|
||||
Specifies either ``<PackageName>_FOUND`` or
|
||||
``<PACKAGENAME>_FOUND`` as the result variable. This exists only
|
||||
for compatibility with older versions of CMake and is now ignored.
|
||||
Result variables of both names are always set for compatibility.
|
||||
|
||||
``REQUIRED_VARS <required-var>...``
|
||||
Specify the variables which are required for this package.
|
||||
These may be named in the generated failure message asking the
|
||||
user to set the missing variable values. Therefore these should
|
||||
typically be cache entries such as ``FOO_LIBRARY`` and not output
|
||||
variables like ``FOO_LIBRARIES``.
|
||||
|
||||
.. versionchanged:: 3.18
|
||||
If ``HANDLE_COMPONENTS`` is specified, this option can be omitted.
|
||||
|
||||
``VERSION_VAR <version-var>``
|
||||
Specify the name of a variable that holds the version of the package
|
||||
that has been found. This version will be checked against the
|
||||
(potentially) specified required version given to the
|
||||
:command:`find_package` call, including its ``EXACT`` option.
|
||||
The default messages include information about the required
|
||||
version and the version which has been actually found, both
|
||||
if the version is ok or not.
|
||||
|
||||
``HANDLE_VERSION_RANGE``
|
||||
.. versionadded:: 3.19
|
||||
|
||||
Enable handling of a version range, if one is specified. Without this
|
||||
option, a developer warning will be displayed if a version range is
|
||||
specified.
|
||||
|
||||
``HANDLE_COMPONENTS``
|
||||
Enable handling of package components. In this case, the command
|
||||
will report which components have been found and which are missing,
|
||||
and the ``<PackageName>_FOUND`` variable will be set to ``FALSE``
|
||||
if any of the required components (i.e. not the ones listed after
|
||||
the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are
|
||||
missing.
|
||||
|
||||
``CONFIG_MODE``
|
||||
Specify that the calling find module is a wrapper around a
|
||||
call to ``find_package(<PackageName> NO_MODULE)``. This implies
|
||||
a ``VERSION_VAR`` value of ``<PackageName>_VERSION``. The command
|
||||
will automatically check whether the package configuration file
|
||||
was found.
|
||||
|
||||
``REASON_FAILURE_MESSAGE <reason-failure-message>``
|
||||
.. versionadded:: 3.16
|
||||
|
||||
Specify a custom message of the reason for the failure which will be
|
||||
appended to the default generated message.
|
||||
|
||||
``FAIL_MESSAGE <custom-failure-message>``
|
||||
Specify a custom failure message instead of using the default
|
||||
generated message. Not recommended.
|
||||
|
||||
``NAME_MISMATCHED``
|
||||
.. versionadded:: 3.17
|
||||
|
||||
Indicate that the ``<PackageName>`` does not match
|
||||
``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a
|
||||
warning, but it may be intentional for usage of the command for components
|
||||
of a larger package.
|
||||
|
||||
Example for the simple signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_handle_standard_args(LibXml2 DEFAULT_MSG
|
||||
LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
|
||||
|
||||
The ``LibXml2`` package is considered to be found if both
|
||||
``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid.
|
||||
Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found
|
||||
and ``REQUIRED`` was used, it fails with a
|
||||
:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was
|
||||
used or not. If it is found, success will be reported, including
|
||||
the content of the first ``<required-var>``. On repeated CMake runs,
|
||||
the same message will not be printed again.
|
||||
|
||||
.. note::
|
||||
|
||||
If ``<PackageName>`` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the
|
||||
calling module, a warning that there is a mismatch is given. The
|
||||
``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using
|
||||
the old signature and the ``NAME_MISMATCHED`` argument using the new
|
||||
signature. To avoid forcing the caller to require newer versions of CMake for
|
||||
usage, the variable's value will be used if defined when the
|
||||
``NAME_MISMATCHED`` argument is not passed for the new signature (but using
|
||||
both is an error)..
|
||||
|
||||
Example for the full signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_handle_standard_args(LibArchive
|
||||
REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR
|
||||
VERSION_VAR LibArchive_VERSION)
|
||||
|
||||
In this case, the ``LibArchive`` package is considered to be found if
|
||||
both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid.
|
||||
Also the version of ``LibArchive`` will be checked by using the version
|
||||
contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given,
|
||||
the default messages will be printed.
|
||||
|
||||
Another example for the full signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)
|
||||
find_package_handle_standard_args(Automoc4 CONFIG_MODE)
|
||||
|
||||
In this case, a ``FindAutmoc4.cmake`` module wraps a call to
|
||||
``find_package(Automoc4 NO_MODULE)`` and adds an additional search
|
||||
directory for ``automoc4``. Then the call to
|
||||
``find_package_handle_standard_args`` produces a proper success/failure
|
||||
message.
|
||||
|
||||
.. command:: find_package_check_version
|
||||
|
||||
.. versionadded:: 3.19
|
||||
|
||||
Helper function which can be used to check if a ``<version>`` is valid
|
||||
against version-related arguments of :command:`find_package`.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_check_version(<version> <result-var>
|
||||
[HANDLE_VERSION_RANGE]
|
||||
[RESULT_MESSAGE_VARIABLE <message-var>]
|
||||
)
|
||||
|
||||
The ``<result-var>`` will hold a boolean value giving the result of the check.
|
||||
|
||||
The options are:
|
||||
|
||||
``HANDLE_VERSION_RANGE``
|
||||
Enable handling of a version range, if one is specified. Without this
|
||||
option, a developer warning will be displayed if a version range is
|
||||
specified.
|
||||
|
||||
``RESULT_MESSAGE_VARIABLE <message-var>``
|
||||
Specify a variable to get back a message describing the result of the check.
|
||||
|
||||
Example for the usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE
|
||||
RESULT_MESSAGE_VARIABLE reason)
|
||||
if (result)
|
||||
message (STATUS "${reason}")
|
||||
else()
|
||||
message (FATAL_ERROR "${reason}")
|
||||
endif()
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake)
|
||||
|
||||
|
||||
cmake_policy(PUSH)
|
||||
# numbers and boolean constants
|
||||
cmake_policy (SET CMP0012 NEW)
|
||||
# IN_LIST operator
|
||||
cmake_policy (SET CMP0057 NEW)
|
||||
|
||||
|
||||
# internal helper macro
|
||||
macro(_FPHSA_FAILURE_MESSAGE _msg)
|
||||
set (__msg "${_msg}")
|
||||
if (FPHSA_REASON_FAILURE_MESSAGE)
|
||||
string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n")
|
||||
endif()
|
||||
if (${_NAME}_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "${__msg}")
|
||||
else ()
|
||||
if (NOT ${_NAME}_FIND_QUIETLY)
|
||||
message(STATUS "${__msg}")
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro()
|
||||
|
||||
|
||||
# internal helper macro to generate the failure message when used in CONFIG_MODE:
|
||||
macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
|
||||
# <PackageName>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:
|
||||
if(${_NAME}_CONFIG)
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})")
|
||||
else()
|
||||
# If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.
|
||||
# List them all in the error message:
|
||||
if(${_NAME}_CONSIDERED_CONFIGS)
|
||||
set(configsText "")
|
||||
list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)
|
||||
math(EXPR configsCount "${configsCount} - 1")
|
||||
foreach(currentConfigIndex RANGE ${configsCount})
|
||||
list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
|
||||
list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
|
||||
string(APPEND configsText "\n ${filename} (version ${version})")
|
||||
endforeach()
|
||||
if (${_NAME}_NOT_FOUND_MESSAGE)
|
||||
if (FPHSA_REASON_FAILURE_MESSAGE)
|
||||
string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ")
|
||||
else()
|
||||
set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}")
|
||||
endif()
|
||||
else()
|
||||
string(APPEND configsText "\n")
|
||||
endif()
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}")
|
||||
|
||||
else()
|
||||
# Simple case: No Config-file was found at all:
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}")
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
function(FIND_PACKAGE_CHECK_VERSION version result)
|
||||
cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "")
|
||||
|
||||
if (FPCV_UNPARSED_ARGUMENTS)
|
||||
message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments")
|
||||
endif()
|
||||
if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES)
|
||||
message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument")
|
||||
endif()
|
||||
|
||||
set (${result} FALSE PARENT_SCOPE)
|
||||
if (FPCV_RESULT_MESSAGE_VARIABLE)
|
||||
unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if (_CMAKE_FPHSA_PACKAGE_NAME)
|
||||
set (package "${_CMAKE_FPHSA_PACKAGE_NAME}")
|
||||
elseif (CMAKE_FIND_PACKAGE_NAME)
|
||||
set (package "${CMAKE_FIND_PACKAGE_NAME}")
|
||||
else()
|
||||
message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'")
|
||||
endif()
|
||||
|
||||
if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE
|
||||
AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE)
|
||||
message(AUTHOR_WARNING
|
||||
"`find_package()` specify a version range but the option "
|
||||
"HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. "
|
||||
"Only the lower endpoint of the range will be used.")
|
||||
endif()
|
||||
|
||||
|
||||
set (version_ok FALSE)
|
||||
unset (version_msg)
|
||||
|
||||
if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE)
|
||||
if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE"
|
||||
AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN)
|
||||
AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE"
|
||||
AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX)
|
||||
OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE"
|
||||
AND version VERSION_LESS ${package}_FIND_VERSION_MAX)))
|
||||
set (version_ok TRUE)
|
||||
set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")")
|
||||
else()
|
||||
set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"")
|
||||
endif()
|
||||
elseif (DEFINED ${package}_FIND_VERSION)
|
||||
if(${package}_FIND_VERSION_EXACT) # exact version required
|
||||
# count the dots in the version string
|
||||
string(REGEX REPLACE "[^.]" "" version_dots "${version}")
|
||||
# add one dot because there is one dot more than there are components
|
||||
string(LENGTH "${version_dots}." version_dots)
|
||||
if (version_dots GREATER ${package}_FIND_VERSION_COUNT)
|
||||
# Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT
|
||||
# is at most 4 here. Therefore a simple lookup table is used.
|
||||
if (${package}_FIND_VERSION_COUNT EQUAL 1)
|
||||
set(version_regex "[^.]*")
|
||||
elseif (${package}_FIND_VERSION_COUNT EQUAL 2)
|
||||
set(version_regex "[^.]*\\.[^.]*")
|
||||
elseif (${package}_FIND_VERSION_COUNT EQUAL 3)
|
||||
set(version_regex "[^.]*\\.[^.]*\\.[^.]*")
|
||||
else()
|
||||
set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*")
|
||||
endif()
|
||||
string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}")
|
||||
if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head)
|
||||
set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"")
|
||||
else ()
|
||||
set(version_ok TRUE)
|
||||
set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")")
|
||||
endif ()
|
||||
else ()
|
||||
if (NOT ${package}_FIND_VERSION VERSION_EQUAL version)
|
||||
set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"")
|
||||
else ()
|
||||
set(version_ok TRUE)
|
||||
set(version_msg "(found suitable exact version \"${version}\")")
|
||||
endif ()
|
||||
endif ()
|
||||
else() # minimum version
|
||||
if (${package}_FIND_VERSION VERSION_GREATER version)
|
||||
set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"")
|
||||
else()
|
||||
set(version_ok TRUE)
|
||||
set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")")
|
||||
endif()
|
||||
endif()
|
||||
else ()
|
||||
set(version_ok TRUE)
|
||||
set(version_msg "(found version \"${version}\")")
|
||||
endif()
|
||||
|
||||
set (${result} ${version_ok} PARENT_SCOPE)
|
||||
if (FPCV_RESULT_MESSAGE_VARIABLE)
|
||||
set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
|
||||
|
||||
# Set up the arguments for `cmake_parse_arguments`.
|
||||
set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE)
|
||||
set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR)
|
||||
set(multiValueArgs REQUIRED_VARS)
|
||||
|
||||
# Check whether we are in 'simple' or 'extended' mode:
|
||||
set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
|
||||
list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
|
||||
|
||||
unset(FPHSA_NAME_MISMATCHED_override)
|
||||
if (DEFINED FPHSA_NAME_MISMATCHED)
|
||||
# If the variable NAME_MISMATCHED variable is set, error if it is passed as
|
||||
# an argument. The former is for old signatures, the latter is for new
|
||||
# signatures.
|
||||
list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx)
|
||||
if (NOT name_mismatched_idx EQUAL "-1")
|
||||
message(FATAL_ERROR
|
||||
"The `NAME_MISMATCHED` argument may only be specified by the argument or "
|
||||
"the variable, not both.")
|
||||
endif ()
|
||||
|
||||
# But use the variable if it is not an argument to avoid forcing minimum
|
||||
# CMake version bumps for calling modules.
|
||||
set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}")
|
||||
endif ()
|
||||
|
||||
if(${INDEX} EQUAL -1)
|
||||
set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
|
||||
set(FPHSA_REQUIRED_VARS ${ARGN})
|
||||
set(FPHSA_VERSION_VAR)
|
||||
else()
|
||||
cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN})
|
||||
|
||||
if(FPHSA_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT FPHSA_FAIL_MESSAGE)
|
||||
set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG")
|
||||
endif()
|
||||
|
||||
# In config-mode, we rely on the variable <PackageName>_CONFIG, which is set by find_package()
|
||||
# when it successfully found the config-file, including version checking:
|
||||
if(FPHSA_CONFIG_MODE)
|
||||
list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)
|
||||
list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)
|
||||
set(FPHSA_VERSION_VAR ${_NAME}_VERSION)
|
||||
endif()
|
||||
|
||||
if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS)
|
||||
message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (DEFINED FPHSA_NAME_MISMATCHED_override)
|
||||
set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}")
|
||||
endif ()
|
||||
|
||||
if (DEFINED CMAKE_FIND_PACKAGE_NAME
|
||||
AND NOT FPHSA_NAME_MISMATCHED
|
||||
AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME)
|
||||
message(AUTHOR_WARNING
|
||||
"The package name passed to `find_package_handle_standard_args` "
|
||||
"(${_NAME}) does not match the name of the calling package "
|
||||
"(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling "
|
||||
"code that expects `find_package` result variables (e.g., `_FOUND`) "
|
||||
"to follow a certain pattern.")
|
||||
endif ()
|
||||
|
||||
if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE)
|
||||
message(AUTHOR_WARNING
|
||||
"`find_package()` specify a version range but the module ${_NAME} does "
|
||||
"not support this capability. Only the lower endpoint of the range "
|
||||
"will be used.")
|
||||
endif()
|
||||
|
||||
# to propagate package name to FIND_PACKAGE_CHECK_VERSION
|
||||
set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}")
|
||||
|
||||
# now that we collected all arguments, process them
|
||||
|
||||
if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG")
|
||||
set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}")
|
||||
endif()
|
||||
|
||||
if (FPHSA_REQUIRED_VARS)
|
||||
list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)
|
||||
endif()
|
||||
|
||||
string(TOUPPER ${_NAME} _NAME_UPPER)
|
||||
string(TOLOWER ${_NAME} _NAME_LOWER)
|
||||
|
||||
if(FPHSA_FOUND_VAR)
|
||||
set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND)
|
||||
set(_FOUND_VAR_MIXED ${_NAME}_FOUND)
|
||||
if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER)
|
||||
set(_FOUND_VAR ${FPHSA_FOUND_VAR})
|
||||
else()
|
||||
message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.")
|
||||
endif()
|
||||
else()
|
||||
set(_FOUND_VAR ${_NAME_UPPER}_FOUND)
|
||||
endif()
|
||||
|
||||
# collect all variables which were not found, so they can be printed, so the
|
||||
# user knows better what went wrong (#6375)
|
||||
set(MISSING_VARS "")
|
||||
set(DETAILS "")
|
||||
# check if all passed variables are valid
|
||||
set(FPHSA_FOUND_${_NAME} TRUE)
|
||||
foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})
|
||||
if(NOT ${_CURRENT_VAR})
|
||||
set(FPHSA_FOUND_${_NAME} FALSE)
|
||||
string(APPEND MISSING_VARS " ${_CURRENT_VAR}")
|
||||
else()
|
||||
string(APPEND DETAILS "[${${_CURRENT_VAR}}]")
|
||||
endif()
|
||||
endforeach()
|
||||
if(FPHSA_FOUND_${_NAME})
|
||||
set(${_NAME}_FOUND TRUE)
|
||||
set(${_NAME_UPPER}_FOUND TRUE)
|
||||
else()
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
set(${_NAME_UPPER}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# component handling
|
||||
unset(FOUND_COMPONENTS_MSG)
|
||||
unset(MISSING_COMPONENTS_MSG)
|
||||
|
||||
if(FPHSA_HANDLE_COMPONENTS)
|
||||
foreach(comp ${${_NAME}_FIND_COMPONENTS})
|
||||
if(${_NAME}_${comp}_FOUND)
|
||||
|
||||
if(NOT DEFINED FOUND_COMPONENTS_MSG)
|
||||
set(FOUND_COMPONENTS_MSG "found components:")
|
||||
endif()
|
||||
string(APPEND FOUND_COMPONENTS_MSG " ${comp}")
|
||||
|
||||
else()
|
||||
|
||||
if(NOT DEFINED MISSING_COMPONENTS_MSG)
|
||||
set(MISSING_COMPONENTS_MSG "missing components:")
|
||||
endif()
|
||||
string(APPEND MISSING_COMPONENTS_MSG " ${comp}")
|
||||
|
||||
if(${_NAME}_FIND_REQUIRED_${comp})
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
string(APPEND MISSING_VARS " ${comp}")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endforeach()
|
||||
set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
|
||||
string(APPEND DETAILS "[c${COMPONENT_MSG}]")
|
||||
endif()
|
||||
|
||||
# version handling:
|
||||
set(VERSION_MSG "")
|
||||
set(VERSION_OK TRUE)
|
||||
|
||||
# check that the version variable is not empty to avoid emitting a misleading
|
||||
# message (i.e. `Found unsuitable version ""`)
|
||||
if (DEFINED ${_NAME}_FIND_VERSION)
|
||||
if(DEFINED ${FPHSA_VERSION_VAR})
|
||||
if(NOT "${${FPHSA_VERSION_VAR}}" STREQUAL "")
|
||||
set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}})
|
||||
if (FPHSA_HANDLE_VERSION_RANGE)
|
||||
set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE)
|
||||
else()
|
||||
set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE)
|
||||
endif()
|
||||
find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG
|
||||
${FPCV_HANDLE_VERSION_RANGE})
|
||||
else()
|
||||
set(VERSION_OK FALSE)
|
||||
endif()
|
||||
endif()
|
||||
if("${${FPHSA_VERSION_VAR}}" STREQUAL "")
|
||||
# if the package was not found, but a version was given, add that to the output:
|
||||
if(${_NAME}_FIND_VERSION_EXACT)
|
||||
set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
|
||||
elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE)
|
||||
set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")")
|
||||
else()
|
||||
set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")")
|
||||
endif()
|
||||
endif()
|
||||
else ()
|
||||
# Check with DEFINED as the found version may be 0.
|
||||
if(DEFINED ${FPHSA_VERSION_VAR})
|
||||
set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if(VERSION_OK)
|
||||
string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]")
|
||||
else()
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
|
||||
# print the result:
|
||||
if (${_NAME}_FOUND)
|
||||
FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
|
||||
else ()
|
||||
|
||||
if(FPHSA_CONFIG_MODE)
|
||||
_FPHSA_HANDLE_FAILURE_CONFIG_MODE()
|
||||
else()
|
||||
if(NOT VERSION_OK)
|
||||
set(RESULT_MSG)
|
||||
if (_FIRST_REQUIRED_VAR)
|
||||
string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}")
|
||||
endif()
|
||||
if (COMPONENT_MSG)
|
||||
if (RESULT_MSG)
|
||||
string (APPEND RESULT_MSG ", ")
|
||||
endif()
|
||||
string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}")
|
||||
endif()
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})")
|
||||
else()
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif ()
|
||||
|
||||
set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)
|
||||
set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
cmake_policy(POP)
|
49
cmake/FindPackageMessage.cmake
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindPackageMessage
|
||||
------------------
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_message(<name> "message for user" "find result details")
|
||||
|
||||
This function is intended to be used in FindXXX.cmake modules files.
|
||||
It will print a message once for each unique find result. This is
|
||||
useful for telling the user where a package was found. The first
|
||||
argument specifies the name (XXX) of the package. The second argument
|
||||
specifies the message to display. The third argument lists details
|
||||
about the find result so that if they change the message will be
|
||||
displayed again. The macro also obeys the QUIET argument to the
|
||||
find_package command.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
if(X11_FOUND)
|
||||
find_package_message(X11 "Found X11: ${X11_X11_LIB}"
|
||||
"[${X11_X11_LIB}][${X11_INCLUDE_DIR}]")
|
||||
else()
|
||||
...
|
||||
endif()
|
||||
#]=======================================================================]
|
||||
|
||||
function(find_package_message pkg msg details)
|
||||
# Avoid printing a message repeatedly for the same find result.
|
||||
if(NOT ${pkg}_FIND_QUIETLY)
|
||||
string(REPLACE "\n" "" details "${details}")
|
||||
set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg})
|
||||
if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}")
|
||||
# The message has not yet been printed.
|
||||
string(STRIP "${msg}" msg)
|
||||
message(STATUS "${msg}")
|
||||
|
||||
# Save the find details in the cache to avoid printing the same
|
||||
# message again.
|
||||
set("${DETAILS_VAR}" "${details}"
|
||||
CACHE INTERNAL "Details about finding ${pkg}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
|
@ -1,7 +1,8 @@
|
|||
# Update the source file dependencies of the pot file.
|
||||
#
|
||||
# This globs all files cpp in the src directory and looks for the text domain
|
||||
# definition in that file and outputs these dependencies in POTFILES.in.
|
||||
# definition in that file and outputs these dependencies in POTFILES_CPP.in.
|
||||
# py, pyw are listed in POTFILES_PY.in
|
||||
|
||||
# Remove the old input file.
|
||||
# Dummy target with a non existing (and not created file) is always executed.
|
||||
|
@ -9,71 +10,29 @@ add_custom_command(
|
|||
OUTPUT ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in.dummy
|
||||
# remove the old file.
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-E remove ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in
|
||||
COMMENT "pot-update [${DOMAIN}]: Removed existing POTFILES.in."
|
||||
-E remove ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES*.in
|
||||
COMMENT "pot-update [${DOMAIN}]: Removing existing POTFILES*.in."
|
||||
)
|
||||
|
||||
# Recreate the input file.
|
||||
if(DOMAIN STREQUAL ${DEFAULT_DOMAIN})
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES_CPP.in
|
||||
|
||||
# For the default text domain.
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in
|
||||
# Write list of matching files to POTFILES_CPP.in.
|
||||
COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/po/FINDCPP ${DOMAIN} --initialdomain ${DEFAULT_DOMAIN} >|
|
||||
${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES_CPP.in
|
||||
|
||||
# Find all cpp files which are not in a .git directory, check their
|
||||
# textdomain, and write the list of matching files to POTFILES.in.
|
||||
COMMAND find src -name .git -prune -o -name '*.[hc]pp' -print |
|
||||
sort |
|
||||
while read file\; do
|
||||
# If the file doesn't contain a GETTEXT_DOMAIN
|
||||
# definition it should be added to the default domain.
|
||||
if ! grep '^\#define *GETTEXT_DOMAIN'
|
||||
$$file > /dev/null 2>&1\; then
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in.dummy
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "pot-update [${DOMAIN}]: Creating POTFILES_CPP.in."
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES_PY.in
|
||||
|
||||
echo $$file \;
|
||||
# Write list of matching files to PY.in.
|
||||
COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/po/FINDPY ${DOMAIN} >|
|
||||
${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES_PY.in
|
||||
|
||||
# While files don't need a GETTEXT_DOMAIN to be included in
|
||||
# the default domain, accept files that contain a
|
||||
# GETTEXT_DOMAIN definition for the default domain too.
|
||||
elif grep '^\#define *GETTEXT_DOMAIN *\"${DOMAIN}\"'
|
||||
$$file > /dev/null 2>&1\; then
|
||||
|
||||
echo $$file \;
|
||||
fi
|
||||
done >|
|
||||
${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in
|
||||
|
||||
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in.dummy
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT
|
||||
"pot-update [${DOMAIN}]: Created POTFILES.in for default domain."
|
||||
)
|
||||
|
||||
else(DOMAIN STREQUAL ${DEFAULT_DOMAIN})
|
||||
|
||||
# For the other text domains.
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in
|
||||
|
||||
# Find all cpp files which are not in a .git directory, check their
|
||||
# textdomain, and write the list of matching files to POTFILES.in.
|
||||
COMMAND find src -name .git -prune -o -name '*cpp' -print |
|
||||
sort |
|
||||
while read file\; do
|
||||
# If the file contains a GETTEXT_DOMAIN definition for
|
||||
# the current domain add it to the domain.
|
||||
if grep '^\#define *GETTEXT_DOMAIN *\"${DOMAIN}\"'
|
||||
$$file > /dev/null 2>&1\; then
|
||||
|
||||
echo $$file \;
|
||||
fi
|
||||
done >|
|
||||
${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in
|
||||
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in.dummy
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "pot-update [${DOMAIN}]: Created POTFILES.in."
|
||||
)
|
||||
|
||||
endif(DOMAIN STREQUAL ${DEFAULT_DOMAIN})
|
||||
DEPENDS ${PROJECT_SOURCE_DIR}/po/${DOMAIN}/POTFILES.in.dummy
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "pot-update [${DOMAIN}]: Creating POTFILES_PY.in."
|
||||
)
|
||||
|
|
18392
copyrights.csv
Normal file
|
@ -33,7 +33,7 @@ local function ca_castle_switch()
|
|||
-- Avoid going through the __cfg above every time the evaluation function
|
||||
-- is called if the castle switch CA does not exist
|
||||
local dummy_castle_switch = {}
|
||||
function dummy_castle_switch:evaluation() return 0 end
|
||||
function dummy_castle_switch:evaluation(cfg, data, filter_own, leader) return 0 end
|
||||
return dummy_castle_switch
|
||||
end
|
||||
|
||||
|
@ -64,8 +64,8 @@ local function get_hp_efficiency(table, recruit_id)
|
|||
local regen_amount = 0
|
||||
if abilities then
|
||||
for regen in wml.child_range(abilities, "regenerate") do
|
||||
if regen.value > regen_amount then
|
||||
regen_amount = regen.value
|
||||
if tonumber(regen.value) and tonumber(regen.value) > regen_amount then
|
||||
regen_amount = tonumber(regen.value)
|
||||
end
|
||||
end
|
||||
effective_hp = effective_hp + (regen_amount * effective_hp/30)
|
||||
|
@ -141,22 +141,22 @@ local function analyze_enemy_unit(enemy_type, ally_type)
|
|||
-- Handle marksman and magical
|
||||
mod = wml.get_child(special, 'chance_to_hit')
|
||||
if mod then
|
||||
if mod.value then
|
||||
if tonumber(mod.value) then
|
||||
if mod.cumulative then
|
||||
if mod.value > defense then
|
||||
defense = mod.value
|
||||
if tonumber(mod.value) > defense then
|
||||
defense = tonumber(mod.value) or 0
|
||||
end
|
||||
else
|
||||
defense = mod.value
|
||||
defense = tonumber(mod.value) or 0
|
||||
end
|
||||
elseif mod.add then
|
||||
defense = defense + mod.add
|
||||
defense = defense + (tonumber(mod.add) or 0)
|
||||
elseif mod.sub then
|
||||
defense = defense - mod.sub
|
||||
defense = defense - (tonumber(mod.sub) or 0)
|
||||
elseif mod.multiply then
|
||||
defense = defense * mod.multiply
|
||||
defense = defense * (tonumber(mod.multiply) or 0)
|
||||
elseif mod.divide then
|
||||
defense = defense / mod.divide
|
||||
defense = defense / (tonumber(mod.divide) or 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -166,17 +166,17 @@ local function analyze_enemy_unit(enemy_type, ally_type)
|
|||
local special_multiplier = 1
|
||||
local special_bonus = 0
|
||||
|
||||
if mod.multiply then
|
||||
special_multiplier = special_multiplier*mod.multiply
|
||||
if tonumber(mod.multiply) then
|
||||
special_multiplier = special_multiplier * tonumber(mod.multiply)
|
||||
end
|
||||
if mod.divide then
|
||||
special_multiplier = special_multiplier/mod.divide
|
||||
if tonumber(mod.divide) and tonumber(mod.divide) ~= 0 then
|
||||
special_multiplier = special_multiplier / tonumber(mod.divide)
|
||||
end
|
||||
if mod.add then
|
||||
special_bonus = special_bonus+mod.add
|
||||
if tonumber(mod.add) then
|
||||
special_bonus = special_bonus + tonumber(mod.add)
|
||||
end
|
||||
if mod.subtract then
|
||||
special_bonus = special_bonus-mod.subtract
|
||||
if tonumber(mod.subtract) then
|
||||
special_bonus = special_bonus - tonumber(mod.subtract)
|
||||
end
|
||||
|
||||
if mod.backstab then
|
||||
|
@ -185,13 +185,13 @@ local function analyze_enemy_unit(enemy_type, ally_type)
|
|||
damage_multiplier = damage_multiplier*(special_multiplier*0.5 + 0.5)
|
||||
damage_bonus = damage_bonus+(special_bonus*0.5)
|
||||
if mod.value then
|
||||
weapon_damage = (weapon_damage+mod.value)/2
|
||||
weapon_damage = (weapon_damage + (tonumber(mod.value) or 0) )/2
|
||||
end
|
||||
else
|
||||
damage_multiplier = damage_multiplier*special_multiplier
|
||||
damage_bonus = damage_bonus+special_bonus
|
||||
if mod.value then
|
||||
weapon_damage = mod.value
|
||||
weapon_damage = tonumber(mod.value) or 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,8 +60,9 @@ return {
|
|||
local regen_amount = 0
|
||||
if abilities then
|
||||
for regen in wml.child_range(abilities, "regenerate") do
|
||||
if regen.value > regen_amount then
|
||||
regen_amount = regen.value
|
||||
local val = tonumber(regen.value) or 0
|
||||
if val > regen_amount then
|
||||
regen_amount = val
|
||||
end
|
||||
end
|
||||
effective_hp = effective_hp + (regen_amount * effective_hp/30)
|
||||
|
@ -133,22 +134,22 @@ return {
|
|||
-- Handle marksman and magical
|
||||
mod = wml.get_child(special, 'chance_to_hit')
|
||||
if mod then
|
||||
if mod.value then
|
||||
if tonumber(mod.value) then
|
||||
if mod.cumulative then
|
||||
if mod.value > defense then
|
||||
defense = mod.value
|
||||
if tonumber(mod.value) > defense then
|
||||
defense = tonumber(mod.value)
|
||||
end
|
||||
else
|
||||
defense = mod.value
|
||||
defense = tonumber(mod.value)
|
||||
end
|
||||
elseif mod.add then
|
||||
defense = defense + mod.add
|
||||
elseif mod.sub then
|
||||
defense = defense - mod.sub
|
||||
elseif mod.multiply then
|
||||
defense = defense * mod.multiply
|
||||
elseif mod.divide then
|
||||
defense = defense / mod.divide
|
||||
elseif tonumber(mod.add) then
|
||||
defense = defense + tonumber(mod.add)
|
||||
elseif tonumber(mod.sub) then
|
||||
defense = defense - tonumber(mod.sub)
|
||||
elseif tonumber(mod.multiply) then
|
||||
defense = defense * tonumber(mod.multiply)
|
||||
elseif tonumber(mod.divide) then
|
||||
defense = defense / tonumber(mod.divide)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -158,17 +159,17 @@ return {
|
|||
local special_multiplier = 1
|
||||
local special_bonus = 0
|
||||
|
||||
if mod.multiply then
|
||||
special_multiplier = special_multiplier*mod.multiply
|
||||
if tonumber(mod.multiply) then
|
||||
special_multiplier = special_multiplier * tonumber(mod.multiply)
|
||||
end
|
||||
if mod.divide then
|
||||
special_multiplier = special_multiplier/mod.divide
|
||||
if tonumber(mod.divide) then
|
||||
special_multiplier = special_multiplier / tonumber(mod.divide)
|
||||
end
|
||||
if mod.add then
|
||||
special_bonus = special_bonus+mod.add
|
||||
if tonumber(mod.add) then
|
||||
special_bonus = special_bonus + tonumber(mod.add)
|
||||
end
|
||||
if mod.subtract then
|
||||
special_bonus = special_bonus-mod.subtract
|
||||
if tonumber(mod.subtract) then
|
||||
special_bonus = special_bonus - tonumber(mod.subtract)
|
||||
end
|
||||
|
||||
if mod.backstab then
|
||||
|
@ -176,14 +177,14 @@ return {
|
|||
-- TODO: find out what actual probability of getting to backstab is
|
||||
damage_multiplier = damage_multiplier*(special_multiplier*0.5 + 0.5)
|
||||
damage_bonus = damage_bonus+(special_bonus*0.5)
|
||||
if mod.value then
|
||||
weapon_damage = (weapon_damage+mod.value)/2
|
||||
if tonumber(mod.value) then
|
||||
weapon_damage = (weapon_damage+tonumber(mod.value))/2
|
||||
end
|
||||
else
|
||||
damage_multiplier = damage_multiplier*special_multiplier
|
||||
damage_bonus = damage_bonus+special_bonus
|
||||
if mod.value then
|
||||
weapon_damage = mod.value
|
||||
if tonumber(mod.value) then
|
||||
weapon_damage = tonumber(mod.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,8 +65,8 @@ function retreat_functions.retreat_injured_units(units, avoid_map)
|
|||
local regen_amount = 0
|
||||
if abilities then
|
||||
for regenerates in wml.child_range(abilities, "regenerate") do
|
||||
if (regenerates.value or 0) > regen_amount then
|
||||
regen_amount = regenerates.value
|
||||
if (tonumber(regenerates.value) or 0) > regen_amount then
|
||||
regen_amount = tonumber(regenerates.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -114,7 +114,7 @@ function retreat_functions.get_healing_locations(possible_healers)
|
|||
local cure = 0
|
||||
local abilities = wml.get_child(u.__cfg, "abilities") or {}
|
||||
for ability in wml.child_range(abilities, "heals") do
|
||||
heal_amount = ability.value or 0
|
||||
heal_amount = (tonumber(ability.value) or 0)
|
||||
if ability.poison == "slowed" then
|
||||
cure = 1
|
||||
elseif ability.poison == "cured" then
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local LS = wesnoth.require "location_set"
|
||||
---@type ai_helper_lib
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
local MAISD = wesnoth.require "ai/micro_ais/micro_ai_self_data.lua"
|
||||
|
@ -26,6 +27,7 @@ local function bottleneck_is_my_territory(map, enemy_map)
|
|||
dummy_unit.x, dummy_unit.y = x, y
|
||||
|
||||
-- Find lowest movement cost to own front-line hexes
|
||||
---@type number, location[]?
|
||||
local min_cost, best_path = math.huge, nil
|
||||
map:iter(function(xm, ym, v)
|
||||
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
|
||||
|
@ -35,6 +37,7 @@ local function bottleneck_is_my_territory(map, enemy_map)
|
|||
end)
|
||||
|
||||
-- And the same to the enemy front line
|
||||
---@type number, location[]?
|
||||
local min_cost_enemy, best_path_enemy = math.huge, nil
|
||||
enemy_map:iter(function(xm, ym, v)
|
||||
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
|
||||
|
@ -321,6 +324,7 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
for _,enemy in ipairs(enemies) do
|
||||
for xa,ya in wesnoth.current.map:iter_adjacent(enemy) do
|
||||
if BD_is_my_territory:get(xa, ya) then
|
||||
---@type unit?
|
||||
local unit_in_way = wesnoth.units.get(xa, ya)
|
||||
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way)) then
|
||||
unit_in_way = nil
|
||||
|
@ -439,6 +443,7 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
BD_bottleneck_moves_done = true
|
||||
else
|
||||
-- If there's another unit in the best location, moving it out of the way becomes the best move
|
||||
---@type unit?
|
||||
local unit_in_way = wesnoth.units.find_on_map { x = best_hex[1], y = best_hex[2],
|
||||
{ "not", { id = best_unit.id } }
|
||||
}[1]
|
||||
|
|
|
@ -108,7 +108,7 @@ function ca_fast_attack_utils.single_unit_info(unit_proxy)
|
|||
if abilities then
|
||||
for _,ability in ipairs(abilities) do
|
||||
if ability[1] == 'regenerate' then
|
||||
single_unit_info[ability[1]] = ability[2].value
|
||||
single_unit_info[ability[1]] = (tonumber(ability[2].value) or 0)
|
||||
else
|
||||
single_unit_info[ability[1]] = true
|
||||
end
|
||||
|
|
|
@ -32,6 +32,7 @@ function ca_forest_animals_tusker_attack:execution(cfg)
|
|||
local adjacent_enemies = get_adjacent_enemies(cfg)
|
||||
|
||||
-- Find the closest enemy to any tusker
|
||||
---@type number, unit?, unit?
|
||||
local min_dist, attacker, target = math.huge, nil, nil
|
||||
for _,tusker in ipairs(tuskers) do
|
||||
for _,enemy in ipairs(adjacent_enemies) do
|
||||
|
|
|
@ -30,6 +30,20 @@ local function is_number(value, check_int, ai_type, name)
|
|||
return true
|
||||
end
|
||||
|
||||
---@alias mai_parm_type
|
||||
---| '"tag"'
|
||||
---| '"float"'
|
||||
---| '"integer"'
|
||||
---| '"float_list"'
|
||||
---| '"integer_list"'
|
||||
---| '"boolean"'
|
||||
---| '"string"'
|
||||
|
||||
---Validates a single key in a Micro AI.
|
||||
---@param k_name string The name of the key.
|
||||
---@param k_value any The value of the key.
|
||||
---@param k_type mai_parm_type The expected type of the key.
|
||||
---@param ai_type string The AI being validated.
|
||||
local function check_key_type(k_name, k_value, k_type, ai_type)
|
||||
if k_value then
|
||||
local is_wrong_type = false
|
||||
|
@ -71,18 +85,20 @@ end
|
|||
|
||||
local micro_ai_helper = {}
|
||||
|
||||
function micro_ai_helper.add_CAs(side, ca_id_core, CA_parms, CA_cfg)
|
||||
-- Add the candidate actions defined in @CA_parms to the AI of @side
|
||||
-- @ca_id_core: ca_id= key from the [micro_ai] tag
|
||||
-- @CA_parms: array of tables, one for each CA to be added (CA setup parameters)
|
||||
-- Also contains one key: ai_id
|
||||
-- @CA_cfg: table with the parameters passed to the eval/exec functions
|
||||
--
|
||||
-- Required keys for each table of @CA_parms:
|
||||
-- - ca_id: is used for CA id/name
|
||||
-- - location: the path+file name for the external CA file
|
||||
-- - score: the evaluation score
|
||||
---@class ca_parm
|
||||
---@field ca_id string Used for CA id/name
|
||||
---@field location string The path+file name for the external CA file
|
||||
---@field score integer The evaluation score
|
||||
|
||||
---@class ca_parms : ca_parm[]
|
||||
---@field ai_id string ID of the AI, potentially used as a prefix for CAs.
|
||||
|
||||
---Add the candidate actions defined in CA_parms to the AI of the specified side
|
||||
---@param side integer The side to modify.
|
||||
---@param ca_id_core string? ca_id= key from the [micro_ai] tag, or nil
|
||||
---@param CA_parms ca_parms Specification of the candidate actions to add.
|
||||
---@param CA_cfg table Table with the parameters passed to the eval/exec functions
|
||||
function micro_ai_helper.add_CAs(side, ca_id_core, CA_parms, CA_cfg)
|
||||
-- About ai_id, ca_id_core and ca_id:
|
||||
-- ai_id: If the AI stores information in the [data] variable, we need to
|
||||
-- ensure that it is uniquely attributed to this AI, and not to a separate
|
||||
|
@ -162,12 +178,11 @@ function micro_ai_helper.add_CAs(side, ca_id_core, CA_parms, CA_cfg)
|
|||
end
|
||||
end
|
||||
|
||||
---Delete the candidate actions defined in CA_parms from the AI of the specified side
|
||||
---@param side integer The side to modify.
|
||||
---@param ca_id_core string The CA prefix to use; can be nil.
|
||||
---@param CA_parms ca_parms Specification of candidate actions to delete.
|
||||
function micro_ai_helper.delete_CAs(side, ca_id_core, CA_parms)
|
||||
-- Delete the candidate actions defined in @CA_parms from the AI of @side
|
||||
-- @ca_id_core: ca_id= key from the [micro_ai] tag
|
||||
-- @CA_parms: array of tables, one for each CA to be removed
|
||||
-- We can simply pass the one used for add_CAs(), although only the
|
||||
-- CA_parms.ca_id field is needed
|
||||
|
||||
-- For CA ids, use value of [micro_ai]ca_id= if given, ai_id otherwise
|
||||
ca_id_core = ca_id_core or CA_parms.ai_id
|
||||
|
@ -185,14 +200,14 @@ function micro_ai_helper.delete_CAs(side, ca_id_core, CA_parms)
|
|||
end
|
||||
end
|
||||
|
||||
---@class aspect_parm
|
||||
---@field aspect string The aspect name (e.g. 'attacks' or 'aggression')
|
||||
---@field facet WMLTable A table describing the facet to be added
|
||||
|
||||
---Add the aspects defined in aspect_parms to the AI of the specified side.
|
||||
---@param side integer The side to modify.
|
||||
---@param aspect_parms aspect_parm[] Specifications of the aspects to add.
|
||||
function micro_ai_helper.add_aspects(side, aspect_parms)
|
||||
-- Add the aspects defined in @aspect_parms to the AI of @side
|
||||
-- @aspect_parms is an array of tables, one for each aspect to be added
|
||||
--
|
||||
-- Required keys for @aspect_parms:
|
||||
-- - aspect: the aspect name (e.g. 'attacks' or 'aggression')
|
||||
-- - facet: A table describing the facet to be added
|
||||
--
|
||||
-- Examples of facets:
|
||||
-- 1. Simple aspect, e.g. aggression
|
||||
-- { value = 0.99 }
|
||||
|
@ -201,9 +216,9 @@ function micro_ai_helper.add_aspects(side, aspect_parms)
|
|||
-- { name = "ai_default_rca::aspect_attacks",
|
||||
-- id = "dont_attack",
|
||||
-- invalidate_on_gamestate_change = "yes",
|
||||
-- { "filter_own", {
|
||||
-- wml.tag.filter_own {
|
||||
-- type = "Dark Sorcerer"
|
||||
-- } }
|
||||
-- }
|
||||
-- }
|
||||
|
||||
for _,parms in ipairs(aspect_parms) do
|
||||
|
@ -211,17 +226,20 @@ function micro_ai_helper.add_aspects(side, aspect_parms)
|
|||
end
|
||||
end
|
||||
|
||||
---Delete the aspects defined in aspect_parms from the AI of the specified side.
|
||||
---@param side integer The side to modify.
|
||||
---@param aspect_parms aspect_parm[] The aspects to delete.
|
||||
function micro_ai_helper.delete_aspects(side, aspect_parms)
|
||||
-- Delete the aspects defined in @aspect_parms from the AI of @side
|
||||
-- @aspect_parms is an array of tables, one for each aspect to be removed
|
||||
-- We can simply pass the one used for add_aspects(), although only the
|
||||
-- aspect_parms.aspect_id field is needed
|
||||
|
||||
for _,parms in ipairs(aspect_parms) do
|
||||
wesnoth.sides.delete_ai_component(side, "aspect[attacks].facet[" .. parms.facet.id .. "]")
|
||||
end
|
||||
end
|
||||
|
||||
---Perform basic setup for a Micro AI.
|
||||
---@param cfg WML The contents of the [micro_ai] tag.
|
||||
---@param CA_parms ca_parms Specification of candidate actions to add or remove.
|
||||
---@param required_keys table<string,mai_parm_type> Specification of required keys.
|
||||
---@param optional_keys table<string,mai_parm_type> Specification of optional keys.
|
||||
function micro_ai_helper.micro_ai_setup(cfg, CA_parms, required_keys, optional_keys)
|
||||
if (cfg.action == 'delete') then
|
||||
micro_ai_helper.delete_CAs(cfg.side, cfg.ca_id, CA_parms)
|
||||
|
|
|
@ -15,14 +15,17 @@
|
|||
|
||||
local micro_ai_self_data = {}
|
||||
|
||||
---Modify data [micro_ai] tags
|
||||
---@param self_data WMLTable The AI's self data
|
||||
---@param ai_id string The id of the Micro AI
|
||||
---@param action # The action to perform
|
||||
---| '"delete"'
|
||||
---| '"set"'
|
||||
---| '"insert"'
|
||||
---@param vars_table table<string,string|boolean|number> Table of key=value pairs with the variables to be set or inserted.
|
||||
---If this is set for action="delete", then only the keys in vars_table are deleted,
|
||||
---otherwise the entire [micro_ai] tag is deleted
|
||||
function micro_ai_self_data.modify_mai_self_data(self_data, ai_id, action, vars_table)
|
||||
-- Modify data [micro_ai] tags
|
||||
-- @ai_id (string): the id of the Micro AI
|
||||
-- @action (string): "delete", "set" or "insert"
|
||||
-- @vars_table: table of key=value pairs with the variables to be set or inserted
|
||||
-- if this is set for @action="delete", then only the keys in @vars_table are deleted,
|
||||
-- otherwise the entire [micro_ai] tag is deleted
|
||||
|
||||
-- Always delete the respective [micro_ai] tag, if it exists
|
||||
local existing_table
|
||||
for i,mai in ipairs(self_data, "micro_ai") do
|
||||
|
@ -70,12 +73,15 @@ function micro_ai_self_data.set_mai_self_data(self_data, ai_id, vars_table)
|
|||
micro_ai_self_data.modify_mai_self_data(self_data, ai_id, "set", vars_table)
|
||||
end
|
||||
|
||||
---Get the content of the data [micro_ai] tag for the given ai_id
|
||||
---@param self_data WMLTable The AI's self data
|
||||
---@param ai_id string The id of the Micro AI
|
||||
---@param key? string Specific key to search for
|
||||
---@return boolean|string|number|WMLTag|nil result value of key, or table of data, or nil if key not found
|
||||
--- - If tag is found: value of key if key parameter is given, otherwise
|
||||
--- table of key=value pairs (including the ai_id key)
|
||||
--- - If no such tag is found: nil (if key is set), otherwise empty table
|
||||
function micro_ai_self_data.get_mai_self_data(self_data, ai_id, key)
|
||||
-- Get the content of the data [micro_ai] tag for the given @ai_id
|
||||
-- Return value:
|
||||
-- - If tag is found: value of key if @key parameter is given, otherwise
|
||||
-- table of key=value pairs (including the ai_id key)
|
||||
-- - If no such tag is found: nil (if @key is set), otherwise empty table
|
||||
|
||||
for mai in wml.child_range(self_data, "micro_ai") do
|
||||
if (mai.ai_id == ai_id) then
|
||||
|
|
|
@ -216,7 +216,7 @@
|
|||
[message]
|
||||
speaker=narrator
|
||||
image=wesnoth-icon.png
|
||||
message= _ "This is a reenactment of scenario The Elves Besieged of the mainline campaign Heir to the Throne, just that the AI is playing Konrad's side here. The goal is to move Konrad to the signpost in the northwest, while keeping both Konrad and Delfador alive. The same AI as in scenario Protect Unit is used.
|
||||
message= _ "This is a reenactment of scenario The Elves Besieged of the mainline campaign Heir to the Throne, just that the AI is playing Konrad’s side here. The goal is to move Konrad to the signpost in the northwest, while keeping both Konrad and Delfador alive. The same AI as in scenario Protect Unit is used.
|
||||
|
||||
Note: The Protect Unit AI is coded as a Micro AI. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
[/message]
|
||||
|
@ -244,7 +244,7 @@ Note: The Protect Unit AI is coded as a Micro AI. A Micro AI can be added and ad
|
|||
# wmllint: local spellings HttT
|
||||
[message]
|
||||
speaker=Delfador
|
||||
message= _ "In HttT, we would travel north now, and try to make it to the Isle of Alduin. But for this demo campaign, we'll call it good here."
|
||||
message= _ "In HttT, we would travel north now, and try to make it to the Isle of Alduin. But for this demo campaign, we’ll call it good here."
|
||||
[/message]
|
||||
|
||||
# So that game goes on to next scenario
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
#define ANIMAL_AI_DESCRIPTIONS1
|
||||
_"<u>General</u>: These AIs are set up to simulate (to some extent) how these animals behave in real life. This includes that they are animals, meaning that they are not super smart. As an example, the wolves generally hunt in a pack, but are easily distracted by prey coming into range. They are also decent, but not great at cornering deer. For the most part, this is intentional.
|
||||
|
||||
<u>Bears, Spiders and Yetis</u> mostly just wander in their respective parts of the map. They stay out of each other's way (and out of the way of the dogs), but do attack each other if cornered. They attack the other, weaker animals (deer etc.) if those are within range.
|
||||
<u>Bears, Spiders and Yetis</u> mostly just wander in their respective parts of the map. They stay out of each other’s way (and out of the way of the dogs), but do attack each other if cornered. They attack the other, weaker animals (deer etc.) if those are within range.
|
||||
|
||||
<u>Wolves</u> hunt in a pack. They actively go after the closest deer (as long as it stays in the forest) and try to corner it (not always super successfully), but are easily distracted by other prey coming into range. The wolves try to avoid getting into the range of bears, spiders, dogs and the yeti, except when they are going in for an attack. If you let them move for long enough, they will also learn that it is not healthy to attack the tusklets. When no deer is left, they wander randomly. Note that, unlike the Wolves Multipack AI (used in a different scenario), this Wolves AI combines all wolves of the side in the same pack."
|
||||
#enddef
|
||||
|
||||
#define ANIMAL_AI_DESCRIPTIONS2
|
||||
_"Each <u>Deer (replaced by Vampire Bats)</u> wanders randomly on forest tiles, except when enemies get in its (the deer's) range, in which case it flees to the farthest point it can reach.
|
||||
_"Each <u>Deer (replaced by Vampire Bats)</u> wanders randomly on forest tiles, except when enemies get in its (the deer’s) range, in which case it flees to the farthest point it can reach.
|
||||
|
||||
<u>Tuskers (Woodland Boars)</u> exhibit the same behavior as deer, except when an enemy is next to one of the tusklets. This enemy will then experience the full wrath of an irate boar.
|
||||
|
||||
|
@ -366,12 +366,12 @@
|
|||
# wmllint: unbalanced-on
|
||||
message= _ "It is possible to include a human-controlled Side 1, so that the action stops once every turn for looking around (or to mess with things in debug mode).
|
||||
|
||||
Note that there is no end to this scenario. For demonstration purposes, any unit that is killed is replaced by another unit of the same type at the beginning of the next turn. In order to end the scenario, there's a right-click option - but that only works in human-controlled mode. In AI-only mode, you have to press 'Esc' or reload a previous savefile.
|
||||
Note that there is no end to this scenario. For demonstration purposes, any unit that is killed is replaced by another unit of the same type at the beginning of the next turn. In order to end the scenario, there’s a right-click option — but that only works in human-controlled mode. In AI-only mode, you have to press ‘Esc’ or reload a previous savefile.
|
||||
|
||||
Also note: The Animal AIs are coded as Micro AIs. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information." # wmllint: no spellcheck
|
||||
# wmllint: unbalanced-off
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll just watch the animals.</span>"
|
||||
label= _ "<span font='16'>I’ll just watch the animals.</span>"
|
||||
[command]
|
||||
[end_turn]
|
||||
[/end_turn]
|
||||
|
|
|
@ -90,13 +90,14 @@
|
|||
[message]
|
||||
speaker=Big Bad Orc
|
||||
image=$profile~FL()~RIGHT()
|
||||
message= _ "They there! We them get!"
|
||||
# po: This is orcish speech, deliberately omitting the "are" part of "They're"
|
||||
message= _ "They there! We them get!"
|
||||
[/message]
|
||||
{CLEAR_VARIABLE profile}
|
||||
# wmllint: unbalanced-on
|
||||
[message]
|
||||
speaker=LuaAI
|
||||
message= _ "We need to hold that pass for as long as we can. Let's put our strongest fighters on the front line and bring injured units to the back for healing. If we're careful enough, we might even win this battle. I'll join you as soon as I'm done recruiting and do my share of the fighting.
|
||||
message= _ "We need to hold that pass for as long as we can. Let’s put our strongest fighters on the front line and bring injured units to the back for healing. If we’re careful enough, we might even win this battle. I’ll join you as soon as I’m done recruiting and do my share of the fighting.
|
||||
|
||||
Note: The Bottleneck Defense AI is coded as a Micro AI. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
[/message]
|
||||
|
@ -109,10 +110,10 @@ Note: The Bottleneck Defense AI is coded as a Micro AI. A Micro AI can be added
|
|||
image=wesnoth-icon.png
|
||||
message= _ "In this scenario, the AI playing the humans in the east is instructed to form a defensive line at the pass and hold off the orcs for as long as possible. Do you want to play the orc side or let the default (RCA) AI do that?"
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll watch the two AIs fight it out.</span>"
|
||||
label= _ "<span font='16'>I’ll watch the two AIs fight it out.</span>"
|
||||
[/option]
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll play the orcs.</span>"
|
||||
label= _ "<span font='16'>I’ll play the orcs.</span>"
|
||||
[command]
|
||||
[modify_side]
|
||||
side=2
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
{SET_LABEL 40 5 _"End Scenario"}
|
||||
|
||||
{PLACE_IMAGE "scenery/signpost.png" 3 15}
|
||||
{SET_LABEL 3 15 _"Rowck's Home"}
|
||||
{SET_LABEL 3 15 _"Rowck’s Home"}
|
||||
|
||||
[unit]
|
||||
id=Rowck
|
||||
|
@ -82,7 +82,7 @@
|
|||
{STORE_UNIT_VAR (id=Dreadful Bat) profile profile}
|
||||
[message]
|
||||
speaker=Dreadful Bat
|
||||
message= _ "Be careful to stay out of the way of that dragon. He's a mean one."
|
||||
message= _ "Be careful to stay out of the way of that dragon. He’s a mean one."
|
||||
[/message]
|
||||
{CLEAR_VARIABLE profile}
|
||||
# wmllint: unbalanced-on
|
||||
|
@ -146,7 +146,7 @@ Note: The Hunter AI is coded as a Micro AI. A Micro AI can be added and adapted
|
|||
|
||||
[message]
|
||||
speaker=Dreadful Bat
|
||||
message= _ "I'm out of here."
|
||||
message= _ "I’m out of here."
|
||||
[/message]
|
||||
|
||||
[endlevel]
|
||||
|
|
|
@ -488,7 +488,7 @@ Note: This is a demonstration of how the Goto Micro AI can be used to code guard
|
|||
[/message]
|
||||
[message]
|
||||
speaker=messenger1
|
||||
message= _ "Don't worry, those are really shy ghosts. Nobody knows why, but they always appear in the north this time of year and move through to the south. Also, very unusually for ghosts, they seem to be scared of everybody and avoid other units as much as possible -- except for the bats and saurians, with which they seem to get along just fine for some reason. So as long as we don't corner them, they'll leave us alone."
|
||||
message= _ "Don’t worry, those are really shy ghosts. Nobody knows why, but they always appear in the north this time of year and move through to the south. Also, very unusually for ghosts, they seem to be scared of everybody and avoid other units as much as possible -- except for the bats and saurians, with which they seem to get along just fine for some reason. So as long as we don’t corner them, they’ll leave us alone."
|
||||
[/message]
|
||||
[event]
|
||||
name=side 8 turn
|
||||
|
@ -547,11 +547,11 @@ Note: This is a demonstration of how the Goto Micro AI can be used to code guard
|
|||
|
||||
[message]
|
||||
speaker=messenger2
|
||||
message= _ "I'll take the route straight through the mountains. It's much shorter than following the trail all the way around in the south."
|
||||
message= _ "I’ll take the route straight through the mountains. It’s much shorter than following the trail all the way around in the south."
|
||||
[/message]
|
||||
[message]
|
||||
speaker=messenger1
|
||||
message= _ "You're crazy. It might be shorter as the crow flies, but it will take you forever to get through those mountains.
|
||||
message= _ "You’re crazy. It might be shorter as the crow flies, but it will take you forever to get through those mountains.
|
||||
|
||||
Note: The messengers are controlled by Goto Micro AI definitions that differ by a single line, 'use_straight_line=yes/no'." # wmllint: no spellcheck
|
||||
[/message]
|
||||
|
@ -606,7 +606,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
|
||||
[message]
|
||||
speaker=leader6
|
||||
message= _ "Hah, that's that pencil pusher Gadoc for you! Men, I want you to move toward the River Fort as well, but it's good enough for one of you to report to Sergeant Aethubry. Once that has happened, the rest of you can head into battle directly."
|
||||
message= _ "Hah, that’s that pencil pusher Gadoc for you! Men, I want you to move toward the River Fort as well, but it’s good enough for one of you to report to Sergeant Aethubry. Once that has happened, the rest of you can head into battle directly."
|
||||
[/message]
|
||||
[/event]
|
||||
|
||||
|
@ -624,7 +624,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
|
||||
[message]
|
||||
speaker=unit
|
||||
message= _ "$unit.language_name $unit.name from Lieutenant Gadoc's squadron reporting for duty, sir."
|
||||
message= _ "$unit.language_name $unit.name from Lieutenant Gadoc’s squadron reporting for duty, sir."
|
||||
[/message]
|
||||
|
||||
[fire_event]
|
||||
|
@ -660,7 +660,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
|
||||
[message]
|
||||
speaker=fort_commander
|
||||
message= _ "Maybe if I ignore them, they'll just go away?"
|
||||
message= _ "Maybe if I ignore them, they’ll just go away?"
|
||||
[/message]
|
||||
[/event]
|
||||
[/event]
|
||||
|
@ -679,7 +679,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
|
||||
[message]
|
||||
speaker=unit
|
||||
message= _ "Lieutenant Senvan's men reporting for duty, sir."
|
||||
message= _ "Lieutenant Senvan’s men reporting for duty, sir."
|
||||
[/message]
|
||||
[message]
|
||||
speaker=fort_commander
|
||||
|
@ -691,9 +691,9 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
[/message]
|
||||
[message]
|
||||
speaker=fort_commander
|
||||
message= _ "Well, that's a relief. Somebody knows how to use his ... I mean, very well, off you go then.
|
||||
message= _ "Well, that’s a relief. Somebody knows how to use his ... I mean, very well, off you go then.
|
||||
|
||||
" + {WHISPER _"I wouldn't say anything negative about one of my superior officers now, would I?"}
|
||||
" + {WHISPER _"I wouldn’t say anything negative about one of my superior officers now, would I?"}
|
||||
[/message]
|
||||
[/event]
|
||||
|
||||
|
@ -731,7 +731,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
[message]
|
||||
side=7
|
||||
type=Vampire Bat
|
||||
message= _ "We've all made it to the corners, let's get back together again."
|
||||
message= _ "We’ve all made it to the corners, let’s get back together again."
|
||||
[/message]
|
||||
|
||||
# Send the bats back to their starting point
|
||||
|
@ -772,7 +772,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
[message]
|
||||
side=7
|
||||
type=Vampire Bat
|
||||
message= _ "We're back together, let's head out to the corners again."
|
||||
message= _ "We’re back together, let’s head out to the corners again."
|
||||
[/message]
|
||||
|
||||
# Make the bats go to the corners of the map
|
||||
|
|
|
@ -414,7 +414,7 @@ separate attack Zone"
|
|||
speaker=narrator
|
||||
image=portraits/trolls/troll.png
|
||||
caption= _ "Return Guardian"
|
||||
message= _ "A 'return guardian' is a variation of the standard Wesnoth guardian. It has an assigned guard position (GP) to which it returns after attacks on approaching enemies:
|
||||
message= _ "A ‘return guardian’ is a variation of the standard Wesnoth guardian. It has an assigned guard position (GP) to which it returns after attacks on approaching enemies:
|
||||
- If at GP with no enemy in reach, do nothing.
|
||||
- If at GP with enemy in reach, leave attack to default AI (note that this may include not attacking if the enemy is deemed too strong).
|
||||
- If not at GP, return there, no matter whether an enemy is in reach or not.
|
||||
|
@ -435,9 +435,9 @@ separate attack Zone"
|
|||
speaker=narrator
|
||||
image=portraits/undead/archer.webp
|
||||
caption= _ "Stationed Guardian"
|
||||
message= _ "A 'stationed guardian' is another variation of the standard Wesnoth guardian with a somewhat more complex behavior than that of the 'return guardian'. Two positions are defined for it, a 'station' and a 'guarded location', as well as a 'distance'. The behavior is as follows:
|
||||
- If no enemy is within 'distance' of the guard's current position, do nothing.
|
||||
- Otherwise: If an enemy is within 'distance' of the guard, but not also within the same distance of the guarded location and the station (all of this simultaneously), move the guard in the direction of the station.
|
||||
message= _ "A ‘stationed guardian’ is another variation of the standard Wesnoth guardian with a somewhat more complex behavior than that of the ‘return guardian’. Two positions are defined for it, a ‘station’ and a ‘guarded location’, as well as a ‘distance’. The behavior is as follows:
|
||||
- If no enemy is within ‘distance’ of the guard’s current position, do nothing.
|
||||
- Otherwise: If an enemy is within ‘distance’ of the guard, but not also within the same distance of the guarded location and the station (all of this simultaneously), move the guard in the direction of the station.
|
||||
- Otherwise:
|
||||
- Pick the enemy unit that is closest to the guarded location.
|
||||
- If we can reach it, pick the adjacent hex with the highest defense rating and attack from there.
|
||||
|
@ -457,11 +457,11 @@ separate attack Zone"
|
|||
speaker=narrator
|
||||
image=units/monsters/giant-rat.png
|
||||
caption= _ "Coward"
|
||||
message= _ "Cowards are units that, like guardians, sit around doing nothing until an enemy comes into range. Unlike guardians, however, they run away once enemies approach. Applications might be wild animals, unarmed civilians getting in the way of a battle, etc. The coward macro can be called with two optional locations, 'seek' and 'avoid':
|
||||
message= _ "Cowards are units that, like guardians, sit around doing nothing until an enemy comes into range. Unlike guardians, however, they run away once enemies approach. Applications might be wild animals, unarmed civilians getting in the way of a battle, etc. The coward macro can be called with two optional locations, ‘seek’ and ‘avoid’:
|
||||
- If neither is given, the coward retreats to the position farthest away from the approaching enemies.
|
||||
- If 'seek' is given, it preferentially goes toward that location (but getting away from enemies takes priority).
|
||||
- If 'avoid' is given, it in addition tries to avoid that location (with both maximizing distance from enemies and going toward 'seek' taking priority).
|
||||
- Both 'seek' and 'avoid' may consist of only one coordinate ('x' or 'y'), in which case not a single hex, but a line of hexes is sought or avoided."
|
||||
- If ‘seek’ is given, it preferentially goes toward that location (but getting away from enemies takes priority).
|
||||
- If ‘avoid’ is given, it in addition tries to avoid that location (with both maximizing distance from enemies and going toward ‘seek’ taking priority).
|
||||
- Both ‘seek’ and ‘avoid’ may consist of only one coordinate (‘x’ or ‘y’), in which case not a single hex, but a line of hexes is sought or avoided."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -496,15 +496,15 @@ separate attack Zone"
|
|||
[/message]
|
||||
[message]
|
||||
speaker=Another Bad Orc
|
||||
message= _ "They there! We them get!"
|
||||
message= _ "They there! We them get!"
|
||||
[/message]
|
||||
# wmllint: unbalanced-on
|
||||
[message]
|
||||
speaker=Kraa
|
||||
image=$profile~FL()~RIGHT()
|
||||
message= _ "Gryphons of the High Plains, look at all these enemies. They don't behave normally. Most of them don't move at all unless we get close. Let's check out how they react to us.
|
||||
message= _ "Gryphons of the High Plains, look at all these enemies. They don’t behave normally. Most of them don’t move at all unless we get close. Let’s check out how they react to us.
|
||||
|
||||
Note to the player: the right-click context menu provides information about each of the units' behavior.
|
||||
Note to the player: the right-click context menu provides information about each of the units’ behavior.
|
||||
|
||||
Another note: Most of the Guardian AIs are coded as Micro AIs. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
[/message]
|
||||
|
|
|
@ -117,14 +117,14 @@
|
|||
# wmllint: unbalanced-on
|
||||
[message]
|
||||
speaker=Good Bandit
|
||||
message= _ "That outlaw over there is going to run for the keep in the southeast. He's only going to recruit for three rounds before he'll start moving and he and his footpads are much faster than we are. Let's make haste or we'll never catch him.
|
||||
message= _ "That outlaw over there is going to run for the keep in the southeast. He’s only going to recruit for three rounds before he’ll start moving and he and his footpads are much faster than we are. Let’s make haste or we’ll never catch him.
|
||||
|
||||
Note: This scenario uses a combination of two Micro AIs, the Hang Out Micro AI which makes the Side 2 units remain around the keep for two turns (while moving off castle tiles to allow for recruiting) and the Messenger Escort AI which takes over after that. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
Note: This scenario uses a combination of two Micro AIs, the Hang Out Micro AI which makes the Side 2 units remain around the keep for two turns (while moving off castle tiles to allow for recruiting) and the Messenger Escort AI which takes over after that. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
[/message]
|
||||
# wmllint: unbalanced-off
|
||||
|
||||
[objectives]
|
||||
summary= _ "Get into the outlaw's way before he can make it to the south-eastern keep"
|
||||
summary= _ "Get into the outlaw’s way before he can make it to the south-eastern keep"
|
||||
[objective]
|
||||
description= _ "Death of Bad Outlaw"
|
||||
condition=win
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
speaker=Rebels1
|
||||
message= _ "In this scenario, we demonstrate the use of the Healer Support Micro AI. This AI configures the healers of a side to stay behind the battle lines and heal injured and/or threatened units rather than participate in the attacks under all circumstances. It includes several configurable options (which are set differently for the two sides in this scenario) that determine how aggressive/careful the healers are, whether they also attack, how much risk they are willing to take, etc.
|
||||
|
||||
For clarity, each healer announces her upcoming support move. If you don't want to see that each time, just hit 'esc' when it happens the first time.
|
||||
For clarity, each healer announces her upcoming support move. If you don’t want to see that each time, just hit ‘esc’ when it happens the first time.
|
||||
|
||||
Note: The Healer Support AI is coded as a Micro AI. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information."
|
||||
[/message]
|
||||
|
@ -97,7 +97,7 @@ Note: The Healer Support AI is coded as a Micro AI. A Micro AI can be added and
|
|||
|
||||
[message]
|
||||
speaker=$unit.id
|
||||
message= _ "Argh! They got us..."
|
||||
message= _ "Argh! They got us..."
|
||||
[/message]
|
||||
|
||||
# So that game goes on to next scenario
|
||||
|
|
|
@ -402,7 +402,7 @@
|
|||
speaker=Pekzs
|
||||
message= _ "In this scenario we demonstrate the Lurker Micro AI. A lurker is a unit that is capable of moving across most terrains, but that only stops on and attacks from specific terrain. It might also have the ability to hide on this terrain (which is the reason why this is called the Lurker AI).
|
||||
|
||||
Lurkers move individually without any strategy and always attack the weakest enemy within their reach. If no enemy is in reach, the lurker does a random move instead - or it just sits and waits (lurks)."
|
||||
Lurkers move individually without any strategy and always attack the weakest enemy within their reach. If no enemy is in reach, the lurker does a random move instead — or it just sits and waits (lurks)."
|
||||
[/message]
|
||||
|
||||
# wmllint: unbalanced-on
|
||||
|
|
|
@ -139,19 +139,20 @@
|
|||
[message]
|
||||
speaker=Vanak
|
||||
image=$profile~FL()~RIGHT()
|
||||
message= _ "They there! We them get!"
|
||||
# po: This is orcish speech, deliberately omitting the "are" part of "They're"
|
||||
message= _ "They there! We them get!"
|
||||
[/message]
|
||||
{CLEAR_VARIABLE profile}
|
||||
[message]
|
||||
speaker=messenger
|
||||
message= _ "Men, I need to get to that signpost in the north, to get the message to our leader. Let's head up there as quickly as we can."
|
||||
message= _ "Men, I need to get to that signpost in the north, to get the message to our leader. Let’s head up there as quickly as we can."
|
||||
[/message]
|
||||
# wmllint: unbalanced-on
|
||||
[message]
|
||||
speaker=narrator
|
||||
image=wesnoth-icon.png
|
||||
caption= _ "Notes"
|
||||
message= _ "The Messenger Escort AI will try to move the dragoon messenger to the signpost in the north, while protecting him as well as possible with the other units. Vanak's orcs need to stop him.
|
||||
message= _ "The Messenger Escort AI will try to move the dragoon messenger to the signpost in the north, while protecting him as well as possible with the other units. Vanak’s orcs need to stop him.
|
||||
|
||||
Note that the messenger route is set up through a series of waypoints here simply to demonstrate how to use waypoints. On this map, using only a single waypoint at the end of the route would work just as well (or probably even better).
|
||||
|
||||
|
@ -188,7 +189,7 @@ The Messenger Escort AI is coded as a Micro AI. A Micro AI can be added and adap
|
|||
|
||||
[message]
|
||||
speaker=messenger
|
||||
message= _ "I made it! Now our people will be safe."
|
||||
message= _ "I made it! Now our people will be safe."
|
||||
[/message]
|
||||
[endlevel]
|
||||
result=defeat
|
||||
|
@ -203,7 +204,7 @@ The Messenger Escort AI is coded as a Micro AI. A Micro AI can be added and adap
|
|||
[/filter]
|
||||
[message]
|
||||
speaker=messenger
|
||||
message= _ "Nooo! All is lost. We will never stop the orcs now!"
|
||||
message= _ "Nooo! All is lost. We will never stop the orcs now!"
|
||||
[/message]
|
||||
|
||||
# So that game goes on to next scenario
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
[message]
|
||||
speaker=Konrad
|
||||
image=$profile~FL()~RIGHT()
|
||||
message= _ "Hello! I'm a Konrad impostor. We are going to demonstrate the Patrol AI to you in this scenario.
|
||||
message= _ "Hello! I’m a Konrad impostor. We are going to demonstrate the Patrol AI to you in this scenario.
|
||||
|
||||
I am heading for the keep east of the central mountain via a couple waypoints in the south. I will stay there once I get there. By contrast, those two fellas in the center are perpetually circling the mountain, one of them always in the same direction, the other changing directions after every lap.
|
||||
|
||||
|
@ -222,7 +222,7 @@ All of this is implemented by use of the Patrol [micro_ai] tag."
|
|||
{CLEAR_VARIABLE profile}
|
||||
[message]
|
||||
speaker=guard1
|
||||
message= _ "By contrast, I am a zone guardian patrolling, in a way, the southernmost part of the map. This AI is implemented via the Guardian [micro_ai] tag. It is here mostly to demonstrate how to set up different Micro AIs for the same side. For more details on different types of guardian AIs, there is a separate test scenario specializing on those."}
|
||||
message= _ "By contrast, I am a zone guardian patrolling, in a way, the southernmost part of the map. This AI is implemented via the Guardian [micro_ai] tag. It is here mostly to demonstrate how to set up different Micro AIs for the same side. For more details on different types of guardian AIs, there is a separate test scenario specializing on those."}
|
||||
[/message]
|
||||
[message]
|
||||
speaker=Urudin
|
||||
|
@ -236,7 +236,7 @@ This is an AI separate from the Patrols of Side 2."
|
|||
speaker=narrator
|
||||
image=wesnoth-icon.png
|
||||
caption= _ "Notes"
|
||||
message= _ "You, as the player, are in charge of Gertburt's bandits in this scenario. You can either simply watch the patrols move around, or you can move units into their way. The three patrol units are instructed to behave differently when facing enemy units:
|
||||
message= _ "You, as the player, are in charge of Gertburt’s bandits in this scenario. You can either simply watch the patrols move around, or you can move units into their way. The three patrol units are instructed to behave differently when facing enemy units:
|
||||
|
||||
Konrad only attacks Gertburt, or any enemy unit that blocks his final waypoint.
|
||||
|
||||
|
@ -278,7 +278,7 @@ The Patrol AI controlling all Side 2 units is coded as a Micro AI. A Micro AI ca
|
|||
|
||||
[message]
|
||||
speaker=Konrad
|
||||
message= _ "Well, that was fun! I'll just hang out here now and watch those two guys walk and walk and ..."
|
||||
message= _ "Well, that was fun! I’ll just hang out here now and watch those two guys walk and walk and ..."
|
||||
[/message]
|
||||
[/event]
|
||||
|
||||
|
@ -322,7 +322,7 @@ The Patrol AI controlling all Side 2 units is coded as a Micro AI. A Micro AI ca
|
|||
|
||||
[message]
|
||||
speaker=Gertburt
|
||||
message= _ "Let's go home, chaps."
|
||||
message= _ "Let’s go home, chaps."
|
||||
[/message]
|
||||
|
||||
[endlevel]
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
[message]
|
||||
speaker=Koorzhar
|
||||
image="$profile~FL()~RIGHT()"
|
||||
message= _ "There's that traitor wizard. Let's get him."
|
||||
message= _ "There’s that traitor wizard. Let’s get him."
|
||||
[/message]
|
||||
{CLEAR_VARIABLE profile}
|
||||
[message]
|
||||
|
@ -142,11 +142,11 @@
|
|||
[/message]
|
||||
[message]
|
||||
speaker=Rossauba
|
||||
message= _ "That's very kind of you, but ..."
|
||||
message= _ "That’s very kind of you, but ..."
|
||||
[/message]
|
||||
[message]
|
||||
speaker=Langzhar
|
||||
message= _ "No buts! You stay behind the lines and do not engage in battle unless there is no risk to your life, is that understood? And get to that signpost in the northwest if it is safe."
|
||||
message= _ "No buts! You stay behind the lines and do not engage in battle unless there is no risk to your life, is that understood? And get to that signpost in the northwest if it is safe."
|
||||
[/message]
|
||||
|
||||
[message]
|
||||
|
@ -154,15 +154,15 @@
|
|||
caption= _ "Question for the Player"
|
||||
image=wesnoth-icon.png
|
||||
# wmllint: unbalanced-on
|
||||
message= _ "In this scenario, the AI playing the humans in the east (Langzhar) is instructed to protect the wizard Rossauba, while moving him safely to the signpost. On the other side, Koorzhar's units (in the west) will primarily attack Rossauba, even if a better target is available. Do you want to play either of the sides or let the AIs battle it out among themselves?
|
||||
message= _ "In this scenario, the AI playing the humans in the east (Langzhar) is instructed to protect the wizard Rossauba, while moving him safely to the signpost. On the other side, Koorzhar’s units (in the west) will primarily attack Rossauba, even if a better target is available. Do you want to play either of the sides or let the AIs battle it out among themselves?
|
||||
|
||||
Note: The Protect Unit AI is coded as a Micro AI. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information." # wmllint: no spellcheck
|
||||
# wmllint: unbalanced-off
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll watch the two AIs fight it out</span>" # wmllint: no spellcheck
|
||||
label= _ "<span font='16'>I’ll watch the two AIs fight it out</span>" # wmllint: no spellcheck
|
||||
[/option]
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll play Langzhar's side (to see how Koorzhar's units target Rossauba)</span>"
|
||||
label= _ "<span font='16'>I’ll play Langzhar’s side (to see how Koorzhar’s units target Rossauba)</span>"
|
||||
[command]
|
||||
[modify_side]
|
||||
side=1
|
||||
|
@ -171,7 +171,7 @@ Note: The Protect Unit AI is coded as a Micro AI. A Micro AI can be added and ad
|
|||
[/command]
|
||||
[/option]
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll play Koorzhar's side (to see how Langzhar's units protect Rossauba)</span>"
|
||||
label= _ "<span font='16'>I’ll play Koorzhar’s side (to see how Langzhar’s units protect Rossauba)</span>"
|
||||
[command]
|
||||
[modify_side]
|
||||
side=2
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "Guardian Micro AI demo"
|
||||
message= _ "In 'Guardians', several variations of the standard Wesnoth guardian are shown, including a ""coward"" unit that runs away from any approaching unit (an ""inverse guardian"", in a way)."
|
||||
message= _ "In ‘Guardians’, several variations of the standard Wesnoth guardian are shown, including a ""coward"" unit that runs away from any approaching unit (an ""inverse guardian"", in a way)."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -166,7 +166,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "Patrol Micro AI demo"
|
||||
message= _ "'Patrols' contains AI modifications for units following patrol routes."
|
||||
message= _ "‘Patrols’ contains AI modifications for units following patrol routes."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -224,7 +224,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "HttT: The Elves Besieged Micro AI demo"
|
||||
message= _ "This is a reenactment of scenario ""The Elves Besieged"" of the mainline campaign ""Heir to the Throne"", just that the AI is playing Konrad's side here. The same algorithm as for scenario ""Protect Unit"" is used."
|
||||
message= _ "This is a reenactment of scenario ""The Elves Besieged"" of the mainline campaign ""Heir to the Throne"", just that the AI is playing Konrad’s side here. The same algorithm as for scenario ""Protect Unit"" is used."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -243,7 +243,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "Messenger Escort Micro AI demo"
|
||||
message= _ "'Messenger Escort' has the AI actively protect a messenger while he makes his way to the edge of the map. The escort will also try to open the path for the messenger if there are enemies in the way."
|
||||
message= _ "‘Messenger Escort’ has the AI actively protect a messenger while he makes his way to the edge of the map. The escort will also try to open the path for the messenger if there are enemies in the way."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -281,7 +281,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "Wolves Micro AI demo"
|
||||
message= _ "Another demonstration of wolves wandering and attacking in packs, with a different behavior from that in 'Animals'."
|
||||
message= _ "Another demonstration of wolves wandering and attacking in packs, with a different behavior from that in ‘Animals’."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
@ -376,7 +376,7 @@
|
|||
[message]
|
||||
speaker=Grnk
|
||||
caption= _ "Combined Hang Out and Messenger Escort Micro AI demo"
|
||||
message= _ "This scenario is a demonstration of the Hang Out Micro AI which keeps units around a (customizable) location until a (customizable) condition is met. After that the units are released to follow other AI behavior. The scenario also shows how to combine two Micro AIs on the same side by having the Messenger Escort Micro AI take over at that point."
|
||||
message= _ "This scenario is a demonstration of the Hang Out Micro AI which keeps units around a (customizable) location until a (customizable) condition is met. After that the units are released to follow other AI behavior. The scenario also shows how to combine two Micro AIs on the same side by having the Messenger Escort Micro AI take over at that point."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
|
|
@ -192,9 +192,9 @@
|
|||
[message]
|
||||
speaker=narrator
|
||||
image=wesnoth-icon.png
|
||||
message= _ "This scenario features a different kind of wolf behavior from 'Animals'. First, there can be an arbitrary number of wolf packs and the pack size on each side is a free parameter (set to 3 for Side 2 and 4 for Side 3 in this scenario). At the beginning of the scenario, close wolves are grouped into packs in a semi-methodical way. Wolves of the same pack begin by joining each other on the map. After that, they stay together until only one wolf is left, which then tries to join up with an incomplete pack or with other single wolves. Individual wolves entering the map during the scenario behave in that way as well.
|
||||
message= _ "This scenario features a different kind of wolf behavior from ‘Animals’. First, there can be an arbitrary number of wolf packs and the pack size on each side is a free parameter (set to 3 for Side 2 and 4 for Side 3 in this scenario). At the beginning of the scenario, close wolves are grouped into packs in a semi-methodical way. Wolves of the same pack begin by joining each other on the map. After that, they stay together until only one wolf is left, which then tries to join up with an incomplete pack or with other single wolves. Individual wolves entering the map during the scenario behave in that way as well.
|
||||
|
||||
Second, wolves do not actively hunt here. For the most part they just wander (often long distance). However, the pack ferociously (and without regard for its own health) attacks any enemy units that come into range, as long as that does not mean separating the pack by more than a few hexes. Staying together, or joining with a new wolf assigned to the pack, is the only thing that takes priority over satisfying the wolves' thirst for blood.
|
||||
Second, wolves do not actively hunt here. For the most part they just wander (often long distance). However, the pack ferociously (and without regard for its own health) attacks any enemy units that come into range, as long as that does not mean separating the pack by more than a few hexes. Staying together, or joining with a new wolf assigned to the pack, is the only thing that takes priority over satisfying the wolves’ thirst for blood.
|
||||
|
||||
To emphasize which wolf belongs to which pack, the pack number will be displayed below each wolf in this scenario once the AI takes control of a side the first time."
|
||||
[/message]
|
||||
|
@ -206,12 +206,12 @@ To emphasize which wolf belongs to which pack, the pack number will be displayed
|
|||
# wmllint: unbalanced-on
|
||||
message= _ "It is possible to include a human-controlled Side 1, so that the action stops once every turn for looking around (or for messing with things in debug mode). In human-controlled mode, several options are available through the right-click menu, such as adding additional wolves to either side, taking wolves off the map, adding peasants to the human-controlled side or ending the scenario. This enables easy exploring of the wolf AI behavior under different circumstances.
|
||||
|
||||
Note that the leader of the human-controlled side, Rutburt, can move 99 hexes per turn, so that it is always possible to keep him out of harm's way.
|
||||
Note that the leader of the human-controlled side, Rutburt, can move 99 hexes per turn, so that it is always possible to keep him out of harm’s way.
|
||||
|
||||
Also note that the wolves AI is coded as a Micro AI. A Micro AI can be added and adapted to the need of a scenario easily using only WML and the [micro_ai] tag. Check out the <span color='#00A000'>Micro AI wiki page</span> at https://wiki.wesnoth.org/Micro_AIs for more information." # wmllint: no spellcheck
|
||||
# wmllint: unbalanced-off
|
||||
[option]
|
||||
label= _ "<span font='16'>I'll just watch the two wolf sides.</span>"
|
||||
label= _ "<span font='16'>I’ll just watch the two wolf sides.</span>"
|
||||
[command]
|
||||
[end_turn]
|
||||
[/end_turn]
|
||||
|
|
|
@ -202,7 +202,7 @@
|
|||
[/command]
|
||||
[/option]
|
||||
[option]
|
||||
label= _ "I've come with my own AI, and I am ready to test its strength"
|
||||
label= _ "I’ve come with my own AI, and I am ready to test its strength"
|
||||
[command]
|
||||
[message]
|
||||
speaker=narrator
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
|
||||
[message]
|
||||
id=dwarf
|
||||
message=_"You're in for a nasty surprise ..."
|
||||
message=_"You’re in for a nasty surprise ..."
|
||||
[/message]
|
||||
|
||||
[message]
|
||||
|
@ -124,7 +124,7 @@
|
|||
|
||||
[message]
|
||||
x,y=19,54
|
||||
message=_"The scout in the northwest is the better choice for forcing the skeleton to level up, so I'll move out of the way for him."
|
||||
message=_"The scout in the northwest is the better choice for forcing the skeleton to level up, so I’ll move out of the way for him."
|
||||
[/message]
|
||||
|
||||
[message]
|
||||
|
@ -138,7 +138,7 @@
|
|||
|
||||
[message]
|
||||
x,y=35,40
|
||||
message=_"There's a high chance that I will die in attacking that revenant, so the AI will not attack with aggression=0.4 (the default). By contrast, with aggression=1 (which you can set in a moment), it does attack."
|
||||
message=_"There’s a high chance that I will die in attacking that revenant, so the AI will not attack with aggression=0.4 (the default). By contrast, with aggression=1 (which you can set in a moment), it does attack."
|
||||
[/message]
|
||||
[message]
|
||||
x,y=43,40
|
||||
|
|
|
@ -431,7 +431,7 @@
|
|||
[message]
|
||||
speaker=narrator
|
||||
image=wesnoth-icon.png
|
||||
message= _ "You can choose when the slaves come out of their villages and attack their captors. When you are ready, right-click anywhere and select 'Inspire the slaves to revolt'."
|
||||
message= _ "You can choose when the slaves come out of their villages and attack their captors. When you are ready, right-click anywhere and select ‘Inspire the slaves to revolt’."
|
||||
[/message]
|
||||
|
||||
#define PUT_SLAVE TYPE X Y
|
||||
|
|
|
@ -261,7 +261,10 @@
|
|||
[effect]
|
||||
apply_to=attack
|
||||
range=melee
|
||||
set_type=arcane
|
||||
[set_specials]
|
||||
mode=append
|
||||
{WEAPON_SPECIAL_ARCANE}
|
||||
[/set_specials]
|
||||
[/effect]
|
||||
[effect]
|
||||
apply_to=overlay
|
||||
|
|
|
@ -146,9 +146,9 @@
|
|||
[/gold_carryover]
|
||||
[/objectives]
|
||||
|
||||
{OBJ_POTION_HOLY 16 5 shadows_holywater1}
|
||||
{OBJ_POTION_HOLY 32 5 shadows_holywater2}
|
||||
{OBJ_POTION_HOLY 20 10 shadows_holywater3}
|
||||
{OBJ_POTION_HOLY_ALT 16 5 shadows_holywater1}
|
||||
{OBJ_POTION_HOLY_ALT 32 5 shadows_holywater2}
|
||||
{OBJ_POTION_HOLY_ALT 20 10 shadows_holywater3}
|
||||
[/event]
|
||||
|
||||
[event]
|
||||
|
|
|
@ -332,6 +332,9 @@
|
|||
[/message]
|
||||
[message]
|
||||
speaker=Delfador
|
||||
#po: The same text is used both before and after closing the portal.
|
||||
#po: Shown if the player kills Iliah-Malal instead of following the hints to run away,
|
||||
#po: this is his last breath event, but he gets fully healed and the battle continues.
|
||||
message= _ "We cannot defeat him now. We must allow some time to pass after the portal has been closed. By then, his power will have weakened, rendering him vulnerable."
|
||||
[/message]
|
||||
# He gets a full heal, not an advance, otherwise we'd have to make sure we ported him to the next scenario properly
|
||||
|
|
|
@ -201,9 +201,9 @@
|
|||
[/note]
|
||||
[/objectives]
|
||||
|
||||
{OBJ_POTION_HOLY 26 19 showdown_holywater1}
|
||||
{OBJ_POTION_HOLY 30 20 showdown_holywater2}
|
||||
{OBJ_POTION_HOLY 29 20 showdown_holywater3}
|
||||
{OBJ_POTION_HOLY_ALT 26 19 showdown_holywater1}
|
||||
{OBJ_POTION_HOLY_ALT 30 20 showdown_holywater2}
|
||||
{OBJ_POTION_HOLY_ALT 29 20 showdown_holywater3}
|
||||
|
||||
[remove_shroud]
|
||||
side=1,2
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
{TURNS_RUN_OUT}
|
||||
{IS_LAST_SCENARIO}
|
||||
[/objectives]
|
||||
{OBJ_POTION_HOLY 23 3 object7_holywater}
|
||||
{OBJ_POTION_HOLY_ALT 23 3 object7_holywater}
|
||||
[/event]
|
||||
|
||||
[event]
|
||||
|
|
|
@ -2097,6 +2097,11 @@ All sense of time and being are entombed within darkness. How long has passed si
|
|||
|
||||
{CLEAR_VARIABLE units}
|
||||
|
||||
# Restore Darken Volk's MP so that he isn't stuck behind the shroud after the redraw
|
||||
{MODIFY_UNIT (id=Darken Volk) max_moves 6}
|
||||
{MODIFY_UNIT (id=Darken Volk) moves 6}
|
||||
{MODIFY_UNIT (id=Darken Volk) side 1}
|
||||
|
||||
[redraw]
|
||||
side=1
|
||||
clear_shroud=yes
|
||||
|
@ -2153,6 +2158,7 @@ All sense of time and being are entombed within darkness. How long has passed si
|
|||
object_id="potion_poison_icon"
|
||||
[/remove_object]
|
||||
|
||||
# Duplicated from the rebel soul event for debugging purposes
|
||||
{MODIFY_UNIT (id=Darken Volk) max_moves 6}
|
||||
{MODIFY_UNIT (id=Darken Volk) moves 6}
|
||||
{MODIFY_UNIT (id=Darken Volk) side 1}
|
||||
|
|
|
@ -1183,7 +1183,7 @@
|
|||
[/message]
|
||||
|
||||
[message]
|
||||
role=unit
|
||||
speaker=unit
|
||||
message= _ "It’s not catching fire!"
|
||||
[/message]
|
||||
|
||||
|
@ -1211,7 +1211,7 @@
|
|||
[/message]
|
||||
|
||||
[message]
|
||||
role=unit
|
||||
speaker=unit
|
||||
message= _ "It’s not catching fire!"
|
||||
[/message]
|
||||
|
||||
|
|
|
@ -332,8 +332,8 @@
|
|||
id=did_soul_rend
|
||||
name= _ "soul rend"
|
||||
name_inactive= _ "soul rend"
|
||||
description= _ "Every turn, this unit loses 5 hitpoints, but drains 4 hitpoints from each adjacent enemy."
|
||||
description_inactive= _ "Every turn, this unit loses 5 hitpoints, but drains 4 hitpoints from each adjacent enemy."
|
||||
description= _ "Every turn, this unit loses 3 hitpoints, but drains 6 hitpoints from each adjacent enemy."
|
||||
description_inactive= _ "Every turn, this unit loses 3 hitpoints, but drains 6 hitpoints from each adjacent enemy."
|
||||
[/dummy]
|
||||
#enddef
|
||||
|
||||
|
@ -363,7 +363,7 @@
|
|||
ability=did_soul_rend
|
||||
[/filter]
|
||||
|
||||
amount=5
|
||||
amount=3
|
||||
animate=no
|
||||
kill=no
|
||||
[/harm_unit]
|
||||
|
@ -372,7 +372,7 @@
|
|||
x,y=$runit.x,$runit.y
|
||||
color=255,0,0
|
||||
# wmllint: markcheck off
|
||||
text=5
|
||||
text=3
|
||||
# wmllint: markheck on
|
||||
[/floating_text]
|
||||
|
||||
|
@ -390,7 +390,7 @@
|
|||
[/filter]
|
||||
fire_event=yes
|
||||
animate=yes
|
||||
amount=4
|
||||
amount=6
|
||||
delay=25
|
||||
[/harm_unit]
|
||||
[if]
|
||||
|
@ -410,7 +410,7 @@
|
|||
[/if]
|
||||
[/do]
|
||||
[/foreach]
|
||||
{VARIABLE_OP counter multiply 4}
|
||||
{VARIABLE_OP counter multiply 6}
|
||||
[heal_unit]
|
||||
[filter]
|
||||
ability=did_soul_rend
|
||||
|
|
|
@ -304,7 +304,7 @@
|
|||
[effect]
|
||||
apply_to=attack
|
||||
name=shadow wave
|
||||
increase_damage=2
|
||||
increase_damage=1
|
||||
[/effect]
|
||||
[effect]
|
||||
apply_to=hitpoints
|
||||
|
|
305
data/campaigns/Eastern_Invasion/ART_MUSIC_LICENSE.txt
Normal file
|
@ -0,0 +1,305 @@
|
|||
|
||||
AUDIO
|
||||
===============
|
||||
|
||||
MUSIC
|
||||
---------------
|
||||
|
||||
cry from elensefar (pluft) - CC BY-SA 4.0
|
||||
https://forums.wesnoth.org/viewtopic.php?t=48565
|
||||
https://soundcloud.com/pluft/cry-from-elensefar
|
||||
|
||||
heavens (gabriel silver), remixed by dalas - CC BY-SA 4.0 (both)
|
||||
https://forums.wesnoth.org/viewtopic.php?t=51483
|
||||
https://www.youtube.com/watch?v=AOvwWOGH7bQ&list=PLk-SdLbgd5ol62NX0q7ys5ZbmJFVAMzCF&index=2
|
||||
|
||||
|
||||
SOUNDS
|
||||
---------------
|
||||
|
||||
wind (hackerb9/Tony B kksm) - CC 0
|
||||
edited by dalas; I release the edited sound under CC BY-SA 4.0
|
||||
https://freesound.org/people/hackerb9/sounds/555246/
|
||||
|
||||
gate-fall
|
||||
lich_die_wail
|
||||
magic-twilight
|
||||
magic-twilight-miss
|
||||
made by dalas by modifying pre-existing wesnoth sounds (GPL v2.0 or later)
|
||||
|
||||
warhammer - GPL v2.0 or later
|
||||
made by nemaara using pre-existing wesnoth sounds
|
||||
|
||||
ASSORTED IMAGES
|
||||
===============
|
||||
|
||||
ATTACKS
|
||||
---------------
|
||||
|
||||
blast-wave
|
||||
sword-astral-short
|
||||
sword-orcish-flaming
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
sword-orcish-swing
|
||||
from the Reign of Lords add-on (GPL v2.0 or later)
|
||||
|
||||
|
||||
HALO
|
||||
---------------
|
||||
|
||||
terror - CC BY-SA 4.0
|
||||
created by dalas
|
||||
|
||||
idle-flash
|
||||
fire-aura-small
|
||||
light-shield
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
shadow-mage-halo - from the Liberty campaign (GPL v2.0 or later)
|
||||
|
||||
|
||||
ICONS
|
||||
---------------
|
||||
|
||||
banana
|
||||
drowning
|
||||
pacifist
|
||||
made by dalas (CC BY-SA 4.0)
|
||||
|
||||
coins
|
||||
ninja
|
||||
ogre
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
planb (Q_x), modified by dalas - GPL 2.0 and GPL 3.0
|
||||
https://opengameart.org/content/dwarven-map
|
||||
source file: https://github.com/wesnoth/resources/blob/master/campaigns/Eastern_Invasion/images/plan.psd
|
||||
|
||||
skull.png (Atmostatic), modified by dalas - GPL 2.0 and GPL 3.0
|
||||
https://opengameart.org/content/skull-1
|
||||
source files: https://github.com/wesnoth/resources/blob/master/campaigns/Eastern_Invasion/images/Skull_Monster_512.png
|
||||
https://github.com/wesnoth/resources/blob/master/campaigns/Eastern_Invasion/images/skull_monster.mtl
|
||||
https://github.com/wesnoth/resources/blob/master/campaigns/Eastern_Invasion/images/skull_monster.obj
|
||||
https://github.com/wesnoth/resources/blob/master/campaigns/Eastern_Invasion/images/skull_monsterSnap00.png
|
||||
|
||||
|
||||
|
||||
ITEMS
|
||||
---------------
|
||||
|
||||
altar-evil-amulet
|
||||
crystal-quiver
|
||||
horse-cage
|
||||
gohere-empty
|
||||
lamp-genie
|
||||
plauge-staff
|
||||
rider-corpse
|
||||
secret-item
|
||||
shield-of-the-sentinel
|
||||
shield-of-the-sentinel-stand
|
||||
swamp_herb
|
||||
whitefang-flag
|
||||
all either pre-existing wesnoth content or made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
ant-ambrosia (admurin), modified by dalas - CC BY-SA 4.0
|
||||
https://opengameart.org/content/admurins-insect-items
|
||||
copyright: https://admurin.itch.io/ - Admurin
|
||||
|
||||
yeti meat
|
||||
drawn by nemaara (CC BY-SA 4.0)
|
||||
|
||||
|
||||
MAPS
|
||||
---------------
|
||||
|
||||
ei.webp
|
||||
based on the original EI map (GPL v2.0 or later)
|
||||
|
||||
|
||||
MISC
|
||||
---------------
|
||||
|
||||
arcane-icon
|
||||
crystal-quiver-icon
|
||||
herb-icon
|
||||
leader-gold
|
||||
plague-staff-icon
|
||||
potion-of-vitality-icon
|
||||
ring-of-invisibility-icon
|
||||
sentinel-icon
|
||||
soulsword-icon
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
PORTRAITS
|
||||
---------------
|
||||
|
||||
gaennell-adept
|
||||
mal-ravanal-ghost
|
||||
pyre-wight
|
||||
terraent
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
remainder is pre-existing wesnoth content (GPL v2.0 or later)
|
||||
|
||||
|
||||
PROJECTILES
|
||||
---------------
|
||||
|
||||
shadow-blast - from the Liberty campaign (GPL v2.0 or later)
|
||||
|
||||
|
||||
SCENERY
|
||||
---------------
|
||||
|
||||
blood
|
||||
bookshelf
|
||||
gore
|
||||
from After The Storm, used with Iris's permission (GPL v2.0 or later)
|
||||
|
||||
cave_entrance
|
||||
cave_entrance_empty
|
||||
dwarven-keep-tile
|
||||
rune_line
|
||||
rune_line_glow
|
||||
rune_line_glow_base
|
||||
statue-delfador
|
||||
statue-haldric
|
||||
throne
|
||||
throne-small
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
TERRAIN
|
||||
---------------
|
||||
|
||||
terrain_type_urban
|
||||
terrain_type_urban_30
|
||||
made by dalas (CC BY-SA 4.0)
|
||||
|
||||
remainder is original wesnoth content modified by dalas (GPL v2.0 or later)
|
||||
|
||||
|
||||
WELDYN
|
||||
---------------
|
||||
|
||||
walls_S15
|
||||
walls_S18b
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################################################################################################################
|
||||
# UNIT IMAGES
|
||||
################################################################################################################################################################################
|
||||
|
||||
ANT
|
||||
---------------
|
||||
|
||||
egg sac
|
||||
egg sac remains
|
||||
made by dalas (CC BY-SA 4.0)
|
||||
|
||||
queen ant
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
BANNERET
|
||||
---------------
|
||||
|
||||
base image from The_Gnat's Reign of the Lords add-on (GPL v2.0 or later)
|
||||
|
||||
animations
|
||||
by dalas (GPL v2.0 or later)
|
||||
|
||||
|
||||
DACYN
|
||||
---------------
|
||||
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
NAGA
|
||||
---------------
|
||||
|
||||
base image from The_Gnat's Reign of the Lords add-on (GPL v2.0 or later)
|
||||
|
||||
animations
|
||||
by dalas (GPL v2.0 or later)
|
||||
|
||||
|
||||
OGRES
|
||||
---------------
|
||||
|
||||
all images from The_Gnat's Reign of the Lords add-on (GPL v2.0 or later)
|
||||
|
||||
|
||||
OWAEC
|
||||
---------------
|
||||
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
ROYAL WARRIOR
|
||||
---------------
|
||||
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
SHADOW MAGE
|
||||
---------------
|
||||
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
TERRAENT
|
||||
---------------
|
||||
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
TOWNSFOLK
|
||||
---------------
|
||||
|
||||
all drawn by nemaara (CC BY-SA 4.0)
|
||||
|
||||
|
||||
UNDEAD-RAVANAL
|
||||
---------------
|
||||
|
||||
ancient lich
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
dark-shape (The_Gnat) Reign of the Lords add-on
|
||||
with modificataions by dalas (GPL v2.0 or later)
|
||||
|
||||
|
||||
UNDEAD-SKELETAL
|
||||
---------------
|
||||
all images
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
||||
|
||||
UNDEAD-WIGHT
|
||||
---------------
|
||||
base image (The_Gnat) Reign of the Lords add-on (GPL v2.0 or later)
|
||||
|
||||
animations (GPL v2.0 or later)
|
||||
|
||||
|
||||
OTHER
|
||||
---------------
|
||||
|
||||
gate
|
||||
unknown-unit1
|
||||
unknown-unit2
|
||||
made by dalas by modifying pre-existing wesnoth images (GPL v2.0 or later)
|
||||
|
|
@ -3,56 +3,123 @@
|
|||
name="wesnoth-ei"
|
||||
[/textdomain]
|
||||
|
||||
#ifdef CAMPAIGN_EASTERN_INVASION
|
||||
[lua]
|
||||
code="wesnoth.require 'campaigns/Eastern_Invasion/lua/bandits.lua'"
|
||||
[/lua]
|
||||
[lua]
|
||||
code="wesnoth.require 'campaigns/Eastern_Invasion/lua/items.lua'"
|
||||
[/lua]
|
||||
[lua]
|
||||
code = <<
|
||||
wesnoth.dofile('campaigns/Eastern_Invasion/lua/' .. 'skip_animations.lua')
|
||||
>>
|
||||
[/lua]
|
||||
|
||||
{campaigns/Eastern_Invasion/utils}
|
||||
[units]
|
||||
{campaigns/Eastern_Invasion/units}
|
||||
[/units]
|
||||
{campaigns/Eastern_Invasion/scenarios}
|
||||
|
||||
[binary_path]
|
||||
path=data/campaigns/Eastern_Invasion
|
||||
[/binary_path]
|
||||
{campaigns/Eastern_Invasion/terrain.cfg}
|
||||
{campaigns/Eastern_Invasion/terrain-graphics.cfg}
|
||||
#endif
|
||||
#ifdef EDITOR
|
||||
[binary_path]
|
||||
path=data/campaigns/Eastern_Invasion
|
||||
[/binary_path]
|
||||
[editor_group]
|
||||
id=eastern_invasion
|
||||
name= _ "Eastern Invasion"
|
||||
icon="group_mainline"
|
||||
[/editor_group]
|
||||
{campaigns/Eastern_Invasion/terrain.cfg}
|
||||
{campaigns/Eastern_Invasion/terrain-graphics.cfg}
|
||||
#endif
|
||||
|
||||
# wmlscope: set export=no
|
||||
[campaign]
|
||||
id=Eastern_Invasion
|
||||
rank=108
|
||||
start_year="625 YW"
|
||||
start_year="626 YW"
|
||||
end_year="627 YW"
|
||||
icon="units/human-loyalists/general.png~RC(magenta>red)"
|
||||
name= _ "Eastern Invasion"
|
||||
abbrev= _ "EI"
|
||||
define=CAMPAIGN_EASTERN_INVASION
|
||||
first_scenario=01_The_Outpost
|
||||
first_scenario=01_Eastern_Invasion
|
||||
|
||||
{CAMPAIGN_DIFFICULTY EASY "units/human-loyalists/spearman.png~RC(magenta>red)" ( _ "Spearman") ( _ "Normal")}
|
||||
{CAMPAIGN_DIFFICULTY NORMAL "units/human-loyalists/swordsman.png~RC(magenta>red)" ( _ "Swordsman") ( _ "Challenging")} {DEFAULT_DIFFICULTY}
|
||||
{CAMPAIGN_DIFFICULTY HARD "units/human-loyalists/royalguard.png~RC(magenta>red)" ( _ "Royal Guard") ( _ "Difficult")}
|
||||
{CAMPAIGN_DIFFICULTY EASY "units/human-loyalists/sergeant.png~RC(magenta>red)" ( _ "Skirmish") ( _ "1x enemies")}
|
||||
{CAMPAIGN_DIFFICULTY NORMAL "units/human-loyalists/lieutenant.png~RC(magenta>red)" ( _ "Incursion")( _ "3x enemies")} {DEFAULT_DIFFICULTY}
|
||||
{CAMPAIGN_DIFFICULTY HARD "units/human-loyalists/general.png~RC(magenta>red)" ( _ "Invasion") ( _ "5x enemies")}
|
||||
|
||||
#
|
||||
# ----DIFFICULTY NOTES:
|
||||
# for most scenarios in this campaign, there is little player gold variation and enemy gold variation provides the variation between difficulties
|
||||
# this is for 2 main reasons:
|
||||
# 1) scenarios are easier to balance when there's fewer variables
|
||||
# 2) I want the campaign to feel as similar as possible despite varying difficulty
|
||||
# e.g. Giving extra gold or increasing the turn limit can change a small skirmish (on hard) into a big-army battle (on easy)
|
||||
# e.g. Reducing an ally's gold on high difficulty can make them feel useless, which is not my intention for any of the scenarios with allies
|
||||
#
|
||||
# my goal is for Easy/Normal/Hard difficulty to be 20%/60%/100%; that is to say Easy has 1/5 as many enemies as Hard, and Normal has 3/5 as many
|
||||
# this many not always translate directly to 20/60/100 gold/income, since enemies with many villages effectively have a higher guranteed incoime
|
||||
# also, remember that [side] {INCOME} doesn't include everyone's base +2 income
|
||||
# and also, some optional "challenge" scenarios have reduced scaling (e.g. All-In), since they're intended as challenges and easily avoidable
|
||||
#
|
||||
|
||||
#
|
||||
# ----STRUCTURE NOTES:
|
||||
# broadly speaking, this campaign can be divided into 3 sections
|
||||
# I've tried to keep each section different both narratively and mechanically, so that the campaign doesn't feel stale (it's pretty long)
|
||||
#
|
||||
# S01-S07, Gweddry and Co are trying to keep their little band of soldiers alive through initial stages of the undead invasion
|
||||
# regular battles, focused around building up a recall list while achieving bonus objectives (Hahid, Terraent, Yannic)
|
||||
# S08-S12, Gweddry and Co are in the northlands, dealing with the Chief Dra-Nak subplot and trying to get Dacyn's amulet
|
||||
# cramped and/or small battles, focused around looting items and saving enough gold to keep your recalls alive in S12
|
||||
# S13-S18, Gweddry and Co are witnessing a dying Wesnoth, and finally fighting as part of a proper army in a full-fledged war
|
||||
# big battles, where your performance in S01-S12 determine how successful you'll be
|
||||
#
|
||||
# the campaign is designed with lots of easy-or-hard branching decision points, where one option is easier but the other option has better rewards
|
||||
# S12 "Evacuation" also functions as a decision point - you're guranteed a win even if you don't recruit any units, but the fewer recalls escape the more difficult the future will be
|
||||
# players should be able to complete the campaign by taking only the easy, lower-reward options, culminating in the easier finale S17a "The Duel"
|
||||
# but only players who've succeeded at some challenges will have a strong enough army to complete the more difficult finale S17b "All-In"
|
||||
#
|
||||
|
||||
#
|
||||
# ----AI NOTES:
|
||||
# many scenarios have AI that's scripted to retreat during the daytime, and to regroup when low on unit count
|
||||
# partially for balance, but also because it (IMO) makes for more interesting ebb-and-flow gameplay
|
||||
# some scenarios also have ally AI that's scripted to play defensively, with [avoid]s to keep it from being too aggressive
|
||||
# partially for balance, but mostly so that you aren't silently cursing your allies every turn
|
||||
# therefore, before altering terrain, please check if it messes with AI behavior (e.g. AI tries to retreat to a village that's no longer there)
|
||||
#
|
||||
|
||||
description= _ "There are rumors of undead attacks on the eastern border of Wesnoth. You, an officer in the Royal Army, have been sent to the eastern front to protect the villagers and find out what is happening.
|
||||
|
||||
" + _"(Intermediate level, 16 scenarios.)"
|
||||
image="data/campaigns/Eastern_Invasion/images/campaign_image.png"
|
||||
image="data/campaigns/Eastern_Invasion/images/campaign_image.png~FL()"
|
||||
|
||||
[about]
|
||||
title = _ "Campaign Design"
|
||||
title = _ "Newest Campaign Version Rewrite"
|
||||
[entry]
|
||||
name = "Dalas"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Original Campaign Design"
|
||||
[entry]
|
||||
name = "Joseph Simmons (turin)"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Campaign Maintenance"
|
||||
title = _ "Prose and Story Editing, Code Preparation"
|
||||
[entry]
|
||||
name = "Dimitar Ilccov (Mythological)"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Lari Nieminen (zookeeper)"
|
||||
comment = "current maintainer"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Astrid Halberkamp"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Campaign Epilogue and Continuity"
|
||||
[entry]
|
||||
name = "Eric S. Raymond (ESR)"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Prose and Story Edits"
|
||||
[entry]
|
||||
name = "Loci"
|
||||
name = "nemaara"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
|
@ -73,22 +140,69 @@
|
|||
name = "Neoriceisgood"
|
||||
[/entry]
|
||||
[/about]
|
||||
[/campaign]
|
||||
[about]
|
||||
title = _ "Campaign Redesign Playtesters"
|
||||
[entry]
|
||||
name = "Konrad2"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Lord-Knightmare"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Toranks"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Previous Campaign Maintenance"
|
||||
[entry]
|
||||
name = "Dimitar Ilccov (Mythological)"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Lari Nieminen (zookeeper)"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Astrid Halberkamp"
|
||||
[/entry]
|
||||
[/about]
|
||||
[about]
|
||||
title = _ "Others"
|
||||
[entry]
|
||||
name = "Eric S. Raymond (ESR)"
|
||||
[/entry]
|
||||
[entry]
|
||||
name = "Loci"
|
||||
[/entry]
|
||||
[/about]
|
||||
|
||||
#ifdef CAMPAIGN_EASTERN_INVASION
|
||||
[binary_path]
|
||||
path=data/campaigns/Eastern_Invasion
|
||||
[/binary_path]
|
||||
{GLOBAL__ANIMATIONS_RECRUIT}
|
||||
{GLOBAL__UNIVERSAL_AMLAS}
|
||||
{GLOBAL__ABILITY_TERROR}
|
||||
{GLOBAL__TRAIT_DESERTER}
|
||||
{GLOBAL__IMPLMENTATION_SHIELD_OF_THE_SENTINEL}
|
||||
{GLOBAL__PLAGUE_STAFF_RECRUIT_VARIATIONS}
|
||||
{GLOBAL__PLAGUE_STAFF_USE_UPKEEP}
|
||||
|
||||
[lua]
|
||||
code="wesnoth.require 'campaigns/Eastern_Invasion/lua/bandits.lua'"
|
||||
[/lua]
|
||||
# initialize right-click menus for the various items in the game.
|
||||
{GLOBAL__DROPPABLE_HOLY_AMULET {ID_HOLY_AMULET_1}}
|
||||
{GLOBAL__DROPPABLE_HOLY_AMULET {ID_HOLY_AMULET_2}}
|
||||
{GLOBAL__DROPPABLE_HOLY_AMULET {ID_HOLY_AMULET_3}}
|
||||
{GLOBAL__DROPPABLE_CRYSTAL_QUIVER}
|
||||
{GLOBAL__DROPPABLE_SHIELD_OF_THE_SENTINEL}
|
||||
{GLOBAL__DROPPABLE_BANEBLADE}
|
||||
{GLOBAL__DROPPABLE_INVISIBILITY}
|
||||
{GLOBAL__DROPPABLE_PLAGUE_STAFF}
|
||||
|
||||
{campaigns/Eastern_Invasion/utils}
|
||||
[units]
|
||||
{campaigns/Eastern_Invasion/units}
|
||||
[/units]
|
||||
{campaigns/Eastern_Invasion/scenarios}
|
||||
{ENABLE_PARAGON} # dunefolk are a challenge to get, so they may as well be powerful
|
||||
{DISABLE_GRAND_MARSHAL} # don't let S17/S18b's generals overshadow Gweddry
|
||||
[modify_unit_type]
|
||||
type=Ogre
|
||||
set_advances_to=Great Ogre
|
||||
set_experience=80
|
||||
[/modify_unit_type]
|
||||
[modify_unit_type]
|
||||
type=Dwarvish Miner
|
||||
set_cost=8
|
||||
[/modify_unit_type]
|
||||
#endif
|
||||
|
||||
# wmllint: directory spellings Gweddry Owaec Mal-Ravanal
|
||||
[/campaign]
|
||||
|
|
|
@ -1 +1,76 @@
|
|||
#textdomain wesnoth-ei
|
||||
|
||||
#define ACHIEVEMENT ICON ID NAME DESC
|
||||
[achievement]
|
||||
icon,id,name,description={ICON},{ID},{NAME},{DESC}
|
||||
[/achievement]
|
||||
#enddef
|
||||
#define HIDDEN ICON ID NAME DESC
|
||||
[achievement]
|
||||
icon,id,name,description={ICON},{ID},{NAME},{DESC}
|
||||
hidden=yes
|
||||
[/achievement]
|
||||
#enddef
|
||||
#define PATH
|
||||
data/campaigns/Eastern_Invasion/images/icons#enddef
|
||||
#define CORE_PATH
|
||||
data/core/images#enddef
|
||||
|
||||
[achievement_group]
|
||||
display_name=_"Eastern Invasion"
|
||||
content_for=eastern_invasion
|
||||
|
||||
#
|
||||
# many of these achievements are intended as content guides, teasing the player with things like "recruit the dunefolk" or "ally with the dwarves"
|
||||
# this helps players find content they might have missed
|
||||
#
|
||||
# but some achievements are VERY hard. Frenzied and Warmonger, for example.
|
||||
# they are, at least, theoretically possible. But I would be very impressed if someone could achieve them on hard
|
||||
# (that's why it's called an "achieve"-ment, yeah?)
|
||||
#
|
||||
|
||||
{ACHIEVEMENT "{CORE_PATH}/attacks/blank-attack.png~SCALE(84,84)~BLIT({CORE_PATH}/scenery/trapdoor-open.png,8,8)"
|
||||
"ei_S01" _"Scenario 1: Tactical Withdrawal" _"Complete the ‘<i>Eastern Invasion</i>’ scenario with at least 10 surviving units."}
|
||||
{ACHIEVEMENT {CORE_PATH}/attacks/foot-shoe.png
|
||||
"ei_S02" _"Scenario 2: Speedrun" _"Complete ‘<i>The Escape Tunnel</i>’ before the undead arrive."}
|
||||
{ACHIEVEMENT {CORE_PATH}/icons/amla-default.png
|
||||
"ei_S03" _"Scenario 3: Merciful" _"Complete ‘<i>An Unexpected Appearance</i>’ without killing Mal-Tar, the dark adept."}
|
||||
{ACHIEVEMENT {CORE_PATH}/attacks/torch.png
|
||||
"ei_S04a" _"Scenario 4a: Scorched Earth" _"Defeat both the bandit and elven leaders in ‘<i>Elven Interlude</i>’."}
|
||||
{ACHIEVEMENT {PATH}/coins_gold.png
|
||||
"ei_S04b" _"Scenario 4b: Mercenary" _"Recruit the dunefolk in ‘<i>Ill Humours</i>’."}
|
||||
{ACHIEVEMENT "{CORE_PATH}/attacks/blank-attack.png~SCALE(84,84)~BLIT({CORE_PATH}/units/human-loyalists/knight/knight.png~RC(magenta>white),1,5)~BLIT(data/campaigns/Eastern_Invasion/images/items/horse-cage.png,8,10)"
|
||||
"ei_S04c" _"Scenario 4c: No (Horse)Man Left Behind" _"Rescue all 6 prisoners from ‘<i>Mal Ravanal’s Capital</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/icons/hat-huntsman.png
|
||||
"ei_S05" _"Scenario 5: Folk Hero" _"Complete ‘<i>Northern Outpost</i>’ without any peasants dying."}
|
||||
{ACHIEVEMENT {PATH}/drowning.png
|
||||
"ei_S06a" _"Scenario 6a: A Little Help Here?" _"Defeat the necromancer in ‘<i>Undead Crossing</i>’ before rescuing Dolburras."}
|
||||
{ACHIEVEMENT {PATH}/ninja.png
|
||||
"ei_S06b" _"Scenario 6b: Ninja" _"Complete ‘<i>Soradoc</i>’ without killing any enemy leaders."}
|
||||
{ACHIEVEMENT {PATH}/ogre.png
|
||||
"ei_S07a" _"Scenario 7a: Ogre Rights Advocate" _"Refuse to enslave any ogres in ‘<i>Capturing the Ogres</i>’."}
|
||||
{ACHIEVEMENT "{CORE_PATH}/attacks/blank-attack.png~SCALE(72,72)~BLIT({CORE_PATH}/units/dwarves/berserker/berserker-attack-8.png,-2,-2)"
|
||||
"ei_S07b" _"Scenario 7b: Frenzied" _"Kill 5 enemy leaders in ‘<i>Ogre Crossing</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/attacks/battleaxe.png
|
||||
"ei_S08" _"Scenario 8: And My Axe" _"Ally with the dwarves in ‘<i>Xenophobia</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/attacks/cleaver.png
|
||||
"ei_S09" _"Scenario 9: Guuuh..." _"Recruit the wild ogres in ‘<i>Castle in the Ice</i>’."}
|
||||
{ACHIEVEMENT "{CORE_PATH}/attacks/blank-attack.png~SCALE(72,72)~BLIT({CORE_PATH}/units/undead/zombie.png,-2,-2)"
|
||||
"ei_S10" _"Scenario 10: Brains" _"Find the legendary Staff of Power in ‘<i>Dark Sanctuary</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/icons/ring_gold.png
|
||||
"ei_S11" _"Scenario 11: And In The Darkness Bind Them" _"Find the Ring of Invisibility in ‘<i>Captured</i>’."}
|
||||
{ACHIEVEMENT {PATH}/planb.png
|
||||
"ei_S12" _"Scenario 12: Plan B" _"Complete ‘<i>Evacuation</i>’ by defeating all enemy leaders."}
|
||||
{ACHIEVEMENT {CORE_PATH}/attacks/sword-holy.png
|
||||
"ei_S13" _"Scenario 13: Oath of Redemption" _"Recruit Gaennell, the dark adept, in ‘<i>Spoils of War</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/icons/book.png
|
||||
"ei_S14" _"Scenario 14: Historian" _"Read the story of the Clans’ defeat in ‘<i>The Drowned Plains</i>’."}
|
||||
{ACHIEVEMENT {CORE_PATH}/icons/helmet_frogmouth.png
|
||||
"ei_S16" _"Scenario 16: Alternate History" _"Complete ‘<i>Eleventh Hour</i>’ with no recalling and no items."}
|
||||
{ACHIEVEMENT {PATH}/pacifist.png
|
||||
"ei_S17a" _"Scenario 17a: Pacifist" _"Kill no enemies before defeating Mal-Ravanal in ‘<i>The Duel</i>’."}
|
||||
{ACHIEVEMENT {PATH}/skull.png
|
||||
"ei_S17b" _"Scenario 17b: Warmonger" _"Kill all enemies before defeating Mal-Ravanal in ‘<i>All-In</i>’."}
|
||||
{ACHIEVEMENT "{CORE_PATH}/attacks/blank-attack.png~SCALE(76,76)~BLIT({CORE_PATH}/units/orcs/sovereign-attack-5.png,2,7)"
|
||||
"ei_S99" _"Scenario 99: Haw, Haw, Haw" _"Complete the secret bonus scenario."}
|
||||
[/achievement_group]
|
||||
|
|
|
@ -42,14 +42,15 @@ function ca_ogres_flee:execution()
|
|||
local rating = - dist
|
||||
|
||||
-- If we can reach the edge, we do so
|
||||
if (dist == 0) then
|
||||
rating = rating + 1000
|
||||
end
|
||||
if (dist == 0) then rating = rating + 1000 end
|
||||
|
||||
local enemy_weight = 0.5
|
||||
-- local enemy_rating = - (enemy_attack_map.units:get(r[1], r[2]) or 0) * enemy_weight
|
||||
|
||||
local enemy_rating = 0
|
||||
for k,e in ipairs(enemies) do
|
||||
local enemy_dist = M.distance_between(r[1], r[2], e.x, e.y)
|
||||
enemy_rating = enemy_rating + math.sqrt(enemy_dist)
|
||||
dist = M.distance_between(r[1], r[2], e.x, e.y)
|
||||
enemy_rating = enemy_rating + math.sqrt(dist)
|
||||
end
|
||||
|
||||
rating = rating + enemy_rating
|
||||
|
@ -57,9 +58,9 @@ function ca_ogres_flee:execution()
|
|||
-- Also, maximize distance from own units that have already moved
|
||||
local own_unit_weight = 0.5
|
||||
local own_unit_rating = 0
|
||||
for k,u_no_mp in ipairs(units_no_mp) do
|
||||
local no_mp_dist = M.distance_between(r[1], r[2], u_no_mp.x, u_no_mp.y)
|
||||
own_unit_rating = own_unit_rating + math.sqrt(no_mp_dist)
|
||||
for k,u_noMP in ipairs(units_no_mp) do
|
||||
dist = M.distance_between(r[1], r[2], u_noMP.x, u_noMP.y)
|
||||
own_unit_rating = own_unit_rating + math.sqrt(dist)
|
||||
end
|
||||
|
||||
rating = rating + own_unit_rating * own_unit_weight
|
||||
|
|
BIN
data/campaigns/Eastern_Invasion/images/attacks/blast-wave.png
Normal file
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 22 KiB |
BIN
data/campaigns/Eastern_Invasion/images/attacks/sword-astral.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 378 KiB |
BIN
data/campaigns/Eastern_Invasion/images/halo/fire-aura-small.png
Normal file
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 759 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |