Merge branch 'master' into asio_wesnothd
This commit is contained in:
commit
94c8533e1f
4363 changed files with 460652 additions and 380035 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -30,7 +30,7 @@ INSTALL.vcproj
|
|||
PACKAGE.vcproj
|
||||
ZERO_CHECK.vcproj
|
||||
uninstall.vcproj
|
||||
*.vcproj.*.user
|
||||
projectfiles/VC*/*.user
|
||||
projectfiles/VC[12]*
|
||||
src/**/*.vcproj
|
||||
/WindowsTimeout.ilk
|
||||
|
|
27
.travis.yml
27
.travis.yml
|
@ -11,13 +11,14 @@ compiler:
|
|||
env:
|
||||
- BUILD="-O0"
|
||||
- BUILD="-O2"
|
||||
- BUILD="C++11 -O0"
|
||||
- BUILD="translations"
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- compiler: gcc
|
||||
env: BUILD="-O2"
|
||||
- compiler: gcc
|
||||
env: BUILD="C++14 -O2"
|
||||
- compiler: gcc
|
||||
env: BUILD="translations"
|
||||
|
||||
|
@ -32,17 +33,23 @@ before_install:
|
|||
- export EXTRA_FLAGS_RELEASE="-O0"
|
||||
- export WML_TEST_TIME=20
|
||||
- export NLS=false
|
||||
- export CXX11=false
|
||||
- export CXXSTD=11
|
||||
|
||||
- if [ "$CXX" == "g++" ]; then export CXX=g++-4.7; fi
|
||||
|
||||
- if [ "$BUILD" == "-O2" ]; then export STRICT_COMPILATION=false; fi
|
||||
- if [ "$BUILD" == "-O2" ]; then export EXTRA_FLAGS_RELEASE=""; fi
|
||||
- if [ "$BUILD" == "-O2" ]; then export WML_TEST_TIME=15; fi
|
||||
|
||||
- if [ "$BUILD" == "C++11 -O0" ]; then export CXX11=true; fi
|
||||
- if [ "$BUILD" == "C++11 -O0" ]; then export EXTRA_FLAGS_RELEASE="-O0 -Wno-literal-suffix -Wno-deprecated-declarations"; fi
|
||||
- if [[ "$BUILD" == "C++11 -O0" ]] && [[ "$CXX" == "clang++" ]]; then export EXTRA_FLAGS_RELEASE="-O0 -Wno-literal-suffix -Wno-deprecated-declarations -Wno-deprecated-register"; fi
|
||||
- if [ "$BUILD" == "C++11 -O0" ]; then export PLAY_TEST=false; fi
|
||||
- if [ "$BUILD" == "C++11 -O0" ]; then export MP_TEST=false; fi
|
||||
- if [ "$BUILD" == "C++14 -O2" ]; then export STRICT_COMPILATION=false; fi
|
||||
- if [ "$BUILD" == "C++14 -O2" ]; then export EXTRA_FLAGS_RELEASE=""; fi
|
||||
- if [ "$BUILD" == "C++14 -O2" ]; then export WML_TEST_TIME=15; fi
|
||||
- if [ "$BUILD" == "C++14 -O2" ]; then export CXXSTD="1y"; fi
|
||||
|
||||
- if [ "$BUILD" == "-O0" ]; then export EXTRA_FLAGS_RELEASE="-O0 -Wno-deprecated-declarations"; fi
|
||||
- if [[ "$BUILD" == "-O0" ]] && [[ "$CXX" == "clang++" ]]; then export EXTRA_FLAGS_RELEASE="-O0 -Wno-literal-suffix -Wno-deprecated-declarations -Wno-deprecated-register"; fi
|
||||
- if [ "$BUILD" == "-O0" ]; then export PLAY_TEST=false; fi
|
||||
- if [ "$BUILD" == "-O0" ]; then export MP_TEST=false; fi
|
||||
|
||||
- if [ "$BUILD" == "translations" ]; then export NLS=true; fi
|
||||
- if [ "$BUILD" == "translations" ]; then export TARGETS="translations"; fi
|
||||
|
@ -53,14 +60,14 @@ before_install:
|
|||
|
||||
install:
|
||||
- travis_wait sudo apt-get update -qq
|
||||
- travis_wait sudo apt-get install -qq libboost-filesystem-dev libboost-iostreams-dev libboost-random-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-test-dev libboost-locale-dev libcairo2-dev libfribidi-dev libpango1.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-ttf2.0-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev gdb moreutils scons xvfb
|
||||
- travis_wait sudo apt-get install -qq libboost-filesystem-dev libboost-iostreams-dev libboost-random-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-test-dev libboost-locale-dev libboost-thread-dev libcairo2-dev libfribidi-dev libpango1.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-ttf2.0-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev gdb moreutils scons xvfb g++-4.7
|
||||
|
||||
script:
|
||||
- ./utils/travis/check_utf8.sh
|
||||
- ./utils/travis/utf8_bom_dog.sh
|
||||
- $CXX --version
|
||||
- echo "*Params* --- " "cxxtool=$CXX --debug=time build=release extra_flags_release="$EXTRA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS cxx0x=$CXX11 nls=$NLS jobs=2"
|
||||
- scons cxxtool=$CXX --debug=time build=release extra_flags_config="$EXTRA_FLAGS_ALL" extra_flags_release="$EXTRA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS cxx0x=$CXX11 nls=$NLS jobs=2
|
||||
- echo "*Params* --- " "cxxtool=$CXX --debug=time build=release extra_flags_release="$EXTRA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS cxx_std=$CXXSTD nls=$NLS jobs=2"
|
||||
- scons cxxtool=$CXX --debug=time build=release extra_flags_config="$EXTRA_FLAGS_ALL" extra_flags_release="$EXTRA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS cxx_std=$CXXSTD nls=$NLS jobs=2
|
||||
- "export DISPLAY=:99.0"
|
||||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1024x768x24"
|
||||
- if [ "$CPP_TESTS" = true ]; then ./utils/travis/test_wrapper.sh; fi
|
||||
|
|
|
@ -49,7 +49,6 @@ option(ENABLE_CAMPAIGN_SERVER "Enable compilation of campaign server")
|
|||
option(ENABLE_SERVER "Enable compilation of server" ON)
|
||||
option(ENABLE_TOOLS "Enable building and installation of tools for artists and WML maintainers")
|
||||
option(ENABLE_SDL2_TOOLS "Enable building and installation of tools for testing with SDL2" OFF)
|
||||
option(ENABLE_SDL2 "Enable building the game with SDL2" ON)
|
||||
option(ENABLE_TESTS "Build unit tests")
|
||||
option(ENABLE_NLS "Enable building of translations" ON)
|
||||
option(ENABLE_LOW_MEM "Reduce memory usage by removing extra functionality" OFF)
|
||||
|
@ -60,17 +59,14 @@ option(ENABLE_LIBPNG "Enable support for writing png files (screenshots, images)
|
|||
option(ENABLE_LIBINTL "Enable using libintl for translations instead of Boost.Locale library (not recommended)" OFF)
|
||||
option(ENABLE_HISTORY "Enable using GNU history for history in lua console" ON)
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT CYGWIN)
|
||||
find_package(SDL2 2.0.2 REQUIRED)
|
||||
else (UNIX AND NOT APPLE AND NOT CYGWIN)
|
||||
find_package(SDL2 2.0.4 REQUIRED)
|
||||
endif (UNIX AND NOT APPLE AND NOT CYGWIN)
|
||||
else(ENABLE_SDL2)
|
||||
find_package(SDL 1.2.7 REQUIRED)
|
||||
endif(ENABLE_SDL2)
|
||||
|
||||
find_package(Boost 1.36 REQUIRED COMPONENTS iostreams program_options regex system)
|
||||
find_package(Boost 1.36 REQUIRED COMPONENTS iostreams program_options regex system thread)
|
||||
find_package(Boost 1.40 REQUIRED COMPONENTS random)
|
||||
|
||||
# no, gettext executables are not required when NLS is deactivated
|
||||
|
@ -79,17 +75,10 @@ find_package(Gettext)
|
|||
find_package(X11)
|
||||
|
||||
if(NOT MSVC)
|
||||
if(ENABLE_SDL2)
|
||||
#needed to get some SDL2 defines in... (as of rev31694 -D_GNU_SOURCE=1 is required!)
|
||||
set(SDL2_CONFIG "sdl2-config" CACHE STRING "Path to sdl2-config script")
|
||||
exec_program(${SDL2_CONFIG} ARGS "--cflags" OUTPUT_VARIABLE SDL2_CFLAGS)
|
||||
add_definitions(${SDL2_CFLAGS})
|
||||
else(ENABLE_SDL2)
|
||||
#needed to get some SDL defines in... (as of rev31694 -D_GNU_SOURCE=1 is required!)
|
||||
set(SDL_CONFIG "sdl-config" CACHE STRING "Path to sdl-config script")
|
||||
exec_program(${SDL_CONFIG} ARGS "--cflags" OUTPUT_VARIABLE SDL_CFLAGS)
|
||||
add_definitions(${SDL_CFLAGS})
|
||||
endif(ENABLE_SDL2)
|
||||
#needed to get some SDL2 defines in... (as of rev31694 -D_GNU_SOURCE=1 is required!)
|
||||
set(SDL2_CONFIG "sdl2-config" CACHE STRING "Path to sdl2-config script")
|
||||
exec_program(${SDL2_CONFIG} ARGS "--cflags" OUTPUT_VARIABLE SDL2_CFLAGS)
|
||||
add_definitions(${SDL2_CFLAGS})
|
||||
endif(NOT MSVC)
|
||||
|
||||
if(NOT WIN32)
|
||||
|
@ -256,13 +245,16 @@ if(MSVC AND NOT DEFINED CXX_FLAGS_MSVC)
|
|||
endif(MSVC AND NOT DEFINED CXX_FLAGS_MSVC)
|
||||
|
||||
set(CXX_FLAGS_PROJECT)
|
||||
check_compiler_has_flag(CXX_FLAGS_PROJECT "-std=c++98" HAS_COMPILER_FLAG_STD)
|
||||
check_compiler_has_flag(CXX_FLAGS_PROJECT "-std=c++11" HAS_COMPILER_FLAG_STD)
|
||||
check_compiler_has_flag(CXX_FLAGS_PROJECT "-W" HAS_COMPILER_FLAG_W)
|
||||
# MSVC's -Wall is not like gcc's, it really enables *all* warnings which include zillions for system headers and doesn't make sense.
|
||||
if(NOT MSVC)
|
||||
check_compiler_has_flag(CXX_FLAGS_PROJECT "-Wall" HAS_COMPILER_FLAG_WALL)
|
||||
endif(NOT MSVC)
|
||||
|
||||
### Set some extra var for C++11
|
||||
add_definitions(-DHAVE_CXX0X)
|
||||
|
||||
|
||||
### Set strict compiler flags.
|
||||
|
||||
|
@ -570,20 +562,11 @@ endif(ENABLE_DEBUG_WINDOW_LAYOUT)
|
|||
#
|
||||
|
||||
if(ENABLE_TOOLS OR ENABLE_GAME OR ENABLE_TESTS)
|
||||
if(ENABLE_SDL2)
|
||||
find_package( SDL2_image 2.0.0 REQUIRED )
|
||||
else(ENABLE_SDL2)
|
||||
find_package( SDL_image 1.2 REQUIRED )
|
||||
endif(ENABLE_SDL2)
|
||||
find_package( SDL2_image 2.0.0 REQUIRED )
|
||||
endif(ENABLE_TOOLS OR ENABLE_GAME OR ENABLE_TESTS)
|
||||
if(ENABLE_GAME OR ENABLE_TESTS)
|
||||
if(ENABLE_SDL2)
|
||||
find_package( SDL2_mixer 2.0.0 REQUIRED )
|
||||
find_package( SDL2_ttf 2.0.8 REQUIRED )
|
||||
else(ENABLE_SDL2)
|
||||
find_package( SDL_mixer 1.2.12 REQUIRED )
|
||||
find_package( SDL_ttf 2.0.8 REQUIRED )
|
||||
endif(ENABLE_SDL2)
|
||||
find_package( SDL2_mixer 2.0.0 REQUIRED )
|
||||
find_package( SDL2_ttf 2.0.8 REQUIRED )
|
||||
if(NOT MSVC)
|
||||
find_package(VorbisFile REQUIRED)
|
||||
find_package( PkgConfig REQUIRED )
|
||||
|
@ -594,11 +577,7 @@ if(ENABLE_GAME OR ENABLE_TESTS)
|
|||
|
||||
endif(ENABLE_GAME OR ENABLE_TESTS)
|
||||
if(ENABLE_GAME OR ENABLE_SERVER OR ENABLE_CAMPAIGN_SERVER OR ENABLE_TESTS)
|
||||
if(ENABLE_SDL2)
|
||||
find_package( SDL2_net 2.0.0 REQUIRED )
|
||||
else(ENABLE_SDL2)
|
||||
find_package( SDL_net REQUIRED )
|
||||
endif(ENABLE_SDL2)
|
||||
find_package( SDL2_net 2.0.0 REQUIRED )
|
||||
endif(ENABLE_GAME OR ENABLE_SERVER OR ENABLE_CAMPAIGN_SERVER OR ENABLE_TESTS)
|
||||
if(ENABLE_TOOLS)
|
||||
find_package( ZLIB REQUIRED )
|
||||
|
|
2
Doxyfile
2
Doxyfile
|
@ -31,7 +31,7 @@ PROJECT_NAME = "The Battle for Wesnoth"
|
|||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.13.2+dev
|
||||
PROJECT_NUMBER = 1.13.4+dev
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
|
|
|
@ -2,9 +2,9 @@ About
|
|||
=====
|
||||
|
||||
**The Battle for Wesnoth** is a Free, turn-based tactical strategy game with a
|
||||
high fantasy theme, featuring both single-player, and online/hotseat
|
||||
multiplayer combat. Fight a desperate battle to reclaim the throne of Wesnoth,
|
||||
or take hand in any number of other adventures.
|
||||
high fantasy theme, featuring both singleplayer and online/hotseat multiplayer
|
||||
combat. Fight a desperate battle to reclaim the throne of Wesnoth, or take
|
||||
hand in any number of other adventures.
|
||||
|
||||
|
||||
License
|
||||
|
|
|
@ -18,22 +18,57 @@ CHANGES
|
|||
Example contents.
|
||||
[/rasection]
|
||||
|
||||
[rasection="Support for SDL 1.2 has been dropped"]
|
||||
SDL version 1.2.x has been removed in favour of SDL version 2.0.x. The build systems have been and all code has been removed that utilised verions 1.2.x.
|
||||
[/rasection]
|
||||
|
||||
[rasection="WML/Lua compatibility changes and deprecations"]
|
||||
[list]
|
||||
[/list]
|
||||
[/rasection]
|
||||
|
||||
[rasection="SDL2 support"]
|
||||
The game now uses SDL2 for all operations. There is still a fall-back option of using SDL 1.2, but it is deprecated and slated for removal.
|
||||
[rasection="Wesnoth Formula Language update"]
|
||||
The Wesnoth formula language used by $(...) substitution, the formula= key in standard unit filters, and a few other places has received some significant updates. The changlog has the full list, but some highlights are:
|
||||
[list]
|
||||
[*]New operators for string concatenation, ranges, and testing containment
|
||||
[*]New string interpolation syntax
|
||||
[*]Dot operator can access identifier-friendly string keys in maps
|
||||
[*]Function definitions work outside FormulaAI and GUI2
|
||||
[*]Many new functions for math and string manipulation
|
||||
[*]Exponentiation operator is now right-associative (meaning 2^3^2 now produces 512 rather than 64)
|
||||
[*]Dice operator is now synced
|
||||
[*]Improved formula debugger
|
||||
[/list]
|
||||
The "fai" and "faiend" keywords are deprecated in favour of "wfl" and "wflend" respectively. This is part of a move to more clearly distinguish [wiki]Wesnoth Formula Language[/wiki], the language itself, from [wiki]FormulaAI[/wiki], which is just one use of the language in Wesnoth.
|
||||
|
||||
The attributes leader, total_movement, movement_left, and states in WFL unit variables were renamed to canrecruit, max_moves, moves, and status respectively; this was done to make them match the Lua and WML names for the same things. Also, the special attribute in WFL weapon variables is now specials. The old names will continue to work for now, but they may be removed in a future version.
|
||||
[/rasection]
|
||||
|
||||
[rasection="Hotkeys rely on scancodes"]
|
||||
The hotkey implementation has been changed to rely on scancodes over characters. This means that the hotkey configuration now uses the same physical buttons on the keyboard, regardless of the keyboard layout.
|
||||
[rasection="Changes to the AI"]
|
||||
After several development cycles of adding new and/or experimental features to the AI engine, a long overdue AI code cleanup effort was undertaken in order to facilitate future AI maintenance and development. This included a refactoring and reorganization of the code of the current AI as well as the removal of non-functional, outdated or deprecated code.
|
||||
|
||||
As a result, the format for configuring some AI parameters has been simplified. The AI functionality has also been extended in several ways. For example, it now allows Lua facets and the [modify_ai] tag has been extended to allow editing the internal jobs of the recruitment_instructions aspect.
|
||||
|
||||
While making these changes, we tried to retain backward compatibility as much as feasible. Thus, most of the changes are invisible to the WML and custom AI developer and the vast majority of existing UMC code will continue to function as is. It is, however, unavoidable that some old AIs or AI configurations will require updating. A summary of those changes is given below. For more details, see the full changelog and the Wesnoth wiki. In addition, we have set up a separate [forum thread; create first post and add link] in which you can post any questions and problems you might encounter while updating your code.
|
||||
[/rasection]
|
||||
|
||||
[rasection="New WML event"]
|
||||
A new 'unit placed' event has been added, making it possible to better implement certain kinds of event-driven behavior. [wiki=EventWML]See the wiki for details[/wiki].
|
||||
[/rasection]
|
||||
[rawarn="AI compatibility breaking changes"]
|
||||
Several old, deprecated AI components were removed:
|
||||
[list]
|
||||
[*]Several aspects that no longer did anything anyway: number_of_possible_recruits_to_force_recruit, recruitment_ignore_bad_combat, recruitment_ignore_bad_movement
|
||||
[*]The recruitment aspect; however, this is automatically translated to a recruitment_instructions aspect, which means it will now be honored where it was previously ignored
|
||||
[*]Stages that were either old and deprecated or experimental and unmaintained/broken: old recruitment, fallback, Strategy Formulation with RCA
|
||||
[*]Candidate actions that were either old and deprecated or experimental and unmaintained/broken: old recruitment, simple move-to-targets, global fallback, Akihara recruitment, experimental recruitment
|
||||
[*]The protect_my_unit [goal] type
|
||||
[/list]
|
||||
|
||||
A number of changes were made to the way Lua AI components are declared in the WML and how the code is called by the engine. The wiki documents the new method in detail, but unfortunately these changes are likely to break some AIs. However, the fixes will be simple in most cases:
|
||||
[list=1]
|
||||
[*]If you used old-style Lua candidate actions (with execute= and evaluate= keys) together with an explicit [engine]name=lua tag, they will no longer work as written. In many cases, the fix will be simple - the code within [engine] frequently begins with a line similar to [c]local ai = ...[/c]. Simply removing this line will be sufficient to fix most such cases; if you accessed a "data" variable in the AI, see the Experimental AI definition (data/ai/ais/ai_generic_rush.cfg) for an example of how to fix it.
|
||||
[*]If you used old-style Lua candidate actions without an explicit engine tag, you may need to remove a line similar to [c]local ai = ...[/c] from some of your code. In general, anything of the form [c]ai = <something>[/c] needs to be changed.
|
||||
[*]If you used new-style (external) Lua candidate actions (with a location key), they will still work for the time being (provided that they use the exec_parms= and/or eval_parms= keys), but we recommend updating them nevertheless. The exec_parms= and eval_parms= keys should be combined into an [args] tag, and the argument list of your execution and evaluation functions should be changed from [c](ai, cfg, self)[/c] to [c](cfg, data)[/c]. The data parameter is the same as self.data was before; however, provided you do not have an explicit [engine] tag, self.data will continue to work.
|
||||
[/list]
|
||||
[/rawarn]
|
||||
|
||||
==========
|
||||
KNOWN BUGS
|
||||
|
@ -44,14 +79,12 @@ KNOWN BUGS
|
|||
|
||||
[list]
|
||||
[*]The MP server has trouble with Local player types in campaigns (bug [bug]21965[/bug]). We have decided to postpone dealing with this. In the meantime, you might try assigning such sides to the host, or running multiple instances of Wesnoth.
|
||||
[*]2p mp survival map Dark Forecast is bugged (24200).
|
||||
[/list]
|
||||
[*]Doubled-up GUI1 dialogs don't redraw the secondary in SDL2 (bug [bug]24294[/bug]).
|
||||
[*]Window state handling inconsistent across different OSes in SDL2 builds (bug [bug]24270[/bug]).
|
||||
[*]Area under Objectives not redrawn on resize (bug [bug]24261[/bug]).
|
||||
[*]Menu and Action buttons disappear on resize (bug [bug]24260[/bug]).
|
||||
[*]Wesnoth does not exist on the panel (bug [bug]24202[/bug]).
|
||||
[*]SDL2 build handled input incorrectly once window focus is lost when menus are open (bug [bug]24212[/bug]).
|
||||
[*]"Name of Game" bleeds onto "Player/Type" label when leaving/entering the app (bug [bug]24437[/bug]).
|
||||
[*]Inconsistent transparency on credits screen (bug [bug]24428[/bug]).
|
||||
[*]options become black on hover (bug [bug]24478[/bug]).
|
||||
[*]Dialog In Replay Clearing Issue (bug [bug]24491[/bug]).
|
||||
[/list]
|
||||
[/raissue]
|
||||
|
||||
[raissue="Carried over from 1.12.x"]
|
||||
|
@ -59,8 +92,6 @@ KNOWN BUGS
|
|||
|
||||
[list]
|
||||
[*]The game can crash when planning recruits in Planning Mode.
|
||||
[*]It’s not possible to clear some default hotkeys with the Clear Hotkey option (bug [bug]21983[/bug]).
|
||||
[*]Attempting to assign hotkeys including both the Ctrl and Alt modifiers does not work (bug [bug]22219[/bug]).
|
||||
[/list]
|
||||
|
||||
[b]Bugs specific to Microsoft Windows:[/b]
|
||||
|
|
49
SConstruct
49
SConstruct
|
@ -103,13 +103,12 @@ opts.AddVariables(
|
|||
BoolVariable('ccache', "Use ccache", False),
|
||||
('ctool', 'Set c compiler command if not using standard compiler.'),
|
||||
('cxxtool', 'Set c++ compiler command if not using standard compiler.'),
|
||||
BoolVariable('cxx0x', 'Use C++0x features.', False),
|
||||
EnumVariable('cxx_std', 'Target c++ std version', '11', ['11', '14', '1y']),
|
||||
BoolVariable('openmp', 'Enable openmp use.', False),
|
||||
BoolVariable("fast", "Make scons faster at cost of less precise dependency tracking.", False),
|
||||
BoolVariable("lockfile", "Create a lockfile to prevent multiple instances of scons from being run at the same time on this working copy.", False),
|
||||
BoolVariable("OS_ENV", "Forward the entire OS environment to scons", False),
|
||||
BoolVariable("history", "Clear to disable GNU history support in lua console", True),
|
||||
BoolVariable("sdl2", "Build with SDL2 support (experimental!)", True)
|
||||
BoolVariable("history", "Clear to disable GNU history support in lua console", True)
|
||||
)
|
||||
|
||||
#
|
||||
|
@ -223,6 +222,7 @@ You can make the following special build targets:
|
|||
data-dist = make data tarball as wesnoth-data.tar.bz2 (*).
|
||||
binary-dist = make data tarball as wesnoth-binaries.tar.bz2 (*).
|
||||
wesnoth-bundle = make Mac OS application bundle from game (*)
|
||||
windows-installer = create Windows distribution with NSIS (*)
|
||||
sanity-check = run a pre-release sanity check on the distribution.
|
||||
manual = regenerate English-language manual and, possibly, localized manuals if appropriate xmls exist.
|
||||
|
||||
|
@ -363,31 +363,18 @@ if env["prereqs"]:
|
|||
conf.CheckLib("vorbis")
|
||||
conf.CheckLib("mikmod")
|
||||
|
||||
if env['sdl2']:
|
||||
def have_sdl_net():
|
||||
return \
|
||||
conf.CheckSDL(require_version = SDL2_version) & \
|
||||
conf.CheckSDL("SDL2_net", header_file = "SDL_net")
|
||||
|
||||
def have_sdl_other():
|
||||
return \
|
||||
conf.CheckSDL(require_version = SDL2_version) & \
|
||||
conf.CheckSDL("SDL2_ttf", header_file = "SDL_ttf") & \
|
||||
conf.CheckSDL("SDL2_mixer", header_file = "SDL_mixer") & \
|
||||
conf.CheckSDL("SDL2_image", header_file = "SDL_image")
|
||||
def have_sdl_net():
|
||||
return \
|
||||
conf.CheckSDL(require_version = SDL2_version) & \
|
||||
conf.CheckSDL("SDL2_net", header_file = "SDL_net")
|
||||
|
||||
else:
|
||||
def have_sdl_net():
|
||||
return \
|
||||
conf.CheckSDL(require_version = '1.2.10') & \
|
||||
conf.CheckSDL('SDL_net')
|
||||
|
||||
def have_sdl_other():
|
||||
return \
|
||||
conf.CheckSDL(require_version = '1.2.10') & \
|
||||
conf.CheckSDL("SDL_ttf", require_version = "2.0.8") & \
|
||||
conf.CheckSDL("SDL_mixer", require_version = '1.2.12') & \
|
||||
conf.CheckSDL("SDL_image", require_version = '1.2.0')
|
||||
def have_sdl_other():
|
||||
return \
|
||||
conf.CheckSDL(require_version = SDL2_version) & \
|
||||
conf.CheckSDL("SDL2_ttf", header_file = "SDL_ttf") & \
|
||||
conf.CheckSDL("SDL2_mixer", header_file = "SDL_mixer") & \
|
||||
conf.CheckSDL("SDL2_image", header_file = "SDL_image")
|
||||
|
||||
if env["libintl"]:
|
||||
def have_i18n_prereqs():
|
||||
|
@ -427,7 +414,8 @@ if env["prereqs"]:
|
|||
conf.CheckPango("cairo", require_version = "1.21.3") & \
|
||||
conf.CheckPKG("fontconfig") & \
|
||||
conf.CheckBoost("program_options", require_version="1.35.0") & \
|
||||
conf.CheckBoost("regex", require_version = "1.35.0") \
|
||||
conf.CheckBoost("thread") & \
|
||||
conf.CheckBoost("regex") \
|
||||
or Warning("WARN: Client prerequisites are not met. wesnoth, cutter and exploder cannot be built")
|
||||
|
||||
have_X = False
|
||||
|
@ -517,11 +505,8 @@ for env in [test_env, campaignd_env, client_env, env]:
|
|||
if "gcc" in env["TOOLS"]:
|
||||
env.AppendUnique(CCFLAGS = Split("-W -Wall"), CFLAGS = ["-std=c99"])
|
||||
|
||||
if env['cxx0x']:
|
||||
env.AppendUnique(CXXFLAGS = "-std=c++0x")
|
||||
env.Append(CPPDEFINES = "HAVE_CXX0X")
|
||||
else:
|
||||
env.AppendUnique(CXXFLAGS = "-std=c++98")
|
||||
env.AppendUnique(CXXFLAGS = "-std=c++" + env["cxx_std"])
|
||||
env.Append(CPPDEFINES = "HAVE_CXX0X")
|
||||
|
||||
if env['openmp']:
|
||||
env.AppendUnique(CXXFLAGS = ["-fopenmp"], LIBS = ["gomp"])
|
||||
|
|
368
changelog
368
changelog
|
@ -1,4 +1,315 @@
|
|||
Version 1.13.2+dev:
|
||||
Version 1.13.4+dev:
|
||||
* Language and i18n:
|
||||
* Updated translations:
|
||||
* Removed support for SDL 1.2. SDL 2 is now the only supported version.
|
||||
* Terrains:
|
||||
* Changed terrain code of Desert Mountains from Mdy to Mdd.
|
||||
* WML engine:
|
||||
* Fix some issues with [foreach]
|
||||
* Fix some issues with backstab-like weapon specials
|
||||
* Support [effect]times=<integer>
|
||||
* Add highlight=yes|no to [scroll_to], [scroll_to_unit], [message]
|
||||
Defaults to no in the first two cases, yes in the third
|
||||
If yes, the target hex is outlined.
|
||||
* New ~SCALE_INTO(w,h) IPF which preserves aspect ratio, using bilinear
|
||||
interpolation scaling.
|
||||
* New ~SCALE_INTO_SHARP(w,h) IPF which preserves aspect ratio, using
|
||||
nearest neighbor scaling.
|
||||
* Support delayed_variable_substitution= in [on_undo], [on_redo]
|
||||
Note that this means $unit.x and $unit.y may not reflect the unit's
|
||||
true location, so using [unstore_unit] on $unit may have unexpected effects.
|
||||
This applies to $second_unit too. The $x1, $y1, $x2, $y2 variables work fine
|
||||
though, so in most cases they can be used instead. Anything else in $unit
|
||||
or $second_unit is also fine.
|
||||
* formula= in SUF can now reference $other_unit via the formula variable "other"
|
||||
* formula= now supported in location, side, and weapon filters
|
||||
* Weapon filters now support number, parry, accuracy, and movement_used
|
||||
* New [has_attack] in standard unit filters; supercedes has_weapon= and
|
||||
uses full weapon filter.
|
||||
* lua_function=var.member now works in SUF; however, 'var' still needs to
|
||||
be a global variable.
|
||||
* AiWML:
|
||||
* Simplified aspect syntax which works for all aspects, present and future:
|
||||
* All aspects with simple values can be specified as key=value
|
||||
* Except attacks, all aspects with complex values have a simple tag form
|
||||
containing only the aspect value (e.g. [avoid])
|
||||
* All aspects, simple and complex, can be specified using a tag named by
|
||||
the aspect, whose contents is the same as a corresponding [facet]
|
||||
* The full [aspect] and [facet] syntax also still works
|
||||
* [ai] configs no longer recognize the version= key
|
||||
* ai_algorithm key now selects a preset AI; possible values include
|
||||
"ai_default_rca", "experimental_ai", and "idle_ai", but custom AIs
|
||||
defined by eras or modifications with an [ai] tag can also be used
|
||||
* [leader_goal] now automatically sets facet ID for auto_remove
|
||||
(Only if using simplified syntax; in full syntax, the ID must still be
|
||||
specified in two places.)
|
||||
* The AI config in the gamestate inspector is now split into multiple
|
||||
sections according to the type of component.
|
||||
* The following deprecated components have been removed:
|
||||
* recruitment stage (name=ai_default::recruitment)
|
||||
* old recruitment candidate action
|
||||
(name=ai_default_rca::aspect_recruitment_phase)
|
||||
* old simple move-to-targets candidate action
|
||||
(name=ai_default_rca::simple_move_and_targeting_phase)
|
||||
* number_of_possible_recruits_to_force_recruit aspect
|
||||
* recruitment_ignore_bad_combat aspect
|
||||
* recruitment_ignore_bad_movement aspect
|
||||
* The recruitment aspect has been removed from the engine; however,
|
||||
"recruitment" is now accepted as a synonym for
|
||||
"recruitment_instructions". Old code should work without changes.
|
||||
* [goal]name=protect_my_unit
|
||||
* The following experimental components have been removed:
|
||||
(Most of these were broken or non-functional)
|
||||
* Experimental recruitment candidate action
|
||||
This worked, but was worse than the default recruitment.
|
||||
* Global fallback candidate action
|
||||
* Akihara recruitment candidate action
|
||||
* Fallback stage (functional but useless)
|
||||
* Strategy Formulation with RCA stage
|
||||
This worked, but was slow and unmaintained. If a maintainer
|
||||
steps up, it may be restored later.
|
||||
* Several of the development AIs available in debug mode in the
|
||||
multiplayer setup menu
|
||||
* The "Strong AI" has been renamed to "Default AI (RCA) with Alternate
|
||||
Recruiting" and is now only available in debug mode.
|
||||
* Lua components (goals, aspects, stages, and candidate actions)
|
||||
now support an [args] subtag, which passes any sort of WML data
|
||||
to the Lua code - this works similarly to [args] in a [lua] tag,
|
||||
though not quite identically.
|
||||
* Aspect syntax with [aspect] tag has been fully generalized so that
|
||||
[aspect], [facet], and [default] are all exactly the same in terms of
|
||||
what contents they expect. (Except that [facet] additionally takes
|
||||
turns= and time_of_day=.) The most useful consequences of this:
|
||||
* You can nest a [facet] inside another [facet] if the outer one has
|
||||
name=composite_aspect
|
||||
* You can define a [facet] with the Lua engine
|
||||
* invalidate_on_tod_change= has been implemented for aspects. It applies
|
||||
when the bonus resulting from time of day modifiers (excluding any
|
||||
illumination abilities) changes at any location on the map. Thus, it
|
||||
occurs on average less often than invalidate_on_turn_start and may
|
||||
be best combined with an explicit invalidate_on_turn_start=no.
|
||||
* Minor shorthands have been introduced in the recruitment_instructions
|
||||
aspect:
|
||||
* [pattern] tag is like [recruit] with pattern=yes
|
||||
* [total] tag is like [recruit] with total=yes
|
||||
* If no [recruit] tag is present, a default is added with importance=0.
|
||||
This means recruitment will happen even with only [limit] tags.
|
||||
* Extensions to [modify_ai]:
|
||||
* [modify_ai]action=change works on aspects, using path=aspect[id].
|
||||
Useful if you need to change all the facets at once, but note that
|
||||
it also wipes the [default] facet.
|
||||
* The [default] facet can be referenced as facet[default_facet].
|
||||
This should rarely be needed, but is there in case it is.
|
||||
It also makes them easily identifiable in the inspector.
|
||||
* Automatically copy over the id= with action=change, if the
|
||||
new component did not specify one.
|
||||
* It can add, remove, and change jobs and limits in the
|
||||
recruitment_instructions aspect. The path to use for this is
|
||||
aspect[recruitment_instructions].facet[facet_id].recruit[recruit_id].
|
||||
(For a limit, replace "recruit" with "limit".)
|
||||
The [recruit] and [limit] tags now support id keys for this.
|
||||
* Lua API:
|
||||
* wesnoth.match_unit can now take a location (rather than a unit) as
|
||||
the optional third parameter. This will cause the filter to consider
|
||||
the unit to be on that location instead of its actual location.
|
||||
This even works for units on a recall list.
|
||||
* wesnoth.highlight_hex is no longer deprecated, but its effect is
|
||||
slightly different from the old one. It outlines a hex, nothing more.
|
||||
* wesnoth.select_hex is now deprecated in favour of the new
|
||||
wesnoth.select_unit (or u:select_unit). The effect is almost the same
|
||||
(with the exception that it does not outline the hex if true is
|
||||
passed as the second argument), but this name change was done to
|
||||
emphasize that it acts on a unit, more than a location.
|
||||
* New wesnoth.set_time_of_day function which sets the current time
|
||||
of day, taken either as the time ID (eg "second_watch") or the index
|
||||
of the time in the overall schedule (eg, 1 would be dawn in the default
|
||||
schedule). Optional second argument takes a time area ID, to set
|
||||
local instead of global time.
|
||||
* New wesnoth.add_fog and wesnoth.remove_fog functions allow changing fog
|
||||
on the map. The [lift_fog] and [clear_fog] tags now use this.
|
||||
* New wesnoth.add_sound_source, wesnoth.remove_sound_source, and
|
||||
wesnoth.get_sound_source functions to allow manipulation of sound
|
||||
sources. The [sound_source] and [remove_sound_source] now use these.
|
||||
* New wesnoth.log function for printing log messages. The [wml_message]
|
||||
and [deprecated_message] tags now use this.
|
||||
* WML tables defined in Lua now accept string keys with array values
|
||||
(where "array" is a table whose keys are all integers). This joins
|
||||
the elements of the array with commas and produces a single string
|
||||
value. eg {x = {1,2,3}} is equivalent to {x = "1,2,3"}.
|
||||
* wesnoth.effects table can now be used to alter the behaviour of
|
||||
built-in effects - for example, to add a new feature to
|
||||
[effect]apply_to=attack. It also now supports effect descriptions,
|
||||
for use by the [trait] tag.
|
||||
* Additional fields in unit proxy:
|
||||
* usage, cost - self-explanatory
|
||||
* traits - list of the IDs of all traits
|
||||
* abilities - list of the IDs of all abilities
|
||||
* Additional fields in table returned by wesnoth.get_terrain_info:
|
||||
* icon, editor_image, light
|
||||
* Additional fields in unit type proxu:
|
||||
* race, id, alignment
|
||||
* attacks, which returns the same thing as unit.attacks
|
||||
* abilities, same as unit.abilities
|
||||
* LuaAI:
|
||||
* The table returned by check_*() now has a "result" field which
|
||||
gives a description of the action's result; similar to "status"
|
||||
but more descriptive.
|
||||
* Target tables now use a descriptive name for "type", instead of
|
||||
an integer. This applies to the elements of the table returned by
|
||||
ai.get_targets() and also to the elements of tables returned by
|
||||
Lua goals. (However, Lua goals that used integers will continue
|
||||
to work for now.)
|
||||
* There are some compatibility-breaking changes to Lua candidate
|
||||
actions - see the release notes or the wiki for full details.
|
||||
* Lua AI code can now access the AI routines through the global ai
|
||||
object. This object is only accessible to AI code; it does not exist
|
||||
in the general Lua global scope.
|
||||
* When executing Lua goals, aspects, or candidate action evaluations,
|
||||
the ai is in "read-only" mode - the read_only key is set to true,
|
||||
and functions that change the gamestate, such as ai.attack(), are
|
||||
missing from the table. (ai.check_*() functions are still present.)
|
||||
* The ai.aspects table provides access to every aspect known by the
|
||||
engine, including several that previously did not have corresponding
|
||||
ai.get_*() functions. You access them as ai.aspects.avoid, for example.
|
||||
The table is read-only and raises an error if you attempt to write to it.
|
||||
* The way to create Lua candidate actions has changed a little. Old code
|
||||
will require minor changes.
|
||||
* New wesnoth.micro_ais table contains the loaders for all Micro AIs.
|
||||
New loaders can easily be installed by add-ons. See any built-in
|
||||
micro AI (in ai/micro_ais/mai-defs/) for an example of how to do this.
|
||||
* The attacks aspect can now be defined as a Lua aspect. The code
|
||||
should return a table with keys "own" and "enemy", each of which may
|
||||
be either a unit filter table or a function which takes a unit as a
|
||||
parameter and returns true or false.
|
||||
* Wesnoth formula engine:
|
||||
* Formulas in unit filters can now access nearly all unit attributes
|
||||
The following attributes were renamed (old names still work, for now):
|
||||
leader -> canrecruit
|
||||
total_movement -> max_moves
|
||||
movement_left -> moves
|
||||
states -> status
|
||||
* Nearly all unit type, side, weapon, and terrain attributes available
|
||||
to Lua code are now also exposed to WFL. The exceptions are mainly
|
||||
translatable strings.
|
||||
* Unit and side WML variables are now accessible under "wml_vars".
|
||||
Since WML variables don't easily translate to formula variables, the
|
||||
special attributes __all_children, __children, and __attributes provide
|
||||
specialized views of the variables config as a list, string-list map,
|
||||
and string-value map, respectively.
|
||||
* The 'special' attribute of weapons was renamed to 'specials', and it now
|
||||
contains the special IDs rather than their translateable names.
|
||||
* New syntax features:
|
||||
* String interpolation syntax. Within a formula string (enclosed in
|
||||
'single quotes'), the syntax [some_formula] interpolates the result
|
||||
of the inner formula into the string. (The simplest use case is
|
||||
interpolating the values of variables.)
|
||||
* String can now escape special characters:
|
||||
['] single quote, [(] open square bracket, [)] close square bracket
|
||||
* New 'in' operator which tests if a list contains an item or if a map
|
||||
contains a key.
|
||||
* New concatenation operator a..b which works on strings and lists
|
||||
* New range operator a~b which produces a list of consecutive integers
|
||||
This can also be used for "list slicing" - eg my_list[3~5] returns
|
||||
a new list containing elements 3 through 5 of my_list.
|
||||
* Lists can be used as an index for a list. This is "selection indexing"
|
||||
and returns a new list with only the elements specified by the indexing
|
||||
list.
|
||||
* Function definitions (using the def keyword) are now supported in all
|
||||
formula contexts, which means that they can be used outside FormulaAI.
|
||||
However, non-FormulaAI functions are currently local to the formula
|
||||
that declares them.
|
||||
* Maps containing string keys that are valid identifiers can now be
|
||||
indexed with the dot operator instead of the indexing operator.
|
||||
* Strings can now be indexed via 'string'.char[n]. Also supported are
|
||||
'string'.word[n] and 'string'.item[n] (the latter splits on commas)
|
||||
* Changes to core functions:
|
||||
* head() takes an optional argument - if present, a sublist is returned.
|
||||
* abs(), max(), min() now work on decimal numbers
|
||||
* reduce() function can specify an optional initial accumulator
|
||||
This will be returned for an empty list instead of null.
|
||||
* substring() function can now accept a negative size parameter
|
||||
This counts backwards from the specified offset
|
||||
A size of -1 is the same as 1.
|
||||
* if() can take two arguments; returns null if the condition is false
|
||||
* tolist() will now invert the effect of tomap()
|
||||
* debug_print() now shows in console if debug mode is on
|
||||
* New core functions:
|
||||
* Trig functions tan, acos, asin, atan have been added. (Sin and cos
|
||||
existed since at least 1.9 but were undocumented until very recently.)
|
||||
* Other common math functions - root(), sqrt(), cbrt(), log(), exp()
|
||||
* hypot(x,y) function calculates sqrt(x*x+y*y) with minimal error
|
||||
* pi() returning the circle ratio
|
||||
* tail() - opposite of head()
|
||||
* reverse() function for strings and lists
|
||||
* zip() function - converts [[1,2,3],[4,5,6]] to [[1,4],[2,5],[3,6]]
|
||||
* take_while() function returns items from a list until the first one
|
||||
that fails a condition
|
||||
* find_string() locates a substring within a string
|
||||
* replace() replaces a sequence within a string
|
||||
* type() function checks the type of a formula result
|
||||
* distance_between() - this was promoted from FormulaAI to core
|
||||
* pair() function that produces a key-value pair suitable for
|
||||
passing to tomap() - this also means key-value pairs are now
|
||||
serializable (relevant in FormulaAI)
|
||||
* Bugfixes:
|
||||
* Dice operator is now synced (where possible)
|
||||
* Modulus (%) operator now works on decimal numbers
|
||||
* Exponentiation (^) operator is now right-associative
|
||||
* Fix several math operations returning a very large negative number when
|
||||
the operation was invalid (for example, (-2) ^ 0.5).
|
||||
Now they return null instead.
|
||||
* Formula debugger (accessed with the debug() function):
|
||||
* Now works again, but is skipped unless in debug mode
|
||||
* No longer explodes on formulas using less-than
|
||||
* Some better information and formatting in the execution trace
|
||||
* You can now step through "where" variable assignments
|
||||
* FormulaAI no longer starts recruiting if a formula evaluates to the
|
||||
string 'recruit'.
|
||||
* Deprecated:
|
||||
* The fai/faiend keywords are deprecated as part of a move to clearly
|
||||
define the difference between "Wesnoth Formula Language", the language,
|
||||
and "FormulaAI", the AI engine that uses the language. For now they
|
||||
still work, but any future code should use wfl/wflend instead.
|
||||
Use of the .fai file extension is still fine for FormulaAI code;
|
||||
for other formula code in a separate file, .wfl is recommended instead.
|
||||
* Miscellaneous and bug fixes:
|
||||
* Resolve translated logo images not being used (bug #24357)
|
||||
* Ported the "hexometer" tool from Bash to Python 3
|
||||
|
||||
Version 1.13.4:
|
||||
* Language and i18n:
|
||||
* Updated translations: British English, Russian
|
||||
* Graphics:
|
||||
* Improved or new terrain graphics: Mine Walls (replaces Hewn Cave Walls).
|
||||
* User interface:
|
||||
* Fix vertical alignment of individual attacks listed in the Attack Unit
|
||||
dialog.
|
||||
* Removed extra padding below unit stats in the Attack Unit dialog.
|
||||
* Miscellaneous and bug fixes:
|
||||
* Fix non-deterministic crashes in the Attack Unit dialog resulting from
|
||||
invalid memory references (regression introduced in 1.13.3).
|
||||
* Fix uninitialized variable in event handling code (bug #24498).
|
||||
|
||||
Version 1.13.3:
|
||||
* Greatly improved SDL 2 support. SDL 2 is now used by default build when
|
||||
building. This fixes the following bugs, among others:
|
||||
* Bug #18112: Color cursors cause slow mouse movement at menus
|
||||
* Bug #19666: When I resize windows during dialog I lose the menu buttons
|
||||
* Bug #20332: Cursor not mapping correctly on Retina display when in Windowed mode
|
||||
* Bug #23820: SDL 2 alpha blending issues
|
||||
* Bug #23821: Text input is broken in GUI1 under SDL 2
|
||||
* Bug #23908: SDL 2 SDL_BlitSurface causes crashes
|
||||
* Bug #23918: UI graphics garbled on OS X 10.11 El Capitan
|
||||
* Bug #23934: Resize actions are laggy
|
||||
* Bug #24138: SDL 2 crash on resize after starting a game and returning to the title screen
|
||||
* Bug #24209: Screen becomes black upon minimise and restore
|
||||
* bug #24212: SDL 2 build handled input incorrectly once window focus is lost when menus are open
|
||||
* Bug #24213: SDL 2 build leaves menu items stuck if window dimensions change while open
|
||||
* Bug #24214: SDL 2 build handles fullscreen toggle badly
|
||||
* Bug #24260: Menu and Action buttons disappear on resize
|
||||
* Bug #24261: Area under Objectives not redrawn on resize
|
||||
* Bug #24294: Doubled-up GUI1 dialogs don't redraw the secondary in SDL2
|
||||
* Bug #24477: Segfault when launching Credits
|
||||
* Campaigns:
|
||||
* Liberty:
|
||||
* Added some animations for the Rogue Mage line.
|
||||
|
@ -12,39 +323,62 @@ Version 1.13.2+dev:
|
|||
* Updated sprites for Naga Hunter, Naga Guardian line, Crab Man.
|
||||
* Crab Man changed to Monster Crab.
|
||||
* Graphics:
|
||||
* Improved terrain graphics: Stones with Sand Drifts, Igloo Village.
|
||||
* Improved or new terrain graphics: Stones with Sand Drifts, Igloo Village,
|
||||
Adobe Village.
|
||||
* Added option for toggling off water animations to Preferences -> Display.
|
||||
* Language and i18n:
|
||||
* Updated translations: British English, Russian, Swedish
|
||||
* New translation: Asturian
|
||||
* Updated translations: British English, Galician, Russian, Swedish
|
||||
* Sound effects:
|
||||
* Fixed various subtle timing problems with attack sounds.
|
||||
* Terrains:
|
||||
* New terrain: Desert Mountains (Mdy), Impassable Desert Mountains (Mdy^Xm),
|
||||
Ruined Adobe Village (^Vdr).
|
||||
* User Interface:
|
||||
* GUI1 comboboxes now use the thinner menu frame style.
|
||||
* Implemented a new GUI2 Attack dialog
|
||||
* Added gui2 comboboxes.
|
||||
* Removed prompt when purging the WML cache from Preferences.
|
||||
* Implemented a new GUI2 Preferences dialog
|
||||
* Implemented a new font scaling option on the Display panel.
|
||||
* Selecting an entry in the friend/ignore list panel now copies it to the
|
||||
input field; this makes it easier to edit friend/ignore notes.
|
||||
* WML engine:
|
||||
* Added new event "unit placed", which triggers when (and regardless of how)
|
||||
a unit appears on the map.
|
||||
* Added support for color= in [unstore_unit] and [print]
|
||||
* removed network and network_ai controlles, whether a side is networked is
|
||||
now stored in the is_local attribute.
|
||||
* Eventnames (received in wesnoth.game_events.on_event) now have all their
|
||||
spaces replaced with underscores.
|
||||
* lua can now read/write the 'persistent' attribute of sides.
|
||||
* lua can now read/write the 'alignment' attribute of units.
|
||||
* Added {CURRENT_FILE} and {CURRENT_DIRECTORY} macros.
|
||||
* add support for relative dirs in wesnoth.dofile/require
|
||||
* Added name= and write_name= attributes in [item]
|
||||
* Added description_alignment= key to [campaign]
|
||||
* Fixed [put_to_recall_list] not working correctly (bug #24390)
|
||||
* Miscellaneous and bug fixes:
|
||||
* Fix the new log code on Windows to actually use Unicode-aware functions
|
||||
in a couple of places so Wesnoth does not quit on startup when trying to
|
||||
relocate the log file to a path with Unicode characters (bug #22897,
|
||||
definitely fixed this time).
|
||||
* Decreased high memory consumption caused by the animated water.
|
||||
* Fix bug #23108: exclude aborted attacks from statistics
|
||||
* imgcheck now runs on Python 3
|
||||
* Fix bug #15259: Secondary click uses control-click instead of command-click in OS X
|
||||
* New hi-res icon using new logo for OS X
|
||||
* Greatly improved SDL2 support. SDL2 is now the default build.
|
||||
* Fix bug #18112: Color cursor causes slow mouse movement at menus
|
||||
* Fix bug #20332: Cursor not mapping correctly on Retina display when in Windowed mode
|
||||
* Fix bug #24138: SDL2 crash on resize after starting a game and returning to title screen
|
||||
* Fix bug #23908: SDL2 SDL_BlitSurface Causes Crashes
|
||||
* Fix bug #23821: Text input is broken in GUI1 under SDL2
|
||||
* Fix bug #23820: SDL 2 alpha blending issues
|
||||
* Fix bug #23918: UI Graphics in OS X 10.11 El Capitan
|
||||
* Fix bug #18112: Color cursor causes slow mouse movement at menus
|
||||
* Fix bug #24214: SDL2 build handles fullscreen toggle badly
|
||||
* Fix bug #23934: Resize actions are laggy
|
||||
* Fix bug #24209: Screen becomes black upon minimise and restore
|
||||
* Fix bug #24213: SDL2 build leaves menu items stuck if window dimensions change when open
|
||||
* Removed the "wmlmove" Python tool
|
||||
* Fixed [event] in [unit_type].
|
||||
* Fixed oos caused by mp replay turn feature.
|
||||
* Fixed oos bugs caused by plattform dependent rounding from double to int in lua.
|
||||
* Fixed custom (lua-defined) scenario tags beeing removed from [replay_start]
|
||||
* Fixed savefile bloat caused by unit variations (walking corpses)
|
||||
* Fixed preferences file bloat caused by null-command hot-keys (bug #21969)
|
||||
* Fixed clearing of default hot-keys not working (bugs #21983/#22218/#23981)
|
||||
* Fix [for] not correctly handling the case when the array length changes
|
||||
during iteration
|
||||
* Fix variables in [message][command] (bug #24288)
|
||||
|
||||
Version 1.13.2:
|
||||
* Add-ons client:
|
||||
|
@ -158,7 +492,7 @@ Version 1.13.2:
|
|||
* WML engine:
|
||||
* controller= in side filters can now be used in mp games for unsynced code
|
||||
* Added [effect] apply_to=max_expereince set=<value>
|
||||
* Added enable_if= to mod and era events
|
||||
* Added enable_if= to mod and era events
|
||||
* Added $varname?default_value| in variable substitution
|
||||
* Fixed side_for= parameter in [message]s with input
|
||||
* New actionwml tag [on_undo] contains actionswml that is executed when the
|
||||
|
|
|
@ -56,15 +56,9 @@
|
|||
|
||||
#ifdef TEST
|
||||
{scenario-test.cfg}
|
||||
{scenario-formula.cfg}
|
||||
{scenario-formula-recruitment.cfg}
|
||||
{scenario-poisoning.cfg}
|
||||
{scenario-leaders.cfg}
|
||||
{scenario-movethrough.cfg}
|
||||
{ai/scenarios/scenario-AI_Arena_small.cfg}
|
||||
{ai/scenarios/scenario-test_move_to_targets.cfg}
|
||||
{ai/scenarios/scenario-lua-ai.cfg}
|
||||
{ai/scenarios/scenario-no_engine.cfg}
|
||||
{ai/scenarios/}
|
||||
{ai/micro_ais/scenarios/}
|
||||
#define DONT_RELOAD_CORE
|
||||
#enddef
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
#textdomain wesnoth
|
||||
[advanced_preference]
|
||||
field=compress_saves
|
||||
# po: Associated save_compression_short^ entries need to be as short as
|
||||
# po: possible to avoid stretching the advanced preferences list too
|
||||
# po: much.
|
||||
name= _ "Compress saved games"
|
||||
type=combo
|
||||
default=gzip
|
||||
[option]
|
||||
id=gzip
|
||||
name= _ "save_compression^Gzip"
|
||||
name_short= _ "save_compression_short^gzip"
|
||||
description= _ "save_compression_desc^Default compression, faster"
|
||||
[/option]
|
||||
[option]
|
||||
id=bzip2
|
||||
name= _ "save_compression^Bzip2"
|
||||
name_short= _ "save_compression_short^bzip2"
|
||||
description= _ "save_compression_desc^Best compression, slower"
|
||||
[/option]
|
||||
[option]
|
||||
id=none
|
||||
name= _ "save_compression^No"
|
||||
name_short= _ "save_compression_short^no"
|
||||
description= _ "save_compression_desc^Large plain text files"
|
||||
[/option]
|
||||
[/advanced_preference]
|
||||
|
@ -55,20 +49,6 @@
|
|||
default=yes
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=local_tod_lighting
|
||||
name= _ "Local time of day area lighting"
|
||||
type=boolean
|
||||
default=yes
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=startup_effect
|
||||
name= _ "Show titlescreen animation"
|
||||
type=boolean
|
||||
default=no
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=show_combat
|
||||
name= _ "Show combat"
|
||||
|
@ -87,13 +67,6 @@
|
|||
step=5
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=lobby_whisper_friends_only
|
||||
name= _ "Accept whispers from friends only"
|
||||
type=boolean
|
||||
default=no
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=lobby_auto_open_whisper_windows
|
||||
name= _ "Auto-open whisper windows in lobby"
|
||||
|
@ -108,14 +81,6 @@
|
|||
default=yes
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=flip_time
|
||||
name= _ "Reverse time graphics"
|
||||
description= _ "Choose whether the sun moves left-to-right or right-to-left"
|
||||
type=boolean
|
||||
default=no
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=scroll_to_action
|
||||
name= _ "Follow unit actions"
|
||||
|
@ -205,6 +170,18 @@
|
|||
default=no
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=advanced_graphic_options
|
||||
name= _ "Graphics scaling options"
|
||||
type=custom
|
||||
[/advanced_preference]
|
||||
|
||||
[advanced_preference]
|
||||
field=orb_color
|
||||
name= _ "Customize orb colors"
|
||||
type=custom
|
||||
[/advanced_preference]
|
||||
|
||||
#ifdef __UNUSED__
|
||||
[advanced_preference]
|
||||
field=joystick_support_enabled
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
id=ai_default_rca
|
||||
description=_"Multiplayer_AI^Default AI (RCA)" # wmllint: no spellcheck
|
||||
# RCA := Register Candidate Action; more info at http://forums.wesnoth.org/viewtopic.php?p=419625#p419625
|
||||
version=10710
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai.cfg}
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=ai_default_rca_strong
|
||||
description=_"Multiplayer_AI^Strong AI (RCA)" # wmllint: no spellcheck
|
||||
# RCA := Register Candidate Action; more info at http://forums.wesnoth.org/viewtopic.php?p=419625#p419625
|
||||
version=10710
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
{AI_CA_LEADER_SHARES_KEEP}
|
||||
[/stage]
|
||||
|
||||
### Aspects for a strong AI ###
|
||||
|
||||
{AI_SIMPLE_ALWAYS_ASPECT recruitment_diversity 0.8}
|
||||
{AI_SIMPLE_ALWAYS_ASPECT recruitment_randomness 0}
|
||||
{AI_SIMPLE_ALWAYS_ASPECT villages_per_scout 0}
|
||||
{AI_ASPECT recruitment_save_gold {AI_DEACTIVATE_SAVE_GOLD} }
|
||||
[/ai]
|
33
data/ai/dev/ai_default_rca_alternate_recruiting.cfg
Normal file
33
data/ai/dev/ai_default_rca_alternate_recruiting.cfg
Normal file
|
@ -0,0 +1,33 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai.cfg}
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=ai_default_rca_alternate_recruiting
|
||||
description=_"Multiplayer_AI^Dev AI: Default AI (RCA) with Alternate Recruiting" # wmllint: no spellcheck
|
||||
# RCA := Register Candidate Action; more info at http://forums.wesnoth.org/viewtopic.php?p=419625#p419625
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
{AI_CA_LEADER_SHARES_KEEP}
|
||||
[/stage]
|
||||
|
||||
### Aspects for a strong AI ###
|
||||
|
||||
{AI_SIMPLE_ALWAYS_ASPECT recruitment_diversity 0.8}
|
||||
{AI_SIMPLE_ALWAYS_ASPECT recruitment_randomness 0}
|
||||
{AI_SIMPLE_ALWAYS_ASPECT villages_per_scout 0}
|
||||
{AI_ASPECT recruitment_save_gold {AI_DEACTIVATE_SAVE_GOLD} }
|
||||
[/ai]
|
|
@ -1,32 +0,0 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=ai_old_recruitment
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Old Recruitment" # wmllint: no spellcheck
|
||||
# RCA := Register Candidate Action; more info at http://forums.wesnoth.org/viewtopic.php?p=419625#p419625
|
||||
version=10710
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
[candidate_action]
|
||||
id=recruitment
|
||||
engine=cpp
|
||||
name=ai_default_rca::aspect_recruitment_phase
|
||||
max_score=180000
|
||||
score=180000
|
||||
[/candidate_action]
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
{AI_CA_LEADER_SHARES_KEEP}
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -1,25 +0,0 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=ai_sf_with_rca
|
||||
description=_"Multiplayer_AI^Dev AI: Strategy formulation with RCA " # wmllint: no spellcheck
|
||||
version=11300
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=testing_ai_default::strategy_formulation_with_rca
|
||||
{AI_CA_GOTO}
|
||||
{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
{AI_CA_LEADER_SHARES_KEEP}
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -1,30 +0,0 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=ai_akihara
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Experimental Recruitment (C++ Akihara)" # wmllint: no spellcheck
|
||||
version=10800
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
[candidate_action]
|
||||
id=alternate_recruitment
|
||||
engine=cpp
|
||||
name=akihara_recruitment::recruitment
|
||||
max_score={AI_CA_RECRUITMENT_SCORE}
|
||||
score={AI_CA_RECRUITMENT_SCORE}
|
||||
[/candidate_action]
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -9,14 +9,17 @@
|
|||
id=formula_ai # id is needed to uniquely identify a MP AI, it is not needed in the scenario AI
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Experimental Recruitment (Formula AI)" # wmllint: no spellcheck
|
||||
# this description is, again, needed for MP AI (it shows in AI list under this description
|
||||
version=10710 # no spaces here, version should be parsed as int. This version marker is a sign that ALL snippets of AI are written using new syntax
|
||||
|
||||
{AI_SIMPLE_FORMULA_AI_EXPERIMENTAL_RECRUITMENT}
|
||||
[stage]
|
||||
engine=fai
|
||||
name=side_formulas
|
||||
move="{ai/formula/new_recruitment.fai}"
|
||||
[/stage]
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
{AI_CA_RECRUITMENT}
|
||||
#{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
#endif
|
||||
|
||||
[ai]
|
||||
id=default_ai_poisoning
|
||||
id=formula_ai_poisoning
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Poisoning (Formula AI)" # wmllint: no spellcheck
|
||||
version=10710
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#textdomain wesnoth-ai
|
||||
[ai]
|
||||
id=ai_idle
|
||||
id=idle_ai
|
||||
description=_"Multiplayer_AI^Dev AI: Idle AI" # wmllint: no spellcheck
|
||||
version=10703
|
||||
#well, we can just fall back to idle ai. But we can just do nothing, and it will work (if no additional stages are brought in by other config sources - MP faction config, scenario config, era config, defaults, etc
|
||||
# Needs to define at least one stage; otherwise, the default AI will be injected
|
||||
# when used with [modify_side]switch_ai
|
||||
[stage]
|
||||
name=empty
|
||||
[/stage]
|
||||
[/ai]
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=testing_ai_recruitment
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Experimental Recruitment (C++)" # wmllint: no spellcheck
|
||||
version=10800
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
[candidate_action]
|
||||
id=alternate_recruitment
|
||||
engine=cpp
|
||||
name=ai_default_rca::testing_recruitment_phase
|
||||
max_score={AI_CA_RECRUITMENT_SCORE}
|
||||
score={AI_CA_RECRUITMENT_SCORE}
|
||||
[/candidate_action]
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -35,7 +35,7 @@ def get_important_locations(ai* )
|
|||
def enemy_leaders( ai* )
|
||||
sum(
|
||||
map( enemies, 'enemy',
|
||||
filter( units_of_side[enemy], leader )
|
||||
filter( units_of_side[enemy], canrecruit )
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -123,7 +123,7 @@ def locally_normalize_to_lowest( input_map )
|
|||
# Returns a List of all enemy leaders #
|
||||
# (for some reason this function is defined twice) #
|
||||
def enemy_leaders(ai*)
|
||||
map( enemies, 'enemy_side', find(units_of_side[enemy_side], leader ) );
|
||||
map( enemies, 'enemy_side', find(units_of_side[enemy_side], canrecruit ) );
|
||||
|
||||
# UNUSED FUNCTION #
|
||||
# Returns a Map. #
|
||||
|
@ -249,7 +249,7 @@ def movement_eval(ai*, recruits_id_map)
|
|||
my_recruits_movement_cost( ai ),
|
||||
tomap( recruits_id_map,
|
||||
map( my_recruits,
|
||||
total_movement
|
||||
max_moves
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -357,7 +357,7 @@ if( vars.side_terrain,
|
|||
mark_important_locations( self,
|
||||
calculate_map_ownership(
|
||||
recruits_of_side,
|
||||
map(filter(sum(units_of_side), leader), loc),
|
||||
map(filter(sum(units_of_side), canrecruit), loc),
|
||||
4, 7, 4
|
||||
)
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ def opening(ai*)
|
|||
move(loc(11,23), loc(14,22)) ],
|
||||
if(turn = 2, [
|
||||
move(loc(11,21),loc(13,17)),
|
||||
if(unit_at(loc(11,22)).total_movement = 6,
|
||||
if(unit_at(loc(11,22)).max_moves = 6,
|
||||
move(loc(11,22),loc(13,18)),
|
||||
move(loc(11,22),loc(15,19))),
|
||||
move(loc(10,22),loc(7,19)),
|
||||
|
@ -75,7 +75,7 @@ def rate_village_capture(ai*,src,dst) village_value(ai);
|
|||
def rate_village_proximity(ai*, unit, dst)
|
||||
if(distance = 1,
|
||||
0,
|
||||
village_value(ai)/(distance/unit.total_movement + 1))
|
||||
village_value(ai)/(distance/unit.max_moves + 1))
|
||||
where distance = distance_to_nearest_unowned_village(dst);
|
||||
|
||||
def rate_move(ai*,src,dst)
|
||||
|
|
|
@ -25,7 +25,7 @@ where desired_path = shortest_path( me.loc, me.vars.next_step );
|
|||
|
||||
def move_ahead(ai*, me)
|
||||
if( enemy_units,
|
||||
if( distance_between( closest_unit(ai, me).loc, me.loc ) > me.movement_left-1,
|
||||
if( distance_between( closest_unit(ai, me).loc, me.loc ) > me.moves-1,
|
||||
move_partial(
|
||||
me.loc,
|
||||
me.vars.next_step
|
||||
|
@ -50,7 +50,7 @@ def patrol_move(ai*, me)
|
|||
move_ahead(ai,me)
|
||||
);
|
||||
|
||||
if( me.movement_left = 0,
|
||||
if( me.moves = 0,
|
||||
end,
|
||||
if(attack,
|
||||
attack,
|
||||
|
|
|
@ -8,6 +8,6 @@ def get_best_defense_loc(moves, attacker, enemy)
|
|||
attack(me.loc, get_best_defense_loc(my_moves.moves, me, target), target.loc, att_weap)
|
||||
|
||||
|
||||
where att_weap = index_of(['poison'],map(me.attacks,special))
|
||||
where att_weap = index_of(['poison'],map(me.attacks,specials))
|
||||
|
||||
faiend
|
||||
|
|
|
@ -16,7 +16,7 @@ min( map(
|
|||
)
|
||||
) +
|
||||
#leader is always a good target! #
|
||||
if( target.leader, 50, 0 ) +
|
||||
if( target.canrecruit, 50, 0 ) +
|
||||
# consider target abilities if needed #
|
||||
if( target.abilities,
|
||||
if( index_of('regenerates', target.abilities) != -1, -10, 0 ) +
|
||||
|
|
|
@ -32,7 +32,7 @@ def get_important_locations(ai* )
|
|||
def enemy_leaders( ai* )
|
||||
sum(
|
||||
map( enemies, 'enemy',
|
||||
filter( units_of_side[enemy], leader )
|
||||
filter( units_of_side[enemy], canrecruit )
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -112,7 +112,7 @@ def locally_normalize_to_lowest( input_map )
|
|||
|
||||
# look for who we fight against #
|
||||
def enemy_leaders(ai*)
|
||||
map( enemies, 'enemy_side', find(units_of_side[enemy_side], leader ) );
|
||||
map( enemies, 'enemy_side', find(units_of_side[enemy_side], canrecruit ) );
|
||||
|
||||
|
||||
def distance_to_enemies(ai*)
|
||||
|
@ -206,7 +206,7 @@ def combine_maps_div( map_A, map_B )
|
|||
[ locally_normalize_to_highest(my_recruits_defense(self)),
|
||||
tomap(map(my_recruits, id),map(my_recruits, hitpoints) ),
|
||||
locally_normalize_to_lowest(my_recruits_movement_cost(self)),
|
||||
tomap(map(my_recruits, id),map(my_recruits, total_movement))]
|
||||
tomap(map(my_recruits, id),map(my_recruits, max_moves))]
|
||||
#
|
||||
|
||||
def consider_unit_cost(ai*)
|
||||
|
@ -233,7 +233,7 @@ def movement_eval(ai*, recruits_id_map)
|
|||
my_recruits_movement_cost( ai ),
|
||||
tomap( recruits_id_map,
|
||||
map( my_recruits,
|
||||
total_movement
|
||||
max_moves
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -324,7 +324,7 @@ if( vars.side_terrain,
|
|||
mark_important_locations( self,
|
||||
calculate_map_ownership(
|
||||
recruits_of_side,
|
||||
map(filter(sum(units_of_side), leader), loc),
|
||||
map(filter(sum(units_of_side), canrecruit), loc),
|
||||
4, 7, 4
|
||||
)
|
||||
)
|
||||
|
|
|
@ -6,10 +6,10 @@ if( size(shroud) = 0,
|
|||
-5,
|
||||
if( size(enemies_in_range) != 0,
|
||||
-5,
|
||||
if( me.total_movement > 5,
|
||||
if( me.max_moves > 5,
|
||||
50,
|
||||
-5))))
|
||||
where enemies_in_range = filter( enemy_units, 'enemy', distance_between( me.loc, enemy.loc ) < me.total_movement),
|
||||
where enemies_in_range = filter( enemy_units, 'enemy', distance_between( me.loc, enemy.loc ) < me.max_moves),
|
||||
shroud = find_shroud()
|
||||
|
||||
faiend
|
||||
|
|
|
@ -2,21 +2,11 @@
|
|||
-- This is the engine used by the Lua AI when no engine is
|
||||
-- defined specifically in the [side] tag
|
||||
|
||||
return {
|
||||
get_ai = function(ai)
|
||||
local my_ai = {}
|
||||
-- This provides a cache level for the move map functions,
|
||||
-- making them a bit easier to use
|
||||
local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
|
||||
ai_stdlib.init(ai)
|
||||
|
||||
local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
|
||||
ai_stdlib.init(ai)
|
||||
|
||||
-- Make the ai table available to the eval/exec functions
|
||||
function my_ai:get_ai()
|
||||
return ai
|
||||
end
|
||||
|
||||
-- Make the persistent data table available to the eval/exec functions
|
||||
my_ai.data = {}
|
||||
|
||||
return my_ai
|
||||
end
|
||||
}
|
||||
-- This is only returned for minor backwards compatibility
|
||||
local p, d = ...
|
||||
return {data = d}
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
|
||||
example_ca = {}
|
||||
|
||||
function example_ca:eval(ai)
|
||||
wesnoth.message("External eval says hi!")
|
||||
|
||||
return 10000
|
||||
function example_ca:evaluation()
|
||||
wesnoth.message("External CA evaluation says hi.")
|
||||
|
||||
return 10000
|
||||
end
|
||||
|
||||
function example_ca:exec(ai)
|
||||
wesnoth.message("External CA exec attacks!")
|
||||
ai.attack(2, 12, 3, 12, 1, 1) -- showcasing the presence of the AI table
|
||||
function example_ca:execution()
|
||||
wesnoth.message("External CA execution attacks.")
|
||||
|
||||
-- Note that there is no check whether these attacks are possible.
|
||||
-- The CA will therefore be blacklisted the second time it gets called.
|
||||
ai.attack(2, 12, 3, 12, 1, 1)
|
||||
ai.attack(3, 13, 3, 12, 2, 1)
|
||||
ai.attack(3, 11, 3, 12)
|
||||
end
|
||||
|
||||
return example_ca
|
|
@ -440,9 +440,7 @@ return {
|
|||
return score
|
||||
end
|
||||
|
||||
function ai_cas:recruit_rushers_exec(ai_local)
|
||||
if ai_local then ai = ai_local end
|
||||
|
||||
function ai_cas:recruit_rushers_exec()
|
||||
if AH.show_messages() then W.message { speaker = 'narrator', message = 'Recruiting' } end
|
||||
|
||||
local enemy_counts = recruit_data.recruit.enemy_counts
|
||||
|
|
|
@ -1,33 +1,36 @@
|
|||
function patrol_gen(n, wp) -- n is the name of the unit, like Kiressh
|
||||
-- wp - a table of waypoint tables of form {x,y}
|
||||
function patrol_gen(n, wp)
|
||||
-- n is the name of the unit, like Kiressh
|
||||
-- wp - a table of waypoint tables of form {x,y}
|
||||
|
||||
local unit = wesnoth.get_units({name=n})[1]
|
||||
|
||||
local x, y = unit.x, unit.y
|
||||
local wpn = 1 --WayPoint Number - we have to remember which waypoint we are heading to
|
||||
wesnoth.message('data/ai/lua/patrol.lua is deprecated. Use the Patrols Micro AI instead.')
|
||||
|
||||
if (x == wp[1].x and y == wp[1].y) then
|
||||
wpn = wpn + 1
|
||||
--w1, w2 = w2, w2 -- if we are standing on the first waypoint, swap them
|
||||
end
|
||||
local unit = wesnoth.get_units({name=n})[1]
|
||||
|
||||
--local waypoints = {w1, w2} -- this form might be just received from the args
|
||||
local wpcount = # wp
|
||||
local x, y = unit.x, unit.y
|
||||
local wpn = 1 --WayPoint Number - we have to remember which waypoint we are heading to
|
||||
|
||||
|
||||
local patrol = {}
|
||||
|
||||
patrol.exec = function()
|
||||
x, y = unit.x, unit.y
|
||||
if (x == wp[wpn].x and y == wp[wpn].y) then
|
||||
wpn = wpn % wpcount + 1 -- advance by one waypoint(this construct loops in range [1, wpcount])
|
||||
end
|
||||
ai.move_full(unit, wp[wpn].x, wp[wpn].y) -- @note: should we change this to ai.move()?
|
||||
end
|
||||
|
||||
patrol.eval = function()
|
||||
return 300000
|
||||
end
|
||||
if (x == wp[1].x and y == wp[1].y) then
|
||||
wpn = wpn + 1
|
||||
--w1, w2 = w2, w2 -- if we are standing on the first waypoint, swap them
|
||||
end
|
||||
|
||||
return patrol
|
||||
--local waypoints = {w1, w2} -- this form might be just received from the args
|
||||
local wpcount = # wp
|
||||
|
||||
|
||||
local patrol = {}
|
||||
|
||||
patrol.exec = function()
|
||||
x, y = unit.x, unit.y
|
||||
if (x == wp[wpn].x and y == wp[wpn].y) then
|
||||
wpn = wpn % wpcount + 1 -- advance by one waypoint(this construct loops in range [1, wpcount])
|
||||
end
|
||||
ai.move_full(unit, wp[wpn].x, wp[wpn].y) -- @note: should we change this to ai.move()?
|
||||
end
|
||||
|
||||
patrol.eval = function()
|
||||
return 300000
|
||||
end
|
||||
|
||||
return patrol
|
||||
end
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Hh , Hh , Hh , Wo , Wo , Wo , Hh , Wo , Hh , Wo , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Ww , Wo , Ww , Wo , Ww , Wo , Ww , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Ww , Wo , Ww , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gg , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Ch , Ch , Ch , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Ch , Ch , Ch , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ch , Ch , Ch , Hh , Hh , Hh , Hh , Ww , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Ch , 1 Kh , Ch , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Ch , 2 Kh , Ch , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ch , 3 Kh , Ch , Hh , Hh , Hh , Hh , Ww , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Ch , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Ch , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ch , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Gs , Gs , Gs , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Gs , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Ww , Ww , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Ww , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Wo , Ww , Wo , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Ww , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Ww , Ww , Ww , Ww , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Ww , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Ww , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Ww , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
||||
Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Hh , Wo , Wo , Wo , Wo , Wo , Wo , Wo , Wo
|
|
@ -6,27 +6,28 @@ local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
|
|||
local function get_big_animals(cfg)
|
||||
local big_animals = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and" , cfg.filter }
|
||||
{ "and" , H.get_child(cfg, "filter") }
|
||||
}
|
||||
return big_animals
|
||||
end
|
||||
|
||||
local ca_big_animals = {}
|
||||
|
||||
function ca_big_animals:evaluation(ai, cfg)
|
||||
function ca_big_animals:evaluation(cfg)
|
||||
if get_big_animals(cfg)[1] then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_big_animals:execution(ai, cfg)
|
||||
function ca_big_animals:execution(cfg)
|
||||
-- Big animals just move toward a goal that gets (re)set occasionally
|
||||
-- and attack whatever is in their range (except for some units that they avoid)
|
||||
|
||||
local big_animals = get_big_animals(cfg)
|
||||
local avoid_tag = H.get_child(cfg, "avoid_unit")
|
||||
local avoid_map = LS.create()
|
||||
if cfg.avoid_unit then
|
||||
if avoid_tag then
|
||||
avoid_map = LS.of_pairs(wesnoth.get_locations { radius = 1,
|
||||
{ "filter", { { "and", cfg.avoid_unit },
|
||||
{ "filter", { { "and", avoid_tag },
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
|
||||
} }
|
||||
})
|
||||
|
@ -38,7 +39,7 @@ function ca_big_animals:execution(ai, cfg)
|
|||
-- Unit gets a new goal if none is set or on any move with a 10% random chance
|
||||
local r = math.random(10)
|
||||
if (not goal.goal_x) or (r == 1) then
|
||||
local locs = AH.get_passable_locations(cfg.filter_location or {})
|
||||
local locs = AH.get_passable_locations(H.get_child(cfg, "filter_location") or {})
|
||||
local rand = math.random(#locs)
|
||||
|
||||
goal.goal_x, goal.goal_y = locs[rand][1], locs[rand][2]
|
||||
|
@ -46,7 +47,7 @@ function ca_big_animals:execution(ai, cfg)
|
|||
end
|
||||
|
||||
local reach_map = AH.get_reachable_unocc(unit)
|
||||
local wander_terrain = cfg.filter_location_wander or {}
|
||||
local wander_terrain = H.get_child(cfg, "filter_location_wander") or {}
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Remove tiles that do not comform to the wander terrain filter
|
||||
if (not wesnoth.match_location(x, y, wander_terrain)) then
|
||||
|
|
|
@ -3,7 +3,7 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
|
||||
local ca_bottleneck_attack = {}
|
||||
|
||||
function ca_bottleneck_attack:evaluation(ai, cfg, self)
|
||||
function ca_bottleneck_attack:evaluation(cfg, data)
|
||||
local attackers = AH.get_units_with_attacks {
|
||||
side = wesnoth.current.side,
|
||||
{ "filter_adjacent", {
|
||||
|
@ -55,29 +55,29 @@ function ca_bottleneck_attack:evaluation(ai, cfg, self)
|
|||
|
||||
if (not best_attacker) then
|
||||
-- In this case we take attacks away from all units
|
||||
self.data.BD_bottleneck_attacks_done = true
|
||||
data.BD_bottleneck_attacks_done = true
|
||||
else
|
||||
self.data.BD_bottleneck_attacks_done = false
|
||||
self.data.BD_attacker = best_attacker
|
||||
self.data.BD_target = best_target
|
||||
self.data.BD_weapon = best_weapon
|
||||
data.BD_bottleneck_attacks_done = false
|
||||
data.BD_attacker = best_attacker
|
||||
data.BD_target = best_target
|
||||
data.BD_weapon = best_weapon
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_bottleneck_attack:execution(ai, cfg, self)
|
||||
if self.data.BD_bottleneck_attacks_done then
|
||||
function ca_bottleneck_attack:execution(cfg, data)
|
||||
if data.BD_bottleneck_attacks_done then
|
||||
local units = AH.get_units_with_attacks { side = wesnoth.current.side }
|
||||
for _,unit in ipairs(units) do
|
||||
AH.checked_stopunit_attacks(ai, unit)
|
||||
end
|
||||
else
|
||||
AH.checked_attack(ai, self.data.BD_attacker, self.data.BD_target, self.data.BD_weapon)
|
||||
AH.checked_attack(ai, data.BD_attacker, data.BD_target, data.BD_weapon)
|
||||
end
|
||||
|
||||
self.data.BD_attacker, self.data.BD_target, self.data.BD_weapon = nil, nil, nil
|
||||
self.data.BD_bottleneck_attacks_done = nil
|
||||
data.BD_attacker, data.BD_target, data.BD_weapon = nil, nil, nil
|
||||
data.BD_bottleneck_attacks_done = nil
|
||||
end
|
||||
|
||||
return ca_bottleneck_attack
|
||||
|
|
|
@ -81,19 +81,19 @@ local function bottleneck_triple_from_keys(key_x, key_y, max_value)
|
|||
return AH.LS_of_triples(coords)
|
||||
end
|
||||
|
||||
local function bottleneck_create_positioning_map(max_value, self)
|
||||
local function bottleneck_create_positioning_map(max_value, data)
|
||||
-- Create the positioning maps for the healers and leaders, if not given by WML keys
|
||||
-- @max_value: the rating value for the first hex in the set
|
||||
-- self.data.BD_def_map must have been created when this function is called.
|
||||
-- data.BD_def_map must have been created when this function is called.
|
||||
|
||||
-- Find all locations adjacent to def_map.
|
||||
-- This might include hexes on the line itself.
|
||||
-- Only store those that are not in enemy territory.
|
||||
local map = LS.create()
|
||||
self.data.BD_def_map:iter(function(x, y, v)
|
||||
data.BD_def_map:iter(function(x, y, v)
|
||||
for xa,ya in H.adjacent_tiles(x, y) do
|
||||
if self.data.BD_is_my_territory:get(xa, ya) then
|
||||
local rating = self.data.BD_def_map:get(x, y) or 0
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
local rating = data.BD_def_map:get(x, y) or 0
|
||||
rating = rating + (map:get(xa, ya) or 0)
|
||||
map:insert(xa, ya, rating)
|
||||
end
|
||||
|
@ -108,14 +108,14 @@ local function bottleneck_create_positioning_map(max_value, self)
|
|||
|
||||
-- We merge the defense map into this, as healers/leaders (by default)
|
||||
-- can take position on the front line
|
||||
map:union_merge(self.data.BD_def_map,
|
||||
map:union_merge(data.BD_def_map,
|
||||
function(x, y, v1, v2) return v1 or v2 end
|
||||
)
|
||||
|
||||
return map
|
||||
end
|
||||
|
||||
local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, self)
|
||||
local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, data)
|
||||
-- Calculate rating of a unit @unit at coordinates (@x,@y).
|
||||
-- Don't want to extract @is_healer and @has_leadership inside this function, as it is very slow.
|
||||
-- Thus they are provided as parameters from the calling function.
|
||||
|
@ -125,18 +125,18 @@ local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, self
|
|||
-- Defense positioning rating
|
||||
-- We exclude healers/leaders here, as we don't necessarily want them on the front line
|
||||
if (not is_healer) and (not has_leadership) then
|
||||
rating = self.data.BD_def_map:get(x, y) or 0
|
||||
rating = data.BD_def_map:get(x, y) or 0
|
||||
end
|
||||
|
||||
-- Healer positioning rating
|
||||
if is_healer then
|
||||
local healer_rating = self.data.BD_healer_map:get(x, y) or 0
|
||||
local healer_rating = data.BD_healer_map:get(x, y) or 0
|
||||
if (healer_rating > rating) then rating = healer_rating end
|
||||
end
|
||||
|
||||
-- Leadership unit positioning rating
|
||||
if has_leadership then
|
||||
local leadership_rating = self.data.BD_leadership_map:get(x, y) or 0
|
||||
local leadership_rating = data.BD_leadership_map:get(x, y) or 0
|
||||
|
||||
-- If leadership unit is injured -> prefer hexes next to healers
|
||||
if (unit.hitpoints < unit.max_hitpoints) then
|
||||
|
@ -154,18 +154,18 @@ local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, self
|
|||
|
||||
-- Injured unit positioning
|
||||
if (unit.hitpoints < unit.max_hitpoints) then
|
||||
local healing_rating = self.data.BD_healing_map:get(x, y) or 0
|
||||
local healing_rating = data.BD_healing_map:get(x, y) or 0
|
||||
if (healing_rating > rating) then rating = healing_rating end
|
||||
end
|
||||
|
||||
-- If this did not produce a positive rating, we add a
|
||||
-- distance-based rating, to get units to the bottleneck in the first place
|
||||
if (rating <= 0) and self.data.BD_is_my_territory:get(x, y) then
|
||||
if (rating <= 0) and data.BD_is_my_territory:get(x, y) then
|
||||
local combined_dist = 0
|
||||
self.data.BD_def_map:iter(function(x_def, y_def, v)
|
||||
data.BD_def_map:iter(function(x_def, y_def, v)
|
||||
combined_dist = combined_dist + H.distance_between(x, y, x_def, y_def)
|
||||
end)
|
||||
combined_dist = combined_dist / self.data.BD_def_map:size()
|
||||
combined_dist = combined_dist / data.BD_def_map:size()
|
||||
rating = 1000 - combined_dist * 10.
|
||||
end
|
||||
|
||||
|
@ -177,7 +177,7 @@ local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, self
|
|||
return rating
|
||||
end
|
||||
|
||||
local function bottleneck_move_out_of_way(unit_in_way, self)
|
||||
local function bottleneck_move_out_of_way(unit_in_way, data)
|
||||
-- Find the best move out of the way for a unit @unit_in_way and choose the
|
||||
-- shortest possible move. Returns nil if no move was found.
|
||||
|
||||
|
@ -193,7 +193,7 @@ local function bottleneck_move_out_of_way(unit_in_way, self)
|
|||
|
||||
local best_reach, best_hex = -9e99
|
||||
for _,loc in ipairs(reach) do
|
||||
if self.data.BD_is_my_territory:get(loc[1], loc[2]) and (not occ_hexes:get(loc[1], loc[2])) then
|
||||
if data.BD_is_my_territory:get(loc[1], loc[2]) and (not occ_hexes:get(loc[1], loc[2])) then
|
||||
-- Criterion: MP left after the move has been done
|
||||
if (loc[3] > best_reach) then
|
||||
best_reach, best_hex = loc[3], { loc[1], loc[2] }
|
||||
|
@ -206,9 +206,9 @@ end
|
|||
|
||||
local ca_bottleneck_move = {}
|
||||
|
||||
function ca_bottleneck_move:evaluation(ai, cfg, self)
|
||||
function ca_bottleneck_move:evaluation(cfg, data)
|
||||
if cfg.active_side_leader and
|
||||
(not MAISD.get_mai_self_data(self.data, cfg.ai_id, "side_leader_activated"))
|
||||
(not MAISD.get_mai_self_data(data, cfg.ai_id, "side_leader_activated"))
|
||||
then
|
||||
local can_still_recruit = false -- Enough gold left for another recruit?
|
||||
for _,recruit_type in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
|
@ -218,12 +218,12 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
end
|
||||
end
|
||||
if (not can_still_recruit) then
|
||||
MAISD.set_mai_self_data(self.data, cfg.ai_id, { side_leader_activated = true })
|
||||
MAISD.set_mai_self_data(data, cfg.ai_id, { side_leader_activated = true })
|
||||
end
|
||||
end
|
||||
|
||||
local units = {}
|
||||
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "side_leader_activated") then
|
||||
if MAISD.get_mai_self_data(data, cfg.ai_id, "side_leader_activated") then
|
||||
units = AH.get_units_with_moves { side = wesnoth.current.side }
|
||||
else
|
||||
units = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
|
||||
|
@ -231,54 +231,54 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
if (not units[1]) then return 0 end
|
||||
|
||||
-- Set up the array that tells the AI where to defend the bottleneck
|
||||
self.data.BD_def_map = bottleneck_triple_from_keys(cfg.x, cfg.y, 10000)
|
||||
data.BD_def_map = bottleneck_triple_from_keys(cfg.x, cfg.y, 10000)
|
||||
|
||||
-- Territory map, describing which hex is on AI's side of the bottleneck
|
||||
-- This one is a bit expensive, esp. on large maps -> don't delete every move and reuse
|
||||
-- However, after a reload, self.data.BD_is_my_territory is an empty string
|
||||
-- However, after a reload, data.BD_is_my_territory is an empty string
|
||||
-- -> need to recalculate in that case also (the reason is that is_my_territory is not a WML table)
|
||||
if (not self.data.BD_is_my_territory) or (type(self.data.BD_is_my_territory) == 'string') then
|
||||
if (not data.BD_is_my_territory) or (type(data.BD_is_my_territory) == 'string') then
|
||||
local enemy_map = bottleneck_triple_from_keys(cfg.enemy_x, cfg.enemy_y, 10000)
|
||||
self.data.BD_is_my_territory = bottleneck_is_my_territory(self.data.BD_def_map, enemy_map)
|
||||
data.BD_is_my_territory = bottleneck_is_my_territory(data.BD_def_map, enemy_map)
|
||||
end
|
||||
|
||||
-- Healer positioning map
|
||||
if cfg.healer_x and cfg.healer_y then
|
||||
self.data.BD_healer_map = bottleneck_triple_from_keys(cfg.healer_x, cfg.healer_y, 5000)
|
||||
data.BD_healer_map = bottleneck_triple_from_keys(cfg.healer_x, cfg.healer_y, 5000)
|
||||
else
|
||||
self.data.BD_healer_map = bottleneck_create_positioning_map(5000, self)
|
||||
data.BD_healer_map = bottleneck_create_positioning_map(5000, data)
|
||||
end
|
||||
-- Use def_map values for any healer hexes that are defined in def_map as well
|
||||
self.data.BD_healer_map:inter_merge(self.data.BD_def_map,
|
||||
data.BD_healer_map:inter_merge(data.BD_def_map,
|
||||
function(x, y, v1, v2) return v2 or v1 end
|
||||
)
|
||||
|
||||
-- Leadership position map
|
||||
if cfg.leadership_x and cfg.leadership_y then
|
||||
self.data.BD_leadership_map = bottleneck_triple_from_keys(cfg.leadership_x, cfg.leadership_y, 4000)
|
||||
data.BD_leadership_map = bottleneck_triple_from_keys(cfg.leadership_x, cfg.leadership_y, 4000)
|
||||
else
|
||||
self.data.BD_leadership_map = bottleneck_create_positioning_map(4000, self)
|
||||
data.BD_leadership_map = bottleneck_create_positioning_map(4000, data)
|
||||
end
|
||||
-- Use def_map values for any leadership hexes that are defined in def_map as well
|
||||
self.data.BD_leadership_map:inter_merge(self.data.BD_def_map,
|
||||
data.BD_leadership_map:inter_merge(data.BD_def_map,
|
||||
function(x, y, v1, v2) return v2 or v1 end
|
||||
)
|
||||
|
||||
-- Healing map: positions next to healers
|
||||
-- Healers get moved with higher priority, so don't need to check their MP
|
||||
local healers = wesnoth.get_units { side = wesnoth.current.side, ability = "healing" }
|
||||
self.data.BD_healing_map = LS.create()
|
||||
data.BD_healing_map = LS.create()
|
||||
for _,healer in ipairs(healers) do
|
||||
for xa,ya in H.adjacent_tiles(healer.x, healer.y) do
|
||||
-- Cannot be on the line, and needs to be in own territory
|
||||
if self.data.BD_is_my_territory:get(xa, ya) then
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
local min_dist = 9e99
|
||||
self.data.BD_def_map:iter( function(xd, yd, vd)
|
||||
data.BD_def_map:iter( function(xd, yd, vd)
|
||||
local dist_line = H.distance_between(xa, ya, xd, yd)
|
||||
if (dist_line < min_dist) then min_dist = dist_line end
|
||||
end)
|
||||
if (min_dist > 0) then
|
||||
self.data.BD_healing_map:insert(xa, ya, 3000 + min_dist) -- Farther away from enemy is good
|
||||
data.BD_healing_map:insert(xa, ya, 3000 + min_dist) -- Farther away from enemy is good
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -299,13 +299,13 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
local is_healer = (unit.__cfg.usage == "healer")
|
||||
local has_leadership = AH.has_ability(unit, "leadership")
|
||||
|
||||
local rating = bottleneck_get_rating(unit, unit.x, unit.y, has_leadership, is_healer, self)
|
||||
local rating = bottleneck_get_rating(unit, unit.x, unit.y, has_leadership, is_healer, data)
|
||||
current_rating_map:insert(unit.x, unit.y, rating)
|
||||
|
||||
-- A unit that cannot move any more, (or at least cannot move out of the way)
|
||||
-- must be considered to have a very high rating (it's in the best position
|
||||
-- it can possibly achieve)
|
||||
local best_move_away = bottleneck_move_out_of_way(unit, self)
|
||||
local best_move_away = bottleneck_move_out_of_way(unit, data)
|
||||
if (not best_move_away) then current_rating_map:insert(unit.x, unit.y, 20000) end
|
||||
end
|
||||
|
||||
|
@ -315,7 +315,7 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
local attacks = {}
|
||||
for _,enemy in ipairs(enemies) do
|
||||
for xa,ya in H.adjacent_tiles(enemy.x, enemy.y) do
|
||||
if self.data.BD_is_my_territory:get(xa, ya) then
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
local unit_in_way = wesnoth.get_unit(xa, ya)
|
||||
local data = { x = xa, y = ya,
|
||||
defender = enemy,
|
||||
|
@ -346,7 +346,7 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
|
||||
local reach = wesnoth.find_reach(unit)
|
||||
for _,loc in ipairs(reach) do
|
||||
local rating = bottleneck_get_rating(unit, loc[1], loc[2], has_leadership, is_healer, self)
|
||||
local rating = bottleneck_get_rating(unit, loc[1], loc[2], has_leadership, is_healer, data)
|
||||
|
||||
-- A move is only considered if it improves the overall rating,
|
||||
-- that is, its rating must be higher than:
|
||||
|
@ -408,7 +408,7 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
-- Small penalty if there's a unit in the way
|
||||
-- We also need to check whether this unit can actually move out of the way
|
||||
if attack.unit_in_way then
|
||||
if bottleneck_move_out_of_way(attack.unit_in_way, self) then
|
||||
if bottleneck_move_out_of_way(attack.unit_in_way, data) then
|
||||
level_up_rating = level_up_rating - 0.001
|
||||
else
|
||||
level_up_rating = 0
|
||||
|
@ -417,8 +417,8 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
|
||||
if (level_up_rating > max_rating) then
|
||||
max_rating, best_unit, best_hex = level_up_rating, unit, { loc[1], loc[2] }
|
||||
self.data.BD_level_up_defender = attack.defender
|
||||
self.data.BD_level_up_weapon = n_weapon
|
||||
data.BD_level_up_defender = attack.defender
|
||||
data.BD_level_up_weapon = n_weapon
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -428,7 +428,7 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
|
||||
-- Set the variables for the exec() function
|
||||
if (not best_hex) then
|
||||
self.data.BD_bottleneck_moves_done = true
|
||||
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
|
||||
local unit_in_way = wesnoth.get_units { x = best_hex[1], y = best_hex[2],
|
||||
|
@ -436,23 +436,23 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
|
|||
}[1]
|
||||
|
||||
if unit_in_way then
|
||||
best_hex = bottleneck_move_out_of_way(unit_in_way, self)
|
||||
best_hex = bottleneck_move_out_of_way(unit_in_way, data)
|
||||
best_unit = unit_in_way
|
||||
self.data.BD_level_up_defender = nil
|
||||
self.data.BD_level_up_weapon = nil
|
||||
data.BD_level_up_defender = nil
|
||||
data.BD_level_up_weapon = nil
|
||||
end
|
||||
|
||||
self.data.BD_bottleneck_moves_done = false
|
||||
self.data.BD_unit, self.data.BD_hex = best_unit, best_hex
|
||||
data.BD_bottleneck_moves_done = false
|
||||
data.BD_unit, data.BD_hex = best_unit, best_hex
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_bottleneck_move:execution(ai, cfg, self)
|
||||
if self.data.BD_bottleneck_moves_done then
|
||||
function ca_bottleneck_move:execution(cfg, data)
|
||||
if data.BD_bottleneck_moves_done then
|
||||
local units = {}
|
||||
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "side_leader_activated") then
|
||||
if MAISD.get_mai_self_data(data, cfg.ai_id, "side_leader_activated") then
|
||||
units = AH.get_units_with_moves { side = wesnoth.current.side }
|
||||
else
|
||||
units = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
|
||||
|
@ -462,23 +462,23 @@ function ca_bottleneck_move:execution(ai, cfg, self)
|
|||
AH.checked_stopunit_moves(ai, unit)
|
||||
end
|
||||
else
|
||||
if (self.data.BD_unit.x ~= self.data.BD_hex[1]) or (self.data.BD_unit.y ~= self.data.BD_hex[2]) then
|
||||
if (data.BD_unit.x ~= data.BD_hex[1]) or (data.BD_unit.y ~= data.BD_hex[2]) then
|
||||
-- Don't want full move, as this might be stepping out of the way
|
||||
AH.checked_move(ai, self.data.BD_unit, self.data.BD_hex[1], self.data.BD_hex[2])
|
||||
AH.checked_move(ai, data.BD_unit, data.BD_hex[1], data.BD_hex[2])
|
||||
end
|
||||
if (not self.data.BD_unit) or (not self.data.BD_unit.valid) then return end
|
||||
if (not data.BD_unit) or (not data.BD_unit.valid) then return end
|
||||
|
||||
if self.data.BD_level_up_defender then
|
||||
AH.checked_attack(ai, self.data.BD_unit, self.data.BD_level_up_defender, self.data.BD_level_up_weapon)
|
||||
if data.BD_level_up_defender then
|
||||
AH.checked_attack(ai, data.BD_unit, data.BD_level_up_defender, data.BD_level_up_weapon)
|
||||
end
|
||||
end
|
||||
|
||||
-- Now delete almost everything
|
||||
-- Keep only self.data.BD_is_my_territory because it is very expensive
|
||||
self.data.BD_unit, self.data.BD_hex = nil, nil
|
||||
self.data.BD_level_up_defender, self.data.BD_level_up_weapon = nil, nil
|
||||
self.data.BD_bottleneck_moves_done = nil
|
||||
self.data.BD_def_map, self.data.BD_healer_map, self.data.BD_leadership_map, self.data.BD_healing_map = nil, nil, nil, nil
|
||||
-- Keep only data.BD_is_my_territory because it is very expensive
|
||||
data.BD_unit, data.BD_hex = nil, nil
|
||||
data.BD_level_up_defender, data.BD_level_up_weapon = nil, nil
|
||||
data.BD_bottleneck_moves_done = nil
|
||||
data.BD_def_map, data.BD_healer_map, data.BD_leadership_map, data.BD_healing_map = nil, nil, nil, nil
|
||||
end
|
||||
|
||||
return ca_bottleneck_move
|
||||
|
|
|
@ -2,7 +2,7 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local function get_coward(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local coward = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -13,17 +13,17 @@ end
|
|||
|
||||
local ca_coward = {}
|
||||
|
||||
function ca_coward:evaluation(ai, cfg)
|
||||
function ca_coward:evaluation(cfg)
|
||||
if get_coward(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_coward:execution(ai, cfg)
|
||||
function ca_coward:execution(cfg)
|
||||
local coward = get_coward(cfg)
|
||||
local reach = wesnoth.find_reach(coward)
|
||||
|
||||
local filter_second =
|
||||
cfg.filter_second
|
||||
H.get_child(cfg, "filter_second")
|
||||
or { { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } } }
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "and", filter_second },
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local T = H.set_wml_tag_metatable{}
|
||||
|
||||
-- Functions to perform fast evaluation of attacks and attack combinations.
|
||||
-- The emphasis with all of this is on speed, not elegance.
|
||||
|
@ -19,31 +20,156 @@ function ca_fast_attack_utils.get_avoid_map(cfg)
|
|||
-- Get map of locations to be avoided.
|
||||
-- Use [micro_ai][avoid] tag with priority over [ai][avoid].
|
||||
-- If neither is given, return an empty location set.
|
||||
-- Note that ai.get_avoid() cannot be used as it always returns an array,
|
||||
-- even when the aspect is not set, and an empty array could also mean that
|
||||
-- no hexes match the filter
|
||||
|
||||
local avoid_tag
|
||||
local avoid_tag = H.get_child(cfg, "avoid")
|
||||
|
||||
if cfg.avoid then
|
||||
avoid_tag = cfg.avoid
|
||||
if not avoid_tag then
|
||||
return LS.of_pairs(ai.aspects.avoid)
|
||||
end
|
||||
|
||||
return LS.of_pairs(wesnoth.get_locations(avoid_tag))
|
||||
end
|
||||
|
||||
local function attack_filter(which, filter, is_leader)
|
||||
if (which == 'leader') then
|
||||
which = 'own'
|
||||
is_leader = true
|
||||
end
|
||||
if (which == 'own') then
|
||||
return {
|
||||
side = wesnoth.current.side,
|
||||
canrecruit = is_leader,
|
||||
{ "and", filter or {} }
|
||||
}
|
||||
elseif (which == 'enemy') then
|
||||
return {
|
||||
T.filter_side { T.enemy_of { side = wesnoth.current.side } },
|
||||
{ "not", filter or {} }
|
||||
}
|
||||
else
|
||||
return filter
|
||||
end
|
||||
end
|
||||
|
||||
ca_fast_attack_utils.build_attack_filter = attack_filter
|
||||
|
||||
local function get_attack_filter_from_aspect(aspect, which, data, is_leader)
|
||||
if (aspect.name == "composite_aspect") then
|
||||
--print("Found composite aspect")
|
||||
for facet in H.child_range(aspect, 'facet') do
|
||||
local active = true
|
||||
if facet.turns then
|
||||
active = false
|
||||
local turns = AH.split(facet.turns)
|
||||
local current_turn = tostring(wesnoth.current.turn)
|
||||
--print("Found facet with turns requirement (current turn is '" .. current_turn .. "')")
|
||||
for i,v in ipairs(turns) do
|
||||
if current_turn == v then
|
||||
--print(" Matched with '" .. v .. "'")
|
||||
active = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if facet.time_of_day then
|
||||
active = false
|
||||
local times = AH.split(facet.time_of_day)
|
||||
local current_time = wesnoth.get_time_of_day().id
|
||||
--print("Found facet with time requirement (current time is '" .. current_time .. "')")
|
||||
for i,v in ipairs(times) do
|
||||
if current_time == v then
|
||||
--print(" Matched with '" .. v .. "'")
|
||||
active = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if active then
|
||||
return get_attack_filter_from_aspect(facet, which, data, is_leader)
|
||||
end
|
||||
end
|
||||
elseif (aspect.name == "lua_aspect") then
|
||||
--print("Found lua aspect")
|
||||
local filter = loadstring(aspect.code)(nil, H.get_child(aspect, 'args'), data)
|
||||
if (type(filter[which]) == 'function') then
|
||||
temporary_attacks_filter_fcn = filter[which]
|
||||
local units = wesnoth.get_units(attack_filter(which, {
|
||||
lua_function = 'temporary_attacks_filter_fcn'
|
||||
}, is_leader))
|
||||
temporary_attacks_filter_fcn = nil
|
||||
return units
|
||||
else
|
||||
return wesnoth.get_units(attack_filter(which, filter[which], is_leader))
|
||||
end
|
||||
else -- Standard attacks aspect (though not name=standard_aspect)
|
||||
--print("Found standard aspect")
|
||||
return wesnoth.get_units(attack_filter(which,
|
||||
H.get_child(aspect, 'filter_' .. which), is_leader))
|
||||
end
|
||||
return wesnoth.get_units(attack_filter(which, {}, is_leader))
|
||||
end
|
||||
|
||||
function ca_fast_attack_utils.get_attackers(data, which)
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'attacks') then
|
||||
if (which == 'leader') then
|
||||
return get_attack_filter_from_aspect(aspect, 'own', data, true)
|
||||
else
|
||||
return get_attack_filter_from_aspect(aspect, which, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
--[[
|
||||
This is a benchmarking function to compare the old, incorrect method of
|
||||
fetching the attacks aspect to the new method and the standard method.
|
||||
It's meant to be called from the Lua console.
|
||||
|
||||
Example usage:
|
||||
$ my_ai = wesnoth.debug_ai(1).ai
|
||||
$ FAU = wesnoth.dofile "ai/micro_ais/cas/ca_fast_attack_utils.lua"
|
||||
$ FAU.test_attacks(my_ai, 2000)
|
||||
]]
|
||||
function ca_fast_attack_utils.test_attacks(my_ai, times)
|
||||
local t1, t2 = os.clock()
|
||||
for i = 1,times do
|
||||
my_ai.get_attacks()
|
||||
end
|
||||
t2 = os.clock()
|
||||
print("get_attacks() executed in average time " .. (os.difftime(t2,t1) / times))
|
||||
t1 = os.clock()
|
||||
for i = 1,times do
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'avoid') then
|
||||
if (aspect.id == 'attacks') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
avoid_tag = H.get_child(facet, 'value')
|
||||
wesnoth.get_units{
|
||||
side = wesnoth.current.side,
|
||||
canrecruit = false,
|
||||
{ "and", H.get_child(facet, 'filter_own') }
|
||||
}
|
||||
wesnoth.get_units{
|
||||
side = wesnoth.current.side,
|
||||
canrecruit = false,
|
||||
{ "and", H.get_child(facet, 'filter_enemy') }
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if avoid_tag then
|
||||
return LS.of_pairs(wesnoth.get_locations(avoid_tag))
|
||||
else
|
||||
return LS.create()
|
||||
t2 = os.clock()
|
||||
print("original sloppy method executed in time " .. (os.difftime(t2,t1) / times))
|
||||
t1 = os.clock()
|
||||
for i = 1,times do
|
||||
ca_fast_attack_utils.get_attackers(nil, "own", false)
|
||||
ca_fast_attack_utils.get_attackers(nil, "enemy", false)
|
||||
end
|
||||
t2 = os.clock()
|
||||
print("new method executed in time " .. (os.difftime(t2,t1) / times))
|
||||
end
|
||||
|
||||
function ca_fast_attack_utils.gamedata_setup()
|
||||
|
|
|
@ -5,51 +5,49 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
|
||||
local ca_fast_combat = {}
|
||||
|
||||
function ca_fast_combat:evaluation(ai, cfg, self)
|
||||
self.data.move_cache = { turn = wesnoth.current.turn }
|
||||
self.data.gamedata = FAU.gamedata_setup()
|
||||
function ca_fast_combat:evaluation(cfg, data)
|
||||
data.move_cache = { turn = wesnoth.current.turn }
|
||||
data.gamedata = FAU.gamedata_setup()
|
||||
|
||||
local filter_own = cfg.filter
|
||||
local filter_enemy = cfg.filter_second
|
||||
local filter_own = H.get_child(cfg, "filter")
|
||||
local filter_enemy = H.get_child(cfg, "filter_second")
|
||||
local excluded_enemies
|
||||
local units_sorted = true
|
||||
if (not filter_own) and (not filter_enemy) then
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'attacks') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
filter_own = H.get_child(facet, 'filter_own')
|
||||
filter_enemy = H.get_child(facet, 'filter_enemy')
|
||||
end
|
||||
end
|
||||
if (not data.fast_combat_units) or (not data.fast_combat_units[1]) then
|
||||
data.fast_combat_units = FAU.get_attackers(data, "own")
|
||||
if (not data.fast_combat_units[1]) then return 0 end
|
||||
units_sorted = false
|
||||
end
|
||||
excluded_enemies = FAU.get_attackers(data, "enemy")
|
||||
else
|
||||
if (not data.fast_combat_units) or (not data.fast_combat_units[1]) then
|
||||
data.fast_combat_units = wesnoth.get_units(
|
||||
FAU.build_attack_filter("own", filter_own)
|
||||
)
|
||||
if (not data.fast_combat_units[1]) then return 0 end
|
||||
units_sorted = false
|
||||
end
|
||||
if filter_enemy then
|
||||
excluded_enemies = wesnoth.get_units(
|
||||
FAU.build_attack_filter("enemy", filter_enemy)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if (not self.data.fast_combat_units) or (not self.data.fast_combat_units[1]) then
|
||||
self.data.fast_combat_units = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
canrecruit = 'no',
|
||||
{ "and", filter_own }
|
||||
}
|
||||
|
||||
if (not self.data.fast_combat_units[1]) then return 0 end
|
||||
|
||||
if not units_sorted then
|
||||
-- For speed reasons, we'll go through the arrays from the end, so they are sorted backwards
|
||||
if cfg.weak_units_first then
|
||||
table.sort(self.data.fast_combat_units, function(a,b) return a.hitpoints > b.hitpoints end)
|
||||
table.sort(data.fast_combat_units, function(a,b) return a.hitpoints > b.hitpoints end)
|
||||
else
|
||||
table.sort(self.data.fast_combat_units, function(a,b) return a.hitpoints < b.hitpoints end)
|
||||
table.sort(data.fast_combat_units, function(a,b) return a.hitpoints < b.hitpoints end)
|
||||
end
|
||||
end
|
||||
|
||||
local excluded_enemies_map = LS.create()
|
||||
|
||||
-- Exclude enemies not matching [filter_enemy]
|
||||
if filter_enemy then
|
||||
local excluded_enemies = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "not", filter_enemy }
|
||||
}
|
||||
|
||||
if excluded_enemies then
|
||||
for _,e in ipairs(excluded_enemies) do
|
||||
excluded_enemies_map:insert(e.x, e.y)
|
||||
end
|
||||
|
@ -74,10 +72,10 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
-- Get the locations to be avoided
|
||||
local avoid_map = FAU.get_avoid_map(cfg)
|
||||
|
||||
for i = #self.data.fast_combat_units,1,-1 do
|
||||
local unit = self.data.fast_combat_units[i]
|
||||
local unit_info = FAU.get_unit_info(unit, self.data.gamedata)
|
||||
local unit_copy = FAU.get_unit_copy(unit.id, self.data.gamedata)
|
||||
for i = #data.fast_combat_units,1,-1 do
|
||||
local unit = data.fast_combat_units[i]
|
||||
local unit_info = FAU.get_unit_info(unit, data.gamedata)
|
||||
local unit_copy = FAU.get_unit_copy(unit.id, data.gamedata)
|
||||
|
||||
if (unit.attacks_left > 0) and (H.get_child(unit.__cfg, 'attack')) then
|
||||
local attacks = AH.get_attacks({ unit }, { include_occupied = cfg.include_occupied_attack_hexes })
|
||||
|
@ -89,16 +87,16 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
and (not avoid_map:get(attack.dst.x, attack.dst.y))
|
||||
then
|
||||
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
|
||||
local target_info = FAU.get_unit_info(target, self.data.gamedata)
|
||||
local target_info = FAU.get_unit_info(target, data.gamedata)
|
||||
|
||||
local att_stat, def_stat = FAU.battle_outcome(
|
||||
unit_copy, target, { attack.dst.x, attack.dst.y },
|
||||
unit_info, target_info, self.data.gamedata, self.data.move_cache
|
||||
unit_info, target_info, data.gamedata, data.move_cache
|
||||
)
|
||||
|
||||
local rating, attacker_rating, defender_rating, extra_rating = FAU.attack_rating(
|
||||
{ unit_info }, target_info, { { attack.dst.x, attack.dst.y } },
|
||||
{ att_stat }, def_stat, self.data.gamedata,
|
||||
{ att_stat }, def_stat, data.gamedata,
|
||||
{
|
||||
own_value_weight = own_value_weight,
|
||||
leader_weight = cfg.leader_weight
|
||||
|
@ -114,30 +112,30 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
end
|
||||
|
||||
if best_target then
|
||||
self.data.fast_combat_unit_i = i
|
||||
self.data.fast_target, self.data.fast_dst = best_target, best_dst
|
||||
data.fast_combat_unit_i = i
|
||||
data.fast_target, data.fast_dst = best_target, best_dst
|
||||
return cfg.ca_score
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.data.fast_combat_units[i] = nil
|
||||
data.fast_combat_units[i] = nil
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_fast_combat:execution(ai, cfg, self)
|
||||
local unit = self.data.fast_combat_units[self.data.fast_combat_unit_i]
|
||||
AH.movefull_outofway_stopunit(ai, unit, self.data.fast_dst.x, self.data.fast_dst.y)
|
||||
function ca_fast_combat:execution(cfg, data)
|
||||
local unit = data.fast_combat_units[data.fast_combat_unit_i]
|
||||
AH.movefull_outofway_stopunit(ai, unit, data.fast_dst.x, data.fast_dst.y)
|
||||
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
if (not self.data.fast_target) or (not self.data.fast_target.valid) then return end
|
||||
if (H.distance_between(unit.x, unit.y, self.data.fast_target.x, self.data.fast_target.y) ~= 1) then return end
|
||||
if (not data.fast_target) or (not data.fast_target.valid) then return end
|
||||
if (H.distance_between(unit.x, unit.y, data.fast_target.x, data.fast_target.y) ~= 1) then return end
|
||||
|
||||
AH.checked_attack(ai, unit, self.data.fast_target)
|
||||
AH.checked_attack(ai, unit, data.fast_target)
|
||||
|
||||
self.data.fast_combat_units[self.data.fast_combat_unit_i] = nil
|
||||
data.fast_combat_units[data.fast_combat_unit_i] = nil
|
||||
end
|
||||
|
||||
return ca_fast_combat
|
||||
|
|
|
@ -5,7 +5,7 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
|
||||
local ca_fast_combat_leader = {}
|
||||
|
||||
function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
||||
function ca_fast_combat_leader:evaluation(cfg, data)
|
||||
-- Some parts of this are very similar to ca_fast_combat.lua.
|
||||
-- However, for speed reasons we really want this to be a separate CA, so
|
||||
-- that the (more expensive) calculations for keeping the leader safe only
|
||||
|
@ -15,42 +15,34 @@ function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
|||
leader_attack_max_units = (cfg and cfg.leader_attack_max_units) or 3
|
||||
leader_additional_threat = (cfg and cfg.leader_additional_threat) or 1
|
||||
|
||||
self.data.move_cache = { turn = wesnoth.current.turn }
|
||||
self.data.gamedata = FAU.gamedata_setup()
|
||||
data.move_cache = { turn = wesnoth.current.turn }
|
||||
data.gamedata = FAU.gamedata_setup()
|
||||
|
||||
local filter_own = cfg.filter
|
||||
local filter_enemy = cfg.filter_second
|
||||
local filter_own = H.get_child(cfg, "filter")
|
||||
local filter_enemy = H.get_child(cfg, "filter_second")
|
||||
local excluded_enemies, leader
|
||||
if (not filter_own) and (not filter_enemy) then
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'attacks') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
filter_own = H.get_child(facet, 'filter_own')
|
||||
filter_enemy = H.get_child(facet, 'filter_enemy')
|
||||
end
|
||||
end
|
||||
leader = FAU.get_attackers(data, "leader")[1]
|
||||
if (not leader) then return 0 end
|
||||
excluded_enemies = FAU.get_attackers(data, "enemy")
|
||||
else
|
||||
leader = wesnoth.get_units(
|
||||
FAU.build_attack_filter("leader", filter_own)
|
||||
)[1]
|
||||
if (not leader) then return 0 end
|
||||
if filter_enemy then
|
||||
excluded_enemies = wesnoth.get_units(
|
||||
FAU.build_attack_filter("enemy", filter_enemy)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local leader = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
canrecruit = 'yes',
|
||||
{ "and", filter_own }
|
||||
}[1]
|
||||
|
||||
if (not leader) then return 0 end
|
||||
if (leader.attacks_left == 0) or (not H.get_child(leader.__cfg, 'attack')) then return 0 end
|
||||
|
||||
local excluded_enemies_map = LS.create()
|
||||
|
||||
-- Exclude enemies not matching [filter_enemy]
|
||||
if filter_enemy then
|
||||
local excluded_enemies = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "not", filter_enemy }
|
||||
}
|
||||
|
||||
if excluded_enemies then
|
||||
for _,e in ipairs(excluded_enemies) do
|
||||
excluded_enemies_map:insert(e.x, e.y)
|
||||
end
|
||||
|
@ -108,8 +100,8 @@ function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
|||
end
|
||||
end
|
||||
|
||||
local leader_info = FAU.get_unit_info(leader, self.data.gamedata)
|
||||
local leader_copy = FAU.get_unit_copy(leader.id, self.data.gamedata)
|
||||
local leader_info = FAU.get_unit_info(leader, data.gamedata)
|
||||
local leader_copy = FAU.get_unit_copy(leader.id, data.gamedata)
|
||||
|
||||
-- If threatened_leader_fights=yes, check out the current threat (power only,
|
||||
-- not units) on the AI leader
|
||||
|
@ -156,16 +148,16 @@ function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
|||
|
||||
if acceptable_attack then
|
||||
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
|
||||
local target_info = FAU.get_unit_info(target, self.data.gamedata)
|
||||
local target_info = FAU.get_unit_info(target, data.gamedata)
|
||||
|
||||
local att_stat, def_stat = FAU.battle_outcome(
|
||||
leader_copy, target, { attack.dst.x, attack.dst.y },
|
||||
leader_info, target_info, self.data.gamedata, self.data.move_cache
|
||||
leader_info, target_info, data.gamedata, data.move_cache
|
||||
)
|
||||
|
||||
local rating, attacker_rating, defender_rating, extra_rating = FAU.attack_rating(
|
||||
{ leader_info }, target_info, { { attack.dst.x, attack.dst.y } },
|
||||
{ att_stat }, def_stat, self.data.gamedata,
|
||||
{ att_stat }, def_stat, data.gamedata,
|
||||
{
|
||||
own_value_weight = own_value_weight,
|
||||
leader_weight = cfg.leader_weight
|
||||
|
@ -182,8 +174,8 @@ function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
|||
end
|
||||
|
||||
if best_target then
|
||||
self.data.leader = leader
|
||||
self.data.fast_target, self.data.fast_dst = best_target, best_dst
|
||||
data.leader = leader
|
||||
data.fast_target, data.fast_dst = best_target, best_dst
|
||||
return cfg.ca_score
|
||||
end
|
||||
end
|
||||
|
@ -191,17 +183,17 @@ function ca_fast_combat_leader:evaluation(ai, cfg, self)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_fast_combat_leader:execution(ai, cfg, self)
|
||||
local leader = self.data.leader
|
||||
AH.movefull_outofway_stopunit(ai, leader, self.data.fast_dst.x, self.data.fast_dst.y)
|
||||
function ca_fast_combat_leader:execution(cfg, data)
|
||||
local leader = data.leader
|
||||
AH.movefull_outofway_stopunit(ai, leader, data.fast_dst.x, data.fast_dst.y)
|
||||
|
||||
if (not leader) or (not leader.valid) then return end
|
||||
if (not self.data.fast_target) or (not self.data.fast_target.valid) then return end
|
||||
if (H.distance_between(leader.x, leader.y, self.data.fast_target.x, self.data.fast_target.y) ~= 1) then return end
|
||||
if (not data.fast_target) or (not data.fast_target.valid) then return end
|
||||
if (H.distance_between(leader.x, leader.y, data.fast_target.x, data.fast_target.y) ~= 1) then return end
|
||||
|
||||
AH.checked_attack(ai, leader, self.data.fast_target)
|
||||
AH.checked_attack(ai, leader, data.fast_target)
|
||||
|
||||
self.data.leader = nil
|
||||
data.leader = nil
|
||||
end
|
||||
|
||||
return ca_fast_combat_leader
|
||||
|
|
|
@ -4,14 +4,14 @@ local FAU = wesnoth.require "ai/micro_ais/cas/ca_fast_attack_utils.lua"
|
|||
|
||||
local ca_fast_move = {}
|
||||
|
||||
function ca_fast_move:evaluation(ai, cfg, self)
|
||||
function ca_fast_move:evaluation(cfg)
|
||||
local unit = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }[1]
|
||||
|
||||
if unit then return 20000 end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_fast_move:execution(ai, cfg, self)
|
||||
function ca_fast_move:execution(cfg)
|
||||
local move_cost_factor = cfg.move_cost_factor or 2.0
|
||||
if (move_cost_factor < 1.1) then move_cost_factor = 1.1 end
|
||||
|
||||
|
|
|
@ -32,12 +32,12 @@ end
|
|||
|
||||
local ca_forest_animals_move = {}
|
||||
|
||||
function ca_forest_animals_move:evaluation(ai, cfg)
|
||||
function ca_forest_animals_move:evaluation(cfg)
|
||||
if get_forest_animals(cfg)[1] then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_forest_animals_move:execution(ai, cfg)
|
||||
function ca_forest_animals_move:execution(cfg)
|
||||
-- These animals run from any enemy
|
||||
local forest_animals = get_forest_animals(cfg)
|
||||
local enemies = wesnoth.get_units { { "filter_side", { { "enemy_of", {side = wesnoth.current.side } } } } }
|
||||
|
@ -73,7 +73,7 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
end
|
||||
|
||||
-- If no close enemies, do a random move
|
||||
local wander_terrain = cfg.filter_location or {}
|
||||
local wander_terrain = H.get_child(cfg, "filter_location") or {}
|
||||
if (not close_enemies[1]) then
|
||||
local reach = AH.get_reachable_unocc(unit)
|
||||
local width, height = wesnoth.get_map_size()
|
||||
|
@ -156,7 +156,7 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
if unit and unit.valid
|
||||
and (unit.type == rabbit_type) and hole_map:get(farthest_hex[1], farthest_hex[2])
|
||||
then
|
||||
local command = "wesnoth.put_unit(x1, y1)"
|
||||
local command = "wesnoth.erase_unit(x1, y1)"
|
||||
ai.synced_command(command, farthest_hex[1], farthest_hex[2])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
|
||||
local ca_forest_animals_new_rabbit = {}
|
||||
|
||||
function ca_forest_animals_new_rabbit:evaluation(ai, cfg)
|
||||
function ca_forest_animals_new_rabbit:evaluation(cfg)
|
||||
-- Put new rabbits on map if there are fewer than cfg.rabbit_number
|
||||
-- To end this, we'll let the CA black-list itself
|
||||
|
||||
|
@ -12,7 +12,7 @@ function ca_forest_animals_new_rabbit:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
||||
function ca_forest_animals_new_rabbit:execution(cfg)
|
||||
local number = cfg.rabbit_number or 6
|
||||
local rabbit_enemy_distance = cfg.rabbit_enemy_distance or 3
|
||||
|
||||
|
@ -59,11 +59,11 @@ function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
|||
x, y = wesnoth.find_vacant_tile(holes[i].x, holes[i].y)
|
||||
end
|
||||
|
||||
local command = "wesnoth.put_unit(x1, y1, { side = "
|
||||
local command = "wesnoth.put_unit({ side = "
|
||||
.. wesnoth.current.side
|
||||
.. ", type = '"
|
||||
.. cfg.rabbit_type
|
||||
.. "' })"
|
||||
.. "' }, x1, y1)"
|
||||
ai.synced_command(command, x, y)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ end
|
|||
|
||||
local ca_forest_animals_tusker_attack = {}
|
||||
|
||||
function ca_forest_animals_tusker_attack:evaluation(ai, cfg)
|
||||
function ca_forest_animals_tusker_attack:evaluation(cfg)
|
||||
-- Check whether there is an enemy next to a tusklet and attack it ("protective parents" AI)
|
||||
|
||||
if (not cfg.tusker_type) or (not cfg.tusklet_type) then return 0 end
|
||||
|
@ -28,7 +28,7 @@ function ca_forest_animals_tusker_attack:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_forest_animals_tusker_attack:execution(ai, cfg)
|
||||
function ca_forest_animals_tusker_attack:execution(cfg)
|
||||
local tuskers = get_tuskers(cfg)
|
||||
local adjacent_enemies = get_adjacent_enemies(cfg)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ end
|
|||
|
||||
local ca_forest_animals_tusklet_move = {}
|
||||
|
||||
function ca_forest_animals_tusklet_move:evaluation(ai, cfg)
|
||||
function ca_forest_animals_tusklet_move:evaluation(cfg)
|
||||
-- Tusklets will simply move toward the closest tusker, without regard for anything else
|
||||
-- Except if no tuskers are left, in which case ca_forest_animals_move takes over and does a random move
|
||||
|
||||
|
@ -29,7 +29,7 @@ function ca_forest_animals_tusklet_move:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_forest_animals_tusklet_move:execution(ai, cfg)
|
||||
function ca_forest_animals_tusklet_move:execution(cfg)
|
||||
local tusklets = get_tusklets(cfg)
|
||||
local tuskers = get_tuskers(cfg)
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@ local function custom_cost(x, y, unit, enemy_map, enemy_attack_map, multiplier)
|
|||
return move_cost
|
||||
end
|
||||
|
||||
local ca_goto = {}
|
||||
local ca_goto, GO_units, GO_locs = {}
|
||||
|
||||
function ca_goto:evaluation(ai, cfg, self)
|
||||
function ca_goto:evaluation(cfg, data)
|
||||
-- If cfg.release_all_units_at_goal is set, check whether the goal has
|
||||
-- already been reached, in which case we do not do anything
|
||||
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "release_all") then
|
||||
if MAISD.get_mai_self_data(data, cfg.ai_id, "release_all") then
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,7 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
local all_locs = wesnoth.get_locations {
|
||||
x = '1-' .. width,
|
||||
y = '1-' .. height,
|
||||
{ "and", cfg.filter_location }
|
||||
{ "and", H.get_child(cfg, "filter_location") }
|
||||
}
|
||||
if (#all_locs == 0) then return 0 end
|
||||
|
||||
|
@ -40,17 +40,17 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
if cfg.unique_goals then
|
||||
-- First, some cleanup of previous turn data
|
||||
local str = 'goal_taken_' .. (wesnoth.current.turn - 1)
|
||||
local old_goals = MAISD.get_mai_self_data(self.data, cfg.ai_id)
|
||||
local old_goals = MAISD.get_mai_self_data(data, cfg.ai_id)
|
||||
for goal,_ in pairs(old_goals) do
|
||||
if string.find(goal, str) then
|
||||
old_goals[goal] = nil -- This also removes it from self.data
|
||||
old_goals[goal] = nil -- This also removes it from data
|
||||
end
|
||||
end
|
||||
|
||||
-- Now on to the current turn
|
||||
for _,loc in ipairs(all_locs) do
|
||||
local str = 'goal_taken_' .. wesnoth.current.turn .. '_' .. loc[1] .. '_' .. loc[2]
|
||||
if (not MAISD.get_mai_self_data(self.data, cfg.ai_id, str)) then
|
||||
if (not MAISD.get_mai_self_data(data, cfg.ai_id, str)) then
|
||||
table.insert(locs, loc)
|
||||
end
|
||||
end
|
||||
|
@ -61,7 +61,7 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
|
||||
local all_units = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
|
||||
local units = {}
|
||||
|
@ -76,14 +76,14 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
end
|
||||
if (not units[1]) then return 0 end
|
||||
|
||||
-- Now store units and locs in self.data, so that we don't need to duplicate this in the exec function
|
||||
self.data.GO_units, self.data.GO_locs = units, locs
|
||||
-- Now store units and locs, so that we don't need to duplicate this in the exec function
|
||||
GO_units, GO_locs = units, locs
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_goto:execution(ai, cfg, self)
|
||||
local units, locs = self.data.GO_units, self.data.GO_locs
|
||||
function ca_goto:execution(cfg, data)
|
||||
local units, locs = GO_units, GO_locs
|
||||
|
||||
local enemy_map, enemy_attack_map
|
||||
if cfg.avoid_enemies then
|
||||
|
@ -150,7 +150,7 @@ function ca_goto:execution(ai, cfg, self)
|
|||
end
|
||||
end
|
||||
|
||||
-- Make all hexes within the unit's current MP equaivalent
|
||||
-- Make all hexes within the unit's current MP equivalent
|
||||
if (cost <= unit.moves) then cost = 0 end
|
||||
|
||||
local rating = - cost
|
||||
|
@ -174,7 +174,7 @@ function ca_goto:execution(ai, cfg, self)
|
|||
local str = 'goal_taken_' .. wesnoth.current.turn .. '_' .. closest_hex[1] .. '_' .. closest_hex[2]
|
||||
local tmp_table = {}
|
||||
tmp_table[str] = true
|
||||
MAISD.insert_mai_self_data(self.data, cfg.ai_id, tmp_table)
|
||||
MAISD.insert_mai_self_data(data, cfg.ai_id, tmp_table)
|
||||
end
|
||||
|
||||
-- If any of the non-standard path finding options were used,
|
||||
|
@ -227,12 +227,12 @@ function ca_goto:execution(ai, cfg, self)
|
|||
end
|
||||
|
||||
if cfg.release_all_units_at_goal then
|
||||
MAISD.insert_mai_self_data(self.data, cfg.ai_id, { release_all = true })
|
||||
MAISD.insert_mai_self_data(data, cfg.ai_id, { release_all = true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.data.GO_units, self.data.GO_locs = nil, nil
|
||||
GO_units, GO_locs = nil, nil
|
||||
end
|
||||
|
||||
return ca_goto
|
||||
|
|
|
@ -7,27 +7,28 @@ local MAISD = wesnoth.require "ai/micro_ais/micro_ai_self_data.lua"
|
|||
local function get_hang_out_units(cfg)
|
||||
local units = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
return units
|
||||
end
|
||||
|
||||
local ca_hang_out = {}
|
||||
|
||||
function ca_hang_out:evaluation(ai, cfg, self)
|
||||
function ca_hang_out:evaluation(cfg, data)
|
||||
-- Return 0 if the mobilize condition has previously been met
|
||||
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "mobilize_units") then
|
||||
if MAISD.get_mai_self_data(data, cfg.ai_id, "mobilize_units") then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Otherwise check if any of the mobilize conditions are now met
|
||||
if (cfg.mobilize_condition and wesnoth.eval_conditional(cfg.mobilize_condition))
|
||||
local mobilize_condition = H.get_child(cfg, "mobilize_condition")
|
||||
if (mobilize_condition and wesnoth.eval_conditional(mobilize_condition))
|
||||
or (cfg.mobilize_on_gold_less_than and (wesnoth.sides[wesnoth.current.side].gold < cfg.mobilize_on_gold_less_than))
|
||||
then
|
||||
MAISD.insert_mai_self_data(self.data, cfg.ai_id, { mobilize_units = true })
|
||||
MAISD.insert_mai_self_data(data, cfg.ai_id, { mobilize_units = true })
|
||||
|
||||
-- Need to unmark all units also (all units, with and without moves)
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter } }
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter") } }
|
||||
for _,unit in ipairs(units) do
|
||||
MAIUV.delete_mai_unit_variables(unit, cfg.ai_id)
|
||||
end
|
||||
|
@ -39,12 +40,12 @@ function ca_hang_out:evaluation(ai, cfg, self)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_hang_out:execution(ai, cfg, self)
|
||||
function ca_hang_out:execution(cfg)
|
||||
local units = get_hang_out_units(cfg)
|
||||
|
||||
-- Get the locations close to which the units should hang out
|
||||
-- cfg.filter_location defaults to the location of the side leader(s)
|
||||
local filter_location = cfg.filter_location or {
|
||||
local filter_location = H.get_child(cfg, "filter_location") or {
|
||||
{ "filter", { side = wesnoth.current.side, canrecruit = "yes" } }
|
||||
}
|
||||
|
||||
|
@ -58,25 +59,29 @@ function ca_hang_out:execution(ai, cfg, self)
|
|||
-- Get map of locations to be avoided.
|
||||
-- Use [micro_ai][avoid] tag with priority over [ai][avoid].
|
||||
-- If neither is given, default to all castle terrain.
|
||||
-- ai.get_avoid() cannot be used as it always returns an array, even when the aspect is not set,
|
||||
-- and an empty array could also mean that no hexes match the filter
|
||||
local avoid_tag
|
||||
if cfg.avoid then
|
||||
avoid_tag = cfg.avoid
|
||||
local avoid_tag = H.get_child(cfg, "avoid")
|
||||
local avoid_map
|
||||
if avoid_tag then
|
||||
avoid_map = LS.of_pairs(wesnoth.get_locations(avoid_tag))
|
||||
else
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'avoid') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
avoid_tag = H.get_child(facet, 'value')
|
||||
else
|
||||
avoid_tag = { terrain = 'C*,C*^*,*^C*' }
|
||||
if facet or aspect.name ~= "composite_aspect" then
|
||||
-- If there's a [facet] child, it's set as a composite aspect,
|
||||
-- with at least one facet.
|
||||
-- But it might not be a composite aspect; it could be
|
||||
-- a Lua aspect or a standard aspect.
|
||||
avoid_map = LS.of_pairs(ai.aspects.avoid)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local avoid_map = LS.of_pairs(wesnoth.get_locations(avoid_tag))
|
||||
if avoid_map == nil then
|
||||
avoid_map = LS.of_pairs(wesnoth.get_locations { terrain = 'C*,C*^*,*^C*' })
|
||||
end
|
||||
|
||||
local max_rating, best_hex, best_unit = -9e99
|
||||
for _,unit in ipairs(units) do
|
||||
|
|
|
@ -3,7 +3,7 @@ local W = H.set_wml_action_metatable {}
|
|||
|
||||
local ca_healer_initialize = {}
|
||||
|
||||
function ca_healer_initialize:evaluation(ai)
|
||||
function ca_healer_initialize:evaluation()
|
||||
-- Set variables and aspects so that healers are excluded from attacks at beginning of turn
|
||||
-- This will be blacklisted after first execution each turn
|
||||
|
||||
|
@ -11,7 +11,7 @@ function ca_healer_initialize:evaluation(ai)
|
|||
return score
|
||||
end
|
||||
|
||||
function ca_healer_initialize:execution(ai, cfg, self)
|
||||
function ca_healer_initialize:execution(cfg, data)
|
||||
W.modify_ai {
|
||||
side = wesnoth.current.side,
|
||||
action = "try_delete",
|
||||
|
@ -27,14 +27,14 @@ function ca_healer_initialize:execution(ai, cfg, self)
|
|||
id = "no_healers_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_own", {
|
||||
{ "not", { ability = "healing", { "and", cfg.filter } } }
|
||||
{ "not", { ability = "healing", { "and", H.get_child(cfg, "filter") } } }
|
||||
} }
|
||||
} }
|
||||
}
|
||||
|
||||
-- We also need to set the score of healer moves to happen after
|
||||
-- combat (of other units) at beginning of turn
|
||||
self.data.HS_healer_move_score = 95000
|
||||
data.HS_healer_move_score = 95000
|
||||
end
|
||||
|
||||
return ca_healer_initialize
|
||||
|
|
|
@ -3,7 +3,7 @@ local W = H.set_wml_action_metatable {}
|
|||
|
||||
local ca_healer_may_attack = {}
|
||||
|
||||
function ca_healer_may_attack:evaluation(ai)
|
||||
function ca_healer_may_attack:evaluation()
|
||||
-- After attacks by all other units are done, reset things so that healers can attack, if desired
|
||||
-- This will be blacklisted after first execution each turn
|
||||
|
||||
|
@ -11,7 +11,7 @@ function ca_healer_may_attack:evaluation(ai)
|
|||
return score
|
||||
end
|
||||
|
||||
function ca_healer_may_attack:execution(ai, cfg, self)
|
||||
function ca_healer_may_attack:execution(cfg, data)
|
||||
W.modify_ai {
|
||||
side = wesnoth.current.side,
|
||||
action = "try_delete",
|
||||
|
@ -20,7 +20,7 @@ function ca_healer_may_attack:execution(ai, cfg, self)
|
|||
|
||||
-- Once combat (by other units) is done, set the healer move score so that it
|
||||
-- now happens before combat (of the healers which were so far excluded from combat)
|
||||
self.data.HS_healer_move_score = nil
|
||||
data.HS_healer_move_score = nil
|
||||
end
|
||||
|
||||
return ca_healer_may_attack
|
||||
|
|
|
@ -3,19 +3,19 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
|
||||
local ca_healer_move = {}
|
||||
local ca_healer_move, best_healer, best_hex = {}
|
||||
|
||||
function ca_healer_move:evaluation(ai, cfg, self)
|
||||
function ca_healer_move:evaluation(cfg, data)
|
||||
-- Should happen with higher priority than attacks, except at beginning of turn,
|
||||
-- when we want attacks (by other units) done first
|
||||
-- This is done so that it is possible for healers to attack, if they do not
|
||||
-- find an appropriate hex to back up other units
|
||||
local score = self.data.HS_healer_move_score or 105000
|
||||
local score = data.HS_healer_move_score or 105000
|
||||
|
||||
local all_healers = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
ability = "healing",
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
|
||||
local healers, healers_noMP = {}, {}
|
||||
|
@ -30,7 +30,7 @@ function ca_healer_move:evaluation(ai, cfg, self)
|
|||
|
||||
local all_healees = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
|
||||
local healees, healees_MP = {}, {}
|
||||
|
@ -65,7 +65,7 @@ function ca_healer_move:evaluation(ai, cfg, self)
|
|||
|
||||
local avoid_map = LS.of_pairs(ai.get_avoid())
|
||||
|
||||
local max_rating, best_healer, best_hex = -9e99
|
||||
local max_rating = -9e99
|
||||
for _,healer in ipairs(healers) do
|
||||
local reach = wesnoth.find_reach(healer)
|
||||
|
||||
|
@ -119,16 +119,15 @@ function ca_healer_move:evaluation(ai, cfg, self)
|
|||
end
|
||||
|
||||
if best_healer then
|
||||
self.data.HS_unit, self.data.HS_hex = best_healer, best_hex
|
||||
return score
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_healer_move:execution(ai, cfg, self)
|
||||
AH.movefull_outofway_stopunit(ai, self.data.HS_unit, self.data.HS_hex)
|
||||
self.data.HS_unit, self.data.HS_hex = nil, nil
|
||||
function ca_healer_move:execution(cfg)
|
||||
AH.movefull_outofway_stopunit(ai, best_healer, best_hex)
|
||||
best_healer, best_hex = nil, nil
|
||||
end
|
||||
|
||||
return ca_healer_move
|
||||
|
|
|
@ -4,7 +4,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local function get_sheep(cfg)
|
||||
local sheep = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
return sheep
|
||||
end
|
||||
|
@ -12,7 +12,7 @@ end
|
|||
local function get_dogs(cfg)
|
||||
local dogs = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
return dogs
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ local function get_enemies(cfg, radius)
|
|||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "filter_location",
|
||||
{ radius = radius,
|
||||
{ "filter", { side = wesnoth.current.side, { "and", cfg.filter_second } } } }
|
||||
{ "filter", { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter_second") } } } }
|
||||
}
|
||||
}
|
||||
return enemies
|
||||
|
@ -30,7 +30,7 @@ end
|
|||
|
||||
local ca_herding_attack_close_enemy = {}
|
||||
|
||||
function ca_herding_attack_close_enemy:evaluation(ai, cfg)
|
||||
function ca_herding_attack_close_enemy:evaluation(cfg)
|
||||
-- Any enemy within attention_distance (default = 8) hexes of a sheep will get the dogs' attention
|
||||
-- with enemies within attack_distance (default: 4) being attacked
|
||||
if not get_sheep(cfg)[1] then return 0 end
|
||||
|
@ -41,7 +41,7 @@ function ca_herding_attack_close_enemy:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_attack_close_enemy:execution(ai, cfg)
|
||||
function ca_herding_attack_close_enemy:execution(cfg)
|
||||
local sheep = get_sheep(cfg)
|
||||
local dogs = get_dogs(cfg)
|
||||
|
||||
|
|
|
@ -5,24 +5,24 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
local function get_dog(cfg)
|
||||
local dogs = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter },
|
||||
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter_second } } } } }
|
||||
{ "and", H.get_child(cfg, "filter") },
|
||||
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter_second") } } } } }
|
||||
}
|
||||
return dogs[1]
|
||||
end
|
||||
|
||||
local ca_herding_dog_move = {}
|
||||
|
||||
function ca_herding_dog_move:evaluation(ai, cfg)
|
||||
function ca_herding_dog_move:evaluation(cfg)
|
||||
-- As a final step, any dog not adjacent to a sheep moves within herding_perimeter
|
||||
if get_dog(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_dog_move:execution(ai, cfg)
|
||||
function ca_herding_dog_move:execution(cfg)
|
||||
-- We simply move the first dog first, order does not matter
|
||||
local dog = get_dog(cfg)
|
||||
local herding_perimeter = LS.of_pairs(wesnoth.get_locations(cfg.filter_location))
|
||||
local herding_perimeter = LS.of_pairs(wesnoth.get_locations(H.get_child(cfg, "filter_location")))
|
||||
|
||||
-- Find average distance of herding_perimeter from center
|
||||
local av_dist = 0
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local function get_dog(cfg)
|
||||
local dogs = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter },
|
||||
{ "and", H.get_child(cfg, "filter") },
|
||||
}
|
||||
return dogs[1]
|
||||
end
|
||||
|
@ -14,12 +15,12 @@ end
|
|||
|
||||
local ca_herding_dog_stopmove = {}
|
||||
|
||||
function ca_herding_dog_stopmove:evaluation(ai, cfg)
|
||||
function ca_herding_dog_stopmove:evaluation(cfg)
|
||||
if get_dog(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_dog_stopmove:execution(ai, cfg)
|
||||
function ca_herding_dog_stopmove:execution(cfg)
|
||||
local dog = get_dog(cfg)
|
||||
|
||||
AH.checked_stopunit_moves(ai, dog)
|
||||
|
|
|
@ -4,17 +4,18 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
return function(cfg)
|
||||
-- Find the area that the sheep can occupy
|
||||
-- First, find all contiguous hexes around center hex that are inside herding_perimeter
|
||||
local location_filter = H.get_child(cfg, "filter_location")
|
||||
local herding_area = LS.of_pairs(wesnoth.get_locations {
|
||||
x = cfg.herd_x,
|
||||
y = cfg.herd_y,
|
||||
radius = 999,
|
||||
{ "filter_radius", { { "not", cfg.filter_location } } }
|
||||
{ "filter_radius", { { "not", location_filter } } }
|
||||
})
|
||||
|
||||
-- Then, also exclude hexes next to herding_perimeter; some of the functions work better like that
|
||||
herding_area:iter( function(x, y, v)
|
||||
for xa, ya in H.adjacent_tiles(x, y) do
|
||||
if (wesnoth.match_location(xa, ya, cfg.filter_location) ) then
|
||||
if (wesnoth.match_location(xa, ya, location_filter) ) then
|
||||
herding_area:remove(x, y)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ local herding_area = wesnoth.require "ai/micro_ais/cas/ca_herding_f_herding_area
|
|||
local function get_dogs(cfg)
|
||||
local dogs = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
return dogs
|
||||
end
|
||||
|
@ -14,8 +14,8 @@ end
|
|||
local function get_sheep_to_herd(cfg)
|
||||
local all_sheep = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second },
|
||||
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter } } } } }
|
||||
{ "and", H.get_child(cfg, "filter_second") },
|
||||
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter") } } } } }
|
||||
}
|
||||
|
||||
local sheep_to_herd = {}
|
||||
|
@ -30,7 +30,7 @@ end
|
|||
|
||||
local ca_herding_herd_sheep = {}
|
||||
|
||||
function ca_herding_herd_sheep:evaluation(ai, cfg)
|
||||
function ca_herding_herd_sheep:evaluation(cfg)
|
||||
-- If dogs have moves left, and there is a sheep with moves left outside the
|
||||
-- herding area, chase it back
|
||||
if (not get_dogs(cfg)[1]) then return 0 end
|
||||
|
@ -38,7 +38,7 @@ function ca_herding_herd_sheep:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_herding_herd_sheep:execution(ai, cfg)
|
||||
function ca_herding_herd_sheep:execution(cfg)
|
||||
local dogs = get_dogs(cfg)
|
||||
local sheep_to_herd = get_sheep_to_herd(cfg)
|
||||
|
||||
|
@ -62,7 +62,7 @@ function ca_herding_herd_sheep:execution(ai, cfg)
|
|||
-- And the closer dog goes first (so that it might be able to chase another sheep afterward)
|
||||
rating = rating - H.distance_between(x, y, dog.x, dog.y) / 100.
|
||||
-- Finally, prefer to stay on path, if possible
|
||||
if (wesnoth.match_location(x, y, cfg.filter_location) ) then rating = rating + 0.001 end
|
||||
if (wesnoth.match_location(x, y, H.get_child(cfg, "filter_location")) ) then rating = rating + 0.001 end
|
||||
|
||||
reach_map:insert(x, y, rating)
|
||||
|
||||
|
|
|
@ -6,29 +6,30 @@ local herding_area = wesnoth.require "ai/micro_ais/cas/ca_herding_f_herding_area
|
|||
local function get_next_sheep(cfg)
|
||||
local sheep = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
return sheep[1]
|
||||
end
|
||||
|
||||
local ca_herding_sheep_move = {}
|
||||
|
||||
function ca_herding_sheep_move:evaluation(ai, cfg)
|
||||
function ca_herding_sheep_move:evaluation(cfg)
|
||||
-- If nothing else is to be done, the sheep do a random move
|
||||
if get_next_sheep(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_sheep_move:execution(ai, cfg)
|
||||
function ca_herding_sheep_move:execution(cfg)
|
||||
-- We simply move the first sheep first, the order does not matter
|
||||
local sheep = get_next_sheep(cfg)
|
||||
|
||||
local reach_map = AH.get_reachable_unocc(sheep)
|
||||
local dogs_filter = H.get_child(cfg, "filter")
|
||||
-- Exclude those that are next to a dog
|
||||
reach_map:iter( function(x, y, v)
|
||||
for xa, ya in H.adjacent_tiles(x, y) do
|
||||
local dog = wesnoth.get_unit(xa, ya)
|
||||
if dog and (wesnoth.match_unit(dog, cfg.filter)) then
|
||||
if dog and (wesnoth.match_unit(dog, dogs_filter)) then
|
||||
reach_map:remove(x, y)
|
||||
end
|
||||
end
|
||||
|
@ -45,7 +46,7 @@ function ca_herding_sheep_move:execution(ai, cfg)
|
|||
local herding_area = herding_area(cfg)
|
||||
local dogs = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", dogs_filter }
|
||||
}
|
||||
if herding_area:get(x, y) or (not dogs[1]) or ((x == sheep.x) and (y == sheep.y)) then
|
||||
AH.movefull_stopunit(ai, sheep, x, y)
|
||||
|
|
|
@ -4,26 +4,26 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local function get_next_sheep(cfg)
|
||||
local sheep = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second },
|
||||
{ "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter } } }
|
||||
{ "and", H.get_child(cfg, "filter_second") },
|
||||
{ "filter_adjacent", { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter") } } }
|
||||
}
|
||||
return sheep[1]
|
||||
end
|
||||
|
||||
local ca_herding_sheep_runs_dog = {}
|
||||
|
||||
function ca_herding_sheep_runs_dog:evaluation(ai, cfg)
|
||||
function ca_herding_sheep_runs_dog:evaluation(cfg)
|
||||
-- Any sheep with moves left next to a dog runs away
|
||||
if get_next_sheep(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_sheep_runs_dog:execution(ai, cfg)
|
||||
function ca_herding_sheep_runs_dog:execution(cfg)
|
||||
-- Simply get the first sheep, order does not matter
|
||||
local sheep = get_next_sheep(cfg)
|
||||
|
||||
-- Get the first dog that the sheep is adjacent to
|
||||
local dog = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter },
|
||||
local dog = wesnoth.get_units { side = wesnoth.current.side, { "and", H.get_child(cfg, "filter") },
|
||||
{ "filter_adjacent", { x = sheep.x, y = sheep.y } }
|
||||
}[1]
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local function get_next_sheep(cfg)
|
||||
local sheep = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second },
|
||||
{ "and", H.get_child(cfg, "filter_second") },
|
||||
{ "filter_location",
|
||||
{
|
||||
{ "filter", { { "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } } }
|
||||
|
@ -18,13 +18,13 @@ end
|
|||
|
||||
local ca_herding_sheep_runs_enemy = {}
|
||||
|
||||
function ca_herding_sheep_runs_enemy:evaluation(ai, cfg)
|
||||
function ca_herding_sheep_runs_enemy:evaluation(cfg)
|
||||
-- Sheep runs from any enemy within attention_distance hexes (after the dogs have moved in)
|
||||
if get_next_sheep(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_herding_sheep_runs_enemy:execution(ai, cfg)
|
||||
function ca_herding_sheep_runs_enemy:execution(cfg)
|
||||
-- Simply start with the first sheep, order does not matter
|
||||
local sheep = get_next_sheep(cfg)
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ local function hunter_attack_weakest_adj_enemy(ai, hunter)
|
|||
end
|
||||
|
||||
local function get_hunter(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local hunter = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -45,12 +45,12 @@ end
|
|||
|
||||
local ca_hunter = {}
|
||||
|
||||
function ca_hunter:evaluation(ai, cfg)
|
||||
function ca_hunter:evaluation(cfg)
|
||||
if get_hunter(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_hunter:execution(ai, cfg)
|
||||
function ca_hunter:execution(cfg)
|
||||
-- Hunter does a random wander in area given by @cfg.hunting_ground until it finds
|
||||
-- and kills an enemy unit, then retreats to position given by @cfg.home_x/y
|
||||
-- for @cfg.rest_turns turns, or until fully healed
|
||||
|
@ -64,7 +64,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
local rand = math.random(10)
|
||||
if (not hunter_vars.goal_x) or (rand == 1) then
|
||||
-- 'locs' includes border hexes, but that does not matter here
|
||||
locs = AH.get_passable_locations((cfg.filter_location or {}), hunter)
|
||||
locs = AH.get_passable_locations((H.get_child(cfg, "filter_location") or {}), hunter)
|
||||
local rand = math.random(#locs)
|
||||
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = locs[rand][1], locs[rand][2]
|
||||
|
|
|
@ -6,19 +6,19 @@ local function get_lurker(cfg)
|
|||
-- We simply pick the first of the lurkers, they have no strategy
|
||||
local lurker = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}[1]
|
||||
return lurker
|
||||
end
|
||||
|
||||
local ca_lurkers = {}
|
||||
|
||||
function ca_lurkers:evaluation(ai, cfg)
|
||||
function ca_lurkers:evaluation(cfg)
|
||||
if get_lurker(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_lurkers:execution(ai, cfg)
|
||||
function ca_lurkers:execution(cfg)
|
||||
local lurker = get_lurker(cfg)
|
||||
local targets = AH.get_live_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
|
||||
|
@ -28,10 +28,11 @@ function ca_lurkers:execution(ai, cfg)
|
|||
table.sort(targets, function(a, b) return (a.hitpoints < b.hitpoints) end)
|
||||
|
||||
local reach = LS.of_pairs(wesnoth.find_reach(lurker.x, lurker.y))
|
||||
local lurk_area = H.get_child(cfg, "filter_location")
|
||||
local reachable_attack_terrain =
|
||||
LS.of_pairs(wesnoth.get_locations {
|
||||
{ "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } },
|
||||
{ "and", cfg.filter_location }
|
||||
{ "and", lurk_area }
|
||||
})
|
||||
reachable_attack_terrain:inter(reach)
|
||||
|
||||
|
@ -72,7 +73,7 @@ function ca_lurkers:execution(ai, cfg)
|
|||
local reachable_wander_terrain =
|
||||
LS.of_pairs( wesnoth.get_locations {
|
||||
{ "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } },
|
||||
{ "and", (cfg.filter_location_wander or cfg.filter_location) }
|
||||
{ "and", H.get_child(cfg, "filter_location_wander") or lurk_area }
|
||||
})
|
||||
reachable_wander_terrain:inter(reach)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f_next_waypoint.lua"
|
||||
local best_attack
|
||||
|
||||
local function messenger_find_enemies_in_way(messenger, goal_x, goal_y)
|
||||
-- Returns the first unit on or next to the path of the messenger
|
||||
|
@ -49,17 +50,17 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
|
|||
if (not enemy_in_way) then return end
|
||||
|
||||
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local units = AH.get_units_with_attacks {
|
||||
side = wesnoth.current.side,
|
||||
{ "not", filter },
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
if (not units[1]) then return end
|
||||
|
||||
local attacks = AH.get_attacks(units, { simulate_combat = true })
|
||||
|
||||
local max_rating, best_attack = -9e99
|
||||
local max_rating = -9e99
|
||||
for _,attack in ipairs(attacks) do
|
||||
if (attack.target.x == enemy_in_way.x) and (attack.target.y == enemy_in_way.y) then
|
||||
|
||||
|
@ -96,7 +97,7 @@ end
|
|||
|
||||
local ca_messenger_attack = {}
|
||||
|
||||
function ca_messenger_attack:evaluation(ai, cfg, self)
|
||||
function ca_messenger_attack:evaluation(cfg)
|
||||
-- Attack units in the path of the messengers
|
||||
|
||||
local messenger, x, y = messenger_next_waypoint(cfg)
|
||||
|
@ -105,23 +106,22 @@ function ca_messenger_attack:evaluation(ai, cfg, self)
|
|||
local attack = messenger_find_clearing_attack(messenger, x, y, cfg)
|
||||
|
||||
if attack then
|
||||
self.data.ME_best_attack = attack
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_messenger_attack:execution(ai, cfg, self)
|
||||
local attacker = wesnoth.get_unit(self.data.ME_best_attack.src.x, self.data.ME_best_attack.src.y)
|
||||
local defender = wesnoth.get_unit(self.data.ME_best_attack.target.x, self.data.ME_best_attack.target.y)
|
||||
function ca_messenger_attack:execution(cfg)
|
||||
local attacker = wesnoth.get_unit(best_attack.src.x, best_attack.src.y)
|
||||
local defender = wesnoth.get_unit(best_attack.target.x, best_attack.target.y)
|
||||
|
||||
AH.movefull_stopunit(ai, attacker, self.data.ME_best_attack.dst.x, self.data.ME_best_attack.dst.y)
|
||||
AH.movefull_stopunit(ai, attacker, best_attack.dst.x, best_attack.dst.y)
|
||||
if (not attacker) or (not attacker.valid) then return end
|
||||
if (not defender) or (not defender.valid) then return end
|
||||
|
||||
AH.checked_attack(ai, attacker, defender)
|
||||
self.data.ME_best_attack = nil
|
||||
best_attack = nil
|
||||
end
|
||||
|
||||
return ca_messenger_attack
|
||||
|
|
|
@ -8,14 +8,14 @@ local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f
|
|||
local function get_escorts(cfg)
|
||||
local escorts = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
return escorts
|
||||
end
|
||||
|
||||
local ca_messenger_escort_move = {}
|
||||
|
||||
function ca_messenger_escort_move:evaluation(ai, cfg)
|
||||
function ca_messenger_escort_move:evaluation(cfg)
|
||||
-- Move escort units close to messengers, and in between messengers and enemies
|
||||
-- The messengers have moved at this time, so we don't need to exclude them,
|
||||
-- but we check that there are messengers left
|
||||
|
@ -28,7 +28,7 @@ function ca_messenger_escort_move:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_messenger_escort_move:execution(ai, cfg)
|
||||
function ca_messenger_escort_move:execution(cfg)
|
||||
local escorts = get_escorts(cfg)
|
||||
local _, _, _, messengers = messenger_next_waypoint(cfg)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ return function(cfg)
|
|||
-- Returns nil for first 3 arguments if no messenger has moves left
|
||||
-- Returns nil for all arguments if there are no messengers on the map
|
||||
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local messengers = wesnoth.get_units { side = wesnoth.current.side, { "and", filter } }
|
||||
if (not messengers[1]) then return end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f
|
|||
|
||||
local ca_messenger_move = {}
|
||||
|
||||
function ca_messenger_move:evaluation(ai, cfg)
|
||||
function ca_messenger_move:evaluation(cfg)
|
||||
-- Move the messenger toward goal, potentially attack adjacent unit
|
||||
|
||||
local messenger = messenger_next_waypoint(cfg)
|
||||
|
@ -14,7 +14,7 @@ function ca_messenger_move:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_messenger_move:execution(ai, cfg)
|
||||
function ca_messenger_move:execution(cfg)
|
||||
local messenger, x, y = messenger_next_waypoint(cfg)
|
||||
|
||||
if (messenger.x ~= x) or (messenger.y ~= y) then
|
||||
|
@ -57,17 +57,17 @@ function ca_messenger_move:execution(ai, cfg)
|
|||
if (unit_in_way == messenger) then unit_in_way = nil end
|
||||
if unit_in_way then wesnoth.extract_unit(unit_in_way) end
|
||||
|
||||
wesnoth.put_unit(next_hop[1], next_hop[2], messenger)
|
||||
wesnoth.put_unit(messenger, next_hop[1], next_hop[2])
|
||||
local _, cost1 = wesnoth.find_path(messenger, x, y, { ignore_units = 'yes' })
|
||||
|
||||
local unit_in_way2 = wesnoth.get_unit(optimum_hop[1], optimum_hop[2])
|
||||
if (unit_in_way2 == messenger) then unit_in_way2 = nil end
|
||||
if unit_in_way2 then wesnoth.extract_unit(unit_in_way2) end
|
||||
|
||||
wesnoth.put_unit(optimum_hop[1], optimum_hop[2], messenger)
|
||||
wesnoth.put_unit(messenger, optimum_hop[1], optimum_hop[2])
|
||||
local _, cost2 = wesnoth.find_path(messenger, x, y, { ignore_units = 'yes' })
|
||||
|
||||
wesnoth.put_unit(x_current, y_current, messenger)
|
||||
wesnoth.put_unit(messenger, x_current, y_current)
|
||||
if unit_in_way then wesnoth.put_unit(unit_in_way) end
|
||||
if unit_in_way2 then wesnoth.put_unit(unit_in_way2) end
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local function get_patrol(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local patrol = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -12,12 +13,12 @@ end
|
|||
|
||||
local ca_patrol = {}
|
||||
|
||||
function ca_patrol:evaluation(ai, cfg)
|
||||
function ca_patrol:evaluation(cfg)
|
||||
if get_patrol(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_patrol:execution(ai, cfg)
|
||||
function ca_patrol:execution(cfg)
|
||||
local patrol = get_patrol(cfg)
|
||||
local patrol_vars = MAIUV.get_mai_unit_variables(patrol, cfg.ai_id)
|
||||
|
||||
|
|
|
@ -2,16 +2,16 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
|
||||
local ca_protect_unit_attack = {}
|
||||
local ca_protect_unit_attack, best_attack = {}
|
||||
|
||||
function ca_protect_unit_attack:evaluation(ai, cfg, self)
|
||||
function ca_protect_unit_attack:evaluation(cfg)
|
||||
-- Find possible attacks for the units
|
||||
-- This is set up very conservatively: if a unit can die in the attack
|
||||
-- or the counter attack on the enemy turn, it does not attack, even if that's really unlikely
|
||||
|
||||
local units = {}
|
||||
for _,id in ipairs(cfg.id) do
|
||||
table.insert(units, AH.get_units_with_attacks { id = id }[1])
|
||||
for u in H.child_range(cfg, "unit") do
|
||||
table.insert(units, AH.get_units_with_attacks { id = u.id }[1])
|
||||
end
|
||||
if (not units[1]) then return 0 end
|
||||
|
||||
|
@ -32,7 +32,7 @@ function ca_protect_unit_attack:evaluation(ai, cfg, self)
|
|||
-- Set up a counter attack damage table, as many pairs of attacks will be the same
|
||||
local counter_damage_table = {}
|
||||
|
||||
local max_rating, best_attack = -9e99
|
||||
local max_rating = -9e99
|
||||
for _,attack in pairs(attacks) do
|
||||
-- Only consider attack if there is no chance to die or to be poisoned or slowed
|
||||
if (attack.att_stats.hp_chance[0] == 0)
|
||||
|
@ -96,22 +96,21 @@ function ca_protect_unit_attack:evaluation(ai, cfg, self)
|
|||
end
|
||||
|
||||
if best_attack then
|
||||
self.data.PU_best_attack = best_attack
|
||||
return 95000
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_protect_unit_attack:execution(ai, cfg, self)
|
||||
local attacker = wesnoth.get_unit(self.data.PU_best_attack.src.x, self.data.PU_best_attack.src.y)
|
||||
local defender = wesnoth.get_unit(self.data.PU_best_attack.target.x, self.data.PU_best_attack.target.y)
|
||||
function ca_protect_unit_attack:execution(cfg)
|
||||
local attacker = wesnoth.get_unit(best_attack.src.x, best_attack.src.y)
|
||||
local defender = wesnoth.get_unit(best_attack.target.x, best_attack.target.y)
|
||||
|
||||
AH.movefull_stopunit(ai, attacker, self.data.PU_best_attack.dst.x, self.data.PU_best_attack.dst.y)
|
||||
AH.movefull_stopunit(ai, attacker, best_attack.dst.x, best_attack.dst.y)
|
||||
if (not attacker) or (not attacker.valid) then return end
|
||||
if (not defender) or (not defender.valid) then return end
|
||||
|
||||
AH.checked_attack(ai, attacker, defender)
|
||||
self.data.PU_best_attack = nil
|
||||
best_attack = nil
|
||||
end
|
||||
|
||||
return ca_protect_unit_attack
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local ca_protect_unit_finish = {}
|
||||
local ca_protect_unit_finish, PU_unit, PU_goal = {}
|
||||
|
||||
function ca_protect_unit_finish:evaluation(ai, cfg, self)
|
||||
function ca_protect_unit_finish:evaluation(cfg)
|
||||
-- If a unit can make it to the goal, this is the first thing that happens
|
||||
for i,id in ipairs(cfg.id) do
|
||||
local unit = AH.get_units_with_moves { id = id }[1]
|
||||
for u in H.child_range(cfg, "unit") do
|
||||
local unit = AH.get_units_with_moves { id = u.id }[1]
|
||||
if unit then
|
||||
local path, cost = wesnoth.find_path(unit, cfg.goal_x[i], cfg.goal_y[i])
|
||||
if (cost <= unit.moves) and ((unit.x ~= cfg.goal_x[i]) or (unit.y ~= cfg.goal_y[i])) then
|
||||
self.data.PU_unit = unit
|
||||
self.data.PU_goal = { cfg.goal_x[i], cfg.goal_y[i] }
|
||||
local path, cost = wesnoth.find_path(unit, u.goal_x, u.goal_y)
|
||||
if (cost <= unit.moves) and ((unit.x ~= u.goal_x) or (unit.y ~= u.goal_y)) then
|
||||
PU_unit = unit
|
||||
PU_goal = { u.goal_x, u.goal_y }
|
||||
return 300000
|
||||
end
|
||||
end
|
||||
|
@ -18,9 +19,9 @@ function ca_protect_unit_finish:evaluation(ai, cfg, self)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_protect_unit_finish:execution(ai, cfg, self)
|
||||
AH.movefull_stopunit(ai, self.data.PU_unit, self.data.PU_goal)
|
||||
self.data.PU_unit, self.data.PU_goal = nil, nil
|
||||
function ca_protect_unit_finish:execution(cfg)
|
||||
AH.movefull_stopunit(ai, PU_unit, PU_goal)
|
||||
PU_unit, PU_goal = nil, nil
|
||||
end
|
||||
|
||||
return ca_protect_unit_finish
|
||||
|
|
|
@ -5,20 +5,20 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
|||
|
||||
local function get_protected_units(cfg)
|
||||
local units = {}
|
||||
for _,id in ipairs(cfg.id) do
|
||||
table.insert(units, AH.get_units_with_moves { id = id }[1])
|
||||
for u in H.child_range(cfg, "unit") do
|
||||
table.insert(units, AH.get_units_with_moves { id = u.id }[1])
|
||||
end
|
||||
return units
|
||||
end
|
||||
|
||||
local ca_protect_unit_move = {}
|
||||
|
||||
function ca_protect_unit_move:evaluation(ai, cfg, self)
|
||||
function ca_protect_unit_move:evaluation(cfg)
|
||||
if get_protected_units(cfg)[1] then return 94999 end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_protect_unit_move:execution(ai, cfg, self)
|
||||
function ca_protect_unit_move:execution(cfg, data)
|
||||
-- Find and execute best (safest) move toward goal
|
||||
local protected_units = get_protected_units(cfg)
|
||||
|
||||
|
@ -40,8 +40,8 @@ function ca_protect_unit_move:execution(ai, cfg, self)
|
|||
-- We move the weakest (fewest HP unit) first
|
||||
local unit = AH.choose(protected_units, function(u) return - u.hitpoints end)
|
||||
local goal = {}
|
||||
for i,id in ipairs(cfg.id) do
|
||||
if (unit.id == id) then goal = { cfg.goal_x[i], cfg.goal_y[i] } end
|
||||
for u in H.child_range(cfg, "unit") do
|
||||
if (unit.id == u.id) then goal = { u.goal_x, u.goal_y } end
|
||||
end
|
||||
|
||||
local reach_map = AH.get_reachable_unocc(unit)
|
||||
|
@ -58,10 +58,10 @@ function ca_protect_unit_move:execution(ai, cfg, self)
|
|||
end)
|
||||
|
||||
-- Configuration parameters (no option to change these enabled at the moment)
|
||||
local enemy_weight = self.data.PU_enemy_weight or 100.
|
||||
local my_unit_weight = self.data.PU_my_unit_weight or 1.
|
||||
local distance_weight = self.data.PU_distance_weight or 3.
|
||||
local terrain_weight = self.data.PU_terrain_weight or 0.1
|
||||
local enemy_weight = data.PU_enemy_weight or 100.
|
||||
local my_unit_weight = data.PU_my_unit_weight or 1.
|
||||
local distance_weight = data.PU_distance_weight or 3.
|
||||
local terrain_weight = data.PU_terrain_weight or 0.1
|
||||
|
||||
-- If there are no enemies left, only distance to goal matters
|
||||
-- This is to avoid rare situations where moving toward goal rating is canceled by rating for moving away from own units
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
local LS = wesnoth.dofile "lua/location_set.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local recruit_type
|
||||
|
||||
local ca_recruit_random = {}
|
||||
|
||||
function ca_recruit_random:evaluation(ai, cfg)
|
||||
function ca_recruit_random:evaluation(cfg)
|
||||
-- Random recruiting from all the units the side has
|
||||
|
||||
-- Check if leader is on keep
|
||||
|
@ -59,15 +59,14 @@ function ca_recruit_random:evaluation(ai, cfg)
|
|||
local probabilities, probability_sum = {}, 0
|
||||
|
||||
-- Go through all the types listed in [probability] tags (which can be comma-separated lists)
|
||||
-- Types and probabilities are put into cfg.type and cfg.prob arrays by micro_ai_wml_tag.lua
|
||||
for ind,types in ipairs(cfg.type) do
|
||||
types = AH.split(types, ",")
|
||||
for prob in H.child_range(cfg, "probability") do
|
||||
types = AH.split(prob.type, ",")
|
||||
for _,typ in ipairs(types) do -- 'type' is a reserved keyword in Lua
|
||||
-- If this type is in the recruit list, add it
|
||||
for _,recruit in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
if (recruit == typ) then
|
||||
probabilities[typ] = { value = cfg.prob[ind] }
|
||||
probability_sum = probability_sum + cfg.prob[ind]
|
||||
probabilities[typ] = { value = prob.probability }
|
||||
probability_sum = probability_sum + prob.probability
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -119,7 +118,7 @@ function ca_recruit_random:evaluation(ai, cfg)
|
|||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_recruit_random:execution(ai, cfg)
|
||||
function ca_recruit_random:execution(cfg)
|
||||
-- Let this function blacklist itself if the chosen recruit is too expensive
|
||||
if wesnoth.unit_types[recruit_type].cost <= wesnoth.sides[wesnoth.current.side].gold then
|
||||
AH.checked_recruit(ai, recruit_type)
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
local internal_recruit_cas = {}
|
||||
local internal_params = {}
|
||||
local ai
|
||||
|
||||
-- The following external engine creates the CA functions recruit_rushers_eval and recruit_rushers_exec
|
||||
-- It also exposes find_best_recruit and find_best_recruit_hex for use by other recruit engines
|
||||
|
||||
-- 'ai' is nil here (not defined), so we pass it directly in the execution function below
|
||||
wesnoth.require("ai/lua/generic_recruit_engine.lua").init(ai, internal_recruit_cas, internal_params)
|
||||
|
||||
local ca_recruit_rushers = {}
|
||||
|
||||
function ca_recruit_rushers:evaluation(ai, cfg)
|
||||
function ca_recruit_rushers:evaluation(cfg)
|
||||
internal_params.randomness = cfg.randomness
|
||||
internal_params.score_function = function() return cfg.ca_score end
|
||||
return internal_recruit_cas:recruit_rushers_eval()
|
||||
end
|
||||
|
||||
function ca_recruit_rushers:execution(ai)
|
||||
return internal_recruit_cas:recruit_rushers_exec(ai)
|
||||
function ca_recruit_rushers:execution()
|
||||
return internal_recruit_cas:recruit_rushers_exec()
|
||||
end
|
||||
|
||||
return ca_recruit_rushers
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local function get_guardian(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local guardian = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -12,7 +13,7 @@ end
|
|||
|
||||
local ca_return_guardian = {}
|
||||
|
||||
function ca_return_guardian:evaluation(ai, cfg)
|
||||
function ca_return_guardian:evaluation(cfg)
|
||||
local guardian = get_guardian(cfg)
|
||||
if guardian then
|
||||
if (guardian.x == cfg.return_x) and (guardian.y == cfg.return_y) then
|
||||
|
@ -25,7 +26,7 @@ function ca_return_guardian:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_return_guardian:execution(ai, cfg)
|
||||
function ca_return_guardian:execution(cfg)
|
||||
local guardian = get_guardian(cfg)
|
||||
|
||||
-- In case the return hex is occupied:
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local ca_simple_attack = {}
|
||||
local ca_simple_attack, best_attack = {}
|
||||
|
||||
function ca_simple_attack:evaluation(ai, cfg, self)
|
||||
function ca_simple_attack:evaluation(cfg)
|
||||
local units = AH.get_units_with_attacks {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
if (not units[1]) then return 0 end
|
||||
|
||||
-- If cfg.filter_second is set, set up a map (location set)
|
||||
-- of enemies that it is okay to attack
|
||||
local enemy_filter = H.get_child(cfg, "filter_second")
|
||||
local enemy_map
|
||||
if cfg.filter_second then
|
||||
if enemy_filter then
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", enemy_filter }
|
||||
}
|
||||
if (not enemies[1]) then return 0 end
|
||||
|
||||
|
@ -29,10 +31,10 @@ function ca_simple_attack:evaluation(ai, cfg, self)
|
|||
local attacks = AH.get_attacks(units, { include_occupied = true })
|
||||
if (not attacks[1]) then return 0 end
|
||||
|
||||
local max_rating, best_attack = -9e99
|
||||
local max_rating = -9e99
|
||||
for _, att in ipairs(attacks) do
|
||||
local valid_target = true
|
||||
if cfg.filter_second and (not enemy_map:get(att.target.x, att.target.y)) then
|
||||
if enemy_filter and (not enemy_map:get(att.target.x, att.target.y)) then
|
||||
valid_target = false
|
||||
end
|
||||
|
||||
|
@ -50,23 +52,22 @@ function ca_simple_attack:evaluation(ai, cfg, self)
|
|||
end
|
||||
|
||||
if best_attack then
|
||||
self.data.SA_attack = best_attack
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_simple_attack:execution(ai, cfg, self)
|
||||
local attacker = wesnoth.get_unit(self.data.SA_attack.src.x, self.data.SA_attack.src.y)
|
||||
local defender = wesnoth.get_unit(self.data.SA_attack.target.x, self.data.SA_attack.target.y)
|
||||
function ca_simple_attack:execution(cfg)
|
||||
local attacker = wesnoth.get_unit(best_attack.src.x, best_attack.src.y)
|
||||
local defender = wesnoth.get_unit(best_attack.target.x, best_attack.target.y)
|
||||
|
||||
AH.movefull_outofway_stopunit(ai, attacker, self.data.SA_attack.dst.x, self.data.SA_attack.dst.y)
|
||||
AH.movefull_outofway_stopunit(ai, attacker, best_attack.dst.x, best_attack.dst.y)
|
||||
if (not attacker) or (not attacker.valid) then return end
|
||||
if (not defender) or (not defender.valid) then return end
|
||||
|
||||
AH.checked_attack(ai, attacker, defender, (cfg.weapon or -1))
|
||||
self.data.SA_attack = nil
|
||||
best_attack = nil
|
||||
end
|
||||
|
||||
return ca_simple_attack
|
||||
|
|
|
@ -2,7 +2,7 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local function get_guardian(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local guardian = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -12,12 +12,12 @@ end
|
|||
|
||||
local ca_stationed_guardian = {}
|
||||
|
||||
function ca_stationed_guardian:evaluation(ai, cfg)
|
||||
function ca_stationed_guardian:evaluation(cfg)
|
||||
if get_guardian(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_stationed_guardian:execution(ai, cfg)
|
||||
function ca_stationed_guardian:execution(cfg)
|
||||
-- (s_x, s_y): coordinates where guardian is stationed; tries to move here if there is nobody to attack
|
||||
-- (g_x, g_y): location that the guardian guards
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
|
||||
local ca_swarm_move = {}
|
||||
|
||||
function ca_swarm_move:evaluation(ai, cfg)
|
||||
function ca_swarm_move:evaluation(cfg)
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for _,unit in ipairs(units) do
|
||||
if (unit.moves > 0) then return cfg.ca_score end
|
||||
|
@ -12,7 +12,7 @@ function ca_swarm_move:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_swarm_move:execution(ai, cfg)
|
||||
function ca_swarm_move:execution(cfg)
|
||||
local enemy_distance = cfg.enemy_distance or 5
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
||||
|
|
|
@ -18,13 +18,13 @@ end
|
|||
|
||||
local ca_swarm_scatter = {}
|
||||
|
||||
function ca_swarm_scatter:evaluation(ai, cfg)
|
||||
function ca_swarm_scatter:evaluation(cfg)
|
||||
if (not get_enemies(cfg)[1]) then return 0 end
|
||||
if (not get_swarm_units(cfg)[1]) then return 0 end
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_swarm_scatter:execution(ai, cfg)
|
||||
function ca_swarm_scatter:execution(cfg)
|
||||
local enemies = get_enemies(cfg)
|
||||
local units = get_swarm_units(cfg)
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
|
|
@ -5,7 +5,7 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
|||
local function get_wolves(cfg)
|
||||
local wolves = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
return wolves
|
||||
end
|
||||
|
@ -13,20 +13,20 @@ end
|
|||
local function get_prey(cfg)
|
||||
local prey = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "and", cfg.filter_second }
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
}
|
||||
return prey
|
||||
end
|
||||
|
||||
local ca_wolves_move = {}
|
||||
|
||||
function ca_wolves_move:evaluation(ai, cfg)
|
||||
function ca_wolves_move:evaluation(cfg)
|
||||
if (not get_wolves(cfg)[1]) then return 0 end
|
||||
if (not get_prey(cfg)[1]) then return 0 end
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_wolves_move:execution(ai, cfg)
|
||||
function ca_wolves_move:execution(cfg)
|
||||
local wolves = get_wolves(cfg)
|
||||
local prey = get_prey(cfg)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ local WMPF = wesnoth.require "ai/micro_ais/cas/ca_wolves_multipacks_functions.lu
|
|||
|
||||
local ca_wolves_multipacks_attack = {}
|
||||
|
||||
function ca_wolves_multipacks_attack:evaluation(ai, cfg)
|
||||
function ca_wolves_multipacks_attack:evaluation(cfg)
|
||||
-- If wolves have attacks left, call this CA
|
||||
-- It will be disabled by being black-listed, so as to avoid
|
||||
-- having to do the full attack evaluation for every single move evaluation
|
||||
|
@ -19,7 +19,7 @@ function ca_wolves_multipacks_attack:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_wolves_multipacks_attack:execution(ai, cfg)
|
||||
function ca_wolves_multipacks_attack:execution(cfg)
|
||||
local packs = WMPF.assign_packs(cfg)
|
||||
|
||||
-- Attacks are dealt with on a pack by pack basis
|
||||
|
|
|
@ -6,7 +6,7 @@ local WMPF = wesnoth.require "ai/micro_ais/cas/ca_wolves_multipacks_functions.lu
|
|||
|
||||
local ca_wolves_multipacks_wander = {}
|
||||
|
||||
function ca_wolves_multipacks_wander:evaluation(ai, cfg)
|
||||
function ca_wolves_multipacks_wander:evaluation(cfg)
|
||||
-- When there's nothing to attack, the wolves wander and regroup into their packs
|
||||
|
||||
local wolves = AH.get_units_with_moves {
|
||||
|
@ -18,7 +18,7 @@ function ca_wolves_multipacks_wander:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
function ca_wolves_multipacks_wander:execution(ai, cfg)
|
||||
function ca_wolves_multipacks_wander:execution(cfg)
|
||||
local packs = WMPF.assign_packs(cfg)
|
||||
|
||||
for pack_number,pack in pairs(packs) do
|
||||
|
|
|
@ -6,20 +6,20 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
local function get_wolves(cfg)
|
||||
local wolves = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", H.get_child(cfg, "filter") }
|
||||
}
|
||||
return wolves
|
||||
end
|
||||
|
||||
local ca_wolves_wander = {}
|
||||
|
||||
function ca_wolves_wander:evaluation(ai, cfg)
|
||||
function ca_wolves_wander:evaluation(cfg)
|
||||
-- When there's no prey left, the wolves wander and regroup
|
||||
if get_wolves(cfg)[1] then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_wolves_wander:execution(ai, cfg)
|
||||
function ca_wolves_wander:execution(cfg)
|
||||
local wolves = get_wolves(cfg)
|
||||
|
||||
-- Number of wolves that can reach each hex
|
||||
|
|
|
@ -3,7 +3,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local function get_guardian(cfg)
|
||||
local filter = cfg.filter or { id = cfg.id }
|
||||
local filter = H.get_child(cfg, "filter") or { id = cfg.id }
|
||||
local guardian = AH.get_units_with_moves {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
|
@ -13,16 +13,17 @@ end
|
|||
|
||||
local ca_zone_guardian = {}
|
||||
|
||||
function ca_zone_guardian:evaluation(ai, cfg)
|
||||
function ca_zone_guardian:evaluation(cfg)
|
||||
if get_guardian(cfg) then return cfg.ca_score end
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_zone_guardian:execution(ai, cfg)
|
||||
function ca_zone_guardian:execution(cfg)
|
||||
local guardian = get_guardian(cfg)
|
||||
local reach = wesnoth.find_reach(guardian)
|
||||
|
||||
local zone_enemy = cfg.filter_location_enemy or cfg.filter_location
|
||||
local zone = H.get_child(cfg, "filter_location")
|
||||
local zone_enemy = H.get_child(cfg, "filter_location_enemy") or zone
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "filter_location", zone_enemy }
|
||||
|
@ -95,7 +96,7 @@ function ca_zone_guardian:execution(ai, cfg)
|
|||
local locs_map = LS.of_pairs(wesnoth.get_locations {
|
||||
x = '1-' .. width,
|
||||
y = '1-' .. height,
|
||||
{ "and", cfg.filter_location }
|
||||
{ "and", zone }
|
||||
})
|
||||
|
||||
-- Check out which of those hexes the guardian can reach
|
||||
|
|
|
@ -6,7 +6,7 @@ def is_swamp(map, xx, yy)
|
|||
map(
|
||||
filter( map.terrain, (x=xx) and (y=yy)),
|
||||
self.id
|
||||
)[0] = 'swamp_water';
|
||||
)[0] = 'swamp_water_reed';
|
||||
|
||||
def reachable_swamp(unit_loc,map)
|
||||
# get all reachable swamp locs for unit at unit_loc #
|
||||
|
@ -36,9 +36,9 @@ def weakest_defender(attacks)
|
|||
# Attack if possible, otherwise random move #
|
||||
if( size(possible_attacks) != 0,
|
||||
weakest_defender(possible_attacks),
|
||||
if( size(swamp_in_reach) != 0,
|
||||
move(me.loc,random(swamp_in_reach)),
|
||||
end)
|
||||
if( size(swamp_in_reach) != 0,
|
||||
move(me.loc,random(swamp_in_reach)),
|
||||
end)
|
||||
)
|
||||
|
||||
where possible_attacks = reachable_enemies_next_to_swamp( me.loc, my_attacks,map)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
return {
|
||||
init = function(ai)
|
||||
init = function()
|
||||
local priority_target = {}
|
||||
|
||||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
return {
|
||||
init = function(ai)
|
||||
init = function()
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local urudin = {}
|
||||
|
|
133
data/ai/micro_ais/mai-defs/animals.lua
Normal file
133
data/ai/micro_ais/mai-defs/animals.lua
Normal file
|
@ -0,0 +1,133 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
function wesnoth.micro_ais.big_animals(cfg)
|
||||
local required_keys = { "[filter]"}
|
||||
local optional_keys = { "[avoid_unit]", "[filter_location]", "[filter_location_wander]" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_big_animals',
|
||||
{ ca_id = "move", location = 'ca_big_animals.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves(cfg)
|
||||
local required_keys = { "[filter]", "[filter_second]" }
|
||||
local optional_keys = { "attack_only_prey", "avoid_type" }
|
||||
local score = cfg.ca_score or 90000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves',
|
||||
{ ca_id = "move", location = 'ca_wolves_move.lua', score = score },
|
||||
{ ca_id = "wander", location = 'ca_wolves_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
if cfg.attack_only_prey then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
elseif cfg.avoid_type then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "not", {
|
||||
type=cfg.avoid_type
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
end
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.herding(cfg)
|
||||
local required_keys = { "[filter_location]", "[filter]", "[filter_second]", "herd_x", "herd_y" }
|
||||
local optional_keys = { "attention_distance", "attack_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_herding',
|
||||
{ ca_id = "attack_close_enemy", location = 'ca_herding_attack_close_enemy.lua', score = score },
|
||||
{ ca_id = "sheep_runs_enemy", location = 'ca_herding_sheep_runs_enemy.lua', score = score - 1 },
|
||||
{ ca_id = "sheep_runs_dog", location = 'ca_herding_sheep_runs_dog.lua', score = score - 2 },
|
||||
{ ca_id = "herd_sheep", location = 'ca_herding_herd_sheep.lua', score = score - 3 },
|
||||
{ ca_id = "sheep_move", location = 'ca_herding_sheep_move.lua', score = score - 4 },
|
||||
{ ca_id = "dog_move", location = 'ca_herding_dog_move.lua', score = score - 5 },
|
||||
{ ca_id = "dog_stopmove", location = 'ca_herding_dog_stopmove.lua', score = score - 6 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.forest_animals(cfg)
|
||||
local optional_keys = { "rabbit_type", "rabbit_number", "rabbit_enemy_distance", "rabbit_hole_img",
|
||||
"tusker_type", "tusklet_type", "deer_type", "[filter_location]"
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_forest_animals',
|
||||
{ ca_id = "new_rabbit", location = 'ca_forest_animals_new_rabbit.lua', score = score },
|
||||
{ ca_id = "tusker_attack", location = 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
|
||||
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
|
||||
{ ca_id = "tusklet_move", location = 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.swarm(cfg)
|
||||
local optional_keys = { "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_swarm',
|
||||
{ ca_id = "scatter", location = 'ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "move", location = 'ca_swarm_move.lua', score = score - 1 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves_multipacks(cfg)
|
||||
local optional_keys = { "type", "pack_size", "show_pack_number" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
{ ca_id = "attack", location = 'ca_wolves_multipacks_attack.lua', score = score },
|
||||
{ ca_id = "wander", location = 'ca_wolves_multipacks_wander.lua', score = score - 1 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.hunter(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "home_x", "home_y" }
|
||||
local optional_keys = { "id", "[filter]", "[filter_location]", "rest_turns", "show_messages" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hunter',
|
||||
{ ca_id = "move", location = 'ca_hunter.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
12
data/ai/micro_ais/mai-defs/bottleneck.lua
Normal file
12
data/ai/micro_ais/mai-defs/bottleneck.lua
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
function wesnoth.micro_ais.bottleneck_defense(cfg)
|
||||
local required_keys = { "x", "y", "enemy_x", "enemy_y" }
|
||||
local optional_keys = { "healer_x", "healer_y", "leadership_x", "leadership_y", "active_side_leader" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_bottleneck',
|
||||
{ ca_id = 'move', location = 'ca_bottleneck_move.lua', score = score },
|
||||
{ ca_id = 'attack', location = 'ca_bottleneck_attack.lua', score = score - 1 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
17
data/ai/micro_ais/mai-defs/escort.lua
Normal file
17
data/ai/micro_ais/mai-defs/escort.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.messenger_escort(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
{ ca_id = 'attack', location = 'ca_messenger_attack.lua', score = score },
|
||||
{ ca_id = 'move', location = 'ca_messenger_move.lua', score = score - 1 },
|
||||
{ ca_id = 'escort_move', location = 'ca_messenger_escort_move.lua', score = score - 2 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
115
data/ai/micro_ais/mai-defs/fast.lua
Normal file
115
data/ai/micro_ais/mai-defs/fast.lua
Normal file
|
@ -0,0 +1,115 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
|
||||
function wesnoth.micro_ais.fast_ai(cfg)
|
||||
local optional_keys = {
|
||||
"attack_hidden_enemies", "[avoid]", "dungeon_mode",
|
||||
"[filter]", "[filter_second]", "include_occupied_attack_hexes",
|
||||
"leader_additional_threat", "leader_attack_max_units", "leader_weight", "move_cost_factor",
|
||||
"weak_units_first", "skip_combat_ca", "skip_move_ca", "threatened_leader_fights"
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_fast',
|
||||
{ ca_id = 'combat', location = 'ca_fast_combat.lua', score = 100000 },
|
||||
{ ca_id = 'move', location = 'ca_fast_move.lua', score = 20000 },
|
||||
{ ca_id = 'combat_leader', location = 'ca_fast_combat_leader.lua', score = 19900 }
|
||||
}
|
||||
|
||||
-- Also need to delete/add some default CAs
|
||||
if (cfg.action == 'delete') then
|
||||
-- This can be done independently of whether these were removed earlier
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="combat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::combat_phase",
|
||||
max_score=100000,
|
||||
score=100000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="villages",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::get_villages_phase",
|
||||
max_score=60000,
|
||||
score=60000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="retreat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::retreat_phase",
|
||||
max_score=40000,
|
||||
score=40000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_to_targets",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_to_targets_phase",
|
||||
max_score=20000,
|
||||
score=20000
|
||||
} }
|
||||
}
|
||||
else
|
||||
if (not cfg.skip_combat_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[combat]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'combat') or (parm.ca_id == 'combat_leader') then
|
||||
table.remove(CA_parms, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not cfg.skip_move_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[villages]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[retreat]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_to_targets]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'move') then
|
||||
table.remove(CA_parms, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
53
data/ai/micro_ais/mai-defs/guardian.lua
Normal file
53
data/ai/micro_ais/mai-defs/guardian.lua
Normal file
|
@ -0,0 +1,53 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.stationed_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "distance", "station_x", "station_y" }
|
||||
local optional_keys = { "id", "[filter]", "guard_x", "guard_y" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_stationed_guardian',
|
||||
{ ca_id = 'move', location = 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.zone_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Zone Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "[filter_location]" }
|
||||
local optional_keys = { "id", "[filter]", "[filter_location_enemy]", "station_x", "station_y" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_zone_guardian',
|
||||
{ ca_id = 'move', location = 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.return_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "return_x", "return_y" }
|
||||
local optional_keys = { "id", "[filter]" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_return_guardian',
|
||||
{ ca_id = 'move', location = 'ca_return_guardian.lua', score = cfg.ca_score or 100010 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.coward(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Coward [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "distance" }
|
||||
local optional_keys = { "attack_if_trapped", "id", "[filter]", "[filter_second]", "seek_x", "seek_y","avoid_x","avoid_y" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_coward',
|
||||
{ ca_id = 'move', location = 'ca_coward.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
17
data/ai/micro_ais/mai-defs/healers.lua
Normal file
17
data/ai/micro_ais/mai-defs/healers.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
function wesnoth.micro_ais.healer_support(cfg)
|
||||
local optional_keys = { "aggression", "injured_units_only", "max_threats", "[filter]", "[filter_second]" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_healer',
|
||||
{ ca_id = 'initialize', location = 'ca_healer_initialize.lua', score = 999990 },
|
||||
{ ca_id = 'move', location = 'ca_healer_move.lua', score = 105000 },
|
||||
}
|
||||
|
||||
-- The healers_can_attack CA is only added to the table if aggression ~= 0
|
||||
-- But: make sure we always try removal
|
||||
if (cfg.action == 'delete') or (tonumber(cfg.aggression) ~= 0) then
|
||||
table.insert(CA_parms, { ca_id = 'may_attack', location = 'ca_healer_may_attack.lua', score = 99990 })
|
||||
end
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
42
data/ai/micro_ais/mai-defs/misc.lua
Normal file
42
data/ai/micro_ais/mai-defs/misc.lua
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
function wesnoth.micro_ais.lurkers(cfg)
|
||||
local required_keys = { "[filter]", "[filter_location]" }
|
||||
local optional_keys = { "stationary", "[filter_location_wander]" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_lurkers',
|
||||
{ ca_id = 'move', location = 'ca_lurkers.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
-- goto is a keyword, so need to use index operator directly
|
||||
wesnoth.micro_ais["goto"] = function(cfg)
|
||||
local required_keys = { "[filter_location]" }
|
||||
local optional_keys = {
|
||||
"avoid_enemies", "[filter]", "ignore_units", "ignore_enemy_at_goal",
|
||||
"release_all_units_at_goal", "release_unit_at_goal", "unique_goals", "use_straight_line"
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_goto',
|
||||
{ ca_id = 'move', location = 'ca_goto.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.hang_out(cfg)
|
||||
local optional_keys = { "[filter]", "[filter_location]", "[avoid]", "[mobilize_condition]", "mobilize_on_gold_less_than" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hang_out',
|
||||
{ ca_id = 'move', location = 'ca_hang_out.lua', score = cfg.ca_score or 170000 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.simple_attack(cfg)
|
||||
local optional_keys = { "[filter]", "[filter_second]", "weapon" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
14
data/ai/micro_ais/mai-defs/patrol.lua
Normal file
14
data/ai/micro_ais/mai-defs/patrol.lua
Normal file
|
@ -0,0 +1,14 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.patrol(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { "id", "[filter]", "attack", "one_time_only", "out_and_back" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
75
data/ai/micro_ais/mai-defs/protect.lua
Normal file
75
data/ai/micro_ais/mai-defs/protect.lua
Normal file
|
@ -0,0 +1,75 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
function wesnoth.micro_ais.protect_unit(cfg)
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_protect_unit',
|
||||
{ ca_id = 'finish', location = 'ca_protect_unit_finish.lua', score = 300000 },
|
||||
{ ca_id = 'attack', location = 'ca_protect_unit_attack.lua', score = 95000 },
|
||||
{ ca_id = 'move', location = 'ca_protect_unit_move.lua', score = 94999 }
|
||||
}
|
||||
|
||||
local unit_ids = {}
|
||||
for u in H.child_range(cfg, "unit") do
|
||||
if not u.id then
|
||||
H.wml_error("Protect Unit Micro AI missing id key in [unit] tag")
|
||||
end
|
||||
if not u.goal_x then
|
||||
H.wml_error("Protect Unit Micro AI missing goal_x key in [unit] tag")
|
||||
end
|
||||
if not u.goal_y then
|
||||
H.wml_error("Protect Unit Micro AI missing goal_y key in [unit] tag")
|
||||
end
|
||||
table.insert(unit_ids, u.id)
|
||||
end
|
||||
|
||||
-- Optional key disable_move_leader_to_keep: needs to be dealt with
|
||||
-- separately as it affects a default CA
|
||||
if cfg.disable_move_leader_to_keep then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_leader_to_keep]"
|
||||
}
|
||||
end
|
||||
|
||||
-- attacks aspects also needs to be set separately
|
||||
local aspect_parms = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
ca_id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_own", {
|
||||
{ "not", {
|
||||
id = unit_ids
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, aspect_parms)
|
||||
-- We also need to add the move_leader_to_keep CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_leader_to_keep",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_leader_to_keep_phase",
|
||||
max_score=160000,
|
||||
score=160000
|
||||
} }
|
||||
}
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, aspect_parms)
|
||||
end
|
||||
return {"[unit]"}, {}, CA_parms
|
||||
end
|
50
data/ai/micro_ais/mai-defs/recruiting.lua
Normal file
50
data/ai/micro_ais/mai-defs/recruiting.lua
Normal file
|
@ -0,0 +1,50 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
|
||||
local function handle_default_recruitment(cfg)
|
||||
-- Also need to delete/add the default recruitment CA
|
||||
if cfg.action == 'add' then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[recruitment]"
|
||||
}
|
||||
elseif cfg.action == 'delete' then
|
||||
-- We need to add the recruitment CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="recruitment",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::aspect_recruitment_phase",
|
||||
max_score=180000,
|
||||
score=180000
|
||||
} }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_rushers(cfg)
|
||||
local optional_keys = { "randomness" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_rusher_recruit',
|
||||
{ ca_id = "move", location = 'ca_recruit_rushers.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
handle_default_recruitment(cfg)
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_random(cfg)
|
||||
local optional_keys = { "skip_low_gold_recruiting", "[probability]" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
handle_default_recruitment(cfg)
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
|
@ -1,24 +1,38 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local T = H.set_wml_tag_metatable {}
|
||||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local micro_ai_helper = {}
|
||||
|
||||
function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
||||
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_parms is an array of tables, one for each CA to be added (CA setup parameters)
|
||||
-- and also contains one key: ai_id
|
||||
-- @CA_cfg is a table with the parameters passed to the eval/exec functions
|
||||
-- @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
|
||||
|
||||
-- We need to make sure that the id/name of each CA are unique.
|
||||
-- We do this by checking if CAs starting with ai_id exist already
|
||||
-- If yes, we add numbers to the end of ai_id until we find an id that does not exist yet
|
||||
-- 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
|
||||
-- AI of the same type. ai_id is used for this and must therefore be unique.
|
||||
-- We ensure this by checking if CAs or [data][micro_ai] tags using the
|
||||
-- default ai_id value exist already and if so, by adding numbers to the end
|
||||
-- until we find an id that is not used yet.
|
||||
-- ca_id_core: This is used as base for the id= and name= keys of the
|
||||
-- [candidate_action] tags. If [micro_ai]ca_id= is given, we use it as is
|
||||
-- without checking if an AI with this id already exists. This is required in
|
||||
-- order to ensure that removal with action=delete is possible and it is the
|
||||
-- responsibility of the user to ensure uniqueness. If [micro_ai]ca_id= is not
|
||||
-- given, use ai_id for ca_id_core, which also makes ids unique for this case.
|
||||
-- ca_id: This is specific to the individual CAs of an AI and is added to
|
||||
-- ca_id_core for the names and ids of each CA.
|
||||
|
||||
local ai_id, id_found = CA_parms.ai_id, true
|
||||
|
||||
|
@ -38,7 +52,7 @@ function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
|||
end
|
||||
|
||||
-- Ideally, we would also delete previous occurrences of [micro_ai] tags in the
|
||||
-- AI's self.data variable. However, the MAI can be changed while it is not
|
||||
-- AI's data variable. However, the MAI can be changed while it is not
|
||||
-- the AI's turn, when this is not possible. So instead, we check for the
|
||||
-- existence of such tags and make sure we are using a different ai_id.
|
||||
for ai_tag in H.child_range(wesnoth.sides[side].__cfg, 'ai') do
|
||||
|
@ -58,9 +72,12 @@ function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
|||
n = n + 1
|
||||
end
|
||||
|
||||
-- For CA ids and names, use value of [micro_ai]ca_id= if given, ai_id otherwise
|
||||
ca_id_core = ca_id_core or ai_id
|
||||
|
||||
-- Now add the CAs
|
||||
for _,parms in ipairs(CA_parms) do
|
||||
local ca_id = ai_id .. '_' .. parms.ca_id
|
||||
local ca_id = ca_id_core .. '_' .. parms.ca_id
|
||||
|
||||
-- Always pass the ai_id and ca_score to the eval/exec functions
|
||||
CA_cfg.ai_id = ai_id
|
||||
|
@ -74,27 +91,29 @@ function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
|||
}
|
||||
|
||||
CA.location = parms.location
|
||||
local cfg = string.sub(AH.serialize(CA_cfg), 2, -2) -- need to strip surrounding {}
|
||||
CA.eval_parms = cfg
|
||||
CA.exec_parms = cfg
|
||||
table.insert(CA, T.args(CA_cfg))
|
||||
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", CA }
|
||||
T.candidate_action(CA)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function micro_ai_helper.delete_CAs(side, CA_parms)
|
||||
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_parms is an 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
|
||||
-- @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
|
||||
|
||||
for _,parms in ipairs(CA_parms) do
|
||||
local ca_id = CA_parms.ai_id .. '_' .. parms.ca_id
|
||||
local ca_id = ca_id_core .. '_' .. parms.ca_id
|
||||
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
|
@ -136,7 +155,7 @@ function micro_ai_helper.add_aspects(side, aspect_parms)
|
|||
side = side,
|
||||
action = "add",
|
||||
path = "aspect[" .. parms.aspect .. "].facet",
|
||||
{ "facet", parms.facet }
|
||||
T.facet(parms.facet)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -157,16 +176,8 @@ function micro_ai_helper.delete_aspects(side, aspect_parms)
|
|||
end
|
||||
|
||||
function micro_ai_helper.micro_ai_setup(cfg, CA_parms, required_keys, optional_keys)
|
||||
-- If cfg.ca_id is set, it gets used as the ai_id= key
|
||||
-- This allows for selective removal of CAs
|
||||
-- Note: the ca_id key of the [micro_ai] tag should really be renamed to ai_id,
|
||||
-- but that would mean breaking backward compatibility, so we'll just deal with it internally instead
|
||||
|
||||
CA_parms.ai_id = cfg.ca_id or CA_parms.ai_id
|
||||
|
||||
-- If action=delete, we do that and are done
|
||||
if (cfg.action == 'delete') then
|
||||
micro_ai_helper.delete_CAs(cfg.side, CA_parms)
|
||||
micro_ai_helper.delete_CAs(cfg.side, cfg.ca_id, CA_parms)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -175,26 +186,39 @@ function micro_ai_helper.micro_ai_setup(cfg, CA_parms, required_keys, optional_k
|
|||
|
||||
-- Required keys
|
||||
for _,v in pairs(required_keys) do
|
||||
local child = H.get_child(cfg, v)
|
||||
if (not cfg[v]) and (not child) then
|
||||
H.wml_error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: " .. v)
|
||||
if v:match('%[[a-zA-Z0-9_]+%]') then
|
||||
v = v:sub(2,-2)
|
||||
if not H.get_child(cfg, v) then
|
||||
H.wml_error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: [" .. v .. "]")
|
||||
end
|
||||
for child in H.child_range(cfg, v) do
|
||||
table.insert(CA_cfg, T[v](child))
|
||||
end
|
||||
else
|
||||
if not cfg[v] then
|
||||
H.wml_error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: " .. v .."=")
|
||||
end
|
||||
CA_cfg[v] = cfg[v]
|
||||
end
|
||||
CA_cfg[v] = cfg[v]
|
||||
if child then CA_cfg[v] = child end
|
||||
end
|
||||
|
||||
-- Optional keys
|
||||
for _,v in pairs(optional_keys) do
|
||||
CA_cfg[v] = cfg[v]
|
||||
local child = H.get_child(cfg, v)
|
||||
if child then CA_cfg[v] = child end
|
||||
if v:match('%[[a-zA-Z0-9_]+%]') then
|
||||
v = v:sub(2,-2)
|
||||
for child in H.child_range(cfg, v) do
|
||||
table.insert(CA_cfg, T[v](child))
|
||||
end
|
||||
else
|
||||
CA_cfg[v] = cfg[v]
|
||||
end
|
||||
end
|
||||
|
||||
-- Finally, set up the candidate actions themselves
|
||||
if (cfg.action == 'add') then micro_ai_helper.add_CAs(cfg.side, CA_parms, CA_cfg) end
|
||||
if (cfg.action == 'add') then micro_ai_helper.add_CAs(cfg.side, cfg.ca_id, CA_parms, CA_cfg) end
|
||||
if (cfg.action == 'change') then
|
||||
micro_ai_helper.delete_CAs(cfg.side, CA_parms, cfg.id)
|
||||
micro_ai_helper.add_CAs(cfg.side, CA_parms, CA_cfg)
|
||||
micro_ai_helper.delete_CAs(cfg.side, cfg.ca_id, CA_parms)
|
||||
micro_ai_helper.add_CAs(cfg.side, cfg.ca_id, CA_parms, CA_cfg)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
-- This set of functions provides a consistent way of storing Micro AI variables
|
||||
-- in the AI's persistent self.data variable. These need to be stored inside
|
||||
-- in the AI's persistent data variable. These need to be stored inside
|
||||
-- a [micro_ai] tag, so that they are bundled together for a given Micro AI
|
||||
-- together with an ai_id= key. Their existence can then be checked when setting
|
||||
-- up another MAI. Otherwise other Micro AIs used in the same scenario might
|
||||
-- work incorrectly or not at all.
|
||||
-- Note that, ideally, we would delete these [micro_ai] tags when a Micro AI is
|
||||
-- deleted, but that that is not always possible as deletion can happen on
|
||||
-- another side's turn, while the self.data variable is only accessible during
|
||||
-- another side's turn, while the data variable is only accessible during
|
||||
-- the AI turn.
|
||||
-- Note that, with this method, there can only ever be one of these tags for each
|
||||
-- Micro AI (but of course several when there are several Micro AIs for the
|
||||
|
@ -18,7 +18,7 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local micro_ai_self_data = {}
|
||||
|
||||
function micro_ai_self_data.modify_mai_self_data(self_data, ai_id, action, vars_table)
|
||||
-- Modify self.data [micro_ai] tags
|
||||
-- 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
|
||||
|
@ -73,7 +73,7 @@ function micro_ai_self_data.set_mai_self_data(self_data, ai_id, vars_table)
|
|||
end
|
||||
|
||||
function micro_ai_self_data.get_mai_self_data(self_data, ai_id, key)
|
||||
-- Get the content of the self.data [micro_ai] tag for the given @ai_id
|
||||
-- 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)
|
||||
|
|
|
@ -2,51 +2,25 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local W = H.set_wml_action_metatable {}
|
||||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
wesnoth.micro_ais = {}
|
||||
|
||||
-- Load all default MicroAIs
|
||||
wesnoth.require("ai/micro_ais/mai-defs/animals.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/bottleneck.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/escort.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/fast.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/guardian.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/healers.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/misc.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/patrol.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/protect.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/recruiting.lua")
|
||||
|
||||
function wesnoth.wml_actions.micro_ai(cfg)
|
||||
local CA_path = 'ai/micro_ais/cas/'
|
||||
|
||||
cfg = cfg.__parsed
|
||||
|
||||
-- Add translation for old-syntax animal MAIs to new syntax plus deprecation message
|
||||
if (cfg.ai_type == 'animals') and (cfg.animal_type) then
|
||||
wesnoth.message("The syntax 'ai_type=animals animal_type=" .. cfg.animal_type .. "' is deprecated. Use 'ai_type=" .. cfg.animal_type .. "' instead.")
|
||||
|
||||
cfg.ai_type = cfg.animal_type
|
||||
cfg.animal_type = nil
|
||||
end
|
||||
|
||||
-- Add translation for old-syntax guardian MAIs to new syntax plus deprecation message
|
||||
if (cfg.ai_type == 'guardian_unit') and (cfg.guardian_type) then
|
||||
wesnoth.message("The syntax 'ai_type=guardian_unit guardian_type=" .. cfg.guardian_type .. "' is deprecated. Use 'ai_type=" .. cfg.guardian_type .. "' instead.")
|
||||
|
||||
cfg.ai_type = cfg.guardian_type
|
||||
cfg.guardian_type = nil
|
||||
end
|
||||
|
||||
-- Add translation for old-syntax hunter_unit MAI to new syntax plus deprecation message
|
||||
if (cfg.ai_type == 'hunter_unit') then
|
||||
wesnoth.message("'ai_type=hunter_unit' is deprecated. Use 'ai_type=hunter' instead.")
|
||||
|
||||
cfg.ai_type = 'hunter'
|
||||
end
|
||||
|
||||
-- Add translation for old-syntax patrol_unit MAI to new syntax plus deprecation message
|
||||
if (cfg.ai_type == 'patrol_unit') then
|
||||
wesnoth.message("'ai_type=patrol_unit' is deprecated. Use 'ai_type=patrol' instead.")
|
||||
|
||||
cfg.ai_type = 'patrol'
|
||||
end
|
||||
|
||||
-- Add translation for old-syntax recruiting MAI to new syntax plus deprecation message
|
||||
if (cfg.ai_type == 'recruiting') and (cfg.recruiting_type) then
|
||||
local new_type = 'recruit_random'
|
||||
if (cfg.recruiting_type == 'rushers') then new_type = 'recruit_rushers' end
|
||||
wesnoth.message("The syntax 'ai_type=recruiting recruiting_type=" .. cfg.recruiting_type .. "' is deprecated. Use 'ai_type=" .. new_type .. "' instead.")
|
||||
|
||||
cfg.ai_type = new_type
|
||||
cfg.recruiting_type = nil
|
||||
end
|
||||
|
||||
-- Check that the required common keys are all present and set correctly
|
||||
if (not cfg.ai_type) then H.wml_error("[micro_ai] is missing required ai_type= key") end
|
||||
if (not cfg.side) then H.wml_error("[micro_ai] is missing required side= key") end
|
||||
|
@ -61,518 +35,18 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
end
|
||||
|
||||
-- Set up the configuration tables for the different Micro AIs
|
||||
local required_keys, optional_keys, CA_parms = {}, {}, {}
|
||||
|
||||
--------- Healer Support Micro AI ------------------------------------
|
||||
if (cfg.ai_type == 'healer_support') then
|
||||
optional_keys = { "aggression", "injured_units_only", "max_threats", "filter", "filter_second" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
CA_parms = {
|
||||
ai_id = 'mai_healer',
|
||||
{ ca_id = 'initialize', location = CA_path .. 'ca_healer_initialize.lua', score = 999990 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_healer_move.lua', score = 105000 },
|
||||
}
|
||||
|
||||
-- The healers_can_attack CA is only added to the table if aggression ~= 0
|
||||
-- But: make sure we always try removal
|
||||
if (cfg.action == 'delete') or (tonumber(cfg.aggression) ~= 0) then
|
||||
table.insert(CA_parms, { ca_id = 'may_attack', location = CA_path .. 'ca_healer_may_attack.lua', score = 99990 })
|
||||
end
|
||||
|
||||
--------- Bottleneck Defense Micro AI -----------------------------------
|
||||
elseif (cfg.ai_type == 'bottleneck_defense') then
|
||||
required_keys = { "x", "y", "enemy_x", "enemy_y" }
|
||||
optional_keys = { "healer_x", "healer_y", "leadership_x", "leadership_y", "active_side_leader" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_bottleneck',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_bottleneck_move.lua', score = score },
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_bottleneck_attack.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
--------- Messenger Escort Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'messenger_escort') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "waypoint_x", "waypoint_y" }
|
||||
optional_keys = { "id", "enemy_death_chance", "filter", "filter_second", "invert_order", "messenger_death_chance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_messenger_attack.lua', score = score },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_messenger_move.lua', score = score - 1 },
|
||||
{ ca_id = 'escort_move', location = CA_path .. 'ca_messenger_escort_move.lua', score = score - 2 }
|
||||
}
|
||||
|
||||
--------- Lurkers Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'lurkers') then
|
||||
required_keys = { "filter", "filter_location" }
|
||||
optional_keys = { "stationary", "filter_location_wander" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_lurkers',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_lurkers.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Protect Unit Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'protect_unit') then
|
||||
required_keys = { "id", "goal_x", "goal_y" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
CA_parms = {
|
||||
ai_id = 'mai_protect_unit',
|
||||
{ ca_id = 'finish', location = CA_path .. 'ca_protect_unit_finish.lua', score = 300000 },
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_protect_unit_attack.lua', score = 95000 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_protect_unit_move.lua', score = 94999 }
|
||||
}
|
||||
|
||||
-- [unit] tags need to be dealt with separately
|
||||
cfg.id, cfg.goal_x, cfg.goal_y = {}, {}, {}
|
||||
if (cfg.action ~= 'delete') then
|
||||
for unit in H.child_range(cfg, "unit") do
|
||||
if (not unit.id) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required id= key")
|
||||
end
|
||||
if (not unit.goal_x) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_x= key")
|
||||
end
|
||||
if (not unit.goal_y) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_y= key")
|
||||
end
|
||||
table.insert(cfg.id, unit.id)
|
||||
table.insert(cfg.goal_x, unit.goal_x)
|
||||
table.insert(cfg.goal_y, unit.goal_y)
|
||||
end
|
||||
|
||||
if (not cfg.id[1]) then
|
||||
H.wml_error("Protect Unit Micro AI is missing required [unit] tag")
|
||||
end
|
||||
end
|
||||
|
||||
-- Optional key disable_move_leader_to_keep: needs to be dealt with
|
||||
-- separately as it affects a default CA
|
||||
if cfg.disable_move_leader_to_keep then
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_leader_to_keep]"
|
||||
}
|
||||
end
|
||||
|
||||
-- attacks aspects also needs to be set separately
|
||||
local unit_ids_str = 'dummy'
|
||||
for _,id in ipairs(cfg.id) do
|
||||
unit_ids_str = unit_ids_str .. ',' .. id
|
||||
end
|
||||
local aspect_parms = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
ca_id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_own", {
|
||||
{ "not", {
|
||||
id = unit_ids_str
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, aspect_parms)
|
||||
-- We also need to add the move_leader_to_keep CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_leader_to_keep",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_leader_to_keep_phase",
|
||||
max_score=160000,
|
||||
score=160000
|
||||
} }
|
||||
}
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, aspect_parms)
|
||||
end
|
||||
|
||||
--------- Micro AI Guardian -----------------------------------
|
||||
elseif (cfg.ai_type == 'stationed_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "distance", "station_x", "station_y" }
|
||||
optional_keys = { "id", "filter", "guard_x", "guard_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_stationed_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'zone_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Zone Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "filter_location" }
|
||||
optional_keys = { "id", "filter", "filter_location_enemy", "station_x", "station_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_zone_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'return_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "return_x", "return_y" }
|
||||
optional_keys = { "id", "filter" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_return_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_return_guardian.lua', score = cfg.ca_score or 100010 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'coward') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Coward [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "distance" }
|
||||
optional_keys = { "attack_if_trapped", "id", "filter", "filter_second", "seek_x", "seek_y","avoid_x","avoid_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_coward',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_coward.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Micro AI Animals ------------------------------------
|
||||
elseif (cfg.ai_type == 'big_animals') then
|
||||
required_keys = { "filter"}
|
||||
optional_keys = { "avoid_unit", "filter_location", "filter_location_wander" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_big_animals',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_big_animals.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'wolves') then
|
||||
required_keys = { "filter", "filter_second" }
|
||||
optional_keys = { "attack_only_prey", "avoid_type" }
|
||||
local score = cfg.ca_score or 90000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_wolves',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_wolves_move.lua', score = score },
|
||||
{ ca_id = "wander", location = CA_path .. 'ca_wolves_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
if cfg.attack_only_prey then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
elseif cfg.avoid_type then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "not", {
|
||||
type=cfg.avoid_type
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
end
|
||||
|
||||
elseif (cfg.ai_type == 'herding') then
|
||||
required_keys = { "filter_location", "filter", "filter_second", "herd_x", "herd_y" }
|
||||
optional_keys = { "attention_distance", "attack_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_herding',
|
||||
{ ca_id = "attack_close_enemy", location = CA_path .. 'ca_herding_attack_close_enemy.lua', score = score },
|
||||
{ ca_id = "sheep_runs_enemy", location = CA_path .. 'ca_herding_sheep_runs_enemy.lua', score = score - 1 },
|
||||
{ ca_id = "sheep_runs_dog", location = CA_path .. 'ca_herding_sheep_runs_dog.lua', score = score - 2 },
|
||||
{ ca_id = "herd_sheep", location = CA_path .. 'ca_herding_herd_sheep.lua', score = score - 3 },
|
||||
{ ca_id = "sheep_move", location = CA_path .. 'ca_herding_sheep_move.lua', score = score - 4 },
|
||||
{ ca_id = "dog_move", location = CA_path .. 'ca_herding_dog_move.lua', score = score - 5 },
|
||||
{ ca_id = "dog_stopmove", location = CA_path .. 'ca_herding_dog_stopmove.lua', score = score - 6 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'forest_animals') then
|
||||
optional_keys = { "rabbit_type", "rabbit_number", "rabbit_enemy_distance", "rabbit_hole_img",
|
||||
"tusker_type", "tusklet_type", "deer_type", "filter_location"
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_forest_animals',
|
||||
{ ca_id = "new_rabbit", location = CA_path .. 'ca_forest_animals_new_rabbit.lua', score = score },
|
||||
{ ca_id = "tusker_attack", location = CA_path .. 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
|
||||
{ ca_id = "move", location = CA_path .. 'ca_forest_animals_move.lua', score = score - 2 },
|
||||
{ ca_id = "tusklet_move", location = CA_path .. 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'swarm') then
|
||||
optional_keys = { "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_swarm',
|
||||
{ ca_id = "scatter", location = CA_path .. 'ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "move", location = CA_path .. 'ca_swarm_move.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'wolves_multipacks') then
|
||||
optional_keys = { "type", "pack_size", "show_pack_number" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
{ ca_id = "attack", location = CA_path .. 'ca_wolves_multipacks_attack.lua', score = score },
|
||||
{ ca_id = "wander", location = CA_path .. 'ca_wolves_multipacks_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'hunter') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "home_x", "home_y" }
|
||||
optional_keys = { "id", "filter", "filter_location", "rest_turns", "show_messages" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_hunter',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_hunter.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Patrol Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'patrol') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "waypoint_x", "waypoint_y" }
|
||||
optional_keys = { "id", "filter", "attack", "one_time_only", "out_and_back" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Recruiting Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'recruit_rushers') or (cfg.ai_type == 'recruit_random')then
|
||||
if (cfg.ai_type == 'recruit_rushers') then
|
||||
optional_keys = { "randomness" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_rusher_recruit',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_recruit_rushers.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
else
|
||||
optional_keys = { "skip_low_gold_recruiting", "type", "prob" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
if (cfg.action ~= 'delete') then
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
for probability in H.child_range(cfg, "probability") do
|
||||
if (not probability.type) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required type= key")
|
||||
end
|
||||
if (not probability.probability) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required probability= key")
|
||||
end
|
||||
table.insert(cfg.type, probability.type)
|
||||
table.insert(cfg.prob, probability.probability)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Also need to delete/add the default recruitment CA
|
||||
if cfg.action == 'add' then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[recruitment]"
|
||||
}
|
||||
elseif cfg.action == 'delete' then
|
||||
-- We need to add the recruitment CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="recruitment",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::aspect_recruitment_phase",
|
||||
max_score=180000,
|
||||
score=180000
|
||||
} }
|
||||
}
|
||||
end
|
||||
|
||||
--------- Goto Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'goto') then
|
||||
required_keys = { "filter_location" }
|
||||
optional_keys = {
|
||||
"avoid_enemies", "filter", "ignore_units", "ignore_enemy_at_goal",
|
||||
"release_all_units_at_goal", "release_unit_at_goal", "unique_goals", "use_straight_line"
|
||||
}
|
||||
CA_parms = {
|
||||
ai_id = 'mai_goto',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_goto.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Hang Out Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'hang_out') then
|
||||
optional_keys = { "filter", "filter_location", "avoid", "mobilize_condition", "mobilize_on_gold_less_than" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_hang_out',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_hang_out.lua', score = cfg.ca_score or 170000 }
|
||||
}
|
||||
|
||||
--------- Simple Attack Micro AI ---------------------------
|
||||
elseif (cfg.ai_type == 'simple_attack') then
|
||||
optional_keys = { "filter", "filter_second", "weapon" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'fast_ai') then
|
||||
optional_keys = {
|
||||
"attack_hidden_enemies", "avoid", "dungeon_mode",
|
||||
"filter", "filter_second", "include_occupied_attack_hexes",
|
||||
"leader_additional_threat", "leader_attack_max_units", "leader_weight", "move_cost_factor",
|
||||
"weak_units_first", "skip_combat_ca", "skip_move_ca", "threatened_leader_fights"
|
||||
}
|
||||
CA_parms = {
|
||||
ai_id = 'mai_fast',
|
||||
{ ca_id = 'combat', location = CA_path .. 'ca_fast_combat.lua', score = 100000 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_fast_move.lua', score = 20000 },
|
||||
{ ca_id = 'combat_leader', location = CA_path .. 'ca_fast_combat_leader.lua', score = 19900 }
|
||||
}
|
||||
|
||||
-- Also need to delete/add some default CAs
|
||||
if (cfg.action == 'delete') then
|
||||
-- This can be done independently of whether these were removed earlier
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="combat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::combat_phase",
|
||||
max_score=100000,
|
||||
score=100000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="villages",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::get_villages_phase",
|
||||
max_score=60000,
|
||||
score=60000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="retreat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::retreat_phase",
|
||||
max_score=40000,
|
||||
score=40000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_to_targets",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_to_targets_phase",
|
||||
max_score=20000,
|
||||
score=20000
|
||||
} }
|
||||
}
|
||||
else
|
||||
if (not cfg.skip_combat_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[combat]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'combat') or (parm.ca_id == 'combat_leader') then
|
||||
table.remove(CA_parms, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not cfg.skip_move_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[villages]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[retreat]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_to_targets]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'move') then
|
||||
table.remove(CA_parms, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If we got here, none of the valid ai_types was specified
|
||||
else
|
||||
if wesnoth.micro_ais[cfg.ai_type] == nil then
|
||||
H.wml_error("unknown value for ai_type= in [micro_ai]")
|
||||
end
|
||||
|
||||
local required_keys, optional_keys, CA_parms = wesnoth.micro_ais[cfg.ai_type](cfg)
|
||||
|
||||
-- Fixup any relative CA paths
|
||||
for i,v in ipairs(CA_parms) do
|
||||
if v.location and v.location:find('~') ~= 1 then
|
||||
v.location = CA_path .. v.location
|
||||
end
|
||||
end
|
||||
|
||||
MAIH.micro_ai_setup(cfg, CA_parms, required_keys, optional_keys)
|
||||
end
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
#against elves, and just do it
|
||||
#define EBESIEGED_RECRUITMENT
|
||||
[ai]
|
||||
recruitment_ignore_bad_movement=yes
|
||||
recruitment_ignore_bad_combat=yes
|
||||
simple_targeting=yes
|
||||
[/ai]
|
||||
#enddef
|
||||
|
@ -166,7 +164,7 @@
|
|||
recruit=Elvish Shaman,Elvish Archer,Elvish Fighter
|
||||
[ai]
|
||||
passive_leader="yes"
|
||||
leader_shares_keep="yes"
|
||||
passive_leader_shares_keep="yes"
|
||||
recruitment_pattern=archer,archer,fighter,fighter,healer
|
||||
[/ai]
|
||||
gold=170
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
[side]
|
||||
side=1
|
||||
controller=human
|
||||
suppress_end_turn_confirmation=yes
|
||||
hidden=no
|
||||
name= _ "Rutburt"
|
||||
id=Rutburt
|
||||
|
@ -357,20 +358,25 @@ Also note: The Animal AIs are coded as Micro AIs. A Micro AI can be added and ad
|
|||
# wmlindent: stop ignoring
|
||||
# wmllint: unbalanced-off
|
||||
[option]
|
||||
message= _ "<span font='16'>I'll just watch the animals.</span>"
|
||||
label= _ "<span font='16'>I'll just watch the animals.</span>"
|
||||
[command]
|
||||
[modify_side]
|
||||
side=1
|
||||
controller=null
|
||||
hidden=yes
|
||||
[/modify_side]
|
||||
[kill]
|
||||
side=1
|
||||
[/kill]
|
||||
[end_turn]
|
||||
[/end_turn]
|
||||
[event]
|
||||
name=side 2 turn
|
||||
[modify_side]
|
||||
side=1
|
||||
controller=null
|
||||
hidden=yes
|
||||
[/modify_side]
|
||||
[kill]
|
||||
side=1
|
||||
[/kill]
|
||||
[/event]
|
||||
[/command]
|
||||
[/option]
|
||||
[option]
|
||||
message= _ "<span font='16'>I want to have control of Side 1.</span>"
|
||||
label= _ "<span font='16'>I want to have control of Side 1.</span>"
|
||||
[/option]
|
||||
[/message]
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
[side]
|
||||
side=2
|
||||
controller=ai
|
||||
suppress_end_turn_confirmation=yes
|
||||
type=Orcish Leader
|
||||
id=Big Bad Orc
|
||||
name= _ "Big Bad Orc"
|
||||
|
@ -98,10 +99,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]
|
||||
message= _ "<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]
|
||||
message= _ "<span font='16'>I'll play the orcs.</span>"
|
||||
label= _ "<span font='16'>I'll play the orcs.</span>"
|
||||
[command]
|
||||
[modify_side]
|
||||
side=2
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
side=2
|
||||
type=Dread Bat
|
||||
controller=human
|
||||
suppress_end_turn_confirmation=yes
|
||||
id=Dreadful Bat
|
||||
name= _ "Dreadful Bat"
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[side]
|
||||
side=1
|
||||
controller=human
|
||||
suppress_end_turn_confirmation=yes
|
||||
hidden=no
|
||||
name=Vaddan
|
||||
id=Vaddan
|
||||
|
@ -297,6 +298,7 @@
|
|||
side=7
|
||||
ai_type=goto
|
||||
action=add
|
||||
ca_id=bats
|
||||
|
||||
[filter_location]
|
||||
x=1,1,44,44
|
||||
|
@ -364,12 +366,15 @@
|
|||
{VARIABLE_CONDITIONAL scenario_name equals goto}
|
||||
[/show_if]
|
||||
[command]
|
||||
[modify_side]
|
||||
side=1
|
||||
controller=null
|
||||
[/modify_side]
|
||||
[end_turn]
|
||||
[/end_turn]
|
||||
[event]
|
||||
name=side turn
|
||||
[modify_side]
|
||||
side=1
|
||||
controller=null
|
||||
[/modify_side]
|
||||
[/event]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
[set_menu_item]
|
||||
|
@ -715,6 +720,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
side=7
|
||||
ai_type=goto
|
||||
action=change
|
||||
ca_id=bats
|
||||
|
||||
[filter_location]
|
||||
x,y=35,13
|
||||
|
@ -755,6 +761,7 @@ Note: The messengers are controlled by Goto Micro AI definitions that differ by
|
|||
side=7
|
||||
ai_type=goto
|
||||
action=change
|
||||
ca_id=bats
|
||||
|
||||
[filter_location]
|
||||
x=1,1,44,44
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue